diff --git a/.env.ci b/.env.ci new file mode 100644 index 000000000..7d7d4d301 --- /dev/null +++ b/.env.ci @@ -0,0 +1,63 @@ +# the follow are for accessing the test database via the IXP Manager UI on +# barryo's development platform: +APP_KEY="base64:8QNjg1qPkdRSRntnjpKbfBpfUtENOu40WQtxHJWdTXs=" +APP_URL="http://127.0.0.1:8000" + +APP_ENV="testing" +APP_DEBUG=true +APP_TIMEZONE="Europe/Dublin" +APP_LOG="single" + +DEBUGBAR_ENABLED=false +TELESCOPE_ENABLED=false + +IXP_PHPUNIT_RUNNING=true + +DB_HOST=127.0.0.1 +DB_DATABASE=ixp_ci +DB_USERNAME=root +DB_PASSWORD= + + +####################################################################################### +### Graphing - see https://ixp-manager.readthedocs.org/en/latest/features/grapher.html +### Needed for the MRTG config generation test. +GRAPHER_BACKENDS="mrtg|smokeping|sflow|dummy" +GRAPHER_BACKEND_MRTG_WORKDIR="/tmp" +GRAPHER_BACKEND_MRTG_LOGDIR="/tmp" +GRAPHER_BACKEND_SFLOW_ENABLED=true +### end graphing + +# GRAPHER_ACCESS_IXP=1 + +CACHE_DRIVER=array + +IXP_API_JSONEXPORTSCHEMA_PUBLIC=true + +MAIL_MAILER=array + +QUEUE_CONNECTION=sync + + + +# IP address and port of the first RPKI local cache: +IXP_RPKI_RTR1_HOST=10.39.5.123 +IXP_RPKI_RTR1_PORT=3323 + +# While not required, we recommend you also install a second validator: +IXP_RPKI_RTR2_HOST=10.39.5.124 +IXP_RPKI_RTR2_PORT=3323 + +IXP_FE_FRONTEND_CUSTOMER_ONE=customer +IXP_FE_FRONTEND_CUSTOMER_MANY=customers +IXP_FE_FRONTEND_CUSTOMER_OWNER="customer's" +IXP_FE_FRONTEND_CUSTOMER_OWNERS="customers'" + +# See: https://docs.ixpmanager.org/latest/features/irrdb/ +IXP_IRRDB_BGPQ3_PATH=/usr/local/bin/bgpq3 + +# 2fa is disabled in the test environment as the tests emulating web requests +# do not have an understanding of 2fa. we enable 2fa manually when we need to +# run the Dusk UI 2fa tests +2FA_ENABLED=false +2FA_ENFORCE_FOR_USERS="1" diff --git a/.env.dev b/.env.dev new file mode 100644 index 000000000..13271edc0 --- /dev/null +++ b/.env.dev @@ -0,0 +1,302 @@ +####################################################################################### +# +# IXP Manager V4+ configuration. +# +# + +APP_ENV=local + +# Run "artisan key:generate" to set the application key: +APP_KEY=base64:I1jSHNC04p4w839KCg3CrpQu0TTeTnUzzNwMlLpSt84= + +# Set this to false in production (but change it to true if you have installation or +# other issues running IXP Manager). +APP_DEBUG=true + +# Web address where IXP Manager is accessed. This is a **required** setting. It is +# currently used for generating all URLs within IXP Manager (action, assets, etc.). +# It is also used / required for sending emails via CLI scripts. +# +APP_URL="http://127.0.0.1:8088" + + +# See http://php.net/manual/en/timezones.php for a list of timezones: +APP_TIMEZONE="Europe/Dublin" + +# Laravel log format (storage/log). See config/log.php and +# https://laravel.com/docs/5.4/errors +LOG_CHANNEL="single" + +# info by default, one of: debug, info, notice, warning, error, critical, alert, emergency. +APP_LOG_LEVEL=debug + +# MySQL Connection Details +DB_HOST="127.0.0.1" +DB_DATABASE="ixpmanager" +DB_USERNAME="ixpmanager" +DB_PASSWORD="ixpmanager" +# DB_DATABASE="ixpm-inex" +# DB_USERNAME="ixpm-inex" +# DB_PASSWORD="ixpm-inex" + +####################################################################################### +### Identity +# +# Used throughout IXP Manager in various ways. +# +# This has grown organically and we intend to clean this up in a coming release and +# documenting where and how each one is spceifically used. + + +# Used in various emails, etc. +IDENTITY_SITENAME="Vagrant IXP Manager" + +# Shown in title bar of web portal. Defaults to IDENTITY_SITENAME +# IDENTITY_TITLENAME="Vagrant IXP Manager" + +IDENTITY_LEGALNAME="Vagrant City IXP" +IDENTITY_CITY="Dublin" +IDENTITY_COUNTRY="IE" +IDENTITY_ORGNAME="${IDENTITY_LEGALNAME}" + +# As well as uses in other places, emails are sent from the following name/email: +IDENTITY_NAME="${IDENTITY_LEGALNAME}" +IDENTITY_EMAIL="ixp@example.com" + +IDENTITY_TESTEMAIL="${IDENTITY_EMAIL}" + +# Used on some traffic graphs: +IDENTITY_WATERMARK="Vagrant City IXP" + +IDENTITY_SUPPORT_EMAIL="${IDENTITY_EMAIL}" +IDENTITY_SUPPORT_PHONE="+1 111 555 5555" +IDENTITY_SUPPORT_HOURS="24x7" + +IDENTITY_BILLING_EMAIL="${IDENTITY_EMAIL}" +IDENTITY_BILLING_PHONE="+1 111 555 5555" +IDENTITY_BILLING_HOURS="24x7" + +# Web address of your IXP's website. Used in IX-F Export schema, etc. +IDENTITY_CORPORATE_URL="http://www.example.com/" + +# The logo to show on the login page. Should be a URL. +# (the example here works - the leading '//' means the browser should match http/https based on the web page) +IDENTITY_BIGLOGO="//www.ixpmanager.org/images/logos/ixp-manager.png" + +# For some actions (e.g. peering matrix) we need to know what VLAN to show by default. +# This is the vlan.id database entry (i.e. not the VLAN number/tag!) +IDENTITY_DEFAULT_VLAN=1 + + +######################################################################################### +### Member vs. Customer +### +### IXP Manager is an open source project and typically used in member-owned IXPs. +### As such, the language used mostly is 'member'. To change this to 'customer' just +### uncomment the following lines: +# +# IXP_FE_FRONTEND_CUSTOMER_ONE=customer +# IXP_FE_FRONTEND_CUSTOMER_MANY=customers +# IXP_FE_FRONTEND_CUSTOMER_OWNER="customer's" +# IXP_FE_FRONTEND_CUSTOMER_OWNERS="customers'" + + + +####################################################################################### +### Features +# + +# See: https://docs.ixpmanager.org/latest/features/reseller/ +IXP_RESELLER_ENABLED=false + +# See: https://docs.ixpmanager.org/latest/features/as112/ +IXP_AS112_UI_ACTIVE=false + + +####################################################################################### +### Frontend controllers and controller configuration +# +# Some frontend controllers are disabled by default. This is for a variety of reasons +# including: additional configuration may be required, maintain backwards +# compatibility, etc. + +# Allow customers / admins to upload logos for members. Set to false to enabled. +# See: https://docs.ixpmanager.org/latest/usage/customers/#customer-logos +IXP_FE_FRONTEND_DISABLED_LOGO=false + + +# Send email notifications when a customer's billing details are updated. +# See: https://docs.ixpmanager.org/latest/usage/customers/#notification-of-billing-details-changed +# IXP_FE_CUSTOMER_BILLING_UPDATES_NOTIFY="mail@example.com" + + +# Disable links to the peering matrix if you have not set it up (with sflow): +# IXP_FE_FRONTEND_DISABLED_PEERING_MATRIX=true + + +####################################################################################### +### Email Settings. +# +# We use Laravel's mail system which in turn uses SwiftMailer. +# +# See config/mail.php abd https://laravel.com/docs/5.5/mail +# +# The default setting is 'sendmail' which tries to use your local systems mail client. +# +# MAIL_MAILER="sendmail" +# MAIL_HOST="localhost" +# MAIL_PORT=25 +# MAIL_ENCRYPTION="tls" + +MAIL_MAILER=smtp +MAIL_HOST=localhost +MAIL_PORT=2525 +MAIL_USERNAME=Inbox-Name +MAIL_PASSWORD=null +MAIL_ENCRYPTION=false + + +####################################################################################### +### Graphing - see https://docs.ixpmanager.org/latest/grapher/introduction + +# Enable the backends you have configured. E.g.: +# GRAPHER_BACKENDS="mrtg|sflow|smokeping" + +# On a new installation, we just use placeholders from the dummy backend: +GRAPHER_BACKENDS="dummy" + +# With the cache enabled, IXP Manager does not have to regenerate / reload / reprocess +# log / rrd / image files if we have cached them and they are less than 5mins old. This +# is enabled by default which is the recommended setting. +GRAPHER_CACHE_ENABLED=true + +################################################################################# +## Grapher - Mrtg - see: https://docs.ixpmanager.org/latest/grapher/mrtg/ +## + +# For backwards compatibility, the default is 'log' but 'rrd' is more modern: +GRAPHER_BACKEND_MRTG_DBTYPE="rrd" + +# The defaults for these are '/tmp' to require you to change them to something +# more sensible such as: +# GRAPHER_BACKEND_MRTG_WORKDIR="/srv/mrtg" +# GRAPHER_BACKEND_MRTG_LOGDIR="/srv/mrtg" + +################################################################################# +## Grapher - sflow - see: https://docs.ixpmanager.org/latest/grapher/sflow/ +## + +# GRAPHER_BACKEND_SFLOW_ENABLED=false +# GRAPHER_BACKEND_SFLOW_ROOT="http://sflow-server.example.com/grapher-sflow" + + +################################################################################# +## Grapher - smokeping - see: https://docs.ixpmanager.org/latest/grapher/smokeping/ +## + +# Mark it as enabled (this just affects whether certain UI elements are shown): +# GRAPHER_BACKEND_SMOKEPING_ENABLED=true + +# And set the default location to fetch the Smokeping graphs from: +# GRAPHER_BACKEND_SMOKEPING_URL="http://www.example.com/smokeping" + + +################################################################################# +## IX-F Member Export - see: https://docs.ixpmanager.org/latest/features/ixf-export/ + + +IXP_API_JSONEXPORTSCHEMA_PUBLIC=true + + +####################################################################################### +### Skinning +# +# See https://ixp-manager.readthedocs.io/en/latest/features/skinning.html +# +# VIEW_SKIN="myskin" + + +####################################################################################### +# See config/cache.php +CACHE_DRIVER=array + +####################################################################################### +# Session Lifetimes - standard and remember me. +# +# See https://docs.ixpmanager.org/latest/usage/authentication/ +# +# SESSION_LIFETIME=120 +# AUTH_TOKEN_EXPIRE=43200 + +####################################################################################### +# see config/doctrine.php +DOCTRINE_PROXY_AUTOGENERATE=false +DOCTRINE_CACHE=array +DOCTRINE_CACHE_NAMESPACE=IXPMANAGERNAMESPACE + +####################################################################################### +# PeeringDB Authentication +# +# PeeringDb's API is used, for example, to pre-populate new customer details. If you +# provide a working PeeringDb username/password then these will be used to get more +# complete information. +# +# IXP_API_PEERING_DB_USERNAME=username +# IXP_API_PEERING_DB_PASSWORD=password + + + +####################################################################################### +# Options for updating RIR Objects - see https://docs.ixpmanager.org/latest/features/rir-objects/ + +# Your RIR password to allow the updating of a RIR object by email: +# IXP_API_RIR_PASSWORD=soopersecret + +# Rather than specifiying the destination address on the command line, you can set it here +# (useful for cronjobs and required for use with artisan schedule:run in >=v5.0) +# IXP_API_RIR_EMAIL_TO=test-dbm@ripe.net + +# Rather than specifiying the from address on the command line, you can set it here +# (useful for cronjobs and required for use with artisan schedule:run in >=v5.0) +# IXP_API_RIR_EMAIL_FROM=ixp@example.com + + + +####################################################################################### +# Utility paths + +# See: https://docs.ixpmanager.org/latest/features/irrdb/ +IXP_IRRDB_BGPQ3_PATH=/usr/local/bin/bgpq3 + +# See: https://docs.ixpmanager.org/latest/features/rpki/ +# IXP_RPKI_RTR1_HOST=192.0.2.11 +# IXP_RPKI_RTR1_PORT=3323 +# IXP_RPKI_RTR2_HOST=192.0.2.12 +# IXP_RPKI_RTR2_PORT=3323 + + +######################################################################################### +### Development Helpers +### + +# Disable HTML5 validation to test PHP code based request validators +# FORMER_LIVE_VALIDATION=false + + +######################################################################################### +### PeeringDB OAuth +### +### https://docs.ixpmanager.org/latest/features/peeringdb-oauth/ +### + +# AUTH_PEERINGDB_ENABLED=true + +# PEERINGDB_OAUTH_CLIENT_ID="xxx" +# PEERINGDB_OAUTH_CLIENT_SECRET="xxx" +# PEERINGDB_OAUTH_REDIRECT="https://www.example.com/auth/login/peeringdb/callback" + +2FA_ENABLED=false + +#IXP_NO_TRANSIT_ASNS_EXCLUDE=174,1299 +#IXP_NO_TRANSIT_ASNS_OVERRIDE=25,45,174 + diff --git a/.env.example b/.env.example new file mode 100644 index 000000000..01a89370c --- /dev/null +++ b/.env.example @@ -0,0 +1,318 @@ +####################################################################################### +# +# IXP Manager V4+ configuration. +# +# + +# Run "artisan key:generate" to set the application key: +APP_KEY= + +# Set this to false in production (but change it to true if you have installation or +# other issues running IXP Manager). +APP_DEBUG=false + +# Web address where IXP Manager is accessed. This is a **required** setting. It is +# currently used for generating all URLs within IXP Manager (action, assets, etc.). +# It is also used / required for sending emails via CLI scripts. +# +APP_URL=https://ixp.example.com + +# HTTPS Only Cookies +# 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. +# +SESSION_SECURE_COOKIE=false + + +# See http://php.net/manual/en/timezones.php for a list of timezones: +APP_TIMEZONE=UTC + +# Laravel log format (storage/log). See config/logging.php and +# https://laravel.com/docs/11.x/logging +# LOG_CHANNEL="daily" + +# info by default, one of: debug, info, notice, warning, error, critical, alert, emergency. +LOG_LEVEL=debug + +# MySQL Connection Details +DB_HOST=127.0.0.1 +DB_DATABASE=ixp +DB_USERNAME=ixp +DB_PASSWORD=password + + +####################################################################################### +### Email Settings. +# +# We use Laravel's mail system - see: https://docs.ixpmanager.org/latest/usage/email/ +# +# The default setting are as follows: +# +# MAIL_MAILER=smtp +# MAIL_HOST=localhost +# MAIL_PORT=25 +# MAIL_ENCRYPTION=false + + + +####################################################################################### +### Identity +# +# Used throughout IXP Manager in various ways. +# + +# Used in various emails, etc. +IDENTITY_SITENAME="IXP Manager" +APP_NAME="IXP Manager" + +# Shown in title bar of web portal. +IDENTITY_TITLENAME="IXP Manager" + +IDENTITY_LEGALNAME="Some City IXP CLG" +IDENTITY_CITY=Cork +IDENTITY_COUNTRY=IE +IDENTITY_ORGNAME="Some City IXP" + +# As well as uses in other places, emails are sent from the following name/email: +IDENTITY_NAME="Some City IXP" +IDENTITY_EMAIL="ixp@example.com" + +IDENTITY_TESTEMAIL="ixp@example.com" + +# Used on some traffic graphs: +IDENTITY_WATERMARK="Some City IXP" + +IDENTITY_SUPPORT_EMAIL="ixp@example.com" +IDENTITY_SUPPORT_PHONE="+353 20 912 2000" +IDENTITY_SUPPORT_HOURS=8x5 + +# IXP Manager will need to send alert emails. This is the recipient email for these alerts. +IDENTITY_ALERTS_EMAIL="ixp@example.com" +IDENTITY_ALERTS_NAME="IXP Manager Alerts" + + +IDENTITY_BILLING_EMAIL="ixp@example.com" +IDENTITY_BILLING_PHONE="+1 111 555 5555" +IDENTITY_BILLING_HOURS=24x7 + +# Web address of your IXP's website. Used in IX-F Export schema, etc. +IDENTITY_CORPORATE_URL=https://www.example.com/ + +# The logo to show on the login page. Should be a URL. +# (the example here works - the leading '//' means the browser should match http/https based on the web page) +IDENTITY_BIGLOGO=https://www.ixpmanager.org/images/logos/ixp-manager.png + + + +######################################################################################### +### Member vs. Customer +### +### IXP Manager is an open source project and typically used in member-owned IXPs. +### As such, the language used mostly is 'member'. To change this to 'customer' just +### uncomment the following lines: +# +# IXP_FE_FRONTEND_CUSTOMER_ONE=customer +# IXP_FE_FRONTEND_CUSTOMER_MANY=customers +# IXP_FE_FRONTEND_CUSTOMER_OWNER="customer's" +# IXP_FE_FRONTEND_CUSTOMER_OWNERS="customers'" + + + +####################################################################################### +### Features +# + +# See: https://docs.ixpmanager.org/latest/features/reseller/ +IXP_RESELLER_ENABLED=false + +# See: https://docs.ixpmanager.org/latest/features/as112/ +IXP_AS112_UI_ACTIVE=false + + +####################################################################################### +### Frontend controllers and controller configuration +# +# Some frontend controllers are disabled by default. This is for a variety of reasons +# including: additional configuration may be required, maintain backwards +# compatibility, etc. + +# Allow customers / admins to upload logos for members. Set to false to enabled. +# See: https://docs.ixpmanager.org/latest/usage/customers/#customer-logos +IXP_FE_FRONTEND_DISABLED_LOGO=false + + +# Send email notifications when a customer's billing details are updated. +# See: https://docs.ixpmanager.org/latest/usage/customers/#notification-of-billing-details-changed +# IXP_FE_CUSTOMER_BILLING_UPDATES_NOTIFY="mail@example.com" + + +# Disable links to the peering matrix if you have not set it up (with sflow): +# IXP_FE_FRONTEND_DISABLED_PEERING_MATRIX=true + +# Enable the UI for route server community-based filtering by uncommenting this line: +# IXP_FE_FRONTEND_DISABLED_RS_FILTERS=false + +####################################################################################### +### Graphing - see https://docs.ixpmanager.org/latest/grapher/introduction + +# Enable the backends you have configured. E.g.: +# GRAPHER_BACKENDS="mrtg|sflow|smokeping" + +# On a new installation, we just use placeholders from the dummy backend: +GRAPHER_BACKENDS="dummy" + +# With the cache enabled, IXP Manager does not have to regenerate / reload / reprocess +# log / rrd / image files if we have cached them and they are less than 5mins old. This +# is enabled by default which is the recommended setting. +GRAPHER_CACHE_ENABLED=true + +################################################################################# +## Grapher - Mrtg - see: https://docs.ixpmanager.org/latest/grapher/mrtg/ +## + +# For backwards compatibility, the default is 'log' but 'rrd' is more modern: +GRAPHER_BACKEND_MRTG_DBTYPE=rrd + +# The defaults for these are '/tmp' to require you to change them to something +# more sensible such as: +# GRAPHER_BACKEND_MRTG_WORKDIR=/srv/mrtg +# GRAPHER_BACKEND_MRTG_LOGDIR=/srv/mrtg + +################################################################################# +## Grapher - sflow - see: https://docs.ixpmanager.org/latest/grapher/sflow/ +## + +# GRAPHER_BACKEND_SFLOW_ENABLED=false +# GRAPHER_BACKEND_SFLOW_ROOT=http://sflow-server.example.com/grapher-sflow + + +################################################################################# +## Grapher - smokeping - see: https://docs.ixpmanager.org/latest/grapher/smokeping/ +## + +# Mark it as enabled (this just affects whether certain UI elements are shown): +# GRAPHER_BACKEND_SMOKEPING_ENABLED=true + +# And set the default location to fetch the Smokeping graphs from: +# GRAPHER_BACKEND_SMOKEPING_URL=http://www.example.com/smokeping + + +################################################################################# +## IX-F Member Export - see: https://docs.ixpmanager.org/latest/features/ixf-export/ + + +# IXP_API_JSONEXPORTSCHEMA_PUBLIC=true + +# Some variables can be excluded as required. +# See: https://docs.ixpmanager.org/latest/features/ixf-export/ +# +# IXP_API_JSONEXPORTSCHEMA_EXCLUDE_SWITCH="model|software" +# IXP_API_JSONEXPORTSCHEMA_EXCLUDE_IXP="name|url" +# IXP_API_JSONEXPORTSCHEMA_EXCLUDE_MEMBER="asnum|name" +# IXP_API_JSONEXPORTSCHEMA_EXCLUDE_INTINFO="mac_addresses|routeserver" + +# Exclude members with certain AS numbers +# IXP_API_JSONEXPORTSCHEMA_EXCLUDE_ASNUM="65001|65002|65003" + +# Exclude members with certain tags +# IXP_API_JSONEXPORTSCHEMA_EXCLUDE_TAGS="tag1|tag2" + +# Exclude documentation ASNs (64496 - 64511, 65536 - 65551) +# IXP_API_JSONEXPORTSCHEMA_EXCLUDE_RFC5398=true + +# Exclude private ASNs (64512 - 65534, 4200000000 - 4294967294) + +# Exclude private ASNs: +# IXP_API_JSONEXPORTSCHEMA_EXCLUDE_RFC6996=true + + +####################################################################################### +### Skinning +# +# See https://ixp-manager.readthedocs.io/en/latest/features/skinning.html +# +# VIEW_SKIN=myskin + + +####################################################################################### +# See config/cache.php +CACHE_DRIVER=array + +####################################################################################### +# Session Lifetimes - standard and remember me. +# +# See https://docs.ixpmanager.org/latest/usage/authentication/ +# +# SESSION_LIFETIME=120 +# AUTH_TOKEN_EXPIRE=43200 + + +####################################################################################### +# PeeringDB Authentication +# +# PeeringDb's API is used, for example, to pre-populate new customer details. If you +# provide a working PeeringDb username/password then these will be used to get more +# complete information. +# +# IXP_API_PEERING_DB_USERNAME=username +# IXP_API_PEERING_DB_PASSWORD=password + + + +####################################################################################### +# Options for updating RIR Objects - see https://docs.ixpmanager.org/latest/features/rir-objects/ + +# Your RIR password to allow the updating of a RIR object by email: +# IXP_API_RIR_PASSWORD=soopersecret + +# Rather than specifiying the destination address on the command line, you can set it here +# (useful for cronjobs and required for use with artisan schedule:run in >=v5.0) +# IXP_API_RIR_EMAIL_TO="test-dbm@ripe.net" + +# Rather than specifiying the from address on the command line, you can set it here +# (useful for cronjobs and required for use with artisan schedule:run in >=v5.0) +# IXP_API_RIR_EMAIL_FROM="ixp@example.com" + + + +####################################################################################### +# Utility paths + +# See: https://docs.ixpmanager.org/latest/features/irrdb/ +IXP_IRRDB_BGPQ3_PATH=/usr/bin/bgpq3 + +# See: https://docs.ixpmanager.org/latest/features/rpki/ +# IXP_RPKI_RTR1_HOST=192.0.2.11 +# IXP_RPKI_RTR1_PORT=3323 +# IXP_RPKI_RTR2_HOST=192.0.2.12 +# IXP_RPKI_RTR2_PORT=3323 + + +######################################################################################### +### Development Helpers +### + +# Disable HTML5 validation to test PHP code based request validators +# FORMER_LIVE_VALIDATION=false + + +######################################################################################### +### PeeringDB OAuth +### +### https://docs.ixpmanager.org/latest/features/peeringdb-oauth/ +### + +# AUTH_PEERINGDB_ENABLED=true + +# PEERINGDB_OAUTH_CLIENT_ID="xxx" +# PEERINGDB_OAUTH_CLIENT_SECRET="xxx" +# PEERINGDB_OAUTH_REDIRECT="https://www.example.com/auth/login/peeringdb/callback" + + +######################################################################################### +### See: https://docs.ixpmanager.org/latest/features/routers/#filtering-known-transit-networks +# IXP_NO_TRANSIT_ASNS_EXCLUDE=65501,65502 +# IXP_NO_TRANSIT_ASNS_OVERRIDE=65501,65502,65503 + +# Full slash 'description' with /slashes\ and "quotes" diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..7d06fdcc1 --- /dev/null +++ b/.github/CODE_OF_CONDUCT.md @@ -0,0 +1,5 @@ +# Code of Conduct + +IXP Manager is an *[INEX](https://www.inex.ie/) for Good* project. The [INEX Code of Conduct](https://www.inex.ie/about-us/code-of-conduct/) extends to our IXP Manager community also - all our users, contributors and supportors. + +If you believe someone has been acting contrary to this approach or if you have suggestions on making IXP Manager even more inclusive, then, do please get in contact and let us know use the details on the referenced page. diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 000000000..dd7602b3c --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,45 @@ +# How to contribute + +Third-party patches are welcomed for adding functionality, fixing bugs, and just correcting typos on IXP Manager. + +We want to keep it as easy as possible to contribute changes, but there are a few guidelines that we +need contributors to follow so that we can accept them. + + + +## Getting Started + +* Make sure you have a [GitHub account](https://github.com/) +* Submit a ticket for your issue, assuming one does not already exist. + * Clearly describe the issue, including steps to reproduce when it is a bug. + * Make sure you fill in the earliest version you know has the issue. +* Fork the repository on GitHub + +## Making Changes + +* Create a topic branch from where you want to base your work. + * This is usually the master branch. + * Only target release branches if you are certain your fix must be on that + branch. + * To quickly create a topic branch based on master; `git checkout -b + fix/master/my_contribution master`. Please avoid working directly on the + `master` branch. +* Make commits of logical units. +* Check for unnecessary whitespace with `git diff --check` before committing. +* Make sure your commit messages reference issue numbers where appropriate. + + +## Submitting Changes + +* Sign the [Contributor License Agreement](https://docs.ixpmanager.org/latest/dev/cla/) (`gpg --clearsign inex-cla.txt`) and email it to ''operations (at) inex (dot) ie''. +* Push your changes to a topic branch in your fork of the repository. +* Submit a pull request to the repository in the inex organisation. +* Submit a parallel pull request [with new/updated documentation](https://docs.ixpmanager.org/latest/dev/docs/) + +# Additional Resources + +* [Contributor License Agreement](https://docs.ixpmanager.org/latest/dev/cla/) +* [General GitHub documentation](http://help.github.com/) +* [GitHub pull request documentation](http://help.github.com/send-pull-requests/) +* [IXP Manager Mailing List](https://www.inex.ie/mailman/listinfo/ixpmanager) + diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..a9474abc5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,100 @@ +--- +name: Bug report +about: Create a detailed report to help us improve + +--- + + + +##### ISSUE TYPE + +Bug Report + +##### OS + + + +##### VERSION + + + +``` + +``` + +##### ENVIRONMENT + + + +``` + +``` + + + +##### CONFIGURATION + + + +``` + +``` + + + +##### SUMMARY + + +##### STEPS TO REPRODUCE + + +##### EXPECTED RESULTS + + +##### ACTUAL RESULTS + + +##### IMPORTANCE + + +##### RELEVANT LOGS + + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..67b1e2a17 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,24 @@ +--- +name: Feature request +about: Suggest an idea for this project + +--- + +The ideal feature request comes with code via a pull request - after you have discussed the idea and approach with the developers via the mailing lists. This (or commercial support) is the most probable way to get a feature into IXP Manager! Feel free to file feature requests as an issue and tag it as such. + +When evaluating pull requests or feature requests that would be developed by by the IXP Manager team, the question we ask ourselves is *would 80% of small to medium sized IXPs benefit from this?* If the answer is a clear yes then it's a valid and appropriate feature request that should be added to the list. If the answer is less clear or no, then do not assume it won't be accepted or developed - open a feature request and see what the community thinks about it. + +Those feature requests that we are unlikely to implement through the open source program or those that do not align with the core purpose of IXP Manager may be closed or moved to [a feature request file](https://github.com/inex/IXP-Manager/blob/master/IDEAS) so as not to clog up the issue tracker. + + +**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/other-request.md b/.github/ISSUE_TEMPLATE/other-request.md new file mode 100644 index 000000000..1dbb8065e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/other-request.md @@ -0,0 +1,20 @@ +--- +name: Other request +about: Chose this option if you want to open a non-bug / non-feature issue + +--- + +**NB: Do not create an issue for requests for help.** + + diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 000000000..a0f099423 --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1,31 @@ +# Security Policy + +## Secure Application Development Policy + +Please see: [https://docs.ixpmanager.org/dev/policy/](https://docs.ixpmanager.org/dev/policy/). + +## Reporting a Vulnerability + +Thank you in advance for looking to responsibly report security issues. + +Please contact us by email at [security@ixpmanager.org](mailto:security@ixpmanager.org) to report security issues or discuss security concerns. All security vulnerabilities will be promptly addressed. + + +### Confidentiality and Acknowledgements + +We understand that some organisations do not wish to disclose their use of specific software for security reasons. If you do not want to be named or acknowledged in the release notes where the security issue is addressed, please let us know, and we will ensure your anonymity. + +Likewise, we are delighted to acknowledge and thank anyone who responsibly reports security issues to us. We usually do this in the release notes and related announcements. Please do let us know the appropriate attribution when you contact us. + + +## Supported Versions + + +| Version | Supported | Details / Until | +|---------|--------------------|-----------------------------------------------------------------------------| +| 7.x.y | :white_check_mark: | Current release, full support until min. 6 months after a future v7 release | +| 6.x.y | :white_check_mark: | March 1st 2026, security issues only | +| 5.x.y | :x: | | +| 4.x.y | :x: | | +| < 4.0 | :x: | | + diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..55ba1421d --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,17 @@ +*PR template - remove this line and edit below* + +[BF] Summary of fix - fixes [inex|islandbridgenetworks]/IXP-Manager#x + +[NF] New feature summary - closes [inex|islandbridgenetworks]/IXP-Manager#x + +*Longer description* + + +In addition to the above, I have: + + - [ ] ensure unit tests all run without error + - [ ] ran pslam and corrected any static analysis issues + - [ ] ensured all relevant template output is escaped to avoid XSS attached with `ee( $data ) ?>` or equivalent. + - [ ] ensured appropriate checks against user privilege / resources accessed + - [ ] API calls (particular for add/edit/delete/toggle) are not implemented with GET and use CSRF tokens to avoid CSRF attacks + diff --git a/.github/workflows/ci-dusk.yml b/.github/workflows/ci-dusk.yml new file mode 100644 index 000000000..91471a98d --- /dev/null +++ b/.github/workflows/ci-dusk.yml @@ -0,0 +1,44 @@ +name: CI - Dusk / Browser + +on: workflow_dispatch + +jobs: + laravel-tests: + + runs-on: ubuntu-latest + + steps: + - uses: shivammathur/setup-php@v2 + with: + php-version: '8.4' + env: + update: true + - uses: actions/checkout@v2 + - uses: shogo82148/actions-setup-mysql@v1 + with: + mysql-version: '8.0' + - run: mysql -uroot -h127.0.0.1 -e 'CREATE DATABASE myapp_test' + - name: Copy .env + run: php -r "file_exists('.env') || copy('.env.ci', '.env');" + - name: Install bgpq3 + run: sudo apt-get install bgpq3 + - name: Install Dependencies + run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist + - name: Set up Dusk chrome-driver + run: | + php artisan dusk:install + php artisan dusk:chrome-driver 138 + - name: Directory Permissions + run: chmod -R 777 storage bootstrap/cache + - name: Create Database + run: | + cat data/ci/ci_test_db.sql | mysql --default-character-set=utf8mb4 -h 127.0.0.1 -u root myapp_test + - name: Set up Dusk and php serve + run: | + php ./artisan serve &>php-built-in.log & + ls -la vendor/laravel/dusk/bin/* + ./vendor/laravel/dusk/bin/chromedriver-linux --port=9515 & + - name: Execute tests (Unit and Feature tests) via PHPUnit + run: vendor/bin/phpunit --testsuite 'Dusk / Browser Test Suite' + + diff --git a/.github/workflows/ci-ex-dusk.yml b/.github/workflows/ci-ex-dusk.yml new file mode 100644 index 000000000..f81007505 --- /dev/null +++ b/.github/workflows/ci-ex-dusk.yml @@ -0,0 +1,41 @@ +name: CI Tests (ex Dusk) + +on: [ push, pull_request ] + +jobs: + ci-ex-dusk: + + runs-on: ubuntu-24.04 + + steps: + - uses: shivammathur/setup-php@v2 + with: + php-version: '8.4' + env: + update: true + - uses: actions/checkout@v2 + - uses: shogo82148/actions-setup-mysql@v1 + with: + mysql-version: '8.0' + - name: Print PHP version + run: echo ${{ steps.setup-php.outputs.php-version }} + - run: mysql -u root -h 127.0.0.1 -e 'CREATE DATABASE ixp_ci; CREATE USER IF NOT EXISTS ixp_ci@localhost IDENTIFIED BY "ixp_ci"; GRANT ALL ON ixp_ci.* TO ixp_ci@localhost;' + - name: Copy .env + run: php -r "file_exists('.env') || copy('.env.ci', '.env');" + - name: Install bgpq3 + run: sudo apt-get install bgpq3 + - name: Install Dependencies + run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist + - name: Directory Permissions + run: chmod -R 777 storage bootstrap/cache + - name: Create Database + run: | + cat data/ci/ci_test_db.sql | mysql --default-character-set=utf8mb4 -h localhost -u root ixp_ci + - name: Set up php server + run: | + php ./artisan serve &>php-built-in.log & + - name: Execute tests (Unit and Feature tests) via PHPUnit + run: vendor/bin/phpunit --testsuite 'Docstore Test Suite,IXP Manager Test Suite' + - name: Static code analysis + run: vendor/bin/psalm + diff --git a/.gitignore b/.gitignore index c21912d97..a122cf699 100644 --- a/.gitignore +++ b/.gitignore @@ -1,34 +1,138 @@ -.project -.buildpath -.settings/ -ixp-manager.sublime-workspace -bin/mailing-list-sync.sh -bin/fixtures.php -public/.htaccess -application/configs/application.ini -application/configs/pp-application.ini* -data/mrtg -var/cache/* -var/log/* -var/meetings/* -var/session/* -var/templates_c/* -library/Bootbox -library/Doctrine -library/Zend -library/ZFDebug -library/Minify -library/smarty -library/Smarty -library/Bootstrap-Zend-Framework -library/bootstrap-wysihtml5 -library/Throbber.js -library/wysihtml5 -library/jwysiwyg -library/OSS -library/OSS-Framework.git -library/wiki/ + +/.env +/.env.barryo +/.env*.local +/.env.by-vagrant.* +/.env.sage +/.project +/.buildpath +/.settings/ +/.phpunit.cache/ +/vendor/ +/public/.htaccess +/public/logos/* +/public/build +/public/favicon.ico + +/.junie + +.directory +.DS_Store +.vagrant/ *~ -public/images/.directory -public/images/joomla-admin/.directory -public/images/joomla-admin/menu/.directory +*.rrd + +.cache/ +.composer/ +.config/ +.ixp-manager-installer-settingsrc +composer.phar +/tools/installers/Vagrantfile + +/.phpstorm.meta.php +/_ide_helper.php + +/storage/.local +/storage/grapher/ +/storage/files/* +/storage/tmp/* + +/tests/Browser/ExampleTest.php + +/tools/perl-lib/IXPManager/MYMETA.yml +/tools/perl-lib/IXPManager/Makefile +/tools/perl-lib/IXPManager/blib/ +/tools/perl-lib/IXPManager/pm_to_blib +/tools/perl-lib/IXPManager/ixpmanager.conf + +/config/contact_group.php +/config/custom.php +/config/grapher_cli.php +/config/grapher_trunks.php +/config/grapher_smokeping_overrides.php +/config/ixp_tools.php +/config/lookingglass.php +/config/mailinglists.php +/config/trustedproxy.php + +ubuntu-*-cloudimg-console.log + + +# Let's not accidently add SQL dumps to Git +/*.sql.bz2 +/*.sql.gz +/*.sql + +/node_modules/ +npm-debug.log +yarn-error.log + +/tools/docker/containers/mysql/docker.sql + +/.phpunit.result.cache + +/app/Console/Commands/Redcentric.php + +.idea/httpRequests +.phpactor.json + + +/.idea/workspace.xml +/.idea/tasks.xml +/.idea/codeStyles/ +/.idea/dataSources.local.xml +/.idea/dataSources.xml +/.idea/dataSources +/.idea/inspectionProfiles +/.idea/ixpv.iml +/.idea/php.xml +/.idea/symfony2.xml +/.idea/composerJson.xml +/.idea/misc.xml + + +# ######################################################################## +# JetBrains.gitignore +# +# https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore + +# Covers JetBrains IDEs: IntelliJ, GoLand, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Editor-based HTTP Client +.idea/httpRequests +http-client.private.env.json + + + + + + + + + + + + diff --git a/.idea/IXP-Manager.iml b/.idea/IXP-Manager.iml new file mode 100644 index 000000000..c921bcb2a --- /dev/null +++ b/.idea/IXP-Manager.iml @@ -0,0 +1,167 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/blade.xml b/.idea/blade.xml new file mode 100644 index 000000000..1572b3317 --- /dev/null +++ b/.idea/blade.xml @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/codeStyleSettings.xml b/.idea/codeStyleSettings.xml new file mode 100644 index 000000000..6dce3cafc --- /dev/null +++ b/.idea/codeStyleSettings.xml @@ -0,0 +1,38 @@ + + + + + + \ No newline at end of file diff --git a/.idea/codeception.xml b/.idea/codeception.xml new file mode 100644 index 000000000..330f2dd32 --- /dev/null +++ b/.idea/codeception.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/.idea/commandlinetools/Laravel_06_08_2020__00_27.xml b/.idea/commandlinetools/Laravel_06_08_2020__00_27.xml new file mode 100644 index 000000000..29d7cc7be --- /dev/null +++ b/.idea/commandlinetools/Laravel_06_08_2020__00_27.xml @@ -0,0 +1,5370 @@ + + + + + clear-compiled +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + down +
Options:
--messageThe message for the maintenance mode
--retryThe number of seconds after which the request may be retried
--allowIP or networks allowed to access the application while in maintenance mode
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + + + + +
+ + dump-server +
Options:
--formatThe output format (cli,html).
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + + +
+ + dusk +
Options:
--without-ttyDisable output to TTY
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + + +
+ + env +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + help + help command displays help for a given command:

php /Users/barryo/dev/ixp-inex/artisan help list

You can also output the help in other formats by using the --format option:

php /Users/barryo/dev/ixp-inex/artisan help --format=xml list

To display the list of available commands, please use the list command.

Options:
--formatThe output format (txt, xml, json, or md)
--rawTo output raw command help
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ command_name[=null] + + + + + + + + + + + + +
+ + horizon +
Options:
--environmentThe environment name
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + + +
+ + inspire + Options: --help(-h)Display this help message --quiet(-q)Do not output any message --verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug --version(-V)Display this application version --ansiForce ANSI output --no-ansiDisable ANSI output --no-interaction(-n)Do not ask any interactive question --envThe environment the command should run under
]]>
+ + + + + + + + + + +
+ + list + list command lists all commands:

php /Users/barryo/dev/ixp-inex/artisan list

You can also display the commands for a specific namespace:

php /Users/barryo/dev/ixp-inex/artisan list test

You can also output the information in other formats by using the --format option:

php /Users/barryo/dev/ixp-inex/artisan list --format=xml

It's also possible to get raw list of commands (useful for embedding command runner):

php /Users/barryo/dev/ixp-inex/artisan list --raw

Options:
--rawTo output raw command list
--formatThe output format (txt, xml, json, or md)

]]>
+ namespace[=null] + + + + +
+ + migrate +
Options:
--databaseThe database connection to use
--forceForce the operation to run when in production
--pathThe path(s) to the migrations files to be executed
--realpathIndicate any provided migration file paths are pre-resolved absolute paths
--pretendDump the SQL queries that would be run
--seedIndicates if the seed task should be re-run
--stepForce the migrations to be run so they can be rolled back individually
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + + + + + + + + +
+ + optimize +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + preset +
Options:
--optionPass an option to the preset command
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ type + + + + + + + + + + + +
+ + serve +
Options:
--hostThe host address to serve the application on
--portThe port to serve the application on
--triesThe max number of ports to attempt to serve from
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + + + + +
+ + tinker +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ include[=null] + + + + + + + + + + +
+ + up +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + audit:port-speeds +
Options:
--cronRunning as a cronjob, only output if mismatched ports found
--ignore-pidsComma separated physical interface database IDs to ignore
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + + + +
+ + auth:clear-resets +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ name[=null] + + + + + + + + + + +
+ + cache:clear +
Options:
--tagsThe cache tags you would like to clear
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ store[=null] + + + + + + + + + + + +
+ + cache:forget +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ key store[=null] + + + + + + + + + + +
+ + cache:table +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + config:cache +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + config:clear +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + contact:export-group +
Options:
--typeContact group type (e.g. ROLE)
--nameContact group name (e.g. beer)
--formatOutput format - one of json (default) or csv
--cidOptionally limit results to given customer id
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + + + + + +
+ + countries:migration +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + db:seed +
Options:
--classThe class name of the root seeder
--databaseThe database connection to seed
--forceForce the operation to run when in production
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + + + + +
+ + db:wipe +
Options:
--databaseThe database connection to use
--drop-viewsDrop all tables and views
--drop-typesDrop all tables and types (Postgres only)
--forceForce the operation to run when in production
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + + + + + +
+ + doctrine:clear:metadata:cache +
Options:
--flushIf defined, cache entries will be flushed instead of deleted/invalidated.
--emClear cache for a specific entity manager
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + + + +
+ + doctrine:clear:query:cache +
Options:
--flushIf defined, cache entries will be flushed instead of deleted/invalidated.
--emClear cache for a specific entity manager
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + + + +
+ + doctrine:clear:result:cache +
Options:
--flushIf defined, cache entries will be flushed instead of deleted/invalidated.
--emClear cache for a specific entity manager
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + + + +
+ + doctrine:config:convert +
Options:
--dest-pathWhere the generated configuration should be placed
--source-fileWhere the source configuration file is located.
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ author + + + + + + + + + + + + +
+ + doctrine:convert:mapping +
Options:
--emGenerate getter and setter for a specific entity manager.
--filterA string pattern used to match entities that should be processed.
--forceForce to overwrite existing mapping files.
--from-databaseWhether or not to convert mapping information from existing database.
--extendDefines a base class to be extended by generated entity classes.
--num-spacesDefines the number of indentation spaces
--namespaceDefines a namespace for the generated entity classes, if converted from database.
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ to-type dest-path + + + + + + + + + + + + + + + + + +
+ + doctrine:dump:sqlite +
Options:
--connection
--em
--dumpChoose the path for your dump file
--no-seedingDisable seeding in the dump process
--seederChoose the seeder class
--binary
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + + + + +
+ + doctrine:ensure:production +
Options:
--with-dbFlag to also inspect database connection existence.
--emEnsure production settings for a specific entity manager
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + + + +
+ + doctrine:generate:entities +
Options:
--filterA string pattern used to match entities that should be processed.
--emGenerate getter and setter for a specific entity manager.
--generate-annotationsFlag to define if generator should generate annotation metadata on entities.
--generate-methodsFlag to define if generator should generate stub methods on entities.
--regenerate-entitiesFlag to define if generator should regenerate entity if it exists.
--update-entitiesFlag to define if generator should only update entity if it exists.
--extendDefines a base class to be extended by generated entity classes.
--num-spacesDefines the number of indentation spaces.
--no-backupFlag to define if generator should avoid backuping existing entity file if it exists
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ dest-path[=null] + + + + + + + + + + + + + + + + + + + +
+ + doctrine:generate:proxies +
Options:
--filterA string pattern used to match entities that should be processed.
--emGenerate proxies for a specific entity manager
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ dest-path[=null] + + + + + + + + + + + + +
+ + doctrine:generate:repositories +
Options:
--filterA string pattern used to match entities that should be processed.
--emGenerate proxies for a specific entity manager
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ dest-path[=null] + + + + + + + + + + + + +
+ + doctrine:info +
Options:
--emInfo for a specific entity manager
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + + +
+ + doctrine:mapping:import +
Options:
--emInfo for a specific entity manager
--filterA string pattern used to match entities that should be mapped
--forceForce to overwrite existing mapping files
--namespaceNamespace to use
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ mapping-type[=null] dest-path[=null] + + + + + + + + + + + + + + +
+ + doctrine:schema:create +
Options:
--sqlDumps the generated SQL statements to the screen (does not execute them)
--emCreate schema for a specific entity manager
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + + + +
+ + doctrine:schema:drop +
Options:
--sqlInstead of trying to apply generated SQLs into EntityManager Storage Connection, output them.
--forceDon't ask for the deletion of the database, but force the operation to run.
--fullInstead of using the Class Metadata to detect the database table schema, drop ALL assets that the database contains.
--emDrop schema for a specific entity manager
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + + + + + +
+ + doctrine:schema:update +
Options:
--cleanIf defined, all assets of the database which are not relevant to the current metadata will be dropped.
--sqlDumps the generated SQL statements to the screen (does not execute them)
--forceCauses the generated SQL statements to be physically executed against your database
--emUpdate schema for a specific entity manager
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + + + + + +
+ + doctrine:schema:validate +
Options:
--skip-mappingSkip the mapping validation check
--skip-syncSkip checking if the mapping is in sync with the database
--emValidate schema for a specific entity manager
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + + + + +
+ + dusk:chrome-driver +
Options:
--allInstall a ChromeDriver binary for every OS
--proxyThe proxy to download the binary through (example: "tcp://127.0.0.1:9000")
--ssl-no-verifyBypass SSL certificate verification when installing through a proxy
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ version[=null] + + + + + + + + + + + + + +
+ + dusk:component +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ name + + + + + + + + + + +
+ + dusk:fails +
Options:
--without-ttyDisable output to TTY
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + + +
+ + dusk:install +
Options:
--proxyThe proxy to download the binary through (example: "tcp://127.0.0.1:9000")
--ssl-no-verifyBypass SSL certificate verification when installing through a proxy
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + + + +
+ + dusk:make +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ name + + + + + + + + + + +
+ + dusk:page +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ name + + + + + + + + + + +
+ + dusk:update +
Options:
--detectDetect the installed Chrome/Chromium version, optionally in a custom path
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ version[=null] + + + + + + + + + + + +
+ + event:cache +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + event:clear +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + event:generate +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + event:list +
Options:
--eventFilter the events by name
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + + +
+ + grapher:backend:mrtg:upgrade +
Options:
--logdir(-L)MRTG log/rrd directory
--ixp(-X)Show upgrade commands for the IXP
--infrastructures(-I)Show upgrade commands for infrastructures
--switches(-S)Show upgrade commands for switches
--trunks(-T)Show upgrade commands for trunks
--memberdirs(-M)Show upgrade commands for member directories
--physints(-P)Show upgrade commands for member physical interfaces
--memberlags(-Q)Show upgrade commands for member LAG interfaces
--customeragg(-C)Show upgrade commands for member aggregate graphs
--corebundles(-B)Show upgrade commands for core bundle graphs
--agg-nameName of aggregate graphs for IXP and infrastructure migration
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ operation + + + + + + + + + + + + + + + + + + + + + +
+ + grapher:email-port-utilisations +
Options:
--threshold(-T)Min percentage usage to include in report (default 80)
--backend(-B)Which graphing backend to use
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ email + + + + + + + + + + + + +
+ + grapher:email-ports-with-counts + 0

Options:
--errorsPorts with an error count (default)
--discardsPorts with a discard count
--backend(-B)Which graphing backend to use
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ email + + + + + + + + + + + + + +
+ + grapher:email-traffic-deltas +
Options:
--stddevMultiple of the stddev to report on (default 1.5)
--backend(-B)Which graphing backend to use
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ email + + + + + + + + + + + + +
+ + grapher:generate-configuration +
Options:
--backend(-B)Which graphing backend to use
--output(-O)Save configuration to specified file (default: stdout)
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + + + +
+ + grapher:upload-pi-stats-to-db +
Options:
--backend(-B)Which graphing backend to use
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + + +
+ + grapher:upload-stats-to-db +
Options:
--backend(-B)Which graphing backend to use
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + + +
+ + helpdesk:update-organisations +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + horizon:assets +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + horizon:continue +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + horizon:install +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + horizon:list +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + horizon:pause +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + horizon:purge +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + horizon:snapshot +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + horizon:status +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + horizon:supervisor +
Options:
--balanceThe balancing strategy the supervisor should apply
--delayAmount of time to delay failed jobs
--forceForce the worker to run even in maintenance mode
--max-processesThe maximum number of total workers to start
--min-processesThe minimum number of workers to assign per queue
--memoryThe memory limit in megabytes
--niceThe process priority
--pausedStart the supervisor in a paused state
--queueThe names of the queues to work
--sleepNumber of seconds to sleep when no job is available
--timeoutThe number of seconds a child process can run
--triesNumber of times to attempt a job before logging it failed
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ name connection + + + + + + + + + + + + + + + + + + + + + + +
+ + horizon:supervisors +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + horizon:terminate +
Options:
--waitWait for all workers to terminate
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + + +
+ + horizon:timeout +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ environment[=null] + + + + + + + + + + +
+ + horizon:work +
Options:
--delayAmount of time to delay failed jobs
--daemonRun the worker in daemon mode (Deprecated)
--forceForce the worker to run even in maintenance mode
--memoryThe memory limit in megabytes
--onceOnly process the next job on the queue
--stop-when-emptyStop when the queue is empty
--queueThe names of the queues to work
--sleepNumber of seconds to sleep when no job is available
--supervisorThe name of the supervisor the worker belongs to
--timeoutThe number of seconds a child process can run
--triesNumber of times to attempt a job before logging it failed
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ connection[=null] + + + + + + + + + + + + + + + + + + + + + +
+ + ide-helper:eloquent +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + ide-helper:generate +
Options:
--format(-F)The format for the IDE Helper
--write_mixins(-W)Write mixins to Laravel Model?
--helpers(-H)Include the helper files
--memory(-M)Use sqlite memory driver
--sublime(-S)DEPRECATED: Use different style for SublimeText CodeIntel
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ filename[=null] + + + + + + + + + + + + + + + +
+ + ide-helper:meta +
Options:
--filename(-F)The path to the meta file
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + + +
+ + ide-helper:models +
Options:
--filename(-F)The path to the helper file
--dir(-D)The model dir
--write(-W)Write to Model file
--nowrite(-N)Don't write to Model file
--reset(-R)Remove the original phpdocs instead of appending
--smart-reset(-r)Refresh the properties/methods list, but keep the text
--phpstorm-noinspections(-p)Add PhpFullyQualifiedNameUsageInspection and PhpUnnecessaryFullyQualifiedNameInspection PHPStorm noinspection tags
--ignore(-I)Which models to ignore
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ model[=null] + + + + + + + + + + + + + + + + + + +
+ + irrdb:update-asn-db +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ customer[=null] + + + + + + + + + + +
+ + irrdb:update-prefix-db +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ customer[=null] + + + + + + + + + + +
+ + ixp-manager:update-in-manrs +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + ixp-manager:update-in-peeringdb +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + key:generate +
Options:
--showDisplay the key instead of modifying files
--forceForce the operation to run when in production
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + + + +
+ + l2addresses:populate +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + mailing-list:get-subscribers +
Options:
--formatOutput format - one of text (default) or json
--unsubscribedProvide a list of user emails that are unsubscribed rather than subscribed
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ list + + + + + + + + + + + + +
+ + mailing-list:init +
Options:
--formatResponse format - either text (default) or json)
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ list + + + + + + + + + + + +
+ + mailing-list:sync-script +
Options:
--shGenerate for shell commands rather than API
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + + +
+ + make:channel +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ name + + + + + + + + + + +
+ + make:command +
Options:
--commandThe terminal command that should be assigned
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ name + + + + + + + + + + + +
+ + make:controller +
Options:
--apiExclude the create and edit methods from the controller.
--forceCreate the class even if the controller already exists
--invokable(-i)Generate a single method, invokable controller class.
--model(-m)Generate a resource controller for the given model.
--parent(-p)Generate a nested resource controller class.
--resource(-r)Generate a resource controller class.
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ name + + + + + + + + + + + + + + + + +
+ + make:event +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ name + + + + + + + + + + +
+ + make:exception +
Options:
--renderCreate the exception with an empty render method
--reportCreate the exception with an empty report method
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ name + + + + + + + + + + + + +
+ + make:factory +
Options:
--model(-m)The name of the model
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ name + + + + + + + + + + + +
+ + make:job +
Options:
--syncIndicates that job should be synchronous
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ name + + + + + + + + + + + +
+ + make:listener +
Options:
--event(-e)The event class being listened for
--queuedIndicates the event listener should be queued
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ name + + + + + + + + + + + + +
+ + make:mail +
Options:
--force(-f)Create the class even if the mailable already exists
--markdown(-m)Create a new Markdown template for the mailable
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ name + + + + + + + + + + + + +
+ + make:middleware +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ name + + + + + + + + + + +
+ + make:migration +
Options:
--createThe table to be created
--tableThe table to migrate
--pathThe location where the migration file should be created
--realpathIndicate any provided migration file paths are pre-resolved absolute paths
--fullpathOutput the full path of the migration
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ name + + + + + + + + + + + + + + + +
+ + make:model +
Options:
--all(-a)Generate a migration, seeder, factory, and resource controller for the model
--controller(-c)Create a new controller for the model
--factory(-f)Create a new factory for the model
--forceCreate the class even if the model already exists
--migration(-m)Create a new migration file for the model
--seed(-s)Create a new seeder file for the model
--pivot(-p)Indicates if the generated model should be a custom intermediate table model
--resource(-r)Indicates if the generated controller should be a resource controller
--apiIndicates if the generated controller should be an API controller
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ name + + + + + + + + + + + + + + + + + + + +
+ + make:notification +
Options:
--force(-f)Create the class even if the notification already exists
--markdown(-m)Create a new Markdown template for the notification
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ name + + + + + + + + + + + + +
+ + make:observer +
Options:
--model(-m)The model that the observer applies to.
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ name + + + + + + + + + + + +
+ + make:policy +
Options:
--model(-m)The model that the policy applies to
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ name + + + + + + + + + + + +
+ + make:provider +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ name + + + + + + + + + + +
+ + make:request +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ name + + + + + + + + + + +
+ + make:resource +
Options:
--collection(-c)Create a resource collection
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ name + + + + + + + + + + + +
+ + make:rule +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ name + + + + + + + + + + +
+ + make:seeder +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ name + + + + + + + + + + +
+ + make:test +
Options:
--unitCreate a unit test
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ name + + + + + + + + + + + +
+ + migrate:fresh +
Options:
--databaseThe database connection to use
--drop-viewsDrop all tables and views
--drop-typesDrop all tables and types (Postgres only)
--forceForce the operation to run when in production
--pathThe path(s) to the migrations files to be executed
--realpathIndicate any provided migration file paths are pre-resolved absolute paths
--seedIndicates if the seed task should be re-run
--seederThe class name of the root seeder
--stepForce the migrations to be run so they can be rolled back individually
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + + + + + + + + + + +
+ + migrate:install +
Options:
--databaseThe database connection to use
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + + +
+ + migrate:refresh +
Options:
--databaseThe database connection to use
--forceForce the operation to run when in production
--pathThe path(s) to the migrations files to be executed
--realpathIndicate any provided migration file paths are pre-resolved absolute paths
--seedIndicates if the seed task should be re-run
--seederThe class name of the root seeder
--stepThe number of migrations to be reverted & re-run
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + + + + + + + + +
+ + migrate:reset +
Options:
--databaseThe database connection to use
--forceForce the operation to run when in production
--pathThe path(s) to the migrations files to be executed
--realpathIndicate any provided migration file paths are pre-resolved absolute paths
--pretendDump the SQL queries that would be run
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + + + + + + +
+ + migrate:rollback +
Options:
--databaseThe database connection to use
--forceForce the operation to run when in production
--pathThe path(s) to the migrations files to be executed
--realpathIndicate any provided migration file paths are pre-resolved absolute paths
--pretendDump the SQL queries that would be run
--stepThe number of migrations to be reverted
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + + + + + + + +
+ + migrate:status +
Options:
--databaseThe database connection to use
--pathThe path(s) to the migrations files to use
--realpathIndicate any provided migration file paths are pre-resolved absolute paths
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + + + + +
+ + notifications:table +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + optimize:clear +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + package:discover +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + queue:failed +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + queue:failed-table +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + queue:flush +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + queue:forget +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ id + + + + + + + + + + +
+ + queue:listen +
Options:
--delayThe number of seconds to delay failed jobs
--forceForce the worker to run even in maintenance mode
--memoryThe memory limit in megabytes
--queueThe queue to listen on
--sleepNumber of seconds to sleep when no job is available
--timeoutThe number of seconds a child process can run
--triesNumber of times to attempt a job before logging it failed
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ connection[=null] + + + + + + + + + + + + + + + + + +
+ + queue:restart +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + queue:retry +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ id + + + + + + + + + + +
+ + queue:table +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + queue:work +
Options:
--queueThe names of the queues to work
--daemonRun the worker in daemon mode (Deprecated)
--onceOnly process the next job on the queue
--stop-when-emptyStop when the queue is empty
--delayThe number of seconds to delay failed jobs
--forceForce the worker to run even in maintenance mode
--memoryThe memory limit in megabytes
--sleepNumber of seconds to sleep when no job is available
--timeoutThe number of seconds a child process can run
--triesNumber of times to attempt a job before logging it failed
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ connection[=null] + + + + + + + + + + + + + + + + + + + + +
+ + rir:generate-object +
Options:
--send-emailRather than printing to screen, sends and email for updating a RIR automatically
--forceSend email even if it matches the cached version
--toThe email address to send the object to (if not specified then uses IXP_API_RIR_EMAIL_TO)
--fromThe email address from which the email is sent (if not specified, tries IXP_API_RIR_EMAIL_FROM and then defaults to IDENTITY_EMAIL)
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ object + + + + + + + + + + + + + + +
+ + route:cache +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + route:clear +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + route:list +
Options:
--columnsColumns to include in the route table
--compact(-c)Only show method, URI and action columns
--jsonOutput the route list as JSON
--methodFilter the routes by method
--nameFilter the routes by name
--pathFilter the routes by path
--reverse(-r)Reverse the ordering of the routes
--sortThe column (domain, method, uri, name, action, middleware) to sort by
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + + + + + + + + + +
+ + router:filtered-prefixes +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ customer[=null] + + + + + + + + + + +
+ + router:generate-configuration +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ handle + + + + + + + + + + +
+ + schedule:finish +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ id code[=null] + + + + + + + + + + +
+ + schedule:run +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + session:table +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + storage:link +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + switch:snmp-poll +
Options:
--noflushIf specified no modification will be made to the database
--logOutput detailed polling information to the log
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ switch[=null] + + + + + + + + + + + + +
+ + telescope:clear +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + telescope:install +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + telescope:prune +
Options:
--hoursThe number of hours to retain Telescope data
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + + +
+ + telescope:publish +
Options:
--forceOverwrite any existing files
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + + +
+ + update:customer2users +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + update:promote-custusers +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + update:remove-custadmins +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + update:reset-mysql-views +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + utils:expunge-logs +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + utils:json-schema-post +
Options:
--verSchema version to export (defualt: v1.0)
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ url + + + + + + + + + + + +
+ + utils:oui-update +
Options:
--refresh
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ file[=null] + + + + + + + + + + +
+ + vendor:publish +
Options:
--forceOverwrite any existing files
--allPublish assets for all service providers without prompt
--providerThe service provider that has assets you want to publish
--tagOne or many tags that have assets you want to publish
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + + + + + +
+ + view:cache +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+ + view:clear +
Options:
--help(-h)Display this help message
--quiet(-q)Do not output any message
--verbose(-v)Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version(-V)Display this application version
--ansiForce ANSI output
--no-ansiDisable ANSI output
--no-interaction(-n)Do not ask any interactive question
--envThe environment the command should run under

]]>
+ + + + + + + + + + +
+
+ diff --git a/.idea/commandlinetools/schemas/frameworkDescriptionVersion1.1.4.xsd b/.idea/commandlinetools/schemas/frameworkDescriptionVersion1.1.4.xsd new file mode 100644 index 000000000..f2efc6de1 --- /dev/null +++ b/.idea/commandlinetools/schemas/frameworkDescriptionVersion1.1.4.xsd @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.idea/composerJson.xml b/.idea/composerJson.xml new file mode 100644 index 000000000..4199499cf --- /dev/null +++ b/.idea/composerJson.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/deployment.xml b/.idea/deployment.xml new file mode 100644 index 000000000..513ecf03f --- /dev/null +++ b/.idea/deployment.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/dictionaries/barryo.xml b/.idea/dictionaries/barryo.xml new file mode 100644 index 000000000..9c3c7a966 --- /dev/null +++ b/.idea/dictionaries/barryo.xml @@ -0,0 +1,25 @@ + + + + asns + channelgroup + cust + dvli + facs + irrdb + irrdblog + ixps + lacp + peeringdb + physicalinterface + svlid + switchid + switchport + switchportid + virtualinterface + virtualinterfaceid + vlan + vlis + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 000000000..15a15b218 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/laravel-idea-personal.xml b/.idea/laravel-idea-personal.xml new file mode 100644 index 000000000..d6e15d2b5 --- /dev/null +++ b/.idea/laravel-idea-personal.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/.idea/laravel-idea.xml b/.idea/laravel-idea.xml new file mode 100644 index 000000000..5eb5bec5b --- /dev/null +++ b/.idea/laravel-idea.xml @@ -0,0 +1,30 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/laravel-plugin.xml b/.idea/laravel-plugin.xml new file mode 100644 index 000000000..116f05eae --- /dev/null +++ b/.idea/laravel-plugin.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 000000000..28a804d89 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 000000000..7a7dbd346 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/php-test-framework.xml b/.idea/php-test-framework.xml new file mode 100644 index 000000000..19c3f0ae9 --- /dev/null +++ b/.idea/php-test-framework.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/php.xml b/.idea/php.xml new file mode 100644 index 000000000..4c1e1df69 --- /dev/null +++ b/.idea/php.xml @@ -0,0 +1,353 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/phpspec.xml b/.idea/phpspec.xml new file mode 100644 index 000000000..3ba44d71a --- /dev/null +++ b/.idea/phpspec.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/phpunit.xml b/.idea/phpunit.xml new file mode 100644 index 000000000..4f8104cfb --- /dev/null +++ b/.idea/phpunit.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/.idea/vagrant.xml b/.idea/vagrant.xml new file mode 100644 index 000000000..c1be4bd12 --- /dev/null +++ b/.idea/vagrant.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 000000000..e22b24b47 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index a9bfab198..000000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,42 +0,0 @@ -# V3.x.x - 2013xxxx - -[BF] Fix table width in Chrome (1f96d5b - Barry O'Donovan - 2013-01-10) -[HK] Freshly pressed CSS/JS files (1831cbf - Barry O'Donovan - 2013-01-10) -[HK] Update Bootstrap to 2.2.1 (1f1032e - Barry O'Donovan - 2013-01-10) -[BF] Missing end div (7333917 - Barry O'Donovan - 2013-01-05) -[BF] Typo (78ceb65 - Barry O'Donovan - 2013-01-05) -[IM] Better initial consistency with menu options (9d70abc - Barry O'Donovan - 2013-01-05) -[BF] When one tried to edit a switch port, they always got the Add Port(s) form (96f9ca0 - Barry O'Donovan - 2013-01-05) -[BF] Typo in variable name in user welcome email (d732b59 - Barry O'Donovan - 2013-01-05) -[BF] I missed the associates tab in my refactoring - bugs fixed (28029ce - Barry O'Donovan - 2013-01-05) -[IM/BF] Push 64bit interpretation of MySQL NULL date up the function chain (65f9dfe - Barry O'Donovan - 2013-01-05) -[BF] This relates to the previous IXP Manager. Updated for OSS Frontend. (1e0cc96 - Barry O'Donovan - 2013-01-04) -[IM] One can now set the default country for forms. (4690259 - Barry O'Donovan - 2013-01-04) -[IM] Remove static reference and replace with config variable (e5232bc - Barry O'Donovan - 2013-01-04) -[IM] Remove hardcoded reference to INEX (e5b5e0e - Barry O'Donovan - 2013-01-04) -[BF/IM] Fix reference to old Doctrine1 code (6229201 - Barry O'Donovan - 2013-01-04) -[IM] Update welcome email (f0eddfe - Barry O'Donovan - 2012-12-20) -[BF] This should be a dist file so local installs can have their own ignored copy (2efa72f - Barry O'Donovan - 2012-12-18) -[BF] On a clean / fresh install there are no candidate users to set as parents (ab2ccdc - Barry O'Donovan - 2012-12-15) -[BF] Check that DateLeave is a DateTime object before calling methods on it (2eb02f8 - Barry O'Donovan - 2012-12-15) -[BF] Incorrectly named class (43e8ee8 - Barry O'Donovan - 2012-12-15) -[HK] Add schema diagrams (eef662a - Barry O'Donovan - 2012-12-12) -[IM] Adding vendors to fixtures (0b7a559 - Barry O'Donovan - 2012-12-12) -[BF] Min password length is 8 (9298ad9 - Barry O'Donovan - 2012-12-12) -[IM] Use better cross-os sh-banhs (365c7f3 - Barry O'Donovan - 2012-12-12) -[IM] Updating fixtures.php to match documentation on GitHub (92abccc - Barry O'Donovan - 2012-12-12) - - -# V3.0.0 - 20121212 - -Initial release of version 3.0.0. - -IXP Manager V3 was officially released on 2012-12-12 and primarily featured a significant amount of backend changes: - -* code refactoring -* migration to Doctrine2 -* removal of all non JQuery JS libraries -* better library consistancy and API interfaces -* security audit - -IXP Manager V3 is primarily about INEX trying to fashion IXP Manager as a true open source project rather than something INEX specific. diff --git a/IDEAS b/IDEAS index 1e6e91d23..96b51664d 100644 --- a/IDEAS +++ b/IDEAS @@ -2,44 +2,83 @@ Potential Todo / Feature Ideas ------------------------------ -* Add support for INEX member private VLAN service: +* IP address state (history and reserved) - see #274 -Existing schema. Private vlans should be defined in the vlan table. The -vlan table needs an extra field called "private", maybe. Need a separate -privatevlan table to link up vlaninterface ids to these vlans + a little -front-end glue to show what private VLANs are defined and who they are -associated with. +* API - "another thing that could potentially be useful.. the way you + generate RIR objects.. if members could generate their macro and maybe + import/exports based on the data route servers receive this could be + useful.. especially keeping import/exports up to date with a lot of + peering is a major pain.." -Finally, this all needs to be checked to make sure it works with the sflow -stuff, including that the specific private vlan member owners can see the -vlan, but no-one else can. +* "the peer2peer stats page does not scale well for networks with many peers. + I think it could be an idea to make it configurable if you want the + actual graphs or just a list where you can select which graphs you would + like to see in one view" +* you know what would be incredibly useful? an ixpmanager.php + file which bleated about whether the correct modules are installed or not - -* Put a frontend on the new RIB database table(s) +* Review MRTG script for reseller and fanout ports * INEX require that members peer "with at least the greater of 4 other Members, or 10% of other Members". A tool to list members failing this requirement would be useful. -* Email members on >80% of port utilisation +* Member audit - port states, errors / discards, route collector session + status, route server session status, etc. -* Show who a member peers with (or not) on the overview page +* Member health report -* Show if a member is on the route server or not +* Allow member to modify template that Request Peering email is populated + with when sending invite using "Peering Manager". Ideally there could be a + list of predefined variables available that could be used while templating + email (ie. peer name, peer ASN, own name, own ASN, own WWW, common peering + LAN IPs, AS MACRO, peering email, NOC email and so on), so member could + create own template using above variables + own text. -* Member audit - port states, errors / discards, route collector session - status, route server session status, etc. + Also consider adding maxprefixes to peering manager template (see #174). + + +* Update suggested prefix limit / real prefix limit based on IRRDB entries? + +* Add ASN to graph titles + +* Multiple P2P graphs for LAG ports + +* "hose that you have bilateral peerings with is the difference between the + 'Peers' tab and the 'Potential Bilateral Peers' tab. But I'll add a note + to add a tab that also shows this." + + +* Maintenance Announcements / Diary -was issue #30. + * Page for upcoming scheduled maintenance + * Select affected switches and generate a list of affected members (Name, + ASN, IPv4, IPv6 addresses) + * Add notification when members login, especially affected members, link + to maintenance pages. + +* Per location member statistics graphs to show all member traffic is a + specific location - see issue #204 + +* An annual (or more frequent?) email to members reminding them of their + subscriptions. Also email to custadmins reminding them of their users. + Probably staggered over the year to prevent a flux of inbound emails. + +* Single page view of all notes for a customer: https://github.com/inex/IXP-Manager/issues/273 +* Billing details reorg - https://github.com/inex/IXP-Manager/issues/476 -* Go through application/config/application.ini.dist and fix FIXMEs and add new - documentation where indicated +* Route Servers - PeeringDB never via route server + * Never via route servers - https://github.com/inex/IXP-Manager/issues/610 + * BFD - https://github.com/inex/IXP-Manager/issues/351 + * Prefix aggregation - https://github.com/inex/IXP-Manager/issues/281 + * Update templates to use bgp_path.last instead of last_nonaggregated - https://github.com/inex/IXP-Manager/pull/692 -* integrate contacts / users and make contacts much better / CRM like. -* Would be useful to display last login information in the user edit screen or - the list of users. Also the created by, last updated date? +* Patch Panel Updates: + * Link port provisioning with patch panel information - https://github.com/inex/IXP-Manager/issues/660 + * Moving existing port doesn't update patch panel port record - https://github.com/inex/IXP-Manager/issues/661 + * Customer views shows MMR ODF with cabinet position for patch-panel demarc - https://github.com/inex/IXP-Manager/issues/535 -* Member field for "MD5 Required" with values: "No", "Yes - mandatory", - "Yes - on request", "Unknown" +* Querying the signal level in optical modules - https://github.com/inex/IXP-Manager/issues/465 diff --git a/README.md b/README.md index d761f1ef5..1a2849f3c 100644 --- a/README.md +++ b/README.md @@ -1,59 +1,55 @@ +![IXP Manager Logo](https://github.com/inex/IXP-Manager/raw/master/public/images/ixp-manager.png) -# IXP Manager - -INEX are pleased to release "IXP Manager" under an open source license (the +[INEX](https://www.inex.ie/) are pleased to release [IXP Manager](http://www.ixpmanager.org/) under an open source license (the GNU Public License V2) which we hope will benefit the wider IXP community, and especially new and small IXPs looking to expand. -## Documentation +[![CI Tests (ex Dusk)](https://github.com/inex/IXP-Manager/actions/workflows/ci-ex-dusk.yml/badge.svg)](https://github.com/inex/IXP-Manager/actions/workflows/ci-ex-dusk.yml) - courtesy of [GitHub Actions](https://github.com/inex/IXP-Manager/actions/workflows/ci-ex-dusk.yml). -Please see the [wiki](https://github.com/inex/IXP-Manager/wiki) for general -documentation including [installation -instructions](https://github.com/inex/IXP-Manager/wiki/Installation). +## Main Links -## IXP Manager V3 Released +* [IXP Manager Website](https://www.ixpmanager.org) +* [Documentation](https://docs.ixpmanager.org) including: + * Installation [overview](https://docs.ixpmanager.org/latest/install/), + [by automated script](https://docs.ixpmanager.org/latest/install/automated-script/), + and [manually](https://docs.ixpmanager.org/latest/install/manually/) + * [Upgrade Instructions](https://docs.ixpmanager.org/latest/install/upgrading/) +* [Releases](https://github.com/inex/IXP-Manager/releases) +* [List of Users](https://www.ixpmanager.org/community/world-map) ([register here](https://www.ixpmanager.org/community/users/submit)) +* [How to Get Help](https://www.ixpmanager.org/support) +* [Presentations & Talks](https://www.ixpmanager.org/support/talks) +* [Sponsors and Sponsorship](https://www.ixpmanager.org/sponsors) +* Follow [@ixpmanager](https://twitter.com/ixpmanager) on Twitter -IXP Manager V3 was officially released on 2012-12-12 and primarily featured -a significant amount of backend changes: +## Documentation -* code refactoring -* migration to Doctrine2 -* removal of all non JQuery JS libraries -* better library consistancy and API interfaces -* security audit +Our documentation is hosted on GitHub pages: https://docs.ixpmanager.org/. -IXP Manager V3 is primarily about INEX trying to fashion IXP Manager as a -true open source project rather than something INEX specific. +## About IXP Manager +For detailed information, see: https://www.ixpmanager.org/ -## About IXP Manager +IXP Manager is a full stack management platform for Internet eXchange Points (IXPs) which includes an administration and customer portal; provides end to end provisioning; and both teaches and implements best practice. -IXP Manager is primarily a web application with associated scripts and -utilities which will allow IXPs to manage new customers, provision new -connections / services and monitor traffic usage. It also has a self -contained customer portal allowing IXP members to view their IXP traffic -statistics and a unique tool called My Peering Manager enabling IXP -members to request, manage and track peerings with other members. +IXP Manager began as an internal project at INEX circa 2007. Over that time it has changed a lot, but always for the better and always to help us run INEX efficiently and with lower overhead. Three of our key requirements as an IXP are security, consistency and reliability. IXP Manager has been designed to help us achieve these. -IXP Manager is written in PHP using the Zend Framework, the Doctrine2 ORM -and the Smarty templating engine. The project website and source code -can be viewed at https://github.com/inex/IXP-Manager. +> Our vision for IXP Manager as a technology is that it will enable the creation of IXPs wherever they are required which in turn will create a stronger, open, more robust and better connected internet. And not only is it hoped that IXP Manager will help start these new IXPs but that it will also enable these IXPs to launch from the best place possible: secure by default, proven configurations and designs, implementing best practice. -INEX is an Internet eXchange Point and Ireland's IP peering hub. It is a -neutral, industry-owned Association, founded in 1996, that provides IP -peering facilities for its members. INEX membership is open to all -organisations that can benefit from peering their IP traffic. +Our hope is also to gather and foster an *IXP Manager community* - a place where the many IXPs that use IXP Manager can come and help each other, learn from each other and share their stories. Our website will highlight some of the internet heroes in the IXP space and feature case studies. -See: https://www.inex.ie/ +Today, IXP Manager is in use by many IXPs around the world - with more being added every month. This is something that gives all of the team at INEX, our community sponsors, the developers and contributors immense pride. It is also quite sobering - it reminds us all to keep all the community in mind when developing and prioritising new features. And it focuses our attention on maintaining the excellent reliability and security standards that have been set over the past two decades. + +See also: https://www.inex.ie/ Authors: - Barry O'Donovan, Senior Coding Dude - Nick Hilliard, Junior Coding Dweeb - Contact us via: operations (at) inex.ie -Copyright (C) 2009-2012 Internet Neutral Exchange Association Limited. +* [Barry O'Donovan](https://www.barryodonovan.com/) +* Nick Hilliard + +Copyright (C) 2009 - 2025 Internet Neutral Exchange Association Company Limited By Guarantee. All Rights Reserved. + ## License @@ -73,4 +69,3 @@ along with IXP Manager. If not, see: http://www.gnu.org/licenses/gpl-2.0.html - diff --git a/Vagrantfile b/Vagrantfile new file mode 100644 index 000000000..a8c8bc994 --- /dev/null +++ b/Vagrantfile @@ -0,0 +1,39 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +Vagrant.configure(2) do |config| + config.vm.box = "bento/ubuntu-24.04" + + config.vm.network "forwarded_port", guest: 80, host: 8088 + config.vm.network "forwarded_port", guest: 3306, host: 33061 + + +# config.vm.provider "virtualbox" do |vb| +# vb.memory = "1536" +# vb.gui = true +# +# config.vm.synced_folder ".", "/vagrant/", id: "vagrant-root0", +# owner: "vagrant" +# +# config.vm.synced_folder "./storage", "/vagrant/storage", id: "vagrant-root1", +# owner: "vagrant", +# group: "www-data", +# mount_options: ["dmode=775,fmode=664"] +# +# config.vm.synced_folder "./bootstrap/cache", "/vagrant/bootstrap/cache", id: "vagrant-root4", +# owner: "vagrant", +# group: "www-data", +# mount_options: ["dmode=775,fmode=664"] +# +# end + + config.vm.provider "parallels" do |prl| + prl.memory = 2048 + prl.name = "ixpm-vagrant-24.04" + prl.cpus = 2 + + # config.vm.synced_folder ".", "/vagrant/", mount_options: ["share"] + end + + config.vm.provision :shell, path: "tools/vagrant/bootstrap.sh" +end diff --git a/app/Console/Commands/Audit/PortSpeeds.php b/app/Console/Commands/Audit/PortSpeeds.php new file mode 100644 index 000000000..495cd4361 --- /dev/null +++ b/app/Console/Commands/Audit/PortSpeeds.php @@ -0,0 +1,101 @@ + + * @category Audit + * @package IXP\Console\Commands + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class PortSpeeds extends Command +{ + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'audit:port-speeds + {--cron : Running as a cronjob, only output if mismatched ports found} + {--ignore-pids= : Comma separated physical interface database IDs to ignore}'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Audit configured port speeds against actual switch speeds (as discovered by last SNMP run)'; + + /** + * Execute the console command. + * + * @return mixed + */ + public function handle(): int + { + $mismatched = $ignorepiids = []; + if( $this->option('ignore-pids' ) ) { + $ignorepiids = explode( ',', $this->option('ignore-pids' ) ); + } + + foreach( PhysicalInterface::whereNotIn( 'id', $ignorepiids )->get() as $pi ) { + if( $pi->isConnectedOrQuarantine() + && $pi->switchPort + && $pi->speed !== $pi->switchPort->ifHighSpeed + && $pi->switchPort->ifOperStatus === IfaceMIB::IF_OPER_STATUS_UP ) { + + $mismatched[] = [ + $pi->virtualInterface->customer->getFormattedName(), + $pi->id, + $pi->switchPort->switcher->name, + $pi->switchPort->name, + $pi->switchPort->ifHighSpeed, + $pi->speed, + ]; + } + } + + if( $this->option('cron') && !count( $mismatched ) ) { + return 0; + } + + $this->info( "\nAudit of Configured Physical Interface Port Speeds Against SNMP Discovered Speeds\n" ); + + if( !count( $mismatched ) ) { + $this->info( "No mismatched ports found." ); + } else { + $this->table( [ 'Customer', 'PI DB ID', 'Switch', 'Switchport', 'PI Speed', 'SNMP Speed' ], $mismatched ); + } + + return !count( $mismatched ) ? 0 : 1; + } +} \ No newline at end of file diff --git a/app/Console/Commands/Command.php b/app/Console/Commands/Command.php new file mode 100644 index 000000000..201ab136c --- /dev/null +++ b/app/Console/Commands/Command.php @@ -0,0 +1,175 @@ +getOutput()->getVerbosity() === OutputInterface::VERBOSITY_QUIET; + } + + /** + * Returns true if verbosity is at least: VERBOSITY_NORMAL + * + * @return bool + */ + protected function isVerbosityNormal(): bool + { + return $this->getOutput()->getVerbosity() >= OutputInterface::VERBOSITY_NORMAL; + } + + /** + * Returns true if verbosity is at least: VERBOSITY_VERBOSE + * + * @return bool + */ + protected function isVerbosityVerbose(): bool + { + return $this->getOutput()->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE; + } + + /** + * Returns true if verbosity is at least: VERBOSITY_VERY_VERBOSE + * + * @return bool + */ + protected function isVerbosityVeryVerbose(): bool + { + return $this->getOutput()->getVerbosity() >= OutputInterface::VERBOSITY_VERY_VERBOSE; + } + + /** + * Returns true if verbosity is at least: VERBOSITY_DEBUG + * + * @return bool + */ + protected function isVerbosityDebug(): bool + { + return $this->getOutput()->getVerbosity() >= OutputInterface::VERBOSITY_DEBUG; + } + + /** + * Returns the list of customers that the keyword match the ASN or the name + * + * @param string|int $search + * + * @return array + */ + protected function customersViaNameOrASN( string|int $search ): array + { + return Customer::selectRaw( 'id,name,autsys' ) + ->when( is_numeric( $search ), function( Builder $q ) use( $search ) { + return $q->where( 'autsys', $search ); + }) + ->orWhere( 'name', 'LIKE', '%' . $search . '%' ) + ->orderBy( 'id' )->get()->toArray(); + } + + /** + * Returns the list of users that the keyword match the username or the email + * + * @param string $search + * + * @return array + */ + protected function usersViaUsernameOrEmail( string $search ): array + { + return User::selectRaw( + 'user.id as id, user.name as name, + user.username as username, user.email as email, + GROUP_CONCAT( c.name SEPARATOR "\n" ) AS cname, + GROUP_CONCAT( + CASE + WHEN c2u.privs = 3 THEN "SU" + WHEN c2u.privs = 2 THEN "CA" + WHEN c2u.privs = 1 THEN "CU" + END + SEPARATOR "\n" ) + AS privs' ) + ->leftJoin( 'customer_to_users AS c2u', 'c2u.user_id', 'user.id' ) + ->leftJoin( 'cust AS c', 'c.id', 'c2u.customer_id' ) + ->where( 'username', 'LIKE', '%' . $search . '%' ) + ->orWhere( 'email', 'LIKE', '%' . $search . '%' ) + ->groupBy( 'id', 'name', 'username', 'email' ) + ->orderBy( 'id' )->get()->keyBy( 'id' )->toArray(); + } + + /** + * Validate an input. + * + * @param mixed $method + * @param array $rules + * + * @return mixed + */ + protected function validate_cmd( mixed $method, array $rules ): mixed + { + $value = $method(); + $validate = $this->validateInput( $rules, $value ); + + if( $validate !== true ) { + $this->warn( $validate ); + $value = $this->validate_cmd( $method, $rules ); + } + return $value; + } + + + /** + * Simple validator function for validating a single value against a given rule + * and error and exit it if fails. + * + * @param string $rule The Laravel validator rule e.g. 'required|string|min:8|max:255' + * @param string $name The parameter name for output in error messages + * @param mixed $value The value to validate against $rule + * + * @return true + * + * @throws \Exception + */ + protected function validateOrExit( string $rule, string $name, string $value ): bool + { + $validator = \Validator::make( [ $name => $value ], [ $name => $rule ] ); + + if ($validator->fails()) { + $this->error( $validator->errors()->first( $name ) ); + exit -1; + } + + return true; + } +} \ No newline at end of file diff --git a/app/Console/Commands/Contact/ExportGroup.php b/app/Console/Commands/Contact/ExportGroup.php new file mode 100644 index 000000000..ddfc9aa16 --- /dev/null +++ b/app/Console/Commands/Contact/ExportGroup.php @@ -0,0 +1,124 @@ + + * @author Yann Robin + * @category Contact + * @package IXP\Console\Commands + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class ExportGroup extends Command +{ + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'contact:export-group + {--type= : Contact group type (e.g. ROLE)} + {--name= : Contact group name (e.g. beer)} + {--format=json : Output format - one of json (default) or csv} + {--cid= : Optionally limit results to given customer id}'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Export contacts based on group information'; + + + /** + * Execute the console command. + * + * @return mixed + */ + public function handle(): int + { + $type = $this->option('type'); + $name = $this->option('name'); + // Imported from Zend Framework with little change on 2017-11 + if( !( $type && $name ) || ( $type && $name ) ) { + $this->error( "Group name or type must be set (and not both)." ); + return -1; + } + + if( !in_array( $this->option('format'), [ 'json', 'csv' ] ) ) { + $this->error( "Format must be either 'json' or 'csv'." ); + return -2; + } + + $contacts = Contact::selectRaw( 'c.name AS name, c.position as position, c.email AS email, c.phone AS phone, c.mobile AS mobile, + c.facilityaccess AS facilityaccess, c.mayauthorize AS mayauthorize, c.notes as notes' ) + ->from( 'contact AS c' ) + ->leftJoin( 'contact_to_group AS ctg', 'ctg.contact_id', 'c.id' ) + ->leftJoin( 'contact_group AS cg', 'cg.id', 'ctg.contact_group_id' ) + ->leftJoin( 'cust AS cu', 'cu.id', 'c.custid') + ->when( $type, function( Builder $q, $type ) { + return $q->where( 'cg.type', $type ); + }, function( $query ) use( $name ) { + return $query->where( 'cg.name', $name ); + } ) + ->when( $cid = $this->option('cid'), function( Builder $q, $cid ) { + return $q->where( 'cu.id', $cid ); + })->groupBy( 'c.id' )->get()->toArray(); + + if( !count( $contacts ) ) { + if( $this->option('format' ) === 'json' ) { + echo json_encode( [] ) . "\n"; + } + return 0; + } + + if( $this->option('format') === "csv" ) { + $names= []; + foreach( $contacts[0] as $name => $data ) { + $names[] = $name; + } + + array_unshift( $contacts, $names ); + + $out = fopen('php://output', 'w'); + + foreach( $contacts as $c ) { + fputcsv( $out, $c ); + } + + fclose( $out ); + } else { + echo json_encode( $contacts ); + } + echo "\n"; + return 0; + } +} \ No newline at end of file diff --git a/app/Console/Commands/Customer/Find.php b/app/Console/Commands/Customer/Find.php new file mode 100644 index 000000000..ae2a88df9 --- /dev/null +++ b/app/Console/Commands/Customer/Find.php @@ -0,0 +1,72 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Console\Commands\Customer + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ + +class Find extends Command +{ + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'customer:find + {search : ASN or customer name fragment to search for}'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Command to find and print customer(s) details via the ASN or Name.'; + + /** + * Execute the console command. + * + * @return int + */ + public function handle(): int + { + $this->table( + ['ID', 'Name', 'ASN'], + $this->customersViaNameOrASN( $this->argument('search') ) + ); + + return 0; + } +} diff --git a/app/Console/Commands/Grapher/EmailPortUtilisation.php b/app/Console/Commands/Grapher/EmailPortUtilisation.php new file mode 100644 index 000000000..309c1023c --- /dev/null +++ b/app/Console/Commands/Grapher/EmailPortUtilisation.php @@ -0,0 +1,154 @@ + + * @author Yann Robin + * @category Grapher + * @package IXP\Console\Commands + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class EmailPortUtilisation extends GrapherCommand +{ + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'grapher:email-port-utilisations {email} + {--T|threshold=80 : Min percentage usage to include in report (default 80)} + {--B|backend= : Which graphing backend to use}'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Email a port utilisation report (separate multiple emails with commas)'; + + + /** + * Execute the console command. + * + * @return mixed + */ + public function handle(): int + { + Grapher::backend( $this->option( 'backend' ) ); + $this->setGrapher( Grapher::getFacadeRoot() ); + + if( ( $retval = $this->verifyArgsAndOptions() ) !== 0 ) { + return $retval; + } + + $custs = Customer::currentActive(true, true )->get(); + $excess = []; + foreach( $custs as $c ) { + foreach( $c->virtualInterfaces as $vi ) { + if( ( $speed = $vi->speed() * 1000 * 1000 ) === 0 ) { + continue; + } + + if( $vi->physicalInterfaces->count() === 1 ) { + $graph = $this->grapher()->physint( $vi->physicalInterfaces()->first() )->setCategory( Graph::CATEGORY_BITS )->setPeriod( Graph::PERIOD_WEEK ); + } else { + $graph = $this->grapher()->virtint( $vi )->setCategory( Graph::CATEGORY_BITS )->setPeriod( Graph::PERIOD_WEEK ); + } + + $stats = $graph->statistics(); + $utilIn = ( $stats->maxIn() * 100.0 ) / $speed; + $utilOut = ( $stats->maxOut() * 100.0 ) / $speed; + + if( $utilIn > $this->option('threshold') || $utilOut > $this->option('threshold') ) { + $excess[ $c->id ]['cust'] = $c; + + $port['speed'] = $speed/1000/1000/1000; + $port['utilIn'] = $utilIn; + $port['utilOut'] = $utilOut; + $port['switch'] = $vi->physicalInterfaces()->first()->switchPort->switcher; + $port['png'] = $graph->png(); + + if( $this->isVerbosityVerbose() ) { + $this->warn( sprintf( "%s\n\tIN %0.2f%%\tOUT: %0.2f%%", $c->name, $utilIn, $utilOut ) ); + } + + $excess[ $c->id ]['ports'][] = $port; + + } elseif( $this->isVerbosityVeryVerbose() ) { + $this->info( sprintf( "%s\n\tIN %0.2f%%\tOUT: %0.2f%%", $c->name, $utilIn, $utilOut ) ); + } + } + } + + if( count( $excess ) ) { + Mail::to( explode( ',', $this->argument( 'email' ) ) ) + ->send( new PortUtilisationMail( $excess, (float)$this->option('threshold') ) ); + } + + return 0; + } + + + /** + * Check the various arguments and options that have been password to the console command + * + * @return int 0 for success or else an error code + * + * @psalm-return 0|253|254 + */ + protected function verifyArgsAndOptions(): int + { + $emails = explode( ',', $this->argument('email') ); + + foreach( $emails as $e ) { + if( filter_var( $e, FILTER_VALIDATE_EMAIL ) === false ) { + $this->error( "Invalid email address: $e" ); + return 254; + } + } + + $t = $this->option('threshold'); + if( !is_numeric($t) || (float)$t < 0.0 || (float)$t > 100.0 ) { + $this->error( "Invalid value for threshold. Must be between 0 and 100." ); + return 253; + } + + // all good :-D + return 0; + } +} \ No newline at end of file diff --git a/app/Console/Commands/Grapher/EmailPortsWithCounts.php b/app/Console/Commands/Grapher/EmailPortsWithCounts.php new file mode 100644 index 000000000..de37df498 --- /dev/null +++ b/app/Console/Commands/Grapher/EmailPortsWithCounts.php @@ -0,0 +1,162 @@ + 0 + * + * @author Barry O'Donovan + * @author Yann Robin + * @category Grapher + * @package IXP\Console\Commands + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class EmailPortsWithCounts extends GrapherCommand +{ + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'grapher:email-ports-with-counts {email} + {--errors : Ports with an error count (default)} + {--discards : Ports with a discard count} + {--B|backend= : Which graphing backend to use}'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Email ports with an error / discards count > 0'; + + + /** + * Execute the console command. + * + * @return mixed + */ + public function handle(): int + { + Grapher::backend( $this->option( 'backend' ) ); + $this->setGrapher( Grapher::getFacadeRoot() ); + + if( ( $retval = $this->verifyArgsAndOptions() ) !== 0 ) { + return $retval; + } + + if( $this->option( 'discards' ) ) { + $category = Graph::CATEGORY_DISCARDS; + } else { + $category = Graph::CATEGORY_ERRORS; + } + + $ports = $this->emailPortsWithCounts( $category ); + + if( count( $ports ) ) { + Mail::to( explode( ',', $this->argument( 'email' ) ) )->send( new PortsWithCountsMail( $ports, $category ) ); + } else if( $this->isVerbosityVerbose() ) { + $this->info("No ports with packet counts > 0"); + } + + return 0; + } + + /** + * @param $category + * + * @return (mixed|string)[][] + * + * @throws + * + * @psalm-param 'discs'|'errs' $category + * + * @psalm-return list + */ + private function emailPortsWithCounts( string $category ): array + { + $data = TrafficDaily::loadTraffic( Carbon::yesterday(), $category ); + $ports = []; + + foreach( $data as $d ) { + if( $d[ 'day_tot_in' ] === 0 && $d[ 'day_tot_out' ] === 0 ) { + continue; + } + + $port = []; + + if( $this->isVerbosityVerbose() ) { + $this->info( "{$d['name']}\n\t\tIN / OUT: {$d[ 'day_tot_in' ]} / {$d[ 'day_tot_out' ]}" ); + } + + $graph = $this->grapher()->customer( Customer::find( $d[ 'cust_id' ] ) )->setCategory( $category )->setPeriod( Graph::PERIOD_DAY ); + + $port['cust_id'] = $d[ 'cust_id' ]; + $port['name'] = $d[ 'name' ]; + $port['in'] = $d[ 'day_tot_in' ]; + $port['out'] = $d[ 'day_tot_out' ]; + $port['png'] = $graph->png(); + + $ports[] = $port; + } + + return $ports; + } + + /** + * Check the various arguments and options that have been password to the console command + * + * @return int 0 for success or else an error code + * + * @psalm-return 0|254 + */ + protected function verifyArgsAndOptions(): int + { + $emails = explode( ',', $this->argument('email') ); + + foreach( $emails as $e ) { + if( filter_var( $e, FILTER_VALIDATE_EMAIL ) === false ) { + $this->error( "Invalid email address: $e" ); + return 254; + } + } + + // all good :-D + return 0; + } +} diff --git a/app/Console/Commands/Grapher/EmailTrafficDeltas.php b/app/Console/Commands/Grapher/EmailTrafficDeltas.php new file mode 100644 index 000000000..b9d3b7ed3 --- /dev/null +++ b/app/Console/Commands/Grapher/EmailTrafficDeltas.php @@ -0,0 +1,217 @@ + + * @author Yann Robin + * @category Grapher + * @package IXP\Console\Commands + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class EmailTrafficDeltas extends GrapherCommand +{ + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'grapher:email-traffic-deltas {email} + {--stddev=1.5 : Multiple of the stddev to report on (default 1.5)} + {--B|backend= : Which graphing backend to use}'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Email ports with a swing in the standard deviation'; + + /** + * Execute the console command. + * + * @return mixed + */ + public function handle(): int + { + Grapher::backend( $this->option( 'backend' ) ); + $this->setGrapher( Grapher::getFacadeRoot() ); + + if( ( $retval = $this->verifyArgsAndOptions() ) !== 0 ) { + return $retval; + } + $day = Carbon::now()->subDays( 1 ); + $ports = $this->portsWithDelta( $day, (float)$this->option( 'stddev' ) ); + + if( count( $ports ) ) { + Mail::to( explode( ',', $this->argument( 'email' ) ) )->send( new TrafficDeltasMailable( $ports, (float)$this->option( 'stddev' ), $day ) ); + } else if( $this->isVerbosityVerbose() ) { + $this->info("No ports have a traffic delta within the requested deviation"); + } + + return 0; + } + + /** + * Find ports with the given stddev delta + * + * @param Carbon $day + * @param float $stddev + * + * @return (|\Illuminate\Database\Eloquent\Model|float|int|mixed|string)[][] + * + * @throws + * + * @psalm-return list, dOut: float|int<0, max>, days: (TGeneratedFromParam0 is null ? Illuminate\Config\Repository : (TGeneratedFromParam0 is string ? mixed : null)), meanIn: mixed, meanOut: mixed, percentIn: float|int, percentOut: float|int, pngMonth: string, pngYear: string, sIn: 'decrease'|'increase', sOut: 'decrease'|'increase', stddevIn: float, stddevOut: float, thresholdIn: float, thresholdOut: float, todayAvgIn: mixed, todayAvgOut: mixed}> + */ + private function portsWithDelta( Carbon $day, float $stddev ): array + { + $custs = Customer::currentActive(true, true )->get(); + $ports = []; + + foreach( $custs as $c ) { + $tds = TrafficDaily::where( 'cust_id', $c->id ) + ->where( 'category', Graph::CATEGORY_BITS ) + ->where( 'day', '<=', $day->format( 'Y-m-d' ) ) + ->whereRaw( "DATE_FORMAT(day,'%w') = DATE_FORMAT( '" . $day->format( 'Y-m-d' ) . "','%w') ") + ->orderByDesc( 'day' ) + ->limit( config('grapher.cli.traffic_differentials.stddev_calc_length', 60)+1 ) + ->get()->toArray(); + + + if( $tds === null || count( $tds ) <= 3 ) { + continue; + } + + $port = []; + + $port['cust'] = $c; + $port['days'] = config('grapher.cli.traffic_differentials.stddev_calc_length'); + + $port['meanIn'] = 0.0; $port['stddevIn'] = 0.0; + $port['meanOut'] = 0.0; $port['stddevOut'] = 0.0; + $port['count'] = 0.0; + + $t = array_shift($tds); + $port['todayAvgIn'] = $t['day_avg_in']; + $port['todayAvgOut'] = $t['day_avg_out']; + + foreach( $tds as $t ) { + $port['count'] += 1.0; + $port['meanIn'] += $t['day_avg_in']; + $port['meanOut'] += $t['day_avg_out']; + } + + $port['meanIn'] /= $port['count']; + $port['meanOut'] /= $port['count']; + + foreach( $tds as $t ) { + $port['stddevIn'] += ( $t['day_avg_in'] - $port['meanIn'] ) * ( $t['day_avg_in'] - $port['meanIn'] ); + $port['stddevOut'] += ( $t['day_avg_out'] - $port['meanOut'] ) * ( $t['day_avg_out'] - $port['meanOut'] ); + } + + $port['stddevIn'] = sqrt( $port['stddevIn'] / ( $port['count'] - 1 ) ); + $port['stddevOut'] = sqrt( $port['stddevOut'] / ( $port['count'] - 1 ) ); + + // so, is yesterday's traffic outside of the standard deviation? And is it an increase or decrease? + $port['sIn'] = ( $port['todayAvgIn'] - $port['meanIn'] ) > 0 ? 'increase' : 'decrease'; + $port['sOut'] = ( $port['todayAvgOut'] - $port['meanOut'] ) > 0 ? 'increase' : 'decrease'; + $port['dIn'] = abs( $port['todayAvgIn'] - $port['meanIn'] ); + $port['dOut'] = abs( $port['todayAvgOut'] - $port['meanOut'] ); + + $port['thresholdIn'] = $stddev * $port['stddevIn']; + $port['thresholdOut'] = $stddev * $port['stddevOut']; + + $port['percentIn'] = $port['meanIn'] ? (int)( ( $port[ 'dIn' ] / $port[ 'meanIn' ] ) * 100 ) : $port['dIn']; + $port['percentOut'] = $port['meanOut'] ? (int)( ( $port[ 'dOut' ] / $port[ 'meanOut' ] ) * 100 ) : $port['dOut']; + + + if( $port['dIn'] > $port['thresholdIn'] || $port['dOut'] > $port['thresholdOut'] ) { + if( $this->isVerbosityVerbose() ) { + $this->warn( $c->name ); + $this->warn( sprintf( "\tIN M: %d\tSD: %d\tDiff: %d\tT: %d\tR: %s", + (int)$port[ 'meanIn' ], (int)$port[ 'stddevIn' ], (int)$port[ 'dIn' ], $port['thresholdIn'], ( $port['dIn'] > $port['thresholdIn'] ? 'OUT' : 'IN' ) + ) ); + $this->warn( sprintf( "\tOUT M: %d\tSD: %d\tDiff: %d\tT: %d\tR: %s\n", + (int)$port[ 'meanOut' ], (int)$port[ 'stddevOut' ], (int)$port[ 'dOut' ], $port['thresholdOut'], ( $port['dOut'] > $port['thresholdOut'] ? 'OUT' : 'IN' ) + ) ); + } + + $port['pngMonth'] = $this->grapher()->customer( $c )->setCategory( Graph::CATEGORY_BITS )->setPeriod( Graph::PERIOD_MONTH )->png(); + $port['pngYear'] = $this->grapher()->customer( $c )->setCategory( Graph::CATEGORY_BITS )->setPeriod( Graph::PERIOD_YEAR )->png(); + $ports[] = $port; + + } else if( $this->isVerbosityVeryVerbose() ) { + $this->info( $c->name ); + $this->info( sprintf( "\tIN M: %d\tSD: %d\tDiff: %d\tT: %d\tR: %s", + (int)$port[ 'meanIn' ], (int)$port[ 'stddevIn' ], (int)$port[ 'dIn' ], $port['thresholdIn'], ( $port['dIn'] > $port['thresholdIn'] ? 'OUT' : 'IN' ) + ) ); + $this->info( sprintf( "\tOUT M: %d\tSD: %d\tDiff: %d\tT: %d\tR: %s\n", + (int)$port[ 'meanOut' ], (int)$port[ 'stddevOut' ], (int)$port[ 'dOut' ], $port['thresholdOut'], ( $port['dOut'] > $port['thresholdOut'] ? 'OUT' : 'IN' ) + ) ); + } + } + + return $ports; + } + + /** + * Check the various arguments and options that have been password to the console command + * + * @return int 0 for success or else an error code + * + * @psalm-return 0|253|254 + */ + protected function verifyArgsAndOptions(): int + { + $emails = explode( ',', $this->argument('email') ); + + foreach( $emails as $e ) { + if( filter_var( $e, FILTER_VALIDATE_EMAIL ) === false ) { + $this->error( "Invalid email address: $e" ); + return 254; + } + } + + if( !is_numeric( $this->option('stddev') ) ) { + $this->error( "Invalid stddev: " . $this->option('stddev') ); + return 253; + } + + return 0; + } +} \ No newline at end of file diff --git a/app/Console/Commands/Grapher/GenerateConfiguration.php b/app/Console/Commands/Grapher/GenerateConfiguration.php new file mode 100644 index 000000000..d19bdba68 --- /dev/null +++ b/app/Console/Commands/Grapher/GenerateConfiguration.php @@ -0,0 +1,137 @@ + + * @author Yann Robin + * @category Grapher + * @package IXP\Console\Commands + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class GenerateConfiguration extends GrapherCommand +{ + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'grapher:generate-configuration + {--B|backend= : Which graphing backend to use} + {--O|output=- : Save configuration to specified file (default: stdout)}'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Generate configuration for a graphing backend'; + + /** + * Execute the console command. + * + * @return mixed + */ + public function handle(): int + { + $grapher = Grapher::backend( $this->option( 'backend' ) ); + + if( !$grapher->isConfigurationRequired() ) { + $this->info("This grapher backend (" . $grapher->name() . ") does not require any configuration to be generated"); + return 100; + } + + if( ( $retval = $this->verifyArgsAndOptions($grapher) ) !== 0 ) + return $retval; + + // backend and options are now valid + // let's generate the configuration + return $this->outputConfiguration( $grapher->generateConfiguration( GrapherBackend::GENERATED_CONFIG_TYPE_MONOLITHIC )[0] ); + } + + /** + * Output the configuration in the requested format + * + * @param string $conf The Configuration + * + * @return int Suggested status code for script exit (0 == success) + * + * @psalm-return -2|0 + */ + protected function outputConfiguration( $conf ): int + { + if( $this->option('output') === '-' ) { + echo $conf; + return 0; + } + + if( !@file_put_contents( $this->option('output'), $conf ) ) { + $this->error( "Could not save configuration to the specified file [{$this->option('output')}]" ); + return -2; + } + + return 0; + } + + /** + * Check the various arguments and options that have been password to the console command + * + * @param GrapherBackend $grapher + * + * @return int 0 for success or else an error code + */ + protected function verifyArgsAndOptions( GrapherBackend $grapher ): int + { + $fn = $this->option('output'); + + if( !$grapher->isMonolithicConfigurationSupported() ) { + $this->error( "This backend ({$grapher->name()}) does not support single configuration files" ); + return 251; + } + + if( $fn === '-' ) return 0; + + // does it exist but is not writable? + if( is_file($fn) && !is_writable($fn) ) { + $this->error( "The output file exists but is not writable" ); + return 253; + } + + // can we write to the directory? + if( !( dirname($fn) && is_dir( dirname($fn) ) && is_writable( dirname($fn) ) ) ) { + $this->error( "The output file does not exists and cannot be created" ); + return 252; + } + + // all good :-D + return 0; + } +} \ No newline at end of file diff --git a/app/Console/Commands/Grapher/GrapherCommand.php b/app/Console/Commands/Grapher/GrapherCommand.php new file mode 100644 index 000000000..7c26eafb1 --- /dev/null +++ b/app/Console/Commands/Grapher/GrapherCommand.php @@ -0,0 +1,62 @@ + + * @author Yann Robin + * @category Grapher + * @package IXP\Console\Commands + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +abstract class GrapherCommand extends IXPCommand +{ + /** + * @var Grapher + */ + private $grapher; + + /** + * @return Grapher + */ + protected function grapher(): Grapher + { + return $this->grapher; + } + + /** + * @param Grapher $g + */ + protected function setGrapher( Grapher $g ): static + { + $this->grapher = $g; + return $this; + } +} \ No newline at end of file diff --git a/app/Console/Commands/Grapher/PruneDailyP2p.php b/app/Console/Commands/Grapher/PruneDailyP2p.php new file mode 100644 index 000000000..73ea53077 --- /dev/null +++ b/app/Console/Commands/Grapher/PruneDailyP2p.php @@ -0,0 +1,74 @@ + + * @package IXP\Console\Commands\Grapher + * @copyright Copyright (C) 2009 - 2024 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class PruneDailyP2p extends IXPCommand +{ + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'grapher:prune-daily-p2p {--all} {--days=}'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Delete --all or records older than --days from p2p_daily_stats'; + + public function handle(): int + { + if( $this->option('all') ) { + $before = now(); + } else { + if( !$this->option('days') || !is_numeric( $this->option('days') ) ) { + $this->error( 'Please specify if you want to delete --all records or those from more than --days=x ago'); + } + $before = now()->subDays( (int)$this->option('days' ) ); + } + + $cnt = P2pDailyStats::where( 'day', '<', $before->format('Y-m-d') )->delete(); + + if( $this->isVerbosityVerbose() ) { + $this->info( "Deleted $cnt records older than {$before->format('Y-m-d')}" ); + } + + return 0; + } +} \ No newline at end of file diff --git a/app/Console/Commands/Grapher/UploadDailyP2p.php b/app/Console/Commands/Grapher/UploadDailyP2p.php new file mode 100644 index 000000000..142a64520 --- /dev/null +++ b/app/Console/Commands/Grapher/UploadDailyP2p.php @@ -0,0 +1,190 @@ +argument('day') ) ) { + $this->error("Invalid day parameter - expected format is " . now()->subDay()->format('Y-m-d') ); + return -1; + } + + $this->setGrapher( Grapher::getFacadeRoot() ); + + $start = Carbon::parse( $this->argument('day') . ' 00:00:00' ); + $end = $start->copy()->endOfDay(); + $startTime = microtime(true); + + Customer::currentActive(true,true,true) + ->when( $this->option('customer-id'), function ($query, string $cid) { + $query->where('id', $cid); + }) + ->each( function( Customer $c ) use ( $start, $end ) { + + $iterTime = microtime(true); + + if($this->isVerbosityNormal()) { + $this->info("Processing {$c->name} for " . $start->format('Y-m-d')); + } + + $stats = $this->collectStatistics( $c, $start, $end ); + $this->storeStatistics( $stats, $c, $start ); + + if($this->isVerbosityNormal()) { + $this->info("Completed {$c->name} in " . (microtime(true) - $iterTime) . " seconds"); + } + }); + + if($this->isVerbosityNormal()) { + $this->info("All Completed in " . (microtime(true) - $startTime) . " seconds"); + } + return 0; + } + + /** + * Collect Statistics data + * + * @param Customer $customer + * @param Carbon $start + * @param Carbon $end + * + * @return int[][] + * + * @throws \IXP\Exceptions\Services\Grapher\ParameterException + * + * @psalm-return array<''|int, array>> + */ + protected function collectStatistics(Customer $customer, Carbon $start, Carbon $end) : array + { + $stats = []; + + + foreach($customer->virtualinterfaces as $vi) { + + /** @var VlanInterface $svli */ + foreach($vi->vlaninterfaces as $svli) { + + if(!$svli->vlan->export_to_ixf) { + continue; + } + + foreach([4,6] as $protocol) { + + if( !$svli->ipvxEnabled($protocol) ) { + continue; + } + + + /** @var VlanInterface $dvli */ + foreach( VlanInterfaceAggregator::forVlan( $svli->vlan, $protocol ) as $dvli ) { + + // skip if it's this customer's own vlan interface or another of their own connections + if( $svli->id === $dvli->id || $customer->id == $dvli->virtualInterface->custid ) { + continue; + } + + if($this->isVerbosityVeryVerbose() ) { + $this->line( "\t- {$svli->vlan->name} ipv$protocol with {$dvli->virtualInterface->customer->name}" ); + } + + $peerId = $dvli->virtualInterface->custid; + if(!isset($stats[$peerId])) { + $stats[$peerId] = [ + 'ipv4_total_in' => 0, + 'ipv4_total_out' => 0, + 'ipv6_total_in' => 0, + 'ipv6_total_out' => 0, + 'ipv4_max_in' => 0, + 'ipv4_max_out' => 0, + 'ipv6_max_in' => 0, + 'ipv6_max_out' => 0, + ]; + } + + + $graph = $this->grapher()->p2p($svli, $dvli) + ->setProtocol('ipv'.$protocol) + ->setPeriod(Graph::PERIOD_CUSTOM, $start, $end); + + $statistics = $graph->statistics()->all(); + + $stats[$peerId]["ipv{$protocol}_total_in"] += (int)$statistics['totalin']; + $stats[$peerId]["ipv{$protocol}_total_out"] += (int)$statistics['totalout']; + $stats[$peerId]["ipv{$protocol}_max_in"] += (int)$statistics['maxin']; + $stats[$peerId]["ipv{$protocol}_max_out"] += (int)$statistics['maxout']; + } + + + } + + } + + } + + return $stats; + } + + /** + * Store the statistics data into the database + * + * @param array $stats + * @param Customer $customer + * @param Carbon $start + * @return void + */ + protected function storeStatistics(array $stats, Customer $customer, Carbon $start) : void { + + if( $this->isVerbosityVerbose() ) { + $this->line( "\tStoring date for {$customer->name} in database" ); + } + + DB::transaction(function () use( $stats, $customer, $start ) { + + foreach( $stats as $peerId => $traffic ) { + + P2pDailyStats::updateOrCreate( [ + 'cust_id' => $customer->id, + 'day' => $start->format( 'Y-m-d' ), + 'peer_id' => $peerId, + ], + $traffic + ); + + } + }); + + } +} diff --git a/app/Console/Commands/Grapher/UploadPhysIntStatsToDb.php b/app/Console/Commands/Grapher/UploadPhysIntStatsToDb.php new file mode 100644 index 000000000..c2bba7ae5 --- /dev/null +++ b/app/Console/Commands/Grapher/UploadPhysIntStatsToDb.php @@ -0,0 +1,130 @@ + + * @author Yann Robin + * @category Grapher + * @package IXP\Console\Commands + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class UploadPhysIntStatsToDb extends GrapherCommand +{ + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'grapher:upload-pi-stats-to-db + {--B|backend= : Which graphing backend to use}'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Upload individual physical interface stats to the database (daily task)'; + + /** + * Execute the console command. + * + * @throws + */ + public function handle(): int + { + Grapher::backend( $this->option( 'backend' ) ); + $this->setGrapher( Grapher::getFacadeRoot() ); + + // disable the cache if it's in-memory + if( config('cache.default') === 'array' ) { + $this->grapher()->disableCache(); + } + + // This should only be done once a day and if values already exist for 'today', just delete them. + $today = now(); + TrafficDailyPhysInt::where( 'day', $today->format('Y-m-d') )->delete(); + + $custs = Customer::getConnected( true ); + + foreach( $custs as $cust ) { + if( $this->isVerbosityVerbose() ) { + $this->info( "\t- processing customer " . $cust->name ); + } + + foreach( $cust->virtualInterfaces as $vi ) { + foreach( $vi->physicalInterfaces as $pi ) { + foreach( Graph::CATEGORIES as $category ) { + $graph = $this->grapher()->physint( $pi )->setCategory( $category ); + + $td = new TrafficDailyPhysInt; + $td->physicalinterface_id = $pi->id; + $td->day = $today; + $td->category = $category; + + foreach( Graph::PERIOD_DESCS as $period => $name ) { + $stats = $graph->setPeriod( $period )->statistics(); + $lname = strtolower( $name ); + $fn = "{$lname}_avg_in"; $td->$fn = $stats->averageIn(); + $fn = "{$lname}_avg_out"; $td->$fn = $stats->averageOut(); + $fn = "{$lname}_max_in"; $td->$fn = $stats->maxIn(); + $fn = "{$lname}_max_out"; $td->$fn = $stats->maxOut(); + $fn = "{$lname}_max_in_at"; $td->$fn = $stats->maxInAt(); + $fn = "{$lname}_max_out_at"; $td->$fn = $stats->maxOutAt(); + $fn = "{$lname}_tot_in"; $td->$fn = $stats->totalIn(); + $fn = "{$lname}_tot_out"; $td->$fn = $stats->totalOut(); + } + + unset( $graph ); + $td->save(); + } + } + } + } + + if( config( 'grapher.cli.traffic_daily.delete_old', true ) ) { + if( $this->isVerbosityVerbose() ) { + $this->warn( "Deleting old daily traffic records that are no longer required" ); + } + + $day = new Carbon( "-" . config( 'grapher.cli.traffic_daily.delete_old_days', 140 ) . " days" ); + TrafficDailyPhysInt::where( 'day', '<', $day->format('Y-m-d') )->delete(); + } + + return 0; + } +} \ No newline at end of file diff --git a/app/Console/Commands/Grapher/UploadStatsToDb.php b/app/Console/Commands/Grapher/UploadStatsToDb.php new file mode 100644 index 000000000..62132107e --- /dev/null +++ b/app/Console/Commands/Grapher/UploadStatsToDb.php @@ -0,0 +1,118 @@ + + * @author Yann Robin + * @category Grapher + * @package IXP\Console\Commands + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class UploadStatsToDb extends GrapherCommand +{ + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'grapher:upload-stats-to-db + {--B|backend= : Which graphing backend to use}'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Upload port stats to the database (daily task)'; + + /** + * Execute the console command. + * + * @return mixed + * @throws \Exception + */ + public function handle(): int + { + Grapher::backend( $this->option( 'backend' ) ); + $this->setGrapher( Grapher::getFacadeRoot() ); + + // This should only be done once a day and if values already exist for 'today', just delete them. + $today = now(); + TrafficDaily::where( 'day', $today->format('Y-m-d') )->delete(); + + $custs = Customer::getConnected( true ); + + foreach( $custs as $cust ) { + if( $this->isVerbosityVerbose() ) { + $this->info( "\t- processing customer " . $cust->name ); + } + + foreach( Graph::CATEGORIES as $category ) { + $graph = $this->grapher()->customer( $cust )->setCategory( $category ); + + $td = new TrafficDaily; + $td->day = $today; + $td->category = $category; + $td->cust_id = $cust->id; + + foreach( Graph::PERIOD_DESCS as $period => $name ) { + $stats = $graph->setPeriod($period)->statistics(); + $lname = strtolower( $name ); + $fn = "{$lname}_avg_in"; $td->$fn = $stats->averageIn(); + $fn = "{$lname}_avg_out"; $td->$fn = $stats->averageOut(); + $fn = "{$lname}_max_in"; $td->$fn = $stats->maxIn(); + $fn = "{$lname}_max_out"; $td->$fn = $stats->maxOut(); + $fn = "{$lname}_tot_in"; $td->$fn = $stats->totalIn(); + $fn = "{$lname}_tot_out"; $td->$fn = $stats->totalOut(); + } + + $td->save(); + } + } + + if( config( 'grapher.cli.traffic_daily.delete_old', true ) ) { + if( $this->isVerbosityVerbose() ) { + $this->warn( "Deleting old daily traffic records that are no longer required" ); + } + + $day = new Carbon( "-" . config( 'grapher.cli.traffic_daily.delete_old_days', 140 ) . " days" ); + TrafficDaily:: where( 'day', '<', $day->format('Y-m-d') )->delete(); + } + + return 0; + } +} \ No newline at end of file diff --git a/app/Console/Commands/Helpdesk/HelpdeskCommand.php b/app/Console/Commands/Helpdesk/HelpdeskCommand.php new file mode 100644 index 000000000..9d387a957 --- /dev/null +++ b/app/Console/Commands/Helpdesk/HelpdeskCommand.php @@ -0,0 +1,51 @@ +helpdesk === null ) { + $this->helpdesk = App::make( Helpdesk::class ); + } + + if( $this->helpdesk === false ) { + $this->error( 'No helpdesk provider defined' ); + exit -1; + } + + return $this->helpdesk; + } +} \ No newline at end of file diff --git a/app/Console/Commands/Helpdesk/UpdateOrganisations.php b/app/Console/Commands/Helpdesk/UpdateOrganisations.php new file mode 100644 index 000000000..90aa72e2f --- /dev/null +++ b/app/Console/Commands/Helpdesk/UpdateOrganisations.php @@ -0,0 +1,207 @@ + + * @author Yann Robin + * @category Helpdesk + * @package IXP\Console\Commands + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class UpdateOrganisations extends HelpdeskCommand +{ + /** + * The console command name. + * + * @var string + */ + protected $name = 'helpdesk:update-organisations'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Update the organisation/contacts records on the helpdesk'; + + /** + * Execute the console command. + * + * @return mixed + */ + public function handle() + { + /** FIXME FIXME-YR update this later */ + $this->info( "Command not ready to use yet" ); + return -1; +// if( $this->getOutput()->isVerbose() ) { +// $this->info( "{$this->name} :: Starting..." ); +// } +// +// $contactemails = []; +// +// // iterate over all customers and add/update customer and its contacts +// foreach( Customer::all() as $cust ) { +// if( $this->getOutput()->isVeryVerbose() ) +// $this->info( "{$this->name} :: processing {$cust->name}..." ); +// +// if( $cust->typeInternal() ){ +// continue; +// } +// +// if( $org = $this->updateOrganisation( $cust ) ) { +// foreach( $cust->contacts as $contact ) { +// // weed out duplicates: +// if( !in_array( $contact->email, $contactemails, false ) ){ +// $contactemails[] = $contact->email; +// } +// +// $this->updateContact( $cust, $contact, $org ); +// } +// } +// } +// +// if( $this->getOutput()->isVerbose() ) { +// $this->info( "{$this->name} :: Finished" ); +// } + } + + + /** + * @param Customer $cust + * + * @return Customer|bool + * + * @throws ApiException + */ + protected function updateOrganisation( Customer $cust ) + { + $helpdesk = $this->getHelpdesk(); + if( $org = $helpdesk->organisationFind( $cust->id ) ) { + if( $helpdesk->organisationNeedsUpdating( $cust, $org ) ) { + if( $helpdesk->organisationUpdate( $org->helpdesk_id, $cust ) ){ + $this->info( "{$this->name} :: updated {$cust->name}" ); + } else { + $this->error( "{$this->name} :: could not update {$cust->name}" ); + return false; + } + } + return $org; + } + + // create it: + if( $org = $helpdesk->organisationCreate( $cust ) ) { + $this->info( "{$this->name}} :: created {$cust->name}" ); + $this->createNetworkUsers( $cust, $org ); + return $org; + } + $this->error( "{$this->name} :: could not create {$cust->name}" ); + return false; + } + + /** + * @param Customer $cust + * @param Contact $contact + * @param $org + * @return false|void + * @throws ApiException + */ + protected function updateContact( Customer $cust, Contact $contact, $org ) + { + if( $contact->getUser() && $contact->getUser()->getPrivs() !== User::AUTH_CUSTUSER ) + return; + + if( $this->getOutput()->isVeryVerbose() ){ + $this->info( "{$this->name} :: processing {$cust->name} :: contact {$contact->name}..." ); + } + + if( $user = $this->getHelpdesk()->userFind( $contact->id ) ) { + if( $this->getHelpdesk()->contactNeedsUpdating( $contact, $user ) ) { + if( $this->getHelpdesk()->userUpdate( $user->helpdesk_id, $contact ) ){ + $this->info( "{$this->name} :: updated {$contact->name}" ); + } else { + $this->error( "{$this->name} :: could not update {$contact->name}" ); + return false; + } + } + } else { + try { + // create it: + if( $user = $this->getHelpdesk()->userCreate( $contact, $org->helpdesk_id ) ){ + $this->info( "{$this->name}} :: created {$contact->name}" ); + } else { + $this->error( "{$this->name} :: could not create {$contact->name}" ); + return false; + } + } catch ( ApiException $e ) { + if( $e->userIsDuplicateEmail() ) { + if( $this->getOutput()->isVerbose() ){ + $this->info( "{$this->name} :: skipping contact {$contact->name}/{$contact->id} as email is a duplicate on helpdesk..." ); + } + return; + } + throw $e; + } + } + } + + /** + * @param Customer $cust + * @param $org + */ + protected function createNetworkUsers( Customer $cust, $org ): void + { + $noc = Contact::create([ + 'name' => $cust->name . ' - NOC', + 'email' => $cust->nocemail, + 'mobile' => $cust->nocphone, + ]); + + $peering = Contact::create([ + 'name' => $cust->name . ' - Peering', + 'email' => $cust->peeringemail, + ]); + + try { + $this->getHelpdesk()->userCreate( $noc, $org->helpdesk_id ); + $this->info( "{$this->name}} :: created {$noc->name}" ); + } catch( \Exception $e ) {} + + try { + $this->getHelpdesk()->userCreate( $peering, $org->helpdesk_id ); + $this->info( "{$this->name}} :: created {$peering->name}" ); + } catch( \Exception $e ) {} + } +} \ No newline at end of file diff --git a/app/Console/Commands/InManrs.php b/app/Console/Commands/InManrs.php new file mode 100644 index 000000000..28dbd92eb --- /dev/null +++ b/app/Console/Commands/InManrs.php @@ -0,0 +1,104 @@ + + * @category Irrdb + * @package IXP\Console\Commands + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class InManrs extends Command +{ + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'ixp-manager:update-in-manrs'; + + /** + * The console command description. + * + * @var string + */ + protected $description = "Update customers 'in MANRS' state"; + + /** + * Execute the console command. + * + * @return mixed + * @throws \Throwable + */ + public function handle(): int + { + // get list of peeringdb networks: + try { + $resp = Http::withHeaders( [ + 'X-Request-Client' => 'IXP Manager', + 'X-Request-Client-Version' => APPLICATION_VERSION, + ] )->throw()->acceptJson() + ->get( 'https://api.manrs.org/asns' ); + } catch(\Exception $e) { + $this->error( 'Could not load ASNs via MANRS\'s API: ' . $e->getMessage() ); + return 1; + } + + $asns = $resp['asns']; + + if( !is_array( $asns ) || !count( $asns ) ) { + $this->error( 'Empty or no ASNs returned from MANRS\'s API' ); + return 2; + } + + // easiest thing to do here is, in a transaction, set all in_manrs to false + // and then update those that are in MANRS to true + + DB::transaction( function () use ( $asns ) { + $qualifying = Customer::trafficking()->current()->count(); + $before = Customer::trafficking()->current()->where([ 'in_manrs' => true ])->count(); + $after = 0; + + DB::table( 'cust' )->update( [ 'in_manrs' => false ] ); + + foreach( Customer::trafficking()->current()->get() as $c ) { + if( in_array( $c->autsys, $asns, false ) ) { + $c->in_manrs = true; + $c->save(); + $after++; + } + } + $this->info( "MANRS membership updated - before/after/missing: {$before}/{$after}/" . ( $qualifying - $after ) ); + + }); + + return 0; + } +} diff --git a/app/Console/Commands/InPeeringDb.php b/app/Console/Commands/InPeeringDb.php new file mode 100644 index 000000000..547c15f19 --- /dev/null +++ b/app/Console/Commands/InPeeringDb.php @@ -0,0 +1,103 @@ + + * @author Yann Robin + * @package IXP\Console\Commands + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class InPeeringDb extends Command +{ + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'ixp-manager:update-in-peeringdb'; + + /** + * The console command description. + * + * @var string + */ + protected $description = "Update customers 'in peeringdb' state"; + + /** + * Execute the console command. + * + * @throws + * + * @psalm-return 0|1|2 + */ + public function handle(): int + { + // get list of peeringdb networks: + if( !( $json = file_get_contents( 'https://www.peeringdb.com/api/net.json?fields=asn' ) ) ) { + $this->error( 'Could not load ASNs via PeeringDB\'s API' ); + return 1; + } + + $peeringdb_asns = json_decode( $json, false ); + + if( !isset( $peeringdb_asns->data ) || !count( $peeringdb_asns->data ) ) { + $this->error( 'Empty or no ASNs returned from PeeringDB\'s API' ); + return 2; + } + + $asns = []; + foreach( $peeringdb_asns->data as $net ) { + $asns[] = $net->asn; + } + + // easiest thing to do here is, in a transaction, set all in_peeringdb to false + // and then update those that are in peeringdb to true + + DB::transaction( function () use ( $asns ) { + $qualifying = Customer::trafficking()->current()->count(); + $before = Customer::trafficking()->current()->where([ 'in_peeringdb' => true ])->count(); + $after = 0; + + DB::table( 'cust' )->update( [ 'in_peeringdb' => false ] ); + + foreach( Customer::trafficking()->current()->get() as $c ) { + if( in_array( $c->autsys, $asns, false ) ) { + $c->in_peeringdb = true; + $c->save(); + $after++; + } + } + + $this->info( "PeeringDB membership updated - before/after/missing: {$before}/{$after}/" . ( $qualifying - $after ) ); + + }); + return 0; + } +} \ No newline at end of file diff --git a/app/Console/Commands/Irrdb/UpdateAsnDb.php b/app/Console/Commands/Irrdb/UpdateAsnDb.php new file mode 100644 index 000000000..eac2d919b --- /dev/null +++ b/app/Console/Commands/Irrdb/UpdateAsnDb.php @@ -0,0 +1,95 @@ + + * @author Yann Robin + * @author Laszlo Kiss + * @category Irrdb + * @package IXP\Console\Commands + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class UpdateAsnDb extends UpdateDb +{ + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'irrdb:update-asn-db + {--asn= : Only update the member with this ASN} + {--id= : Only update the member with this customer ID} + {--alert-email : If set, send an alert email to IDENTITY_ALERTS_EMAIL on error}'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Update the IRRDB ASN database for all customers (or a given customer by ASN/ID/shortname)'; + + /** + * Execute the console command. + * + * @throws + * + * @psalm-return -99|0 + */ + public function handle(): int + { + if( !$this->setupChecks() ) { + return -99; + } + + $customers = $this->resolveCustomers( $this->options() ); + + foreach( $customers as $c ) { + try { + $task = new UpdateAsnDbTask( $c ); + $this->printResults( $c, $task->update(), 'asn' ); + } catch( Exception $e ) { + $this->handleException( $c, $e, 'ASN' ); + $this->info( "Continuing to next customer..."); + continue; + } + } + + if( count( $customers ) > 1 && $this->isVerbosityVerbose() ) { + $this->info( "Total time for net/database/processing: " + . sprintf( "%0.6f/", $this->netTime ) + . sprintf( "%0.6f/", $this->dbTime ) + . sprintf( "%0.6f (secs)", $this->procTime ) + ); + } + + return 0; + } +} \ No newline at end of file diff --git a/app/Console/Commands/Irrdb/UpdateDb.php b/app/Console/Commands/Irrdb/UpdateDb.php new file mode 100644 index 000000000..8cb8b13a3 --- /dev/null +++ b/app/Console/Commands/Irrdb/UpdateDb.php @@ -0,0 +1,166 @@ + + * @author Yann Robin + * @category Irrdb + * @package IXP\Console\Commands + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +abstract class UpdateDb extends Command +{ + protected $netTime = 0.0; + protected $dbTime = 0.0; + protected $procTime = 0.0; + + /** + * Setup checks + * + * @return true + */ + protected function setupChecks(): bool + { + if( !extension_loaded("ds") ) { + $this->warn( "The PHP Data Structure Ds\Set extension is not loaded/available. Falling back to polyfill which, in extreme cases, can take ~3 hours. Install the php-ds extension!" ); + } + return true; + } + + /** + * Returns all customers or, if specified on the command line, a specific customer + * + * @return mixed + */ + protected function resolveCustomers( array $options ): mixed + { + if( $options[ 'asn' ] ) { + $c = Customer::whereAutsys( $options[ 'asn' ] )->get(); + + if( !count( $c ) ) { + $this->error( "No customer found with ASN {$options[ 'asn' ]}" ); + exit(-1); + } + + return $c; + } + + if( $options[ 'id' ] ) { + $c = Customer::whereId( $options[ 'id' ] )->get(); + + if( !count( $c ) ) { + $this->error( "No customer found with ID {$options[ 'id' ]}" ); + exit(-1); + } + + return $c; + } + + return Customer::currentActive( true )->get(); + } + + /** + * Print Results + * @param Customer $c + * @param array $r + * @param string $irrdbType + * + * @return void + */ + protected function printResults( Customer $c, array $r, string $irrdbType = 'prefix' ): void + { + $this->netTime += $r[ 'netTime' ]; + $this->dbTime += $r[ 'dbTime' ]; + $this->procTime += $r[ 'procTime' ]; + + if( $this->isVerbosityQuiet() ) { + return; + } + + $base = $c->abbreviatedName . ': [IPv4: ' + . $r[ 'v4' ][ 'count' ] . ' total; ' . count( $r[ 'v4' ][ 'stale' ] ) . ' stale; ' . count( $r[ 'v4' ][ 'new' ] ) + . ' new; DB ' . ( $r[ 'v4' ][ 'dbUpdated' ] ? 'updated' : 'not updated' ) . '] [IPv6: ' + . $r[ 'v6' ][ 'count' ] . ' total; ' . count( $r[ 'v6' ][ 'stale' ] ) . ' stale; ' . count( $r[ 'v6' ][ 'new' ] ) + . ' new; DB ' . ( $r[ 'v6' ][ 'dbUpdated' ] ? 'updated' : 'not updated' ) . ']'; + + $this->info( $base ); + + if( $r[ 'msg' ] ) { + $this->comment( " " . $r[ 'msg' ] ); + } + + if( $this->isVerbosityVeryVerbose() ) { + $this->info( " Time for net/database/processing: " + . sprintf( "%0.6f/", $r[ 'netTime' ] ) + . sprintf( "%0.6f/", $r[ 'dbTime' ] ) + . sprintf( "%0.6f (secs)", $r[ 'procTime' ] ) + ); + } + + if( $this->isVerbosityDebug() ) { + foreach( [ 4, 6 ] as $protocol ) { + foreach( [ 'stale', 'new' ] as $type ) { + if( count( $r[ 'v' . $protocol ][ $type ] ) ) { + foreach( $r[ 'v' . $protocol ][ $type ] as $e ) { + $this->line( " " . ( $type === 'stale' ? '-' . $e[ $irrdbType ] : '+' . $e ) . " [IPv{$protocol}]" ); + } + } + } + } + } + } + + /** + * Handle exceptions + */ + protected function handleException( Customer $c, \Exception $e, string $type ): void + { + $this->error( "IRRDB {$type} update failed for {$c->name}/AS{$c->autsys}" ); + $this->error( $e->getMessage() ); + + if( !$this->option('alert-email') ) { + return; + } + + if( !config('mail.alerts_recipient.address') ) { + $this->warn( "Alert email not sent as IDENTITY_ALERTS_EMAIL .env option not set." ); + return; + } + + Mail::to( [ [ 'name' => config( 'mail.alerts_recipient.name' ), 'email' => config( 'mail.alerts_recipient.address' ) ] ] ) + ->send( new Alert("IRRDB {$type} update failed for {$c->name}/AS{$c->autsys}", $e ) ); + + $this->info( "Alert email sent to " . config( 'mail.alerts_recipient.address' ) ); + } + +} \ No newline at end of file diff --git a/app/Console/Commands/Irrdb/UpdatePrefixDb.php b/app/Console/Commands/Irrdb/UpdatePrefixDb.php new file mode 100644 index 000000000..55bc00b76 --- /dev/null +++ b/app/Console/Commands/Irrdb/UpdatePrefixDb.php @@ -0,0 +1,99 @@ + + * @author Yann Robin + * @author Laszlo Kiss + * @category Irrdb + * @package IXP\Console\Commands + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class UpdatePrefixDb extends UpdateDb +{ + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'irrdb:update-prefix-db + {--asn= : Only update the member with this ASN} + {--id= : Only update the member with this customer ID} + {--alert-email : If set, send an alert email to IDENTITY_ALERTS_EMAIL on error}'; + + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Update the IRRDB prefix database for all customers (or a given customer by ID/shortname)'; + + /** + * Execute the console command. + * + * @return int + * + * @throws + * + * @psalm-return -99|0 + */ + public function handle(): int + { + if( !$this->setupChecks() ) { + return -99; + } + + $customers = $this->resolveCustomers( $this->options() ); + + foreach( $customers as $c ) { + try { + $task = new UpdatePrefixDbTask( $c ); + $this->printResults( $c, $task->update(), 'prefix' ); + } catch( Exception $e ) { + $this->handleException( $c, $e, 'ASN' ); + $this->info( "Continuing to next customer..."); + continue; + } + } + + + if( count( $customers ) > 1 && $this->isVerbosityVerbose() ) { + $this->info( "Total time for net/database/processing: " + . sprintf( "%0.6f/", $this->netTime ) + . sprintf( "%0.6f/", $this->dbTime ) + . sprintf( "%0.6f (secs)", $this->procTime ) + ); + } + + return 0; + } +} \ No newline at end of file diff --git a/app/Console/Commands/MailingList/GetSubscribers.php b/app/Console/Commands/MailingList/GetSubscribers.php new file mode 100644 index 000000000..86fe2bb61 --- /dev/null +++ b/app/Console/Commands/MailingList/GetSubscribers.php @@ -0,0 +1,82 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Console\Commands\MailingList + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class GetSubscribers extends MailingList +{ + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'mailing-list:get-subscribers + {list : Handle of the mailing list} + {--format=text : Output format - one of text (default) or json} + {--unsubscribed : Provide a list of user emails that are unsubscribed rather than subscribed}'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Get subscribers to the specified mailing list'; + + + /** + * Execute the console command. + * + * @throws + */ + public function handle(): int + { + if( !config( 'mailinglists.enabled' ) ) { + die( "Mailing list functionality is disabled. See: https://docs.ixpmanager.org/latest/features/mailing-lists/\n" ); + } + + $ml = new ML( $this->argument('list' ) ); + + $subscriberEmails = $ml->getSubscriberEmails( !$this->option( 'unsubscribed' ) ); + + if( $this->option('format') === 'json' ) { + echo json_encode( $subscriberEmails, JSON_THROW_ON_ERROR ); + } else { + echo implode( "\n", $subscriberEmails ); + } + + echo "\n"; + return 0; + } +} \ No newline at end of file diff --git a/app/Console/Commands/MailingList/Init.php b/app/Console/Commands/MailingList/Init.php new file mode 100644 index 000000000..dabc8bd3b --- /dev/null +++ b/app/Console/Commands/MailingList/Init.php @@ -0,0 +1,112 @@ + + * @category MailingList + * @package IXP\Console\Commands + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Init extends MailingList +{ + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'mailing-list:init + {list : Handle of the mailing list} + {--format=text : Response format - either text (default) or json)}'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Initialise a mailing list - see documentation'; + + + /** + * Mailing list initialisation script + * + * First sets a user preference for ALL users *WITHOUT* a mailing list sub for this list to unsub'd. + * + * Then takes a list of *existing* mailing list addresses from stdin and: + * - is a user does not exist with same email, skips + * - if a user does exist with same email, sets his mailing list preference + * + * NB: This function is NON-DESTRUCTIVE. It will *NOT* affect any users with *EXISTING* settings + * but set those without a setting to on / off as appropriate. + * + * @throws + */ + public function handle(): int + { + if( !config( 'mailinglists.enabled' ) ) { + die( "Mailing list functionality is disabled. See: https://docs.ixpmanager.org/latest/features/mailing-lists/\n" ); + } + + $ml = new ML( $this->argument('list') ); + + $stdin = fopen( "php://stdin","r" ); + /** @var Collection $addresses */ + $addresses = collect(); + + while( $address = strtolower( trim( fgets( $stdin ) ) ) ) { + if( !$addresses->contains( $address ) ) { + $addresses->add( $address ); + } + } + + fclose( $stdin ); + + $this->info( "Setting mailing list subscription for all users without a pre-existing subscription setting...\n" ); + + $result = $ml->init( $addresses ); + + if( $this->isVerbosityVerbose() || $this->option('format') === 'json' ) { + if( $this->option( 'format' ) === 'json' ) { + echo json_encode( $result, JSON_THROW_ON_ERROR ); + } else { + foreach( $result as $k => $v ) { + foreach( $v as $e ) { + $this->info( "{$k}: {$e}" ); + } + } + } + } + + echo "\n"; + return 0; + } +} \ No newline at end of file diff --git a/app/Console/Commands/MailingList/MailingList.php b/app/Console/Commands/MailingList/MailingList.php new file mode 100644 index 000000000..11bf9f402 --- /dev/null +++ b/app/Console/Commands/MailingList/MailingList.php @@ -0,0 +1,28 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Console\Commands\MailingList + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class SyncScript extends MailingList +{ + + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'mailing-list:sync-script + {--sh : Generate for shell commands rather than API}'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Generate a sample mailing list syncronisation script'; + + /** + * Mailing list initialisation script + * + * First sets a user preference for ALL users *WITHOUT* a mailing list sub for this list to unsub'd. + * + * Then takes a list of *existing* mailing list addresses from stdin and: + * - is a user does not exist with same email, skips + * - if a user does exist with same email, sets his mailing list preference + * + * NB: This function is NON-DESTRUCTIVE. It will *NOT* affect any users with *EXISTING* settings + * but set those without a setting to on / off as appropriate. + * + * @return mixed + */ + public function handle(): int + { + if( !config( 'mailinglists.enabled' ) ) { + die( "Mailing list functionality is disabled. See: https://docs.ixpmanager.org/latest/features/mailing-lists/\n" ); + } + + // do we have mailing lists defined? + if( !is_array( config( 'mailinglists.lists' ) ) || !count( config( 'mailinglists.lists' ) ) ) { + $this->error( "No valid mailing lists defined in config/mailinglist.php" ); + $this->info( "See: https://docs.ixpmanager.org/latest/features/mailing-lists/" ); + return -1; + } + + echo view( 'console/commands/mailing-list/sync-' . ( $this->option( 'sh' ) ? 'sh' : 'apiv4' ) ) + ->with( [ 'lists' => config( 'mailinglists.lists' ) ] ) + ->render(); + + return 0; + } +} \ No newline at end of file diff --git a/app/Console/Commands/PeeringDB/AsnLookup.php b/app/Console/Commands/PeeringDB/AsnLookup.php new file mode 100644 index 000000000..a8fc0c111 --- /dev/null +++ b/app/Console/Commands/PeeringDB/AsnLookup.php @@ -0,0 +1,68 @@ +warn( + str_repeat( '-', 70 ) + . "\nNo API key defined in .env for PeeringDB.\n\n" + . "See https://docs.peeringdb.com/howto/api_keys/ and set IXP_API_PEERING_DB_API_KEY in .env.\n\n" + . "Without an API key, only public information will be returned and PeeringDB request throttling will apply.\n" + . str_repeat( '-', 70 ) . "\n\n" + ); + } + + if( config('ixp_api.peeringDB.username') && config('ixp_api.peeringDB.password') ) { + $this->warn( + str_repeat( '-', 70 ) . "\n" + . "Username and password are set in .env for PeeringDB. This is deprecated and they should be replaced with an API key.\n\n" + . "See https://docs.peeringdb.com/howto/api_keys/ and set IXP_API_PEERING_DB_API_KEY in .env.\n" + . str_repeat( '-', 70 ) . "\n\n" + ); + } + + $pdb = app()->make( PeeringDb::class ); + + if( $net = $pdb->getNetworkByAsn( (integer)$this->argument('asn') ) ) { + echo $pdb->netAsAscii($net); + return 0; + } + + if( $pdb->status === 404 ) { + $this->line( $pdb->error ); + return 0; + } + + $this->error( $pdb->error ); + return -1; + } +} diff --git a/app/Console/Commands/RipeAtlas/CompleteRequests.php b/app/Console/Commands/RipeAtlas/CompleteRequests.php new file mode 100644 index 000000000..1e225c903 --- /dev/null +++ b/app/Console/Commands/RipeAtlas/CompleteRequests.php @@ -0,0 +1,96 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Console\Commands\RipeAtlas + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class CompleteRequests extends Command +{ + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'ripe-atlas:complete-requests'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Mark requests complete if all measurements are complete'; + + /** + * Execute the console command. + * + * @throws + */ + public function handle(): int + { + if( $this->isVerbosityVerbose() ) { + $this->info("---- COMPLETE REQUESTS START ----"); + } + + $ars = AtlasRun::whereNull( 'completed_at' )->get(); + + $bar = $this->output->createProgressBar( $ars->count() ); + $bar->start(); + + // find new requests: + if( $ars->isEmpty() ) { + if( $this->isVerbosityVerbose() ) { + $this->info("No open requests to process"); + } + return 0; + } + + $ars->each( function ( $ar ) use ( $bar ) { + if( CompleteRequestsJob::dispatchSync( $ar ) && $this->isVerbosityVerbose() ) { + $this->info("Marking request: Atals Run ID:{$ar->id} complete"); + } + $bar->advance(); + }); + + $bar->finish(); + + if( $this->isVerbosityVerbose() ) { + $this->info("\n---- COMPLETE REQUESTS STOP ----"); + } + return 0; + } +} \ No newline at end of file diff --git a/app/Console/Commands/RipeAtlas/CreateMeasurements.php b/app/Console/Commands/RipeAtlas/CreateMeasurements.php new file mode 100644 index 000000000..f6f48001c --- /dev/null +++ b/app/Console/Commands/RipeAtlas/CreateMeasurements.php @@ -0,0 +1,87 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Console\Commands\RipeAtlas + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class CreateMeasurements extends Command +{ + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'ripe-atlas:create-measurements'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Process new end user requests and queue measurements'; + + /** + * Execute the console command. + * + * @throws + */ + public function handle(): int + { + if( $this->isVerbosityVerbose() ) { + $this->info("---- CREATE MEASUREMENTS START ----"); + } + + $ars = AtlasRun::whereNull('started_at' )->get(); + + $bar = $this->output->createProgressBar( $ars->count() ); + $bar->start(); + + $ars->each( function( $ar ) use ( $bar ) { + CreateMeasurementsJob::dispatchSync( $ar ); + $bar->advance(); + }); + + $bar->finish(); + + if( $this->isVerbosityVerbose() ) { + $this->info("---- CREATE MEASUREMENTS STOP ----"); + } + + return 0; + } +} \ No newline at end of file diff --git a/app/Console/Commands/RipeAtlas/RunMeasurements.php b/app/Console/Commands/RipeAtlas/RunMeasurements.php new file mode 100644 index 000000000..c8c834119 --- /dev/null +++ b/app/Console/Commands/RipeAtlas/RunMeasurements.php @@ -0,0 +1,105 @@ + + * @author Yann Robin + * @category RipeAtlas + * @package IXP\Console\Commands + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class RunMeasurements extends Command +{ + + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'ripe-atlas:run-measurements'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Run queued measurements'; + + /** + * Execute the console command. + * + * @throws + */ + public function handle(): int + { + $ams = AtlasMeasurement::select('am.*' ) + ->from( 'atlas_measurements AS am' ) + ->LeftJoin( 'atlas_runs AS ar' , 'am.run_id', 'ar.id' ) + ->where( 'ar.scheduled_at', '<=', now()->toDateTimeString() ) + ->whereNull( 'am.atlas_id' )->get(); + + if( $ams->isEmpty() ) { + if( $this->isVerbosityVerbose() ) { + $this->info("No queued measurements to process"); + } + return 0; + } + + if( $this->isVerbosityVerbose() ) { + $this->info("---- RUN MEASUREMENTS START ----"); + } + + $bar = $this->output->createProgressBar( $ams->count() ); + $bar->start(); + + $atlasRuns = []; + + foreach( $ams as $am ){ + $atlasRuns[ $am->run_id ] = $am->atlasRun(); + RunMeasurementsJob::dispatchSync( $am ); + $bar->advance(); + } + + foreach( $atlasRuns as $run ){ + $run->update( [ 'started_at' => now() ] ); + } + + $bar->finish(); + + if( $this->isVerbosityVerbose() ) { + $this->info("\n---- RUN MEASUREMENTS STOP ----"); + } + + return 0; + } +} \ No newline at end of file diff --git a/app/Console/Commands/RipeAtlas/StopAllMeasurements.php b/app/Console/Commands/RipeAtlas/StopAllMeasurements.php new file mode 100644 index 000000000..5ffbcc2c9 --- /dev/null +++ b/app/Console/Commands/RipeAtlas/StopAllMeasurements.php @@ -0,0 +1,101 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Console\Commands\RipeAtlas + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class StopAllMeasurements extends Command +{ + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'ripe-atlas:stop-all-measurements'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Stops all outstanding measurements for the API key in use'; + + /** + * Execute the console command. + * + * @throws + */ + public function handle(): int + { + if( $this->isVerbosityVerbose() ) { + $this->info("---- STOP MEASUREMENTS START ----"); + } + + $measurements = App::make(ApiCall::class )->myAtlasMeasurements(); + + if( $measurements[ 'error' ] === true ) { + $this->error( $measurements[ 'response' ]->error->detail ); + return 0; + } + + // get all measurements + if( $measurements = $measurements[ 'response' ] ) { + $this->info( $measurements->count . " on going measurements found"); + $bar = $this->output->createProgressBar( $measurements->count ); + $bar->start(); + + foreach( $measurements->results as $am ) { + StopAllMeasurementsJob::dispatchSync( $am->id ); + $this->info("Stop requested for {$am->id}"); + $bar->advance(); + } + $bar->finish(); + + } else { + $this->error('Could not query RIPE Atlas API'); + } + + if( $this->isVerbosityVerbose() ) { + $this->info("\n---- STOP MEASUREMENTS STOP ----"); + } + + return 0; + } +} \ No newline at end of file diff --git a/app/Console/Commands/RipeAtlas/UpdateMeasurements.php b/app/Console/Commands/RipeAtlas/UpdateMeasurements.php new file mode 100644 index 000000000..84817feb1 --- /dev/null +++ b/app/Console/Commands/RipeAtlas/UpdateMeasurements.php @@ -0,0 +1,88 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Console\Commands\RipeAtlas + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class UpdateMeasurements extends Command +{ + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'ripe-atlas:update-measurements'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Update any pending atlas measurements'; + + /** + * Execute the console command. + * + * @throws + */ + public function handle(): int + { + if( $this->isVerbosityVerbose() ) { + $this->info("---- UPDATE MEASUREMENTS START ----"); + } + + $ams = AtlasMeasurement::whereNull( 'atlas_stop' )->get(); + + $bar = $this->output->createProgressBar( $ams->count() ); + + $bar->start(); + + // find uncompleted measurements: + $ams->each( function( $am ) use( $bar ) { + UpdateMeasurementsJob::dispatchSync( $am ); + $bar->advance(); + }); + + $bar->finish(); + + if( $this->isVerbosityVerbose() ) { + $this->info("\n---- UPDATE MEASUREMENTS STOP ----"); + } + return 0; + } +} \ No newline at end of file diff --git a/app/Console/Commands/RipeAtlas/UpdateProbes.php b/app/Console/Commands/RipeAtlas/UpdateProbes.php new file mode 100644 index 000000000..c708e352c --- /dev/null +++ b/app/Console/Commands/RipeAtlas/UpdateProbes.php @@ -0,0 +1,126 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Console\Commands\RipeAtlas + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class UpdateProbes extends Command +{ + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'ripe-atlas:update-probes + {customer? : Customer ASN, ID or shortname (in that order). Otherwise all customers.}'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Update the Ripe Atlas probes for all customers (or a given customer by ASN/ID/shortname)'; + + /** + * Execute the console command. + * + * @throws + */ + public function handle(): int + { + if( $this->isVerbosityVerbose() ) { + $this->info("---- PROBES UPDATE START ----"); + } + + $customers = $this->resolveCustomers(); + + $bar = $this->output->createProgressBar( count( $customers ) ); + $bar->start(); + + foreach( $customers as $c ){ + UpdateProbesJob::dispatchSync( $c ); + $bar->advance(); + } + + $bar->finish(); + + if( $this->isVerbosityVerbose() ) { + $this->info("---- PROBES UPDATE STOP ----"); + } + + return 0; + } + + + /** + * Returns all customers or, if specified on the command line, a specific customer + * + * @return Collection|array + * + * @psalm-return Collection|list{mixed} + */ + protected function resolveCustomers(): array|Collection + { + $cust = $this->argument('customer'); + + // if no customer, return all appropriate ones: + if( !$cust ) { + return Customer::CurrentActive( true, false, false )->get(); + } + + // assume ASN first: + if( is_numeric( $cust )) { + $c = Customer::where( 'autsys', $cust )->first(); + if ( !$c ) { + // then ID: + $c = Customer::find( $cust ); + } + } else { + // then check shortname: + $c = Customer::where( 'shortname', $cust )->first(); + } + + if($c) { + return [ $c ]; + } + + $this->error( "Could not find a customer matching asn/id/shortname: " . $cust ); + exit( 0 ); + } +} \ No newline at end of file diff --git a/app/Console/Commands/Rir/GenerateObject.php b/app/Console/Commands/Rir/GenerateObject.php new file mode 100644 index 000000000..7060e3848 --- /dev/null +++ b/app/Console/Commands/Rir/GenerateObject.php @@ -0,0 +1,117 @@ + + * @author Barry O'Donovan + * @package IXP\Console\Commands\Rir + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class GenerateObject extends Command +{ + /** + * The name and signature of the console command. + * + * @var string + */ + + protected $signature = 'rir:generate-object + {object : The RIR object template to use} + {--send-email : Rather than printing to screen, sends and email for updating a RIR automatically} + {--force : Send email even if it matches the cached version} + {--to= : The email address to send the object to (if not specified then uses IXP_API_RIR_EMAIL_TO)} + {--from= : The email address from which the email is sent (if not specified, tries IXP_API_RIR_EMAIL_FROM and then defaults to IDENTITY_EMAIL)}'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'This command will generate and display a RIR object (and optionally send by email)'; + + /** + * Execute the console command. + * + * @throws + */ + public function handle(): int + { + $gen = new RirGenerator( $this->argument ('object' ) ); + + $obj = $gen->generate(); + + $key = 'rir-object-' . $this->argument ('object' ); + $cobj = Cache::store('file' )->get( $key ); + + if( $this->option( "send-email" ) && ( $this->option( "force" ) || $obj !== $cobj ) ) { + if( !$this->option( "to" ) && !config( 'ixp_api.rir.email.to' ) ){ + $this->error( "Please specify the TO email address" ); + exit( -1 ); + } + + Mail::raw( $obj, function( $m ) { + $m->to( $this->checkEmail( 'to', $this->option( "to" ) ?? config( 'ixp_api.rir.email.to' ) ) ) + ->from( $this->checkEmail( 'from', ( $this->option( "from" ) ?: config( 'ixp_api.rir.email.from' ) ) ?: config( 'mail.from.address' ) ) ) + ->subject( "Changes to {$this->argument ('object' )} via IXP Manager" ); + } ); + + if( !$this->isVerbosityQuiet() ) { + $this->info( "Email sent." ); + } + + if( $obj !== $cobj ) { + Cache::store('file')->forever( $key, $obj ); + } + + } else if( !$this->option( "send-email" ) ) { + echo $obj; + } + + return 0; + } + + /** + * @param string $w + * @param string $e + */ + private function checkEmail( string $w, string $e ): string + { + if( filter_var( $e, FILTER_VALIDATE_EMAIL ) ) { + return $e; + } + + $this->error( "Invalid {$w} email address: {$e}" ); + exit( -1 ); + } +} \ No newline at end of file diff --git a/app/Console/Commands/Router/FilteredPrefixes.php b/app/Console/Commands/Router/FilteredPrefixes.php new file mode 100644 index 000000000..898fe8ccb --- /dev/null +++ b/app/Console/Commands/Router/FilteredPrefixes.php @@ -0,0 +1,133 @@ + + * @author Yann Robin + * @category Router + * @package IXP\Console\Commands + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class FilteredPrefixes extends Command +{ + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'router:filtered-prefixes + {customer? : Customer ASN, ID or shortname (in that order). Otherwise all customers.}'; + + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Generate route server/collector/etc. configurations'; + + + /** + * Execute the console command. + * + * @return mixed + */ + public function handle(): int + { + $customer = $this->resolveCustomer(); + + $this->info( "Checking route server filtering for " . $customer->abbreviatedName . ". Please wait..." ); + + FetchFilteredPrefixesForCustomer::dispatchSync( $customer ); + $filteredPrefixes = Cache::get( 'filtered-prefixes-' . $customer->id ); + + if( $filteredPrefixes === [] ) { + $this->info( "No filtered prefixes found" ); + } + + $headers = [ "Prefix", "Reason", "Routers Where Filtered" ]; + $prefixes = []; + $bfe = new BirdFoilExtension(); + + foreach( $filteredPrefixes as $network => $p ) { + $fp[0] = $network; + + $reason = ''; + foreach( $p['reasons'] as $r ) { + $bfe_couple = $bfe->translateBgpFilteringLargeCommunity( substr( $r, strpos( $r, ':' ) ) ); + $reason .= ( $bfe_couple ? $bfe_couple[0] : 'UNKNOWN' ) . ' '; + } + $fp[1] = trim( $reason ); + $fp[2] = implode( ' ', array_keys( $p['age'] ) ); + $prefixes[] = $fp; + } + + $this->table( $headers, $prefixes ); + + return 0; + } + + + /** + * Returns all customers or, if specified on the command line, a specific customer + * + * @return Customer + */ + protected function resolveCustomer(): Customer + { + $custarg = $this->argument('customer'); + + // assume ASN first: + if( is_numeric( $custarg )) { + $c = Customer::where( 'autsys', $custarg )->first(); + if(!$c) { + // then ID: + $c = Customer::find( $custarg ); + } + } else { + // then check shortname: + $c = Customer::where( 'shortname', $custarg )->first(); + } + + if ($c) { + return $c; + } + + $this->error( "Could not find a customer matching asn/id/shortname: " . $custarg ); + + exit(1); + } +} \ No newline at end of file diff --git a/app/Console/Commands/Router/GenerateConfiguration.php b/app/Console/Commands/Router/GenerateConfiguration.php new file mode 100644 index 000000000..efda253c6 --- /dev/null +++ b/app/Console/Commands/Router/GenerateConfiguration.php @@ -0,0 +1,84 @@ + + * @author Yann Robin + * @category Router + * @package IXP\Console\Commands + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class GenerateConfiguration extends Command +{ + + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'router:generate-configuration + {handle : Router handle (from "Routers" admin action) to use}'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Generate route server/collector/etc. configurations'; + + + /** + * Execute the console command. + * + * @return mixed + */ + public function handle() + { + if( !( $router = Router::where( 'handle', $this->argument('handle') )->first() ) ){ + $this->error( "Unknown router handle" ); + return -1; + } + + /** @psalm-suppress InvalidArgument - render() casts as a string, no issue here */ + echo ( new RouterConfigurationGenerator( $router ) )->render(); + + /** @psalm-suppress UndefinedConstant - LARAVEL_START defined in artisan command line tool */ + Log::info( sprintf( "Generated router configuration for %s and used %0.1f MB ( %0.1f MB real) of memory in %0.3f seconds.", + $router->handle, memory_get_peak_usage()/1024/1024, memory_get_peak_usage( true )/1024/1024, + microtime(true) - LARAVEL_START ) + ); + + return 0; + } +} \ No newline at end of file diff --git a/app/Console/Commands/Router/ResetAllUpdateTimestampsCommand.php b/app/Console/Commands/Router/ResetAllUpdateTimestampsCommand.php new file mode 100644 index 000000000..91ae4c882 --- /dev/null +++ b/app/Console/Commands/Router/ResetAllUpdateTimestampsCommand.php @@ -0,0 +1,22 @@ +update( [ + 'last_update_started' => null, + 'last_updated' => null, + ]); + } +} diff --git a/app/Console/Commands/SetupWizard.php b/app/Console/Commands/SetupWizard.php new file mode 100644 index 000000000..9a04b6b2f --- /dev/null +++ b/app/Console/Commands/SetupWizard.php @@ -0,0 +1,396 @@ + + * @author Barry O'Donovan + * @package IXP\Console\Commands + * @copyright Copyright (C) 2009 - 2025 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class SetupWizard extends Command +{ + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'ixp-manager:setup-wizard' + . ' {--ixp-shortname= : The short name of the IXP (e.g. DIX)}' + . ' {--admin-name= : The name of the admin user}' + . ' {--admin-username= : The username of the admin user}' + . ' {--admin-password= : The password for the admin user (if unset, taken from the IXP_SETUP_ADMIN_PASSWORD environment variable or random value assigned)}' + . ' {--admin-email= : The email of the admin user}' + . ' {--asn= : The ASN of your IXP}' + . ' {--echo-password : Echo the password to the console}' + . ' {--skip-confirm : Skip the confirmation prompt}' + . ' {--force : Force the installation without validation}'; + + /** + * The console command description. + * @var string + */ + protected $description = "Create initial database objects for IXP Manager"; + + + protected array $ixpdata = [ + 'ixp-name' => [ + 'config' => 'identity.name', + 'env' => 'IXP_NAME', + 'value' => null, + ], + 'ixp-legalname' => [ + 'config' => 'identity.legalname', + 'env' => 'IXP_LEGALNAME', + 'value' => null, + ], + 'ixp-shortname' => [ + 'default' => 'IXP', + 'prompt' => 'Enter the short name of your IXP', + 'ask' => true, + 'value' => null, + ], + 'admin-name' => [ + 'default' => 'Joe Bloggs', + 'prompt' => 'Enter the full name of the admin user', + 'ask' => true, + 'value' => null, + ], + 'admin-username' => [ + 'default' => 'jbloggs', + 'prompt' => 'Enter the username of the admin user', + 'ask' => true, + 'value' => null, + ], + 'admin-password' => [ + 'default' => null, + 'prompt' => 'Enter the password of the admin user', + 'ask' => true, + 'value' => null, + ], + 'admin-email' => [ + 'default' => 'joebloggs@example.com', + 'prompt' => 'Enter the email of the admin user', + 'ask' => true, + 'value' => null, + ], + 'asn' => [ + 'default' => '65535', + 'prompt' => 'Enter the ASN of your IXP', + 'ask' => true, + 'value' => null, + ], + 'ixp-email' => [ + 'config' => 'identity.support_email', + 'prompt' => 'Enter the support email of the IXP', + 'env' => 'IXP_SUPPORT_EMAIL', + 'value' => null, + ], + 'ixp-phone' => [ + 'config' => 'identity.support_phone', + 'prompt' => 'Enter the support phone number of the IXP', + 'env' => 'IXP_SUPPORT_PHONE', + 'value' => null, + ], + 'ixp-billing-email' => [ + 'config' => 'identity.billing_email', + 'prompt' => 'Enter the billing email of the IXP', + 'env' => 'IXP_BILLING_EMAIL', + 'value' => null, + ], + 'ixp-billing-phone' => [ + 'config' => 'identity.billing_phone', + 'prompt' => 'Enter the billing phone number of the IXP', + 'env' => 'IXP_BILLING_PHONE', + 'value' => null, + ], + 'ixp-url' => [ + 'config' => 'identity.corporate_url', + 'prompt' => 'Enter the web address of the IXP', + 'env' => 'IXP_CORPORATE_URL', + 'value' => null, + ], + ]; + + + /** + * Execute the console command. + * + * @return int + * @throws \Throwable + */ + public function handle(): int + { + + // The premise of this script is to create the necessary database objects for IXP Manager + // after a new installation. + // + // One key element is that a number of settings, which the script will inform and ask for + // confirmation of, are set in the .env file. + // + // A handful of others are prompted from the user. In particular, the detaails for the + // first admin user are asked for. + // + // This admin user's password should preferably be set in the IXP_SETUP_ADMIN_PASSWORD + // environment variable. + + + + if (Customer::count() > 0) { + $this->error('IXP Manager has already been setup. Exiting.'); + return 1; + } + + $this->line( self::BANNER ); + + $this->line("Welcome to the IXP Manager setup wizard!\n\n"); + + // print a warning to inform the user that some options are taken from .env + if( !$this->confirmDotEnv() ) { + return 1; + } + + $data = $this->gatherData(); + + + try { + + DB::beginTransaction(); + + Infrastructure::create( [ + 'name' => $data['ixp-name'], + 'shortname' => $data['ixp-shortname'], + 'isPrimary' => 1, + 'created_at' => Carbon::now(), + 'updated_at' => Carbon::now(), + ] ); + + $billingDetail = CompanyBillingDetail::create( [ + 'billingContatName' => $data['ixp-shortname'] . ' Billing Team', + 'billingEmail' => $data['ixp-billing-email'], + 'billingTelephone' => $data['ixp-billing-phone'], + 'invoiceMethod' => CompanyBillingDetail::INVOICE_METHOD_EMAIL, + 'billingFrequency' => CompanyBillingDetail::BILLING_FREQUENCY_NOBILLING, + 'created_at' => Carbon::now(), + 'updated_at' => Carbon::now(), + ] ); + + $registrationDetail = CompanyRegisteredDetail::create( [ + 'registeredName' => $data['ixp-legalname'], + 'created_at' => Carbon::now(), + 'updated_at' => Carbon::now(), + ] ); + + $cust = Customer::create( [ + 'name' => $data['ixp-name'], + 'type' => Customer::TYPE_INTERNAL, + 'shortname' => $data['ixp-shortname'], + 'autsys' => $data['asn'], + 'maxprefixes' => 1, + 'peeringemail' => $data['ixp-email'], + 'peeringpolicy' => Customer::PEERING_POLICY_MANDATORY, + 'nocphone' => $data['ixp-phone'], + 'noc24hphone' => $data['ixp-phone'], + 'nocemail' => $data['ixp-email'], + 'nochours' => Customer::NOC_HOURS_24x7, + 'nocwww' => $data['ixp-url'], + 'corpwww' => $data['ixp-url'], + 'datejoin' => Carbon::now(), + 'status' => Customer::STATUS_NORMAL, + 'activepeeringmatrix' => true, + 'company_registered_detail_id' => $registrationDetail->id, + 'company_billing_details_id' => $billingDetail->id, + 'abbreviatedName' => $data['ixp-shortname'], + 'isReseller' => false, + 'created_at' => Carbon::now(), + 'updated_at' => Carbon::now(), + ] ); + + $cust->contacts()->create( [ + 'name' => $data['admin-name'], + 'email' => $data['admin-email'], + 'created_at' => Carbon::now(), + 'updated_at' => Carbon::now(), + ] ); + + $user = new User; + $user->name = $data['admin-name']; + $user->username = $data['admin-username']; + $user->password = password_hash( $data['admin-password'], PASSWORD_BCRYPT, [ 'cost' => config( 'hashing.bcrypt.rounds' ) ] ); + $user->email = $data['admin-email']; + $user->created_at = Carbon::now(); + $user->updated_at = Carbon::now(); + $user->save(); + + CustomerToUser::create( [ + 'customer_id' => $cust->id, + 'user_id' => $user->id, + 'privs' => User::AUTH_SUPERUSER, + 'created_at' => Carbon::now(), + 'updated_at' => Carbon::now(), + ] ); + + DB::commit(); + + } catch (\Exception $e) { + $this->error('A database error occurred while setting up IXP Manager:' . $e->getMessage()); + return 4; + } + + $this->info('IXP Manager has been setup successfully!'); + + return 0; + } + + + + public const string BANNER = " + + _____ ______ __ __ +|_ _\ \/ / _ \ | \/ | __ _ _ __ __ _ __ _ ___ _ __ + | | \ /| |_) | | |\/| |/ _` | '_ \ / _` |/ _` |/ _ \ '__| + | | / \| __/ | | | | (_| | | | | (_| | (_| | __/ | +|___/_/\_\_|_ |_| |_|\__,_|_| |_|\__,_|\__, |\___|_|_ +/ ___| ___| |_ _ _ _ _\ \ / (_)____|___/_ __ __| | +\___ \ / _ \ __| | | | '_ \ \ /\ / /| |_ / _` | '__/ _` | + ___) | __/ |_| |_| | |_) \ V V / | |/ / (_| | | | (_| | +|____/ \___|\__|\__,_| .__/ \_/\_/ |_/___\__,_|_| \__,_| + |_| + +"; + + private function confirmDotEnv(): bool + { + $this->alert( "The following options are taken directly from the .env file:" ); + + foreach( $this->ixpdata as $setting => $attributes ) { + + if( !isset( $attributes[ 'ask' ] ) || !$attributes[ 'ask' ] ) { + $this->line( "\t{$attributes['env']} => " . config( $attributes[ 'config' ] ) ); + } + + } + + $this->newLine(2); + + if( $this->option( 'force' ) ) { + return true; + } + + return $this->confirm( 'Do you want to continue?' ); + } + + private function gatherData(): array { + $table = []; + $data = []; + + // gather data + foreach( $this->ixpdata as $setting => $attributes ) { + + if( !isset( $attributes[ 'ask' ] ) || !$attributes[ 'ask' ] ) { + $this->ixpdata[$setting][ 'value' ] = config( $attributes[ 'config' ] ); + + } elseif( $this->option( $setting ) ) { + $this->ixpdata[$setting][ 'value' ] = $this->option( $setting ); + + } else { + + if( $setting === 'admin-password' ) { + + if( getenv( 'IXP_SETUP_ADMIN_PASSWORD' ) !== false ) { + // Do not use laravel's `env()` because it reads the .env file. + $this->ixpdata[$setting][ 'value' ] = getenv( 'IXP_SETUP_ADMIN_PASSWORD' ); + + // Unset the variable as soon as we read it to reduce the risk of it leaking. + putenv( 'IXP_SETUP_ADMIN_PASSWORD' ); + } else { + $this->ixpdata[$setting][ 'value' ] = str()->random( 12 ); + } + + } else { + + $this->ixpdata[$setting][ 'value' ] = ask( $attributes[ 'prompt' ] . ' [' . $attributes[ 'default' ] . '] ' ); + + if( !$this->ixpdata[$setting][ 'value' ] ) { + $this->ixpdata[$setting][ 'value' ] = $attributes[ 'default' ]; + } + } + } + + $data[ $setting ] = $this->ixpdata[$setting][ 'value' ]; + + if( $setting !== 'admin-password' || $this->option( 'echo-password' ) ) { + $table[] = [ $setting, $this->ixpdata[$setting][ 'value' ] ]; + } + + } + + $this->table( ['Setting', 'Value'], $table ); + + $validator = \Validator::make($data, [ + 'asn' => 'required|integer|between:1,4294967295', + 'ixp-name' => 'required|string', + 'ixp-legalname' => 'required|string', + 'ixp-shortname' => 'required|string', + 'admin-name' => 'required|string', + 'admin-username' => 'required|string', + 'admin-email' => 'required|email', + 'admin-password' => 'required|string|min:10', + 'ixp-phone' => 'required|string', + 'ixp-email' => 'required|email', + 'ixp-billing-phone' => 'required|string', + 'ixp-billing-email' => 'required|email', + 'ixp-url' => 'required|url', + ]); + + if ( !$this->option('force') && $validator->fails()) { + $this->error('The following errors occurred:'); + foreach ($validator->errors()->all() as $error) { + $this->error("\t" . $error); + } + exit(2); + } + + if( !$this->option('force') && !$this->option( 'skip-confirm' ) ) { + if( !$this->confirm( 'Is this information correct, and do you want to continue to create the database objects?' ) ) { + $this->error( 'No confirmation was given. Exiting.' ); + exit(3); + } + } + + return $data; + } + +} \ No newline at end of file diff --git a/app/Console/Commands/Switches/ReindexIfIndex.php b/app/Console/Commands/Switches/ReindexIfIndex.php new file mode 100644 index 000000000..0e7adeb36 --- /dev/null +++ b/app/Console/Commands/Switches/ReindexIfIndex.php @@ -0,0 +1,112 @@ + + * @author Yann Robin + * @package IXP\Console\Commands + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class ReindexIfIndex extends Command +{ + /** + * The name and signature of the console command. + * + * @var string + */ + + protected $signature = 'switch:reindex-ifindex + {switch : The name of the switch} + {--noflush : If specified no modification will be made to the database}'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Reindex switch ports\' ifIndex based on ifName'; + + /** + * Execute the console command. + * + * @throws + * + * @psalm-return -2|-1|0 + */ + public function handle(): int + { + if( ! ( $s = Switcher::where( 'name', $this->argument('switch') )->first() ) ) { + $this->error( "ERR: No switch found with name: " . $this->argument('switch' ) ); + return -1; + } + + try { + $host = new SNMP( $s->hostname, $s->snmppasswd ); + + // array in ifIndex => ifName format: + $snmpports = $host->useIface()->names(); + + foreach( $s->switchPorts as $sp ) { + foreach( $snmpports as $ifIndex => $ifName ) { + if( $sp->ifName !== $ifName ) { + continue; + } + + if( $sp->ifName === $ifIndex ) { + $this->comment( " - {$sp->ifName} unchanged, ifIndex remains the same"); + } else { + $this->info(" - {$sp->ifName} ifIndex changed from {$sp->ifIndex} to {$ifIndex}"); + $sp->ifIndex = $ifIndex; + } + + unset( $snmpports[$ifIndex] ); + break; + } + } + + if( $this->option( 'noflush', false ) ) { + $this->error( "\n*** --noflush parameter set - NO CHANGES MADE TO DATABASE" ); + } else{ + $sp->save(); + } + } catch( Exception $e ) { + $this->error("ERROR: OSS_SNMP exception polling switch ports for {$s->getName()} by SNMP"); + $this->error( $e->getMessage() ); + return -2; + } + return 0; + } +} \ No newline at end of file diff --git a/app/Console/Commands/Switches/SnmpPoll.php b/app/Console/Commands/Switches/SnmpPoll.php new file mode 100644 index 000000000..a983e41e2 --- /dev/null +++ b/app/Console/Commands/Switches/SnmpPoll.php @@ -0,0 +1,122 @@ + + * @author Barry O'Donovan + * @package IXP\Console\Commands + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class SnmpPoll extends Command +{ + /** + * The name and signature of the console command. + * + * @var string + */ + + protected $signature = 'switch:snmp-poll + {switch? : The name of the switch, if not name specified the command will loop over all switches} + {--nosave : If specified no modification will be made to the database} + {--log : Output detailed polling information to the log}'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Poll and update switches and switch ports via SNMP'; + + /** + * Execute the console command. + * + * @throws + * + * @psalm-return -1|0 + */ + public function handle(): int + { + if( $this->argument('switch') ) { + if( ! ( $switches = Switcher::where( 'name', $this->argument('switch') )->get() ) ) { + $this->error( "ERR: No switch found with name: " . $this->argument('switch' ) ); + return -1; + } + } else { + $switches = Switcher::where( 'active', true )->where( 'poll', true )->get(); + } + + if( count( $switches ) ){ + foreach( $switches as $s ) { + if( !$s->snmppasswd || trim( $s->snmppasswd ) === '' ) { + if( !$this->isVerbosityQuiet() ) { + $this->info( "Skipping {$s->name} as no SNMP password set" ); + } + continue; + } + + if( !$this->isVerbosityQuiet() ) { + if( !$s->lastPolled ) { + $this->info( "First time polling {$s->name} with SNMP request to {$s->hostname}" ); + } else { + $this->info( "Polling {$s->name} with SNMP request to {$s->hostname}" ); + } + } + + try { + $sPolled = false; + $host = new SNMP( $s->hostname, $s->snmppasswd ); + $s->snmpPoll( $host, $this->option( 'log', false ), $this->option( 'nosave', false ) ); + $sPolled = true; + + $result = false; + $s->snmpPollSwitchPorts( $host, $this->option( 'log', false ), $result , $this->option( 'nosave', false ) ); + + if( $this->option( 'nosave', false ) ){ + $this->warn( ' *** --nosave parameter set - NO CHANGES MADE TO DATABASE' ); + } + } catch( Exception $e ) { + if( $sPolled ){ + $this->error("ERROR: OSS_SNMP exception polling switch {$s->name} by SNMP"); + } else { + $this->error("ERROR: OSS_SNMP exception polling switch ports for {$s->name} by SNMP"); + } + + } + } + } + return 0; + } +} \ No newline at end of file diff --git a/app/Console/Commands/Upgrade/Customer2Users.php b/app/Console/Commands/Upgrade/Customer2Users.php new file mode 100644 index 000000000..dc1f92153 --- /dev/null +++ b/app/Console/Commands/Upgrade/Customer2Users.php @@ -0,0 +1,111 @@ + + * @author Barry O'Donovan + * @package IXP\Console\Commands\Upgrade + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Customer2Users extends IXPCommand +{ + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'update:customer2users'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Migrate customer/user from 1:m to n:m (part of the upgrade to V5.0.0 process)'; + + /** + * Execute the console command. + * + * Transfers data from the table 'customer' and 'user' to the table 'customer_to_users' + * + * @return int|null + * + * @throws + * + * @psalm-return 1|null + */ + public function handle() + { + echo "\n\n"; + $this->warn( "ONLY RUN ONCE AND ONLY WHEN UPGRADING TO IXP Manager v5.0.0 from v4.9.x" ); + echo "\n"; + $this->warn( "THIS WILL TRUNCATE THE customer:user n:m TABLE - meaning any users created after upgrading to v5.0.0 will be unlinked from their customers." ); + + if( !$this->confirm( "\nThis command will restructure the customer/user data from 1:m to n:m.\n\n" + ."Generally, this command should only ever be run once and only when migrating to V5.0.0.\n\n" + . 'Are you sure you wish to proceed? ' ) ) { + return 1; + } + + // Delete all the rows from the table Customer2User + DB::table( 'customer_to_users' )->delete(); + + $this->info( 'The customer_to_users table has been truncated' ); + + $this->info( 'Migration in progress, please wait...' ); + + $users = User::all(); + + $bar = $this->output->createProgressBar( $users->count() ); + $bar->start(); + + foreach( $users as $u ) { + // create the new CustomerToUserEntity entity with the information of the current User and Customer table entries + $c2u = new CustomerToUser; + $c2u->user_id = $u->id; + $c2u->customer_id = $u->custid; + $c2u->privs = $u->privs; + $c2u->last_login_date = null; + $c2u->last_login_from = null; + $c2u->extra_attributes = [ "created_by" => [ "type" => "migration-script" ] ]; + $c2u->save(); + + UserLoginHistory::where( 'user_id', $u->id )->update( ['customer_to_user_id' => $c2u->id ] ); + + $bar->advance(); + } + + $bar->finish(); + echo "\n\n"; + $this->info( 'Migration completed successfully' ); + } +} \ No newline at end of file diff --git a/app/Console/Commands/Upgrade/MigrateL2Addresses.php b/app/Console/Commands/Upgrade/MigrateL2Addresses.php new file mode 100644 index 000000000..e243e34b5 --- /dev/null +++ b/app/Console/Commands/Upgrade/MigrateL2Addresses.php @@ -0,0 +1,122 @@ + + * @author Barry O'Donovan + * @package IXP\Console\Commands\Upgrade + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class MigrateL2Addresses extends IXPCommand +{ + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'l2addresses:populate'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'This command will clear the layer2address table and then copy addresses from the read-only macaddress table.'; + + /** + * Execute the console command. + * + * Transfers data from the table 'macaddress' to the table 'layer2address' + * + * @return mixed + */ + public function handle() + { + if( !$this->confirm( 'Are you sure you wish to proceed? This command will CLEAR the layer2address table and then copy ' + . 'addresses from the read-only macaddress table. Generally, this command should only ever be run once when initially ' + . 'populating the new table.' ) ) { + return 1; + } + + // Delete all the rows from the table Layer2Address + DB::table( 'l2address' )->truncate(); + $this->info( 'The layer2address table has been truncated' ); + + $this->info( 'Migration in progress, please wait...' ); + + // get all the entries form the macaddress table + DB::table( 'macaddress' )->orderBy( 'virtualinterfaceid' )->chunk( 100, function( $listMacAddresses ) { + foreach( $listMacAddresses as $mac) { + $vi = VirtualInterface::find( $mac->virtualinterfaceid ); + $cnt = 0; + foreach( $vi->vlanInterfaces as $vli ) { + // Ensure the MAC address is unique for this LAN: + $forVlan = Layer2Address::from( 'l2address AS l' ) + ->join( 'vlaninterface AS vli', 'vli.id', 'l.vlan_interface_id' ) + ->join( 'vlan AS v', 'v.id', 'vli.vlanid' ) + ->where( 'mac' , $mac->mac ) + ->where( 'v.id', $vli->vlanid ) + ->get(); + + if( $forVlan->count() ) { + $this->alert( 'Could not add additional instance of ' . $mac->mac . ' for ' + . $vi->customer->name . ' with virtual interface: ' . route('virtual-interface@edit', [ 'vi' => $vi->id ] ) + . ' as it already exists in this Vlan ' . $vli->vlan->name + ); + continue; + } + + // create the new Layer2Address entity with the information of the current macaddress table entry + Layer2Address::create([ + 'mac' => $mac->mac, + 'vlan_interface_id' => $vli->id, + 'firstseen' => now(), + ]); + $cnt++; + } + + // if you create more than one layer2address for a virtualinterface, let the user know + if( $cnt > 1 ) { + $this->alert( 'Created >1 layer2address for ' . $vi->customer->name . ' with virtual interface: ' + . route('virtual-interface@edit', [ 'vi' => $vi->id ] ) ); + } + } + }); + + $this->info( 'Also consider checking your database with: select mac, count(mac) as c from l2address group by mac having count(mac) > 1;' ); + $this->info( 'Migration completed successfully' ); + } +} \ No newline at end of file diff --git a/app/Console/Commands/Upgrade/PromoteCustUser.php b/app/Console/Commands/Upgrade/PromoteCustUser.php new file mode 100644 index 000000000..d7074158e --- /dev/null +++ b/app/Console/Commands/Upgrade/PromoteCustUser.php @@ -0,0 +1,102 @@ + + * @author Barry O'Donovan + * @package IXP\Console\Commands\Upgrade + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class PromoteCustUser extends IXPCommand +{ + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'update:promote-custusers'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Promote users with CustUser privilege to CustAdmin (part of the upgrade to V5.0.0 process)'; + + /** + * Execute the console command. + * + * Delete data from the table 'User' + * + * @throws + * + * @psalm-return 0|1 + */ + public function handle(): int + { + echo "\n\n"; + $this->warn( "ONLY RUN ONCE AND ONLY WHEN UPGRADING TO IXP Manager v5.0.0 from v4.9.x" ); + echo "\n"; + $this->warn( "THIS HAS TO BE RUN AFTER THE COMMAND: update:remove-custadmins" ); + echo "\n"; + $this->warn( "THIS WILL PROMOTE ALL the CUSTUSER users to CUSTADMIN users." ); + + if( !$this->confirm( "\nThis command will promote all the CustUser users to CustAdmin users.\n\n" + ."Generally, this command should only ever be run once and only when migrating to V5.0.0.\n\n" + . 'Are you sure you wish to proceed? ' ) ) { + return 1; + } + + $this->info( 'Migration in progress, please wait...' ); + + $C2UCustUser = CustomerToUser::where( 'privs', User::AUTH_CUSTUSER )->get(); + + $bar = $this->output->createProgressBar( $C2UCustUser->count() ); + $bar->start(); + + foreach( $C2UCustUser as $c2u ) { + // Changing user privilege + $c2u->privs = User::AUTH_CUSTADMIN; + $c2u->save(); + + $bar->advance(); + } + + $bar->finish(); + echo "\n\n"; + $this->info( 'Migration completed successfully' ); + + return 0; + } +} \ No newline at end of file diff --git a/app/Console/Commands/Upgrade/RemoveCustAdmin.php b/app/Console/Commands/Upgrade/RemoveCustAdmin.php new file mode 100644 index 000000000..496cc6a5c --- /dev/null +++ b/app/Console/Commands/Upgrade/RemoveCustAdmin.php @@ -0,0 +1,115 @@ + + * @author Barry O'Donovan + * @package IXP\Console\Commands\Upgrade + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class RemoveCustAdmin extends IXPCommand +{ + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'update:remove-custadmins'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Remove all users with CustAdmin privilege (part of the upgrade to V5.0.0 process)'; + + /** + * Execute the console command. + * + * Delete data from the table 'User' + * + * @throws + * + * @psalm-return 0|1 + */ + public function handle(): int + { + echo "\n\n"; + $this->warn( "ONLY RUN ONCE AND ONLY WHEN UPGRADING TO IXP Manager v5.0.0 from v4.9.x" ); + echo "\n"; + $this->warn( "THIS WILL DELETE ALL the CUSTADMIN USERS." ); + + if( !$this->confirm( "\nThis command will remove all the custadmin users from the database.\n\n" + ."Generally, this command should only ever be run once and only when migrating to V5.0.0.\n\n" + . 'Are you sure you wish to proceed? ' ) ) { + return 1; + } + + $this->info( 'Deletion in progress, please wait...' ); + + $C2Ucustadmin = CustomerToUser::where( 'privs', User::AUTH_CUSTADMIN )->get(); + + $bar = $this->output->createProgressBar( $C2Ucustadmin->count() ); + $bar->start(); + + foreach( $C2Ucustadmin as $c2u ) { + // Deleting the history user + UserLoginHistory::where( 'customer_to_user_id', $c2u->id )->delete(); + + $user = $c2u->user; +// $user->removeCustomer( $c2u ); + $c2u->delete(); + $user->refresh(); + // Check if the user has c2u left, if not we delete the user + if( !count( $c2u->user->customerToUser ) ) { + // delete all the user's API keys + ApiKey::where( 'user_id', $user->id )->delete(); + $user->delete(); + } else { + // setting a new default customer for the user + $user->custid = $c2u->user->customers()->first()->id; + $user->save(); + } + + $bar->advance(); + } + + $bar->finish(); + echo "\n\n"; + $this->info( 'Migration completed successfully' ); + return 0; + } +} \ No newline at end of file diff --git a/app/Console/Commands/Upgrade/ResetMysqlViews.php b/app/Console/Commands/Upgrade/ResetMysqlViews.php new file mode 100644 index 000000000..5b8e6f263 --- /dev/null +++ b/app/Console/Commands/Upgrade/ResetMysqlViews.php @@ -0,0 +1,99 @@ + + * @author Yann Robin + * @package IXP\Console\Commands\Upgrade + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class ResetMysqlViews extends IXPCommand +{ + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'update:reset-mysql-views'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Reset MySQL views (idempotent and should be run after each upgrade)'; + + /** + * Execute the console command. + * + * @throws + */ + public function handle(): int + { + DB::unprepared( resolve( 'Foil\Engine' )->render( 'database/views.foil.sql' ) ); + + $sql = << + * @author Yann Robin + * @category IXP + * @package IXP\Console\Commands\Customer + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ + +class Create extends Command +{ + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = "user:create + {--e|email= : User's email} + {--na|name= : User's name} + {--u|username= : User's username} + {--m|mobile= : User's mobile number} + {--c|custid= : User's customer} + {--pr|priv= : User's privilege} + {--pa|password= : User's password} + {--s|send-welcome-email : Should we send the welcome email to the user?}"; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Create a new user.'; + + /** + * Validator rules + * + * @var array + */ + protected $rules = [ + 'email' => 'required|email|unique:user,email', + 'name' => 'required|string|max:255', + 'username' => 'required|string|min:3|max:255|regex:/^[a-z0-9\-_\.]{3,255}$/|unique:user,username', + 'mobile' => 'nullable|string|max:50', + 'custid' => 'required|integer|exists:cust,id', + 'priv' => 'required|integer|in:' . User::AUTH_SUPERUSER . ',' . User::AUTH_CUSTADMIN . ',' . User::AUTH_CUSTUSER , + 'password' => 'required|string|min:8|max:255', + ]; + + /** + * Execute the console command. + * + * @return int + */ + public function handle(): int + { + // getting all the options in an array + $options = [ + 'email' => $this->option('email' ), + 'name' => $this->option('name' ), + 'username' => $this->option('username' ), + 'mobile' => $this->option('mobile' ), + 'custid' => $this->option('custid' ), + 'priv' => $this->option('priv' ), + 'password' => $this->option('password' ), + ]; + + $sendEmail = $this->option('send-welcome-email'); + + foreach( $options as $option => $value ){ + //${$option} = $value; + + if( $option !== 'send-welcome-email' ){ + $validator = \Validator::make( [ $option => $value], [$option => $this->rules[ $option ] ] ); + if( $value && $validator->fails() ) { + $this->error( $validator->errors()->first( $option ) ); + return 0; + } + + if( $option === 'priv' ) { + $this->info('[ 1 => member, read only; 2 => member, read/write; 3 => super admin (careful!!)'); + } + + if( !$value ) { + if( $option === 'custid' ){ + $result = $this->ask('Search Customer by ASN or Name' ); + + $this->table( + ['ID', 'Name', 'ASN'], + $this->customersViaNameOrASN( $result ) + ); + } + + $options[$option] = $this->validate_cmd( function() use( $option ) { + if( $option === 'password' ){ + return $this->secret('Enter '. $option); + } + return $this->ask('Enter ' . $option); + }, [ $option => $this->rules[ $option ] ]); + } + } + } + + if( !$sendEmail && $this->confirm('Do you want to send the welcome email to the used?', false ) ) { + $sendEmail = true; + } + + // Creating the User object + $user = new User; + $user->creator = 'artisan'; + $user->password = Hash::make( $options['password'] ); + $user->name = $options['name']; + $user->authorisedMobile = $options['mobile']; + $user->username = strtolower( $options['username'] ); + $user->email = strtolower( $options['email'] ); + $user->disabled = false; + $user->privs = (int) $options['priv']; + $user->custid = (int) $options['custid']; + $user->save(); + + // Creating the CustomerToUser object + $c2u = new CustomerToUser; + $c2u->customer_id = $user->custid; + $c2u->user_id = $user->id; + $c2u->privs = $user->privs; + $c2u->extra_attributes = [ "created_by" => [ "type" => "artisan" , "user_id" => $user->id ] ]; + $c2u->save(); + + if( $sendEmail ){ + // Send Email related to the event + event( new UserCreatedEvent( $user ) ); + } + + $this->info( 'User created.' ); + return 0; + } +} diff --git a/app/Console/Commands/User/Find.php b/app/Console/Commands/User/Find.php new file mode 100644 index 000000000..a6c4f53f5 --- /dev/null +++ b/app/Console/Commands/User/Find.php @@ -0,0 +1,69 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Console\Commands\Customer + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Find extends Command +{ + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'user:find + {search : Username or email fragment to search for}'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Find and print user details.'; + + /** + * Execute the console command. + * + * @return int + */ + public function handle(): int + { + $this->table( + ['ID', 'Name', 'Username', 'Email', 'Customers', 'Privs'], + $this->usersViaUserNameOrEmail( $this->argument('search') ) + ); + + return 0; + } +} diff --git a/app/Console/Commands/User/SetPassword.php b/app/Console/Commands/User/SetPassword.php new file mode 100644 index 000000000..c480529c0 --- /dev/null +++ b/app/Console/Commands/User/SetPassword.php @@ -0,0 +1,126 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Console\Commands\Customer + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class SetPassword extends Command +{ + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = "user:set-password + {--search= : Username or email fragment to search for} + {--u|uid= : User's ID} + {--p|password= : User's password}"; + + /** + * The console command description. + * + * @var string + */ + protected $description = "Set a user's password"; + + protected $rules = [ + 'password' => 'required|string|min:8|max:255', + 'confirm_password' => 'required|same:password', + ]; + + /** + * Execute the console command. + * + * @return int + * + * @throws \Exception + * + * @psalm-return -1|0 + */ + public function handle(): int + { + $search = $this->option('search' ); + $uid = $this->option('uid' ); + $password = $this->option('password' ); + + if( ( !$search && !$uid ) || ( $search && $uid ) ){ + $this->error( "Search or UID must be set (and not both)." ); + return -1; + } + + if( $search ){ // Display result the --search parameter + $this->table( + ['ID', 'Name', 'Username', 'Email', 'Customers', 'Privs'], + $u = $this->usersViaUsernameOrEmail( $search ) + ); + + $uid = $this->anticipate( 'Enter ID to change password for', array_keys( $u )); + } + + if( !$user = User::find( $uid ) ){ + $this->error( "UID does not exist !" ); + return -1; + } + + if( !$password ){ // --password option not specified, ask for password + $password = $this->secret( 'Password or (return to have one generated)' ); + if( $password ){ // if the user type a password + $pw_valid = $this->validateOrExit( $this->rules['password'], 'password', $password ); + + $confirmPassword = $this->secret( 'Confirm password' ); + + if( $password !== $confirmPassword ){// check if password match + $this->error( "The passwords does not match!" ); + return -1; + } + } else {// if the user type return generate password + $password = Str::random(16 ); + $this->info( "Generated password: " . $password ); + } + } else { + $this->validateOrExit( $this->rules['password'], 'password', $password ); + } + + $user->password = Hash::make( $password ); + $user->save(); + + $this->info( "Password set." ); + + return 0; + } + +} \ No newline at end of file diff --git a/app/Console/Commands/Utils/Export/JsonSchema/Post.php b/app/Console/Commands/Utils/Export/JsonSchema/Post.php new file mode 100644 index 000000000..5522169d2 --- /dev/null +++ b/app/Console/Commands/Utils/Export/JsonSchema/Post.php @@ -0,0 +1,90 @@ + + * @category Utils + * @package IXP\Console\Commands + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Post extends IXPCommand +{ + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'utils:json-schema-post + {url : The url end point} + {--ver= : Schema version to export (default: v' . JsonSchema::EUROIX_JSON_LATEST . ')}'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'POST the JSON Schema export to IXF / Euro-IX '; + + /** + * Execute the console command. + * + * @return mixed + */ + public function handle() + { + if( $this->getOutput()->isVerbose() ){ + $this->info( "{$this->name} :: Generating schema..." ); + } + + $exporter = new JsonSchema(); + $json = $exporter->get( $this->option( 'ver' ) ); + + try { + if( $this->getOutput()->isVerbose() ){ + $this->info( "{$this->name} :: Posting schema..." ); + } + + $client = new Client( [ 'base_uri' => $this->argument( 'url' ), 'timeout' => 60.0, ] ); + $response = $client->post( $this->argument( 'url' ), [ 'body' => $json ] ); + } catch( \Exception $e ) { + $this->error( 'Could not post data: ' . $e->getMessage() ); + return -1; + } + + if( $this->getOutput()->isVerbose() ){ + $this->info( "{$this->name} :: Schema posted." ); + } + } +} \ No newline at end of file diff --git a/app/Console/Commands/Utils/ExpungeLogs.php b/app/Console/Commands/Utils/ExpungeLogs.php new file mode 100644 index 000000000..2c518ec2c --- /dev/null +++ b/app/Console/Commands/Utils/ExpungeLogs.php @@ -0,0 +1,138 @@ + + * @author Yann Robin + * @package IXP\Console\Commands\Utils + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class ExpungeLogs extends IXPCommand +{ + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'utils:expunge-logs {--all}'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'This command will delete old data from database tables > 6 months old (or all if --all set)'; + + /** + * Execute the console command. + * + * @return mixed + */ + public function handle() + { + if( $this->option('all') ) { + $sixmonthsago = now()->addDay(); + } else { + $sixmonthsago = now()->subMonths( 6 )->format( 'Y-m-d 00:00:00' ); + } + + // Deleting user login logs older than 6 months + $this->isVerbosityVerbose() && $this->output->write('Expunging user login records > 6 months...', false ); + UserLoginHistory::where( 'at', '<', $sixmonthsago )->delete(); + $this->isVerbosityVerbose() && $this->info(' [done]' ); + + // Deleting expired API Keys older than 1 week + $this->isVerbosityVerbose() && $this->output->write('Expunging expired API Key records > 1 week...', false ); + ApiKey::whereNotNull( 'expires' )->where( 'expires', '<', now()->subWeek()->format( 'Y-m-d 00:00:00' ) )->delete(); + $this->isVerbosityVerbose() && $this->info(' [done]' ); + + // Deleting expired UserRememberTokens + $this->isVerbosityVerbose() && $this->output->write('Expunging expired user remember tokens...', false ); + UserRememberToken::where( 'expires', '<', now()->format( 'Y-m-d H:i:s' ) )->delete(); + $this->isVerbosityVerbose() && $this->info(' [done]' ); + + // Deleting Model logs older than 6 months + $this->isVerbosityVerbose() && $this->output->write('Expunging model logs records > 6 months...', false ); + Log::where( 'created_at', '<', $sixmonthsago )->delete(); + $this->isVerbosityVerbose() && $this->info(' [done]' ); + + // we want to delete docstore file download logs > 6 months (but not the first / earliest entry). + $this->isVerbosityVerbose() && $this->output->write('Expunging non-unique document store download logs...', false ); + DB::raw( 'DELETE dsl1 FROM docstore_logs dsl1, docstore_logs dsl2 + WHERE dsl1.created_at > dsl2.created_at AND dsl1.downloaded_by = dsl2.downloaded_by + AND dsl1.docstore_file_id = dsl2.docstore_file_id + AND dsl1.created_at < "' . $sixmonthsago . '"' + ); + $this->isVerbosityVerbose() && $this->info(' [done]' ); + + // delete all learned mac address records which can't be linked to a vlaninterface + $this->isVerbosityVerbose() && $this->output->write('Expunging unused entries from macaddress database table...', false ); + DB::table('macaddress')->whereRaw( + 'id NOT IN ( + SELECT id FROM ( + SELECT m.id FROM macaddress AS m + INNER JOIN virtualinterface vi ON m.virtualinterfaceid = vi.id + INNER JOIN vlaninterface vli ON (vli.virtualinterfaceid = vi.id) + ) sq_hoodwink_sql + )' + )->delete(); + $this->isVerbosityVerbose() && $this->info(' [done]' ); + + // delete learned mac address records from deleted vlans + $this->isVerbosityVerbose() && $this->output->write('Expunging macaddress entries for unreferenced vlans...', false ); + DB::table('macaddress')->whereRaw( + 'id IN ( + SELECT id FROM ( + SELECT m.id FROM macaddress AS m + INNER JOIN virtualinterface vi ON m.virtualinterfaceid = vi.id + INNER JOIN vlaninterface vli ON (vli.virtualinterfaceid = vi.id) + WHERE vli.vlanid NOT IN (SELECT id FROM vlan) + ) sq_hoodwink_sql + )' + )->delete(); + $this->isVerbosityVerbose() && $this->info(' [done]' ); + + return 0; + } +} \ No newline at end of file diff --git a/app/Console/Commands/Utils/SmtpMailTest.php b/app/Console/Commands/Utils/SmtpMailTest.php new file mode 100644 index 000000000..b4d0be17f --- /dev/null +++ b/app/Console/Commands/Utils/SmtpMailTest.php @@ -0,0 +1,122 @@ + + * @package IXP\Console\Commands\Utils + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class SmtpMailTest extends IXPCommand +{ + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'utils:smtp-mail-test {email : Email address to be sent to}'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Send a test email to check SMTP mail settings'; + + /** + * Execute the console command. + * + * @return mixed + */ + public function handle() + { + $email = $this->argument( 'email' ); + + $this->info( "This utility allows you to test your SMTP settings to verify that IXP Manager can send email." ); + + if( config( 'mail.default' ) !== 'smtp' ) { + $this->error( "The mail driver ('MAIL_MAILER' in your .env file) is not set to \"smtp\". " ); + $this->error( "SMTP is the only officially supported driver in IXP Manager. " ); + return -1; + } + + if( !filter_var( $email, FILTER_VALIDATE_EMAIL ) ) { + $this->error( "Invalid email address provided - {$email}" ); + return -2; + } + + $this->info( "\nTesting using the following parameters:\n" ); + + $mail_default = config( 'mail.default' ); + + if( !$mail_default ) { + $this->error( "The mail driver ('MAIL_MAILER' in your .env file) is not set." ); + return -3; + } + + $this->table( [], [ + [ 'Driver', $mail_default ], + [ 'Host', config( "mail.mailers.{$mail_default}.host", '(not set)' ) ], + [ 'Port', config( "mail.mailers.{$mail_default}.port", '(not set)' ) ], + [ 'Encryption', config( "mail.mailers.{$mail_default}.encryption", '(not set)' ) ], + [ 'Username', config( "mail.mailers.{$mail_default}.username", '(not set)' ) ], + [ 'Password', config( "mail.mailers.{$mail_default}.password", '(not set)' ) ], + [ 'From Name', config( 'identity.name' ) ], + [ 'From Email', config( 'identity.email' ) ], + ] ); + + $this->line( "\n\n" ); + + $this->info( "Trying to send email...\n" ); + + $mail = new SmtpTest(); + + try { + Mail::to( $email )->send( $mail ); + $this->info( "SUCCESS - email has been sent." ); + } catch( \Exception $e ) { + + $this->error( "FAILED TO SEND EMAIL!" ); + $this->line( "\n\n" ); + $this->line( "Exception thrown: " . get_class( $e ) ); + $this->line( "Error: " . $e->getMessage() ); + $this->line( "File: " . $e->getFile() ); + $this->line( "Line: " . $e->getLine() ); + + $this->line( "\n" ); + + if( $this->getOutput()->isVerbose() ) { + echo $e->getTraceAsString(); + } + } + } +} \ No newline at end of file diff --git a/app/Console/Commands/Utils/UpdateOuiDatabase.php b/app/Console/Commands/Utils/UpdateOuiDatabase.php new file mode 100644 index 000000000..958dd8e78 --- /dev/null +++ b/app/Console/Commands/Utils/UpdateOuiDatabase.php @@ -0,0 +1,97 @@ + + * @author Yann Robin + * @package IXP\Console\Commands\Utils + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class UpdateOuiDatabase extends IXPCommand +{ + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'utils:oui-update {file?} {--refresh}'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Update/populate the OUI database table.'; + + /** + * Execute the console command. + * + * @throws + */ + public function handle(): int + { + $ouitool = new OUIUtil( $this->argument( 'file' ) ); + + if( $refresh = $this->option( 'refresh' ) ) { + $this->info( "Deleted " . Oui::count() . " OUI entries during refresh" ); + Oui::truncate(); + } + + $cnt = 0; + foreach( $ouitool->loadList()->processRawData() as $oui => $organisation ) { + if( $cnt++ >= 1000 ) { + $this->isVerbosityVerbose() && $this->output->write('.', false); + $cnt = 0; + } + + if( !$refresh && ( $o = Oui::where( 'oui', $oui )->first() ) ) { + if( $o->organisation !== $organisation ){ + $o->update( [ 'organisation' => $organisation ] ); + } + } else { + Oui::create([ + 'oui' => $oui, + 'organisation' => $organisation + ]); + } + } + + $this->isVerbosityVerbose() && $this->output->write('.', true); + return 0; + } +} \ No newline at end of file diff --git a/app/Console/Commands/Vagrant/GenerateBirdseyeConfigurationsCommand.php b/app/Console/Commands/Vagrant/GenerateBirdseyeConfigurationsCommand.php new file mode 100644 index 000000000..d0db73dc8 --- /dev/null +++ b/app/Console/Commands/Vagrant/GenerateBirdseyeConfigurationsCommand.php @@ -0,0 +1,40 @@ +option( 'directory' ) ) || !is_writable( $this->option( 'directory' ) ) ) { + $this->error( "Directory path {$this->option('directory')} is not writable" ); + exit( 1 ); + } + + foreach( Router::get() as $router ) { + + file_put_contents( $this->option( 'directory' ) . '/birdseye-' . $router->handle . '.env', + + "# +# Bird's Eye - Vagrant generated configuration + +BIRDC=\"/usr/bin/sudo /srv/birdseye/bin/birdc -2 -s /var/run/bird/bird-{$router->handle}.ctl\" +CACHE_DRIVER=array +MAX_ROUTES=100000 +" + + ); + + } + } +} + diff --git a/app/Console/Commands/Vagrant/GenerateClientRouterConfigurationsCommand.php b/app/Console/Commands/Vagrant/GenerateClientRouterConfigurationsCommand.php new file mode 100644 index 000000000..6a1dba3ef --- /dev/null +++ b/app/Console/Commands/Vagrant/GenerateClientRouterConfigurationsCommand.php @@ -0,0 +1,60 @@ +option('directory')) || !is_writable($this->option('directory'))) { + $this->error("Directory path {$this->option('directory')} is not writable"); + exit(1); + } + + $vlis = VlanInterface::where( 'rsclient', 1 )->whereIn( 'vlanid', [1,2])->get(); + + $confNames = []; + + foreach( $vlis as $vli ) { + + // skip route servers, collector and as112 + if( in_array( $vli->virtualInterface->customer->autsys, [ 112, 65500, 65501 ] ) ) { continue; } + + + + + + $this->info( "Generating route server client for {$vli->virtualInterface->customer->name} / {$vli->vlan->name}" ); + + $confName = "as{$vli->virtualInterface->customer->autsys}-" + . strtolower($vli->virtualInterface->physicalInterfaces[0]->switchPort->switcher->infrastructureModel->shortname) + . "{$vli->id}"; + + $confNames[] = $confName; + + $confFile = $this->option('directory') . '/' . $confName . '.conf'; + + file_put_contents( + $confFile, + view('vagrant/router-client', [ 'vli' => $vli, 'confName' => $confName ] )->render() + ); + } + + file_put_contents( + $this->option('directory').'/start-reload-clients.sh', + view('vagrant/router-client-script', [ + 'directory' => $this->option('directory' ), + 'confNames' => $confNames, + ])->render() + ); + + } +} diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php new file mode 100644 index 000000000..3e400e81f --- /dev/null +++ b/app/Console/Kernel.php @@ -0,0 +1,108 @@ +command( 'utils:expunge-logs' )->dailyAt( '3:04' ); + + // Grapher - https://docs.ixpmanager.org/latest/grapher/mrtg/#inserting-traffic-data-into-the-database-reporting-emails + $schedule->command( 'grapher:upload-stats-to-db' )->dailyAt( '2:00' ) + ->skip( function() { return env( 'TASK_SCHEDULER_SKIP_GRAPHER_UPLOAD_STATS_TO_DB', false ); } ) + ->withoutOverlapping(); + + $schedule->command( 'grapher:upload-pi-stats-to-db' )->dailyAt( '2:10' ) + ->skip( function() { return env( 'TASK_SCHEDULER_SKIP_GRAPHER_UPLOAD_STATS_TO_DB', false ); } ) + ->withoutOverlapping(); + + if( config( 'grapher.backends.sflow.enabled' ) ) { + $schedule->command( 'grapher:prune-daily-p2p --days=30' )->dailyAt( '0:05' ); + $schedule->command( 'grapher:upload-daily-p2p ' . now()->subDay()->format( 'Y-m-d' ) ) + ->dailyAt( '0:10' )->withoutOverlapping(); + } + + + + // https://docs.ixpmanager.org/latest/features/peeringdb/#existence-of-peeringdb-records + $schedule->command('ixp-manager:update-in-peeringdb')->daily() + ->skip( function() { return env( 'TASK_SCHEDULER_SKIP_UPDATE_IN_PEERINGDB', false ); } ); + + // https://docs.ixpmanager.org/latest/features/manrs/ + $schedule->command('ixp-manager:update-in-manrs')->daily() + ->skip( function() { return env( 'TASK_SCHEDULER_SKIP_UPDATE_IN_MANRS', false ); } ); + + // IRRDB - https://docs.ixpmanager.org/latest/features/irrdb/ + if( config( 'ixp.irrdb.bgpq3.path' ) && is_executable( config( 'ixp.irrdb.bgpq3.path' ) ) ) { + + $schedule->command( 'irrdb:update-prefix-db --alert-email' )->cron( '7 */6 * * *' ) + ->skip( function() { return env( 'TASK_SCHEDULER_SKIP_IRRDB_UPDATE_PREFIX_DB', false ); } ) + ->withoutOverlapping(); + + $schedule->command( 'irrdb:update-asn-db --alert-email' )->cron( '37 */6 * * *' ) + ->skip( function() { return env( 'TASK_SCHEDULER_SKIP_IRRDB_UPDATE_ASN_DB', false ); } ) + ->withoutOverlapping(); + } + + // https://laravel.com/docs/5.8/telescope#data-pruning + $schedule->command('telescope:prune --hours=72')->daily(); + + // OUI Update - https://docs.ixpmanager.org/latest/features/layer2-addresses/#oui-database + $schedule->command( 'utils:oui-update' )->weekly()->mondays()->at('9:15') + ->skip( function() { return env( 'TASK_SCHEDULER_SKIP_UTILS_OUI_UPDATE', false ); } ) + ->withoutOverlapping(); + + // Switch SNMP pool - https://docs.ixpmanager.org/latest/usage/switches/#automated-polling-snmp-updates + $schedule->command( 'switch:snmp-poll' )->everyFiveMinutes() + ->skip( function() { return env( 'TASK_SCHEDULER_SKIP_SWITCH_SNMP_POLL', false ); } ) + ->withoutOverlapping(); + + } + + /** + * Register the commands for the application. + * + * @return void + */ + #[\Override] + protected function commands(): void + { + $this->load(__DIR__.'/Commands'); + + require base_path('routes/console.php'); + } +} \ No newline at end of file diff --git a/app/Contracts/Grapher/Backend.php b/app/Contracts/Grapher/Backend.php new file mode 100644 index 000000000..d3e4a0e9f --- /dev/null +++ b/app/Contracts/Grapher/Backend.php @@ -0,0 +1,181 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Contracts + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +interface Backend +{ + /** + * The name of the backend (as would be entered in the config file for example) + * + * @return string + */ + public function name(): string; + + /** + * Not all graphing backends will require a configuration. This function indicates whether the + * backend being implemented requires a configuration or not. + * + * Used, for example, by the Artisan grapher:generate-configuration console command. + * @return bool + */ + public function isConfigurationRequired(): bool; + + /** + * Not all graphing backends are created equal and some will support / require different output formats. + * + * The types we are looking at are: + * + * * single monolithic text - standard output or to a specified file + * * multiple files (and optionally directories) to a specified directory + * * gzip'd bundle of one or more files + * + * This function indicates whether this graphing engine supports single monolithic text + * + * @return bool + */ + public function isMonolithicConfigurationSupported(): bool; + + /** + * @see Backend::isMonolithicConfigurationSupported() for an explanation + * + * This function indicates whether this graphing engine supports multiple files to a directory + * + * @return bool + */ + public function isMultiFileConfigurationSupported(): bool; + + /** + * Constant for configuration type to generate: one big file + * + * @var int + */ + public const GENERATED_CONFIG_TYPE_MONOLITHIC = 1; + + /** + * Constant for configuration type to generate: one big file + * + * @var int + */ + public const GENERATED_CONFIG_TYPE_MULTIFILE = 2; + + /** + * Generate the configuration file(s) for this graphing backend + * + * For monolithic files, returns a single element array. Otherwise + * an array keyed by the filename (with optional local directory path). + * + * @param int $type The type of configuration to generate + * @param array $options + * @return array + */ + public function generateConfiguration( int $type = self::GENERATED_CONFIG_TYPE_MONOLITHIC, array $options = [] ): array; + + /** + * Examines the provided graph object and determines if this backend is able to + * process the request or not. + * + * @param Graph $graph + * @return bool + */ + public function canProcess( Graph $graph ): bool; + + /** + * Get the data points for a given graph + * + * It **MUST** be returned as an indexed array of arrays where the five elements + * of these arrays are: + * + * [ + * [ + * 0 => unixtime stamp + * 1 => average incoming rate + * 2 => average outgoing rate + * 3 => maximum incoming rate + * 4 => maximum outgoing rate + * ], + * .... + * ] + * + * NB: For errors, discards and packets, the rate is packets per second. For bits, it's bits per second. + * + * NB: The above **MUST** be ordered with the oldest first. + * + * @param Graph $graph + * @return array + */ + public function data( Graph $graph ): array; + + /** + * Get the PNG image for a given graph + * + * {inheritDoc} + * + * @param Graph $graph + * @return false|string + */ + public function png( Graph $graph ): false|string; + + /** + * Get the path to the graphing data file (e.g. path to log or rrd file). + * + * @param Graph $graph + * @return string + */ + public function dataPath( Graph $graph ): string; + + /** + * Get the RRD file for a given graph + * + * {inheritDoc} + * + * @param Graph $graph + * @return false|string + */ + public function rrd( Graph $graph ): false|string; + + /** + * Get a complete list of functionality that this backend supports. + * + * See the IXP\Services\Grapher\Backend\Dummy for a complete list. + * + * {inheritDoc} + * + * @return array + */ + public static function supports(): array; +} \ No newline at end of file diff --git a/app/Contracts/Helpdesk.php b/app/Contracts/Helpdesk.php new file mode 100644 index 000000000..4d76dd050 --- /dev/null +++ b/app/Contracts/Helpdesk.php @@ -0,0 +1,198 @@ + + * @author Yann Robin + * @category Helpdesk + * @package IXP\Contracts + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +interface Helpdesk +{ + /** + * Return the helpdesk debug information + * + * Your implementation should catch API errors, set the $debug member with additional details and throw an ApiException + */ + public function getDebug(); + + /** + * Find all tickets on the helpdesk + */ + public function ticketsFindAll(); + + // ******************************************************************************************** + // ******************************************************************************************** + // ******************************************************************************************** + // + // COMPANIES / ORGANISATIONS + // + // ******************************************************************************************** + // ******************************************************************************************** + // ******************************************************************************************** + + /** + * Examine customer and Zendesk object and see if Zendesk needs to be updated + * + * This belongs here as the different parameters of a customer that one helpdesk + * may support will vary from another. + * + * @param Customer $cdb The IXP customer entity as known here in the database + * @param Customer $chd The IXP customer entity as known in the helpdesk + * + * @return bool True if these objects are not in sync + */ + public function organisationNeedsUpdating( Customer $cdb, Customer $chd ):bool; + + /** + * Create organisation + * + * Create an organisation on the helpdesk. Tickets are usually aligned to + * users and they in turn to organisations. + * + * @param Customer $cust An IXP Manager customer to create as organisation + * + * @return Customer|bool A decoupled customer entity (including `helpdesk_id`) + * + * @throws ApiException + */ + public function organisationCreate( Customer $cust ); + + /** + * Update an organisation **where the helpdesk ID is known!** + * + * Updates an organisation already found via `organisationFind()` as some implementations + * (such as Zendesk's PHP client as of Apr 2015) require knowledge of the helpdesk's ID for + * an organisatoin. + * + * @param int $helpdeskId The ID of the helpdesk's organisation object + * @param Customer $cust An IXP Manager customer as returned by `organisationFind()` + * + * @return Customer|bool A decoupled customer entity (including `helpdesk_id`) + * @throws ApiException + */ + public function organisationUpdate( int $helpdeskId, Customer $cust ); + + /** + * Find an organisation by our own customer ID + * + * **NB:** the returned entity shouldn't have an ID parameter set - you should already know it! + * + * The reason for this is that the returned customer object is incomplete and is only intended + * to be used to compare local with Zendesk and/or identify if a customer exists. + * + * The returned customer object MUST have a member `helpdesk_id` containing the helpdesk provider's + * ID for this organisation. + * + * @param int $id Our own customer ID to find the organisation from + * + * @return Customer|bool A shallow disassociated customer object or false + * + * @throws ApiException + */ + public function organisationFind( int $id ); + + // ******************************************************************************************** + // ******************************************************************************************** + // ******************************************************************************************** + // + // CONTACTS / USERS + // + // ******************************************************************************************** + // ******************************************************************************************** + // ******************************************************************************************** + + /** + * Examine contact and Zendesk object and see if Zendesk needs to be updated + * + * @param Contact $cdb The IXP contact entity as known here in the database + * @param Contact $chd The IXP contact entity as known in the helpdesk + * + * @return bool True if these objects are not in sync + */ + public function contactNeedsUpdating( Contact $cdb, Contact $chd ):bool; + + /** + * Create user + * + * Create user on the helpdesk. + * + * @param Contact $contact An IXP Manager contact to create + * @param int $org_id + * + * @return Contact|bool Decoupled contact object with `helpdesk_id` + * + * @throws ApiException + */ + public function userCreate( Contact $contact, int $org_id ); + + /** + * Update an user **where the helpdesk ID is known!** + * + * Updates an user already found via `userFind()` as some implementations + * (such as Zendesk's PHP client as of Apr 2015) require knowledge of the helpdesk's ID for + * an user. + * + * @param int $helpdeskId The ID of the helpdesk's user object + * @param Contact $contact An IXP Manager contact as returned by `userFind()` + * + * @return Contact|false Decoupled contact object with `helpdesk_id` + * + * @throws ApiException + */ + public function userUpdate( int $helpdeskId, Contact $contact ): Contact|false; + + /** + * Find an user by our own contact ID + * + * **NB:** the returned entity shouldn't have an ID parameter set - you should already know it! + * + * The reason for this is that the returned contact object is incomplete and is only intended + * to be used to compare local with Zendesk and/or identify if a contact exists. + * + * The returned contact object MUST have a member `helpdesk_id` containing the helpdesk provider's + * ID for this organisation. + * + * @param int $id Our own contact ID to find the contact from + * + * @return Contact|bool A shallow disassociated contact object or false + * + * @throws ApiException + */ + public function userFind( int $id ); +} \ No newline at end of file diff --git a/app/Contracts/LookingGlass.php b/app/Contracts/LookingGlass.php new file mode 100644 index 000000000..42024a316 --- /dev/null +++ b/app/Contracts/LookingGlass.php @@ -0,0 +1,168 @@ + + * @category LookingGlassContract + * @package IXP\Contracts + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +interface LookingGlass +{ + /** + * Set the router object + * + * @param Router $r + * + * @return LookingGlass For fluent interfaces + */ + public function setRouter( Router $r ): LookingGlass; + + /** + * Get the router object + * + * @return Router + */ + public function router(): Router; + + /** + * Get BGP Summary information as JSON + * + * Response must use equivalent structure as Bird's Eye: + * https://github.com/inex/birdseye/ + * + * @return string + */ + public function bgpSummary(): string; + + /** + * Get BGP neighbour information as JSON + * + * Response must use equivalent structure as Bird's Eye: + * https://github.com/inex/birdseye/ + * + * @param string $protocol Protocol name + * @return string + */ + public function bgpNeighbourSummary( string $protocol ): string; + + + /** + * Get the router status information as JSON + * + * Response must use equivalent structure as Bird's Eye: + * https://github.com/inex/birdseye/ + * + * @return string + */ + public function status(): string; + + /** + * Get internal symbols. + * + * Particularly we're interested in route tables / vrfs and protocols. + * + * @return string + */ + public function symbols(): string; + + /** + * Get routes for a named routing table (aka. vrf) + * + * @param string $table Table name + * + * @return string + */ + public function routesForTable( string $table ): string; + + /** + * Get routes learnt from named protocol (e.g. BGP session) + * + * @param string $protocol Protocol name + * + * @return string + */ + public function routesForProtocol( string $protocol ): string; + + /** + * Get routes exported to named protocol (e.g. BGP session) + * + * @param string $protocol Protocol name + * + * @return string + */ + public function routesForExport( string $protocol ): string; + + /** + * Get details for a specific route as received by a protocol + * + * @param string $protocol Protocol name + * @param string $network The route to lookup + * @param int $mask The mask of the route to look up + * + * @return string + */ + public function protocolRoute( string $protocol, string $network, int $mask ): string; + + /** + * Get details for a specific route in a named protocol export + * + * @param string $protocol Protocol name + * @param string $network The route to lookup + * @param int $mask The mask of the route to look up + * + * @return string + */ + public function exportRoute( string $protocol, string $network, int $mask ): string; + + /** + * Get details for a specific route in a named table (vrf) + * + * @param string $table Table name + * @param string $network The route to lookup + * @param int $mask The mask of the route to look up + * + * @return string + */ + public function protocolTable( string $table, string $network, int $mask ): string; + + /** + * Get wildcard large communities in protocol table of form ( x, y, * ) + * + * @param string $protocol Protocol name + * @param int $x + * @param int $y + * + * @return string + */ + public function routesProtocolLargeCommunityWildXYRoutes( string $protocol, int $x, int $y ): string; +} \ No newline at end of file diff --git a/app/Events/Auth/ForgotPassword.php b/app/Events/Auth/ForgotPassword.php new file mode 100644 index 000000000..8da88739e --- /dev/null +++ b/app/Events/Auth/ForgotPassword.php @@ -0,0 +1,64 @@ + + * @author Yann Robin + * @category Auth\Event + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class ForgotPassword +{ + use Dispatchable, SerializesModels; + + /** + * @var string + */ + public $token; + + /** + * @var User + */ + public $user; + + /** + * Create a new event instance. + * + * @param string $token + * @param User $user + */ + public function __construct( string $token, User $user ) + { + $this->token = $token; + $this->user = $user; + } +} \ No newline at end of file diff --git a/app/Events/Auth/ForgotUsername.php b/app/Events/Auth/ForgotUsername.php new file mode 100644 index 000000000..a702474e6 --- /dev/null +++ b/app/Events/Auth/ForgotUsername.php @@ -0,0 +1,63 @@ + + * @author Yann Robin + * @category Events\Auth + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class ForgotUsername +{ + use Dispatchable, SerializesModels; + + /** + * @var Collection of users + */ + public Collection $users; + + /** + * @var String Email + */ + public string $email; + + /** + * Create a new event instance. + * + * @param Collection $users + * @param string $email + */ + public function __construct( Collection $users, string $email ) + { + $this->users = $users; + $this->email = $email; + } +} \ No newline at end of file diff --git a/app/Events/Auth/PasswordReset.php b/app/Events/Auth/PasswordReset.php new file mode 100644 index 000000000..47da3c8e7 --- /dev/null +++ b/app/Events/Auth/PasswordReset.php @@ -0,0 +1,56 @@ + + * @author Yann Robin + * @category Events\Auth + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class PasswordReset +{ + use Dispatchable, SerializesModels; + + /** + * @var User + */ + public $user; + + /** + * Create a new event instance. + * + * @param User $user + */ + public function __construct( User $user ) + { + $this->user = $user; + } +} \ No newline at end of file diff --git a/app/Events/Customer/BillingDetailsChanged.php b/app/Events/Customer/BillingDetailsChanged.php new file mode 100644 index 000000000..bfdf2e1a7 --- /dev/null +++ b/app/Events/Customer/BillingDetailsChanged.php @@ -0,0 +1,61 @@ +ocbd = $ocbd; + $this->cbd = $cbd; + } +} \ No newline at end of file diff --git a/app/Events/Customer/Note/Changed.php b/app/Events/Customer/Note/Changed.php new file mode 100644 index 000000000..c4de1519d --- /dev/null +++ b/app/Events/Customer/Note/Changed.php @@ -0,0 +1,199 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Events\Customer\Note + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +abstract class Changed implements ShouldQueue +{ + use Dispatchable, SerializesModels, InteractsWithQueue; + + /** + * old customer note + * + * @var CustomerNote + */ + protected $ocn; + + /** + * new customer note + * + * @var CustomerNote + */ + protected $cn; + + /** + * @var Customer + */ + protected $cust; + + /** + * @var string + */ + protected $type; + + /** + * @var User + */ + protected $user; + + /** + * Create a new event instance. + * + * @param CustomerNote|null $ocn + * @param CustomerNote $cn + * @param User $user + * + * @throws GeneralException + */ + public function __construct( CustomerNote $ocn = null , CustomerNote $cn, User $user ) + { + $this->ocn = $ocn; + $this->cn = $cn; + $this->user = $user; + + if( $ocn ) { + $this->cust = $ocn->customer; + } else if( $cn ) { + $this->cust = $cn->customer; + } else { + throw new GeneralException( "Customer note is missing." ); + } + } + + /** + * Get customer + * + * @return Customer + */ + public function customer(): Customer + { + return $this->cust; + } + + /** + * Get customer + * + * @return User + */ + public function user(): User + { + return $this->user; + } + + /** + * Get old note + */ + public function oldNote(): CustomerNote + { + return $this->ocn; + } + + /** + * Get note + * + * @return CustomerNote + */ + public function note(): CustomerNote + { + return $this->cn; + } + + /** + * Get either note: get the new note if set, otherwise the old note + * + * @return CustomerNote + */ + public function eitherNote(): CustomerNote + { + return $this->cn ?: $this->ocn; + } + + /** + * Is the event type: a customer note was added + * + * @return bool + */ + public function typeCreated(): bool + { + return get_class( $this ) === Created::class; + } + + /** + * Is the event type: a customer note was deleted + * + * @return bool + */ + public function typeDeleted(): bool + { + return get_class( $this ) === Deleted::class; + } + + /** + * Is the event type: a customer note was edited + * + * @return bool + */ + public function typeEdited(): bool + { + return get_class( $this ) === Edited::class; + } + + /** + * Resolve the type + * + * @return string + * + * @psalm-return 'Created'|'Deleted'|'Edited' + */ + public function actionDescription(): string + { + if( $this->typeCreated() ){ + return "Created"; + } + if( $this->typeEdited() ){ + return "Edited"; + } + return "Deleted"; + } +} \ No newline at end of file diff --git a/app/Events/Customer/Note/Created.php b/app/Events/Customer/Note/Created.php new file mode 100644 index 000000000..73de3fa03 --- /dev/null +++ b/app/Events/Customer/Note/Created.php @@ -0,0 +1,36 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Events\Customer\Note + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Created extends Changed +{} diff --git a/app/Events/Customer/Note/Deleted.php b/app/Events/Customer/Note/Deleted.php new file mode 100644 index 000000000..ddb7d2779 --- /dev/null +++ b/app/Events/Customer/Note/Deleted.php @@ -0,0 +1,36 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Events\Customer\Note + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Deleted extends Changed +{} diff --git a/app/Events/Customer/Note/Edited.php b/app/Events/Customer/Note/Edited.php new file mode 100644 index 000000000..dc292124e --- /dev/null +++ b/app/Events/Customer/Note/Edited.php @@ -0,0 +1,37 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Events\Customer\Note + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Edited extends Changed +{} diff --git a/app/Events/Event.php b/app/Events/Event.php new file mode 100644 index 000000000..9248ca53d --- /dev/null +++ b/app/Events/Event.php @@ -0,0 +1,25 @@ + + * @author Yann Robin + * @category Layer2Address\Event + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Added +{ + use Dispatchable, SerializesModels; + + /** + * @var string + */ + public $action; + + /** + * @var string + */ + public $mac; + + /** + * @var User + */ + public $user; + + /** + * @var string + */ + public $customer; + + /** + * @var VlanInterface + */ + public $vli; + + /** + * Create a new event instance. + * + * @param Layer2Address $l2a + * @param User $u + */ + public function __construct( Layer2Address $l2a, User $u ) + { + $this->action = "add"; + $this->mac = $l2a->mac; + $this->user = $u; + $this->customer = $u->customer->getFormattedName(); + $this->vli = $l2a->vlanInterface; + } +} \ No newline at end of file diff --git a/app/Events/Layer2Address/Deleted.php b/app/Events/Layer2Address/Deleted.php new file mode 100644 index 000000000..958a1142d --- /dev/null +++ b/app/Events/Layer2Address/Deleted.php @@ -0,0 +1,88 @@ + + * @author Yann Robin + * @category Layer2Address\Event + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Deleted +{ + use Dispatchable, SerializesModels; + + /** + * @var string + */ + public $action; + + /** + * @var string + */ + public $mac; + + /** + * @var User + */ + public $user; + + /** + * @var string + */ + public $customer; + + /** + * @var VlanInterface + */ + public $vli; + + /** + * Create a new event instance. + * + * @param string $oldmac + * @param VlanInterface $vli + * @param User $u + * + * @return void + */ + public function __construct( string $oldmac, VlanInterface $vli, User $u ) + { + $this->action = "delete"; + $this->mac = $oldmac; + $this->user = $u; + $this->customer = $u->customer->getFormattedName(); + $this->vli = $vli; + } +} \ No newline at end of file diff --git a/app/Events/RipeAtlas/MeasurementComplete.php b/app/Events/RipeAtlas/MeasurementComplete.php new file mode 100644 index 000000000..6b11e3147 --- /dev/null +++ b/app/Events/RipeAtlas/MeasurementComplete.php @@ -0,0 +1,50 @@ +atlasMeasurement = $atlasMeasurement; + } +} \ No newline at end of file diff --git a/app/Events/User/UserAddedToCustomer.php b/app/Events/User/UserAddedToCustomer.php new file mode 100644 index 000000000..e31a1124a --- /dev/null +++ b/app/Events/User/UserAddedToCustomer.php @@ -0,0 +1,57 @@ + + * @author Yann Robin + * @category Events\User + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class UserAddedToCustomer +{ + use Dispatchable, SerializesModels; + + /** + * @var CustomerToUser + */ + public $c2u; + + /** + * Create a new event instance. + * + * @param CustomerToUser $c2u + */ + public function __construct( CustomerToUser $c2u ) + { + $this->c2u = $c2u; + } +} \ No newline at end of file diff --git a/app/Events/User/UserCreated.php b/app/Events/User/UserCreated.php new file mode 100644 index 000000000..83621c276 --- /dev/null +++ b/app/Events/User/UserCreated.php @@ -0,0 +1,57 @@ + + * @author Yann Robin + * @category Events\User + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class UserCreated +{ + use Dispatchable, SerializesModels; + + /** + * @var User + */ + public $user; + + /** + * Create a new event instance. + * + * @param User $user + */ + public function __construct( User $user ) + { + $this->user = $user; + } +} \ No newline at end of file diff --git a/app/Exceptions/ConfigurationException.php b/app/Exceptions/ConfigurationException.php new file mode 100644 index 000000000..6f96e39f4 --- /dev/null +++ b/app/Exceptions/ConfigurationException.php @@ -0,0 +1,28 @@ +, \Psr\Log\LogLevel::*> + */ + protected $levels = [ + // + ]; + + /** + * A list of the exception types that are not reported. + * + * @var array + */ + protected $dontReport = [ + // + ]; + + /** + * A list of the inputs that are never flashed for validation exceptions. + * + * @var array + */ + protected $dontFlash = [ + 'current_password', + 'password', + 'password_confirmation', + ]; + + /** + * Register the exception handling callbacks for the application. + * + * @return void + */ + #[\Override] + public function register() + { + $this->reportable(function (Throwable $e) { + // + }); + } + + /** + * Render an exception into an HTTP response. + * + * @param \Illuminate\Http\Request $request + * @param Throwable $e + * + * @return \Symfony\Component\HttpFoundation\Response + */ + #[\Override] + public function render($request, Throwable $e) + { + if( $e instanceof GraphCannotBeProcessedException ) { + AlertContainer::push( 'No graphing backend configured to support the requested graph type.', Alert::DANGER ); + return Redirect::to(''); + } + + return parent::render($request, $e); + } +} diff --git a/app/Exceptions/IrrdbManage.php b/app/Exceptions/IrrdbManage.php new file mode 100644 index 000000000..b28209fb2 --- /dev/null +++ b/app/Exceptions/IrrdbManage.php @@ -0,0 +1,37 @@ +getMessage(), Alert::DANGER ); + return redirect()->back(); + } +} \ No newline at end of file diff --git a/app/Exceptions/Mailable.php b/app/Exceptions/Mailable.php new file mode 100644 index 000000000..46679685d --- /dev/null +++ b/app/Exceptions/Mailable.php @@ -0,0 +1,28 @@ +getMessage() ); + } +} \ No newline at end of file diff --git a/app/Exceptions/Utils/Grapher/FileError.php b/app/Exceptions/Utils/Grapher/FileError.php new file mode 100644 index 000000000..58d671fcb --- /dev/null +++ b/app/Exceptions/Utils/Grapher/FileError.php @@ -0,0 +1,28 @@ + + * @author Barry O'Donovan + * @category IXP + * @package IXP\Http\Controllers + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class AdminController extends Controller +{ + /** + * Display the home page + * + * @param Request $r + * + * @return view + */ + public function dashboard( Request $r ): View + { + return view( 'admin/dashboard' )->with([ + 'stats' => $this->dashboardStats( $r ), + 'graphs' => $this->publicPeeringGraphs( $r ), + 'graph_period' => $r->query( 'graph_period', config( 'ixp_fe.admin_dashboard.default_graph_period' ) ), + 'graph_periods' => Graph::PERIOD_DESCS, + ]); + } + + /** + * Get type counts statistics + * + * @param Request $r + * + * @return array array of statistics + */ + private function dashboardStats( Request $r ): array + { + // only do this once every 60 minutes + if( $r->query( 'refresh_cache' ) || !( $cTypes = Cache::get( 'admin_ctypes' ) ) ) { + // Full / Associate / Probono / Internal + $cTypes[ 'types' ] = Customer::selectRaw('type AS ctype, COUNT( type ) AS cnt') + ->whereRaw(Customer::SQL_CUST_CURRENT) + ->whereRaw(Customer::SQL_CUST_ACTIVE) + ->groupBy('ctype')->get()->keyBy('ctype')->toArray();; + + // Searches for VirtualInterfaces where custtype us not internal. + // Because it's Virtual Interfaces, it should only be current or unremoved customers, etc. + $vis = VirtualInterfaceAggregator::getByLocation(); + + $speeds = []; + $byLocation = []; + $byLan = []; + $byIxp = []; + $custsByLocation = []; + $custsByInfra = []; + $peeringCusts = []; + + // for rate limited ports: + $rateLimitedPorts = []; + $pispeeds = PhysicalInterface::$SPEED; + krsort( $pispeeds, SORT_NUMERIC ); + + + foreach( $vis as $vi ) { + $location = $vi[ 'locationname' ]; + $cabinet = $vi[ 'cabinetname' ]; + $infrastructure = $vi[ 'infrastructure' ]; + $custid = $vi[ 'customerid' ]; + + if ( !isset($custsByLocation[ $location ])) { + $custsByLocation[ $location ] = [ + 'count' => 1, + 'id' => $vi[ 'locationid' ], + 'name' => $location, + 'custs' => [ $custid ], + 'cabinets' => [], + ]; + } elseif( !in_array( $custid, $custsByLocation[ $location ][ 'custs' ], true ) ){ + $custsByLocation[ $location ][ 'count' ]++; + $custsByLocation[ $location ][ 'custs' ][] = $custid; + } + + if ( !isset( $custsByLocation[ $location ]['cabinets'][$cabinet] ) ) { + $custsByLocation[ $location ]['cabinets'][ $cabinet ] = [ + 'count' => 1, + 'id' => $vi[ 'cabinetid' ], + 'name' => $cabinet, + 'custs' => [ $custid ] + ]; + } elseif( !in_array( $custid, $custsByLocation[ $location ]['cabinets'][$cabinet][ 'custs' ], true ) ){ + $custsByLocation[ $location ]['cabinets'][$cabinet][ 'count' ]++; + $custsByLocation[ $location ]['cabinets'][$cabinet][ 'custs' ][] = $custid; + } + + // Speeds have gotten more complex now that we've add rate limiters, sigh. + // We're not going to go around the houses here to solve odd services - speeds + // should be a multiple of physical speeds. + $speed = $vi[ 'speed' ]; + $numports = 1; + + if( $vi[ 'rlspeed' ] ) { + foreach( array_keys( $pispeeds ) as $kspeed ) { + if( $vi[ 'rlspeed' ] >= $kspeed ) { + $speed = $kspeed; + $numports = round( $vi[ 'rlspeed' ] / $kspeed ); + $rateLimitedPorts[] = [ 'physint' => $vi['speed'], 'numports' => $numports, 'rlspeed' => $speed ]; + break; + } + } + } + + if ( !isset($speeds[ $speed ])) { + $speeds[ $speed ] = $numports; + } else { + $speeds[ $speed ] += $numports; + } + + if ( !isset($custsByInfra[ $infrastructure ])) { + $custsByInfra[ $infrastructure ] = []; + } + if ( !in_array($vi[ 'customerid' ], $custsByInfra[ $infrastructure ], true)) { + $custsByInfra[ $infrastructure ][] = $vi[ 'customerid' ]; + } + + if ( !in_array($vi[ 'customerid' ], $peeringCusts, true)) { + $peeringCusts[] = $vi[ 'customerid' ]; + } + + if ( !isset($byLocation[ $location ])) { + $byLocation[ $location ] = [ + 'id' => $vi[ 'locationid' ], + 'cabinets' => [], + ]; + } + if ( !isset($byLocation[ $location ]['cabinets'][ $cabinet ] )) { + $byLocation[ $location ]['cabinets'][ $cabinet ] = [ 'id' => $vi[ 'cabinetid' ] ]; + } + + if ( !isset($byLocation[ $vi[ 'locationname' ] ][ $speed ])) { + $byLocation[ $location ][ $speed ] = $numports; + } else { + $byLocation[ $location ][ $speed ] += $numports; + } + + if ( !isset($byLocation[ $location ]['cabinets'][ $cabinet ][ $vi[ 'speed' ] ])) { + $byLocation[ $location ]['cabinets'][ $cabinet ][ $vi[ 'speed' ] ] = 1; + } else { + $byLocation[ $location ]['cabinets'][ $cabinet ][ $vi[ 'speed' ] ]++; + } + + if ( !isset( $byLan[ $infrastructure ] ) ) { + $byLan[ $infrastructure ] = [ 'id' => $vi[ 'infrastructureid' ] ]; + } + + if ( !isset( $byLan[ $infrastructure ][ $speed ] ) ) { + $byLan[ $infrastructure ][ $speed ] = $numports; + } else { + $byLan[ $infrastructure ][ $speed ] += $numports; + } + } + + ksort($speeds, SORT_NUMERIC); + + usort($custsByLocation, function ($a, $b) { + return $a[ 'count' ] <=> $b[ 'count' ]; + }); + + $cTypes[ 'speeds' ] = $speeds; + $cTypes[ 'custsByLocation' ] = $custsByLocation; + $cTypes[ 'byLocation' ] = $byLocation; + $cTypes[ 'byLan' ] = $byLan; + $cTypes[ 'byIxp' ] = $byIxp; + $cTypes[ 'custsByInfra' ] = $custsByInfra; + $cTypes[ 'peeringCusts' ] = $peeringCusts; + $cTypes[ 'rateLimitedPorts' ] = $rateLimitedPorts; + + // FROM of query is vlaninterface so should be current: + $cTypes[ 'usage' ] = VlanInterface::selectRaw( + 'v.id AS vlanid, + v.name AS vlanname, + COUNT(vli.id) AS overall_count, + SUM(vli.rsclient = 1) AS rsclient_count, + SUM(vli.ipv6enabled = 1) AS ipv6_count' ) + ->from('vlaninterface AS vli') + ->Join('virtualinterface AS vi', 'vi.id', 'vli.virtualinterfaceid') + ->Join('cust AS c', 'c.id', 'vi.custid') + ->Join('vlan AS v', 'v.id', 'vli.vlanid') + ->where('v.private', false) + ->whereIn('c.type', [1, 4]) + ->groupBy('vlanname')->get()->toArray(); + + // full/probono customers with connected interface by vlan + $cTypes[ 'percentByVlan' ] = VirtualInterfaceAggregator::getPercentageCustomersByVlan(); + $cTypes[ 'cached_at' ] = Carbon::now(); + $cTypes[ 'infras' ] = Infrastructure::orderBy('name' )->get()->toArray(); + $cTypes[ 'cabinets' ] = Cabinet::orderBy('name' )->get()->toArray(); + $cTypes[ 'locations' ] = Location::orderBy('name' )->get()->toArray(); + $cTypes[ 'vlans' ] = Vlan::publicOnly()->orderBy('number')->get()->keyBy('id')->toArray(); + + Cache::put('admin_ctypes', $cTypes, 300); + } + + return $cTypes; + } + + /** + * Get public peering graphs + * + * @param Request $r + * + * @return array array of graphs + * + * @throws + */ + private function publicPeeringGraphs( Request $r ): array + { + $grapher = App::make( Grapher::class ); + + $period = Graph::processParameterPeriod( $r->query( 'graph_period', config( 'ixp_fe.admin_dashboard.default_graph_period' ) ) ); + + if( $r->query( 'refresh_cache' ) || !( $graphs = Cache::get( 'admin_stats_'.$period ) ) ) { + $graphs = []; + + $graphs['ixp'] = $grapher->ixp() + ->setType( Graph::TYPE_PNG ) + ->setProtocol( Graph::PROTOCOL_ALL ) + ->setPeriod( $period ) + ->setCategory( Graph::CATEGORY_BITS ); + + foreach( Infrastructure::all() as $inf ) { + $graphs[ $inf->id ] = $grapher->infrastructure( $inf ) + ->setType( Graph::TYPE_PNG ) + ->setProtocol( Graph::PROTOCOL_ALL ) + ->setPeriod( $period ) + ->setCategory( Graph::CATEGORY_BITS ); + } + + Cache::put( 'admin_stats_'. $period, $graphs, 300 ); + } + return $graphs; + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Api/Controller.php b/app/Http/Controllers/Api/Controller.php new file mode 100644 index 000000000..73b142b05 --- /dev/null +++ b/app/Http/Controllers/Api/Controller.php @@ -0,0 +1,38 @@ + + * @author Yann Robin + * @category APIv4 + * @package IXP\Http\Controllers\Api + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Controller extends BaseController {} diff --git a/app/Http/Controllers/Api/V4/Controller.php b/app/Http/Controllers/Api/V4/Controller.php new file mode 100644 index 000000000..0b146fc6e --- /dev/null +++ b/app/Http/Controllers/Api/V4/Controller.php @@ -0,0 +1,35 @@ + + * @author Yann Robin + * @category APIv4 + * @package IXP\Http\Controllers\Api\V4 + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Controller extends ApiController{} \ No newline at end of file diff --git a/app/Http/Controllers/Api/V4/Customer/Note/CustomerNotesController.php b/app/Http/Controllers/Api/V4/Customer/Note/CustomerNotesController.php new file mode 100644 index 000000000..47d601bbb --- /dev/null +++ b/app/Http/Controllers/Api/V4/Customer/Note/CustomerNotesController.php @@ -0,0 +1,253 @@ + + * @author Yann Robin + * @category Customers + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class CustomerNotesController extends Controller +{ + /** + * Create note for a customer + * + * @param Request $r instance of the current HTTP request + * @param Customer $cust + * + * @return JsonResponse + * + * @throws + */ + public function create( Request $r, Customer $cust ): JsonResponse + { + /** @var User $user */ + $user = Auth::getUser(); + $cn = new CustomerNote; + + $cn->title = $r->title; + $cn->note = $r->note ; + $cn->private = $r->public ? 0 : 1; + $cn->customer_id = $cust->id; + $cn->save(); + + // update the user's notes last read so he won't be told his own is new + $prefs = $user->prefs; + $prefs[ 'notes' ][ 'last_read' ][ $cust->id ] = now()->format( 'Y-m-d H:i:s' ); + $user->prefs = $prefs; + $user->save(); + + event( new CustomerNoteCreatedEvent( null, $cn, $user ) ); + + return response()->json( [ 'noteid' => $cn->id ] ); + } + + /** + * Update note for a customer + * + * @param Request $r instance of the current HTTP request + * @param CustomerNote $cn + * + * @return JsonResponse + * + * @throws GeneralException + */ + public function update( Request $r, CustomerNote $cn ): JsonResponse + { + /** @var User $user */ + $user = Auth::getUser(); + $old = clone( $cn ); + + $cn->title = $r->title; + $cn->note = $r->note ; + $cn->private = $r->public ? 0 : 1; + $cn->save(); + + // update the user's notes last read so he won't be told his own is new + $prefs = $user->prefs; + $prefs[ 'notes' ][ 'last_read' ][ $cn->customer_id ] = now()->format( 'Y-m-d H:i:s' ); + $user->prefs = $prefs; + $user->save(); + + if( $old->title !== $cn->title || $old->note !== $cn->note ) { + event( new CustomerNoteUpdatedEvent( $old, $cn, $user ) ); + } + + return response()->json( [ 'noteid' => $cn->id ] ); + } + + /** + * Get a customer note + * + * @param CustomerNote $cn customer note + * + * @return JsonResponse + */ + public function get( CustomerNote $cn ): JsonResponse + { + /** @var User $user */ + $user = Auth::getUser(); + + // these if's could be joined with '&&' but are separated for readability: + if( !$user->isSuperUser() ) { + if( $cn->private || $cn->customer_id !== Auth::getUser()->custid ) { + abort( 403, 'Insufficient Permissions.' ); + } + } + + $note = $cn->toArray(); + $note[ 'note_parsedown' ] = parsedown( $cn->note ); + $note[ 'created_at' ] = $cn->created_at->format( 'Y-m-d H:i:s' ); + + return response()->json( [ 'note' => $note ] ); + } + + /** + * Delete a customer note + * + * @param CustomerNote $cn customer note + * + * @return JsonResponse + * + * @throws GeneralException|Exception + */ + public function delete( CustomerNote $cn ) : JsonResponse + { + $on = clone( $cn ); + $cn->delete(); + event( new CustomerNoteDeletedEvent ( null , $on, Auth::getUser() ) ); + return response()->json( [ 'noteid' => $on->id ] ); + } + + /** + * Update the last read for this user + * + * @param Customer|null $c + * + * @return JsonResponse + */ + public function ping( Customer $c = null ): JsonResponse + { + /** @var User $u */ + $u = Auth::getUser(); + if( !$u->isSuperUser() ) { + $c = Auth::getUser()->customer; + } + + // update the last read for this user / customer combination + $prefs = $u->prefs; + $prefs[ "notes" ][ "last_read" ][ $c->id ] = now()->format( "Y-m-d H:i:s" ); + $u->prefs = $prefs; + $u->save(); + + return response()->json( true ); + } + + /** + * Watch/Unwatch all notes for a customer + * + * @param Customer $cust Customer + * + * @return JsonResponse + */ + public function notifyToggleCustomer( Customer $cust ): JsonResponse + { + return $this->notifyToggle( $cust, null ); + } + + /** + * Watch/Unwatch a note + * + * @param CustomerNote $cn + * + * @return JsonResponse + */ + public function notifyToggleNote( CustomerNote $cn ): JsonResponse + { + return $this->notifyToggle( null, $cn ); + } + + /** + * Watch/Unwatch a note or All notes for a customer + * + * @param Customer|null $cust + * @param CustomerNote|null $cn + * + * @return JsonResponse + */ + private function notifyToggle( Customer $cust = null, CustomerNote $cn = null ): JsonResponse + { + /** @var User $user */ + $user = Auth::getUser(); + $prefs = $user->prefs; + + if( $cust ){ + $result = 'Watch All'; + $index = 'customer_watching'; + $id = $cust->id; + } else { + $result = 'Watch'; + $index = 'note_watching'; + $id = $cn->id; + } + + if( isset( $prefs[ 'notes' ][ $index ][ $id ] ) ){ + // if exist we delete the entry to unwatch the customer + unset( $prefs[ 'notes' ][ $index ][ $id ] ); + } else { + // if doesnt exist we create the entry to watch the customer + $prefs[ 'notes' ][ $index ][ $id ] = now()->format( 'Y-m-d H:i:s' ); + $result = $cust ? 'Unwatch All' : 'Unwatch'; + } + + $user->prefs = $prefs; + $user->save(); + + return response()->json( $result ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Api/V4/CustomerController.php b/app/Http/Controllers/Api/V4/CustomerController.php new file mode 100644 index 000000000..83831daf0 --- /dev/null +++ b/app/Http/Controllers/Api/V4/CustomerController.php @@ -0,0 +1,122 @@ + + * @author Yann Robin + * @category APIv4 + * @package IXP\Http\Controllers\Api\V4 + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class CustomerController extends Controller +{ + /** + * Get the switches for a customer + * + * @param Request $r instance of the current HTTP request + * @param Customer $cust + * + * @return JsonResponse + */ + public function switches( Request $r, Customer $cust ): JsonResponse + { + $ppp = PatchPanel::findOrFail( $r->patch_panel_id ); + + $switches = Customer::select( [ 's.id AS id', 's.name' ] ) + ->from( 'cust AS c' ) + ->join( 'virtualinterface AS vi', 'vi.custid', 'c.id' ) + ->join( 'physicalinterface AS pi', 'pi.virtualinterfaceid', 'vi.id' ) + ->join( 'switchport AS sp', 'sp.id', 'pi.switchportid' ) + ->join( 'switch AS s', 's.id', 'sp.switchid' ) + ->join( 'cabinet AS cab', 'cab.id', 's.cabinetid' ) + ->where( 'cab.locationid', $ppp->cabinet->locationid ) + ->where( 'c.id', $cust->id ) + ->get()->keyBy( 'id' )->toArray(); + + return response()->json( [ 'hasSwitches' => (bool)count( $switches ) , 'switches' => $switches ] ); + } + + /** + * Get network information from PeeringDb by ASN + * + * For return information: + * @see \IXP\Services\PeeringDb::getNetworkByAsn() + * + * @param string $asn + * + * @return JsonResponse + */ + public function queryPeeringDbWithAsn( string $asn ): JsonResponse + { + $pdb = App::make( PeeringDb::class ); + + if( $response = $pdb->getNetworkByAsn( $asn ) ) { + return response()->json( [ 'net' => $response ] ); + } + + return response()->json( [ 'error' => $pdb->error ] ); + } + + /** + * Get Customer depending on the Vlan and Protocol + * + * @param Request $r instance of the current HTTP request + * + * @return JsonResponse + */ + public function byVlanAndProtocol( Request $r ): JsonResponse + { + $vlanid = null; + + if( $r->vlanid ) { + $vlan = Vlan::findOrFail( $r->vlanid ); + $vlanid = $vlan->id; + } + + if( !in_array( $protocol = $r->protocol, [ null, 4, 6 ], false ) ) { + abort( 404 ); + } + + return response()->json( [ 'listCustomers' => CustomerAggregator::getByVlanAndProtocol( $vlanid, $protocol ) ] ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Api/V4/DnsController.php b/app/Http/Controllers/Api/V4/DnsController.php new file mode 100644 index 000000000..65ee4937b --- /dev/null +++ b/app/Http/Controllers/Api/V4/DnsController.php @@ -0,0 +1,114 @@ + + * @author Yann Robin + * @category APIv4 + * @package IXP\Http\Controllers\Api\V4 + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class DnsController extends Controller +{ + /** + * API call to generate DNS ARPA records in a given format + * + * @param Vlan $vlan Vlan to generate the ARPA entries for (vlan.id) + * @param int $protocol Protocol to generate the ARPA entries for + * + * @return JsonResponse + */ + public function arpa( Vlan $vlan, int $protocol ): JsonResponse + { + return response()->json( $this->loadRecords( $vlan, $protocol ) ); + } + + /** + * Validate request details and load records + * + * @param Vlan $vlan Vlan to generate the ARPA entries for (vlan.id) + * @param int $protocol Protocol to generate the ARPA entries for + * + * @return array + * + * @throws + */ + private function loadRecords( Vlan $vlan, int $protocol ): array + { + if( !in_array( $protocol,[ 4,6 ] ) ) { + abort( 404, "Unknown protocol" ); + } + + return array_map( function( $e ) use ( $protocol ) { + $e['arpa'] = IpAddress::toArpa( $e['address'], $protocol ); + return $e; + }, + VlanAggregator::arpaDetails( $vlan, $protocol ) + ); + } + + /** + * API call to generate DNS ARPA records in a given format + * + * @param Vlan $vlan Vlan to generate the ARPA entries for (vlan.id) + * @param int $protocol Protocol to generate the ARPA entries for + * @param string $template The template to use to generate the response + * + * @return Response + */ + public function arpaTemplated( Vlan $vlan, int $protocol, string $template ): Response + { + $tmpl = sprintf('api/v4/dns/%s', preg_replace('/[^a-z0-9\-]/', '', strtolower( $template ) ) ); + + if( !FacadeView::exists( $tmpl ) ) { + abort(404, 'Unknown template'); + } + + return response() + ->view( $tmpl, + [ + 'arpa' => $this->loadRecords( $vlan , $protocol), + 'vlan' => $vlan, + 'protocol' => $protocol + ], 200 ) + ->header( 'Content-Type', 'text/plain; charset=utf-8' ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Api/V4/IrrdbPrefixController.php b/app/Http/Controllers/Api/V4/IrrdbPrefixController.php new file mode 100644 index 000000000..cbc2f2654 --- /dev/null +++ b/app/Http/Controllers/Api/V4/IrrdbPrefixController.php @@ -0,0 +1,71 @@ + + * @author Yann Robin + * @category APIv4 + * @package IXP\Http\Controllers\Api\V4 + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class IrrdbPrefixController extends Controller +{ + /** + * Get IrrdbPrefixes depending on the customer and Protocol + * + * @param Request $r instance of the current HTTP request + * + * @return JsonResponse + */ + public function byCustomerAndProtocol( Request $r ): JsonResponse + { + $cust = Customer::find( $r->custid ); + + if( !in_array( $protocol = $r->protocol, [ 4, 6 ], false ) ) { + abort( 404 ); + } + + $prefixes = false; + + if( $cust->maxprefixes < 2000 ){ + $prefixes = IrrdbPrefix::where( 'customer_id', $cust->id )->where( 'protocol', $protocol )->get()->toArray(); + } + + return response()->json( [ 'prefixes' => $prefixes ] ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Api/V4/Layer2AddressController.php b/app/Http/Controllers/Api/V4/Layer2AddressController.php new file mode 100644 index 000000000..050766168 --- /dev/null +++ b/app/Http/Controllers/Api/V4/Layer2AddressController.php @@ -0,0 +1,155 @@ + + * @author Yann Robin + * @category APIv4 + * @package IXP\Http\Controllers\Api\V4 + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Layer2AddressController extends Controller +{ + /** + * Create a mac address to a VLAN Interface + * + * @param Request $r instance of the current HTTP request + * @param bool $showFeMessage Should we show the frontend message ? + * + * @return JsonResponse + * + */ + public function store( Request $r, bool $showFeMessage = false ): JsonResponse + { + $vli = VlanInterface::findOrFail( $r->vlan_interface_id ); + /** @var User $user */ + $user = Auth::getUser(); + if( !$user->isSuperUser() ) { + if( !config( 'ixp_fe.layer2-addresses.customer_can_edit' ) ) { + abort( 404 ); + } + + if( $user->custid !== $vli->virtualInterface->custid ) { + abort( 403, 'VLI / Customer mismatch' ); + } + + if( $vli->layer2addresses()->count() >= config( 'ixp_fe.layer2-addresses.customer_params.max_addresses' ) ) { + !$showFeMessage ?: AlertContainer::push( 'The maximum possible MAC addresses have been configured. Please delete a MAC before adding.' , Alert::DANGER ); + return response()->json( [ 'danger' => false, 'message' => 'The maximum possible MAC addresses have been configured. Please delete a MAC before adding.' ] ); + } + } + + $mac = preg_replace( "/[^a-f0-9]/i", '' , strtolower( $r->mac ) ); + + if( strlen( $mac ) !== 12 ) { + !$showFeMessage ?: AlertContainer::push( 'Invalid or missing MAC addresses.' , Alert::DANGER ); + return response()->json( [ 'danger' => false, 'message' => 'Invalid or missing MAC addresses' ] ); + } + + // Get layer2address for a given vlan + $exist = Layer2Address::from( 'l2address AS l' ) + ->join( 'vlaninterface AS vli', 'vli.id', 'l.vlan_interface_id' ) + ->join( 'vlan AS v', 'v.id', 'vli.vlanid' ) + ->where( 'mac' , $mac ) + ->where( 'v.id', $vli->vlanid ) + ->count(); + + if( $exist ) { + !$showFeMessage ?: AlertContainer::push( 'The MAC address already exists within this IXP VLAN.' , Alert::DANGER ); + return response()->json( [ 'danger' => false, 'message' => 'The MAC address already exists within this IXP VLAN' ] ); + } + + $l2a = Layer2Address::create( [ + 'mac' => $mac, + 'vlan_interface_id' => $r->vlan_interface_id + ] ); + + event( new Layer2AddressAddedEvent( $l2a, User::find( Auth::id() ) ) ); + !$showFeMessage ?: AlertContainer::push( 'MAC address created.' , Alert::SUCCESS ); + return response()->json( [ 'success' => true, 'message' => 'MAC address created.' ] ); + } + + /** + * Delete a MAC address from a VLAN Interface + * + * @param Layer2Address $l2a + * @param bool $showFeMessage Should we show the frontend message ? + * + * @return JsonResponse + * + * @throws + */ + public function delete( Layer2Address $l2a, bool $showFeMessage = false ): JsonResponse + { + /** @var User $user */ + $user = Auth::getUser(); + if( !$user->isSuperUser() ) { + if( !config( 'ixp_fe.layer2-addresses.customer_can_edit' ) ) { + abort( 404 ); + } + + if( $user->custid !== $l2a->vlanInterface->virtualInterface->custid ) { + abort( 403, 'MAC address / Customer mismatch' ); + } + + if( $l2a->vlanInterface->layer2addresses->count() <= config( 'ixp_fe.layer2-addresses.customer_params.min_addresses' ) ) { + !$showFeMessage ?: AlertContainer::push( 'The minimum possible MAC addresses have been configured. Please add a MAC before deleting.' , Alert::DANGER ); + return response()->json( [ 'danger' => false, 'message' => 'The minimum possible MAC addresses have been configured. Please add a MAC before deleting.' ] ); + } + } + + $l2a->delete(); + + event( new Layer2AddressDeletedEvent( $l2a->macFormatted( ':' ), $l2a->vlanInterface, User::find( Auth::id() ) ) ); + !$showFeMessage ?: AlertContainer::push( 'MAC address deleted.' , Alert::SUCCESS ); + return response()->json( [ 'success' => true, 'message' => 'MAC address deleted.' ] ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Api/V4/MailingListController.php b/app/Http/Controllers/Api/V4/MailingListController.php new file mode 100644 index 000000000..244d7bbe2 --- /dev/null +++ b/app/Http/Controllers/Api/V4/MailingListController.php @@ -0,0 +1,165 @@ + + * @author Yann Robin + * @category APIv4 + * @package IXP\Http\Controllers\Api\V4 + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class MailingListController extends Controller +{ + /** + * @var ML + */ + private $ml = null; + + /** + * @var string + */ + private $mlkey = null; + + public function __construct() + { + if( PHP_SAPI !== 'cli' && !config( 'mailinglists.enabled' ) ) { + abort( 503, "Mailing list functionality is disabled. See: https://docs.ixpmanager.org/latest/features/mailing-lists/" ); + } + } + + /** + * For the given listname, return the appropriate mailing list object (or throw a 404 if not found) + * + * @param string $listname Name of the mailing list (array index from config/mailinglist.php) + * + * @return ML + */ + private function getMailingList( string $listname ): ML + { + if( $this->ml === null || $this->mlkey !== $listname ) { + try { + $this->ml = new ML( $listname ); + $this->mlkey = $listname; + } catch( MailingListException $e ) { + abort( 404, 'Mailing list not defined in config/mailinglists.php' ); + } + } + + return $this->ml; + } + + /** + * Mailing list subscribers action - list all addresses subscribed to the given list + * + * All emails are validated, normalised to lowercase, duplicates removed and sorted alphabetically. + * + * @param string @listname Name of the mailing list (array index from config/mailinglist.php) + * + * @return JsonResponse|Response + */ + public function subscribers( string $listname ): JsonResponse|Response + { + if( request()->is('api/v4/mailing-list/subscribers/json/*' ) ) { + return response()->json( $this->getMailingList( $listname )->getSubscriberEmails() ); + } + + return response( implode( "\n", $this->getMailingList( $listname )->getSubscriberEmails() ) . "\n", + 200, [ 'Content-Type' => 'text/plain; charset=utf-8' ] ); + } + + /** + * Mailing list unsubscribed action - list all addresses not subscribed to the given list + * + * All emails are validated, normalised to lowercase, duplicates removed and sorted alphabetically. + * + * @param string @listname Name of the mailing list (array index from config/mailinglist.php) + * + * @return JsonResponse|Response + */ + public function unsubscribed( string $listname ): JsonResponse|Response + { + if( request()->is('api/v4/mailing-list/unsubscribed/json/*' ) ) { + return response()->json( $this->getMailingList( $listname )->getSubscriberEmails(false ) ); + } + + return response( implode( "\n", $this->getMailingList( $listname )->getSubscriberEmails(false ) ) . "\n", + 200, [ 'Content-Type' => 'text/plain; charset=utf-8' ] ); + } + + /** + * Mailing list initialisation script + * + * First sets a user preference for ALL users *WITHOUT* a mailing list sub for this list to unsub'd. + * + * Then takes a list of *existing* mailing list addresses from stdin and: + * - is a user does not exist with same email, skips + * - if a user does exist with same email, sets his mailing list preference + * + * NB: This function is NON-DESTRUCTIVE. It will *NOT* affect any users with *EXISTING* settings + * but set those without a setting to on / off as appropriate. + * + * E.g.: for a given file test.txt with addresses from Mailman's list_members: + * + * curl --data "addresses=noc@blacknight.ie\nbarryo@inex.ie\nbarry@opensolutions.ie\nbarry@example.com\n" -X POST -H "X-IXP-Manager-API-Key: NIJm5aYpwrl1MQzgtUWXOx8i7DlVqinOfwfDbhorPRbmztH7" http://ixp-ibn.dev/api/v4/mailing-list/init/members + * + * @param Request $request + * @param string $listname Name of the mailing list (array index from config/mailinglist.php) + * + * @return JsonResponse|Response + */ + public function init( Request $request, string $listname ): JsonResponse|Response + { + $addresses = collect(); + + foreach( explode( "\n", $request->addresses ) as $a ) { + /** @psalm-suppress InvalidArgument - no issue here */ + $addresses->add( strtolower( trim( $a ) ) ); + } + + $result = $this->getMailingList( $listname )->init( $addresses ); + + if( request()->is('api/v4/mailing-list/init/json/*' ) ) { + return response()->json( $result ); + } + + $output = ""; + foreach( $result as $k => $v ) { + foreach( $v as $e ) { + $output .= "{$k}: {$e}\n"; + } + } + + return response( $output, 200, [ 'Content-Type' => 'text/plain; charset=utf-8' ] ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Api/V4/MemberExportController.php b/app/Http/Controllers/Api/V4/MemberExportController.php new file mode 100644 index 000000000..5873a12b6 --- /dev/null +++ b/app/Http/Controllers/Api/V4/MemberExportController.php @@ -0,0 +1,72 @@ + + * @author Yann Robin + * @category APIv4 + * @package IXP\Http\Controllers\Api\V4 + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class MemberExportController extends Controller +{ + /** + * API call to generate IX-F export in a given version + * + * @param Request $r + * @param string $version Version of schema to export + * + * @return JsonResponse + */ + public function ixf( Request $r, string $version = JsonSchemaExporter::EUROIX_JSON_LATEST ): JsonResponse + { + if( $r->access_key ) { + if( $r->access_key !== config( 'ixp_api.json_export_schema.access_key' ) ) { + abort( 401, 'Invalid access key' ); + } + } elseif( !Auth::check() && !config( 'ixp_api.json_export_schema.public', false ) ) { + abort(401, 'Public access not permitted' ); + } + + $withTags = $r->query('withtags', null) === "1"; + + $exporter = new JsonSchemaExporter; + + return response()->json( $exporter->get( $version, true, Auth::check(), $withTags ), 200, [], JSON_PRETTY_PRINT ) + ->header( "Access-Control-Allow-Origin", "*" ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Api/V4/NagiosController.php b/app/Http/Controllers/Api/V4/NagiosController.php new file mode 100644 index 000000000..806cfcf9b --- /dev/null +++ b/app/Http/Controllers/Api/V4/NagiosController.php @@ -0,0 +1,238 @@ + + * @author Yann Robin + * @category APIv4 + * @package IXP\Http\Controllers\Api\V4 + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class NagiosController extends Controller +{ + /** + * An API call to generate customer reachability Nagios configuration for a given VLAN and protocol. + * + * @see https://docs.ixpmanager.org/latest/features/nagios/ + * + * @param Request $r + * @param Vlan $vlan + * @param int $protocol + * @param string|null $template + * + * @return Response + */ + public function customers( Request $r, Vlan $vlan, int $protocol, string $template = null ): Response + { + if( !in_array( $protocol, [ 4, 6 ] ) ) { + abort( 404, 'Unknown protocol' ); + } + + if( $template === null ) { + $tmpl = 'api/v4/nagios/customers/default'; + } else { + $tmpl = sprintf( 'api/v4/nagios/customers/%s', preg_replace( '/[^a-z0-9\-]/', '', strtolower( $template ) ) ); + } + + if( !FacadeView::exists( $tmpl ) ) { + abort(404, 'Unknown template'); + } + + return response() + ->view( $tmpl, [ + 'vlan' => $vlan, + 'protocol' => $protocol, + 'vlis' => VlanInterfaceAggregator::forProto( $vlan, $protocol, PhysicalInterface::STATUS_CONNECTED ), + + // optional POST/GET parameters + 'host_definition' => $r->input( 'host_definition', 'ixp-manager-member-host' ), + 'service_definition' => $r->input( 'service_definition', 'ixp-manager-member-service' ), + 'ping_service_definition' => $r->input( 'ping_service_definition', 'ixp-manager-member-ping-service' ), + 'ping_busy_service_definition' => $r->input( 'ping_busy_service_definition', 'ixp-manager-member-ping-busy-service' ), + + ], 200 ) + ->header( 'Content-Type', 'text/plain; charset=utf-8' ); + } + + /** + * An API call to generate production switch host and hostgroups for Nagios configuration for a given infrastructure. + * + * @see https://docs.ixpmanager.org/latest/features/nagios/ + * + * @param Request $r + * @param Infrastructure $infra + * @param string|null $template + * + * @return Response + */ + public function switches( Request $r, Infrastructure $infra, string $template = null ): Response + { + if( $template === null ) { + $tmpl = 'api/v4/nagios/switches/default'; + } else { + $tmpl = sprintf( 'api/v4/nagios/switches/%s', preg_replace( '/[^a-z0-9\-]/', '', strtolower( $template ) ) ); + } + + if( !FacadeView::exists( $tmpl ) ) { + abort(404, 'Unknown template'); + } + + return response() + ->view( $tmpl, [ + 'infra' => $infra, + 'switches' => $infra->switchers, + + // optional POST/GET parameters + 'host_definition' => $r->input( 'host_definition', 'ixp-manager-production-switch' ), + ], 200 ) + ->header( 'Content-Type', 'text/plain; charset=utf-8' ); + } + + /** + * An API call to generate Birdseye daemon checks for Nagios configuration for all or a given vlan. + * + * @see https://docs.ixpmanager.org/latest/features/nagios/ + * + * @param Request $r + * @param string|null $template + * @param Vlan|null $vlan + * + * @return Response + */ + public function birdseyeDaemons( Request $r, string $template = null, Vlan $vlan = null ): Response + { + $routers = Router::where( 'api_type', Router::API_TYPE_BIRDSEYE ) + ->when( $vlan, function( Builder $q, $vlan ) { + return $q->where( 'vlan_id', $vlan->id ); + } )->orderBy( 'handle' )->get(); + + if( !$routers->count() ) { + abort( 404, "No routers for the provided VLAN ID / Bird's Eye API type." ); + } + + if( $template === null ) { + $tmpl = 'api/v4/nagios/birdseye-daemons/default'; + } else { + $tmpl = sprintf( 'api/v4/nagios/birdseye-daemons/%s', preg_replace( '/[^a-z0-9\-]/', '', strtolower( $template ) ) ); + } + + if( !FacadeView::exists( $tmpl ) ) { + abort(404, 'Unknown template'); + } + + return response() + ->view( $tmpl, [ + 'routers' => $routers, + 'vlanid' => $vlan->id ?? false, + + // optional POST/GET parameters + 'host_definition' => $r->input( 'host_definition', 'ixp-manager-host-birdseye-daemon' ), + 'service_definition' => $r->input( 'host_definition', 'ixp-manager-service-birdseye-daemon' ), + + ], 200) + ->header('Content-Type', 'text/plain; charset=utf-8'); + } + + + /** + * An API call to generate customer BGP session checks for Nagios for a given router type, VLAN and protocol. + * + * @see https://docs.ixpmanager.org/latest/features/nagios/ + * + * @param Request $r + * @param Vlan $vlan + * @param int $protocol + * @param int $type + * @param string|null $template + * + * @return Response + */ + public function birdseyeBgpSessions( Request $r, Vlan $vlan, int $protocol, int $type, string $template = null ): Response + { + if( !in_array( $protocol, [ 4, 6 ] ) ) { + abort( 404, 'Unknown protocol' ); + } + + if( !isset( Router::$TYPES[ $type ] ) ) { + abort( 404, 'Unknown router type' ); + } + + if( $template === null ) { + $tmpl = 'api/v4/nagios/birdseye-bgp-sessions/default'; + } else { + $tmpl = sprintf( 'api/v4/nagios/birdseye-bgp-sessions/%s', preg_replace( '/[^a-z0-9\-]/', '', strtolower( $template ) ) ); + } + + if( !FacadeView::exists( $tmpl ) ) { + abort(404, 'Unknown template'); + } + + $routers = Router::where( 'api_type', Router::API_TYPE_BIRDSEYE ) + ->where( 'type', $type ) + ->where( 'protocol', $protocol ) + ->where( 'vlan_id', $vlan->id ) + ->orderBy( 'handle' )->get(); + + if( !$routers->count() ) { + abort( 404, "No suitable router(s) found." ); + } + + return response() + ->view( $tmpl, [ + 'vlan' => $vlan, + 'protocol' => $protocol, + 'type' => $type, + 'typeName' => Router::$TYPES[ $type ], + 'typeShort' => strtolower( Router::$TYPES_SHORT[ $type ] ), + 'routers' => $routers, + 'vlis' => VlanInterfaceAggregator::forProto( $vlan, $protocol, PhysicalInterface::STATUS_CONNECTED ), + + // optional POST/GET parameters + 'service_definition' => $r->input( 'service_definition', 'ixp-manager-member-bgp-session-service' ), + + ], 200 ) + ->header( 'Content-Type', 'text/plain; charset=utf-8' ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Api/V4/PatchPanelController.php b/app/Http/Controllers/Api/V4/PatchPanelController.php new file mode 100644 index 000000000..036ee91ed --- /dev/null +++ b/app/Http/Controllers/Api/V4/PatchPanelController.php @@ -0,0 +1,76 @@ + + * @author Barry O'Donovan + * @category APIv4 + * @package IXP\Http\Controllers\Api\V4 + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class PatchPanelController extends Controller +{ + /** + * Get the patch panel ports available for a patch panel + * + * @param Request $r instance of the current HTTP request + * @param PatchPanel $pp the patch panel + * + * @return JsonResponse + */ + public function freePort( Request $r, PatchPanel $pp ): JsonResponse + { + return response()->json( [ + 'ports' => PatchPanelPortAggregator::getAvailablePorts( $pp->id, [ $r->pppid ] ) + ]); + } + + /** + * Get the patch panel ports duplex available for a patch panel + * + * @param Request $r instance of the current HTTP request + * @param PatchPanel $pp the patch panel + * + * @return JsonResponse + */ + public function freeDuplexPort( Request $r, PatchPanel $pp ): JsonResponse + { + return response()->json( [ + 'ports' => PatchPanelPortAggregator::getAvailablePorts( $pp->id, [ $r->pppid ], null, false ) + ]); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Api/V4/PatchPanelPortController.php b/app/Http/Controllers/Api/V4/PatchPanelPortController.php new file mode 100644 index 000000000..045a88593 --- /dev/null +++ b/app/Http/Controllers/Api/V4/PatchPanelPortController.php @@ -0,0 +1,75 @@ + + * @author Barry O'Donovan + * @category APIv4 + * @package IXP\Http\Controllers\Api\V4 + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class PatchPanelPortController extends Controller +{ + /** + * Get the details of a patch panel port + * + * @param PatchPanelPort $ppp The ID of the patch panel port to query + * @param bool $deep Return a deep array by including associated objects + * + * @return JsonResponse JSON customer object + */ + public function detail( PatchPanelPort $ppp, bool $deep = false ): JsonResponse + { + return response()->json( + PatchPanelPort::where( 'id', $ppp->id ) + ->with( 'PatchPanelPortFiles' ) + ->when( $deep, function( Builder $q ) { + return $q->with( [ 'PatchPanel', 'SwitchPort.physicalInterface' ] ); + } ) + ->first() + ); + } + + /** + * Get extra details of a patch panel port + * + * @param PatchPanelPort $ppp Patch panel port to query + * + * @return JsonResponse JSON customer object + */ + public function detailDeep( PatchPanelPort $ppp ): JsonResponse + { + return $this->detail( $ppp, true ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Api/V4/Provisioner/YamlController.php b/app/Http/Controllers/Api/V4/Provisioner/YamlController.php new file mode 100644 index 000000000..b827d4db4 --- /dev/null +++ b/app/Http/Controllers/Api/V4/Provisioner/YamlController.php @@ -0,0 +1,484 @@ + + * @author Yann Robin + * @category APIv4 + * @package IXP\Http\Controllers\Api\V4\Provisioner + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class YamlController extends Controller +{ + /** + * Generate a formatted output version of the given structure. + * + * This takes two arguments: the array structure and the output format. + * + * @param array $array + * @param string $format + * + * @return Response + * + * @throws + */ + private function structuredResponse( array $array, string $format ): Response + { + $output = null; + $contentType = 'text/plain; charset=utf-8'; + $httpResponse = 200; + + switch ( $format ) { + case 'yaml': + /** @psalm-suppress UndefinedConstant */ + $output = yaml_emit( $array, YAML_UTF8_ENCODING ); + break; + case 'json': + $output = json_encode($array, + JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)."\n"; + $contentType = 'application/json'; + break; + } + + if ( !$output ) { + $httpResponse = 200; + } + + return response( $output, $httpResponse )->header('Content-Type', $contentType ); + } + + /** + * Generate a Yaml configuration file for a given switch + * + * This just takes one argument: the router handle to generate the configuration for. All + * other parameters are defined by the handle's array in config/router.php. + * + * @param Switcher $switch + * @param string $format + * + * @return Response + * + * @throws + */ + public function forSwitch( Switcher $switch, string $format ): Response + { + return $this->structuredResponse( ( new SwitchConfigurationGenerator( $switch ) )->generate(), $format ); + } + + /** + * Generate a Yaml configuration file for a given switch + * + * This just takes one argument: the router handle to generate the configuration for. All + * other parameters are defined by the handle's array in config/router.php. + * + * @param string $switchname + * @param string $format + * + * @return Response + */ + public function forSwitchByName( string $switchname, string $format ): Response + { + if( !( $switch = Switcher::whereName( $switchname )->first() ) ) { + abort( 404, "Unknown switch" ); + } + + return $this->forSwitch( $switch, $format ); + } + + /** + * Generate a Yaml file of the vlans for a given switch id + * + * This takes one argument: the router handle to generate the vlans for. All + * other parameters are defined by the handle's array in config/router.php. + * + * @param Switcher $switch + * @param string $format + * + * @return Response + * + * @throws + */ + public function vlansForSwitch( Switcher $switch, string $format ): Response + { + $listVlans['vlans'] = Vlan::selectRaw( 'v.name, v.number as tag, v.private, v.config_name' ) + ->from( 'vlan AS v' ) + ->leftJoin( 'infrastructure AS i', 'i.id', 'v.infrastructureid' ) + ->leftJoin( 'switch AS s', 's.infrastructure', 'i.id' ) + ->where( 's.id', $switch->id )->orderBy( 'config_name' )->get()->toArray(); + + return $this->structuredResponse( $listVlans, $format ); + } + + /** + * Generate a Yaml file of the vlans for a given switch name + * + * This just takes one argument: the router name to generate the configuration for. All + * other parameters are handled by the vlansForSwitch() function. + * + * @param string $switchname + * @param string $format + * + * @return Response + */ + public function vlansForSwitchByName( string $switchname, string $format ): Response + { + if( !( $switch = Switcher::whereName( $switchname )->first() ) ) { + abort( 404, "Unknown switch" ); + } + + return $this->vlansForSwitch( $switch, $format ); + } + + /** + * Generate a list of switches + * + * @param string $format + * + * @return Response + * + * @throws + */ + public function listSwitch( string $format ): Response + { + $switches = Switcher::selectRaw( 's.name AS name, s.id AS id, i.id AS infrastructure, s.active AS active' ) + ->from( 'switch AS s' ) + ->leftJoin( 'infrastructure AS i', 'i.id', 's.infrastructure' ) + ->orderByRaw( 'i.id, s.id ASC' )->get()->toArray(); + + + return $this->structuredResponse( [ 'switches' => $switches ] , $format ); + } + + /** + * Generate a Yaml/JSON response for a switch + * + * @param Switcher $switch + * @param string $format + * + * @return Response + * + * @throws + */ + public function showSwitch( Switcher $switch, string $format ): Response + { + return $this->structuredResponse( $this->showSwitchRestructureOutput( $switch ), $format ); + } + + /** + * Generate a Yaml/JSON response for a switch + * + * @param string $switchname + * @param string $format + * + * @return Response + * + * @throws + */ + public function showSwitchByName( string $switchname, string $format ): Response + { + if( !( $switch = Switcher::whereName( $switchname )->first() ) ) { + abort( 404, "Unknown switch" ); + } + + return $this->structuredResponse( $this->showSwitchRestructureOutput( $switch ), $format ); + } + + /** + * Restructure the output from showSwitch. + * + * @param Switcher $switch + * + * @return array + * + * @psalm-return array{switch: mixed} + */ + private function showSwitchRestructureOutput( Switcher $switch ): array + { + $data = $switch->toArray(); + foreach ( ['name', 'asn', 'hostname', 'loopback_ip', 'loopback_name', 'ipv4addr', + 'ipv6addr', 'model', 'active', 'os', 'id' ] as $key ) { + if ( !is_null( $data[ $key ] ) && $data[ $key ] !== '' ) { + $output[ $key ] = $data[ $key ]; + } + } + + if( $data[ 'mgmt_mac_address' ] ) { $output[ 'macaddress' ] = implode(':', str_split( $data[ 'mgmt_mac_address' ], 2 ) ); } + if( $data[ 'serialNumber' ] ) { $output[ 'serial' ] = $data[ 'serialNumber' ]; } + if( $data[ 'lastPolled' ] ) { $output[ 'lastpolled' ] = Carbon::parse( $data[ 'lastPolled' ] )->format('c'); } + if( $data[ 'osVersion' ] ) { $output[ 'osversion' ] = $data[ 'osVersion' ]; } + if( $data[ 'snmppasswd' ] ) { $output[ 'snmpcommunity' ] = $data[ 'snmppasswd' ]; } + + return array("switch" => $output); + } + + + /** + * Generate a Yaml file of the core link interfaces for a given switch id + * + * This just takes one argument: the router handle to generate the configuration for. All + * other parameters are defined by the handle's array in config/router.php. + * + * @param Switcher $switch + * @param string $format + * + * @return Response + * + * @throws + */ + public function coreLinkForSwitch( Switcher $switch, string $format ): Response + { + $cis = []; + foreach( [ 'A', 'B' ] as $side ) { + $listCoreInterface = CoreBundle::selectRaw( "cb.type, cb.ipv4_subnet as cbSubnet, cb.enabled as cbEnabled, + cl.enabled as clEnabled, cb.description, cl.bfd, sp{$side}.name, + pi{$side}.speed, pi{$side}.autoneg, cl.ipv4_subnet as clSubnet, s{$side}.id as saId" ) + ->from( 'corebundles AS cb' ) + ->leftJoin( 'corelinks AS cl', 'cl.core_bundle_id', 'cb.id' ) + ->leftJoin( "coreinterfaces AS ci{$side}", "ci{$side}.id", "cl.core_interface_side{$side}_id" ) + ->leftJoin( "physicalinterface AS pi{$side}", "pi{$side}.id", "ci{$side}.physical_interface_id" ) + ->leftJoin( "switchport AS sp{$side}", "sp{$side}.id", "pi{$side}.switchportid" ) + ->leftJoin( "switch AS s{$side}", "s{$side}.id", "sp{$side}.switchid" ) + ->whereIn( 'cb.type', [ CoreBundle::TYPE_ECMP, CoreBundle::TYPE_L3_LAG ] ) + ->where( "s{$side}.id", $switch->id )->get()->toArray(); + + foreach( $listCoreInterface as $ci ) { + $export = []; + $subnet = ( $ci[ 'type' ] === CoreBundle::TYPE_ECMP ) ? $ci[ 'clSubnet' ] : $ci[ 'cbSubnet' ]; + + $export[ 'ipv4' ] = SwitcherAggregator::linkAddr( $subnet, $side, true ); + $export[ 'description' ] = $ci[ 'description' ]; + $export[ 'bfd' ] = (bool)$ci[ 'bfd' ]; + $export[ 'speed' ] = $ci[ 'speed' ]; + $export[ 'name' ] = $ci[ 'name' ]; + $export[ 'autoneg' ] = (bool)$ci[ 'autoneg' ]; + $export[ 'shutdown' ] = !( $ci[ 'cbEnabled' ] && $ci[ 'clEnabled' ] ); + + $cis[] = $export; + } + } + + if( $switch->loopback_ip && $switch->loopback_name ) { + $ci2['description'] = 'Loopback interface'; + $ci2['loopback'] = true; + $ci2['ipv4'] = $switch->loopback_ip . '/32'; + $ci2['name'] = $switch->loopback_name; + $ci2['shutdown'] = false; + + $cis[] = $ci2; + } + + return $this->structuredResponse( [ 'layer3interfaces' => $cis ] , $format ); + } + + /** + * Generate a Yaml file of the core link interfaces for a given switch name + * + * This just takes one argument: the router name to generate the configuration for. All + * other parameters are handled by the coreLinkForSwitch() function. + * + * @param string $switchname + * @param string $format + * + * @return Response + */ + public function coreLinkForSwitchByName( string $switchname, string $format ): Response + { + if( !( $switch = Switcher::whereName( $switchname )->first() ) ) { + abort( 404, "Unknown switch" ); + } + + return $this->coreLinkForSwitch( $switch, $format ); + } + + + /** + * Generate a Yaml file of the BGP for a given switch id + * + * This just takes one argument: the router handle to generate the configuration for. All + * other parameters are defined by the handle's array in config/router.php. + * + * @param Switcher $switch + * @param string $format + * + * @return Response + * + * @throws + */ + public function bgpForSwitch( Switcher $switch, string $format ): Response + { + $listFlood = Switcher::selectRaw( 'loopback_ip' ) + ->where( 'infrastructure', function( $q ) use( $switch ) { + $q->selectRaw( 'i.id' ) + ->from('switch AS s2') + ->leftJoin('infrastructure AS i', 'i.id', 's2.infrastructure' ) + ->where( 's2.id', $switch->id ); + }) + ->whereNotNull( 'loopback_ip' ) + ->where( 'active', true ) + ->where( 'id', '!=', $switch->id ) + ->get()->pluck( 'loopback_ip' )->toArray(); + + $listNeighbors = []; + foreach( SwitcherAggregator::coreBundleNeighbors( $switch ) as $bgp ){ + $side = ( $bgp[ 'sAid' ] === $switch->id ) ? 'B' : 'A'; + $subnet = ( $bgp[ 'type' ] === CoreBundle::TYPE_ECMP ) ? $bgp['clSubnet'] : $bgp['cbSubnet']; + $listNeighbors[] = [ + 'ip' => SwitcherAggregator::linkAddr( $subnet , $side , false ), + 'description' => $bgp[ 's' .$side. 'name'], + 'asn' => $bgp[ 's' .$side. 'asn'], + 'cost' => $bgp[ 'cost'], + 'preference' => $bgp[ 'preference'], + ] ; + } + + $listAdjacentASNs = []; + foreach( SwitcherAggregator::coreBundleNeighbors( $switch ) as $bgp ){ + $side = ( $bgp[ 'sAid' ] === $switch->id ) ? 'B' : 'A'; + $remoteAsn = $bgp[ 's' .$side. 'asn']; + $listAdjacentASNs[ $remoteAsn ] = [ + 'description' => $bgp[ 's' .$side. 'name'], + 'asn' => $bgp[ 's' .$side. 'asn'], + 'cost' => $bgp[ 'cost'], + 'preference' => $bgp[ 'preference'], + ] ; + } + + $out[ 'bgp' ][ 'floodlist' ] = $listFlood; + $out[ 'bgp' ][ 'adjacentasns' ] = $listAdjacentASNs; + $out[ 'bgp' ][ 'routerid' ] = $switch->loopback_ip; + $out[ 'bgp' ][ 'local_as' ] = $switch->asn; + + $pgentry = null; + foreach( $listNeighbors as $neighbor ) { + $n = []; + $n[ 'description' ] = $neighbor[ 'description' ]; + $n[ 'remote_as' ] = $neighbor[ 'asn' ]; + $n[ 'cost' ] = $neighbor[ 'cost' ]; + $n[ 'preference' ] = $neighbor[ 'preference' ]; + $pgentry[ $neighbor[ 'ip' ] ] = $n; + } + + # XXX replace pg-ebgp-ipv4-ixp with dynamic value + $out[ 'bgp' ][ 'out' ][ 'pg-ebgp-ipv4-ixp' ][ 'neighbors' ] = $pgentry; + + return $this->structuredResponse( $out, $format ); + } + + /** + * Generate a Yaml file of the BGP for a given switch name + * + * This just takes one argument: the router name to generate the configuration for. All + * other parameters are handled by the coreLinkForSwitch() function. + * + * @param string $switchname + * @param string $format + * + * @return Response + */ + public function bgpForSwitchByName( string $switchname, string $format ): Response + { + if( !( $switch = Switcher::whereName( $switchname )->first() ) ) { + abort( 404, "Unknown switch" ); + } + + return $this->bgpForSwitch( $switch, $format ); + } + + /** + * Generate a list of Core Bundles + * + * @return Response + * + * @throws + */ + public function listCoreBundle( string $format ): Response + { + foreach( CoreBundle::all() as $cb ) { + $entry = []; + + $switchSideA = $cb->switchSideX(); + $switchSideB = $cb->switchSideX( false ); + + $entry['id'] = $cb->id; + $entry['description'] = $cb->description; + $entry['graphtitle'] = $cb->graph_title; + $entry['cost'] = $cb->cost; + $entry['preference'] = $cb->preference; + $entry['enabled'] = (bool)$cb->enabled; + $entry['type'] = $cb->type; + /** @psalm-suppress InvalidPropertyFetch */ + $entry['switchsidea'] = $switchSideA !== false ? $switchSideA->name : null; + /** @psalm-suppress InvalidPropertyFetch */ + $entry['switchsideb'] = $switchSideB !== false ? $switchSideB->name : null; + + $speed = $cb->corelinks()->count() * $cb->speedPi() * 1000000; + + $entry['bandwidth'] = $speed; + + $formats = [ "bits", "K", "M", "G", "T" ]; + $nb = count( $formats ); + for( $i = 0; $i < $nb; $i++ ) { + if( ( $speed / 1000.0 < 1.0 ) || ( count( $formats ) === $i + 1 ) ) { + $offset = min( $i, 4 ); + $prettybandwidth = round( $speed ) . $formats[ $offset ]; + break; + } + $speed /= 1000.0; + } + + $entry['prettybandwidth'] = $prettybandwidth; + + $corelinkids = []; + foreach ($cb->corelinks as $cl) { + $corelinkids[] = $cl->id; + $entry['infrastructure'] = $cl->coreInterfaceSideA->physicalInterface->switchPort->switcher->infrastructure; + } + $entry['corelinks'] = $corelinkids; + + $corebundles['corebundles'][] = $entry; + } + + return $this->structuredResponse( $corebundles, $format ); + } + +} diff --git a/app/Http/Controllers/Api/V4/PublicController.php b/app/Http/Controllers/Api/V4/PublicController.php new file mode 100644 index 000000000..dd750684a --- /dev/null +++ b/app/Http/Controllers/Api/V4/PublicController.php @@ -0,0 +1,102 @@ + + * @author Yann Robin + * @category APIv4 + * @package IXP\Http\Controllers\Api\V4 + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class PublicController extends Controller +{ + /** + * Simple test route for API authentication + * + * Documented at: https://docs.ixpmanager.org/latest/features/api/ + * + * @return Response + * + * @throws + */ + public function test( Request $r ): Response|JsonResponse + { + if( $r->get( 'format' ) !== 'json' ) { + return response()->make( "API Test Function!\n\nAuthenticated: " + . ( Auth::check() ? 'Yes, as: ' . Auth::getUser()->username : 'No' ) . "\n\n", 200 ) + ->header( 'Content-Type', 'text/plain; charset=utf-8' ); + } + + if( !Auth::check() ) { + return response()->json(['authenticated' => false ]); + } + + return response()->json([ + 'authenticated' => true, + 'user_id' => Auth::getUser()->id, + 'username' => Auth::getUser()->username, + 'priv' => Auth::getUser()->privs, + 'current_customer_id' => Auth::getUser()->customer->id, + 'current_customer' => Auth::getUser()->customer->name, + ]); + + } + + /** + * Simple ping route for basic public information. + * + * @return JsonResponse + */ + public function ping(): JsonResponse + { + /** @psalm-suppress UndefinedConstant */ + return response()->json([ + 'software' => "IXP Manager", + 'version' => APPLICATION_VERSION, + 'verdate' => APPLICATION_VERDATE, + 'url' => url(''), + 'ixf-export' => config( 'ixp_api.json_export_schema.public' ), + 'infrastructures' => Infrastructure::select( [ 'i.name', 'i.shortname', 'i.ixf_ix_id', 'i.peeringdb_ix_id' ] ) + ->from( 'infrastructure AS i' )->get()->toArray(), + 'identity' => [ + 'sitename' => config( 'identity.sitename' ), + 'legalname' => config( 'identity.legalname' ), + 'orgname' => config( 'identity.orgname' ), + 'corp_url' => config( 'identity.corporate_url' ), + 'city' => config( 'identity.location.city' ), + 'country' => config( 'identity.location.country' ), + ], + ], 200, [], JSON_PRETTY_PRINT ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Api/V4/RipeAtlasController.php b/app/Http/Controllers/Api/V4/RipeAtlasController.php new file mode 100644 index 000000000..c4dd30e0a --- /dev/null +++ b/app/Http/Controllers/Api/V4/RipeAtlasController.php @@ -0,0 +1,109 @@ + + * @category APIv4 + * @package IXP\Http\Controllers\Api\V4 + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class RipeAtlasController extends Controller +{ + /** + * Get the detail of an Atlas measurement via Ripe atlas API + * + * @param int $atlasId + * + * @return (bool|string)[] + * + * @throws + * + * @psalm-return array{error: bool, response: string} + */ + public function getAtlasMeasurementDetail( int $atlasId ): array + { + $apiUrl = "https://atlas.ripe.net/api/v2/measurements/" . $atlasId . "?format=json"; + + $client = new GuzzleHttp(); + + try { + $req = $client->request( 'GET', $apiUrl ); + + if( $req->getStatusCode() === 200 ) { + return [ 'error' => false, 'response' => $req->getBody()->getContents() ]; + + } + } catch (RequestException $e) { + if( $e->hasResponse() ) { + return [ 'error' => true, 'response' => (string)$e->getResponse()->getBody() ]; + } + + return [ 'error' => true, 'response' => $e->getMessage() ]; + } + + return [ 'error' => true, 'response' => "Atlas measurement information API request failed for {$atlasId}" ]; + } + + /** + * Get the detail of an Atlas probe via Ripe atlas API + * + * @param int $atlasid + * + * @return (bool|string)[] + * + * @throws + * + * @psalm-return array{error: bool, response: string} + */ + public function getAtlasProbeDetail( int $atlasid ): array + { + $apiUrl = "https://atlas.ripe.net/api/v2/probes/" . $atlasid . "?format=json"; + + $client = new GuzzleHttp(); + + try { + $req = $client->request( 'GET', $apiUrl ); + + if( $req->getStatusCode() === 200 ) { + return [ 'error' => false, 'response' => $req->getBody()->getContents() ]; + } + + } catch ( RequestException $e ) { + if( $e->hasResponse() ) { + return [ 'error' => true, 'response' => (string)$e->getResponse()->getBody() ]; + } + + return [ 'error' => true, 'response' => $e->getMessage() ]; + } + + return [ 'error' => true, 'response' => "Atlas Probe info API request failed for {$atlasid}" ]; + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Api/V4/RouterController.php b/app/Http/Controllers/Api/V4/RouterController.php new file mode 100644 index 000000000..4b2ff6ba2 --- /dev/null +++ b/app/Http/Controllers/Api/V4/RouterController.php @@ -0,0 +1,268 @@ + + * @author Yann Robin + * @category APIv4 + * @package IXP\Http\Controllers\Api\V4 + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class RouterController extends Controller +{ + /** + * Generate a configuration. + * + * This just takes one argument: the router handle to generate the configuration for. + * + * @param string $handle + * + * @return Response + * + * @throws + */ + public function genConfig( string $handle ): Response + { + if( !( $router = Router::whereHandle( $handle )->first() ) ) { + abort( 404, "Unknown router handle" ); + } + + $configView = ( new RouterConfigurationGenerator( $router ) )->render()->render(); + + /** @psalm-suppress UndefinedConstant */ + \Illuminate\Support\Facades\Log::info( sprintf( "Generated router configuration for %s and used %0.1f MB ( %0.1f MB real) of memory in %0.3f seconds.", + $router->handle, memory_get_peak_usage() / 1024 / 1024, memory_get_peak_usage( true ) / 1024 / 1024, + microtime( true ) - LARAVEL_START ) + ); + + return response( $configView, 200 ) + ->header('Content-Type', 'text/plain'); + } + + + /** + * Get 'last_updated' for the router with the handle provided + * + * Returns the JSON version of the array: + * + * [ + * 'last_update_started' => '2017-05-23T13:50:25+00:00', + * 'last_update_started_unix' => 1495547425 + * 'last_updated' => '2017-05-23T13:50:45+00:00', + * 'last_updated_unix' => 1495547445 + * ] + * + * @param string $handle Handle of the router that we want + * + * @return JsonResponse + */ + public function getLastUpdated( string $handle ) : JsonResponse + { + if( !( $r = Router::whereHandle( $handle )->first() ) ) { + abort( 404, "Unknown router handle" ); + } + + return response()->json( $this->lastUpdatedArray( $r ) ); + } + + /** + * Set `last_update_started` to the current datetime (now) if + * we can update the router. + * + * @param string $handle Handle of the router that we want + * + * @return JsonResponse|Response + */ + public function getUpdateLock( string $handle ): Response|JsonResponse + { + if( !( $r = Router::whereHandle( $handle )->first() ) ) { + abort( 404, "Unknown router handle" ); + } + + if( $r->canUpdate( true ) ) { + $r->refresh(); + return response()->json( $this->lastUpdatedArray( $r ) ); + } + + return response( 'Router not available for update', 423 ) + ->header('Content-Type', 'text/plain'); + } + + /** + * Set `last_updated` to the current datetime (now) + * + * @param string $handle Handle of the router that we want + * + * @return JsonResponse + */ + public function setLastUpdated( string $handle ): JsonResponse + { + if( !( $r = Router::whereHandle( $handle )->first() ) ) { + abort( 404, "Unknown router handle" ); + } + + $r->update( [ 'last_updated' => now() ] ); + + return response()->json( $this->lastUpdatedArray( $r ) ); + } + + /** + * Get 'last_updated' for all routers + * + * Returns the JSON version of the array: + * + * [ + * 'handle' => [ + * 'last_updated' => '2017-05-23T13:50:45+00:00', + * 'last_updated_unix' => 1495547445 + * ], + * ... + * ] + * + * @return JsonResponse + */ + public function getAllLastUpdated(): JsonResponse + { + $result = []; + foreach( Router::all() as $r ) { + $result[ $r->handle ] = $this->lastUpdatedArray( $r ); + } + return response()->json( $result ); + } + + /** + * Get 'last_updated' for all routers where the last updated time exceeds the given number of seconds + * + * Returns the JSON version of the array: + * + * [ + * 'handle' => [ + * 'last_updated' => '2017-05-23T13:50:45+00:00', + * 'last_updated_unix' => 1495547445 + * ], + * ... + * ] + * + * @param int $threshold + * + * @return JsonResponse + */ + public function getAllLastUpdatedBefore( int $threshold ): JsonResponse + { + $result = []; + foreach( Router::all() as $r ) { + if( $r->last_updated && $r->lastUpdatedGreaterThanSeconds( $threshold ) ) { + $result[ $r->handle ] = $this->lastUpdatedArray( $r ); + } + } + + return response()->json( $result ); + } + + /** + * Find any routers that are stuck in a configuration upgrade / locked for longer than $threashold seconds + * + * Returns the JSON version of the array: + * + * [ + * "handle" => [ + * "last_update_started" => "2024-05-23T19:55:29+01:00", + * "last_update_started_unix" => 1716490529, + * "last_updated" => '2024-05-23T19:55:28+01:00', + * "last_updated_unix" => 1716490528 + * ], + * ... + * ] + * + * @param int $threshold + * + * @return JsonResponse + */ + public function getAllLockedLongerThan( int $threshold ): JsonResponse + { + $result = []; + foreach( Router::all() as $r ) { + + if( $r->pause_updates ) { + continue; // skip paused routers + } + + if( !$r->last_update_started && !$r->last_updated ) { + continue; // never updated / never used + } + + if( $r->last_update_started && $r->last_updated && $r->last_updated->gte( $r->last_update_started ) ) { + continue; + } + + if( !$r->last_updated && $r->last_update_started->diffInSeconds( Carbon::now() ) > $threshold ) { + $result[ $r->handle ] = $this->lastUpdatedArray( $r ); + continue; + } + + if( $r->last_updated && $r->last_updated->diffInSeconds( $r->last_update_started ) >= $threshold ) { + $result[ $r->handle ] = $this->lastUpdatedArray( $r ); + } + + } + + return response()->json( $result ); + } + + /** + * Format the router's last updated datetime as an array + * + * @param Router $r + * + * @return (float|int|null|string)[] + * + * @psalm-return array{last_update_started: null|string, last_update_started_unix: float|int|null|string, last_updated: null|string, last_updated_unix: float|int|null|string} + */ + private function lastUpdatedArray( Router $r ): array + { + return [ + 'last_update_started' => $r->last_update_started ? $r->last_update_started->toIso8601String() : null, + 'last_update_started_unix' => $r->last_update_started ? $r->last_update_started->timestamp : null, + + 'last_updated' => $r->last_updated ? $r->last_updated->toIso8601String() : null, + 'last_updated_unix' => $r->last_updated ? $r->last_updated->timestamp : null, + ]; + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Api/V4/SflowReceiverController.php b/app/Http/Controllers/Api/V4/SflowReceiverController.php new file mode 100644 index 000000000..40cca3a77 --- /dev/null +++ b/app/Http/Controllers/Api/V4/SflowReceiverController.php @@ -0,0 +1,164 @@ + + * @author Yann Robin + * @category APIv4 + * @package IXP\Http\Controllers\Api\V4 + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class SflowReceiverController extends Controller +{ + /** + * + * @return Response + */ + public function pretagMap(): Response + { + $map = []; + + foreach( SflowReceiver::all() as $sr ) { + foreach( $sr->virtualInterface->macAddresses as $mac ) { + // looks like there's some crud in the MAC table so filter that: + if( strlen( $mac->mac ) !== 12 ) { + continue; + } + + $m['virtualinterfaceid'] = $sr->virtual_interface_id; + $m['mac'] = $mac->macColonsFormatted(); + $map[] = $m; + } + } + + return response() + ->view('api/v4/sflow-receiver/pretagMap', [ 'map' => $map ], 200 ) + ->header('Content-Type', 'text/html; charset=utf-8'); + } + + /** + * + * @return Response + */ + public function receiversLst(): Response + { + $map = []; + + foreach( SflowReceiver::all() as $sr ) { + $m['virtualinterfaceid'] = $sr->virtual_interface_id; + $m['dst_ip'] = $sr->dst_ip; + $m['dst_port'] = $sr->dst_port; + $map[] = $m; + } + + return response() + ->view( 'api/v4/sflow-receiver/receiversLst', [ 'map' => $map ], 200 ) + ->header('Content-Type', 'text/html; charset=utf-8'); + } + + /** + * + * @param string|null $format + * + * @return Response + * + * @throws + */ + public function getReceiverList( string $format = null ): Response + { + $map = []; + + foreach( SflowReceiver::all() as $sr ) { + $m['virtualinterfaceid'] = $sr->virtual_interface_id; + $m['dst_ip'] = $sr->dst_ip; + $m['dst_port'] = $sr->dst_port; + $macs = []; + foreach( $sr->virtualInterface->macAddresses as $mac ) { + $macs[] = $mac->macColonsFormatted(); + } + $m['macaddresses']['learned'] = $macs; + $macs = []; + foreach( $sr->virtualInterface->vlanInterfaces as $vli ) { + foreach( $vli->layer2addresses as $mac ) { + $macs[] = $mac->macFormatted( ':' ); + } + } + $m['macaddresses']['configured'] = $macs; + $map[] = $m; + } + + $output['receiver_list'] = $map; + + return $this->structuredResponse( $output, $format ); + } + + /** + * Generate a formatted output version of the given structure. + * + * This takes two arguments: the array structure and the output format. + * + * @param array $array + * @param string $format + * + * @return Response + * + * @throws + */ + private function structuredResponse( array $array, string $format ): Response + { + $output = null; + $contenttype = 'text/plain; charset=utf-8'; + $httpresponse = 200; + + $array['timestamp'] = now()->format( 'Y-m-d\TH:i:s\Z' ); + /** @psalm-suppress UndefinedConstant */ + $array['ixpmanager_version'] = APPLICATION_VERSION; + + switch ( $format ) { + case 'yaml': + /** @psalm-suppress UndefinedConstant */ + $output = yaml_emit ( $array, YAML_UTF8_ENCODING ); + break; + case 'json': + $output = json_encode($array, + JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)."\n"; + $contenttype = 'application/json'; + break; + } + + if ( !$output ) { + $httpresponse = 200; + } + + return response( $output, $httpresponse )->header('Content-Type', $contenttype ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Api/V4/StatisticsController.php b/app/Http/Controllers/Api/V4/StatisticsController.php new file mode 100644 index 000000000..41d070973 --- /dev/null +++ b/app/Http/Controllers/Api/V4/StatisticsController.php @@ -0,0 +1,132 @@ + + * @author Yann Robin + * @category APIv4 + * @package IXP\Http\Controllers\Api\V4 + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class StatisticsController extends Controller +{ + /** + * Function to export max traffic stats by month (past six) as JSON + * + * Response is of the form: + * + * [ + * { + * "start": "2017-10-01T00:00:00Z", + * "startTs": 1506816000, + * "end": "2017-10-31T23:59:59Z", + * "endTs": 1509494399, + * "max": 206917750576 + * }, + * ... + * ] + * + * Access via https://ixp.example.com/statistics/overall-by-month + * + * Respects Grapher authorisation settings: + * https://docs.ixpmanager.org/latest/features/grapher/#accessibility-of-aggregate-graphs + * + * This is mostly used by https://www.inex.ie/ 's front page graph. + * + * @return JsonResponse + */ + public function overallByMonth(): JsonResponse + { + $data = Cache::remember( 'public_overall_stats_by_month', 14400, function() { + $graph = Grapher::ixp()->setPeriod( Graph::PERIOD_YEAR ); + $graph->authorise(); + + $mrtg = $graph->data(); + $data = []; + $start = Carbon::now()->subMonths( 5 )->startOfMonth(); + $startTs = $start->timestamp; + $i = 0; + + while( $start->lt( Carbon::now() ) ) { + $data[ $i ][ 'start' ] = $start->copy(); + $data[ $i ][ 'startTs' ] = $start->timestamp; + $data[ $i ][ 'end' ] = $start->endOfMonth()->copy(); + $data[ $i ][ 'endTs' ] = $start->endOfMonth()->timestamp; + $data[ $i ][ 'max' ] = 0; + + $start->startOfMonth()->addMonth(); + $i++; + } + + $endTs = $data[ $i - 1 ][ 'endTs' ]; + + foreach( $mrtg as $m ) { + if( count( $m ) !== 5 ) { + continue; + } + + if( $m[ 0 ] < $startTs || $m[ 0 ] > $endTs ) { + continue; + } + + foreach( $data as $i => $d ) { + if( $m[ 0 ] >= $d[ 'startTs' ] && $m[ 0 ] <= $d[ 'endTs' ] ) { + if( $m[ 3 ] > $data[ $i ][ 'max' ] ) { + $data[ $i ][ 'max' ] = $m[ 3 ]; + } + if( $m[ 4 ] > $data[ $i ][ 'max' ] ) { + $data[ $i ][ 'max' ] = $m[ 4 ]; + } + break; + } + } + } + + // scale to bps + foreach( $data as $i => $d ) { + /** @var Carbon $start */ + $start = $data[ $i ][ 'start' ]; + /** @var Carbon $end */ + $end = $data[ $i ][ 'end' ]; + + $data[ $i ][ 'start' ] = $start->format( 'Y-m-d\TH:i:s\Z' ); + $data[ $i ][ 'end' ] = $end->format( 'Y-m-d\TH:i:s\Z' ); + } + + return $data; + }); + + return response()->json( $data ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Api/V4/SwitchController.php b/app/Http/Controllers/Api/V4/SwitchController.php new file mode 100644 index 000000000..1f82d6db8 --- /dev/null +++ b/app/Http/Controllers/Api/V4/SwitchController.php @@ -0,0 +1,174 @@ + + * @author Yann Robin + * @category APIv4 + * @package IXP\Http\Controllers\Api\V4 + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class SwitchController extends Controller +{ + /** + * Get the switch port for a Switch + * + * @param Request $r instance of the current HTTP request + * @param Switcher $s switch + * + * @return JsonResponse JSON array of listPort + */ + public function ports( Request $r, Switcher $s ): JsonResponse + { + return response()->json( [ + 'ports' => SwitcherAggregator::allPorts( $s->id , $r->types ?? [] , $r->spIdsExcluded ?? [], (bool)$r->notAssignToPI, (bool)$r->piNull ) + ] ); + } + + /** + * Get the switch port for a Switch for patch panel port + * + * @param Request $r + * @param Switcher $s + * + * @return JsonResponse array of listPort + */ + public function switchPortForPPP( Request $r, Switcher $s ): JsonResponse + { + return response()->json( [ + 'listPorts' => SwitchPort::selectRaw( 'sp.name AS name, sp.type AS type, sp.id AS id' ) + ->from( 'switchport AS sp' ) + ->leftJoin( 'patch_panel_port AS ppp', 'ppp.switch_port_id', 'sp.id' ) + ->where( 'sp.switchid', $s->id ) + ->when( $r->custId , function( Builder $q ) use( $r ) { + return $q->leftJoin( 'physicalinterface AS pi', 'pi.switchportid', 'sp.id' ) + ->leftJoin( 'virtualinterface AS vi', 'vi.id', 'pi.virtualinterfaceid' ) + ->where('vi.custid', $r->custId ); + } ) + ->when( $r->spId , function( Builder $q, $r ) { + return $q->where('sp.id', '!=', $r->spId ); + }) + ->when( !$r->custId && !$r->spId, function( Builder $q ) { + return $q->whereNull( 'ppp.switch_port_id' ); + }) + ->orderBy( 'sp.id' )->get()->toArray() + ] ); + } + + /** + * Get the Prewired switch port for a Switch + * + * @param Request $r + * @param Switcher $s + * + * @return JsonResponse array of listPort + */ + public function switchPortPrewired( Request $r, Switcher $s ): JsonResponse + { + return response()->json( [ + 'listPorts' => SwitchPort::selectRaw( 'sp.name AS name, sp.type AS type, sp.id AS id' ) + ->from( 'switchport AS sp' ) + ->leftJoin( 'patch_panel_port AS ppp', 'ppp.switch_port_id', 'sp.id' ) + ->whereRaw( 'sp.id NOT IN ( SELECT pi.switchportid + FROM physicalinterface pi )' ) + ->when( $r->spId , function( Builder $q, $r ) { + return $q->where('sp.id', '!=', $r->spId ); + }) + ->where( 'sp.switchid', $s->id ) + ->whereNull( 'ppp.switch_port_id' ) + ->whereIn( 'sp.type', [ SwitchPort::TYPE_UNSET, SwitchPort::TYPE_PEERING ] ) + ->orderBy( 'sp.id' )->get()->toArray() + ] ); + } + + /** + * Get the switch status for monitoring purposes + * + * @param Switcher $s + * + * @return JsonResponse + */ + public function status( Switcher $s ): JsonResponse + { + return response()->json( $s->status() ); + } + + /** + * Get the switch status for monitoring purposes + * + * @param Switcher $s + * + * @return JsonResponse + */ + public function coreBundlesStatus( Switcher $s ): JsonResponse + { + $okay = true; + $msgs = []; + + foreach( $s->getCoreBundles() as $cb ) { + $switchA = $cb->switchSideX(); + /** @psalm-suppress InvalidPropertyFetch */ + $switchAName = $switchA !== false ? $switchA->name : 'none'; + $switchB = $cb->switchSideX( false ); + /** @psalm-suppress InvalidPropertyFetch */ + $switchBName = $switchB !== false ? $switchB->name : 'none'; + + if( $cb->enabled ) { + $linksup = count( $cb->coreLinksWithIfOperStateX() ); // with no args this defaults to X = oper state up for enabled links + $linksenabled = count( $cb->corelinks()->active()->get()->toArray() ); + + if( $linksup === $linksenabled ) { + $msgs[] = $switchAName . ' - ' . $switchBName . " OK - {$linksup}/{$linksenabled} links up"; + } else { + $okay = false; + $msgs[] = 'ISSUE: ' . $switchAName . ' - ' . $switchBName . " has {$linksup}/{$linksenabled} links up"; + } + } else { + $msgs[] = 'Ignoring ' . $switchAName . ' - ' . $switchBName . ' as core bundle disabled'; + } + } + + if( $msgs === [] ) { + $msgs[] = "No core bundles configured for this switch"; + } + return response()->json( [ 'status' => $okay, 'switchname' => $s->name, 'msgs' => $msgs ] ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Api/V4/SwitchPortController.php b/app/Http/Controllers/Api/V4/SwitchPortController.php new file mode 100644 index 000000000..937c55e38 --- /dev/null +++ b/app/Http/Controllers/Api/V4/SwitchPortController.php @@ -0,0 +1,83 @@ + + * @author Yann Robin + * @category APIv4 + * @package IXP\Http\Controllers\Api\V4 + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class SwitchPortController extends Controller +{ + /** + * Get the customer for a switch port + * + * @param SwitchPort $sp The ID of the switch port to query + * + * @return JsonResponse JSON customer object + */ + public function customer( SwitchPort $sp ): JsonResponse + { + return response()->json( [ + 'customer' => SwitchPort::selectRaw( 'COUNT( c.id ) as nb, c.id, c.name' ) + ->from( 'switchport AS sp' ) + ->leftJoin( 'physicalinterface AS pi', 'pi.switchportid', 'sp.id' ) + ->leftJoin( 'virtualinterface AS vi', 'vi.id', 'pi.virtualinterfaceid' ) + ->leftJoin( 'cust AS c', 'c.id', 'vi.custid' ) + ->where( 'sp.id', $sp->id ) + ->groupBy( 'c.id' ) + ->first()->toArray() + ] ); + } + + /** + * Check if the switch port has a physical interface set + * + * @param SwitchPort $sp Id of the switchport + * + * @return JsonResponse JSON response + */ + public function physicalInterface( SwitchPort $sp ): JsonResponse + { + if( ( $pi = $sp->physicalInterface ) ){ + return response()->json([ + 'pi' => [ + 'id' => $pi->id, + 'status' => $pi->status, + 'statusText' => $pi->status(), + ] + ]); + } + return response()->json( [] ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Api/V4/UserController.php b/app/Http/Controllers/Api/V4/UserController.php new file mode 100644 index 000000000..de926929a --- /dev/null +++ b/app/Http/Controllers/Api/V4/UserController.php @@ -0,0 +1,108 @@ + + * @author Yann Robin + * @category APIv4 + * @package IXP\Http\Controllers\Api\V4 + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class UserController extends Controller +{ + /** + * API call to get users as JSON + * + * @param int|null $priv Optionally limit to users of given privilege + * + * @return JsonResponse + */ + public function json( int $priv = null ): JsonResponse + { + return response()->json( + User::byPrivs( $priv )->get()->toArray() + ); + } + + /** + * API call to get users formatted + * + * @param Request $r + * @param int|null $priv Optionally limit to users of given privilege + * @param string|null $template + * + * @return Response + */ + public function formatted( Request $r, int $priv = null, string $template = null ): Response + { + if( $template === null && !$r->template ) { + $tmpl = 'api/v4/user/formatted/default'; + } else { + if( $template === null ) { + $template = $r->template; + } + $tmpl = sprintf( 'api/v4/user/formatted/%s', preg_replace( '/[^a-z0-9\-]/', '', strtolower( $template ) ) ); + } + + if( !FacadeView::exists( $tmpl ) ) { + abort(404, 'Unknown template'); + } + + if( $priv === null && $r->priv ) { + $priv = $r->priv; + } + + if( $r->users ) { + $users = User::activeOnly()->whereIn( 'username', explode(',', $r->users ) )->get(); + } else if( $priv !== null ) { + $users = User::byPrivs( $priv )->activeOnly()->get(); + } else { + $users = User::activeOnly()->get(); + } + + return response() + ->view( $tmpl, [ + 'users' => $users, + 'reqUsers' => $r->users ? explode(',', $r->users ) : [], + 'priv' => $priv ?? '', + 'bcrypt' => $r->input( 'bcrypt', '2y' ), + 'group' => $r->input( 'group', 'admin' ), + ], 200 ) + ->header( 'Content-Type', 'text/plain; charset=utf-8' ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Api/V4/UtilsController.php b/app/Http/Controllers/Api/V4/UtilsController.php new file mode 100644 index 000000000..079069300 --- /dev/null +++ b/app/Http/Controllers/Api/V4/UtilsController.php @@ -0,0 +1,60 @@ + + * @author Yann Robin + * @category APIv4 + * @package IXP\Http\Controllers\Api\V4 + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ + +class UtilsController extends Controller +{ + /** + * Turn markdown text into HTML + * + * @param Request $r + * + * @return JsonResponse JSON object with 'html' element + */ + public function markdown( Request $r ): JsonResponse + { + $pd = new Parsedown(); + return response()->json([ + 'html' => clean( $pd->text( $r->text ) ) + ]); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Api/V4/VlanController.php b/app/Http/Controllers/Api/V4/VlanController.php new file mode 100644 index 000000000..bddf4f579 --- /dev/null +++ b/app/Http/Controllers/Api/V4/VlanController.php @@ -0,0 +1,106 @@ + + * @author Yann Robin + * @category APIv4 + * @package IXP\Http\Controllers\Api\V4 + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class VlanController extends Controller +{ + /** + * Get all IP addresses (v4 and v6) for a given VLAN. + * + * Returns a JSON object with two array elements: ipv4 and ipv6 + * + * Each of these elements contain address objects of the form: + * + * { + * id: "1040", // address ID from the IPv4/6 table + * address: "2001:7f8:18::20", // address + * v_id: "2", // VLAN id + * vli_id: "16" // VlanInterface ID (or null if not assigned / in use) + * }, + * + * @param Vlan $v Vlan + * + * @return JsonResponse array of IP addresses + * + * @throws + */ + public function ipAddresses( Vlan $v ) : JsonResponse + { + return response()->json([ + 'ipv4' => VlanAggregator::ipAddresses( $v->id, Router::PROTOCOL_IPV4 ), + 'ipv6' => VlanAggregator::ipAddresses( $v->id, Router::PROTOCOL_IPV6 ) + ]); + } + + /** + * Determine is an IP address /really/ free by checking across all vlans + * + * Returns a array of objects where each object is the details of its usage (example below). + * If not used, returns an empty array. + * + * @param Request $r + * + * @return JsonResponse array of object + * + * @see VlanAggregator::usedAcrossVlans() for array structure. + * + */ + public function usedAcrossVlans( Request $r ) : JsonResponse + { + $validator = Validator::make( $r->all(), [ + 'ip' => 'required|ip' + ]); + + if( $validator->fails() ) { + abort( 422, 'Invalid or no IP address - set POST "ip" parameter.' ); + } + + return response()->json( + VlanAggregator::usedAcrossVlans( $r->ip ) + ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Api/V4/VlanInterfaceController.php b/app/Http/Controllers/Api/V4/VlanInterfaceController.php new file mode 100644 index 000000000..d2814b829 --- /dev/null +++ b/app/Http/Controllers/Api/V4/VlanInterfaceController.php @@ -0,0 +1,102 @@ + + * @author Yann Robin + * @category APIv4 + * @package IXP\Http\Controllers\Api\V4 + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class VlanInterfaceController extends Controller +{ + /** + * Get all Layer2Address for a VlanInterface + * + * @param VlanInterface $vli VlanInterface + * + * @return JsonResponse + */ + public function getL2A( VlanInterface $vli ) : JsonResponse + { + return response()->json( $vli->layer2addresses()->pluck('mac', 'id')->toArray() ); + } + + /** + * Get infra / tag / mac / viid structure for sflow data processing + * + * FIXME insert reference to documentation - see islandbridgenetworks/IXP-Manager#34 + * + * @return JsonResponse + */ + public function sflowLearnedMacs(): JsonResponse + { + $macs = VlanInterface::selectRaw( + 'vli.id AS vliid, ma.mac AS mac, vl.number as tag, vl.infrastructureid as infrastructure' + )->from( 'vlaninterface AS vli' ) + ->leftJoin( 'virtualinterface AS vi', 'vi.id', 'vli.virtualinterfaceid' ) + ->join( 'macaddress AS ma', 'ma.virtualinterfaceid', 'vi.id' ) + ->leftJoin( 'vlan AS vl', 'vl.id', 'vli.vlanid' ) + ->whereNotNull( 'ma.mac' )->whereNotNull( 'vli.id' ) + ->orderBy( 'vliid' )->distinct()->get()->toArray(); + + foreach( $macs as $mac ){ + $output[ $mac[ 'infrastructure' ] ][ $mac[ 'tag' ] ][ $mac[ 'mac' ] ] = $mac[ 'vliid' ]; + } + + return response()->json($output ?? []); + } + + /** + * Get infra / tag / mac / viid structure for sflow data processing + * + * FIXME insert reference to documentation - see islandbridgenetworks/IXP-Manager#34 + * + * @return JsonResponse + */ + public function sflowConfiguredMacs(): JsonResponse + { + $macs = VlanInterface::selectRaw( + 'vli.id AS vliid, l2a.mac AS mac, vl.number as tag, vl.infrastructureid as infrastructure' + )->from( 'vlaninterface AS vli' ) + ->leftJoin( 'virtualinterface AS vi', 'vi.id', 'vli.virtualinterfaceid' ) + ->leftJoin( 'l2address AS l2a', 'l2a.vlan_interface_id', 'vli.id' ) + ->leftJoin( 'vlan AS vl', 'vl.id', 'vli.vlanid' ) + ->whereNotNull( 'l2a.mac' ) + ->orderBy( 'vliid' )->distinct()->get()->toArray(); + + foreach( $macs as $mac ) { + $output[ $mac[ 'infrastructure' ] ][ $mac[ 'tag' ] ][ $mac[ 'mac' ] ] = $mac[ 'vliid' ]; + } + return response()->json($output ?? []); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Api/V4/WhoisController.php b/app/Http/Controllers/Api/V4/WhoisController.php new file mode 100644 index 000000000..fc772a571 --- /dev/null +++ b/app/Http/Controllers/Api/V4/WhoisController.php @@ -0,0 +1,95 @@ + + * @author Yann Robin + * @category APIv4 + * @package IXP\Http\Controllers\Api\V4\Provisioner + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class WhoisController extends Controller +{ + /** + * API call to do a Whois looking on an AS number + * + * @param Request $r + * @param string $asn The AS number + * + * @return Response + */ + public function asn( Request $r, string $asn ): Response + { + $response = Cache::remember( 'api-v4-whois-asn-' . $asn, config('ixp_api.whois.cache_ttl'), function () use ( $asn ) { + + // try PeeringDB first + $pdb = new PeeringDb(); + if( $net = $pdb->getNetworkByAsn( $asn ) ) { + return $pdb->netAsAscii( $net ); + } + + if( $pdb->status === 404 ) { + $response = "ASN not registered in PeeringDB. Trying " . config( 'ixp_api.whois.asn2.host' ) . ":\n\n"; + } else { + $response = "Querying PeeringDB failed:\n\nError:{$pdb->error}\n\nTrying " . config( 'ixp_api.whois.asn2.host' ) . ":\n\n"; + } + + $whois = new Whois( config( 'ixp_api.whois.asn2.host' ), config( 'ixp_api.whois.asn2.port' ) ); + $response .= $whois->whois( 'AS' . (int)$asn ); + + return $response; + }); + + return response( $response, 200 )->header('Content-Type', 'text/plain'); + } + + /** + * API call to do a Whois looking on a prefix + * + * @param string $prefix The IP address element of the prefix + * @param string|null $mask The mask length + * + * @return Response + */ + public function prefix( string $prefix, string $mask = null ): Response + { + $response = Cache::remember( 'api-v4-whois-prefix-' . $prefix . '-' . $mask, config('ixp_api.whois.cache_ttl'), function () use ( $prefix, $mask ) { + $whois = new Whois( config('ixp_api.whois.prefix.host'), config('ixp_api.whois.prefix.port') ); + return $whois->whois( $prefix .'/' . $mask ); + }); + + return response( $response, 200 )->header('Content-Type', 'text/plain'); + } +} diff --git a/app/Http/Controllers/ApiKeyController.php b/app/Http/Controllers/ApiKeyController.php new file mode 100644 index 000000000..f8fb065eb --- /dev/null +++ b/app/Http/Controllers/ApiKeyController.php @@ -0,0 +1,293 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class ApiKeyController extends EloquentController +{ + /** + * The object being created / updated + * + * @var ApiKey + */ + protected $object = null; + + /** + * The minimum privileges required to access this controller. + * + * If you set this to less than the superuser, you need to manage privileges and access + * within your own implementation yourself. + * + * @var int + */ + public static $minimum_privilege = User::AUTH_CUSTUSER; + + /** + * This function sets up the frontend controller + */ + #[\Override] + public function feInit(): void + { + $this->feParams = (object)[ + 'model' => ApiKey::class, + 'pagetitle' => 'API Keys', + 'titleSingular' => 'API Key', + 'nameSingular' => 'API key', + 'listOrderBy' => 'created_at', + 'listOrderByDir' => 'ASC', + 'viewFolderName' => 'api-key', + 'documentation' => 'https://docs.ixpmanager.org/latest/features/api/', + 'listColumns' => [ + 'id' => [ 'title' => 'UID', 'display' => false ], + 'apiKey' => [ + 'title' => 'API Key', + 'type' => config( 'ixp_fe.api_keys.show_keys' ) ? self::$FE_COL_TYPES[ 'TEXT' ] : self::$FE_COL_TYPES[ 'LIMIT' ], + 'limitTo' => 6 + ], + 'created_at' => [ + 'title' => 'Created', + 'type' => self::$FE_COL_TYPES[ 'DATETIME' ] + ], + 'expires' => [ + 'title' => 'Expires', + 'type' => self::$FE_COL_TYPES[ 'DATE' ] + ], + 'lastseenAt' => [ + 'title' => 'Lastseen', + 'type' => self::$FE_COL_TYPES[ 'DATETIME' ] + ], + 'lastseenFrom' => 'Lastseen From' + ] + ]; + + // display the same information in the view as the list + $this->feParams->viewColumns = $this->feParams->listColumns; + + // phpunit / artisan trips up here without the cli test: + if( PHP_SAPI !== 'cli' ) { + /** @var User $user */ + $user = Auth::getUser(); + // custom access controls: + switch( Auth::check() ? $user->privs() : User::AUTH_PUBLIC ) { + case User::AUTH_SUPERUSER: + case User::AUTH_CUSTUSER || User::AUTH_CUSTADMIN: + break; + + default: + $this->unauthorized(); + break; + } + } + } + + /** + * Additional routes + * + * @param string $route_prefix + * + * @return void + */ + #[\Override] + protected static function additionalRoutes( string $route_prefix ): void + { + // NB: this route is marked as 'read-only' to disable normal CRUD operations. It's not really read-only. + Route::group( [ 'prefix' => $route_prefix ], static function() use ( $route_prefix ) { + Route::post( 'list-show-keys', 'ApiKeyController@listShowKeys' )->name( $route_prefix . '@list-show-keys' ); + }); + } + + /** + * Provide array of rows for the list action and view action + * + * @param int|null $id The `id` of the row to load for `view` action`. `null` if `listAction` + * + * @return array + */ + #[\Override] + protected function listGetData( ?int $id = null ): array + { + $feParams = $this->feParams; + return ApiKey::where( 'user_id', Auth::id() ) + ->when( $id , function( Builder $q, $id ) { + return $q->where('id', $id ); + } )->when( $feParams->listOrderBy , function( Builder $q, $orderby ) use ( $feParams ) { + return $q->orderBy( $orderby, $feParams->listOrderByDir ?? 'ASC'); + })->get()->toArray(); + } + + /** + * Display the form to create an object + * + * @return ApiKey[] + * + * @psalm-return array{object: ApiKey} + */ + #[\Override] + protected function createPrepareForm(): array + { + return [ + 'object' => $this->object + ]; + } + + /** + * Display the form to edit an object + * + * @param int $id ID of the row to edit + * + * @return array + * + * @psalm-return array{object: mixed} + */ + #[\Override] + protected function editPrepareForm( int $id ): array + { + $this->object = ApiKey::findOrFail( $id ); + + Former::populate( [ + 'apiKey' => request()->old( 'apiKey', config( 'ixp_fe.api_keys.show_keys' ) ? $this->object->apiKey : Str::limit( $this->object->apiKey, 6 ) ), + 'description' => request()->old( 'description', $this->object->description ), + 'expires' => request()->old( 'expires', $this->object->expires ? Carbon::parse( $this->object->expires )->format( 'Y-m-d' ) : null ) + ] ); + + return [ + 'object' => $this->object, + ]; + } + + /** + * Function to do the actual validation and storing of the submitted object. + * + * @param Request $r + * + * @return RedirectResponse|true + * + * @throws + */ + #[\Override] + public function doStore( Request $r ) + { + if( $r->user()->apiKeys()->count() >= config( 'ixp_fe.api_keys.max_keys' ) ) { + AlertContainer::push( "We currently have a limit of " . config( 'ixp_fe.api_keys.max_keys' ) . " API keys per user. Please contact us if you require more.", Alert::DANGER ); + return Redirect::back()->withInput(); + } + + $this->checkForm( $r ); + + $this->object = new ApiKey; + $this->object->apiKey = $key = Str::random(48); + $this->object->expires = $r->expires; + $this->object->description = $r->description; + $this->object->user_id = $r->user()->id; + $this->object->save(); + + AlertContainer::push( "API key created: " . $key . "", Alert::SUCCESS ); + return true; + } + + /** + * Function to do the actual validation and storing of the submitted object. + * + * @param Request $r + * @param int $id + * + * @return true + * + * @throws + */ + #[\Override] + public function doUpdate( Request $r, int $id ) + { + $this->object = ApiKey::findOrFail( $id ); + $this->checkForm( $r ); + $this->object->update( $r->all() ); + return true; + } + + /** + * Show the API Keys if the password match + * + * @param Request $r + * + * @return RedirectResponse|View + */ + public function listShowKeys( Request $r ): RedirectResponse|View + { + if( !Hash::check( $r->pass , $r->user()->password ) ) { + AlertContainer::push( 'Incorrect password entered', Alert::DANGER ); + } else { + AlertContainer::push( 'API keys are visible for this request only. You will need to re-enter your password to view them again.', Alert::SUCCESS ); + config( [ 'ixp_fe.api_keys.show_keys' => true ] ); + } + + return $this->list( $r ); + } + + /** + * Check if the form is valid + * + * @param $r + * + * @return void + */ + #[\Override] + public function checkForm( Request $r ): void + { + $r->validate( [ + 'description' => 'nullable|string|max:255', + 'expires' => 'nullable|date|after:' . now()->format( "Y-m-d" ), + ] ); + } +} diff --git a/app/Http/Controllers/Auth/ForgotPasswordController.php b/app/Http/Controllers/Auth/ForgotPasswordController.php new file mode 100644 index 000000000..ccadbd0ff --- /dev/null +++ b/app/Http/Controllers/Auth/ForgotPasswordController.php @@ -0,0 +1,176 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers\Auth + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class ForgotPasswordController extends Controller +{ + + protected $redirectTo = 'auth/login'; + + /* + |-------------------------------------------------------------------------- + | Password Reset Controller + |-------------------------------------------------------------------------- + | + | This controller is responsible for handling password reset emails and + | includes a trait which assists in sending these notifications from + | your application to your users. Feel free to explore this trait. + | + */ + use SendsPasswordResetEmails; + + /** + * Create a new controller instance. + * + * @return void + */ + public function __construct() + { + $this->middleware('guest' ); + } + + /** + * Display the forgot password form + * + * @return View + */ + public function showLinkRequestForm() : View + { + return view( 'auth/forgot-password' ); + } + + /** + * Send a reset link to the given user. + * + * @param Request $r + * + * @return RedirectResponse + * + * @throws + */ + public function sendResetLinkEmail( Request $r ): RedirectResponse + { + $this->validate( $r, ['username' => 'required'] ); + + // We will send the password reset link to this user. Once we have attempted + // to send the link, we will examine the response then see the message we + // need to show to the user. Finally, we'll send out a proper response. + $response = $this->broker()->sendResetLink( + $r->only('username') + ); + + return $response === Password::RESET_LINK_SENT + ? $this->sendResetLinkResponse() + : $this->sendResetLinkFailedResponse(); + } + + /** + * Get the response for a successful password reset link. + * + * + * @return RedirectResponse + */ + protected function sendResetLinkResponse(): RedirectResponse + { + AlertContainer::push( 'If your email matches user(s) on the system, then an email listing those users has been sent to you.', Alert::INFO ); + return redirect( route( 'login@login' ) ); + } + + /** + * Get the response for a failed password reset link. + * + * @return RedirectResponse + */ + protected function sendResetLinkFailedResponse(): RedirectResponse + { + AlertContainer::push( 'If your email matches user(s) on the system, then an email listing those users has been sent to you.', Alert::INFO ); + return redirect( route( 'login@login' ) ); + } + + /** + * Display the forgot username form + * + * @return View + */ + public function showUsernameForm(): View + { + return view( 'auth/forgot-username' ); + } + + /** + * Send the email with the list of username for an email address + * + * @param ForgotUsernameRequest $r instance of the current HTTP request + * + * @return RedirectResponse + */ + public function sendUsernameEmail( ForgotUsernameRequest $r ) : RedirectResponse + { + $users = User::where( 'email', $r->email )->get(); + + if( count( $users ) ){ + event( new ForgotUsernameEvent( $users, $r->email ) ); + } + + AlertContainer::push( 'If your email matches user(s) on the system, then an email listing those users has been sent to you.', Alert::INFO ); + return Redirect::to( route( "login@showForm" )); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php new file mode 100644 index 000000000..4addbd4eb --- /dev/null +++ b/app/Http/Controllers/Auth/LoginController.php @@ -0,0 +1,317 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers\Auth + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class LoginController extends Controller +{ + /* + |-------------------------------------------------------------------------- + | Login Controller + |-------------------------------------------------------------------------- + | + | This controller handles authenticating users for the application and + | redirecting them to your home screen. The controller uses a trait + | to conveniently provide its functionality to your applications. + | + */ + use AuthenticatesUsers; + + /** + * Where to redirect users after login. + * + * THis isn't used unless the RedirectIfAuthenticated middleware's handle() method is coded to use it. + * @var string + */ + protected $redirectTo = ''; + + /** + * Get the login username to be used by the controller. + * + * @return string + * + * @psalm-return 'username' + */ + public function username(): string + { + return 'username'; + } + + /** + * Create a new controller instance. + * + * @return void + */ + public function __construct() + { + $this->middleware( 'guest' )->except( 'logout' ); + } + + /** + * Show the login form + * + * @return View + */ + public function showLoginForm() : View + { + if( !session()->has('url.intended') && Str::startsWith(url()->previous(), url(''))) { + if( config('google2fa.enabled') ) { + if( strpos( url()->previous(), route( "2fa@authenticate" ) ) !== false ) { + // Store intended url to redirect after login + session( [ 'url.intended' => url()->previous() ] ); + // Store intended url to redirect after 2FA + session( [ 'url.intended.2fa' => url()->previous() ] ); + } + } else { + // Store intended url to redirect after login + session( [ 'url.intended' => url()->previous() ] ); + } + } + return view( 'auth/login' ); + } + + /** + * The user has been authenticated. + * + * @param Request $r + * @param User $user + * + * @return RedirectResponse|null + */ + protected function authenticated( Request $r, User $user ) + { + // Check if the user has Customer(s) linked + if( !$user->customers()->count() ) { + return $this->logout( $r, [ 'message' => "Your user account is not associated with any " . config( "ixp_fe.lang.customer.many" ) . ".", 'class' => Alert::DANGER ] ); + } + + $activeCusts = $user->customers()->active()->notDeleted()->get(); + + // Check if the user has active Customer(s) linked + if( !$activeCusts->count() ) { + return $this->logout( $r, [ 'message' => "Your user account is not associated with any active " . config( "ixp_fe.lang.customer.many" ) . ".", 'class' => Alert::DANGER ] ); + } + + $newCust = $activeCusts->first(); + + // Check if the default customer for the user is not active + if( ( $cust = $user->customer ) && $user->customer()->active()->notDeleted()->doesntExist() ){ + $user->custid = $newCust->id; + $user->save(); + AlertContainer::push( "The default " . config( "ixp_fe.lang.customer.one" ) . " " . ucfirst( $cust->abbreviatedName ) . " is no longer active. Your default " . config( "ixp_fe.lang.customer.one" ) . " is now " . ucfirst( $newCust->abbreviatedName ) . "." , Alert::WARNING ); + } + + $c2u = CustomerToUser::where( [ 'user_id' => $user->id ] )->where( [ "customer_id" => $user->custid ] )->first(); + + // Check if the user has a default customer OR if the default customer is no longer in the C2U, then assign one + if( !$user->customer || !$c2u ){ + $user->custid = $newCust->id; + $user->save(); + } + } + + /** + * Get the failed login response instance. + * + * @param Request $r + * @param string|null $msg + */ + protected function sendFailedLoginResponse( Request $r, $msg = null ) : RedirectResponse + { + AlertContainer::push( $msg ?? "Invalid username or password. Please try again." , Alert::DANGER ); + return redirect()->back()->withInput( $r->only('username') ); + } + + /** + * Log the user out of the application. + * + * @param Request $r + * @param array|null $customMessage Custom message to display + */ + public function logout( Request $r, $customMessage = null ) : RedirectResponse + { + $this->guard()->logout(); + $r->session()->invalidate(); + + AlertContainer::push( $customMessage ? $customMessage[ "message" ] : "You have been logged out." , $customMessage ? $customMessage[ "class" ] : Alert::SUCCESS ); + return redirect(''); + } + + /** + * Redirect the user to the PeeringDB authentication page. + * + * @return RedirectResponseFoundation + */ + public function peeringdbRedirectToProvider(): RedirectResponseFoundation + { + if( Auth::check() ) { + AlertContainer::push( "You are already logged in - Login via PeeringDB aborted." , Alert::WARNING ); + return redirect(''); + } + + if( config( 'auth.peeringdb.enabled' ) ) { + return Socialite::driver( 'peeringdb' )->redirect(); + } + + AlertContainer::push( "Login with PeeringDB not enabled." , Alert::DANGER ); + return redirect( route('login@showForm' ) ); + } + + /** + * Obtain the user information from PeeringDB. + * + * Sample response: + * + * User {#1139 ▼ + * +accessTokenResponseBody: array:5 [▼ + * "access_token" => "xxxx" + * "token_type" => "Bearer" + * "expires_in" => 36000 + * "refresh_token" => "xxxx" + * "scope" => "profile email networks" + * ] + * +token: "xxx" + * +refreshToken: "xxx" + * +expiresIn: 36000 + * +id: null + * +nickname: null + * +name: "Joe Bloggs" + * +email: "ixpmanager@example.com" + * +avatar: null + * +user: array:8 [▼ + * "family_name" => "Bloggs" + * "email" => "ixpmanager@example.com" + * "name" => "Joe Bloggs" + * "verified_user" => true + * "verified_email" => true + * "networks" => array:2 [▼ + * 0 => array:4 [▼ + * "perms" => 1 + * "id" => 888 + * "name" => "INEX Route Collectors" + * "asn" => 65501 + * ] + * 1 => array:4 [▼ + * "perms" => 1 + * "id" => 777 + * "name" => "INEX Route Servers" + * "asn" => 65500 + * ] + * ] + * "id" => 666 + * "given_name" => "Joe" + * ] + * } + * + * @param Request $r + * + * @throws + */ + public function peeringdbHandleProviderCallback( Request $r ): RedirectResponse + { + if( Auth::check() ) { + AlertContainer::push( "You are already logged in - Login via PeeringDB aborted." , Alert::WARNING ); + return redirect(''); + } + + if( !config( 'auth.peeringdb.enabled' ) ) { + AlertContainer::push( "Login with PeeringDB not enabled.", Alert::DANGER ); + return redirect()->route( 'login@showForm' ); + } + + $suser = Socialite::driver('peeringdb' )->user(); + + // valid PeeringDB login with affiliations? + if( !$suser || !isset( $suser->user ) || !isset( $suser->user['networks'] ) || !is_array( $suser->user['networks'] ) || !count( $suser->user['networks'] ) ) { + AlertContainer::push( "Login with PeeringDB failed or you have no existing affiliations.", Alert::DANGER ); + return redirect()->route( 'login@login' ); + } + + // user needs to be verified with PeeringDB first: + if( !$suser->user['verified_user'] || !$suser->user['verified_email'] ) { + return $this->sendFailedLoginResponse( $r, 'Your PeeringDB user or email address has not been validated. Please complete your PeeringDB account registration first.' ); + } + + $result = UserAggregator::findOrCreateFromPeeringDb( $suser->user ); + + if( $result['user'] === null || !( $result['user'] instanceof User ) ) { + return $this->sendFailedLoginResponse( $r, 'Login with PeeringDB failed. Most likely there are no ' . config( "ixp_fe.lang.customer.many" ) . ' at this IXP that match your PeeringDB affiliation(s). ' + . 'If you believe this to be an error or would like to get access to your account, please contact our support team.' ); + } + + /** @var Customer $c */ + foreach( $result['added_to'] as $c ) { + AlertContainer::push( "Your PeeringDB affiliation with {$c->getFormattedName()} has been added to IXP Manager.", Alert::SUCCESS ); + } + + foreach( $result['removed_from'] as $c ) { + AlertContainer::push( "Your PeeringDB affiliation with {$c->getFormattedName()} has been removed from IXP Manager as you are no longer affiliated with this network on PeeringDB.", Alert::WARNING ); + } + + Auth::login( $result['user'] ); + $this->authenticated( $r, $result['user'] ); + return redirect(''); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Auth/ResetPasswordController.php b/app/Http/Controllers/Auth/ResetPasswordController.php new file mode 100644 index 000000000..b74b956f1 --- /dev/null +++ b/app/Http/Controllers/Auth/ResetPasswordController.php @@ -0,0 +1,186 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers\Auth + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class ResetPasswordController extends Controller +{ + /* + |-------------------------------------------------------------------------- + | Password Reset Controller + |-------------------------------------------------------------------------- + | + | This controller is responsible for handling password reset requests + | and uses a simple trait to include this behavior. You're free to + | explore this trait and override any methods you wish to tweak. + | + */ + use ResetsPasswords; + + /** + * Where to redirect users after resetting their password. + * + * @var string + */ + protected $redirectTo = ''; + + /** + * Create a new controller instance. + * + * @return void + */ + public function __construct() + { + $this->middleware('guest' ); + } + + /** + * Display the password reset view for the given token. + * + * If no token is present, display the link request form. + * + * @param Request $r + * @param string|null $token + * + * @return View + */ + public function showResetForm( Request $r, string $token = null ): View + { + Former::populate( [ + 'username' => request()->old( 'username', $r->username ), + 'token' => request()->old( 'token', $token ), + ] ); + + return view('auth/reset-password')->with( [ + 'token' => $token, + 'username' => $r->username + ] ); + } + + /** + * Get the password reset validation rules. + * + * @return ((Password|string)[]|string)[] + * + * @psalm-return array{token: 'required', username: 'required|string', password: list{'required', 'confirmed', Password}} + */ + protected function rules(): array + { + return [ + 'token' => 'required', + 'username' => 'required|string', + 'password' => [ 'required', 'confirmed', Password::min(8)->letters()->mixedCase()->numbers() ], + ]; + } + + /** + * Get the password reset credentials from the request. + * + * @param Request $r + * + * @return array + */ + protected function credentials( Request $r ): array + { + return $r->only( + 'username', 'password', 'password_confirmation', 'token' + ); + } + + /** + * Reset the given user's password. + * + * @param User $user + * @param string $password + * + * @return void + * + * @throws + */ + protected function resetPassword( User $user, string $password ): void + { + $user->password = Hash::make( $password ); + $user->save(); + + event( new PasswordResetEvent( $user ) ); + $this->redirectTo = route("login@showForm" ) . '?username=' . $user->username ; + } + /** + * Get the response for a failed password reset. + * + * @param Request $r + * @param string $response + * + * @return RedirectResponse + */ + protected function sendResetFailedResponse(Request $r, string $response ): RedirectResponse + { + AlertContainer::push( trans( $response ) , Alert::DANGER ); + return redirect()->back()->withInput( $r->only('username' ) ); + } + + /** + * Get the response for a successful password reset. + * + * @param Request $r + * @param string $response + */ + protected function sendResetResponse( Request $r, string $response ): RedirectResponse + { + AlertContainer::push( trans( $response ) , Alert::SUCCESS ); + return redirect( $this->redirectPath() ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Auth/SwitchCustomerController.php b/app/Http/Controllers/Auth/SwitchCustomerController.php new file mode 100644 index 000000000..8b5a27239 --- /dev/null +++ b/app/Http/Controllers/Auth/SwitchCustomerController.php @@ -0,0 +1,98 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers\Auth + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class SwitchCustomerController extends Controller +{ + /** + * Allow a user to switch of customer + * + * @param Customer $cust + * + * @return RedirectResponse + */ + public function switch( Customer $cust ): RedirectResponse + { + /** @var User $user */ + $user = Auth::getUser(); + + // Check if the selected customer is associated with the current user + if( !( $c2u = CustomerToUser::where( 'customer_id', $cust->id )->where( 'user_id', $user->id )->first() ) ){ + AlertContainer::push( "You are not allowed to access to this " . config( "ixp_fe.lang.customer.one" ) . ".", Alert::DANGER ); + return redirect()->to( "/" ); + } + + // Check if the selected customer is active + if( $c2u->customer()->active()->notDeleted()->get()->isEmpty() ){ + AlertContainer::push( "You are not allowed to access to this " . config( "ixp_fe.lang.customer.one" ) . ".", Alert::DANGER ); + return redirect()->to( "/" ); + } + + $c2u->update([ + 'last_login_date' => now(), + 'last_login_from' => $this->getIp(), + ]); + + if( config( "ixp_fe.login_history.enabled" ) ) { + UserLoginHistory::create( [ + 'ip' => $this->getIp(), + 'at' => now(), + 'customer_to_user_id' => $c2u->id, + 'via' => 'SwitchCustomer' + ] ); + } + + $user->custid = $cust->id; + $user->save(); + + AlertContainer::push( "You are now logged in for {$cust->name}.", Alert::SUCCESS ); + return redirect()->to( "/" ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Auth/SwitchUserController.php b/app/Http/Controllers/Auth/SwitchUserController.php new file mode 100644 index 000000000..44c206d3a --- /dev/null +++ b/app/Http/Controllers/Auth/SwitchUserController.php @@ -0,0 +1,142 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers\Auth + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class SwitchUserController extends Controller +{ + use AuthenticatesUsers; + + /** + * Allow to switch users + * + * @param CustomerToUser $c2u + * + * @return RedirectResponse + */ + public function switch( CustomerToUser $c2u ): RedirectResponse + { + /** @var User $us */ + $us = Auth::getUser(); + if( !$us->isSuperUser() ) { + AlertContainer::push( "You are not allowed to switch users!", Alert::DANGER ); + return redirect()->to( "/" ); + } + + $user = $c2u->user; + + if( $user->disabled ){ + AlertContainer::push( "You cannot login as this user", Alert::DANGER ); + return redirect( '/' ); + } + + session()->put( 'switched_user_from', Auth::id() ); + session()->put( 'switched_c2u_to', $c2u->id ); + session()->put( 'switched_customer_from', $user->custid ); + session()->put( 'redirect_after_switch_back', request()->headers->get('referer', "" ) ); + + // Temporary change the default customer for the user + $user->custid = $c2u->customer_id; + $user->save(); + + Auth::login( $user ); + + Log::notice( Auth::getUser()->username . '(' . Auth::getUser()->name . ') logged as the user ' . $user->username . '(' . $user->name . ')' . ' for the customer ' . $user->customer->name ); + AlertContainer::push( "You are now logged in as {$user->username} " . " (" . Auth::getUser()->name . ") for the " . config( 'ixp_fe.lang.customer.one' ) . ' ' . $user->customer->name, Alert::SUCCESS ); + return redirect( '/' ); + } + + /** + * Allow to switch back users + * + * @return RedirectResponse|\Illuminate\Routing\Redirector + */ + public function switchBack(): \Illuminate\Routing\Redirector|RedirectResponse + { + if( !session()->exists( "switched_user_from" ) ) { + AlertContainer::push( "You are not currently logged in as another user. You are logged in as: " . Auth::getUser()->username . "( " . Auth::getUser()->name . " )", Alert::DANGER ); + return redirect()->to( "/" ); + } + + $redirect = "/"; + + if( !( $user = User::find( session()->get( "switched_user_from" ) ) ) ) { + $this->logout( request() ); + return redirect()->to( "/" ); + } + + // Get the previous default customer for the user + if( $c2u = CustomerToUser::find( session()->get( "switched_c2u_to" ) ) ) { + $switchedTo = $c2u->user; + + if( !( $cust = Customer::find( session()->get( "switched_customer_from" ) ) ) ) { + $cust = $c2u->customer; + } + $switchedTo->custid = $cust->id; + $switchedTo->save(); + } + + Auth::login( $user ); + + session()->remove( "switched_user_from" ); + session()->remove( "switched_c2u_to" ); + session()->remove( "switched_customer_from" ); + + AlertContainer::push( "You are now logged in as {$user->username} " . "(" . Auth::getUser()->name . ") for the " . config( 'ixp_fe.lang.customer.one' ) . ' ' . $user->customer->name, Alert::SUCCESS ); + + if( session()->exists( "redirect_after_switch_back" ) ) { + $redirect = session()->get( "redirect_after_switch_back" ); + session()->remove( "redirect_after_switch_back" ); + } + + return redirect( $redirect ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/CabinetController.php b/app/Http/Controllers/CabinetController.php new file mode 100644 index 000000000..d07c1fd17 --- /dev/null +++ b/app/Http/Controllers/CabinetController.php @@ -0,0 +1,274 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class CabinetController extends EloquentController +{ + /** + * The object being created / edited + * + * @var Cabinet + */ + protected $object = null; + + /** + * The URL prefix to use. + * + * Automatically determined based on the controller name if not set. + * + * @var string|null + */ + protected static $route_prefix = "rack"; + + /** + * This function sets up the frontend controller + */ + #[\Override] + public function feInit(): void + { + $this->feParams = ( object )[ + 'model' => Cabinet::class, + 'pagetitle' => 'Racks', + 'titleSingular' => 'Rack', + 'nameSingular' => 'rack', + 'defaultAction' => 'list', + 'defaultController' => 'CabinetController', + 'listOrderBy' => 'name', + 'listOrderByDir' => 'ASC', + 'viewFolderName' => 'cabinet', + 'listColumns' => [ + 'id' => [ 'title' => 'DB ID', 'display' => false ], + 'locationname' => [ + 'title' => 'Facility', + 'type' => self::$FE_COL_TYPES[ 'HAS_ONE' ], + 'controller' => 'facility', + 'action' => 'view', + 'idField' => 'locationid' + ], + 'name' => 'Name', + 'colocation' => 'Colo Location', + 'height' => 'Height' + ] + ]; + + // display the same information in the view as the list + $this->feParams->viewColumns = array_merge( + $this->feParams->listColumns, + [ + 'u_counts_from' => [ + 'title' => "U's Count From", + 'type' => self::$FE_COL_TYPES[ 'XLATE' ], + 'xlator' => Cabinet::$U_COUNTS_FROM + ], + 'type' => 'Type', + 'notes' => [ + 'title' => 'Notes', + 'type' => self::$FE_COL_TYPES[ 'PARSDOWN' ] + ], + 'created_at' => [ + 'title' => 'Created', + 'type' => self::$FE_COL_TYPES[ 'DATETIME' ] + ], + 'updated_at' => [ + 'title' => 'Updated', + 'type' => self::$FE_COL_TYPES[ 'DATETIME' ] + ] + ] + ); + } + + /** + * Provide array of rows for the list action and view action + * + * @param int|null $id The `id` of the row to load for `view` action`. `null` if `listAction` + + * @return array + */ + #[\Override] + protected function listGetData( ?int $id = null ): array + { + $feParams = $this->feParams; + return Cabinet::select( [ 'cabinet.*', 'l.name AS locationname' ] ) + ->leftJoin( 'location AS l', 'l.id', 'cabinet.locationid' ) + ->when( $id , function( Builder $q, $id ) { + return $q->where('cabinet.id', $id ); + } )->when( $feParams->listOrderBy , function( Builder $q, $orderby ) use ( $feParams ) { + return $q->orderBy( $orderby, $feParams->listOrderByDir ?? 'ASC'); + })->get()->toArray(); + } + + /** + * Display the form to add/edit an object + * + * @return (Cabinet|mixed)[] + * + * @psalm-return array{object: Cabinet, locations: mixed} + */ + #[\Override] + protected function createPrepareForm(): array + { + return [ + 'object' => $this->object, + 'locations' => Location::orderBy( 'name' )->get(), + ]; + } + + /** + * Display the form to create/edit an object + * + * @param int $id ID of the row to edit + * + * @return array + * + * @psalm-return array{object: mixed, locations: mixed} + */ + #[\Override] + protected function editPrepareForm( int $id ): array + { + $this->object = Cabinet::findOrFail( $id ); + + Former::populate( [ + 'name' => request()->old( 'name', $this->object->name ), + 'locationid' => request()->old( 'locationid', $this->object->locationid ), + 'colocation' => request()->old( 'colocation', $this->object->colocation ), + 'type' => request()->old( 'type', $this->object->type ), + 'height' => request()->old( 'height', $this->object->height ), + 'u_counts_from' => request()->old( 'u_counts_from', $this->object->u_counts_from ), + 'notes' => request()->old( 'notes', $this->object->notes ), + ] ); + + return [ + 'object' => $this->object, + 'locations' => Location::orderBy( 'name' )->get(), + ]; + } + + /** + * Function to do the actual validation and storing of the submitted object. + * + * @param Request $r + * + * @return true + * + * @throws + */ + #[\Override] + public function doStore( Request $r ) + { + $this->checkForm( $r ); + $this->object = Cabinet::create( $r->all() ); + return true; + } + + /** + * Function to do the actual validation and updating of the submitted object. + * + * @param Request $r + * @param int $id + * + * @return true + */ + #[\Override] + public function doUpdate( Request $r, int $id ) + { + $this->checkForm( $r ); + $this->object = Cabinet::findOrFail( $id ); + $this->object->update( $r->all() ); + return true; + } + + /** + * @inheritdoc + */ + #[\Override] + protected function preDelete(): bool + { + $okay = true; + if( ( $cnt = $this->object->customerEquipment()->count() ) ) { + AlertContainer::push( "Could not delete the rack as at least one piece of customer equipment is located here. Reassign or delete that kit first.", Alert::DANGER ); + $okay = false; + } + + if( ( $cnt = $this->object->switchers()->count() ) ) { + AlertContainer::push( "Could not delete the rack as at least one switch is located here. Reassign or delete the switch first.", Alert::DANGER ); + $okay = false; + } + + if( ( $cnt = $this->object->patchPanels()->count() ) ) { + AlertContainer::push( "Could not delete the rack as at least one patch panel is located here. Reassign or delete the panel first.", Alert::DANGER ); + $okay = false; + } + + + return $okay; + } + + /** + * Check if the form is valid + * + * @param $r + */ + #[\Override] + public function checkForm( Request $r ): void + { + $r->validate( [ + 'name' => 'required|string|max:255', + 'locationid' => 'required|integer|exists:location,id', + 'colocation' => 'required|string|max:255', + 'height' => 'nullable|integer', + 'type' => 'nullable|string|max:255', + 'notes' => 'nullable|string|max:65535', + 'u_counts_from' => 'required|integer|in:' . implode( ',', array_keys( Cabinet::$U_COUNTS_FROM ) ), + ] ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/ConsoleServer/ConsoleServerConnectionController.php b/app/Http/Controllers/ConsoleServer/ConsoleServerConnectionController.php new file mode 100644 index 000000000..6b68a9bba --- /dev/null +++ b/app/Http/Controllers/ConsoleServer/ConsoleServerConnectionController.php @@ -0,0 +1,361 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers\ConsoleServer + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class ConsoleServerConnectionController extends EloquentController +{ + /** + * The object being created / edited + * + * @var ConsoleServerConnection + */ + protected $object = null; + + /** + * This function sets up the frontend controller + */ + #[\Override] + public function feInit(): void + { + $this->feParams = (object)[ + 'model' => ConsoleServerConnection::class, + 'pagetitle' => 'Console Server Connections', + 'titleSingular' => 'Console Server Connection', + 'nameSingular' => 'a console server connection', + 'listOrderBy' => [ 'c.name', 'csc.port' ], + 'listOrderByDir' => 'ASC', + 'viewFolderName' => 'console-server-connection', + 'listColumns' => [ + 'customer' => [ + 'title' => 'Customer', + 'type' => self::$FE_COL_TYPES[ 'HAS_ONE' ], + 'controller' => 'customer', + 'action' => 'overview', + 'idField' => 'customerid' + ], + 'description' => 'Description', + 'port' => 'Port' + ] + ]; + + // display the same information in the view as the list + $this->feParams->viewColumns = array_merge( + $this->feParams->listColumns, + [ + 'speed' => [ + 'title' => 'Speed', + 'hideIfFieldTrue' => "autobaud" + ], + 'parity' => [ + 'title' => 'Parity', + 'type' => self::$FE_COL_TYPES[ 'CONST' ], + 'const' => ConsoleServerConnection::$PARITY, + 'hideIfFieldTrue' => "autobaud" + ], + 'stopbits' => [ + 'title' => 'Stopbits', + 'hideIfFieldTrue' => "autobaud" + ], + 'flowcontrol' => [ + 'title' => 'Flow Control', + 'type' => self::$FE_COL_TYPES[ 'CONST' ], + 'const' => ConsoleServerConnection::$FLOW_CONTROL, + 'hideIfFieldTrue' => "autobaud" + ], + 'autobaud' => [ + 'title' => 'Autobaud', + 'type' => self::$FE_COL_TYPES[ 'YES_NO' ], + ], + 'notes' => [ + 'title' => 'Notes', + 'type' => self::$FE_COL_TYPES[ 'PARSDOWN' ] + ], + 'created_at' => [ + 'title' => 'Created', + 'type' => self::$FE_COL_TYPES[ 'DATETIME' ] + ], + 'updated_at' => [ + 'title' => 'Updated', + 'type' => self::$FE_COL_TYPES[ 'DATETIME' ] + ] + ] + ); + } + + /** + * Additional routes + * + * @param string $route_prefix + * + * @return void + */ + #[\Override] + protected static function additionalRoutes( string $route_prefix ): void + { + Route::group( [ 'prefix' => $route_prefix ], static function() use ( $route_prefix ) { + Route::get( 'list/port/{cs}', 'ConsoleServer\ConsoleServerConnectionController@listPort' )->name( $route_prefix . '@listPort' ); + }); + } + + /** + * Provide array of rows for the list action and view action + * + * @param int|null $id The `id` of the row to load for `view` action`. `null` if `listAction` + * + * @return array + */ + #[\Override] + protected function listGetData( ?int $id = null ): array + { + return ConsoleServerConnectionAggregatore::getFeList( $this->feParams, $id ); + } + + + #[\Override] + protected function preList(): void + { + $this->data[ 'params' ] = [ + 'css' => ConsoleServer::orderBy( 'name' ) + ->get()->keyBy( 'id' )->toArray() + ]; + } + + /** + * Display the form to create an object + * + * @return (ConsoleServerConnection|mixed)[] + * + * @psalm-return array{object: ConsoleServerConnection, custs: mixed, servers: mixed, cs: mixed} + */ + #[\Override] + protected function createPrepareForm() : array + { + return [ + 'object' => $this->object, + 'custs' => Customer::orderBy( 'name' )->get(), + 'servers' => ConsoleServer::orderBy( 'name' )->get()->keyBy( 'id' )->toArray(), + 'cs' => request()->console_server_id + ]; + } + + /** + * Display the form to edit an object + * + * @param int $id ID of the row to edit + * + * @return array + * + * @psalm-return array{object: mixed, custs: mixed, servers: mixed, cs: mixed} + */ + #[\Override] + protected function editPrepareForm( int $id ) : array + { + $this->object = ConsoleServerConnection::find( $id ); + + Former::populate([ + 'description' => request()->old( 'description', $this->object->description ), + 'custid' => request()->old( 'custid', $this->object->custid ), + 'console_server_id' => request()->old( 'console_server_id', $this->object->console_server_id ), + 'port' => request()->old( 'port', $this->object->port ), + 'speed' => request()->old( 'speed', $this->object->speed ), + 'parity' => request()->old( 'parity', $this->object->parity ), + 'stopbits' => request()->old( 'stopbits', $this->object->stopbits ), + 'flowcontrol' => request()->old( 'flowcontrol', $this->object->flowcontrol ), + 'autobaud' => request()->old( 'autobaud', $this->object->autobaud ), + 'notes' => request()->old( 'notes', $this->object->notes ) + ]); + + return [ + 'object' => $this->object, + 'custs' => Customer::orderBy( 'name' )->get(), + 'servers' => ConsoleServer::orderBy( 'name' )->get()->keyBy( 'id' )->toArray(), + 'cs' => request()->console_server_id + ]; + } + + /** + * Function to do the actual validation and storing of the submitted object. + * + * @param Request $r + * + * @return RedirectResponse|true + * + * @throws + */ + #[\Override] + public function doStore( Request $r ) + { + $this->checkForm( $r ); + if( $this->checkIsDuplicate( $r ) ) { + return Redirect::back()->withInput(); + } + + $this->object = ConsoleServerConnection::make( $r->all() ); + $this->object->custid = $r->custid; + $this->object->save(); + return true; + } + + /** + * Function to do the actual validation and storing of the submitted object. + * + * @param Request $r + * @param int $id + * + * @return RedirectResponse|true + */ + #[\Override] + public function doUpdate( Request $r, int $id ) + { + $this->object = ConsoleServerConnection::findOrFail( $id ); + $this->checkForm( $r ); + if( $this->checkIsDuplicate( $r, $this->object->id ) ) { + return Redirect::back()->withInput(); + } + + $this->object->fill( $r->all() ); + $this->object->custid = $r->custid; + $this->object->save(); + return true; + } + + /** + * Display the Console Server Connections for a port + * + * @param ConsoleServer $cs ID of the Console Server + * + * @return View + */ + public function listPort( ConsoleServer $cs ): View + { + $this->listIncludeTemplates(); + $this->preList(); + $this->data[ 'rows' ] = ConsoleServerConnectionAggregatore::getFeList( $this->feParams, null, $cs->id ); + $this->data[ 'params' ][ "cs" ] = $cs->id; + return $this->display( 'list' ); + } + + /** + * @inheritdoc + */ + #[\Override] + protected function postStoreRedirect(): string + { + if( $cs = ConsoleServer::find( request()->cs ) ) { + return route( 'console-server-connection@listPort' , [ "cs" => $cs->id ] ) ; + } + + return route( 'console-server-connection@list' ); + } + + /** + * @inheritdoc + * + * @return null|string + */ + #[\Override] + protected function postDeleteRedirect(): ?string + { + return route('console-server-connection@listPort' , [ 'cs' => $this->object->console_server_id ] ); + } + + /** + * Check if the form is valid + * + * @param $r + */ + #[\Override] + public function checkForm( Request $r ): void + { + $r->validate( [ + 'description' => 'required|string|max:255', + 'custid' => 'required|integer|exists:cust,id', + 'console_server_id' => 'required|integer|exists:console_server,id', + 'port' => 'required|string|max:255', + 'speed' => 'nullable|integer', + 'parity' => 'nullable|string', + 'stopbits' => 'nullable|string', + 'flowcontrol' => 'nullable|string', + 'autobaud' => 'boolean', + 'notes' => 'nullable|string|max:65535', + ] ); + } + + /** + * Check if there is a duplicate console server connection object with those values + * + * @param Request $r + * @param int|null $objectid + * + * @return bool + */ + private function checkIsDuplicate( Request $r , ?int $objectid = null ): bool + { + $exist = ConsoleServerConnection::where( "console_server_id" , $r->console_server_id ) + ->where( 'port' , $r->port ) + ->when( $objectid , function( Builder $q, $objectid ) { + return $q->where( 'id', '!=', $objectid ); + })->count() ? true : false; + + if( $exist ) { + AlertContainer::push( "This port is already used by this console server." , Alert::DANGER ); + return true; + } + return false; + } +} \ No newline at end of file diff --git a/app/Http/Controllers/ConsoleServer/ConsoleServerController.php b/app/Http/Controllers/ConsoleServer/ConsoleServerController.php new file mode 100644 index 000000000..29e2f583f --- /dev/null +++ b/app/Http/Controllers/ConsoleServer/ConsoleServerController.php @@ -0,0 +1,294 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers\ConsoleServer + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class ConsoleServerController extends EloquentController +{ + /** + * The object being created / edited + * + * @var ConsoleServer + */ + protected $object = null; + + /** + * This function sets up the frontend controller + */ + #[\Override] + public function feInit(): void + { + $this->feParams = (object)[ + 'model' => ConsoleServer::class, + 'pagetitle' => 'Console Servers', + 'titleSingular' => 'Console Server', + 'nameSingular' => 'a console server', + 'listOrderBy' => 'id', + 'listOrderByDir' => 'ASC', + 'viewFolderName' => 'console-server', + 'listColumns' => [ + 'name' => [ + 'title' => 'Name', + 'type' => self::$FE_COL_TYPES[ 'HAS_ONE' ], + 'controller' => 'console-server-connection', + 'action' => 'list/port', + 'idField' => 'id' + ], + 'facility' => [ + 'title' => 'Facility', + 'type' => self::$FE_COL_TYPES[ 'HAS_ONE' ], + 'controller' => 'facility', + 'action' => 'view', + 'idField' => 'locationid' + ], + 'cabinet' => [ + 'title' => 'Cabinet', + 'type' => self::$FE_COL_TYPES[ 'HAS_ONE' ], + 'controller' => 'rack', + 'action' => 'view', + 'idField' => 'cabinetid' + ], + 'vendor' => [ + 'title' => 'Vendor', + 'type' => self::$FE_COL_TYPES[ 'HAS_ONE' ], + 'controller' => 'vendor', + 'action' => 'view', + 'idField' => 'vendorid' + ], + 'model' => 'Model', + 'num_connections' => [ + 'title' => 'Connections', + 'type' => self::$FE_COL_TYPES[ 'HAS_ONE' ], + 'controller' => 'console-server-connection', + 'action' => 'list/port', + 'idField' => 'id' + ], + 'active' => [ + 'title' => 'Active', + 'type' => self::$FE_COL_TYPES[ 'YES_NO' ] + ] + ] + ]; + + // display the same information in the view as the list + $this->feParams->viewColumns = array_merge( + $this->feParams->listColumns, + [ + 'serialNumber' => 'Serial Number', + 'notes' => [ + 'title' => 'Notes', + 'type' => self::$FE_COL_TYPES[ 'PARSDOWN' ] + ], + 'created_at' => [ + 'title' => 'Created', + 'type' => self::$FE_COL_TYPES[ 'DATETIME' ] + ], + 'updated_at' => [ + 'title' => 'Updated', + 'type' => self::$FE_COL_TYPES[ 'DATETIME' ] + ] + ] + ); + } + + /** + * Provide array of rows for the list action and view action + * + * @param int|null $id The `id` of the row to load for `view` action`. `null` if `listAction` + * + * @return array + */ + #[\Override] + protected function listGetData( $id = null ): array + { + $feParams = $this->feParams; + return ConsoleServer::selectRaw( + 'cs.*, + v.id AS vendorid, v.name AS vendor, + c.id AS cabinetid, c.name AS cabinet, + l.id AS locationid, l.shortname AS facility, + COUNT( DISTINCT csc.id ) AS num_connections' + ) + ->from( 'console_server AS cs' ) + ->leftJoin( 'consoleserverconnection AS csc', 'csc.console_server_id', 'cs.id') + ->leftJoin( 'cabinet AS c', 'c.id', 'cs.cabinet_id') + ->leftJoin( 'location AS l', 'l.id', 'c.locationid') + ->leftJoin( 'vendor AS v', 'v.id', 'cs.vendor_id') + ->when( $id , function( Builder $q, $id ) { + return $q->where('cs.id', $id ); + } )->when( $feParams->listOrderBy , function( Builder $q, $orderby ) use ( $feParams ) { + return $q->orderBy( $orderby, $feParams->listOrderByDir ?? 'ASC'); + }) + ->groupBy('cs.id' )->get()->toArray(); + } + + /** + * Display the form to create an object + * + * @return (ConsoleServer|mixed)[] + * + * @psalm-return array{object: ConsoleServer, cabinets: mixed, vendors: mixed} + */ + #[\Override] + protected function createPrepareForm(): array + { + return [ + 'object' => $this->object, + 'cabinets' => Cabinet::selectRaw( "id, concat( name, ' [', colocation, ']') AS name" ) + ->orderBy( 'name' )->get(), + 'vendors' => Vendor::orderBy( 'name' )->get(), + ]; + } + + /** + * Display the form to edit an object + * + * @param int $id ID of the row to edit + * + * @return array + * + * @psalm-return array{object: mixed, cabinets: mixed, vendors: mixed} + */ + #[\Override] + protected function editPrepareForm( int $id ): array + { + $this->object = ConsoleServer::findOrFail( $id ); + + Former::populate([ + 'name' => request()->old( 'name', $this->object->name ), + 'hostname' => request()->old( 'hostname', $this->object->hostname ), + 'model' => request()->old( 'model', $this->object->model ), + 'serialNumber' => request()->old( 'serial_number', $this->object->serialNumber ), + 'cabinet_id' => request()->old( 'cabinet', $this->object->cabinet_id ), + 'vendor_id' => request()->old( 'vendor', $this->object->vendor_id ), + 'active' => request()->old( 'active', $this->object->active ), + 'notes' => request()->old( 'notes', $this->object->notes ), + ]); + + return [ + 'object' => $this->object, + 'cabinets' => Cabinet::selectRaw( "id, concat( name, ' [', colocation, ']') AS name" ) + ->orderBy( 'name' )->get(), + 'vendors' => Vendor::orderBy( 'name' )->get() + ]; + } + + /** + * Function to do the actual validation and storing of the submitted object. + * + * @param Request $r + * + * @return true + * + * @throws + */ + #[\Override] + public function doStore( Request $r ): bool|RedirectResponse + { + $this->checkForm( $r ); + $this->object = ConsoleServer::create( $r->all() ); + return true; + } + + /** + * Function to do the actual validation and storing of the submitted object. + * + * @param Request $r + * @param int $id + * + * @return true + * + * @throws + */ + #[\Override] + public function doUpdate( Request $r, int $id ): bool|RedirectResponse + { + $this->object = ConsoleServer::findOrFail( $id ); + $this->checkForm( $r ); + $this->object->update( $r->all() ); + return true; + } + + /** + * Delete all console server connections before deleting the console server. + * + * @inheritdoc + * + * @return bool Return false to stop / cancel the deletion + */ + #[\Override] + protected function preDelete(): bool + { + $this->object->consoleServerConnections()->delete(); + return true; + } + + /** + * Check if the form is valid + * + * @param Request $r + */ + #[\Override] + public function checkForm( Request $r ): void + { + $r->validate( [ + 'name' => 'required|string|max:255|unique:console_server,name' . ( $this->object ? ','. $this->object->id : '' ), + 'vendor_id' => 'required|integer|exists:vendor,id', + 'cabinet_id' => 'required|integer|exists:cabinet,id', + 'model' => 'nullable|string|max:255', + 'serialNumber' => 'nullable|string', + 'notes' => 'nullable|string', + 'hostname' => [ 'required','string', new IdnValidate() ], + 'active' => 'string', + ] ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Contact/ContactController.php b/app/Http/Controllers/Contact/ContactController.php new file mode 100644 index 000000000..97a7dc1a5 --- /dev/null +++ b/app/Http/Controllers/Contact/ContactController.php @@ -0,0 +1,656 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers\Contact + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class ContactController extends EloquentController +{ + /** + * The object being created / edited + * + * @var Contact + */ + protected $object = null; + + /** + * @inheritdoc + */ + protected static $route_prefix = 'contact'; + + /** + * The minimum privileges required to access this controller. + * + * If you set this to less than the superuser, you need to manage privileges and access + * within your own implementation yourself. + * + * @var int + */ + public static $minimum_privilege = User::AUTH_CUSTADMIN; + + /** + * This function sets up the frontend controller + */ + #[\Override] + public function feInit(): void + { + $this->feParams = ( object )[ + 'model' => Contact::class, + 'pagetitle' => 'Contacts', + 'titleSingular' => 'Contact', + 'nameSingular' => 'contact', + 'documentation' => 'https://docs.ixpmanager.org/latest/usage/contacts/', + 'defaultAction' => 'list', + 'defaultController' => 'ContactController', + 'listOrderBy' => 'name', + 'listOrderByDir' => 'ASC', + 'viewFolderName' => 'contact', + ]; + + /** @var User $us */ + $us = Auth::getUser(); + switch( $privs = $us->privs() ) { + case User::AUTH_SUPERUSER: + $this->feParams->listColumns = [ + 'customer' => [ + 'title' => 'Customer', + 'type' => self::$FE_COL_TYPES[ 'HAS_ONE' ], + 'controller' => 'customer', + 'action' => 'overview', + 'idField' => 'custid' + ], + 'name' => 'Name', + 'position' => 'Position', + 'email' => 'Email', + 'created_at' => [ + 'title' => 'Created', + 'type' => self::$FE_COL_TYPES[ 'DATETIME' ] + ], + 'updated_at' => [ + 'title' => 'Updated', + 'type' => self::$FE_COL_TYPES[ 'DATETIME' ] + ] + ]; + + if( config('contact_group.types.ROLE') ) { + $this->feParams->listColumns = array_merge( + $this->feParams->listColumns, + [ + 'contact_roles' => [ + 'title' => 'Role', + 'type' => self::$FE_COL_TYPES[ 'LABEL' ], + 'array' => [ + 'delimiter' => ',', + 'replace' => '', + 'index' => 'name', + ] + ] + ] + ); + } + break; + + case User::AUTH_CUSTADMIN || User::AUTH_CUSTUSER: + $this->feParams->pagetitle = 'Your Contacts'; + + $this->feParams->listColumns = [ + 'name' => 'Name', + 'position' => 'Position', + 'email' => 'Email', + 'phone' => 'Phone', + 'created_at' => [ + 'title' => 'Created', + 'type' => self::$FE_COL_TYPES[ 'DATETIME' ] + ], + ]; + break; + + default: + $this->unauthorized(); + } + + // display the same information in the view as the list + if( $privs === User::AUTH_SUPERUSER ) { + $this->feParams->viewColumns = array_merge( + $this->feParams->listColumns, + [ + 'phone' => 'Phone', + 'mobile' => 'Mobile', + 'notes' => [ + 'title' => 'Notes', + 'type' => self::$FE_COL_TYPES[ 'PARSDOWN' ] + ] + ] + ); + } else { + $this->feParams->viewColumns = $this->feParams->listColumns; + } + } + + /** + * Gets a listing of contacts or a single one if an ID is provided + * + * @param stdClass $feParams + * @param int|null $id + * @param int|null $role + * @param int|null $cgid + * + * @return array + */ + private function getFeList( stdClass $feParams, ?int $id = null, ?int $role = null, ?int $cgid = null ): array + { + /** @var User $us */ + $us = Auth::getUser(); + $isSuperUser = $us->isSuperUser(); + $query = Contact::select( [ 'contact.*', 'cust.name AS customer', 'cust.id AS custid' ]) + ->leftJoin( 'cust', 'cust.id', 'contact.custid' ) + ->when( $id , function ( Builder $query, $id ) { + return $query->where('contact.id', $id ); + }) + ->when( !$isSuperUser, function ( Builder $query ) { + return $query->where('cust.id', Auth::getUser()->custid ); + }) + ->when( $feParams->listOrderBy , function( Builder $q, $orderby ) use ( $feParams ) { + return $q->orderBy( $orderby, $feParams->listOrderByDir ?? 'ASC'); + }); + + if( config('contact_group.types.ROLE') ) { + $groupid = $role ?: ($cgid ?: null); + $query->when( $groupid , function ( Builder $query, $groupid ) { + return $query->leftJoin( 'contact_to_group', function( $join ) { + $join->on( 'contact.id', 'contact_to_group.contact_id'); + })->where('contact_to_group.contact_group_id', $groupid ); + }); + + if( $isSuperUser ) { + $query->with( 'contactRoles', 'contactGroups' ); + } + } + return $query->get()->toArray(); + } + + /** + * Provide array of rows for the list action and view action + * + * @param int|null $id The `id` of the row to load for `view` action`. `null` if `listAction` + * + * @return array + * + * @throws + */ + #[\Override] + protected function listGetData( ?int $id = null ): array + { + $role = $cg = null; + $cgs = []; + + if( config('contact_group.types.ROLE') ) { + $activeGroups = ContactGroupAggregator::getGroupNamesTypeArray( null, null , true); + + if( !in_array( $role = request()->role, array_column( $activeGroups[ "ROLE" ], 'id' ), false ) ) { + $role = null; + } + + if( $cg = request()->cgid ) { + // flatten the multi dimensional array + foreach( $activeGroups as $gname => $gvalue ) { + foreach( $gvalue as $index => $val ){ + $cgs[ $index ] = $val[ 'name' ]; + } + } + + if( !array_key_exists( $cg, $cgs ) ) { + $cg = null; + } + } + + $this->data[ 'params' ] = [ + 'role' => $role, + 'roles' => $activeGroups[ "ROLE" ], + 'cg' => $cg, + 'contactGroups' => $cgs, + ]; + } else { + $this->data[ 'params' ] = [ + 'role' => $role, + 'roles' => [], + 'cg' => $cg, + 'contactGroups' => $cgs, + ]; + } + + return $this->getFeList( $this->feParams, $id, $role, $cg ); + } + + /** + * @inheritdoc + */ + #[\Override] + protected function preView(): void + { + /** @var User $us */ + $us = Auth::getUser(); + if( $us->custid !== (int)$this->data[ 'item' ][ 'custid' ] && !$us->isSuperUser() ) { + $this->unauthorized(); + } + + if( isset( $this->data[ 'item' ][ 'contact_groups' ] ) && count( $this->data[ 'item' ][ 'contact_groups' ] ) ) { + $this->feParams->viewColumns = array_merge( + $this->feParams->viewColumns, + [ + 'contact_groups' => [ + 'title' => 'Group', + 'type' => self::$FE_COL_TYPES[ 'LABEL' ], + 'array' => [ + 'delimiter' => ',', + 'replace' => '
', + 'array_index' => [ + 'type', 'name' + ] + ] + ] + ] + ); + } + } + + /** + * Display the form to add/edit an object + * + * @return (Contact|array|mixed)[] + * + * @throws + * + * @psalm-return array{object: Contact, groupsForContact: array, custs: mixed, roles: mixed, allGroups: mixed} + */ + #[\Override] + protected function createPrepareForm(): array + { + $this->setRedirectSession(); + $data = $this->getContactsData(); + + if( $cust = Customer::find( request()->cust ) ) { + Former::populate( [ + 'custid' => $cust->id, + ] ); + } + + return [ + 'object' => $this->object, + 'groupsForContact' => [], + 'custs' => Customer::orderBy( 'name' )->get(), + 'roles' => $data[ 'roles' ], + 'allGroups' => $data[ 'allGroups' ], + ]; + } + + /** + * Function to do the actual validation and storing of the submitted object. + * + * @param Request $r + * + * @return RedirectResponse|true + * + * @throws + */ + #[\Override] + public function doStore( Request $r ): bool|RedirectResponse + { + /** @var User $us */ + $us = Auth::getUser(); + + $this->checkForm( $r ); + $custid = $us->isSuperUser() ? $r->custid : $us->custid; + + $this->object = Contact::make( + array_merge( $r->all(), [ + 'creator' => $us->username, + 'lastupdatedby' => Auth::id() + ] ) + ); + $this->object->custid = $custid; + $this->object->save(); + + if( !$this->addGroupsToObject( $r->roles ?? [] ) ) { + return redirect( route( self::route_prefix() . '@edit', [ 'id' => $this->object->id ] ) ); + } + + return true; + } + + /** + * Display the form to edit an object + * + * @param int $id ID of the row to edit + * + * @return array + * + * @throws + * + * @psalm-return array{object: mixed, groupsForContact: mixed, custs: mixed, roles: mixed, allGroups: mixed} + */ + #[\Override] + protected function editPrepareForm( int $id ): array + { + /** @var User $us */ + $us = Auth::getUser(); + + $this->setRedirectSession(); + $this->object = Contact::findOrFail( $id ); + $data = $this->getContactsData(); + + if( $us->custid !== $this->object->customer->id && !$us->isSuperUser() ){ + $this->unauthorized(); + } + + $contactDetail = [ + 'name' => request()->old( 'name', $this->object->name ), + 'position' => request()->old( 'position', $this->object->position ), + 'custid' => request()->old( 'custid', $this->object->custid ), + 'email' => request()->old( 'email', $this->object->email ), + 'phone' => request()->old( 'phone', $this->object->phone ), + 'mobile' => request()->old( 'mobile', $this->object->mobile ), + 'notes' => request()->old( 'notes', $this->object->notes ), + ]; + + $contactGroupDetail = []; + $contactGroup = ContactGroupAggregator::getGroupNamesTypeArray( null, $this->object->id ); + + foreach( $data[ 'allGroups' ] as $gname => $gvalue ) { + foreach( $gvalue as $g ){ + $contactGroupDetail[ $gname . '_' . $g[ 'id' ] ] = request()->old( $gname . '_' . $g[ 'id' ] , isset( $contactGroup[ $gname ][ $g[ 'id' ] ] ) ? '1' : '0' ) ; + } + } + + Former::populate( array_merge( $contactDetail, $contactGroupDetail ) ); + + return [ + 'object' => $this->object, + 'groupsForContact' => $this->object->contactGroupsAll()->get()->keyBy( 'id' )->toArray(), + 'custs' => Customer::orderBy( 'name' )->get(), + 'roles' => $data[ 'roles' ], + 'allGroups' => $data[ 'allGroups' ], + ]; + } + + /** + * Function to do the actual validation and storing of the submitted object. + * + * @param Request $r + * @param int $id + * + * @return RedirectResponse|true + * + * @throws + */ + #[\Override] + public function doUpdate( Request $r, int $id ): bool|RedirectResponse + { + /** @var User $us */ + $us = Auth::getUser(); + + $this->object = Contact::findOrFail( $id ); + $this->checkForm( $r ); + + $custid = $us->custid; + + if( $us->isSuperUser() ) { + $custid = $r->custid; + } + + $this->object->fill( + array_merge( $r->all(), [ + 'creator' => $us->username, + 'lastupdatedby' => Auth::id() + ] ) + ); + + $this->object->custid = $custid; + $this->object->save(); + + $objectGroups = $this->object->contactGroupsAll()->get()->keyBy( 'id' )->toArray(); + $groupToAdd = array_diff( $r->roles ?? [], array_keys( $objectGroups ) ); + $groupToDelete = array_diff( array_keys( $objectGroups ), $r->roles ?? [] ); + + if( !$this->addGroupsToObject( $groupToAdd ) ) { + return redirect( route( self::route_prefix() . '@edit', [ 'id' => $this->object->id ] ) ); + } + + // save the object if addGroupsToObject was success + foreach( $groupToDelete as $gToDelete ) { + if( ( $cgroup = ContactGroup::find( $gToDelete ) ) && $cgroup->active ) { + $this->object->contactGroupsAll()->detach( $cgroup->id ); + } + } + + return true; + } + + /** + * @inheritdoc + * + * @return null|string + */ + #[\Override] + protected function postStoreRedirect(): string|null + { + /** @var User $us */ + $us = Auth::getUser(); + + if( !$us->isSuperUser() ) { + return route( 'contact@list' ); + } + + $redirect = session()->get( "contact_post_store_redirect" ); + session()->remove( "contact_post_store_redirect" ); + + // retrieve the customer ID + if( $redirect === 'customer@overview' ) { + return route( 'customer@overview' , [ 'cust' => $this->object->custid , 'tab' => 'contacts' ] ); + } + + return null; + } + + /** + * Function which can be over-ridden to perform any pre-deletion tasks + * + * You can stop the deletion by returning false but you should also add a + * message to explain why (to the AlertContainer). + * + * The object to be deleted is available via `$this->>object` + * + * @return bool Return false to stop / cancel the deletion + */ + #[\Override] + protected function preDelete(): bool + { + /** @var User $us */ + $us = Auth::getUser(); + + session()->remove( 'ixp_contact_delete_custid' ); + if( $us->isSuperUser() ) { + // keep the customer ID for redirection on success + $this->request->session()->put( "ixp_contact_delete_custid", $this->object->customer->id ); + } elseif( $this->object->customer->id !== $us->custid ) { + AlertContainer::push( 'You are not authorised to delete this contact.', Alert::DANGER ); + return false; + } + + $this->object->contactGroupsAll()->detach(); + return true; + } + + /** + * Allow D2F implementations to override where the post-delete redirect goes. + * + * To implement this, have it return a valid route url (e.g. `return route( "route-name" );` + * + * @return string + */ + #[\Override] + protected function postDeleteRedirect(): ?string + { + // retrieve the customer ID + if( strpos( request()->headers->get('referer', "" ), "customer/overview" ) ) { + if( $custid = $this->request->session()->get( "ixp_contact_delete_custid" ) ) { + $this->request->session()->remove( "ixp_contact_delete_custid" ); + return route( "customer@overview", [ "cust" => $custid, "tab" => "contacts" ] ); + } + } + return route( 'contact@list' ); + } + + /** + * Check if the form is valid + * + * @param $r + * + * @return void + */ + #[\Override] + public function checkForm( Request $r ): void + { + $rules = [ + 'name' => 'required|string|max:255', + 'position' => 'nullable|string|max:255', + 'email' => 'nullable|email', + 'phone' => 'nullable|string|max:50', + 'mobile' => 'nullable|string|max:50', + 'notes' => 'nullable|string|max:255', + ]; + + /** @var User $us */ + $us = Auth::getUser(); + if( $us->isSuperUser() ){ + $rules = array_merge( $rules, + [ 'custid' => 'required|integer|exists:cust,id' ] + ); + } + + $r->validate( $rules ); + } + + /** + * Set the session in order to redirect to the good location + * + * @return void + */ + private function setRedirectSession(): void + { + session()->remove( "contact_post_store_redirect" ); + + // check if we come from the customer overview or the contact list + if( strpos( request()->headers->get('referer', '' ), "customer/overview" ) ) { + session()->put( 'contact_post_store_redirect', 'customer@overview' ); + session()->put( 'contact_post_store_redirect_cid', request()->cust ); + } else { + session()->put( 'contact_post_store_redirect', 'contact@list' ); + session()->put( 'contact_post_store_redirect_cid', null ); + } + } + + /** + * return an array of contacts data + * + * @return ((array|mixed)[][]|null)[] + * + * @psalm-return array{roles: array|null, allGroups: array>} + */ + private function getContactsData(): array + { + if( config('contact_group.types.ROLE') ) { + return [ + 'roles' => ContactGroupAggregator::getGroupNamesTypeArray( 'ROLE' )[ "ROLE" ], + 'allGroups' => ContactGroupAggregator::getGroupNamesTypeArray( null, null, true ) + ]; + } + return [ 'roles' => null, 'allGroups' => [] ]; + } + + /** + * Add contact group to the contact object + * + * @param array $groups + * + * @return bool + */ + private function addGroupsToObject( array $groups ): bool + { + foreach( $groups as $index => $groupid ) { + if( $cgroup = ContactGroup::find( $groupid ) ) { + if( $cgroup->limited_to !== 0 ) { + $nbContactForCust = Contact::when( $groupid , function ( Builder $query, $groupid ) { + return $query->leftJoin( 'contact_to_group', function( $join ) { + $join->on( 'contact.id', 'contact_to_group.contact_id'); + })->where('contact_to_group.contact_group_id', $groupid ); + }) + ->where( 'custid', $this->object->custid ) + ->get()->count(); + + if( $cgroup->limited_to <= $nbContactForCust && !$this->object->contactGroupsAll->contains( 'id', $groupid ) ) { + AlertContainer::push( "Contact group " . $cgroup->type . " : " . $cgroup->name . " has a limited membership and is full." , Alert::DANGER ); + return false; + } + } + + if( !$this->object->contactGroupsAll->contains( 'id', $groupid ) ) { + $this->object->contactRoles()->attach( $cgroup ); + } + } + } + return true; + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Contact/ContactGroupController.php b/app/Http/Controllers/Contact/ContactGroupController.php new file mode 100644 index 000000000..5ff7b3d10 --- /dev/null +++ b/app/Http/Controllers/Contact/ContactGroupController.php @@ -0,0 +1,254 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers\Contact + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class ContactGroupController extends EloquentController +{ + /** + * The object being created / edited + * + * @var ContactGroup + */ + protected $object = null; + + protected static $route_prefix = "contact-group"; + + /** + * This function sets up the frontend controller + */ + #[\Override] + public function feInit(): void + { + $this->feParams = ( object )[ + 'model' => ContactGroup::class, + 'pagetitle' => 'Contact Groups', + 'titleSingular' => 'Contact Group', + 'nameSingular' => 'contact group', + 'defaultAction' => 'list', + 'defaultController' => 'ContactGroupController', + 'listOrderBy' => 'type', + 'listOrderByDir' => 'ASC', + 'viewFolderName' => 'contact-group', + 'documentation' => 'https://docs.ixpmanager.org/latest/usage/contacts/#contact-groups', + 'listColumns' => [ + 'type' => [ + 'title' => 'Group Name', + 'type' => self::$FE_COL_TYPES[ 'ARRAY' ], + 'source' => config( "contact_group.types" ) + ], + 'name' => 'Option', + 'active' => [ + 'title' => 'Active', + 'type' => self::$FE_COL_TYPES[ 'YES_NO' ] + ], + 'created_at' => [ + 'title' => 'Created', + 'type' => self::$FE_COL_TYPES[ 'DATETIME' ] + ] + ] + ]; + + // display the same information in the view as the list + $this->feParams->viewColumns = array_merge( + $this->feParams->listColumns, + [ + 'updated_at' => [ + 'title' => 'Updated', + 'type' => self::$FE_COL_TYPES[ 'DATETIME' ] + ], + 'limited_to' => [ + 'title' => 'Limit', + 'type' => self::$FE_COL_TYPES[ 'INTEGER' ] + + ], + 'description' => 'Description' + ] + ); + } + + /** + * Provide array of rows for the list action and view action + * + * @param int|null $id The `id` of the row to load for `view` action`. `null` if `listAction` + + * @return array + * + * @throws + */ + #[\Override] + protected function listGetData( ?int $id = null ): array + { + $feParams = $this->feParams; + return ContactGroup::when( $id , function( Builder $q, $id ) { + return $q->where('id', $id ); + } )->when( $feParams->listOrderBy , function( Builder $q, $orderby ) use ( $feParams ) { + return $q->orderBy( $orderby, $feParams->listOrderByDir ?? 'ASC'); + })->when( $types = config( "contact_group.types" ) , function( Builder $q, $types ) { + return $q->whereIn('type', array_keys( $types ) ); + } )->get()->toArray(); + } + + /** + * @return RedirectResponse|null + */ + #[\Override] + protected function canList(): ?RedirectResponse + { + // are contact groups configured? + if( config( 'contact_group.types', false ) === false ) { + AlertContainer::push( 'Contact groups are not configured. Please see the documentation here.', Alert::INFO ); + return redirect( route( 'contact@list' ) ); + } + return null; + } + + /** + * Display the form to create an object + * + * @return (|ContactGroup)[] + * + * @throws + * + * @psalm-return array{object: ContactGroup, types: } + */ + #[\Override] + protected function createPrepareForm(): array + { + return [ + 'object' => $this->object, + 'types' => config( 'contact_group.types' ) + ]; + } + + /** + * Display the form to edit an object + * + * @param int|null $id ID of the row to edit + * + * @return (|mixed)[] + * + * @throws + * + * @psalm-return array{object: mixed, types: } + */ + #[\Override] + protected function editPrepareForm( ?int $id = null ): array + { + $this->object = ContactGroup::findOrFail( $id ); + + Former::populate( [ + 'name' => request()->old( 'name', $this->object->name ), + 'description' => request()->old( 'description', $this->object->description ), + 'type' => request()->old( 'type', $this->object->type ), + 'active' => request()->old( 'active', $this->object->active ), + 'limited_to' => request()->old( 'limit', $this->object->limited_to ), + ] ); + + return [ + 'object' => $this->object, + 'types' => config( "contact_group.types" ) + ]; + } + + /** + * Function to do the actual validation and storing of the submitted object. + * + * @param Request $r + * + * @return true + * + * @throws + */ + #[\Override] + public function doStore( Request $r ): bool|RedirectResponse + { + $this->checkForm( $r ); + $this->object = ContactGroup::create( $r->all() ); + return true; + } + + /** + * Function to do the actual validation and storing of the submitted object. + * + * @param Request $r + * @param int $id + * + * @return true + * + * @throws + */ + #[\Override] + public function doUpdate( Request $r, int $id ): bool|RedirectResponse + { + $this->object = ContactGroup::findOrFail( $id ); + $this->checkForm( $r ); + $this->object->update( $r->all() ); + return true; + } + + /** + * Check if the form is valid + * + * @param Request $r + */ + #[\Override] + public function checkForm( Request $r ): void + { + $r->validate( [ + 'name' => 'required|string|max:255|unique:contact_group,name' . ( $r->id ? ','. $r->id : '' ), + 'description' => 'required|string|max:255', + 'type' => 'required|string|in:' . implode( ',', array_keys( config( 'contact_group.types' ) ) ), + 'limited_to' => 'required|integer|min:0', + ] ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/ContentController.php b/app/Http/Controllers/ContentController.php new file mode 100644 index 000000000..762614a53 --- /dev/null +++ b/app/Http/Controllers/ContentController.php @@ -0,0 +1,145 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class ContentController extends Controller +{ + /** + * Display the appropriate static content page (if permissions match) + * + * @param int $priv Required privilege for access to the content + * @param string $page Page to display + * + * @return View + */ + public function index( int $priv, string $page ): View + { + /** @var User $us */ + $us = Auth::getUser(); + // check privilege: + if( $priv !== User::AUTH_PUBLIC ) { + if( Auth::guest() || $us->privs() < $priv ) { + abort( 403, 'Unauthorized' ); + } + } + + // sanitise page name: + $page = "content/{$priv}/" . preg_replace( '/[^a-z0-9\-_]/', '', strtolower( $page ) ); + + if( !FacadeView::exists( $page ) ) { + abort( 404, 'Requested page not found' ); + } + + return view( $page ); + } + + /** + * Alias for public only content + * + * @param string $page Page to display + * + * @return View + */ + public function public( string $page ): View + { + return $this->index( 0, $page ); + } + + /** + * Display the appropriate member details page (if permissions match) + * + * @param int $priv Required privilege for access to the content + * @param string $page Page to display + */ + public function members( int $priv, string $page ): Response + { + /** @var User $us */ + $us = Auth::getUser(); + + // check privilege: + if( $priv !== User::AUTH_PUBLIC ) { + if( Auth::guest() || $us->privs() < $priv ) { + abort( 403, 'Unauthorized' ); + } + } + + // check format + $page = strtolower( $page ); + if( strpos( $page, '.' ) === false ) { + $page .= '.html'; + } + + [ $page, $format ] = explode( '.', $page ); + + // sanitise page name: + $page = "content/members/{$priv}/" . preg_replace( '/[^a-z0-9\-_]/', '', $page ); + + if( !FacadeView::exists( $page ) ) { + abort( 404, 'Requested page not found' ); + } + + $r = response()->view( $page, [ 'customers' => Customer::currentActive()->get() ], 200 ); + + if( $format === 'json' ) { + $r->header( 'Content-Type', 'application/json' ); + } + + return $r; + } + + /** + * Display the appropriate member details page (if permissions match) + */ + public function simpleMembers(): Response + { + return $this->members( 0, 'list.json' ); + } + +} diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php new file mode 100644 index 000000000..161703da7 --- /dev/null +++ b/app/Http/Controllers/Controller.php @@ -0,0 +1,144 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Controller extends BaseController +{ + use AuthorizesRequests, DispatchesJobs, ValidatesRequests; + + /** + * Checks if reseller mode is enabled. + * + * To enable reseller mode set the env variable IXP_RESELLER_ENABLED + * + * @see https://docs.ixpmanager.org/latest/features/reseller/ + * + * @return bool + */ + protected function resellerMode(): bool + { + return (bool)config( 'ixp.reseller.enabled', false ); + } + + /** + * Checks if as112 is activated in the UI. + * + * To disable as112 in the UI set the env variable IXP_AS112_UI_ACTIVE + * + * @see https://docs.ixpmanager.org/latest/features/as112/ + * + * @return bool + */ + protected function as112UiActive(): bool + { + return (bool)config( 'ixp.as112.ui_active', false ); + } + + /** + * Checks if logo management is enabled + * + * To enable logos in the UI set IXP_FE_FRONTEND_DISABLED_LOGO=false in .env + * + * @return bool + */ + protected function logoManagementEnabled(): bool + { + return !(bool)config( 'ixp_fe.frontend.disabled.logo' ); + } + + /** + * Try to get the clients real IP address even when behind a proxy. + * + * Source: https://stackoverflow.com/questions/33268683/how-to-get-client-ip-address-in-laravel-5/41769505#41769505 + * @return string|null + */ + protected function getIp(): ?string + { + foreach( [ 'HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR' ] as $key ) { + if( array_key_exists( $key, $_SERVER ) === true ) { + foreach( explode(',', (string) $_SERVER[$key] ) as $ip ) { + $ip = trim($ip); + if( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ) !== false ) { + return $ip; + } + } + } + } + return request()->getClientIp(); + } + + /** + * Return the list of privileges for the dropdown depending on the logged in user priv and if the customer is internal or not + * + * @return array List of privileges + * + * @throws + */ + protected function getAllowedPrivs(): array + { + /** @var User $us */ + $us = Auth::getUser(); + + $privs = User::$PRIVILEGES_TEXT_NONSUPERUSER; + $isSuperUser = $us->isSuperUser(); + + // If we add a user via the customer overview users list + if( request()->custid && request()->is( 'user/create*' ) ) { + if( ( $c = Customer::find( request()->custid ) ) ) { + // Internal customer and SuperUser + if( $c->typeInternal() && $isSuperUser ){ + $privs = User::$PRIVILEGES_TEXT; + } + } + }elseif( $isSuperUser && ( request()->is( 'user/create*' ) || request()->is( 'customer-to-user/create*' ) ) ) { + $privs = User::$PRIVILEGES_TEXT; + } + return $privs; + } + + +} \ No newline at end of file diff --git a/app/Http/Controllers/CustKitController.php b/app/Http/Controllers/CustKitController.php new file mode 100644 index 000000000..d9fc01d21 --- /dev/null +++ b/app/Http/Controllers/CustKitController.php @@ -0,0 +1,237 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers\ConsoleServer + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class CustKitController extends EloquentController +{ + /** + * The object being created / edited + * + * @var CustomerEquipment + */ + protected $object = null; + + /** + * This function sets up the frontend controller + */ + #[\Override] + public function feInit(): void + { + $this->feParams = (object)[ + 'model' => CustomerEquipment::class, + 'pagetitle' => 'Colocated Equipment', + 'titleSingular' => 'Colocated Equipment', + 'nameSingular' => 'colocated equipment', + 'listOrderBy' => 'name', + 'listOrderByDir' => 'ASC', + 'viewFolderName' => 'cust-kit', + 'listColumns' => [ + 'id' => [ 'title' => 'DB ID', 'display' => false ], + 'name' => 'Name', + 'customer' => [ + 'title' => 'Customer', + 'type' => self::$FE_COL_TYPES[ 'HAS_ONE' ], + 'controller' => 'customer', + 'action' => 'overview', + 'idField' => 'custid' + ], + 'cabinet' => [ + 'title' => 'Rack', + 'type' => self::$FE_COL_TYPES[ 'HAS_ONE' ], + 'controller' => 'rack', + 'action' => 'view', + 'idField' => 'cabinetid' + ], + ] + ]; + + // display the same information in the view as the list + $this->feParams->viewColumns = array_merge( + $this->feParams->listColumns, [ + 'descr' => 'Description', + 'created_at' => [ + 'title' => 'Created', + 'type' => self::$FE_COL_TYPES[ 'DATETIME' ] + ], + 'updated_at' => [ + 'title' => 'Updated', + 'type' => self::$FE_COL_TYPES[ 'DATETIME' ] + ] + + ], + + + ); + } + + /** + * Provide array of rows for the list and view + * + * @param int|null $id The `id` of the row to load for `view`. `null` if `list` + * + * @return array + */ + #[\Override] + protected function listGetData( ?int $id = null ): array + { + $feParams = $this->feParams; + return CustomerEquipment::select( [ 'custkit.*', 'cabinet.name AS cabinet', 'cust.name as customer' ] ) + ->leftJoin( 'cabinet', 'cabinet.id', 'custkit.cabinetid' ) + ->leftJoin( 'cust', 'cust.id', 'custkit.custid' ) + ->when( $id , function( Builder $q, $id ) { + return $q->where('custkit.id', $id ); + } )->when( $feParams->listOrderBy , function( Builder $q, $orderby ) use ( $feParams ) { + return $q->orderBy( $orderby, $feParams->listOrderByDir ?? 'ASC'); + })->get()->toArray(); + } + + /** + * Display the form to create an object + * + * @return (CustomerEquipment|mixed)[] + * + * @psalm-return array{object: CustomerEquipment, cabinets: mixed, custs: mixed} + */ + #[\Override] + protected function createPrepareForm(): array + { + return [ + 'object' => $this->object , + 'cabinets' => Cabinet::selectRaw( "id, concat( name, ' [', colocation, ']') AS name" ) + ->orderBy( 'name' )->get(), + 'custs' => Customer::orderBy( 'name' )->get(), + ]; + } + + /** + * Function to do the actual validation and storing of the submitted object. + * + * @param Request $r + * + * @return true + * + * @throws + */ + #[\Override] + public function doStore( Request $r ) + { + $this->checkForm( $r ); + $this->object = CustomerEquipment::make( $r->all() ); + $this->object->custid = $r->custid; + $this->object->save(); + return true; + } + + /** + * Display the form to edit an object + * + * @param int $id ID of the row to edit + * + * @return array + * + * @psalm-return array{object: mixed, cabinets: mixed, custs: mixed} + */ + #[\Override] + protected function editPrepareForm( int $id ): array + { + $this->object = CustomerEquipment::findOrFail( $id ); + + Former::populate([ + 'name' => request()->old( 'name', $this->object->name ), + 'custid' => request()->old( 'custid', $this->object->custid ), + 'cabinetid' => request()->old( 'cabinetid', $this->object->cabinetid ), + 'descr' => request()->old( 'descr', $this->object->descr ), + ]); + + return [ + 'object' => $this->object , + 'cabinets' => Cabinet::selectRaw( "id, concat( name, ' [', colocation, ']') AS name" ) + ->orderBy( 'name')->get(), + 'custs' => Customer::orderBy( 'name' )->get(), + ]; + } + + /** + * Function to do the actual validation and storing of the submitted object. + * + * @param Request $r + * @param int $id + * + * @return true + * + * @throws + */ + #[\Override] + public function doUpdate( Request $r, int $id ) + { + $this->object = CustomerEquipment::findOrFail( $id ); + $this->checkForm( $r ); + $this->object->fill( $r->all() ); + $this->object->custid = $r->custid; + $this->object->save(); + return true; + } + + /** + * Check if the form is valid + * + * @param $r + */ + #[\Override] + public function checkForm( Request $r ): void + { + $r->validate( [ + 'name' => 'required|string|max:255', + 'custid' => 'required|integer|exists:cust,id', + 'cabinetid' => 'required|integer|exists:cabinet,id', + 'descr' => 'nullable|string|max:255', + ] ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Customer/CustomerController.php b/app/Http/Controllers/Customer/CustomerController.php new file mode 100644 index 000000000..e79207580 --- /dev/null +++ b/app/Http/Controllers/Customer/CustomerController.php @@ -0,0 +1,609 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers\Customer + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class CustomerController extends Controller +{ + /** + * Display all the Customers as a list + * + * @param Request $r + * + * @return View + */ + public function list( Request $r ): View + { + if( ( $state = $r->state ) !== null ) { + if( isset( Customer::$CUST_STATUS_TEXT[ $state ] ) ) { + $r->session()->put( "cust-list-state", $state ); + } else { + $r->session()->remove( "cust-list-state" ); + } + } else if( $r->session()->exists( "cust-list-state" ) ) { + $state = $r->session()->get( "cust-list-state" ); + } + + if( ( $type = $r->type ) !== null ) { + if( isset( Customer::$CUST_TYPES_TEXT[ $type ] ) ) { + $r->session()->put( "cust-list-type", $type ); + } else { + $r->session()->remove( "cust-list-type" ); + } + } else if( $r->session()->exists( "cust-list-type" ) ) { + $type = $r->session()->get( "cust-list-type" ); + } + + if( ( $showCurrentOnly = $r->input( 'current-only' ) ) !== null ) { + $r->session()->put( "cust-list-current-only", $showCurrentOnly ); + } else if( $r->session()->exists( "cust-list-current-only" ) ) { + $showCurrentOnly = $r->session()->get( "cust-list-current-only" ); + } else { + $showCurrentOnly = false; + } + + $tags = CustomerTag::all()->keyBy( 'id' )->toArray(); + + if( $r->tag !== null ) { + if( isset( $tags[ $r->tag ] ) ) { + $tid = $tags[ $r->tag ][ 'id' ]; + $r->session()->put( "cust-list-tag", $tid ); + } else { + $r->session()->remove( "cust-list-tag" ); + $tid = false; + } + } else if( $r->session()->exists( "cust-list-tag" ) ) { + $tid = $r->session()->get( "cust-list-tag" ); + } else { + $tid = false; + } + + return view( 'customer/list' )->with([ + 'state' => $state ?? false, + 'type' => $type ?? false, + 'showCurrentOnly' => $showCurrentOnly ?? false, + 'tag' => $tid ?? false, + 'tags' => $tags, + 'custs' => Customer::selectRaw( 'cust.*' ) + ->when( $tid, function( Builder $q, $tid ) { + return $q->leftJoin( 'cust_to_cust_tag AS t', 't.customer_id', 'cust.id' ) + ->where( 't.customer_tag_id', $tid ); + } )->when( $state && isset( Customer::$CUST_STATUS_TEXT[ $state ] ), function( Builder $q ) use( $state ) { + return $q->where( 'cust.status', $state ); + } )->when( $type && isset( Customer::$CUST_TYPES_TEXT[ $type ] ), function( Builder $q ) use( $type ) { + return $q->where( 'cust.type', $type ); + } )->when( $showCurrentOnly, function( Builder $q ) { + return $q->whereRaw( Customer::SQL_CUST_CURRENT ); + } )->orderByRaw( 'cust.name' )->get(), + ]); + } + + /** + * Display the form to create a customer + * + * @return View + */ + public function create(): View + { + // populate the form with default data + Former::populate([ + 'activepeeringmatrix' => 1, + 'peeringdb_oauth' => 1, + ]); + + return view( 'customer/edit' )->with([ + 'cust' => false, + 'irrdbs' => IrrdbConfig::orderBy( 'source' )->get(), + 'resellers' => Customer::select( [ 'id', 'name' ] ) + ->resellerOnly()->orderBy( 'name' )->get(), + ]); + } + + /** + * Create a customer (set all the data needed) + * + * @param CustomerRequest $r instance of the current HTTP request + * + * @return RedirectResponse + * + * @throws + */ + public function store( CustomerRequest $r ): RedirectResponse + { + $bdetail = CompanyBillingDetail::create( [ 'purchaseOrderRequired' => 0 ] ); + $rdetail = CompanyRegisteredDetail::create( [ 'registeredName' => $r->name ] ); + + $cust = Customer::create( array_merge( $r->all(), + [ + 'reseller' => $r->isResold ? $r->reseller : null, + 'company_registered_detail_id' => $rdetail->id, + 'company_billing_details_id' => $bdetail->id, + 'creator' => Auth::id() + ] + ) ); + + Cache::forget( 'admin_home_customers' ); + AlertContainer::push( ucfirst( config( 'ixp_fe.lang.customer.one' ) ) . ' created.', Alert::SUCCESS ); + return redirect( route( 'customer@billing-registration' , [ 'cust' => $cust->id ] ) ); + } + + /** + * Display the form to edit a customer + * + * @param Request $r + * @param Customer $cust The Customer + * + * @return View + */ + public function edit( Request $r, Customer $cust ): View + { + Former::populate([ + 'name' => $r->old( 'name', $cust->name ), + 'type' => $r->old( 'type', $cust->type ), + 'shortname' => $r->old( 'shortname', $cust->shortname ), + 'corpwww' => $r->old( 'corpwww', $cust->corpwww ), + 'datejoin' => $r->old( 'datejoin', !$cust->datejoin ?: Carbon::parse($cust->datejoin)->format( "Y-m-d" ) ) , + 'dateleave' => $r->old( 'dateleave', !$cust->dateleave ?: Carbon::parse($cust->dateleave)->format( "Y-m-d" ) ), + 'status' => $r->old( 'status', $cust->status ), + 'MD5Support' => $r->old( 'MD5Support', $cust->MD5Support ), + 'abbreviatedName' => $r->old( 'abbreviatedName', $cust->abbreviatedName ), + 'autsys' => $r->old( 'autsys', (string) $cust->autsys ), + 'maxprefixes' => $r->old( 'maxprefixes', (string) $cust->maxprefixes ), + 'peeringpolicy' => $r->old( 'peeringpolicy', $cust->peeringpolicy ), + 'peeringemail' => $r->old( 'peeringemail', $cust->peeringemail ), + 'peeringmacro' => $r->old( 'peeringmacro', $cust->peeringmacro ), + 'peeringmacrov6' => $r->old( 'peeringmacrov6', $cust->peeringmacrov6 ), + 'irrdb' => $r->old( 'irrdb', (string) $cust->irrdb ), + 'activepeeringmatrix' => $r->old( 'activepeeringmatrix', (string) $cust->activepeeringmatrix ), + 'nocphone' => $r->old( 'nocphone', $cust->nocphone ), + 'noc24hphone' => $r->old( 'noc24hphone', $cust->noc24hphone ), + 'nocemail' => $r->old( 'nocemail', $cust->nocemail ), + 'nochours' => $r->old( 'nochours', $cust->nochours ), + 'nocwww' => $r->old( 'nocwww', $cust->nocwww ), + 'isReseller' => $r->old( 'isReseller', (string) $cust->isReseller ), + 'isResold' => $r->old( 'isResold', ( $this->resellerMode() && $cust->reseller ) ? '1' : '0' ), + 'reseller' => $r->old( 'reseller', ( $this->resellerMode() && $cust->reseller ) ? (string) $cust->reseller : null ), + 'peeringdb_oauth' => $r->old( 'peeringdb_oauth', (string) $cust->peeringdb_oauth ), + ]); + + return view( 'customer/edit' )->with([ + 'cust' => $cust, + 'irrdbs' => IrrdbConfig::orderBy( 'source' )->get(), + 'resellers' => Customer::select( [ 'id', 'name' ] )->resellerOnly() + ->orderBy( 'name' )->get(), + ]); + } + + /** + * Update a customer (set all the data needed) + * + * @param CustomerRequest $r instance of the current HTTP request + * @param Customer $cust + * + * @return RedirectResponse + * + * @throws + */ + public function update( CustomerRequest $r, Customer $cust ): RedirectResponse + { + $cust->update( array_merge( + $r->all(), + [ + 'lastupdatedby' => Auth::id(), + 'reseller' => $r->isResold ? $r->reseller : null, + ] + )); + + Cache::forget( 'admin_home_customers' ); + AlertContainer::push( ucfirst( config( 'ixp_fe.lang.customer.one' ) ) . ' updated ', Alert::SUCCESS ); + return redirect( route( "customer@overview" , [ "cust" => $cust->id ] ) ); + } + + /** + * Display the billing registration form a customer + * + * @param Request $r + * + * @param Customer $cust The Customer + * + * @return View + */ + public function billingAndRegDetails( Request $r, Customer $cust ): View + { + $cbd = $cust->companyBillingDetail; + $crd = $cust->companyRegisteredDetail; + + $dataBillingDetail = []; + if( !( $cust->reseller && $this->resellerMode() ) ){ + $dataBillingDetail = [ + 'billingContactName' => $r->old( 'billingContactName', $cbd->billingContactName ), + 'billingFrequency' => $r->old( 'billingFrequency', $cbd->billingFrequency ), + 'billingAddress1' => $r->old( 'billingAddress1', $cbd->billingAddress1 ), + 'billingAddress2' => $r->old( 'billingAddress2', $cbd->billingAddress2 ), + 'billingAddress3' => $r->old( 'billingAddress3', $cbd->billingAddress3 ), + 'billingTownCity' => $r->old( 'billingTownCity', $cbd->billingTownCity ), + 'billingPostcode' => $r->old( 'billingPostcode', $cbd->billingPostcode ), + 'billingCountry' => $r->old( 'billingCountry', in_array( $cbd->billingCountry, array_values( Countries::getListForSelect( 'iso_3166_2' ) ), false ) ? $cbd->billingCountry : null ), + 'billingEmail' => $r->old( 'billingEmail', $cbd->billingEmail ), + 'billingTelephone' => $r->old( 'billingTelephone', $cbd->billingTelephone ), + 'purchaseOrderRequired' => $r->old( 'purchaseOrderRequired', $cbd->purchaseOrderRequired ), + 'purchaseOrderNumber' => $r->old( 'purchaseOrderNumber', $cbd->purchaseOrderNumber ), + 'invoiceMethod' => $r->old( 'invoiceMethod', $cbd->invoiceMethod ), + 'invoiceEmail' => $r->old( 'invoiceEmail', $cbd->invoiceEmail ), + 'vatRate' => $r->old( 'vatRate', $cbd->vatRate ), + 'vatNumber' => $r->old( 'vatNumber', $cbd->vatNumber ), + 'billingNotes' => $r->old( 'billingNotes', $cbd->notes ), + ]; + } + + $dataRegistrationDetail = [ + 'registeredName' => $r->old( 'registeredName', $crd->registeredName ), + 'companyNumber' => $r->old( 'companyNumber', $crd->companyNumber ), + 'jurisdiction' => $r->old( 'jurisdiction', $crd->jurisdiction ), + 'address1' => $r->old( 'address1', $crd->address1 ), + 'address2' => $r->old( 'address2', $crd->address2 ), + 'address3' => $r->old( 'address3', $crd->address3 ) , + 'townCity' => $r->old( 'townCity', $crd->townCity ), + 'postcode' => $r->old( 'postcode', $crd->postcode ), + 'country' => $r->old( 'country', in_array( $crd->country, array_values( Countries::getListForSelect( 'iso_3166_2' ) ), false ) ? $crd->country : null ), + 'notes' => $r->old( 'notes', $crd->notes ), + ]; + + Former::populate( array_merge( $dataRegistrationDetail, $dataBillingDetail ) ); + + return view( 'customer/billing-registration' )->with([ + 'c' => $cust, + 'juridictions' => CompanyRegisteredDetail::select( 'jurisdiction' ) + ->where( 'jurisdiction', '!=', '' )->distinct()->get()->toArray(), + 'countries' => Countries::getList('name' ) + ]); + } + + /** + * Create or edit a customer's registration / billing information + * + * Also sends an email notification if so configured. + * + * @param BillingInformationRequest $r instance of the current HTTP request + * @param Customer $cust + * + * @return RedirectResponse + * @throws + */ + public function storeBillingAndRegDetails( BillingInformationRequest $r, Customer $cust ): RedirectResponse + { + $ocbd = clone $cust->companyBillingDetail; + $cbd = $cust->companyBillingDetail; + $crd = $cust->companyRegisteredDetail; + + $crd->update( $r->all() ); + + if( !( $cust->reseller && $this->resellerMode() ) ) { + // resolve name clash before saving + $cbd->update( array_merge( $r->all(), [ 'notes' => $r->billingNotes ] ) ); + } + + event( new CustomerBillingDetailsChangedEvent( $ocbd, $cbd ) ); + return redirect( route( "customer@overview" , [ 'cust' => $cust->id , 'tab' => 'details' ] ) ); + } + + /** + * Display the list of all the Customers + * + * @return RedirectResponse|View + */ + public function details(): RedirectResponse|View + { + if( config( 'ixp_fe.customer.details_public') ) { + return view( 'customer/details' )->with([ + 'custs' => Customer::currentActive( true, false )->get(), + 'associates' => false, + ]); + } + return redirect()->back(); + } + + /** + * Display the list of all associate customers + * + * @return RedirectResponse|View + */ + public function associates(): RedirectResponse|View + { + if( config( 'ixp_fe.customer.details_public') ) { + return view( 'customer/details' )->with([ + 'custs' => Customer::current()->associate() + ->orderBy( 'name' )->get(), + 'associates' => true, + ]); + } + return redirect()->back(); + } + + /** + * Display all the information for a customer + * + * @param Customer $cust + * + * @return RedirectResponse|View + */ + public function detail( Customer $cust ): RedirectResponse|View + { + if( config( 'ixp_fe.customer.details_public') ) { + return view( 'customer/detail' )->with([ + 'c' => $cust->load( [ 'logo', + 'virtualInterfaces.physicalInterfaces', + 'virtualInterfaces.vlanInterfaces.vlan', + 'virtualInterfaces.vlanInterfaces.ipv4address', + 'virtualInterfaces.vlanInterfaces.ipv6address', + ] ), + 'netinfo' => NetworkInfo::vlanProtocol(), + 'rsasns' => Router::routeServer() + ->groupBy( 'asn' )->get()->pluck( 'asn' )->toArray() + ]); + } + return redirect()->back(); + } + + /** + * Display the customer overview + * + * @param Customer $cust the customer + * @param string|null $tab Tab from the overview selected + * + * @return View + * @throws GeneralException + * @throws BindingResolutionException + */ + public function overview( Customer $cust, ?string $tab = null ) : View + { + $grapher = App::make( Grapher::class ); + + // get customer's notes + $notes = $cust->customerNotes()->orderByDesc( 'created_at' )->get(); + + return view( 'customer/overview' )->with([ + 'c' => $cust + ->load( [ 'companyRegisteredDetail', 'companyBillingDetail', + 'virtualInterfaces.physicalInterfaces.switchPort.switcher.infrastructureModel', + 'virtualInterfaces.physicalInterfaces.switchPort.switcher.cabinet.location', + 'virtualInterfaces.physicalInterfaces.switchPort.patchPanelPort.patchPanel', + 'virtualInterfaces.vlanInterfaces.ipv6address', + 'virtualInterfaces.vlanInterfaces.ipv4address', + 'virtualInterfaces.vlanInterfaces.layer2Addresses', + 'virtualInterfaces.vlanInterfaces.vlan.vlanInterfaces', + 'customerToUser.user.user2FA', + 'customerToUser.user.customers', + 'customerToUser.userLoginHistories', + 'contacts.contactRoles', + 'consoleServerConnections.consoleServer.cabinet.location', + 'resoldCustomers', 'irrdbConfig', + ] ), + 'customers' => Customer::select([ 'id', 'name' ])->current() + ->orderBy( 'name' )->get()->keyBy( 'id' )->toArray(), + 'netInfo' => NetworkInfo::vlanProtocol(), + 'crossConnects' => $cust->patchPanelPorts() + ->with( [ 'patchPanel.cabinet.location' ] )->masterPort()->get(), + 'crossConnectsHistory' => $cust->patchPanelPortHistories() + ->with( [ 'patchPanelPort.patchPanel.cabinet.location' ] )->masterPort()->get(), + 'aggregateGraph' => $cust->isGraphable() ? $grapher->customer( $cust ) : false, + 'grapher' => $grapher, + 'rsclient' => $cust->routeServerClient(), + 'as112client' => $cust->isAS112Client(), + 'as112UiActive' => $this->as112UiActive(), + 'countries' => Countries::getList('name' ), + 'tab' => strtolower( $tab ) ?: false, + 'notes' => $notes, + 'notesInfo' => CustomerNote::analyseForUser( $notes, $cust, Auth::getUser() ), + ]); + } + + /** + * Display the customer overview peers + * + * @param Request $r + * @param Customer $cust + * + * @return JsonResponse + */ + public function loadPeersFrag( Request $r, Customer $cust ): JsonResponse + { + return response()->json( [ + 'success' => true, + 'htmlFrag' => view('customer/overview-tabs/peers')->with([ + 'peers' => CustomerAggregator::getPeeringManagerArrayByType( $cust, Vlan::peeringManager()->orderBy( 'number' )->get()->toArray(), [ 4,6 ] ) ?: false + ])->render() + ] ); + } + + /** + * Display the Welcome Email form + * + * @param Customer $cust + * + * @return View + * + * @throws + */ + public function welcomeEmail( Customer $cust ) : View + { + $emails = []; + foreach( $cust->users as $user ){ + if( $email = filter_var( $user->email, FILTER_VALIDATE_EMAIL ) ) { + $emails[] = $email; + } + } + + Former::populate( [ + 'to' => $cust->nocemail, + 'cc' => implode( ',', $emails ), + 'bcc' => config('identity.email'), + 'subject' => config('identity.name'). ' :: Welcome Mail', + ] ); + + return view( 'customer/welcome-email' )->with([ + 'c' => $cust, + 'body' => view( "customer/emails/welcome-email" )->with([ + 'c' => $cust, + 'admins' => $cust->customerToUser()->custAdmin()->get(), + 'netinfo' => NetworkInfo::vlanProtocol(), + 'identityEmail' => config('identity.email'), + 'identityOrgname' => config('identity.orgname'), + ])->render() + ]); + } + + /** + * Send the welcome email to a customer + * + * @param WelcomeEmailRequest $r + * @param Customer $cust + */ + public function sendWelcomeEmail( WelcomeEmailRequest $r, Customer $cust ): RedirectResponse|View + { + $mailable = new WelcomeEmail( $cust, $r ); + + try { + $mailable->checkIfSendable(); + } catch( \Exception $e ) { + AlertContainer::push( $e->getMessage(), Alert::DANGER ); + return back()->withInput(); + } + + Mail::send( $mailable ); + AlertContainer::push( "Welcome email sent.", Alert::SUCCESS ); + return redirect( route( "customer@overview", [ "cust" => $cust->id ] ) ); + } + + /** + * Recap the information that will be deleted with the customer + * + * @param Customer $cust customer + * + * @return View|RedirectResponse + */ + public function deleteRecap( Customer $cust ): RedirectResponse|View + { + // cannot delete a customer with active cross connects: + if( $cust->patchPanelPorts->isNotEmpty() ) { + AlertContainer::push( "This customer has active patch panel ports. Please cease " + . "these (or set them to awaiting cease and unset the customer link in the patch panel " + . "port) to proceed with deleting this customer.", Alert::DANGER + ); + return redirect( route( "customer@overview", [ 'cust' => $cust->id ] ) ); + } + + // cannot delete a customer with fan out ports: + if( Customer::leftJoin( 'virtualinterface AS vi', 'vi.custid', 'cust.id' ) + ->leftJoin( 'physicalinterface AS pi', 'pi.virtualinterfaceid', 'vi.id' ) + ->whereNotNull( 'pi.fanout_physical_interface_id' ) + ->whereNotNull( 'reseller' )->where( 'cust.id', $cust->id )->count() ){ + AlertContainer::push( "This customer has is a resold customer with fan out physical " + . "interfaces. Please delete these manually before proceeding with deleting the customer.", + Alert::DANGER + ); + return redirect( route( "customer@overview", [ 'cust' => $cust->id ] ) ); + } + + return view( 'customer/delete' )->with([ + 'c' => $cust, + ]); + } + + /** + * Delete a customer and everything related !! + * + * @param Customer $cust + * + * @return RedirectResponse + * + * @throws + */ + public function delete( Customer $cust ) : RedirectResponse + { + if( CustomerAggregator::deleteObject( $cust ) ) { + AlertContainer::push( "Customer {$cust->getFormattedName()} deleted.", Alert::SUCCESS ); + Cache::forget( 'admin_home_customers' ); + } else { + AlertContainer::push( "Customer could not be deleted. Please open a GitHub bug report.", Alert::DANGER ); + } + return redirect( route( "customer@list" ) ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Customer/CustomerNotesController.php b/app/Http/Controllers/Customer/CustomerNotesController.php new file mode 100644 index 000000000..f3687c4e7 --- /dev/null +++ b/app/Http/Controllers/Customer/CustomerNotesController.php @@ -0,0 +1,110 @@ + + * @author Yann Robin + * @category Interfaces + * @copyright Copyright (C) 2009 - 2019 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class CustomerNotesController extends Controller +{ + /** + * @return RedirectResponse + * @throws + */ + public function readAll() : RedirectResponse + { + /** @var User $us */ + $us = Auth::getUser(); + + $prefs = $us->prefs; + // Delete all last_read notes prefs + if( isset( $prefs[ 'notes' ][ 'last_read' ] ) ) { + unset( $prefs[ 'notes' ][ 'last_read' ] ); + } + + // Set read_upto at now() + $prefs[ 'notes' ][ 'read_upto' ] = now()->format( 'Y-m-d H:i:s' ); + + $us->prefs = $prefs; + $us->save(); + + AlertContainer::push( 'All notes have been mark as read.', Alert::SUCCESS ); + + return redirect( route( "customerNotes@unreadNotes" ) ); + } + + /** + * Get the list of unread not for the current user + */ + public function unreadNotes(): \Illuminate\Contracts\View\View + { + /** @var User $us */ + $us = Auth::getUser(); + $lastRead = $us->prefs[ 'notes' ][ 'last_read' ] ?? []; + $readUpto = $us->prefs[ 'notes' ][ 'read_upto' ] ?? null; + $latestNotes = []; + + $custs = Customer::selectRaw( + 'cust.id AS cid, cust.name AS cname, cust.shortname AS cshortname, MAX( cn.updated_at) as latest' + )->join( 'cust_notes AS cn', 'cn.customer_id', 'cust.id' ) + ->groupByRaw( 'cid, cname, cshortname' ) + ->orderByDesc( 'latest' )->distinct()->get()->toArray(); + + foreach( $custs as $c ) { + if( ( !$readUpto || $readUpto < $c['latest'] ) + && ( !isset( $lastRead[ $c['cid'] ] ) || $lastRead[ $c[ 'cid' ] ] < $c['latest'] ) ) { + $latestNotes[] = $c; + } + } + + return view( 'customer/unread-notes' )->with([ + 'notes' => $latestNotes, + ]); + } +} + diff --git a/app/Http/Controllers/Customer/CustomerTagController.php b/app/Http/Controllers/Customer/CustomerTagController.php new file mode 100644 index 000000000..6927e44a8 --- /dev/null +++ b/app/Http/Controllers/Customer/CustomerTagController.php @@ -0,0 +1,302 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers\Customer + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class CustomerTagController extends EloquentController +{ + /** + * The object being created / edited + * + * @var CustomerTag + */ + protected $object = null; + + /** + * This function sets up the frontend controller + */ + #[\Override] + public function feInit(): void + { + $this->feParams = ( object )[ + 'model' => CustomerTag::class, + 'pagetitle' => ucfirst( config( 'ixp_fe.lang.customer.one' ) ) . ' Tags', + 'titleSingular' => ucfirst( config( 'ixp_fe.lang.customer.one' ) ) . ' Tag', + 'nameSingular' => config( 'ixp_fe.lang.customer.one' ) . ' tag', + 'defaultAction' => 'list', + 'defaultController' => 'CustomerTagController', + 'listOrderBy' => 'tag', + 'listOrderByDir' => 'ASC', + 'viewFolderName' => 'customer/tag', + 'extraDeleteMessage' => "This tag will be removed from all " . config( 'ixp_fe.lang.customer.many' ) . " tagged with it.", + 'documentation' => 'https://docs.ixpmanager.org/latest/usage/customer-tags/', + 'listColumns' => [ + 'tag' => 'Tag', + 'display_as' => 'Display As', + 'internal_only' => [ + 'title' => 'Internal Only', + 'type' => self::$FE_COL_TYPES[ 'YES_NO' ] + ], + ] + ]; + + // display the same information in the view as the list + $this->feParams->viewColumns = array_merge( + $this->feParams->listColumns, + [ + 'description' => 'Description', + 'created_at' => + [ + 'title' => 'Created', + 'type' => self::$FE_COL_TYPES[ 'DATETIME' ] + ], + 'updated_at' => + [ + 'title' => 'Updated', + 'type' => self::$FE_COL_TYPES[ 'DATETIME' ] + ], + ] + ); + } + + /** + * Additional routes + * + * @param string $route_prefix + * + * @return void + */ + #[\Override] + protected static function additionalRoutes( string $route_prefix ): void + { + Route::group( [ 'prefix' => $route_prefix ], static function() use ( $route_prefix ) { + Route::get( 'cust/{cust}', 'Customer\CustomerTagController@linkCustomer' )->name( $route_prefix . '@link-customer' ); + Route::post( 'link/{cust}', 'Customer\CustomerTagController@link' )->name( $route_prefix . '@link' ); + }); + } + + /** + * Provide array of rows for the list action and view action + * + * @param int|null $id The `id` of the row to load for `view` action`. `null` if `listAction` + * + * @return array + */ + #[\Override] + protected function listGetData( ?int $id = null ): array + { + $feParams = $this->feParams; + return CustomerTag::when( $id , function( Builder $q, $id ) { + return $q->where('id', $id ); + } )->when( $feParams->listOrderBy , function( Builder $q, $orderby ) use ( $feParams ) { + return $q->orderBy( $orderby, $feParams->listOrderByDir ?? 'ASC'); + })->get()->toArray(); + } + + /** + * Display the form to create an object + * + * @return CustomerTag[] + * + * @psalm-return array{object: CustomerTag} + */ + #[\Override] + protected function createPrepareForm(): array + { + return [ + 'object' => $this->object + ]; + } + + /** + * Display the form to edit an object + * + * @param int $id ID of the row to edit + * + * @return array + * + * @psalm-return array{object: mixed} + */ + #[\Override] + protected function editPrepareForm( int $id ): array + { + $this->object = CustomerTag::findOrFail( $id ); + + Former::populate([ + 'tag' => request()->old( 'tag', $this->object->tag ), + 'description' => request()->old( 'description', $this->object->description ), + 'display_as' => request()->old( 'display_as', $this->object->display_as ), + 'internal_only' => request()->old( 'internal_only', $this->object->internal_only ), + ]); + + return [ + 'object' => $this->object + ]; + } + + /** + * Function to do the actual validation and storing of the submitted object. + * + * @param Request $r + * + * @return true + * + * @throws + */ + #[\Override] + public function doStore( Request $r ): bool|RedirectResponse + { + $this->checkForm( $r ); + $r->request->set( 'tag', preg_replace( "/[^a-z0-9\-]/" , "", strtolower( $r->tag ) ) ); + $this->object = CustomerTag::create( $r->all() ); + return true; + } + + /** + * Function to do the actual validation and updating of the submitted object. + * + * @param Request $r + * @param int $id + * + * @return true + * + * @throws + */ + #[\Override] + public function doUpdate( Request $r, int $id ): bool|RedirectResponse + { + $this->object = CustomerTag::findOrFail( $r->id ); + $this->checkForm( $r ); + $r->request->set( 'tag', preg_replace( "/[^a-z0-9\-]/" , "", strtolower( $r->tag ) ) ); + $this->object->update( $r->all() ); + return true; + } + + /** + * @inheritdoc + */ + #[\Override] + protected function preDelete(): bool + { + $this->object->customers()->detach(); + Session::remove( "cust-list-tag" ); + return true; + } + + /** + * Display the form to link/unlink customer/tags + * + * @param Customer $cust The Customer + * + * @return View + */ + public function linkCustomer( Customer $cust ): View + { + return view( 'customer/tag/cust' )->with([ + 'c' => $cust, + 'tags' => CustomerTag::orderBy( 'display_as' )->get(), + ]); + } + + /** + * Add or edit a customer (set all the data needed) + * + * @param Request $r instance of the current HTTP request + * @param Customer $cust + * + * @return RedirectResponse + * @throws + */ + public function link( Request $r, Customer $cust ): RedirectResponse + { + $tagsToCreate = array_diff( $r->tags ?? [], array_keys( $cust->tags->keyBy( 'id' )->toArray() ) ); + $tagsToDelete = array_diff( array_keys( $cust->tags->keyBy( 'id' )->toArray() ), $r->tags ?? [] ); + + // Tags that have been unchecked => unlink from the customer + foreach( $tagsToDelete as $dtag ){ + CustomerToCustomerTag::where( 'customer_tag_id' ,$dtag ) + ->where( 'customer_id', $cust->id )->delete(); + } + + // Tags that have been checked => link to the customer + foreach( $tagsToCreate as $ctag ){ + if( CustomerToCustomerTag::where( 'customer_tag_id' , $ctag )->where( 'customer_id', $cust->id )->doesntExist() ){ + CustomerToCustomerTag::create([ + 'customer_tag_id' => $ctag, + 'customer_id' => $cust->id, + ]); + } + } + + AlertContainer::push( "Tags updated.", Alert::SUCCESS ); + return redirect( route( "customer@overview" , [ "cust" => $cust->id ] ) ); + } + + /** + * Check if the form is valid + * + * @param Request $r + */ + #[\Override] + public function checkForm( Request $r ): void + { + $r->validate( [ + 'tag' => 'required|string|max:255|unique:cust_tag,tag' . ( $this->object ? ',' . $this->object->id : '' ), + 'description' => 'nullable|string|max:255', + 'display_as' => 'required|string|max:255', + ] ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Customer/LogoController.php b/app/Http/Controllers/Customer/LogoController.php new file mode 100644 index 000000000..7019ab72c --- /dev/null +++ b/app/Http/Controllers/Customer/LogoController.php @@ -0,0 +1,198 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers\Customer + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class LogoController extends Controller +{ + /** + * Display all the customers' logos + * + * @return View + * + * @throws + */ + public function logos(): View + { + return view( 'customer/logos' )->with([ + 'customers' => Customer::with( 'logo' ) + ->has( 'logo' )->orderBy( 'name' )->get(), + ]); + } + + /** + * Display the form to create / edit / delete a member's logo + * + * @param int|null $id ID of the customer + * + * @return View + */ + public function manage( ?int $id ): View + { + $c = $this->loadCustomer( $id ); + + return view( 'customer/logo/manage' )->with( [ + 'c' => $c, + 'logo' => $c->logo ?: false + ] ); + } + + /** + * Upload a customer's logo + * + * @param LogoRequest $r instance of the current HTTP request + * + * @return RedirectResponse + * @throws + */ + public function store( LogoRequest $r ): RedirectResponse + { + /** @var User $us */ + $us = Auth::getUser(); + $c = $this->loadCustomer( $r->id ); + + if( !$r->hasFile( 'logo' ) ) { + abort(400); + } + + $file = $r->file('logo'); + $img = Image::make( $file->getPath() . '/' . $file->getFilename() ); + + $img->resize(null, 80, function ( $constraint ) { + $constraint->aspectRatio(); + })->encode('png'); + + // remove old logo + if( $oldLogo = $c->logo ) { + if( file_exists( public_path() . '/logos/' . $oldLogo->shardedPath() ) ) { + @unlink( public_path() . '/logos/' . $oldLogo->shardedPath() ); + } + $oldLogo->delete(); + } + + $logo = Logo::make( [ + 'original_name' => $file->getClientOriginalName(), + 'stored_name' => sha1( $img->getEncoded() ) . '.png', + 'uploaded_by' => $us->username, + 'width' => $img->width(), + 'height' => $img->height(), + ] ); + + $logo->customer_id = $c->id; + $logo->save(); + + $saveTo = $logo->fullPath(); + + if( !is_dir( dirname( $saveTo ) ) ) { + @mkdir( dirname( $saveTo ), 0755, true ); + } + + $img->save( $saveTo ); + + AlertContainer::push( "Logo uploaded.", Alert::SUCCESS ); + return redirect( $us->isSuperUser() ? route( "customer@overview" , [ 'cust' => $c->id ] ) : route( "dashboard@index" ) ); + } + + /** + * Delete a customer's logo + * + * @param int $id + * + * @return RedirectResponse + * + * @throws Exception + */ + public function delete( int $id ) : RedirectResponse + { + /** @var User $us */ + $us = Auth::getUser(); + + $c = $this->loadCustomer( $id ); + + // do we have a logo? + if( !( $oldLogo = $c->logo ) ) { + AlertContainer::push( "Sorry, we could not find any logo for you.", Alert::DANGER ); + return redirect( '' ); + } + + if( file_exists( $oldLogo->fullPath() ) ) { + @unlink( $oldLogo->fullPath() ); + } + + $oldLogo->delete(); + AlertContainer::push( "Logo deleted.", Alert::SUCCESS ); + + return redirect( $us->isSuperUser() ? route( 'customer@overview', [ 'cust' => $c->id ] ) : route( 'dashboard@index' ) ); + } + + /** + * Load a customer from the database with the given ID (or ID in request) + * + * @param int|null $id The customer ID + * + * @return Customer The customer object + */ + private function loadCustomer( ?int $id ): Customer + { + /** @var User $us */ + $us = Auth::getUser(); + if( $us->isSuperUser() ) { + return Customer::findOrFail( $id ); + } + return Customer::find( Auth::getUser()->custid ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/DashboardController.php b/app/Http/Controllers/DashboardController.php new file mode 100644 index 000000000..1efa26ebe --- /dev/null +++ b/app/Http/Controllers/DashboardController.php @@ -0,0 +1,199 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class DashboardController extends Controller +{ + /** + * Display dashboard + * + * @param Request $r + * @param string|null $tab Tab from the overview selected + * + * @return RedirectResponse|View + * + * @throws + */ + public function index( Request $r, ?string $tab = null ): RedirectResponse|View + { + /** @var User $us */ + $us = Auth::getUser(); + // Redirect Super user + if( $us->isSuperUser() ) { + return redirect( '/'); + } + + $c = Auth::getUser()->customer; + $grapher = null; + $cns = $c->customerNotes()->publicOnly()->get(); + $cbd = $c->companyBillingDetail; + + if( !$c->typeAssociate() ) { + $resoldCustomer = $c->reseller; + $netinfo = NetworkInfo::vlanProtocol(); + $grapher = App::make( Grapher::class ); + } + + // array used to populate the details forms + // former doesn't allow us to populate a form the classic way when there is >1 forms on the same view. + $dataNocDetail = [ + 'nocphone' => $r->old( 'nocphone', $c->nocphone ), + 'noc24hphone' => $r->old( 'noc24hphone', $c->noc24hphone ), + 'nocemail' => $r->old( 'nocemail', $c->nocemail ), + 'nochours' => $r->old( 'nochours', $c->nochours ), + 'nocwww' => $r->old( 'nocwww', $c->nocwww ), + ]; + + $dataBillingDetail = [ + 'billingContactName' => $r->old( 'billingContactName', $cbd->billingContactName ), + 'billingAddress1' => $r->old( 'billingAddress1', $cbd->billingAddress1 ), + 'billingAddress2' => $r->old( 'billingAddress2', $cbd->billingAddress2 ), + 'billingAddress3' => $r->old( 'billingAddress3', $cbd->billingAddress3 ), + 'billingTownCity' => $r->old( 'billingTownCity', $cbd->billingTownCity ), + 'billingPostcode' => $r->old( 'billingPostcode', $cbd->billingPostcode ), + 'billingCountry' => $r->old( 'billingCountry', in_array( $cbd->billingCountry, array_values( Countries::getListForSelect( 'iso_3166_2' ) ), false ) ? $cbd->billingCountry : null ), + 'billingEmail' => $r->old( 'billingEmail', $cbd->billingEmail ), + 'billingTelephone' => $r->old( 'billingTelephone', $cbd->billingTelephone ), + 'invoiceEmail' => $r->old( 'invoiceEmail', $cbd->invoiceEmail ), + ]; + + return view( 'dashboard/index' )->with([ + 'recentMembers' => Customer::getConnected( true, true, 'datejoin', 'desc' )->take( 5 ), + 'crossConnects' => $c->patchPanelPorts()->masterPort()->get(), + 'notesInfo' => CustomerNote::analyseForUser( $cns, $c, Auth::getUser() ), + 'resoldCustomer' => $resoldCustomer ?? null, + 'netInfo' => $netinfo ?? null, + 'c' => $c->load( [ + 'logo', 'virtualInterfaces.vlanInterfaces.vlan', 'resoldCustomers', + 'logo', 'virtualInterfaces.vlanInterfaces.layer2Addresses', + 'logo', 'virtualInterfaces.vlanInterfaces.ipv6address', + 'logo', 'virtualInterfaces.vlanInterfaces.ipv4address', + 'virtualInterfaces.physicalInterfaces.switchPort.switcher.infrastructureModel', + 'virtualInterfaces.physicalInterfaces.switchPort.switcher.cabinet.location', + 'virtualInterfaces.physicalInterfaces.switchPort.patchPanelPort.patchPanel', + ] ), + 'notes' => $cns, + 'grapher' => $grapher, + 'dataBillingDetail' => $dataBillingDetail, + 'dataNocDetail' => $dataNocDetail, + 'countries' => Countries::getList('name' ), + 'tab' => strtolower( $tab ) ?: false, + 'p2pstats' => P2pDailyStats::latestN( $c ), + + ]); + } + + /** + * Edit NOC details of a customer via the dashboard + * + * @param NocDetailsRequest $r instance of the current HTTP request + * + * @return RedirectResponse + * + * @throws + */ + public function storeNocDetails( NocDetailsRequest $r ): RedirectResponse + { + $c = Auth::getUser()->customer; + $c->nocphone = $r->nocphone; + $c->noc24hphone = $r->noc24hphone; + $c->nocemail = $r->nocemail; + $c->nochours = $r->nochours; + $c->nocwww = $r->nocwww; + $c->lastupdatedby = Auth::id(); + $c->save(); + + AlertContainer::push( 'NOC details updated', Alert::SUCCESS ); + return redirect( route( "dashboard@index", [ "tab" => "details" ] ) ); + } + + /** + * Edit Billing details of a customer via the dashboard + * + * + * @param BillingDetailsRequest $r instance of the current HTTP request + * + * @return RedirectResponse + * + * @throws + */ + public function storeBillingDetails( BillingDetailsRequest $r ): RedirectResponse + { + $c = Auth::getUser()->customer; + $cbd = $c->companyBillingDetail; + $ocbd = clone $c->companyBillingDetail; + + $cbd->billingContactName = $r->billingContactName; + $cbd->billingAddress1 = $r->billingAddress1; + $cbd->billingAddress2 = $r->billingAddress2; + $cbd->billingAddress3 = $r->billingAddress3; + $cbd->billingTownCity = $r->billingTownCity; + $cbd->billingPostcode = $r->billingPostcode; + $cbd->billingCountry = $r->billingCountry; + $cbd->billingEmail = $r->billingEmail; + $cbd->billingTelephone = $r->billingTelephone; + $cbd->invoiceEmail = $r->invoiceEmail; + $cbd->save(); + + event( new CustomerBillingDetailsChangedEvent( $ocbd, $cbd ) ); + + AlertContainer::push( 'Billing details updated.', Alert::SUCCESS ); + return Redirect::to( route( "dashboard@index", [ "tab" => "details" ] ) ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/DiagnosticsController.php b/app/Http/Controllers/DiagnosticsController.php new file mode 100644 index 000000000..5e1c8ad36 --- /dev/null +++ b/app/Http/Controllers/DiagnosticsController.php @@ -0,0 +1,166 @@ + + * @author Barry O'Donovan + * @category IXP + * @package IXP\Http\Controllers + * @copyright Copyright (C) 2009 - 2024 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class DiagnosticsController extends Controller +{ + public array $badgeTypes; + + public function __construct() { + $this->badgeTypes = [ + DiagnosticResult::TYPE_FATAL => 'tw-border-red-600 tw-bg-red-600', + DiagnosticResult::TYPE_ERROR => 'tw-border-red-400 tw-bg-red-400', + DiagnosticResult::TYPE_WARN => 'tw-border-amber-400 tw-bg-amber-400', + DiagnosticResult::TYPE_INFO => 'tw-border-teal-400 tw-bg-teal-400', + DiagnosticResult::TYPE_UNKNOWN => 'tw-border-gray-600 tw-bg-gray-800', + DiagnosticResult::TYPE_DEBUG => 'tw-border-gray-300 tw-bg-gray-300', + DiagnosticResult::TYPE_TRACE => 'tw-border-gray-400 tw-bg-gray-400', + DiagnosticResult::TYPE_GOOD => 'tw-border-lime-500 tw-bg-lime-500', + ]; + } + + /** + * @return string[] + * + * @psalm-return array + */ + private function generateBadges(): array { + $badges = []; + $enabledBadges = [ + DiagnosticResult::TYPE_FATAL, + DiagnosticResult::TYPE_ERROR, + DiagnosticResult::TYPE_WARN, + DiagnosticResult::TYPE_INFO, + DiagnosticResult::TYPE_UNKNOWN, + //DiagnosticResult::TYPE_DEBUG, + //DiagnosticResult::TYPE_TRACE, + DiagnosticResult::TYPE_GOOD, + ]; + + foreach(DiagnosticResult::$RESULT_TYPES_TEXT as $result => $text) { + + $plainResult = new DiagnosticResult( + name: '', + result: $result, + narrative: '', + ); + $enable = ' tw-opacity-40'; + if(in_array($result, $enabledBadges)) { + $enable = ''; + } + + $badgeExtension = 'badge()); + } + + return $badges; + } + + /** + * Run the diagnostics suite + */ + public function customer( Customer $customer, Diagnostics $diagnostics ): View + { + $resultSets = []; + + $resultSets[] = $diagnostics->getCustomerDiagnostics($customer); + + foreach( $customer->virtualInterfaces as $vi ) { + $viSet = $diagnostics->getVirtualInterfaceDiagnostics( $vi ); + + // get the Physical Interface Diagnostics Data and integrate here into the VI array + foreach( $vi->physicalInterfaces as $pi ) { + $viSet->addSubset( $diagnostics->getPhysicalInterfaceDiagnostics( $pi ) ); + $viSet->addSubset( $diagnostics->getTransceiverDiagnostics( $pi ) ); + } + + // get the Vlan Interface Diagnostics data + $protocols = [4,6]; + foreach( $vi->vlanInterfaces as $vli ) { + // not ready: $viSet->addSubset( $diagnostics->getVlanInterfaceL2Diagnostics( $vli ) ); + + foreach( $protocols as $protocol ) { + // if the protocol disabled, there is no diagnostics info + $protocolCellEnabled = "ipv" . $protocol . "enabled"; + if($vli->$protocolCellEnabled) { + // not ready: $viSet->addSubset( $diagnostics->getVlanInterfaceL3Diagnostics( $vli, $protocol ) ); + $viSet->addSubset( $diagnostics->getRouterBgpSessionsDiagnostics( $vli, $protocol ) ); + } + + } + + } + + $resultSets[] = $viSet; + + } + + + // former view: diagnostics.results (still works) + return view( 'diagnostics.newresults')->with([ + "badgeTypes" => $this->badgeTypes, + "badges" => $this->generateBadges(), + "customer" => $customer, + "resultSets" => $resultSets, + ]); + } + + + /** + * Run the diagnostics suite + */ + public function irrdb( Customer $customer, Diagnostics $diagnostics ): View + { + $resultSets = []; + + $resultSets[] = $diagnostics->getCustomerIrrdbDiagnostics($customer); + + return view( 'diagnostics.results')->with([ + "badgeTypes" => $this->badgeTypes, + "badges" => $this->generateBadges(), + "customer" => $customer, + "resultSets" => $resultSets, + ]); + } + + +} diff --git a/app/Http/Controllers/Docstore/DirectoryController.php b/app/Http/Controllers/Docstore/DirectoryController.php new file mode 100644 index 000000000..344e10447 --- /dev/null +++ b/app/Http/Controllers/Docstore/DirectoryController.php @@ -0,0 +1,223 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers\Docstore + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class DirectoryController extends Controller +{ + /** + * Display the list of directories + * + * @param DocstoreDirectory|null $dir + * + * @return View|RedirectResponse + */ + public function list( ?DocstoreDirectory $dir = null ): View|RedirectResponse + { + $privs = User::AUTH_PUBLIC; + if( $user = Auth::user() ){ + $privs = $user->privs; + } + + /** @var User $us */ + $us = optional($user); + + $dirs = DocstoreDirectory::getHierarchyForUserClass( $us->privs() ?? 0 )[ $dir->id ?? '' ] ?? []; + $files = DocstoreFile::getListing( $dir, $privs ); + + $nbTotalDirs = count( DocstoreDirectory::getHierarchyForUserClass( User::AUTH_SUPERUSER )[ $dir->id ?? '' ] ?? [] ); + $nbTotalFiles = count( DocstoreFile::getListing( $dir, User::AUTH_SUPERUSER ) ); + + if( !count($dirs) && count($dirs) <= $nbTotalDirs && !count($files) && count($files) <= $nbTotalFiles && ( $nbTotalDirs + $nbTotalFiles ) > 0 ) { + // Only show a folder if there's a file (or folder) there for the user to see: + if( !Auth::check() ){ + return redirect( route( 'login@login' ) ); + } + + abort( 401, 'Nothing for you here. You either need to log in or you do not have sufficient privileges.' ); + } + + return view( 'docstore/dir/list', [ + 'dir' => $dir ?: false, + 'dirs' => $dirs, + 'files' => $files, + ] ); + } + + /** + * Create a new directory + * + * @param Request $r + * + * @return View + * + * @throws AuthorizationException + */ + public function create( Request $r ): View + { + $this->authorize( 'create', DocstoreDirectory::class ); + + return view( 'docstore/dir/create', [ + 'dir' => false, + 'dirs' => DocstoreDirectory::getListingForDropdown( DocstoreDirectory::getListing( null, Auth::getUser() ) ), + ] ); + } + + /** + * Store a directory + * + * @param Request $r + * + * @return RedirectResponse + * + * @throws AuthorizationException + */ + public function store( Request $r ): RedirectResponse + { + $this->authorize( 'create', DocstoreDirectory::class ); + $this->checkForm( $r ); + $dir = DocstoreDirectory::create( $r->all() ); + + Log::info( sprintf( "DocStore: new directory [%d|%s] created by %s", $dir->id, $dir->name, Auth::getUser()->username ) ); + AlertContainer::push( "New directory {$dir->name} created.", Alert::SUCCESS ); + return redirect( route( 'docstore-dir@list', [ 'dir' => $dir->id ] ) ); + } + + /** + * Edit a new directory + * + * @param Request $r + * @param DocstoreDirectory $dir + * + * @return View + * + * @throws AuthorizationException + */ + public function edit( Request $r, DocstoreDirectory $dir ): View + { + $this->authorize( 'update', $dir ); + + Former::populate([ + 'name' => $r->old( 'name', $dir->name ), + 'description' => $r->old( 'descripton', $dir->description ), + 'parent_dir_id' => $r->old( 'parent_dir', $dir->parent_dir_id ?: '' ), + ]); + + return view( 'docstore/dir/create', [ + 'dir' => $dir, + 'dirs' => DocstoreDirectory::getListingForDropdown( DocstoreDirectory::getListing( null, Auth::user() ) ), + ] ); + } + + /** + * Update a directory + * + * @param Request $r + * @param DocstoreDirectory $dir + * @return RedirectResponse + * + * @throws AuthorizationException + */ + public function update( Request $r , DocstoreDirectory $dir ): RedirectResponse + { + $this->authorize( 'update', $dir ); + $this->checkForm( $r ); + $dir->update( $r->all() ); + + Log::info( sprintf( "DocStore: directory [%d|%s] edited by %s", $dir->id, $dir->name, Auth::user()->username ) ); + AlertContainer::push( "Directory {$dir->name} updated.", Alert::SUCCESS ); + return redirect( route( 'docstore-dir@list', [ 'dir' => $dir->parent_dir_id ] ) ); + } + + /** + * Delete a directory + * + * @param Request $r + * @param DocstoreDirectory $dir + * + * @return RedirectResponse + * + * @throws AuthorizationException + */ + public function delete( Request $r , DocstoreDirectory $dir ): RedirectResponse + { + $this->authorize( 'delete', $dir ); + + Log::notice( sprintf( "DocStore: start recursive deletion of directory [%d|%s] by %s", $dir->id, $dir->name, $r->user()->username ) ); + DocstoreDirectory::recursiveDelete( $dir ); + Log::notice( sprintf( "DocStore: finish recursive deletion of directory [%d|%s] by %s", $dir->id, $dir->name, $r->user()->username ) ); + + AlertContainer::push( "Directory {$dir->name} deleted.", Alert::SUCCESS ); + return redirect( route( 'docstore-dir@list', [ 'dir' => $dir->parent_dir_id ] ) ); + } + + /** + * Check if the form is valid + * + * @param Request $r + * + * @return void + */ + private function checkForm( Request $r ): void + { + $r->validate( [ + 'name' => 'required|max:100', + 'description' => 'nullable', + 'parent_dir_id' => 'nullable|integer|exists:docstore_directories,id', + ] ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Docstore/FileController.php b/app/Http/Controllers/Docstore/FileController.php new file mode 100644 index 000000000..6908ea8c3 --- /dev/null +++ b/app/Http/Controllers/Docstore/FileController.php @@ -0,0 +1,333 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers\Docstore + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class FileController extends Controller +{ + /** + * Upload a new docstore file + * + * @param Request $r + * + * @return View + * + * @throws AuthorizationException + */ + public function upload( Request $r ): View + { + $this->authorize( 'create', DocstoreFile::class ); + + Former::populate([ + 'min_privs' => $r->old( 'min_privs', (string) User::AUTH_SUPERUSER ) + ]); + + return view( 'docstore/file/upload', [ + 'file' => false, + 'dirs' => DocstoreDirectory::getListingForDropdown( DocstoreDirectory::getListing( null, $r->user() ) ), + ] ); + } + + /** + * Store a docstore file uploaded + * + * @param Request $r + * + * @return RedirectResponse + * + * @throws AuthorizationException + */ + public function store( Request $r ): RedirectResponse + { + $this->authorize( 'create', DocstoreFile::class ); + + $this->checkForm( $r ); + $file = $r->file('uploadedFile'); + $path = $file->store( '', 'docstore' ); + + /** @psalm-suppress InvalidArgument */ + $file = DocstoreFile::create( [ + 'name' => $r->name, + 'description' => $r->description, + 'docstore_directory_id' => $r->docstore_directory_id, + 'min_privs' => $r->min_privs, + 'path' => $path, + 'sha256' => hash_file( 'sha256', $file ), + 'created_by' => Auth::id(), + 'file_last_updated' => now(), + ] ); + + Log::info( sprintf( "DocStore: file [%d|%s] uploaded by %s", $file->id, $file->name, Auth::user()->username ) ); + AlertContainer::push( "File {$r->name} uploaded.", Alert::SUCCESS ); + return redirect( route( 'docstore-dir@list', [ 'dir' => $file->docstore_directory_id ] ) ); + } + + /** + * Edit a docstore file uploaded + * + * @param Request $r + * @param DocstoreFile $file + * + * @return View + * + * @throws AuthorizationException + */ + public function edit( Request $r , DocstoreFile $file ): View + { + $this->authorize( 'update', $file ); + + Former::populate([ + 'name' => $r->old( 'name', $file->name ), + 'description' => $r->old( 'descripton', $file->description ), + 'sha256' => $r->old( 'sha256', $file->sha256 ), + 'min_privs' => $r->old( 'min_privs', (string) $file->min_privs ), + 'docstore_directory_id' => $r->old( 'docstore_directory_id', $file->docstore_directory_id ?: '' ), + ]); + + return view( 'docstore/file/upload', [ + 'file' => $file, + 'dirs' => DocstoreDirectory::getListingForDropdown( DocstoreDirectory::getListing( null, $r->user() ) ) + ] ); + } + + /** + * Update a docstore file uploaded + * + * @param Request $r + * @param DocstoreFile $file + * + * @return RedirectResponse + * + * @throws AuthorizationException + */ + public function update( Request $r , DocstoreFile $file ): RedirectResponse + { + $this->authorize( 'update', $file ); + $this->checkForm( $r, $file ); + + $user = Auth::user(); + + // if a new file is updated + if( $r->uploadedFile ) { + // get path of the old file in order to delete it later + $oldPath = $file->path; + $uploadedFile = $r->file('uploadedFile'); + $path = $uploadedFile->store( '', 'docstore' ); + + /** @psalm-suppress InvalidArgument */ + $file->update([ + 'path' => $path, + 'sha256' => hash_file( 'sha256', $uploadedFile ), + 'file_last_updated' => now(), + ]); + + // Delete the old file + Storage::disk( $file->disk )->delete( $oldPath ); + } + + // Purge the logs of the file + if( $r->purgeLogs ) { + Log::info( sprintf( "DocStore: all download logs for file [%d|%s] purged by %s", $file->id, $file->name, $user->username ) ); + $file->logs()->delete(); + } + + $file->update( [ + 'name' => $r->name, + 'description' => $r->description, + 'docstore_directory_id' => $r->docstore_directory_id, + 'min_privs' => $r->min_privs + ] ); + + Log::info( sprintf( "DocStore: file [%d|%s] edited by %s", $file->id, $file->name, $user->username ) ); + AlertContainer::push( "File {$file->name} updated.", Alert::SUCCESS ); + return redirect( route( 'docstore-dir@list', [ 'dir' => $file->docstore_directory_id ] ) ); + } + + /** + * View a docstore file apply to allowed mimetype ( DocstoreFile::$ + * + * @param DocstoreFile $file + * + * @return RedirectResponse|View + * + * @throws AuthorizationException|FileNotFoundException + */ + public function view( DocstoreFile $file ): RedirectResponse|View + { + $this->authorize( 'view', $file ); + + if( !$file->isViewable() ) { + return redirect( route( 'docstore-file@download', [ 'file' => $file->id ] ) ); + } + + if( Auth::user() ) { + $file->logs()->save( new DocstoreLog( [ 'downloaded_by' => Auth::id() ] ) ); + } + + return view( 'docstore/file/view', [ + 'file' => $file, + 'content' => Storage::disk( $file->disk )->get( $file->path ), + ]); + } + + /** + * Download a docstore file + * + * @param DocstoreFile $file + * + * @return mixed + * + * @throws AuthorizationException + */ + public function download( DocstoreFile $file ): mixed + { + $this->authorize( 'download', $file ); + + if( Auth::user() ) { + $file->logs()->save( new DocstoreLog( [ 'downloaded_by' => Auth::id() ] ) ); + } + + try { + /** @psalm-suppress UndefinedInterfaceMethod */ + return Storage::disk( $file->disk )->download( $file->path, $file->name ); + } catch( FilesystemException $e ) { + AlertContainer::push( "This file could not be found / downloaded. Please report this error to the support team.", Alert::DANGER ); + return redirect()->back(); + } + } + + /** + * Get information on a docstore file + * + * @param DocstoreFile $file + * + * @return mixed + * + * @throws AuthorizationException + */ + public function info( DocstoreFile $file ): mixed + { + $this->authorize( 'info', $file ); + + return view( 'docstore/file/info', [ + 'file' => $file, + 'size' => Storage::disk( $file->disk )->size( $file->path ), + 'last_modified' => Storage::disk( $file->disk )->lastModified( $file->path ), + 'dspath' => config( 'filesystems.disks.' . $file->disk . '.root', '*** UNKNOWN LOCATION ***' ) . '/' . $file->path, + 'created_by' => User::find( $file->created_by ), + 'created_at' => $file->created_at, + ]); + } + + /** + * Delete a file + * + * @param Request $r + * @param DocstoreFile $file + * + * @return RedirectResponse + * + * @throws AuthorizationException + */ + public function delete( Request $r , DocstoreFile $file ): RedirectResponse + { + $this->authorize( 'delete', $file ); + + $dir = $file->directory; + + Storage::disk( $file->disk )->delete( $file->path ); + $file->logs()->delete(); + $file->delete(); + + AlertContainer::push( "File {$file->name} deleted.", Alert::SUCCESS ); + Log::info( sprintf( "DocStore: file [%d|%s] deleted by %s", $file->id, $file->name, $r->user()->username ) ); + return redirect( route( 'docstore-dir@list', [ 'dir' => $dir ] ) ); + } + + /** + * Check if the form is valid + * + * @param Request $r + * @param DocstoreFile|null $file + * + * @return void + */ + private function checkForm( Request $r, ?DocstoreFile $file = null ): void + { + /** @psalm-suppress InvalidArgument */ + $r->validate( [ + 'name' => 'required|max:100', + 'uploadedFile' => Rule::requiredIf( function() use ( $r, $file ) { + return !$file; + }), + 'sha256' => 'nullable|max:64' . ( $r->file( 'uploadedFile' ) ? '|in:' . hash_file( 'sha256', $r->file( 'uploadedFile' ) ) : '' ) , + 'min_privs' => 'required|integer|in:' . implode( ',', array_keys( User::$PRIVILEGES_TEXT_ALL ) ), + 'docstore_directory_id' => 'nullable|integer|exists:docstore_directories,id', + ] ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Docstore/LogController.php b/app/Http/Controllers/Docstore/LogController.php new file mode 100644 index 000000000..e0bec7f4c --- /dev/null +++ b/app/Http/Controllers/Docstore/LogController.php @@ -0,0 +1,87 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers\Docstore + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class LogController extends Controller +{ + /** + * Display the list of unique logs for a file + * + * @param DocstoreFile $file + * + * @return View + * + * @throws AuthorizationException + */ + public function uniqueList( DocstoreFile $file ) : View + { + $this->authorize( 'viewAny', DocstoreLog::class ); + + return view( 'docstore/log/list', [ + 'file' => $file, + 'unique' => true, + 'logs' => DocstoreLog::getUniqueUserListing( $file ) + ] ); + } + + /** + * Display the list of all logs for a file + * + * @param DocstoreFile $file + * + * @return View + * + * @throws AuthorizationException + */ + public function list( DocstoreFile $file ) : View + { + $this->authorize( 'viewAny', DocstoreLog::class ); + + return view( 'docstore/log/list', [ + 'file' => $file, + 'unique' => false, + 'logs' => DocstoreLog::getListing( $file ) + ] ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/DocstoreCustomer/DirectoryController.php b/app/Http/Controllers/DocstoreCustomer/DirectoryController.php new file mode 100644 index 000000000..d07506734 --- /dev/null +++ b/app/Http/Controllers/DocstoreCustomer/DirectoryController.php @@ -0,0 +1,330 @@ + + * @author Yann Robin + * @category DocstoreCustomer + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class DirectoryController extends Controller +{ + /** + * Display the list of all Customer with docstore + * + * @return View + * + * @throws AuthorizationException + */ + public function listCustomers() : View + { + $this->authorize( 'listCustomers', [ DocstoreCustomerDirectory::class ] ); + + return view( 'docstore-customer/dir/customers', [ + 'files' => DocstoreCustomerFile::groupBy( 'cust_id' )->get() + ] ); + } + + /** + * Display the list of directories for a customer + * + * @param Request $r + * @param Customer $cust + * @param DocstoreCustomerDirectory|null $dir + * + * @return View + * + * @throws AuthorizationException + */ + public function list( Request $r, Customer $cust, DocstoreCustomerDirectory $dir = null ) : View + { + $this->authorize( 'list', [ DocstoreCustomerDirectory::class, $cust ] ); + + return view( 'docstore-customer/dir/list', [ + 'dir' => $dir ?? false, + 'cust' => $cust, + 'dirs' => DocstoreCustomerDirectory::getHierarchyForCustomerAndUserClass( $cust, $r->user()->privs(), false )[ $dir ? $dir->id : '' ] ?? [], + 'files' => DocstoreCustomerFile::getListing( $cust, $r->user(), $dir ), + 'ppp_files' => $cust->patchPanelPorts()->with( 'patchPanelPortFiles' ) + ->has($r->user()->isSuperUser() ? 'patchPanelPortFiles' : 'patchPanelPortFilesPublic' )->get() + ->pluck( 'patchPanelPortFiles' )->isNotEmpty(), + 'ppph_files' => $r->user()->isSuperUser() ? $cust->patchPanelPortHistories() + ->with( 'patchPanelPortHistoryFiles' )->has( 'patchPanelPortHistoryFiles' ) + ->get()->pluck( 'patchPanelPortHistoryFiles' )->isNotEmpty() : false, + ] ); + } + + /** + * Display the list of patch panel file for a customer + * + * @param Request $r + * @param Customer|null $cust + * + * @return View + * + * @throws AuthorizationException + */ + public function listPatchPanelPortFiles( Request $r, Customer $cust = null ) : View + { + $this->authorize( 'listPatchPanelPortFiles', [ DocstoreCustomerDirectory::class, $cust ] ); + + return view( 'docstore-customer/dir/list-ppp-files', [ + 'cust' => $cust, + 'history' => false, + 'files' => $cust->patchPanelPorts()->with( $r->user()->isSuperUser() ? 'patchPanelPortFiles' : 'patchPanelPortFilesPublic' ) + ->has($r->user()->isSuperUser() ? 'patchPanelPortFiles' : 'patchPanelPortFilesPublic' )->get() + ->pluck( 'patchPanelPortFiles' )->flatten(), + ] ); + } + + /** + * Display the list of patch panel file history for a customer + * + * @param Request $r + * @param Customer $cust + * + * @return View + * + * @throws AuthorizationException + */ + public function listPatchPanelPortHistoryFiles( Request $r, Customer $cust ) : View + { + $this->authorize( 'listPatchPanelPortFilesHistory', [ DocstoreCustomerDirectory::class, $cust ]); + + return view( 'docstore-customer/dir/list-ppp-files', [ + 'cust' => $cust, + 'history' => true, + 'files' => $r->user()->isSuperUser() ? $cust->patchPanelPortHistories()->with( 'patchPanelPortHistoryFiles' ) + ->has( 'patchPanelPortHistoryFiles' )->get() + ->pluck( 'patchPanelPortHistoryFiles' )->flatten() : [], + ] ); + } + /** + * Create a new customer directory + * + * @param Request $r + * @param Customer $cust + * + * @return View + * + * @throws AuthorizationException + */ + public function create( Request $r, Customer $cust ): view + { + $this->authorize( 'create', [ DocstoreCustomerDirectory::class, $cust ] ); + + return view( 'docstore-customer/dir/create', [ + 'dir' => false, + 'cust' => $cust, + 'dirs' => DocstoreCustomerDirectory::getListingForDropdown( DocstoreCustomerDirectory::getListing( $cust, $r->user() ) ), + 'parent_dir_id' => $r->input( 'parent_dir_id', false ) + ] ); + } + + /** + * Edit a customer directory + * + * @param Request $r + * @param Customer $cust + * @param DocstoreCustomerDirectory $dir + * + * @return View + * + * @throws AuthorizationException + */ + public function edit( Request $r, Customer $cust, DocstoreCustomerDirectory $dir ): View + { + $this->authorize( 'update', [ DocstoreCustomerDirectory::class, $dir ] ); + + Former::populate([ + 'name' => $r->old( 'name', $dir->name ), + 'description' => $r->old( 'descripton', $dir->description ), + 'parent_dir_id' => $r->old( 'parent_dir_id', $dir->parent_dir_id ?? '' ), + ]); + + return view( 'docstore-customer/dir/create', [ + 'dir' => $dir, + 'cust' => $cust, + 'dirs' => DocstoreCustomerDirectory::getListingForDropdown( DocstoreCustomerDirectory::getListing( $cust, $r->user() ) ), + 'parent_dir_id' => $dir->parent_dir_id + ] ); + } + + /** + * Store a customer directory + * + * @param Request $r + * @param Customer $cust + * + * @return RedirectResponse + * + * @throws AuthorizationException + */ + public function store( Request $r, Customer $cust ): RedirectResponse + { + $this->authorize( 'create', [ DocstoreCustomerDirectory::class, $cust ] ); + + $this->checkForm( $r ); + + $dir = DocstoreCustomerDirectory::create( $r->all() ); + + Log::info( sprintf( "DocStore: new directory [%d|%s] created by %s for the customer [%d|%s]", $dir->id, $dir->name, $r->user()->username, $cust->id, $cust->name ) ); + + AlertContainer::push( "New per-" . config( 'ixp_fe.lang.customer.one' ) . " directory {$r->name} created.", Alert::SUCCESS ); + return redirect( route( 'docstore-c-dir@list', [ 'cust' => $cust,'dir' => $dir->id ] ) ); + } + + /** + * Update a customer directory + * + * @param Request $r + * @param Customer $cust + * @param DocstoreCustomerDirectory $dir + * + * @return RedirectResponse + * + * @throws AuthorizationException + */ + public function update( Request $r , Customer $cust, DocstoreCustomerDirectory $dir ): RedirectResponse + { + $this->authorize( 'update', [ DocstoreCustomerDirectory::class, $dir ] ); + + $this->checkForm( $r ); + + $dir->update( [ 'name' => $r->name, 'description' => $r->description, 'parent_dir_id' => $r->parent_dir_id ] ); + + Log::info( sprintf( "DocStore: customer directory [%d|%s] edited by %s", $dir->id, $dir->name, $r->user()->username ) ); + + AlertContainer::push( "Per-" . config( 'ixp_fe.lang.customer.one' ) . " directory {$r->name} updated.", Alert::SUCCESS ); + return redirect( route( 'docstore-c-dir@list', [ 'cust' => $cust, 'dir' => $dir->parent_dir_id ] ) ); + } + + /** + * Delete a directory + * + * @param Request $r + * @param DocstoreCustomerDirectory $dir + * + * @return RedirectResponse + * + * @throws AuthorizationException + */ + public function delete( Request $r , DocstoreCustomerDirectory $dir ): RedirectResponse + { + $this->authorize( 'delete', $dir ); + + Log::notice( sprintf( "DocStore: start recursive deletion of directory [%d|%s] by %s for the customer [%d|%s]", $dir->id, $dir->name, $r->user()->username, $dir->customer->id, $dir->customer->name ) ); + DocstoreCustomerDirectory::recursiveDelete( $dir ); + Log::notice( sprintf( "DocStore: finish recursive deletion of directory [%d|%s] by %s for the customer [%d|%s]", $dir->id, $dir->name, $r->user()->username, $dir->customer->id, $dir->customer->name ) ); + + AlertContainer::push( ucfirst( config( 'ixp_fe.lang.customer.one' ) ) . "Directory {$dir->name} deleted.", Alert::SUCCESS ); + return redirect( route( 'docstore-c-dir@list', [ 'cust' => $dir->customer , 'dir' => $dir->parent_dir_id ] ) ); + } + + /** + * Delete a directory + * + * @param Customer $cust + * + * @return RedirectResponse + * + * @throws AuthorizationException + */ + public function deleteForCustomer( Customer $cust ): RedirectResponse + { + $this->authorize( 'deleteForCustomer', [ DocstoreCustomerDirectory::class, $cust ] ); + + Log::notice( sprintf( "DocStore: start purge for the customer [%d|%s]", $cust->id, $cust->name ) ); + DocstoreCustomerDirectory::deleteAllForCustomer( $cust ); + Log::notice( sprintf( "DocStore: finish purge for the customer [%d|%s]", $cust->id, $cust->name ) ); + + AlertContainer::push( ucfirst( config( 'ixp_fe.lang.customer.one' ) ) . " {$cust->name} purged.", Alert::SUCCESS ); + return redirect( route( 'docstore-c-dir@customers' ) ); + } + + /** + * Check if the form is valid + * + * @param Request $r + */ + private function checkForm( Request $r ): void + { + $r->validate( [ + 'name' => [ 'required', 'max:100', + function( $attribute, $value, $fail ) { + if( Str::startsWith(strtolower( $value ), 'patch panel port' ) ) { + return $fail( '"Patch Panel Port..." is a reserved name.' ); + } + } + ], + 'cust_id' => [ 'required', 'integer', + function( $attribute, $value, $fail ) use ($r) { + if( !Customer::find( $value ) ) { + Log::notice( "Attempt to create/edit a directory where the customer ID [{$value}] is invalid / does not exist by user ID {$r->user()->id}." ); + AlertContainer::push( ucfirst( config( 'ixp_fe.lang.customer.one' ) ) . ' is invalid / does not exist.', Alert::DANGER ); + return $fail( ucfirst( config( 'ixp_fe.lang.customer.one' ) ) . ' is invalid / does not exist.' ); + } + } + ], + 'description' => 'nullable', + 'parent_dir_id' => [ 'nullable', 'integer', + function( $attribute, $value, $fail ) { + if( !DocstoreCustomerDirectory::find( $value ) ) { + return $fail( 'Parent directory is invalid / does not exist.' ); + } + } + ] + ] ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/DocstoreCustomer/FileController.php b/app/Http/Controllers/DocstoreCustomer/FileController.php new file mode 100644 index 000000000..123191b1b --- /dev/null +++ b/app/Http/Controllers/DocstoreCustomer/FileController.php @@ -0,0 +1,339 @@ + + * @author Yann Robin + * @category DocstoreCustomer + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ + +class FileController extends Controller +{ + /** + * View a docstore customer file apply to allowed mimetype ( DocstoreFile::$ + * + * @param Customer $cust + * @param DocstoreCustomerFile $file + * + * @return mixed + * + * @throws AuthorizationException|FileNotFoundException + */ + public function view( Customer $cust, DocstoreCustomerFile $file ) + { + $this->authorize( 'view', $file ); + + if( !$file->isViewable() ) { + return redirect( route( 'docstore-c-file@download', [ 'cust' => $cust, 'file' => $file->id ] ) ); + } + + return view( 'docstore-customer/file/view', [ + 'file' => $file, + 'cust' => $cust, + 'content' => Storage::disk( $file->disk )->get( $file->path ) + ] ); + } + + /** + * Download a docstore customer file + * + * @param Customer $cust + * @param DocstoreCustomerFile $file + * + * @return mixed + * + * @throws AuthorizationException + */ + public function download( Customer $cust, DocstoreCustomerFile $file ) + { + $this->authorize( 'download', $file ); + + try { + /** @psalm-suppress UndefinedInterfaceMethod */ + return Storage::disk( $file->disk )->download( $file->path, $file->name ); + } catch( FilesystemException $e ) { + AlertContainer::push( "This customer file could not be found / downloaded. Please report this error to the support team.", Alert::DANGER ); + return redirect( route( 'docstore-c-dir@list', [ 'cust' => $file->customer->id , 'dir' => $file->docstore_customer_directory_id ] ) ); + } + } + + /** + * Get information on a docstore customer file + * + * @param DocstoreCustomerFile $file + * + * @return mixed + * + * @throws AuthorizationException + */ + public function info( DocstoreCustomerFile $file ) + { + $this->authorize( 'info', $file ); + + return view( 'docstore-customer/file/info', [ + 'file' => $file, + 'size' => Storage::disk( $file->disk )->size( $file->path ), + 'last_modified' => Storage::disk( $file->disk )->lastModified( $file->path ), + 'dspath' => config( 'filesystems.disks.' . $file->disk . '.root', '*** UNKNOWN LOCATION ***' ) . '/' . $file->path, + 'created_by' => User::find( $file->created_by ), + 'created_at' => $file->created_at, + ]); + } + + /** + * Upload a new docstore customer file + * + * @param Request $r + * @param Customer $cust + * + * @return View + * + * @throws AuthorizationException + */ + public function upload( Request $r, Customer $cust ): view + { + $this->authorize( 'create', DocstoreCustomerFile::class ); + + Former::populate([ + 'min_privs' => $r->old( 'min_privs', (string) User::AUTH_SUPERUSER ) + ]); + + return view( 'docstore-customer/file/upload', [ + 'file' => false, + 'cust' => $cust, + 'dirs' => DocstoreCustomerDirectory::getListingForDropdown( DocstoreCustomerDirectory::getListing( $cust, $r->user() ) ), + ] ); + } + + /** + * Store a docstore customer file uploaded + * + * @param Request $r + * @param Customer $cust + * + * @return RedirectResponse + * + * @throws AuthorizationException + */ + public function store( Request $r, Customer $cust ): RedirectResponse + { + $this->authorize( 'create', DocstoreCustomerFile::class ); + + $this->checkForm( $r ); + + $uploadedFile = $r->file('uploadedFile' ); + + $path = $uploadedFile->store( (string) $cust->id, 'docstore_customers' ); + + /** @psalm-suppress InvalidArgument */ + $file = DocstoreCustomerFile::create( [ + 'name' => $r->name, + 'description' => $r->description, + 'cust_id' => $cust->id, + 'min_privs' => $r->min_privs, + 'path' => $path, + 'sha256' => hash_file( 'sha256', $uploadedFile ), + 'created_by' => $r->user()->id, + 'file_last_updated' => now(), + 'docstore_customer_directory_id' => $r->docstore_customer_directory_id, + ] ); + + Log::info( sprintf( "DocStore: file [%d|%s] uploaded by %s for the customer [%d|%s]", $file->id, $file->name, $r->user()->username, $cust->id, $cust->name ) ); + + AlertContainer::push( ucfirst( config( 'ixp_fe.lang.customer.one' ) ) . " File {$r->name} uploaded.", Alert::SUCCESS ); + return redirect( route( 'docstore-c-dir@list', [ 'cust' => $cust , 'dir' => $file->docstore_customer_directory_id ] ) ); + } + + /** + * Edit a docstore customer file uploaded + * + * @param Request $r + * @param Customer $cust + * @param DocstoreCustomerFile $file + * + * @return View + * + * @throws AuthorizationException + */ + public function edit( Request $r, Customer $cust, DocstoreCustomerFile $file ): View + { + $this->authorize( 'update', $file ); + + Former::populate([ + 'name' => $r->old( 'name', $file->name ), + 'description' => $r->old( 'descripton', $file->description ), + 'sha256' => $r->old( 'sha256', $file->sha256 ), + 'min_privs' => $r->old( 'min_privs', (string) $file->min_privs ), + 'docstore_customer_directory_id' => $r->old( 'docstore_customer_directory_id',$file->docstore_customer_directory_id ?? '' ), + ]); + + return view( 'docstore-customer/file/upload', [ + 'file' => $file, + 'cust' => $cust, + 'dirs' => DocstoreCustomerDirectory::getListingForDropdown( DocstoreCustomerDirectory::getListing( $file->customer, $r->user() ) ) + ] ); + } + + /** + * Update a docstore customer file uploaded + * + * @param Request $r + * @param Customer $cust + * @param DocstoreCustomerFile $file + * + * @return RedirectResponse + * + * @throws AuthorizationException + */ + public function update( Request $r, Customer $cust, DocstoreCustomerFile $file ): RedirectResponse + { + $this->authorize( 'update', $file ); + + $this->checkForm( $r, $file ); + + // if a new file is updated + if( $r->uploadedFile ) { + // get path of the old file in order to delete it later + $oldPath = $file->path; + + $uploadedFile = $r->file('uploadedFile'); + $path = $uploadedFile->store( $file->customer->id, 'docstore_customers' ); + + /** @psalm-suppress InvalidArgument */ + $file->update([ + 'path' => $path, + 'sha256' => hash_file( 'sha256', $uploadedFile ), + 'file_last_updated' => now(), + ]); + + // Delete the old file + Storage::disk( $file->disk )->delete( $oldPath ); + } + + $file->update( [ + 'name' => $r->name, + 'description' => $r->description, + 'docstore_customer_directory_id' => $r->docstore_customer_directory_id, + 'min_privs' => $r->min_privs + ] ); + + + Log::info( sprintf( "DocStore: customer file [%d|%s] edited by %s for the customer [%d|%s]", $file->id, $file->name, $r->user()->username, $cust->id, $cust->name ) ); + + AlertContainer::push( ucfirst( config( 'ixp_fe.lang.customer.one' ) ) . " file {$r->name} updated.", Alert::SUCCESS ); + return redirect( route( 'docstore-c-dir@list', [ 'cust' => $cust , 'dir' => $file->docstore_customer_directory_id ] ) ); + } + + /** + * Delete a file + * + * @param Request $r + * @param DocstoreCustomerFile $file + * + * @return RedirectResponse + * + * @throws AuthorizationException + */ + public function delete( Request $r , DocstoreCustomerFile $file ): RedirectResponse + { + $this->authorize( 'delete', $file ); + + $dir = $file->directory; + $cust = $file->customer; + + Storage::disk( $file->disk )->delete( $file->path ); + + AlertContainer::push( ucfirst( config( 'ixp_fe.lang.customer.one' ) ) . " file {$file->name} deleted.", Alert::SUCCESS ); + Log::info( sprintf( "DocStore: customer file [%d|%s] deleted by %s for the customer [%d|%s]", $file->id, $file->name, $r->user()->username, $cust->id, $cust->name ) ); + + $file->delete(); + + return redirect( route( 'docstore-c-dir@list', [ 'cust' => $cust, 'dir' => $dir ] ) ); + } + + /** + * Check if the form is valid + * + * @param Request $r + * @param DocstoreCustomerFile|null $file + * + * @return void + */ + private function checkForm( Request $r, ?DocstoreCustomerFile $file = null ): void + { + $r->validate( [ + 'name' => 'required|max:100', + 'uploadedFile' => Rule::requiredIf( function() use ( $r, $file ) { + return !$file; + }), + 'sha256' => [ 'nullable', 'max:64', + function ( $attribute, $value, $fail ) use( $r ) { + /** @psalm-suppress InvalidArgument */ + if( $value && $r->file('uploadedFile' ) && $value !== hash_file( 'sha256', $r->file( 'uploadedFile' ) ) ) { + return $fail( 'The sha256 checksum calculated on the server does not match the one you provided.' ); + } + return null; + }, + ], + 'min_privs' => 'required|integer|in:' . implode( ',', array_keys( User::$PRIVILEGES ) ), + 'docstore_customer_directory_id' => 'nullable|integer|exists:docstore_customer_directories,id', + ] ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/FilteredPrefixesController.php b/app/Http/Controllers/FilteredPrefixesController.php new file mode 100644 index 000000000..0c3938b77 --- /dev/null +++ b/app/Http/Controllers/FilteredPrefixesController.php @@ -0,0 +1,87 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class FilteredPrefixesController extends Controller +{ + /** + * Get the list + * + * @param Request $r + * @param Customer $cust + * + * @return View + * + * @throws + */ + public function list( Request $r, Customer $cust ): View + { + /** @var User $us */ + $us = Auth::getUser(); + + $this->authorize('view', $cust); + + // are we busting the cache? + if( $r->reset_cache === "1" && $us->isSuperUser() ) { + Cache::forget('filtered-prefixes-' . $cust->id ); + } + + // get the prefixes + $filteredPrefixes = Cache::get( 'filtered-prefixes-' . $cust->id, false ); + + if( $filteredPrefixes === false ) { + // no cached result so schedule a job to gather them: + FetchFilteredPrefixesForCustomer::dispatchAfterResponse( $cust ); + + // if we are using the sync queue runner, it will have completed + $filteredPrefixes = Cache::get( 'filtered-prefixes-' . $cust->id, false ); + } + + return view( 'filtered-prefixes.view' )->with([ + 'cust' => $cust, + 'filteredPrefixes' => $filteredPrefixes, + ]); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/InfrastructureController.php b/app/Http/Controllers/InfrastructureController.php new file mode 100644 index 000000000..69cc288be --- /dev/null +++ b/app/Http/Controllers/InfrastructureController.php @@ -0,0 +1,281 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class InfrastructureController extends Eloquent2Frontend +{ + /** + * The object being created / edited + * + * @var Infrastructure + */ + protected $object = null; + + /** + * This function sets up the frontend controller + */ + #[\Override] + public function feInit(): void + { + $this->feParams = (object)[ + 'model' => Infrastructure::class, + 'pagetitle' => 'Infrastructures', + 'titleSingular' => 'Infrastructure', + 'nameSingular' => 'infrastructure', + 'listOrderBy' => 'name', + 'listOrderByDir' => 'ASC', + 'viewFolderName' => 'infrastructure', + 'listColumns' => [ + 'name' => 'Name', + 'shortname' => 'Shortname', + 'isPrimary' => [ + 'title' => 'Primary', + 'type' => self::$FE_COL_TYPES[ 'YES_NO' ] + ], + 'ixf_ix_id' => [ + 'title' => 'IXF-ID', + 'type' => self::$FE_COL_TYPES[ 'REPLACE' ], + 'subject' => '%%COL%%', + ], + 'peeringdb_ix_id' => [ + 'title' => 'PeeringDB ID', + 'type' => self::$FE_COL_TYPES[ 'REPLACE' ], + 'subject' => '%%COL%%', + ], + ], + ]; + + // display the same information in the view as the list + $this->feParams->viewColumns = array_merge( + $this->feParams->listColumns, [ + 'country' => [ + 'title' => 'Country', + 'type' => self::$FE_COL_TYPES[ 'COUNTRY' ] + ], + 'notes' => [ + 'title' => 'Notes', + 'type' => self::$FE_COL_TYPES[ 'PARSDOWN' ] + ], + 'created_at' => [ + 'title' => 'Created', + 'type' => self::$FE_COL_TYPES[ 'DATETIME' ] + ], + 'updated_at' => [ + 'title' => 'Updated', + 'type' => self::$FE_COL_TYPES[ 'DATETIME' ] + ] + ] + ); + } + + /** + * Provide array of rows for the list action and view action + * + * @param int|null $id The `id` of the row to load for `view` action`. `null` if `listAction` + + * @return array + */ + #[\Override] + protected function listGetData( ?int $id = null ): array + { + $feParams = $this->feParams; + return Infrastructure::when( $id , static function( Builder $q, $id ) { + return $q->where('id', $id ); + } )->when( $feParams->listOrderBy , static function( Builder $q, $orderby ) use ( $feParams ) { + return $q->orderBy( $orderby, $feParams->listOrderByDir ?? 'ASC'); + })->get()->toArray(); + } + + /** + * Display the form to create an object + * + * @return (Infrastructure|array)[] + * + * @psalm-return array{object: Infrastructure, countries: array} + */ + #[\Override] + protected function createPrepareForm(): array + { + return [ + 'object' => $this->object, + 'countries' => Countries::getList('name' ) + ]; + } + + /** + * Display the form to add/edit an object + * + * @param int $id ID of the row to edit + * + * @return (array|mixed)[] + * + * @psalm-return array{object: mixed, countries: array} + */ + #[\Override] + protected function editPrepareForm( int $id ): array + { + $this->object = Infrastructure::findOrFail( $id ); + + Former::populate([ + 'name' => request()->old( 'name', $this->object->name ), + 'shortname' => request()->old( 'shortname', $this->object->shortname ), + 'isPrimary' => request()->old( 'isPrimary', $this->object->isPrimary ), + 'country' => request()->old( 'country', in_array( $this->object->country, array_values( Countries::getListForSelect( 'iso_3166_2' ) ), false ) ? $this->object->country : null ), + 'notes' => request()->old( 'notes', $this->object->notes ), + ]); + + return [ + 'object' => $this->object, + 'countries' => Countries::getList('name' ) + ]; + } + + /** + * Function to do the actual validation and storing of the submitted object. + * + * @param Request $r + * + * @return true + * + * @throws + */ + #[\Override] + public function doStore( Request $r ): bool|RedirectResponse + { + $this->checkForm( $r ); + $this->object = Infrastructure::create( $r->all() ); + $this->resetInfrastructures(); + return true; + } + + /** + * Function to do the actual validation and storing of the submitted object. + * + * @param Request $r + * @param int $id + * + * @return true + * + * @throws + */ + #[\Override] + public function doUpdate( Request $r, int $id ): bool|RedirectResponse + { + $this->object = Infrastructure::findOrFail( $id ); + $this->checkForm( $r ); + $this->object->update( $r->all() ); + $this->resetInfrastructures(); + + return true; + } + + /** + * Function that reset the other infrastructures (isPrimary = false) + * + * @return void + */ + private function resetInfrastructures(): void + { + if( $this->object->isPrimary ) { + // reset the rest: + foreach( Infrastructure::where( 'id', '!=', $this->object->id ) + ->where( 'isPrimary', true )->get() as $infra ) { + $infra->isPrimary = false; + $infra->save(); + } + } + } + + /** + * @inheritdoc + */ + #[\Override] + protected function preDelete(): bool + { + $okay = true; + if( ( $cnt = $this->object->switchers()->count() ) ) { + AlertContainer::push( "You cannot delete this infrastructure there are {$cnt} switch(es) associated with it. " + . "You can view and then reassign or delete those switches $this->object->id ] ) + . "\">by clicking here.", Alert::DANGER + ); + $okay = false; + } + + if( $cnt = $this->object->vlans()->count() ) { + AlertContainer::push( "You cannot delete this infrastructure there are {$cnt} VLAN(s) associated with it. " + . "You can view and then reassign or delete those VLANs $this->object->id ] ) + . "\">by clicking here.", Alert::DANGER + ); + $okay = false; + } + + return $okay; + } + + /** + * Check if the form is valid + * + * @param Request $r + */ + #[\Override] + public function checkForm( Request $r ): void + { + $r->validate( [ + 'name' => 'required|string|max:255|unique:infrastructure,name' . ( $r->id ? ','. $r->id : '' ), + 'shortname' => 'required|string|max:255', + 'country' => 'required|string|max:2|in:' . implode( ',', array_values( Countries::getListForSelect( 'iso_3166_2' ) ) ), + ] ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Interfaces/Common.php b/app/Http/Controllers/Interfaces/Common.php new file mode 100644 index 000000000..063a9fdf8 --- /dev/null +++ b/app/Http/Controllers/Interfaces/Common.php @@ -0,0 +1,434 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers\Interfaces + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +abstract class Common extends Controller +{ + /** + * Removes related interface + * + * Removes a related interface and if it only has one physical interface, removes the virtual interface also + * + * @param PhysicalInterface $pi Physical interface to remove related physical interface. + * + * @return void + * + * @throws + */ + public function removeRelatedInterface( PhysicalInterface $pi ): void + { + if( $related = $pi->relatedInterface() ) { + if( $pi->relatedInterface()->virtualInterface->physicalInterfaces()->count() === 1 ) { + $pi->relatedInterface()->virtualInterface->macAddresses()->delete(); + + foreach( $pi->relatedInterface()->virtualInterface->vlanInterfaces as $vli ) { + $vli->layer2addresses()->delete(); + $vli->delete(); + } + + $vi = $pi->relatedInterface()->virtualInterface(); + $pi->relatedInterface()->update( [ 'virtualinterfaceid' => null ] ); + $vi->delete(); + } + + $pi->relatedInterface()->update( [ 'switchportid' => null ] ); + $pi->fanout_physical_interface_id = null; + $pi->save(); + + $related->delete(); + } + } + + /** + * Links peering and fanout physical interfaces. + * + * If *link with fanout port* is checked in the form, then this function: + * + * * checks if the necessary fields are set; + * * loads the selected SwitchPort; + * * creates/loads this SwitchPort's physical interface; + * * creates a link between the fanout and peering physical interfaces. + * + * If *link with fanout port* is not checked then this function checks + * if the peering port has a related interface and, if so, removes the relation. + * + * @param Request|StoreVirtualInterfaceWizard $r instance of the current HTTP request + * @param PhysicalInterface $pi Peering physical interface to related with fanout physical interface (port). + * @param VirtualInterface $vi Virtual interface of peering physical interface + * + * @return bool + * + * @throws + */ + public function processFanoutPhysicalInterface( \IXP\Http\Requests\StorePhysicalInterface $r, PhysicalInterface $pi, VirtualInterface $vi ): bool + { + if( !$r->fanout ) { + $this->removeRelatedInterface( $pi ); + return true; + } + + $fnsp = SwitchPort::findOrFail( $r->input( 'switch-port-fanout' ) ); + $fnsp->update( [ 'type' => SwitchPort::TYPE_FANOUT ] ); + + // if switch port does not have a physical interface then create one + if( !$fnsp->physicalInterface ) { + $fnpi = new PhysicalInterface; + $fnpi->switchportid = $fnsp->id; + $fnpi->save(); + } else { + $fnpi = $fnsp->physicalInterface; + + // check if the fanout port has a physical interface and if the physical interface is different of the current physical interface + if( $fnsp->physicalInterface->relatedInterface() && $fnsp->physicalInterface->relatedInterface()->id !== $pi->id ) { + AlertContainer::push( "Missing bundle name not assigned as no bundle name set for this switch vendor (see Vendors)", Alert::WARNING ); + return false; + } + } + + $relatedInterface = $pi->relatedInterface(); + + // if the physical interface already has a related physical interface and it's not the same as the fanout physical interface + /** @psalm-suppress InvalidPropertyFetch */ + if( $relatedInterface !== false && $relatedInterface->id !== $fnpi->id ) { + // if fanout does not have a virtual interface, relate it with old fanout port virtual interface. + if( !$fnpi->virtualInterface ) { + /** @psalm-suppress InvalidPropertyFetch */ + $fnpi->virtualinterfaceid = $relatedInterface->virtualinterfaceid; + } + + $this->removeRelatedInterface( $pi ); + + } else if( !$fnpi->virtualInterface ) { + // create virtual interface for fanout physical interface if doesn't have one + $fnvi = new VirtualInterface; + $fnvi->custid = $vi->customer->reseller; + $fnvi->save(); + $fnpi->virtualinterfaceid = $fnvi->id; + } + + $pi->fanout_physical_interface_id = $fnpi->id; + $pi->save(); + + $fnpi->speed = $pi->speed; + $fnpi->status = $pi->status; + $fnpi->duplex = $pi->duplex; + $fnpi->save(); + + return true; + } + + /** + * When we have >1 phys int / LAG framing, we need to set other elements of the virtual interface appropriately: + * + * @param VirtualInterface $vi + * + * @throws + */ + public function setBundleDetails( VirtualInterface $vi ): void + { + if( $vi->physicalInterfaces()->count() ) { + // LAGs must have a channel group and bundle name. But only if they have a phys int: + if( $vi->lag_framing && !$vi->channelgroup ) { + $vi->channelgroup = $this->assignChannelGroup( $vi ); + //$vi->save(); + AlertContainer::push( "Missing channel group assigned as this is a LAG port", Alert::INFO ); + } + + // LAGs must have a bundle name + if( $vi->lag_framing && !$vi->name ) { + // assumption on no mlags (multi chassis lags) here: + if( $vendor = $vi->physicalInterfaces()->first()->switchport->switcher->vendor ) { + $vi->name = $vendor->bundle_name; + //$vi->save(); + AlertContainer::push( "Missing bundle name assigned as this is a LAG port", Alert::INFO ); + } else { + AlertContainer::push( "Missing bundle name not assigned as no bundle name set for this switch vendor (see Vendors)", Alert::WARNING ); + } + } + } + else{ + // we don't allow setting channel group or name until there's >= 1 physical interface / LAG framing: + $vi->name = ''; + $vi->channelgroup = null; + $vi->lag_framing = false; + $vi->fastlacp = false; + //$vi->save(); + } + } + + /** + * For the given $vi, assign a unique channel group + * + * @param VirtualInterface $vi + * + * @return int + * + * @throws + * + * @psalm-return int<1, 999> + */ + public function assignChannelGroup( VirtualInterface $vi ): int + { + if( $vi->physicalInterfaces()->count() === 0 ) { + throw new GeneralException("Channel group number is only relevant when there is at least one physical interface"); + } + + $usedChannelGroups = VirtualInterface::select( [ 'vi.channelgroup' ] ) + ->from( 'virtualinterface AS vi' ) + ->leftJoin( 'physicalinterface AS pi', 'pi.virtualinterfaceid', 'vi.id' ) + ->leftJoin( 'switchport as sp', 'sp.id', 'pi.switchportid' ) + ->whereNotNull( 'vi.channelgroup' ) + ->whereIn( 'sp.switchid', function( $query ) use( $vi ) { + $query->select( [ 's.id' ] ) + ->from( 'switch AS s' ) + ->leftJoin( 'switchport AS sp', 'sp.switchid', 's.id') + ->leftJoin( 'physicalinterface AS pi', 'pi.switchportid', 'sp.id' ) + ->where( 'pi.virtualinterfaceid', $vi->id ); + })->distinct()->get()->pluck('channelgroup')->toArray(); + + + $orig = $vi->channelgroup; + for( $i = 1; $i < 1000; $i++ ) { + if( !in_array( $i, $usedChannelGroups, true ) ) { + $vi->channelgroup = $i; + return $i; + } + } + + $vi->channelgroup = $orig; + throw new GeneralException("Could not assign a free channel group number"); + } + + /** + * Sets IPv4 or IPv6 from form to given VlanInterface from request data. + * + * Function checks if IPvX address is provided if IPvX is enabled. Then + * it checks if given IPvX address exists for current Vlan: + * + * NB: form sanity checking must be performed BEFORE calling this function + * + * * if it exists, it ensures is is not assigned to another interface; + * * if !exists, creates a new one. + * + * @param Request $r + * @param Vlan $v + * @param VlanInterface $vli Vlan interface to assign IP to + * @param bool $ipv6 Bool to define if IP address is IPv4 or IPv6 + * + * @return bool + * + * @throws + */ + public function setIp( Request $r, Vlan $v, VlanInterface $vli, bool $ipv6 = false ): bool + { + $iptype = $ipv6 ? "ipv6" : "ipv4"; + $setterIPv = "{$iptype}addressid"; + $setterEnabled = "{$iptype}enabled"; + $setterHostname = "{$iptype}hostname"; + $setterSecret = "{$iptype}bgpmd5secret"; + $setterPing = "{$iptype}canping"; + $setterMonitor = "{$iptype}monitorrcbgp"; + + /** @var IPv4Address|IPv6Address $model */ + $model = $ipv6 ? IPv6Address::class : IPv4Address::class; + + $addressValue = $r->input( $iptype . 'address' ); + + if( trim( $addressValue ) ) { + if( !( $ip = $model::where( 'vlanid', $v->id)->where( 'address', $addressValue )->first() ) ) { + $ip = new $model; + $ip->vlanid = $v->id; + $ip->address = $addressValue; + $ip->save(); + } else if( $ip->vlanInterface && $ip->vlanInterface->id !== $vli->id ) { + AlertContainer::push( ucfirst( $iptype ) . " address {$addressValue} is already in use by another VLAN interface on the same VLAN.", Alert::DANGER ); + return false; + } + + $vli->$setterIPv = $ip->id; + + } else { + $vli->$setterIPv = null; + } + + $vli->$setterHostname = $r->input( $iptype . 'hostname' ); + $vli->$setterEnabled = $r->input( $iptype . 'enabled' ); + $vli->$setterSecret = $r->input( $iptype . 'bgpmd5secret' ); + $vli->$setterPing = $r->input( $iptype . 'canping' ); + $vli->$setterMonitor = $r->input( $iptype . 'monitorrcbgp' ); + return true; + } + + + /** + * @param VlanInterface $vli + * + * @return void + */ + public function warnIfIrrdbFilteringButNoIrrdbSourceSet( VlanInterface $vli ): void + { + if( $vli->rsclient && $vli->irrdbfilter && !$vli->virtualInterface->customer->irrdb ) { + AlertContainer::push( "You have enabled IRRDB filtering for this VLAN interface's route server sessions. " + . "However, the customer does not have an IRRDB source set. As such, the route servers will block all prefix " + . "advertisements. To rectify this, edit the customer and set an IRRDB source.", Alert::WARNING ); + } + } + + + /** + * Build everything that a Core Bundle need (core link, core Interface etc) + * + * @param CoreBundle $coreBundle Core bundle object + * @param Request $request instance of the current HTTP request + * @param array $virtualInterfaces array of the Virtual interfaces ( side A and B ) linked to the core bundle + * @param bool $edit Are we editing the core bundle ? + * + * @return RedirectResponse|true + * + * @throws + */ + public function buildCorelink( CoreBundle $coreBundle, Request $request, array $virtualInterfaces, bool $edit ): RedirectResponse|true + { + foreach( $request->input( "cl-details" ) as $coreLinkDetail ) { + $coreLink = new CoreLink; + + $coreLink->core_bundle_id = $coreBundle->id; + $coreLink->enabled = $coreLinkDetail[ 'enabled-cl' ] ?? false; + + $bfd = $coreLinkDetail[ 'bfd' ] ?? false; + $type = $edit ? $coreBundle->type : $request->type; + + $coreLink->bfd = (int)$type === CoreBundle::TYPE_ECMP ? $bfd : false; + $coreLink->ipv4_subnet = $coreLinkDetail[ 'subnet' ] ?? null; + + $switchPort = []; + $physicalInterface = []; + $coreInterface = []; + foreach( $virtualInterfaces as $side => $virtualInterface ) { + $switchPort[$side] = SwitchPort::find( $coreLinkDetail[ "hidden-sp-$side" ] ); + if( !$switchPort[$side] ) { + return Redirect::back()->withInput( $request->all() ); + } + + $switchPort[$side]->type = SwitchPort::TYPE_CORE; + $switchPort[$side]->save(); + + // Creating $physicalInterface A/B + $physicalInterface[$side] = new PhysicalInterface; + $physicalInterface[$side]->switchportid = $switchPort[$side]->id; + $physicalInterface[$side]->virtualinterfaceid = $virtualInterface->id; + $physicalInterface[$side]->speed = $edit ? $coreBundle->speedPi() : $request->speed; + $physicalInterface[$side]->duplex = $edit ? $coreBundle->duplexPi() : $request->duplex; + $physicalInterface[$side]->autoneg = $edit ? $coreBundle->autoNegPi() : $request->input('auto-neg' ) ?? false; + $physicalInterface[$side]->status = PhysicalInterface::STATUS_CONNECTED; + $physicalInterface[$side]->save(); + + // Creating $coreInterface A/B + $coreInterface[$side] = new CoreInterface; + $coreInterface[$side]->physical_interface_id = $physicalInterface[$side]->id; + $coreInterface[$side]->save(); + } + + $coreLink->core_interface_sidea_id = $coreInterface['a']->id; + $coreLink->core_interface_sideb_id = $coreInterface['b']->id; + + $coreLink->save(); + } + return true; + } + + /** + * Delete the physical interface and everything related + * + * @param Request $r + * @param PhysicalInterface $pi + * @param bool $setBunleDetails + * + * @throws Exception + */ + protected function deletePi( Request $r, PhysicalInterface $pi, bool $setBunleDetails = false ): void + { + $pi2 = clone $pi; + if( $pi->switchPort->typePeering() && $pi->fanoutPhysicalInterface ) { + $pi->update( [ 'switchportid' => null ] ); + $pi->fanoutPhysicalInterface->switchPort->update( [ 'type' => SwitchPort::TYPE_PEERING ] ); + } else if( $pi->switchPort->typeFanout() && $pi->peeringPhysicalInterface ) { + if( (bool)$r->related ){ + $this->removeRelatedInterface( $pi2 ); + } + + $pi->peeringPhysicalInterface->fanout_physical_interface_id = null; + $pi->peeringPhysicalInterface->save(); + } + if( (bool)$r->related && $pi2->relatedInterface() ) { + $this->removeRelatedInterface( $pi2 ); + } + + if( $setBunleDetails ){ + $this->setBundleDetails( $pi->virtualInterface ); + } + + $pi->delete(); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Interfaces/CoreBundleController.php b/app/Http/Controllers/Interfaces/CoreBundleController.php new file mode 100644 index 000000000..b9e630a85 --- /dev/null +++ b/app/Http/Controllers/Interfaces/CoreBundleController.php @@ -0,0 +1,228 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers\Interfaces + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class CoreBundleController extends Common +{ + /** + * Display the core bundles list + * + * @return View + */ + public function list(): View + { + return view( 'interfaces/core-bundle/list' )->with([ + 'cbs' => CoreBundle::with( 'coreLinks' )->get() + ]); + } + + /** + * Display the form to create a core bundle wizard + * + * @return View + */ + public function createWizard(): View + { + return view( 'interfaces/core-bundle/create/form-wizard' )->with([ + 'switches' => Switcher::select( [ 'id', 'name' ] ) + ->orderBy( 'name' )->get(), + 'customers' => Customer::internal()->get(), + ]); + } + + /** + * Create a core bundle + * + * @param Store $r instance of the current HTTP request + * + * @return RedirectResponse + * + * @throws + */ + public function storeWizard( Store $r ): RedirectResponse + { + $cb = CoreBundle::create( $r->all() ); + + $via = new VirtualInterface; + $vib = new VirtualInterface; + + // Set values to the Virtual Interface side A and B + foreach( [ 'a' => $via , 'b' => $vib ] as $side => $vi ){ + $vi->custid = $r->custid; + $vi->mtu = $r->mtu; + $vi->trunk = $r->framing ?? false; + $vi->fastlacp = $r->input( 'fast-lacp' ) ?? false; + + if( (int)$r->type === CoreBundle::TYPE_L2_LAG ) { + $vi->lag_framing = true; + } + + if( (int)$r->type !== CoreBundle::TYPE_ECMP ) { + $r->merge( [ "vi-name-$side" => trim( $r->input( "vi-name-$side" ) , '"') ] ); + $vi->name = $r->input( "vi-name-$side" ); + $vi->channelgroup = $r->input( "vi-channel-number-$side" ); + } + + $vi->save(); + } + + // Creating all the elements linked to the new core bundle (core links, core interfaces, physical interfaces) + $this->buildCorelink( $cb, $r, [ 'a' => $via , 'b' => $vib ] , false ); + + Log::notice( $r->user()->username . ' created a core bundle with (id: ' . $cb->id . ')' ); + AlertContainer::push( 'Core bundle created', Alert::SUCCESS ); + return Redirect::to( route( "core-bundle@list" ) ); + } + + /** + * Display the form to edit a core bundle + * + * @param Request $r Instance of the current HTTP request + * @param CoreBundle $cb Core bundle + * + * @return View + */ + public function edit( Request $r, CoreBundle $cb ): View + { + $customer = $cb->customer(); + /** @psalm-suppress InvalidPropertyFetch */ + $customerId = !$customer ? 0 : $customer->id; + + // fill the form with the core bundle data + Former::populate( [ + 'custid' => $r->old( 'custid', (string)$customerId ), + 'description' => $r->old( 'description', $cb->description ), + 'graph_title' => $r->old( 'graph_title', $cb->graph_title ), + 'cost' => $r->old( 'cost', (string)$cb->cost ), + 'preference' => $r->old( 'preference', (string)$cb->preference ), + 'type' => $r->old( 'type', (string)$cb->type ), + 'ipv4_subnet' => $r->old( 'ipv4_subnet', $cb->ipv4_subnet ), + 'enabled' => $r->old( 'enabled', (string)$cb->enabled ), + 'bfd' => $r->old( 'bfd', (string)$cb->bfd ), + 'stp' => $r->old( 'stp', (string)$cb->stp ), + ] ); + + $switchSideA = $cb->switchSideX(); + /** @psalm-suppress InvalidPropertyFetch */ + $switchSideAId = $switchSideA ? $switchSideA->id : null; + $switchSideB = $cb->switchSideX( false ); + /** @psalm-suppress InvalidPropertyFetch */ + $switchSideBId = $switchSideB ? $switchSideB->id : null; + + return view( 'interfaces/core-bundle/edit/edit-wizard' )->with( [ + 'cb' => $cb, + 'customers' => Customer::internal()->get(), + 'switchPortsSideA' => SwitcherAggregator::allPorts( $switchSideAId, [ SwitchPort::TYPE_CORE, SwitchPort::TYPE_UNSET ], notAssignToPI: true ), + 'switchPortsSideB' => SwitcherAggregator::allPorts( $switchSideBId, [ SwitchPort::TYPE_CORE, SwitchPort::TYPE_UNSET ], notAssignToPI: true ), + ] ); + } + + /** + * Edit core bundle + * + * @param Store $r instance of the current HTTP request + * @param CoreBundle $cb + * + * @return RedirectResponse + * + * @throws + */ + public function updateWizard( Store $r, CoreBundle $cb): RedirectResponse + { + // Getting the virtual inferfaces (side A/B) + $vis = $cb->virtualInterfaces(); + + // Set the customer to the Virtual interface for each side + $vis[ 'a' ]->custid = $r->custid; + $vis[ 'a' ]->save(); + $vis[ 'b' ]->custid = $r->custid; + $vis[ 'b' ]->save(); + + $cb->update( $r->all() ); + + Log::notice( $r->user()->username . ' updated a core bundle with (id: ' . $cb->id . ')' ); + AlertContainer::push( 'Core bundle updated.', Alert::SUCCESS ); + return redirect( route( 'core-bundle@list' ) ); + } + + /** + * Delete the core bundle and everything associated with + * + ** Delete the core links + ** Delete the core interfaces + ** Delete the physical interfaces + ** Delete the core Virtual Interfaces + ** Change the status of the switch ports to UNSET + * + * @param Request $r + * @param CoreBundle $cb + * + * @return RedirectResponse + * + * @throws Exception + */ + public function delete( Request $r, CoreBundle $cb ): RedirectResponse + { + $cb->deleteObject(); + Log::notice( $r->user()->username." deleted a core bundle (id: " . $cb->id . ')' ); + AlertContainer::push( 'Core bundle deleted.', Alert::SUCCESS ); + return redirect( route( "core-bundle@list" ) ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Interfaces/CoreLinkController.php b/app/Http/Controllers/Interfaces/CoreLinkController.php new file mode 100644 index 000000000..91d46a37f --- /dev/null +++ b/app/Http/Controllers/Interfaces/CoreLinkController.php @@ -0,0 +1,132 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers\Interfaces + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class CoreLinkController extends Common +{ + /** + * Add a core link to a core bundle only when EDITING a core bundle + * + * @param StoreCoreLink $r instance of the current HTTP request + * @param CoreBundle $cb + * + * @return RedirectResponse + * + */ + public function store( StoreCoreLink $r, CoreBundle $cb ): RedirectResponse + { + // Creating all the elements linked to the new core link (core interfaces, physical interfaces) + $this->buildCorelink( $cb, $r, $cb->virtualInterfaces(), true ); + + Log::notice( $r->user()->username . ' created a core link for the core bundle with (id: ' . $cb->id . ')' ); + AlertContainer::push( 'Core link created.', Alert::SUCCESS ); + return redirect( route( "core-bundle@edit" , [ "cb" => $cb->id ] ) ); + } + + /** + * Update the core links (enabled/BFD/Subnet) associated to a core bundle + * + * @param Request $r instance of the current HTTP request + * @param CoreBundle $cb + * + * @return RedirectResponse + */ + public function update( Request $r, CoreBundle $cb ): RedirectResponse + { + foreach( $cb->corelinks as $cl ){ + $cl->enabled = $r->input( 'enabled-' . $cl->id ) ?? false; + + if( $cb->typeECMP() ){ + $cl->bfd = $r->input( 'bfd-' . $cl->id ) ?? false; + $cl->ipv4_subnet = $r->input( 'subnet-' . $cl->id ); + } + $cl->save(); + } + + Log::notice( $r->user()->username . ' updated the core links from the core bundle with (id: ' . $cb->id . ')' ); + AlertContainer::push( 'Core links updated.', Alert::SUCCESS ); + return redirect( route( "core-bundle@edit", [ "cb" => $cb->id ] ) ); + } + + /** + * Delete a Core link + * + * Delete the associated core interface/ physical interface + * Change the type of the switch ports to UNSET + * + * @param Request $r + * @param CoreBundle $cb + * @param CoreLink $cl + * + * @return RedirectResponse + * + * @throws Exception + */ + public function delete( Request $r, CoreBundle $cb, CoreLink $cl ) : RedirectResponse + { + $cl->delete(); + foreach( $cl->coreInterfaces() as $ci ) { + /** @var CoreInterface $ci */ + $pi = $ci->physicalInterface; + $pi->switchPort->update( [ 'type' => SwitchPort::TYPE_UNSET ] ); + + $ci->delete(); + $pi->delete(); + } + + Log::notice( $r->user()->username." deleted a core link (id: " . $cl->id . ')' ); + AlertContainer::push( 'Core link deleted.', Alert::SUCCESS ); + return redirect( route( "core-bundle@edit", [ "cb" => $cb->id ] ) ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Interfaces/PhysicalInterfaceController.php b/app/Http/Controllers/Interfaces/PhysicalInterfaceController.php new file mode 100644 index 000000000..ef48502e5 --- /dev/null +++ b/app/Http/Controllers/Interfaces/PhysicalInterfaceController.php @@ -0,0 +1,393 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers\Interfaces + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class PhysicalInterfaceController extends Common +{ + /** + * Display all the physical interfaces as a list + * + * @return View + */ + public function list(): View + { + return view( 'interfaces/physical/list' )->with([ + 'pis' => PhysicalInterface::selectRaw( + 'pi.id AS id, pi.speed AS speed, pi.duplex AS duplex, pi.status AS status, + pi.notes AS notes, pi.autoneg AS autoneg, pi.rate_limit as rate_limit, + c.name AS customer, c.id AS custid, + s.name AS switch, s.id AS switchid, + vi.id AS vintid, + sp.type as type, ppi.id as ppid, fpi.id as fpid, + sp.name AS port, l.id AS locid, l.name AS location' + )->from( 'physicalinterface AS pi' ) + ->leftJoin( 'physicalinterface AS ppi', 'ppi.fanout_physical_interface_id', 'pi.id' ) + ->leftJoin( 'physicalinterface AS fpi', 'fpi.id', 'pi.fanout_physical_interface_id' ) + ->leftJoin( 'virtualinterface AS vi', 'vi.id', 'pi.virtualinterfaceid' ) + ->leftJoin( 'cust AS c', 'c.id', 'vi.custid' ) + ->leftJoin( 'switchport AS sp', 'sp.id', 'pi.switchportid' ) + ->leftJoin( 'switch AS s', 's.id', 'sp.switchid' ) + ->leftJoin( 'cabinet AS cab', 'cab.id', 's.cabinetid' ) + ->leftJoin( 'location AS l', 'l.id', 'cab.locationid' ) + ->get()->toArray() + ]); + } + + /** + * Display a physical interface + * + * @param PhysicalInterface $pi ID of the physical interface + * + * @return View + */ + public function view( PhysicalInterface $pi ): View + { + return view( 'interfaces/physical/view' )->with([ + 'pi' => $pi + ]); + } + + /** + * Display the form to edit a physical interface + * + * @param VirtualInterface $vi ID of the virtual interface + * + * @return View|RedirectResponse + */ + public function create( VirtualInterface $vi ) + { + return view( 'interfaces/physical/edit' )->with([ + 'switches' => Switcher::orderBy( 'name' )->get(), + 'switchports' => [], + 'pi' => false, + 'otherPICoreLink' => false, + 'vi' => $vi, + 'cb' => false, + 'enableFanout' => $this->resellerMode() && $vi && $vi->customer->reseller, + 'spFanout' => false, + ]); + } + + /** + * Store a physical interface (set all the data needed) + * + * @param StorePhysicalInterface $r instance of the current HTTP request + * + * @return RedirectResponse + * + * @throws + */ + public function store( StorePhysicalInterface $r ): RedirectResponse + { + $vi = VirtualInterface::find( $r->virtualinterfaceid ); + $sp = SwitchPort::find( $r->switchportid ); + + // If the first port in an existing virtual interface is already + // reseller, set the new port to reseller. + if ( $vi->physicalInterfaces()->count() > 0 + && $vi->physicalInterfaces()->first()->switchport->typeReseller() ) { + $sp->update( [ 'type' => SwitchPort::TYPE_RESELLER ] ); + } + + // when presenting the add PI form, we include peering, reseller and unknown port types; + // set the selected port as peering unless already reseller + if ( $sp->type !== SwitchPort::TYPE_RESELLER ) { + $sp->update( [ 'type' => SwitchPort::TYPE_PEERING ] ); + } + + $this->setBundleDetails( $vi ); + $vi->save(); + + $pi = PhysicalInterface::create( $r->all() ); + + if( !$this->processFanoutPhysicalInterface( $r, $pi, $vi) ){ + return Redirect::back()->withInput( $r->all() ); + } + + if( $related = $pi->relatedInterface() ) { + $related->speed = $r->speed; + $related->status = $r->status; + $related->duplex = $r->duplex; + $related->save(); + } + + AlertContainer::push( 'Physical Interface created.', Alert::SUCCESS ); + return Redirect::to( $r->cb ? route( "core-bundle@edit", [ "cb" => $r->cb ] ) : route( "virtual-interface@edit", [ "vi" => $pi->virtualinterfaceid ] ) ); + } + + /** + * Display the form to edit a physical interface from the core bundle + * + * @param Request $r + * @param PhysicalInterface $pi the physical interface + * @param CoreBundle $cb core bundle + * + * @return RedirectResponse|View + */ + public function editFromCb( Request $r, PhysicalInterface $pi , CoreBundle $cb ): View|RedirectResponse + { + return $this->edit( $r, $pi , null, $cb ); + } + + /** + * Display the form to edit a physical interface + * + * @param Request $r + * @param PhysicalInterface $pi the physical interface + * @param VirtualInterface|null $vi the virtual interface + * @param CoreBundle|null $cb we come from the core bundle edit form + * + * @return View|RedirectResponse + */ + public function edit( Request $r, PhysicalInterface $pi, VirtualInterface $vi = null, CoreBundle $cb = null ) + { + // we never edit a fanout port: + if( $pi->switchPort->typeFanout() ) { + AlertContainer::push( 'Do not edit fanout ports directly. Edit the peering interface and the fanout port will be updated to match.', Alert::DANGER ); + return redirect( route( 'virtual-interface@edit', [ 'vi' => $pi->virtualinterfaceid ] ) ); + } + + if( $vi && $pi->virtualinterfaceid !== $vi->id ) { + AlertContainer::push( 'The physical interface does not belong to this virtual interface.', Alert::DANGER ); + return redirect( route( 'virtual-interface@edit', [ 'vi' => $pi->virtualinterfaceid ] ) ); + } + + // fill the form with physical interface data + $data = [ + 'switch' => $r->old( 'switch', $pi->switchPort->switchid ), + 'switchportid' => $r->old( 'switchportid', (string)$pi->switchportid ), + 'status' => $r->old( 'status', $pi->status ), + 'speed' => $r->old( 'speed', $pi->speed ), + 'duplex' => $r->old( 'duplex', $pi->duplex ), + 'rate_limit' => $r->old( 'rate_limit', (string)$pi->rate_limit ), + 'autoneg' => $r->old( 'autoneg', (string)$pi->autoneg ), + 'notes' => $r->old( 'notes', $pi->notes ), + ]; + + // get all the switch ports available and add the switch port associated to the physical interface in the list + $switchports = SwitcherAggregator::allPorts( $pi->switchPort->switchid, [], [], true, true ) + + [ (string)$pi->switchportid => + [ "name" => $pi->switchPort->name, + "id" => $pi->switchportid, + "type" => $pi->switchPort->type, + "porttype" => $pi->switchPort->type ] + ]; + + ksort($switchports); + + // get the fanout details or other side of the core link details as/if applicable + $data = $this->mergeFanoutDetails( $pi, $pi->virtualInterface , $data ); + $data = $this->mergeCoreLinkDetails( $pi, $data ); + + Former::populate( $data ); + + return view( 'interfaces/physical/edit' )->with([ + 'switches' => Switcher::orderBy( 'name' )->get(), + 'switchports' => $switchports ?? [], + 'pi' => $pi, + 'otherPICoreLink' => $pi->otherPICoreLink(), + 'vi' => $vi ?: false, + 'cb' => $cb ?: false, + 'enableFanout' => $this->resellerMode() && $vi && $vi->customer->reseller, + 'spFanout' => isset( $data['fanout'] ) && $data['fanout'] && $pi->fanoutPhysicalInterface()->exists() ? $pi->fanoutPhysicalInterface->switchPort->id : false, + 'notesb' => array_key_exists( 'notes-b', $data ) ? $data['notes-b'] : '' + ]); + } + + /** + * Update a physical interface (set all the data needed) + * + * @param StorePhysicalInterface $r instance of the current HTTP request + * @param PhysicalInterface $pi + * + * @return RedirectResponse + * + * @throws + */ + public function update( StorePhysicalInterface $r, PhysicalInterface $pi ): RedirectResponse + { + $vi = VirtualInterface::find( $r->virtualinterfaceid ); + // when presenting the add PI form, we include peering, reseller uplink and unknown port types; set the selected port as peering: + $sp = SwitchPort::find( $r->switchportid ); + + if ( $sp->type !== SwitchPort::TYPE_RESELLER ) { + $sp->update( [ 'type' => SwitchPort::TYPE_PEERING ] ); + } + + if( $pi->otherPICoreLink() ){ + // check if the user has changed the switch port + if( $sp->id !== $pi->switchportid ){ + $oldSp = $pi->switchPort; + $oldSp->update( [ 'type' =>SwitchPort::TYPE_UNSET ] ); + } + + // check if the user has selected the same switch port + if( $pi->otherPICoreLink()->switchPort->switchid === $sp->switchid ){ + AlertContainer::push( 'The switch port selected for this physical interface is already used by the other physical interface of the core bundle. Please select another switch port', Alert::DANGER ); + return Redirect::back( )->withInput( $r->all() ); + } + + $sp->update( [ 'type' => SwitchPort::TYPE_CORE ] ); + } + + $this->setBundleDetails( $vi ); + $vi->save(); + + $pi->update( $r->all() ); + + if( !$this->processFanoutPhysicalInterface( $r, $pi, $vi) ){ + return Redirect::back( )->withInput( $r->all() ); + } + + if( $related = $pi->relatedInterface() ) { + $related->speed = $r->speed; + $related->status = $r->status; + $related->duplex = $r->duplex; + $related->save(); + } + + AlertContainer::push( 'Physical Interface updated.', Alert::SUCCESS ); + + if( $r->cb ) { + return redirect( route( "core-bundle@edit", [ "cb" => json_decode($r->cb)->id ] ) ); + } + + return redirect( route( "virtual-interface@edit", [ "vi" => $pi->virtualinterfaceid ] ) ); + } + + /** + * Delete a Physical Interface + * + * @param Request $r instance of the current HTTP request + * @param PhysicalInterface $pi + * + * @return RedirectResponse + * + * @throws + */ + public function delete( Request $r, PhysicalInterface $pi): RedirectResponse + { + if( $_SERVER[ "HTTP_REFERER" ] === route( "physical-interface@list" ) ){ + $redirect = route( "physical-interface@list" ); + } else { + $redirect = route( "virtual-interface@edit", [ "vi" => $pi->virtualinterfaceid ] ); + } + + if( $pi->coreInterface ) { + AlertContainer::push( 'You cannot delete this physical interface as there is a core bundle linked with it.', Alert::DANGER ); + return Redirect::to( $redirect ); + } + + $this->deletePi( $r, $pi, true ); + AlertContainer::push( 'Physical Interface deleted.', Alert::SUCCESS ); + return Redirect::to( $redirect ); + } + + /** + * Utility function called by edit(). If this physical interface being edited is part of a core link, + * this function adds the details of the PI on the other end of the core link to the `$data` array. + * + * @param PhysicalInterface|null $pi + * @param array $data + * + * @return array + */ + private function mergeCoreLinkDetails( ?PhysicalInterface $pi, array $data ): array + { + if( !$pi || !( $piB = $pi->otherPICoreLink() ) ) { + return $data; + } + + /** @var PhysicalInterface $piB */ + $data['switch-b'] = $piB->switchPort->switchid; + $data['switch-port-b'] = $piB->switchportid; + $data['status-b'] = $piB->status; + $data['speed-b'] = $piB->speed; + $data['duplex-b'] = $piB->duplex; + $data['autoneg-label-b'] = $piB->autoneg ? 1 : 0; + $data['notes-b'] = $piB->notes ?? ''; + + return $data; + } + + /** + * Utility function called by edit(). If this physical interface being edited is for a resold customer, + * this function adds the details of the fanout port to the `$data` array. + * + * @param PhysicalInterface|null $pi + * @param VirtualInterface|null $vi + * @param array $data + * + * @return array + */ + private function mergeFanoutDetails( ?PhysicalInterface $pi, ?VirtualInterface $vi, array $data ): array + { + if( !( $this->resellerMode() && $vi && $vi->customer->reseller ) ) { + return $data; + } + + if( $pi && $pi->fanoutPhysicalInterface()->exists() ) { + $data['fanout'] = $pi->fanoutPhysicalInterface ? 1 : 0; + $data['switch-fanout'] = $pi->fanoutPhysicalInterface->switchPort->switcher->id; + $data['switch-port-fanout'] = $pi->fanoutPhysicalInterface->switchPort->id; + } + + return $data; + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Interfaces/SflowReceiverController.php b/app/Http/Controllers/Interfaces/SflowReceiverController.php new file mode 100644 index 000000000..a4c976e2f --- /dev/null +++ b/app/Http/Controllers/Interfaces/SflowReceiverController.php @@ -0,0 +1,155 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers\Interfaces + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class SflowReceiverController extends Common +{ + /** + * Display all the SflowReceivers + * + * @return View + */ + public function list(): View + { + return view( 'interfaces/sflow-receiver/list' )->with([ + 'listSr' => SflowReceiver::all() + ]); + } + + /** + * Display the form to create a sflow receiver + * + * @param VirtualInterface|null $vi ID of the Virtual Interface + * + * @return View + */ + public function create( VirtualInterface $vi = null ): View + { + return view( 'interfaces/sflow-receiver/edit' )->with([ + 'sflr' => false, + 'vi' => $vi + ]); + } + + /** + * Create a SflowReceiver + * + * @param StoreSflowReceiver $r instance of the current HTTP request + * + * @return RedirectResponse + */ + public function store( StoreSflowReceiver $r ): RedirectResponse + { + $sflr = SflowReceiver::create( $r->all() ); + AlertContainer::push( 'Sflow receiver created.', Alert::SUCCESS ); + return redirect( route( 'virtual-interface@edit', [ 'vi' => $sflr->virtual_interface_id ] ) ); + } + + /** + * Display the form to add/edit a sflow receiver + * + * @param Request $r + * @param SflowReceiver $sflr ID of the Sflow Receiver + * @param VirtualInterface|null $vi ID of the Virtual Interface + * + * @return View + */ + public function edit( Request $r, SflowReceiver $sflr, VirtualInterface $vi = null ): View + { + Former::populate([ + 'dst_ip' => $r->old( 'dst_ip', $sflr->dst_ip ), + 'dst_port' => $r->old( 'dst_port', (string)$sflr->dst_port ), + ]); + + return view( 'interfaces/sflow-receiver/edit' )->with([ + 'sflr' => $sflr, + 'vi' => $vi ?: false, + ]); + } + + /** + * Update a SflowReceiver + * + * @param StoreSflowReceiver $r instance of the current HTTP request + * @param SflowReceiver $sflr + * + * @return RedirectResponse + */ + public function update( StoreSflowReceiver $r, SflowReceiver $sflr ): RedirectResponse + { + $sflr->update( $r->all() ); + AlertContainer::push( 'Sflow receiver updated.', Alert::SUCCESS ); + return redirect( route( 'virtual-interface@edit', [ 'vi' => $sflr->virtualInterface->id ] ) ); + } + + /** + * Delete a Sflow receiver + * + * @param SflowReceiver $sflr + * + * @return RedirectResponse + * + * @throws Exception + */ + public function delete( SflowReceiver $sflr ): RedirectResponse + { + $sflr->delete(); + AlertContainer::push( 'Sflow receiver deleted.', Alert::SUCCESS ); + + if( $_SERVER[ "HTTP_REFERER" ] === route( "sflow-receiver@list" ) ){ + return redirect( route( "sflow-receiver@list" ) ); + } + return redirect( route( "virtual-interface@edit" , [ "vi" => $sflr->virtualInterface->id ] ) ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Interfaces/VirtualInterfaceController.php b/app/Http/Controllers/Interfaces/VirtualInterfaceController.php new file mode 100644 index 000000000..50620d3b3 --- /dev/null +++ b/app/Http/Controllers/Interfaces/VirtualInterfaceController.php @@ -0,0 +1,416 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers\Interfaces + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class VirtualInterfaceController extends Common +{ + /** + * Display all the virtualInterfaces + * + * @return View + */ + public function list() : View + { + return view( 'interfaces/virtual/list' )->with([ + 'resellerMode' => $this->resellerMode(), + 'vis' => VirtualInterface::selectRaw( + 'vi.id AS id, + SUM( pi.speed ) AS speed, + SUM( pi.rate_limit ) AS rate_limit, + COUNT( pi.id ) AS nbpi, + c.id AS custid, c.name AS custname, + l.id as locationid, l.name AS locationname, + s.id AS switchid, s.name AS switchname, + GROUP_CONCAT( sp.name ) AS switchport, + GROUP_CONCAT( sp.type ) AS switchporttype, + GROUP_CONCAT( ppi.id ) AS peering, + GROUP_CONCAT( fpi.id ) AS fanout' + )->from( 'virtualinterface AS vi' ) + ->leftJoin( 'physicalinterface AS pi', 'pi.virtualinterfaceid', 'vi.id' ) + ->leftJoin( 'physicalinterface AS ppi', 'ppi.fanout_physical_interface_id', 'pi.id' ) + ->leftJoin( 'physicalinterface AS fpi', 'fpi.id', 'pi.fanout_physical_interface_id' ) + ->leftJoin( 'cust AS c', 'c.id', 'vi.custid' ) + ->leftJoin( 'switchport AS sp', 'sp.id', 'pi.switchportid' ) + ->leftJoin( 'switch AS s', 's.id', 'sp.switchid' ) + ->leftJoin( 'cabinet AS cab', 'cab.id', 's.cabinetid' ) + ->leftJoin( 'location AS l', 'l.id', 'cab.locationid' ) + ->groupBy( 'id' ) + ->get()->toArray(), + ]); + } + + /** + * Display all the virtualInterfaces that are allocated to non-active customers + * + * @return View + */ + public function listOrphaned() : View + { + return view( 'interfaces/virtual/list-orphaned' )->with([ + 'resellerMode' => $this->resellerMode(), + 'vis' => VirtualInterface::selectRaw( + 'vi.id AS id, + SUM( pi.speed ) AS speed, + SUM( pi.rate_limit ) AS rate_limit, + COUNT( pi.id ) AS nbpi, + c.id AS custid, c.name AS custname, + c.dateleave AS dateleave, + c.status AS custstatus, + c.type AS custtype, + l.id as locationid, l.name AS locationname, + s.id AS switchid, s.name AS switchname, + GROUP_CONCAT( sp.name ) AS switchport, + GROUP_CONCAT( sp.type ) AS switchporttype, + GROUP_CONCAT( ppi.id ) AS peering, + GROUP_CONCAT( fpi.id ) AS fanout' + )->from( 'virtualinterface AS vi' ) + ->leftJoin( 'physicalinterface AS pi', 'pi.virtualinterfaceid', 'vi.id' ) + ->leftJoin( 'physicalinterface AS ppi', 'ppi.fanout_physical_interface_id', 'pi.id' ) + ->leftJoin( 'physicalinterface AS fpi', 'fpi.id', 'pi.fanout_physical_interface_id' ) + ->leftJoin( 'cust AS c', 'c.id', 'vi.custid' ) + ->leftJoin( 'switchport AS sp', 'sp.id', 'pi.switchportid' ) + ->leftJoin( 'switch AS s', 's.id', 'sp.switchid' ) + ->leftJoin( 'cabinet AS cab', 'cab.id', 's.cabinetid' ) + ->leftJoin( 'location AS l', 'l.id', 'cab.locationid' ) + ->whereNotNull( 'c.dateleave' ) + ->orWhere( 'c.status', '!=', Customer::STATUS_NORMAL ) + ->orWhere( 'c.type', Customer::TYPE_ASSOCIATE ) + ->groupBy( 'id' ) + ->get()->toArray(), + ]); + } + + + /** + * Display the form to create a virtual interface + * + * @param Customer $cust customer + * + * @return View + * + * @throws + */ + public function createWizardForCust( Customer $cust ) : View + { + return $this->wizard( $cust ); + } + + /** + * Display the form to add a virtual interface with a selected customer + * + * @param Customer $cust the customer to preselect + * + * @return View + */ + public function createForCust( Customer $cust ) : View + { + return $this->create( request(), $cust ); + } + + /** + * Display the form to create a virtual interface + * + * @param Request $r + * @param Customer|null $cust the customer to preselect + * + * @return View + */ + public function create( Request $r, Customer $cust = null ): View + { + if( $cust ) { + Former::populate( [ + 'custid' => $r->old( 'cust', (string)$cust->id ), + ] ); + } + + return view( 'interfaces/virtual/add' )->with([ + 'custs' => CustomerAggregator::reformatNameWithDetail( Customer::trafficking()->current()->orderBy('name')->get() ), + 'vlans' => [], + 'vi' => false, + 'cb' => false, + 'selectedCust' => $cust ?: false + ]); + } + + /** + * Create a virtual interface + * + * @param StoreVirtualInterface $r instance of the current HTTP request + * + * @return RedirectResponse + */ + public function store( StoreVirtualInterface $r ): RedirectResponse + { + // we don't allow setting channel group or name until there's >= 1 physical interface / LAG framing: + $r->merge( [ 'name' => '' , 'channelgroup' => null ] ); + $vi = VirtualInterface::make( $r->all() ); + + $this->setBundleDetails( $vi ); + + $vi->save(); + + AlertContainer::push( 'Virtual interface created.', Alert::SUCCESS ); + return redirect( route( 'virtual-interface@edit', [ 'vi' => $vi->id ] ) ); + + } + + /** + * Display the form to edit a virtual interface + * + * @param Request $r + * @param VirtualInterface $vi the virtual interface + * + * @return View + */ + public function edit( Request $r, VirtualInterface $vi ): View + { + $name = $r->old( 'name', $vi->name ); + + // Check if the last character of the Name is a white space, + // if its the case we add Double quotes to keep the space at the end + if( substr( $name, -1 ) === ' ' ) { + $name = '"'. $name . '"'; + } + + // fill the form with Virtual interface data + Former::populate([ + 'custid' => $r->old( 'custid', (string)$vi->custid ), + 'trunk' => $r->old( 'trunk', (string)$vi->trunk ), + 'lag_framing' => $r->old( 'lag_framing', (string)$vi->lag_framing ), + 'fastlacp' => $r->old( 'fastlacp', (string)$vi->fastlacp ), + 'description' => $r->old( 'description', $vi->description ), + 'channelgroup' => $r->old( 'channel-group', (string)$vi->channelgroup ), + 'mtu' => $r->old( 'mtu', (string)$vi->mtu ), + 'name' => $name, + ]); + + return view( 'interfaces/virtual/add' )->with([ + 'custs' => CustomerAggregator::reformatNameWithDetail( Customer::trafficking()->orderBy('name')->get() ), + 'vlans' => Vlan::orderBy( 'number' )->get(), + 'vi' => $vi, + 'cb' => $vi->getCoreBundle(), + 'selectedCust' => false + ]); + } + + /** + * Add or edit a virtual interface (set all the data needed) + * + * @param StoreVirtualInterface $r instance of the current HTTP request + * @param VirtualInterface $vi + * + * @return RedirectResponse + * + * @throws GeneralException|Throwable + */ + public function update( StoreVirtualInterface $r, VirtualInterface $vi ): RedirectResponse + { + $r->merge( [ 'name' => trim( $r->name , '"') ] ); + + // we don't allow setting channel group or name until there's >= 1 physical interface / LAG framing: + if( $vi->physicalInterfaces()->count() === 0 ) { + $r->merge( [ 'name' => '' , 'channelgroup' => null ] ); + } + + if( $vi->physicalInterfaces()->count() > 1 && $vi->channelgroup && !$r->channelgroup ) { + AlertContainer::push( 'You cannot remove a channel group number when there are multiple phsyical interfaces.', Alert::DANGER ); + return redirect( route( 'virtual-interface@edit', [ 'vi' => $vi->id ] ) )->withInput()->exceptInput( 'channelgroup' ); + } + + + DB::beginTransaction(); + $vi->fill( $r->all() ); + $this->setBundleDetails( $vi ); + $vi->save(); + + if( $vi->physicalInterfaces()->count() > 0 ) { + // We need to try and make naming of the virtual interface name automatic as well as choice + // of the channel group number. + + // let's take group number first -> needs to be unique within a switch and > 0 + // (some devices may allow zero but programmatically it may be easier to avoid this due to legacy data) + // if it's a number gt zero and it's changed (if we're editing) + + // ensure it's unique: + if( !$r->lag_framing && $r->channelgroup === null && $vi->physicalInterfaces()->count() === 1 ) { + // no op -> this allows a user to set a null channel group number on an interface with one PI and no lag framing. + } else if( !VirtualInterfaceAggregator::validateChannelGroup( $vi ) ) { + DB::rollback(); + AlertContainer::push( 'Channel group number is not unique within the switch.', Alert::DANGER ); + return redirect( route( 'virtual-interface@edit', [ 'vi' => $vi->id ] ) )->withInput()->exceptInput( 'channelgroup' ); + } + } + DB::commit(); + AlertContainer::push( 'Virtual Interface updated.', Alert::SUCCESS ); + return redirect( route( 'virtual-interface@edit', [ 'vi' => $vi->id ] ) ); + } + + /** + * Display the wizard form to add a virtual interface + * + * @param Customer|null $cust Id of the customer to preselect + * + * @return View + * + * @throws + */ + public function wizard( Customer $cust = null ): View + { + if( $cust ) { + Former::populate( [ + 'custid' => $cust->id, + ] ); + } + + return view( 'interfaces/virtual/wizard' )->with([ + 'custs' => CustomerAggregator::reformatNameWithDetail( Customer::trafficking()->current()->orderBy('name')->get() ), + 'vli' => false, + 'vlans' => Vlan::orderBy( 'number' )->get(), + 'pi_switches' => Switcher::where( 'active', true ) + ->orderBy( 'name' )->get(), + 'resoldCusts' => $this->resellerMode() ? json_encode( Customer::join('cust AS reseller', 'reseller.reseller', 'cust.id') + ->orderBy('reseller.name')->get(), JSON_THROW_ON_ERROR) : json_encode([], JSON_THROW_ON_ERROR), + 'selectedCust' => $cust ?: false + ]); + } + + /** + * Create an interface wizard + * + * @param StoreVirtualInterfaceWizard $r instance of the current HTTP request + * + * @return RedirectResponse + */ + public function storeWizard( StoreVirtualInterfaceWizard $r ): RedirectResponse + { + $v = Vlan::find( $r->vlanid ); + $vi = VirtualInterface::create( $r->all() ); + + PhysicalInterface::create( array_merge( $r->all(), [ + 'virtualinterfaceid' => $vi->id, + ] ) ); + + SwitchPort::find( $r->switchportid )->update( [ 'type' => SwitchPort::TYPE_PEERING ] ); + + $vli = VlanInterface::make( array_merge( $r->all(), + [ + 'virtualinterfaceid' => $vi->id, + 'busyhost' => false + ] + ) ); + + if( !$this->setIp( $r, $v, $vli, false ) || !$this->setIp( $r, $v, $vli, true ) ) { + return redirect(route( 'virtual-interface@wizard' ) )->withInput( $r->all() ); + } + + $vli->save(); + + // add a warning if we're filtering on irrdb but have not configured one for the customer + $this->warnIfIrrdbFilteringButNoIrrdbSourceSet( $vli ); + + AlertContainer::push( "Virtual interface created.", Alert::SUCCESS ); + return redirect( route( 'virtual-interface@edit', [ 'vi' => $vi->id ] ) ); + } + + /** + * Delete a Virtual Interface + * + * @param Request $r instance of the current HTTP request + * @param VirtualInterface $vi + * + * @return RedirectResponse + * + * @throws Exception + */ + public function delete( Request $r, VirtualInterface $vi ): RedirectResponse + { + if( $vi->getCoreBundle() ) { + AlertContainer::push( 'The Virtual Interface is linked to a Core Bundle. Delete the Core Bundle first to be able to delete the Virtual Interface.', Alert::DANGER ); + return redirect( route( 'virtual-interface@edit' , [ 'vi' => $vi->id ] ) ); + } + + foreach( $vi->physicalInterfaces as $pi) { + $this->deletePi( $r, $pi, false ); + } + + foreach( $vi->vlanInterfaces as $vli ) { + $vli->layer2addresses()->delete(); + $vli->delete(); + } + + $vi->macAddresses()->delete(); + $vi->delete(); + + AlertContainer::push( 'Virtual interface deleted.', Alert::SUCCESS ); + + if( $r->user ) { + return redirect( route( "customer@overview", [ 'cust' => $r->user, "tab" => "ports" ] ) ); + } + return redirect( route( "virtual-interface@list" ) ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Interfaces/VlanInterfaceController.php b/app/Http/Controllers/Interfaces/VlanInterfaceController.php new file mode 100644 index 000000000..fb5fab1d5 --- /dev/null +++ b/app/Http/Controllers/Interfaces/VlanInterfaceController.php @@ -0,0 +1,302 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers\Interfaces + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class VlanInterfaceController extends Common +{ + /** + * Display all the physical interfaces as a list + * + * @return View + */ + public function list(): View + { + return view( 'interfaces/vlan/list' )->with( [ + 'vlis' => VlanInterface::with( 'virtualInterface.customer' ) + ->with( 'vlan' ) + ->with( 'ipv4address' ) + ->with( 'ipv6address' ) + ->get(), + ] ); + } + + /** + * Duplicate a VLAN interface + * + * @param VlanInterface $vli VLI that we will get the information from + * @param Vlan $v vlan where we will create the new VLI + * + * @return View + */ + public function duplicateForm( VlanInterface $vli, Vlan $v ): View + { + return $this->edit( request(), $vli, null, $v ); + } + + /** + * Display the form to edit a VLAM interface + * + * @param Request $r + * @param VirtualInterface $vi The virtual interface to add this VLI to + * + * @return View + */ + public function create( Request $r, VirtualInterface $vi ): View + { + Former::populate( [ + 'maxbgpprefix' => $r->old( 'maxbgpprefix', $vi->customer->maxprefixes ), + ] ); + + return view( 'interfaces/vlan/edit' )->with( [ + 'vlans' => Vlan::orderBy( 'number' )->get(), + 'vli' => false, + 'vi' => $vi, + 'redirect2vi' => $vi ? true : false, + 'duplicateTo' => false, + ] ); + } + + /** + * Create a vlan interface + * + * @param StoreVlanInterface $r instance of the current HTTP request + * + * @return RedirectResponse + */ + public function store( StoreVlanInterface $r ): RedirectResponse + { + $vli = VlanInterface::make( $r->all() ); + $v = Vlan::find( $r->vlanid ); + + if( !$this->setIp( $r, $v, $vli, false ) || !$this->setIp( $r, $v, $vli, true ) ) { + return Redirect::back()->withInput( $r->all() ); + } + + $vli->save(); + + // add a warning if we're filtering on irrdb but have not configured one for the customer + $this->warnIfIrrdbFilteringButNoIrrdbSourceSet( $vli ); + + AlertContainer::push( 'VLAN Interface created.', Alert::SUCCESS ); + return redirect( route( 'virtual-interface@edit', [ 'vi' => $r->virtualinterfaceid ] ) ); + } + + /** + * Display the form to edit a VLAN interface + * + * @param Request $r + * @param VlanInterface $vli The VLAN interface + * @param VirtualInterface|null $vi The virtual interface to add this VLI to + * @param Vlan|null $duplicateTo The ID of the vlan Interface that will receive the data of the the other vli ( $id ) + * + * @return View + */ + public function edit( Request $r, VlanInterface $vli, VirtualInterface $vi = null, Vlan $duplicateTo = null ): View + { + $vlanId = $duplicateTo->id ?? $vli->vlanid; + Former::populate( [ + 'vlanid' => $r->old( 'vlanid', (string)$vlanId ), + 'irrdbfilter' => $r->old( 'irrdbfilter', (string)$vli->irrdbfilter ), + 'mcastenabled' => $r->old( 'mcastenabled', (string)$vli->mcastenabled ), + 'ipv4enabled' => $r->old( 'ipv4enabled', (string)$vli->ipv4enabled ), + 'ipv4address' => $r->old( 'ipv4address', (string)$vli->ipv4addressid ), + 'ipv4hostname' => $r->old( 'ipv4hostname', $vli->ipv4hostname ), + 'ipv4bgpmd5secret' => $r->old( 'ipv4bgpmd5secret', $vli->ipv4bgpmd5secret ), + 'ipv4canping' => $r->old( 'ipv4canping', (string)$vli->ipv4canping ), + 'ipv4monitorrcbgp' => $r->old( 'ipv4monitorrcbgp', (string)$vli->ipv4monitorrcbgp ), + 'maxbgpprefix' => $r->old( 'maxbgpprefix', (string)$vli->maxbgpprefix ), + 'rsclient' => $r->old( 'rsclient', (string)$vli->rsclient ), + 'rsmorespecifics' => $r->old( 'rsmorespecifics', (string)$vli->rsmorespecifics ), + 'as112client' => $r->old( 'as112client', (string)$vli->as112client ), + 'busyhost' => $r->old( 'busyhost', (string)$vli->busyhost ), + 'ipv6enabled' => $r->old( 'ipv6enabled', (string)$vli->ipv6enabled ), + 'ipv6address' => $r->old( 'ipv6address', (string)$vli->ipv6addressid ), + 'ipv6hostname' => $r->old( 'ipv6hostname', $vli->ipv6hostname ), + 'ipv6bgpmd5secret' => $r->old( 'ipv6bgpmd5secret', $vli->ipv6bgpmd5secret ), + 'ipv6canping' => $r->old( 'ipv6canping', (string)$vli->ipv6canping ), + 'ipv6monitorrcbgp' => $r->old( 'ipv6monitorrcbgp', (string)$vli->ipv6monitorrcbgp ), + ] ); + + $redirect2vi = (bool)$vi; + if( !$vi ) { + $vi = $vli->virtualInterface; + } + + return view( 'interfaces/vlan/edit' )->with( [ + 'vlans' => Vlan:: orderBy( 'number' )->get(), + 'vli' => $vli, + 'vi' => $vi ?: false, + 'duplicateTo' => $duplicateTo ?: false, + 'redirect2vi' => $redirect2vi, + ] ); + } + + /** + * Update a vlan interface + * + * @param StoreVlanInterface $r instance of the current HTTP request + * @param VlanInterface $vli + * + * @return RedirectResponse + * + * @throws + */ + public function update( StoreVlanInterface $r, VlanInterface $vli ): RedirectResponse + { + $v = Vlan::find( $r->vlanid ); + + if( !$this->setIp( $r, $v, $vli, false ) || !$this->setIp( $r, $v, $vli, true ) ) { + return Redirect::back()->withInput( $r->all() ); + } + + $vli->update( $r->all() ); + + // add a warning if we're filtering on irrdb but have not configured one for the customer + $this->warnIfIrrdbFilteringButNoIrrdbSourceSet( $vli ); + + AlertContainer::push( 'VLAN Interface updated.', Alert::SUCCESS ); + + return redirect( $r->redirect2vi ? route( 'virtual-interface@edit', [ 'vi' => $vli->virtualinterfaceid ] ) : route( 'vlan-interface@list' ) ); + } + + /** + * Duplicate a vlan interface + * + * @param StoreVlanInterface $r instance of the current HTTP request + * @param VlanInterface $vli + * + * @return RedirectResponse + * + * @throws + */ + public function duplicate( StoreVlanInterface $r, VlanInterface $vli ): RedirectResponse + { + // are we duplicating? + $source = $vli; + $v = Vlan::find( $r->vlanid ); + + DB::beginTransaction(); + $vli = VlanInterface::make(); + + if( !$this->setIp( $r, $v, $vli, false ) || !$this->setIp( $r, $v, $vli, true ) ) { + // Rollback if there is issue to avoid to insert the data created above + DB::rollBack(); + return Redirect::back()->withInput( $r->all() ); + } + + $vli->fill( $r->all() ); + $vli->save(); + + foreach( $source->layer2addresses as $l2a ) { + Layer2Address::create( + [ + 'vlan_interface_id' => $vli->id, + 'mac' => $l2a->mac, + ] + ); + } + + DB::commit(); + + // add a warning if we're filtering on irrdb but have not configured one for the customer + $this->warnIfIrrdbFilteringButNoIrrdbSourceSet( $vli ); + + AlertContainer::push( 'VLAN Interface duplicated.', Alert::SUCCESS ); + + return redirect( route( 'virtual-interface@edit', [ 'vi' => $vli->virtualinterfaceid ] ) ); + } + + /** + * Display a VLAN Interface + * + * @param VlanInterface $vli + * + * @return View + */ + public function view( VlanInterface $vli ): View + { + return view( 'interfaces/vlan/view' )->with( [ + 'vli' => $vli, + ] ); + } + + /** + * Delete a VLAN Interface and the Layer2Address associated + * + * @param VlanInterface $vli + * + * @return RedirectResponse + * + * @throws Exception + */ + public function delete( VlanInterface $vli ): RedirectResponse + { + $vli->layer2addresses()->delete(); + $vli->delete(); + + AlertContainer::push( 'VLAN Interface deleted.', Alert::SUCCESS ); + + if( $_SERVER[ "HTTP_REFERER" ] === route( 'vlan-interface@list' ) ) { + return redirect( route( 'vlan-interface@list' ) ); + } + return redirect( route( 'virtual-interface@edit', [ 'vi' => $vli->virtualinterfaceid ] ) ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/IpAddressController.php b/app/Http/Controllers/IpAddressController.php new file mode 100644 index 000000000..ba5cf47c9 --- /dev/null +++ b/app/Http/Controllers/IpAddressController.php @@ -0,0 +1,338 @@ + + * @author Barry O'Donovan + * @category IXP + * @package IXP\Http\Controllers + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class IpAddressController extends Controller +{ + /** + * Return the entity depending on the protocol + * + * @param int $protocol Protocol of the IP address + * @param boolean $model Do we need to return the $model ? + * + * @return int|string + * + * @psalm-return IPv4Address::class|IPv6Address::class|int + */ + private function processProtocol( int $protocol , bool $model = true ): string|int + { + if( !in_array( $protocol, [ 4,6 ] ) ) { + abort( 404 , 'Unknown protocol'); + } + + if( $model ){ + return $protocol === 4 ? IPv4Address::class : IPv6Address::class; + } + + return $protocol; + } + + /** + * Display the list of the IP Address (IPv4 or IPv6) + * + * @param int $protocol Protocol of the IP address + * @param int|null Vlan ID of the vlan + * + * @return view + * + * @throws + */ + public function list( int $protocol, int $vid = null ): View + { + $vlan = false; + if( $vid ) { + $vlan = Vlan::findOrFail( $vid ); + } + + /** @var IPv4Address|IPv6Address $ipvxModel */ + $ipvxModel = $this->processProtocol( $protocol, true ); + + $ips = $ipvxModel::selectRaw( 'ip.id as id, + ip.address as address,' . + ( $protocol === 4 ? 'inet_aton(ip.address) as aton' : 'hex( inet6_aton( ip.address ) ) as aton') . + ",v.name AS vlan, + v.id as vlanid, + vli.id AS vliid, + vli.ipv{$protocol}hostname AS hostname, + c.name AS customer, + c.id AS customerid, + vi.id AS viid" ) + ->from( "ipv{$protocol}address AS ip" ) + ->leftJoin( 'vlan AS v', 'v.id', 'ip.vlanid' ) + ->leftJoin( 'vlaninterface AS vli', "vli.ipv{$protocol}addressid", 'ip.id' ) + ->leftJoin( 'virtualinterface AS vi', 'vi.id', 'vli.virtualinterfaceid' ) + ->leftJoin( 'cust AS c', 'c.id', 'vi.custid' ) + ->when( $vlan, function( Builder $q, $vlan ) { + return $q->where( 'v.id', $vlan->id ); + } ) + ->orderBy( 'address' ) + ->get()->toArray(); + + return view( 'ip-address/list' )->with([ + 'ips' => $vlan ? $ips : [], + 'vlans' => Vlan::publicOnly()->orderBy('number' )->get(), + 'protocol' => $protocol, + 'vlan' => $vlan + ]); + } + + /** + * Display the form to add an IP Address (IPv4 or IPv6) + * + * @param int $protocol Protocol of the IP address + * + * @return view + */ + public function create( int $protocol ): View + { + return view( 'ip-address/add' )->with([ + 'vlans' => Vlan::publicOnly()->orderBy('number' )->get(), + 'protocol' => $this->processProtocol( $protocol, false ) + ]); + } + + /** + * Edit the core links associated to a core bundle + * + * @param Store $r instance of the current HTTP request + * + * @return RedirectResponse + * + * @throws + */ + public function store( Store $r ): RedirectResponse + { + $vlan = Vlan::find( $r->vlan ); + $network = Network::parse( trim( htmlspecialchars( $r->network ) ) ); + $skip = (bool)$r->input( 'skip', false ); + $decimal = (bool)$r->input( 'decimal', false ); + $overflow = (bool)$r->input( 'overflow', false ); + + if( $network->getFirstIP()->version === 'IPv6' ) { + $sequentialAddrs = self::generateSequentialAddresses( $network, $decimal, $overflow ); + $model = IPv6Address::class; + } else { + $sequentialAddrs = []; + foreach( $network as $ip ) { + $sequentialAddrs[] = (string)$ip; + } + $model = IPv4Address::class; + } + + $result = IpAddressAggregator::bulkAdd( $sequentialAddrs, $vlan, $model, $skip ); + + if( !$skip && count( $result['preexisting'] ) ) { + AlertContainer::push( "No addresses were added as the following addresses already exist in the database: " + . implode( ', ', $result['preexisting'] ) . ". You can check skip below to add only the addresses " + . "that do not already exist.", Alert::DANGER ); + return Redirect::back()->withInput(); + } + + if( count( $result['new'] ) === 0 ) { + AlertContainer::push( "No addresses were added. " . count( $result['preexisting'] ) . " already exist in the database.", + Alert::WARNING + ); + return Redirect::back()->withInput(); + } + + AlertContainer::push( count( $result['new'] ) . ' new IP addresses added to ' . $vlan->name . '. ' + . ( $skip ? 'There were ' . count( $result['preexisting'] ) . ' preexisting address(es).' : '' ), + Alert::SUCCESS + ); + + return Redirect::to( route( 'ip-address@list', [ 'protocol' => $network->getFirstIP()->getVersion() === 'IPv6' ? '6' : '4', 'vlanid' => $vlan->id ] ) ); + } + + /** + * Display the form to delete free IP addresses in a VLAN + * + * @param DeleteByNetwork $r Instance of the current HTTP request + * @param Vlan $vlan + * + * @return View|RedirectResponse + * + * @throws + */ + public function deleteByNetwork( DeleteByNetwork $r, Vlan $vlan ): View|RedirectResponse + { + $ips = []; + if( $r->network ) { + $network = Network::parse( trim( htmlspecialchars( $r->network ) ) ); + + if( $network->getFirstIP()->version === 'IPv6' ) { + /** @var IPv6Address $model */ + $model = IPv6Address::class; + $sequentialAddrs = self::generateSequentialAddresses( $network, false, false ); + } else { + /** @var IPv4Address $model */ + $model = IPv4Address::class; + + $sequentialAddrs = []; + foreach( $network as $ip ) { + $sequentialAddrs[] = (string)$ip; + } + } + + $ips = $model::with( 'vlanInterface' )->doesntHave( 'vlanInterface' ) + ->whereIn( 'address', $sequentialAddrs ) + ->where( 'vlanid', $vlan->id )->get(); + + if( $r->input( 'doDelete', false ) === "1" ) { + $model::whereIn( 'id', $ips->pluck( 'id' ) )->delete(); + AlertContainer::push( 'IP Addresses deleted.', Alert::SUCCESS ); + + return redirect( route( 'ip-address@list', [ 'protocol' => $network->getFirstIP()->version === 'IPv6' ? 6 : 4, 'vlanid' => $vlan->id ] ) ); + } + } + + return view( 'ip-address/delete-by-network' )->with([ + 'vlan' => $vlan, + 'network' => $r->network, + 'ips' => $ips, + ]); + } + + /** + * Delete an IP address + * + * @param Request $r + * @param int $id + * + * @return RedirectResponse + * + * @throws + */ + public function delete( Request $r, int $id ): RedirectResponse + { + /** @var IPv4Address|IPv6Address $ipvxModel */ + $ipvxModel = $this->processProtocol( $r->protocol , true ); + $ip = $ipvxModel::findOrFail( $id ); + + if( $ip->vlanInterface ) { + AlertContainer::push( 'This IP address is assigned to a VLAN interface.', Alert::DANGER ); + return redirect()->back(); + } + $ip->delete(); + + AlertContainer::push( 'The IP has been successfully deleted.', Alert::SUCCESS ); + return Redirect::to( route( "ip-address@list", [ "protocol" => $r->protocol , "vlanid" => $ip->vlanid ] ) ); + } + + /** + * For a given IPTools library network object, generate sequential IPv6 addresses. + * + * There is also a `$decimal` option which only returns IPv6 addresses where the + * last block uses only decimal numbering. This exists because typically IXs allocate + * a customer an IPv6 address such that the last block matches the last block of the + * IPv4 address. So, if set, the function will generate the number of addresses as + * indicated by the CIDR block size but skip over any addresses containing + * `a-f` characters. **NB:** the full number of addresses will be generated which means + * this would typically overflow the subnet bound (unless $nooverflow is set). + * + * @param IPToolsNetwork $network + * @param bool $decimal + * @param bool $overflow + * + * @return string[] + * + * @throws + * + * @psalm-return list + */ + private static function generateSequentialAddresses( IPToolsNetwork $network, bool $decimal = false, bool $overflow = true ): array + { + $addresses = []; + + if( $decimal ) { + $ip = $network->getFirstIP(); + $target = 2 ** ( 128 - $network->getPrefixLength() ); + $i = 0; + $loops = 0; + + do { + if( ++$loops === $target && !$overflow ) { + break; + } + + if( !preg_match( '/^([0-9]+|)$/', substr( $ip, strrpos( $ip, ':' ) + 1 ) ) ) { + $ip = $ip->next(); + continue; + } + + $addresses[] = (string)$ip; + $ip = $ip->next(); + $i++; + + } while( $i < $target ); + } else { + foreach( $network as $ip ) { + $addresses[] = (string)$ip; + } + } + return $addresses; + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Irrdb/IrrdbConfigController.php b/app/Http/Controllers/Irrdb/IrrdbConfigController.php new file mode 100644 index 000000000..f828a9509 --- /dev/null +++ b/app/Http/Controllers/Irrdb/IrrdbConfigController.php @@ -0,0 +1,217 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class IrrdbConfigController extends EloquentController +{ + /** + * The object being created / edited + * + * @var IrrdbConfig + */ + protected $object = null; + + /** + * This function sets up the frontend controller + */ + #[\Override] + public function feInit(): void + { + $this->feParams = (object)[ + 'model' => IrrdbConfig::class, + 'pagetitle' => 'IRRDB Sources', + 'titleSingular' => 'IRRDB Source', + 'nameSingular' => 'an IRRDB Sources', + 'listOrderBy' => 'host', + 'listOrderByDir' => 'ASC', + 'viewFolderName' => 'irrdb-config', + 'documentation' => 'https://docs.ixpmanager.org/latest/features/irrdb/', + 'listColumns' => [ + 'host' => 'Host', + 'source' => 'Source' + ], + ]; + + // display the same information in the view as the list + $this->feParams->viewColumns = array_merge( + $this->feParams->listColumns, + [ + 'notes' => [ + 'title' => 'Notes', + 'type' => self::$FE_COL_TYPES[ 'PARSDOWN' ] + ], + 'created_at' => [ + 'title' => 'Created', + 'type' => self::$FE_COL_TYPES[ 'DATETIME' ] + ], + 'updated_at' => [ + 'title' => 'Updated', + 'type' => self::$FE_COL_TYPES[ 'DATETIME' ] + ] + ] + ); + } + + /** + * Provide array of rows for the list action and view action + * + * @param int|null $id The `id` of the row to load for `view` action`. `null` if `listAction` + * + * @return array + */ + #[\Override] + protected function listGetData( ?int $id = null ): array + { + $feParams = $this->feParams; + return IrrdbConfig::when( $id , function( Builder $q, $id ) { + return $q->where('id', $id ); + } )->when( $feParams->listOrderBy , function( Builder $q, $orderby ) use ( $feParams ) { + return $q->orderBy( $orderby, $feParams->listOrderByDir ?? 'ASC'); + })->get()->toArray(); + } + + /** + * Display the form to create an object + * + * @return IrrdbConfig[] + * + * @psalm-return array{object: IrrdbConfig} + */ + #[\Override] + protected function createPrepareForm(): array + { + return [ + 'object' => $this->object + ]; + } + + /** + * Display the form to update an object + * + * @param int|null $id + * + * @return array + * + * @psalm-return array{object: mixed} + */ + #[\Override] + protected function editPrepareForm( ?int $id = null ): array + { + $this->object = IrrdbConfig::findOrFail( $id ); + + Former::populate([ + 'host' => request()->old( 'host', $this->object->host ), + 'source' => request()->old( 'source', $this->object->source ), + 'notes' => request()->old( 'notes', $this->object->notes ), + ]); + + return [ + 'object' => $this->object + ]; + } + + /** + * Function to do the actual validation and storing of the submitted object. + * + * @return true + */ + #[\Override] + public function doStore( Request $r ): bool|RedirectResponse + { + $this->checkForm( $r ); + $this->object = IrrdbConfig::create( $r->all() ); + return true; + } + + /** + * Function to do the actual validation and storing of the submitted object. + * + * @return true + */ + #[\Override] + public function doUpdate( Request $r, int $id ): bool|RedirectResponse + { + $this->object = IrrdbConfig::findOrFail( $id ); + $this->checkForm( $r ); + $this->object->update( $r->all() ); + return true; + } + + /** + * @inheritdoc + */ + #[\Override] + protected function preDelete() : bool + { + $okay = true; + if( ( $cnt = $this->object->customers()->count() ) ) { + AlertContainer::push( "You cannot delete this IRRDB Source there are {$cnt} customer(s) associated with it. ", Alert::DANGER ); + $okay = false; + } + return $okay; + } + + /** + * Check if the form is valid + * + * @param Request $r + */ + #[\Override] + public function checkForm( Request $r ): void + { + $r->validate( [ + 'host' => [ 'required', 'max:255', 'string', new IdnValidate() ], + 'source' => 'required|string|max:255', + ] ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Irrdb/IrrdbController.php b/app/Http/Controllers/Irrdb/IrrdbController.php new file mode 100644 index 000000000..49f788df4 --- /dev/null +++ b/app/Http/Controllers/Irrdb/IrrdbController.php @@ -0,0 +1,146 @@ + + * @author Barry O'Donovan + * @category IXP + * @package IXP\Http\Controllers + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class IrrdbController extends Controller +{ + + /** + * Display a table of all members IRRDB status + * + * Protected via route in web-auth-superuser.php + * + * @param Request $r + * @return View + */ + public function summary( Request $r ) : View + { + // find all customers who use the route servers and are IRRDB filtered: + $custids = VirtualInterface::select('custid') + ->join('vlaninterface', 'vlaninterface.virtualinterfaceid', '=', 'virtualinterface.id') + ->where('vlaninterface.rsclient', 1 ) + ->where('vlaninterface.irrdbfilter', 1 ) + ->groupBy('custid') + ->orderBy('custid') + ->pluck('custid'); + + + // using the above, get these customers details and update log for the table: + $customers_logs = Customer::whereIn('id', $custids) + ->select('id', 'name', 'autsys', 'abbreviatedName') + ->with('irrdbUpdateLog') + ->get(); + + return view( 'irrdb/summary', [ + 'customers_logs' => $customers_logs, + ] ); + + } + + /** + * Display the list of IRRDB (ASN/Prefix), (IPv4/IPv6) for a customer + * + * @param IrrdbRequest $r + * @param Customer $cust + * @param String $type + * @param int $protocol + * + * @return View + */ + public function list( IrrdbRequest $r, Customer $cust, string $type, int $protocol ) : View + { + /** @var User $us */ + $us = Auth::getUser(); + + $irrdbList = IrrdbAggregator::forCustomerAndProtocol( $cust->id, $protocol, $type ); + + // are we busting the cache? + if( $r->reset_cache === "1" && $us->isSuperUser() ) { + Cache::forget('updated-irrdb-' . $type . '-' . $cust->id ); + } + + return view( 'irrdb/list' )->with([ + 'irrdbList' => $irrdbList, + 'type' => $type, + 'customer' => $cust, + 'protocol' => $protocol, + 'updatingIrrdb' => Cache::get( 'updating-irrdb-' . $type . '-' . $protocol . '-' . $cust->id, false ), + 'updatedIrrdb' => Cache::get( 'updated-irrdb-' . $type . '-' . $protocol . '-' . $cust->id, false ), + ]); + } + + /** + * Update the list of IRRDB (ASN/Prefix) for a customer + * + * @param IrrdbRequest $r + * @param Customer $cust + * @param String $type + * @param int $protocol + * + * @return RedirectResponse + */ + public function update( IrrdbRequest $r, Customer $cust, string $type, int $protocol ) : RedirectResponse + { + /** @var User $us */ + $us = Auth::getUser(); + // are we busting the cache? + if( $r->reset_cache === "1" && $us->isSuperUser() ) { + Cache::forget('updated-irrdb-' . $type . '-' . $protocol . '-' . $cust->id ); + } + + // get the status of the irrdb update function + $updatedIrrdb = Cache::get( 'updated-irrdb-' . $type . '-' . $protocol . '-' . $cust->id, false ); + + if( $updatedIrrdb === false ) { + // no cached result so schedule a job to gather them: + Cache::put( 'updating-irrdb-' . $type . '-' . $protocol . '-' . $cust->id, true, 3600 ); + UpdateIrrdb::dispatch( $cust, $type, $protocol ); + } + + return redirect( route( "irrdb@list", [ "cust" => $cust->id, "type" => $type , "protocol" => $protocol ] ) ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Layer2AddressController.php b/app/Http/Controllers/Layer2AddressController.php new file mode 100644 index 000000000..5f6774e17 --- /dev/null +++ b/app/Http/Controllers/Layer2AddressController.php @@ -0,0 +1,210 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Layer2AddressController extends EloquentController +{ + /** + * The object being created / edited + * + * @var Layer2Address + */ + protected $object = null; + + /** + * Is this a read only controller? + * + * @var boolean + */ + public static $read_only = true; + + /** + * The minimum privileges required to access this controller. + * + * If you set this to less than the superuser, you need to manage privileges and access + * within your own implementation yourself. + * + * @var int + */ + public static $minimum_privilege = User::AUTH_CUSTUSER; + + /** + * This function sets up the frontend controller + */ + #[\Override] + public function feInit(): void + { + /** @var User $us */ + $us = Auth::getUser(); + + $this->feParams = (object)[ + 'model' => Layer2Address::class, + 'pagetitle' => 'Configured MAC Addresses', + 'titleSingular' => 'Configured MAC Address', + 'nameSingular' => 'a configured MAC address', + 'listOrderBy' => 'abbreviatedName', + 'listOrderByDir' => 'ASC', + 'viewFolderName' => 'layer2-address', + 'readonly' => self::$read_only, + 'documentation' => 'https://docs.ixpmanager.org/latest/features/layer2-addresses/', + 'listColumns' => [ + 'customer' => 'Customer', + 'switchport' => 'Interface(s)', + 'vlan' => 'VLAN', + 'ip4' => 'IPv4', + 'ip6' => 'IPv6', + 'mac' => 'MAC Address', + 'organisation' => 'Manufacturer' + ], + ]; + + // display the same information in the view as the list + $this->feParams->viewColumns = $this->feParams->listColumns; + + // phpunit / artisan trips up here without the cli test: + if( PHP_SAPI !== 'cli' ) { + // custom access controls: + switch( Auth::check() ? $us->privs() : User::AUTH_PUBLIC ) { + case User::AUTH_SUPERUSER: + break; + case User::AUTH_CUSTUSER || User::AUTH_CUSTADMIN: + switch( Route::current()->getName() ) { + case 'layer2-address@forVlanInterface': + break; + default: + $this->unauthorized(); + } + break; + default: + $this->unauthorized(); + } + } + } + + /** + * Additional routes + * + * @param string $route_prefix + * + * @return void + */ + #[\Override] + protected static function additionalRoutes( string $route_prefix ): Void + { + // NB: this route is marked as 'read-only' to disable normal CRUD operations. It's not really read-only. + Route::group( [ 'prefix' => $route_prefix ], function() use ( $route_prefix ) { + Route::get( 'vlan-interface/{vli}', 'Layer2AddressController@forVlanInterface' )->name( $route_prefix . '@forVlanInterface' ); + }); + } + + /** + * Provide array of rows for the list action and view action + * + * @param int|null t $id The `id` of the row to load for `view` action`. `null` if `listAction` + + * @return array + */ + #[\Override] + protected function listGetData( ?int $id = null ): array + { + $feParams = $this->feParams; + return Layer2Address::selectRaw( "l.*, + vi.id AS viid, + c.id AS customerid, c.abbreviatedName AS customer, + s.name AS switchname, + vl.name as vlan, vl.id as vlanid, + vli.id as vliid, + GROUP_CONCAT( sp.name ) AS switchport, + GROUP_CONCAT( DISTINCT ipv4.address ) AS ip4, + GROUP_CONCAT( DISTINCT ipv6.address ) AS ip6, + COALESCE( o.organisation, 'Unknown' ) AS organisation" + ) + ->from( 'l2address AS l' ) + ->join( 'vlaninterface AS vli', 'vli.id', 'l.vlan_interface_id' ) + ->join( 'vlan AS vl', 'vl.id', 'vli.vlanid' ) + ->leftjoin( 'ipv4address AS ipv4', 'ipv4.id', 'vli.ipv4addressid' ) + ->leftjoin( 'ipv6address AS ipv6', 'ipv6.id', 'vli.ipv6addressid' ) + ->join( 'virtualinterface AS vi', 'vi.id', 'vli.virtualinterfaceid' ) + ->join( 'cust AS c', 'c.id', 'vi.custid' ) + ->leftjoin( 'physicalinterface AS pi', 'pi.virtualinterfaceid', 'vi.id' ) + ->leftjoin( 'switchport AS sp', 'sp.id', 'pi.switchportid' ) + ->leftjoin( 'switch AS s', 's.id', 'sp.switchid' ) + ->leftjoin( 'oui AS o', 'o.oui', '=', DB::raw("SUBSTRING( l.mac, 1, 6 )") ) + ->when( $id , function( Builder $q, $id ) { + return $q->where('l.id', $id ); + } )->groupBy( 'l.mac', 'vi.id', 'l.id', 'l.firstseen', + 'l.lastseen', 'c.id', 'c.abbreviatedName', 's.name', + 'vl.name', 'vl.id', 'vli.id', 'o.organisation' + )->when( $feParams->listOrderBy , function( Builder $q, $orderby ) use ( $feParams ) { + return $q->orderBy( $orderby, $feParams->listOrderByDir ?? 'ASC'); + })->get()->toArray(); + } + + /** + * Display all the layer2addresses for a VlanInterface + * + * @param VlanInterface $vli VlanInterface the VlanInterface + * + * @return View|RedirectResponse + */ + public function forVlanInterface( VlanInterface $vli ): View|RedirectResponse + { + /** @var User $us */ + $us = Auth::getUser(); + + if( $us->isSuperUser() ) { + return view( 'layer2-address/vlan-interface' )->with( [ 'vli' => $vli ] ); + } + + if( config( 'ixp_fe.layer2-addresses.customer_can_edit' ) && $us->custid === $vli->virtualInterface->customer->id ) { + return view( 'layer2-address/vlan-interface-cust' )->with( [ 'vli' => $vli ] ); + } + return redirect(''); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/LocationController.php b/app/Http/Controllers/LocationController.php new file mode 100644 index 000000000..16e14b024 --- /dev/null +++ b/app/Http/Controllers/LocationController.php @@ -0,0 +1,266 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class LocationController extends EloquentController +{ + /** + * The object being created / edited + * + * @var Location + */ + protected $object = null; + + /** + * The URL prefix to use. + * + * Automatically determined based on the controller name if not set. + * + * @var string|null + */ + protected static $route_prefix = "facility"; + + /** + * This function sets up the frontend controller + */ + #[\Override] + public function feInit(): void + { + $this->feParams = (object)[ + 'model' => Location::class, + 'pagetitle' => 'Facilities', + 'titleSingular' => 'Facility', + 'nameSingular' => 'facility', + 'listOrderBy' => 'name', + 'listOrderByDir' => 'ASC', + 'viewFolderName' => 'location', + 'listColumns' => [ + 'id' => [ + 'title' => 'UID', + 'display' => false + ], + 'name' => 'Name', + 'shortname' => 'Shortname', + 'tag' => 'Tag', + 'nocphone' => 'NOC Phone', + 'nocemail' => 'NOC Email', + 'pdb_facility_id' => [ + 'title' => 'PeeringDB ID', + 'type' => self::$FE_COL_TYPES[ 'REPLACE' ], + 'subject' => '%%COL%%', + ], + ] + ]; + + // display the same information in the view as the list + $this->feParams->viewColumns = array_merge( + $this->feParams->listColumns, [ + 'address' => 'Address', + 'city' => 'City', + 'country' => [ + 'title' => 'Country', + 'type' => self::$FE_COL_TYPES[ 'COUNTRY' ] + ], + 'nocfax' => 'NOC Fax', + 'officephone' => 'Office Phone', + 'officefax' => 'Office Fax', + 'officeemail' => 'Office Email', + 'notes' => [ + 'title' => 'Notes', + 'type' => self::$FE_COL_TYPES[ 'PARSDOWN' ] + ], + 'created_at' => [ + 'title' => 'Created', + 'type' => self::$FE_COL_TYPES[ 'DATETIME' ] + ], + 'updated_at' => [ + 'title' => 'Updated', + 'type' => self::$FE_COL_TYPES[ 'DATETIME' ] + ] + ] + ); + } + + /** + * Provide array of rows for the list and view + * + * @param int|null $id The `id` of the row to load for `view`. `null` if `list` + * + * @return array + */ + #[\Override] + protected function listGetData( ?int $id = null ): array + { + $feParams = $this->feParams; + return Location::when( $id , function( Builder $q, $id ) { + return $q->where('id', $id ); + } )->when( $feParams->listOrderBy , function( Builder $q, $orderby ) use ( $feParams ) { + return $q->orderBy( $orderby, $feParams->listOrderByDir ?? 'ASC'); + })->get()->toArray(); + } + + /** + * Display the form to create an object + * + * @return (Location|array)[] + * + * @psalm-return array{object: Location, countries: array} + */ + #[\Override] + protected function createPrepareForm(): array + { + return [ + 'object' => $this->object, + 'countries' => Countries::getList('name' ) + ]; + } + + /** + * Display the form to edit an object + * + * @param int $id ID of the row to edit + * + * @return (array|mixed)[] + * + * @psalm-return array{object: mixed, countries: array} + */ + #[\Override] + protected function editPrepareForm( int $id ): array + { + $this->object = Location::findOrFail( $id ); + + Former::populate([ + 'name' => request()->old( 'name', $this->object->name ), + 'shortname' => request()->old( 'shortname', $this->object->shortname ), + 'tag' => request()->old( 'tag', $this->object->tag ), + 'address' => request()->old( 'address', $this->object->address ), + 'city' => request()->old( 'city', $this->object->city ), + 'country' => request()->old( 'country', in_array( $this->object->country, array_values( Countries::getListForSelect( 'iso_3166_2' ) ), false ) ? $this->object->country : null ), + 'nocphone' => request()->old( 'nocphone', $this->object->nocphone ), + 'nocfax' => request()->old( 'nocfax', $this->object->nocfax ), + 'nocemail' => request()->old( 'nocemail', $this->object->nocemail ), + 'officephone' => request()->old( 'officephone', $this->object->officephone ), + 'officefax' => request()->old( 'officefax', $this->object->officefax ), + 'officeemail' => request()->old( 'officeemail', $this->object->officeemail ), + 'notes' => request()->old( 'notes', $this->object->notes ), + ]); + + return [ + 'object' => $this->object, + 'countries' => Countries::getList('name' ) + ]; + } + + /** + * Function to do the actual validation and storing of the submitted object. + * + * @param Request $r + * + * @return true + */ + #[\Override] + public function doStore( Request $r ): bool|RedirectResponse + { + $this->checkForm( $r ); + $this->object = Location::create( $r->all() ); + return true; + } + + /** + * Function to do the actual validation and updating of the submitted object. + * + * @param Request $r + * @param int $id + * + * @return true + */ + #[\Override] + public function doUpdate( Request $r, int $id ): bool|RedirectResponse + { + $this->object = Location::findOrFail( $id ); + $this->checkForm( $r ); + $this->object->update( $r->all() ); + return true; + } + + /** + * @inheritdoc + */ + #[\Override] + protected function preDelete(): bool + { + if( $this->object->cabinets->count() ) { + AlertContainer::push( "Could not delete the Facility ({$this->object->name}) as at least one rack is located here. Reassign or delete the rack first.", Alert::DANGER ); + return false; + } + return true; + } + + /** + * Check if the form is valid + * + * @param Request $r + */ + #[\Override] + public function checkForm( Request $r ): void + { + $r->validate( [ + 'name' => 'required|string|max:255', + 'shortname' => 'required|string|max:255|unique:location,shortname' . ( $r->id ? ',' . $r->id : '' ), + 'tag' => 'required|string|max:255', + 'city' => 'required|string|max:50', + 'country' => 'required|string|max:2|in:' . implode( ',', array_values( Countries::getListForSelect( 'iso_3166_2' ) ) ), + 'nocemail' => 'nullable|email', + 'officeemail' => 'nullable|email', + ] ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/LogController.php b/app/Http/Controllers/LogController.php new file mode 100644 index 000000000..41252093e --- /dev/null +++ b/app/Http/Controllers/LogController.php @@ -0,0 +1,142 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ + +class LogController extends Controller +{ + /** + * Search in logs list + * + * @param Request $r + * + * @return Factory|\Illuminate\Contracts\View\View|View|Application + */ + public function search( Request $r ): Factory|\Illuminate\Contracts\View\View|View|Application + { + return $this->list( $r ); + } + /** + * Display the Logs list + * + * @param Request $r + */ + public function list( Request $r ): \Illuminate\Contracts\View\View|Factory|View|Application + { + $model_id = $r->model_id ?? null; + $created_at = $r->created_at ?? null; + + $models = Log::select( 'model' )->orderBy( 'model' ) + ->distinct()->get(); + + $r->merge([ + 'action' => $action = in_array( $r->action, Log::$ACTIONS , false ) ? $r->action : null, + 'user' => $user = User::where( 'username', $r->user )->exists() ? $r->user : null, + ]); + + if( !in_array( $r->model, array_values( $models->pluck( 'model' )->toArray() ) ) ) { + $models = $models->toArray(); + array_unshift( $models, [ 'model' => $r->model ] ); + } else { + $models = $models->toArray(); + } + + $logs = Log::selectRaw( 'log.*, u.username' ) + ->leftJoin( 'user AS u', 'u.id', 'log.user_id' ) + ->when( $r->model, function( Builder $q, $model ) { + return $q->where('log.model', 'like', $model ); + } )->when( $model_id, function( Builder $q, $model_id ) { + return $q->where('log.model_id', $model_id ); + } )->when( $user, function( Builder $q, $user ) { + return $q->where('u.username', $user ); + } )->when( $action, function( Builder $q, $action ) { + return $q->where('log.action', $action ); + } )->when( $created_at, function( Builder $q, $created_at ) { + return $q->where('log.created_at', 'like', $created_at . '%' ); + } )->orderByDesc( 'created_at' )->paginate( 20 ); + + // pagination: + if( $r->model ) { + $logs->appends( [ 'model' => $r->model ] ); + } + + if( $model_id ) { + $logs->appends( [ 'model_id' => $model_id ] ); + } + + if( $model_id ) { + $logs->appends( [ 'model_id' => $model_id ] ); + } + + if( $created_at ) { + $logs->appends( [ 'created_at' => $created_at ] ); + } + + if( $r->action ) { + $logs->appends( [ 'action' => $r->action ] ); + } + + return view( 'log/index' )->with([ + 'models' => $models, + 'model' => $r->model, + 'user' => $user, + 'users' => Log::select( [ 'user_id', 'username' ] ) + ->leftJoin( 'user AS u', 'u.id', 'log.user_id') + ->orderBy( 'username' ) + ->distinct()->get()->toArray(), + 'logs' => $logs, + ]); + } + + /** + * Display the log details + * + * @param Log $log the log + */ + public function view( Log $log ): \Illuminate\Contracts\View\View|Application + { + return view( 'log/view' )->with([ + 'log' => $log + ]); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/LoginHistoryController.php b/app/Http/Controllers/LoginHistoryController.php new file mode 100644 index 000000000..30d5e2e17 --- /dev/null +++ b/app/Http/Controllers/LoginHistoryController.php @@ -0,0 +1,181 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class LoginHistoryController extends EloquentController +{ + /** + * The object being created / edited + * + * @var UserLoginHistory + */ + protected $object = null; + + /** + * This function sets up the frontend controller + */ + #[\Override] + public function feInit(): void + { + /** @var User $us */ + $us = Auth::getUser(); + + $this->feParams = (object)[ + 'model' => UserLoginHistory::class, + 'pagetitle' => 'Login History', + 'titleSingular' => 'Login History', + 'nameSingular' => 'a Login History', + 'listOrderBy' => 'last_login_date', + 'listOrderByDir' => 'DESC', + 'readonly' => 'true', + 'viewFolderName' => 'login-history', + 'listColumns' => [ + 'username' => 'Username', + 'email' => 'Email', + 'cust_name' => [ + 'title' => 'Customer', + 'type' => self::$FE_COL_TYPES[ 'HAS_ONE' ], + 'controller' => 'customer', + 'action' => 'overview', + 'idField' => 'cust_id' + ], + 'last_login_via' => 'Via', + 'last_login_date' => [ + 'title' => 'Last Login', + 'type' => self::$FE_COL_TYPES[ 'DATETIME' ] + ] + ] + ]; + + // display the same information in the view as the list + $this->feParams->viewColumns = $this->feParams->listColumns; + + // phpunit / artisan trips up here without the cli test: + if( PHP_SAPI !== 'cli' ) { + // custom access controls: + switch( Auth::check() ? $us->privs() : User::AUTH_PUBLIC ) { + case User::AUTH_SUPERUSER: + break; + default: + $this->unauthorized(); + } + } + } + + /** + * @inheritdoc + */ + #[\Override] + public static function routes(): void + { + Route::group( [ 'prefix' => 'login-history' ], function() { + Route::get( 'list', 'LoginHistoryController@list' )->name( 'login-history@list' ); + Route::get( 'view/{id}', 'LoginHistoryController@view' )->name( 'login-history@view' ); + }); + } + + /** + * Provide array of rows for the list and view + * + * @param int|null $id The `id` of the row to load for `view`. `null` if `list` + * + * @return array + */ + #[\Override] + protected function listGetData( ?int $id = null ): array + { + $feParams = $this->feParams; + return CustomerToUser::select( [ + 'customer_to_users.last_login_date AS last_login_date', + 'customer_to_users.last_login_via AS last_login_via', + 'customer_to_users.id AS AS c2u_id', + 'user.id AS id', + 'user.username AS username', + 'user.email AS email', + 'cust.id AS cust_id', + 'cust.name AS cust_name' + ] ) + ->join( 'user', 'user.id', 'customer_to_users.user_id' ) + ->join( 'cust', 'cust.id', 'customer_to_users.customer_id' ) + ->when( $feParams->listOrderBy , function( Builder $q, $orderby ) use ( $feParams ) { + return $q->orderBy( $orderby, $feParams->listOrderByDir ?? 'ASC'); + })->get()->toArray(); + } + + /** + * Display the login history list for a user/customer + * + * @param Request $r + * @param int $id + * + * @return View + */ + #[\Override] + public function view( Request $r, int $id ): View + { + $u = User::findOrFail( $id ); + + $limit = $r->limit ?? 0; + return view( 'login-history/view' )->with( [ + 'user' => $u, + 'histories' => UserLoginHistory::select( [ 'user_logins.*', 'user.id AS user_id', 'cust.name AS cust_name' ] ) + ->leftJoin( 'customer_to_users', 'customer_to_users.id', 'user_logins.customer_to_user_id' ) + ->leftJoin( 'cust', 'cust.id', 'customer_to_users.customer_id' ) + ->leftJoin( 'user', 'user.id', 'customer_to_users.user_id' ) + ->when( $u->id , function( Builder $q, $userid ) { + return $q->where( 'user.id', $userid ); + }) + ->when( $limit > 0 , function( Builder $q ) use( $limit ) { + return $q->limit( $limit ); + }) + ->orderByDesc( 'at' ) + ->get()->toArray(), + ] ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/MacAddressController.php b/app/Http/Controllers/MacAddressController.php new file mode 100644 index 000000000..8bbb9c678 --- /dev/null +++ b/app/Http/Controllers/MacAddressController.php @@ -0,0 +1,132 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class MacAddressController extends EloquentController +{ + /** + * The object being created / edited + * + * @var MacAddress + */ + protected $object = null; + + /** + * Is this a read only controller? + * + * @var boolean + */ + public static $read_only = true; + + /** + * This function sets up the frontend controller + */ + #[\Override] + public function feInit(): void + { + $this->feParams = (object)[ + 'model' => MacAddress::class, + 'pagetitle' => 'Discovered MAC Addresses', + 'titleSingular' => 'MAC Address', + 'nameSingular' => 'a MAC address', + 'listOrderBy' => 'abbreviatedName', + 'listOrderByDir' => 'ASC', + 'viewFolderName' => 'mac-address', + 'readonly' => self::$read_only, + 'documentation' => 'https://docs.ixpmanager.org/latest/features/layer2-addresses/', + 'listColumns' => [ + 'customer' => 'Customer', + 'switchport' => 'Interface(s)', + 'ip4' => 'IPv4', + 'ip6' => 'IPv6', + 'mac' => 'MAC Address', + 'organisation' => 'Manufacturer' + ], + ]; + + // display the same information in the view as the list + $this->feParams->viewColumns = $this->feParams->listColumns; + } + + /** + * Provide array of rows for the list action and view action + * + * @param int|null $id The `id` of the row to load for `view` action`. `null` if `listAction` + + * @return array + */ + #[\Override] + protected function listGetData( ?int $id = null ): array + { + $feParams = $this->feParams; + return MacAddress::selectRaw( "m.mac, + ANY_VALUE(m.id) AS id, + m.virtualinterfaceid, + m.created_at, + m.updated_at, + vi.id AS viid, + c.id AS customerid, c.abbreviatedName AS customer, + s.name AS switchname, + GROUP_CONCAT( DISTINCT sp.name ) AS switchport, + GROUP_CONCAT( DISTINCT ipv4.address ) AS ip4, + GROUP_CONCAT( DISTINCT ipv6.address ) AS ip6, + COALESCE( o.organisation, 'Unknown' ) AS organisation" + ) + ->from( 'macaddress AS m' ) + ->join( 'virtualinterface AS vi', 'vi.id', 'm.virtualinterfaceid' ) + ->join( 'vlaninterface AS vli', 'vli.virtualinterfaceid', 'vi.id' ) + ->leftjoin( 'ipv4address AS ipv4', 'ipv4.id', 'vli.ipv4addressid' ) + ->leftjoin( 'ipv6address AS ipv6', 'ipv6.id', 'vli.ipv6addressid' ) + ->join( 'cust AS c', 'c.id', 'vi.custid' ) + ->leftjoin( 'physicalinterface AS pi', 'pi.virtualinterfaceid', 'vi.id' ) + ->leftjoin( 'switchport AS sp', 'sp.id', 'pi.switchportid' ) + ->leftjoin( 'switch AS s', 's.id', 'sp.switchid' ) + ->leftjoin( 'oui AS o', 'o.oui', '=', DB::raw("SUBSTRING( m.mac, 1, 6 )") ) + ->when( $id , function( Builder $q, $id ) { + return $q->where('m.id', $id ); + } )->groupBy( 'm.mac', 'vi.id', + 'c.id', 'c.abbreviatedName', 's.name', 'o.organisation' + ) + ->when( $feParams->listOrderBy , function( Builder $q, $orderby ) use ( $feParams ) { + return $q->orderBy( $orderby, $feParams->listOrderByDir ?? 'ASC'); + })->get()->toArray(); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/NetworkInfoController.php b/app/Http/Controllers/NetworkInfoController.php new file mode 100644 index 000000000..31f9ac4d6 --- /dev/null +++ b/app/Http/Controllers/NetworkInfoController.php @@ -0,0 +1,269 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Comtrollers + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class NetworkInfoController extends EloquentController +{ + /** + * The object being created / edited + * + * @var NetworkInfo + */ + protected $object = null; + + /** + * This function sets up the frontend controller + */ + #[\Override] + public function feInit(): void + { + $this->feParams = (object)[ + 'model' => NetworkInfo::class, + 'pagetitle' => 'Network Information', + 'titleSingular' => 'Network Information', + 'nameSingular' => 'Network Information', + 'listOrderBy' => 'vlan_id', + 'listOrderByDir' => 'ASC', + 'viewFolderName' => 'network-info', + 'listColumns' => [ + 'vlanname' => [ + 'title' => 'Vlan', + 'type' => self::$FE_COL_TYPES[ 'HAS_ONE' ], + 'controller' => 'vlan', + 'action' => 'view', + 'idField' => 'vlan_id' + ], + 'protocol' => [ + 'title' => 'Protocol', + 'type' => self::$FE_COL_TYPES[ 'CONST' ], + 'const' => Router::$PROTOCOLS, + ], + 'network' => 'Network', + 'masklen' => 'Masklen', + ], + ]; + // display the same information in the view as the list + $this->feParams->viewColumns = array_merge( + $this->feParams->listColumns, + [ + 'created_at' => [ + 'title' => 'Created', + 'type' => self::$FE_COL_TYPES[ 'DATETIME' ] + ], + 'updated_at' => [ + 'title' => 'Updated', + 'type' => self::$FE_COL_TYPES[ 'DATETIME' ] + ] + ] + ); + } + + /** + * Provide array of rows for the list action and view action + * + * @param int|null $id The `id` of the row to load for `view` action`. `null` if `listAction` + * + * @return array + * + * @throws + */ + #[\Override] + protected function listGetData( ?int $id = null ): array + { + $feParams = $this->feParams; + return NetworkInfo::select( [ 'networkinfo.*', 'vlan.id AS vlan_id', 'vlan.name AS vlanname' ] ) + ->leftJoin( 'vlan', 'vlan.id','networkinfo.vlanid' ) + ->when( $id , function( Builder $q, $id ) { + return $q->where( 'networkinfo.id', $id ); + }) + ->when( $feParams->listOrderBy , function( Builder $q, $orderby ) use ( $feParams ) { + return $q->orderBy( $orderby, $feParams->listOrderByDir ?? 'ASC'); + }) + ->get()->toArray(); + } + + /** + * Display the form to create an object + * + * @return (NetworkInfo|\Illuminate\Database\Eloquent\Collection)[] + * + * @psalm-return array{object: NetworkInfo, vlans: \Illuminate\Database\Eloquent\Collection} + */ + #[\Override] + protected function createPrepareForm(): array + { + return [ + 'object' => $this->object, + 'vlans' => Vlan::publicOnly()->orderBy('number')->get(), + ]; + } + + /** + * Display the form to edit an object + * + * @param int $id ID of the row to edit + * + * @return (\Illuminate\Database\Eloquent\Collection|mixed)[] + * + * @psalm-return array{object: mixed, vlans: \Illuminate\Database\Eloquent\Collection} + */ + #[\Override] + protected function editPrepareForm( int $id ): array + { + $this->object = NetworkInfo::findOrFail( $id ); + + Former::populate([ + 'vlanid' => request()->old( 'vlan', $this->object->vlanid ), + 'protocol' => request()->old( 'protocol', $this->object->protocol ), + 'network' => request()->old( 'network', $this->object->network ), + 'masklen' => request()->old( 'masklen', $this->object->masklen ), + ]); + + return [ + 'object' => $this->object, + 'vlans' => Vlan::publicOnly()->orderBy('number')->get(), + ]; + } + + /** + * Function to do the actual validation and storing of the submitted object. + * + * @param Request $r + * + * @return RedirectResponse|true + * + * @throws + */ + #[\Override] + public function doStore( Request $r ): bool|RedirectResponse + { + $this->checkForm( $r ); + + if( $this->checkIsDuplicate( $r, null ) ) { + return Redirect::back()->withInput(); + } + + $this->object = NetworkInfo::create( $r->all() ); + return true; + } + + /** + * Function to do the actual validation and updating of the submitted object. + * + * @param Request $r + * @param int $id + * + * @return RedirectResponse|true + * + * @throws + */ + #[\Override] + public function doUpdate( Request $r, int $id ): bool|RedirectResponse + { + $this->object = NetworkInfo::findOrFail( $id ); + $this->checkForm( $r ); + + if( $this->checkIsDuplicate( $r, $this->object->id ) ) { + return Redirect::back()->withInput(); + } + + $this->object->update( $r->all() ); + return true; + } + + /** + * Check if there is a duplicate network info object with those values + * + * @param Request $r + * @param int|null $objectid + * + * @return bool + */ + private function checkIsDuplicate( Request $r, ?int $objectid = null ): bool + { + $vlan = Vlan::find( $r->vlanid ); + + $exist = NetworkInfo::where( "vlanid" , $vlan->id ) + ->where( 'protocol' , $r->protocol ) + ->when( $objectid , function( Builder $q, $objectid ) { + return $q->where( 'id', '!=', $objectid ); + })->count(); + + if( $exist ) { + AlertContainer::push( "Network information for this vlan (" . $vlan->name . ") and and protocol (ipv" . $r->protocol . ") already exists. Please edit that instead." , Alert::DANGER ); + return true; + } + return false; + } + + /** + * Check if the form is valid + * + * @param Request $r + */ + #[\Override] + public function checkForm( Request $r ): void + { + $rangeMasklen = (int)$r->protocol === 4 ? 'min:16|max:29' : 'min:32|max:64'; + + $r->validate( [ + 'vlanid' => 'required|integer|exists:vlan,id', + 'protocol' => 'required|integer|in:' . implode( ',', array_keys( Router::$PROTOCOLS ) ), + 'network' => 'required|max:255|ipv' . $r->protocol , + 'masklen' => 'required|integer|' . $rangeMasklen , + ] ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/PatchPanel/PatchPanelController.php b/app/Http/Controllers/PatchPanel/PatchPanelController.php new file mode 100644 index 000000000..208ddf751 --- /dev/null +++ b/app/Http/Controllers/PatchPanel/PatchPanelController.php @@ -0,0 +1,285 @@ + + * @author Barry O'Donovan + * @category IXP + * @package IXP\Http\Controllers\PatchPanel + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class PatchPanelController extends Controller +{ + /** + * Display the patch panel list + * + * @param bool $active display active or inactive patch panels + * + * @return view + */ + public function index( bool $active = true ): View + { + return view( 'patch-panel/index' )->with([ + 'patchPanels' => PatchPanel::where( 'active', $active ) + ->with( 'cabinet', 'patchPanelPorts' )->get(), + 'locations' => Location::select( [ 'id', 'name' ] ) + ->orderBy( 'name' )->get(), + 'cabinets' => Cabinet::select( [ 'id', 'name', 'locationid' ] ) + ->orderBy( 'name' )->get()->toArray(), + 'active' => $active + ]); + } + + /** + * @return View + */ + public function indexInactive(): View + { + return $this->index( false ); + } + + /** + * Allow to display the form to create a patch panel + * + * @return View + */ + public function create(): View + { + return view( 'patch-panel/edit' )->with([ + 'pp' => false, + 'cabinets' => Cabinet::selectRaw( "id, concat( name, ' [', colocation, ']') AS name" ) + ->orderBy( 'name' )->get(), + ]); + } + + /** + * Allow to create a patch panel + * + * @param StorePatchPanel $r instance of the current HTTP request + * + * @return RedirectResponse + */ + public function store( StorePatchPanel $r ): RedirectResponse + { + $pp = PatchPanel::create( $r->merge( [ + 'location_notes' => clean( $r->location_notes ), + 'active' => true, + 'port_prefix' => $r->port_prefix ?? '', + ])->all() + ); + + // create the patch panel ports + $pp->createPorts( $r->numberOfPorts ); + return redirect( route( 'patch-panel-port@list-for-patch-panel', [ "pp" => $pp->id ] ) ); + } + + /** + * Allow to display the form to edit a patch panel + * + * @param Request $r + * @param PatchPanel $pp the patch panel + * + * @return View + */ + public function edit( Request $r, PatchPanel $pp ): View + { + Former::populate([ + 'cabinet_id' => $r->old( 'cabinet_id', (string)$pp->cabinet_id ), + 'name' => $r->old( 'name', $pp->name ), + 'colo_reference' => $r->old( 'colo_reference', $pp->colo_reference ), + 'cable_type' => $r->old( 'cable_type', (string)$pp->cable_type ), + 'connector_type' => $r->old( 'connector_type', (string)$pp->connector_type ), + 'installation_date' => $r->old( 'installation_date', $pp->installation_date ), + 'port_prefix' => $r->old( 'port_prefix', $pp->port_prefix ), + 'location_notes' => $r->old( 'location_notes', $pp->location_notes ), + 'u_position' => $r->old( 'u_position', (string)$pp->u_position ), + 'colo_pp_type' => $r->old( 'colo_pp_type', (string)$pp->colo_pp_type ), + 'mounted_at' => $r->old( 'mounted_at', (string)$pp->mounted_at ), + 'numberOfPorts' => $r->old( 'numberOfPorts','0' ), + ]); + + return view( 'patch-panel/edit' )->with([ + 'pp' => $pp, + 'cabinets' => Cabinet::selectRaw( "id, concat( name, ' [', colocation, ']') AS name" ) + ->orderBy( 'name' )->get(), + ]); + } + + /** + * Allow to update a patch panel + * + * @param StorePatchPanel $r + * @param PatchPanel $pp + * + * @return RedirectResponse + */ + public function update( StorePatchPanel $r, PatchPanel $pp): RedirectResponse + { + $r->merge( [ 'location_notes' => clean( $r->location_notes ) ]); + $pp->update( $r->all() ); + + // create the patch panel ports + $pp->createPorts( $r->numberOfPorts ); + return redirect( route( 'patch-panel-port@list-for-patch-panel', [ "pp" => $pp->id ] ) ); + } + + /** + * Change the status to active or inactive for a patch panel + * + * @param PatchPanel $pp + * @param int $active + * + * @return RedirectResponse + */ + public function changeStatus( PatchPanel $pp, int $active ): RedirectResponse + { + $status = $active ? 'active' : 'inactive'; + + if( $pp->patchPanelPorts()->count() === $pp->availableForUsePortCount() ) { + $pp->update( [ 'active' => (bool)$active ] ); + AlertContainer::push( 'The patch panel has been marked as ' . $status, Alert::SUCCESS ); + } else { + AlertContainer::push( 'To make a patch panel ' . $status . ', all ports must be available for use.', Alert::DANGER ); + } + + return redirect( route( "patch-panel@list" ) ); + } + + /** + * Display the patch panel details + * + * @param PatchPanel $pp the patch panel + * + * @return view + */ + public function view( PatchPanel $pp ): View + { + return view( 'patch-panel/view' )->with([ + 'pp' => $pp->load([ 'cabinet', 'patchPanelPorts' ] ) + ]); + } + + + + /** + * Allow to display the form to edit a patch panel + * + * @param Request $r + * @param PatchPanel $pp the patch panel + * + * @return View + */ + public function expunge( Request $r, PatchPanel $pp ): View + { + Former::populate([ + 'cabinet_id' => $r->old( 'cabinet_id', (string)$pp->cabinet_id ), + 'name' => $r->old( 'name', $pp->name ), + 'colo_reference' => $r->old( 'colo_reference', $pp->colo_reference ), + 'cable_type' => $r->old( 'cable_type', (string)$pp->cable_type ), + 'connector_type' => $r->old( 'connector_type', (string)$pp->connector_type ), + 'installation_date' => $r->old( 'installation_date', $pp->installation_date ), + 'port_prefix' => $r->old( 'port_prefix', $pp->port_prefix ), + 'location_notes' => $r->old( 'location_notes', $pp->location_notes ), + 'u_position' => $r->old( 'u_position', (string)$pp->u_position ), + 'colo_pp_type' => $r->old( 'colo_pp_type', (string)$pp->colo_pp_type ), + 'mounted_at' => $r->old( 'mounted_at', (string)$pp->mounted_at ), + 'numberOfPorts' => $r->old( 'numberOfPorts','0' ), + ]); + + return view( 'patch-panel/expunge' )->with([ + 'pp' => $pp, + 'cabinets' => Cabinet::selectRaw( "id, concat( name, ' [', colocation, ']') AS name" ) + ->orderBy( 'name' )->get(), + ]); + } + + + + /** + * Allow to display the form to edit a patch panel + * + * @param Request $r + * @param PatchPanel $pp the patch panel + * + * @return RedirectResponse + */ + public function doExpunge( Request $r, PatchPanel $pp ): RedirectResponse + { + PatchPanelPortHistory::whereIn( 'patch_panel_port_id', $pp->patchPanelPorts->pluck('id') ) + ->update( [ 'duplex_master_id' => null ] ); + + PatchPanelPort::where( 'patch_panel_id', $pp->id ) + ->update( [ 'duplex_master_id' => null ] ); + + + foreach( $pp->patchPanelPorts as $ppp ) { + + foreach( $ppp->patchPanelPortHistories as $ppph ) { + + foreach( $ppph->patchPanelPortHistoryFiles as $ppphf ) { + @unlink( $ppphf->path() ); + $ppphf->delete(); + } + + $ppph->delete(); + } + + foreach( $ppp->patchPanelPortFiles as $pppf ) { + @unlink( $pppf->path() ); + $pppf->delete(); + } + + $ppp->delete(); + } + + $pp->delete(); + + AlertContainer::push( 'The patch panel has been expunged.', Alert::SUCCESS ); + return redirect( route( "patch-panel@list-inactive" ) ); + } + +} \ No newline at end of file diff --git a/app/Http/Controllers/PatchPanel/Port/Common.php b/app/Http/Controllers/PatchPanel/Port/Common.php new file mode 100644 index 000000000..a22914e27 --- /dev/null +++ b/app/Http/Controllers/PatchPanel/Port/Common.php @@ -0,0 +1,60 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers\PatchPanel\Port + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +abstract class Common extends Controller +{ + /** + * Generate the LoA PDF + * + * @param PatchPanelPort $ppp + * + * @return (\Barryvdh\DomPDF\PDF|string)[] + * + * @psalm-return list{\Barryvdh\DomPDF\PDF, string} + */ + protected function createLoaPDF( PatchPanelPort $ppp ): array + { + $pdf = app('dompdf.wrapper'); + $pdf->loadView( 'patch-panel-port/loa', [ 'ppp' => $ppp ] ); + $pdfName = sprintf( "LoA-%s-%s.pdf", $ppp->circuitReference(), now()->format( 'Y-m-d' ) ); + return [ $pdf, $pdfName ]; + } +} \ No newline at end of file diff --git a/app/Http/Controllers/PatchPanel/Port/DangerActionsController.php b/app/Http/Controllers/PatchPanel/Port/DangerActionsController.php new file mode 100644 index 000000000..1f53db0d7 --- /dev/null +++ b/app/Http/Controllers/PatchPanel/Port/DangerActionsController.php @@ -0,0 +1,169 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers\PatchPanel\Port + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class DangerActionsController extends Controller +{ + /** + * Access to the form that allow to move the information of a port to an other port + * + * @param PatchPanelPort $ppp The patch panel port + * + * @return View + */ + public function moveForm( PatchPanelPort $ppp ): View + { + return view( 'patch-panel-port/move' )->with([ + 'ppp' => $ppp, + 'ppAvailable' => PatchPanel::select( [ 'pp.id', 'pp.name' ] ) + ->from( 'patch_panel AS pp' ) + ->leftJoin( 'cabinet AS cab', 'cab.id', 'pp.cabinet_id' ) + ->where( 'cab.locationid', $ppp->patchPanel->cabinet->locationid ) + ->where( 'pp.cable_type', $ppp->patchPanel->cable_type ) + ->orderBy( 'pp.id' )->get(), + ]); + } + + /** + * Move a patch panel port information to an other + * + * @param MovePatchPanelPortRequest $r instance of the current HTTP request + * @param PatchPanelPort $ppp + * + * @return RedirectResponse + * + * @throws GeneralException + */ + public function move( MovePatchPanelPortRequest $r, PatchPanelPort $ppp ): RedirectResponse + { + $newPort = PatchPanelPort::find( $r->port_id ); + + if( $ppp->duplexSlavePorts()->exists() ){ + if( $newPort->duplexSlavePorts()->exists() ){ + $slave = $newPort->duplexSlavePorts->first(); + } else { + $slave = PatchPanelPort::find( $r->slave_id ); + if( $slave->isDuplexPort() ){ + AlertContainer::push( 'The slave port is port of a duplex port. The slave port has to be a single port.', Alert::DANGER ); + return redirect( route( 'patch-panel-port@move-form', [ 'ppp' => $ppp->id ] ) ); + } + } + } + + if( $ppp->move( $newPort, $slave ?? null ) ) { + AlertContainer::push( 'Patch panel port moved.', Alert::SUCCESS ); + } else { + AlertContainer::push( 'Something went wrong!', Alert::DANGER ); + } + + return redirect( route('patch-panel-port@list-for-patch-panel' , [ 'pp' => $newPort->patch_panel_id ] ) ); + } + + /** + * Remove the linked port from the master and reset it as available. + * + * @param PatchPanelPort $ppp the patch panel **master** port from which to split the slave + * + * @return RedirectResponse + */ + public function split( PatchPanelPort $ppp ): RedirectResponse + { + if( !$ppp->duplexSlavePorts()->count() ) { + AlertContainer::push( 'This patch panel port does not have any slave port.', Alert::DANGER ); + return redirect( route( 'patch-panel-port@list-for-patch-panel', [ 'pp' => $ppp->patch_panel_id ] ) ); + } + + foreach( $ppp->duplexSlavePorts as $slave ){ + $ppp->update( [ + 'duplex_master_id' => null, + 'private_notes' => "### " . now()->format('Y-m-d') . " - ". Auth::getUser()->username ."\n\nThis port had a slave port: " + . $slave->patchPanel->port_prefix . $slave->number . " which was split by " . Auth::getUser()->username + . " on " . now()->format('Y-m-d') . ".\n\n" + . $ppp->private_notes + ]); + + $slave->reset(); + + $slave->update( [ + 'private_notes' => "### " . now()->format('Y-m-d') . " - ". Auth::getUser()->username ."\n\nThis port was a duplex slave port with " + . $ppp->patchPanel->port_prefix . $ppp->number . " and was split by " . Auth::getUser()->username + . " on " . now()->format('Y-m-d') . ".\n\n" + ] ); + } + + AlertContainer::push( 'Patch Panel port splited.', Alert::SUCCESS ); + return redirect( route( 'patch-panel-port@list-for-patch-panel', [ 'pp' => $ppp->patch_panel_id ] ) ); + } + + /** + * Delete a patch panel port + * + * If the patch panel port has a duplex port then it will delete both ports. + * Also deletes associated files and histories. + * + * @param PatchPanelPort $ppp the patch panel port to delete + * + * @return RedirectResponse + */ + public function delete( PatchPanelPort $ppp ): RedirectResponse + { + $ppp->remove(); + AlertContainer::push( 'Patch Panel port deleted.', Alert::SUCCESS ); + return redirect( route( 'patch-panel-port@list-for-patch-panel', [ 'pp' => $ppp->patch_panel_id ] ) ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/PatchPanel/Port/EmailController.php b/app/Http/Controllers/PatchPanel/Port/EmailController.php new file mode 100644 index 000000000..1c8e8a529 --- /dev/null +++ b/app/Http/Controllers/PatchPanel/Port/EmailController.php @@ -0,0 +1,162 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers\PatchPanel\Port + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class EmailController extends Common +{ + /** + * Display and fill the form to send an email to the customer + * + * @param PatchPanelPort $ppp patch panel port + * @param int $type Email type to send + * + * @return view + */ + public function email( PatchPanelPort $ppp, int $type ): View + { + $mailable = $this->setupEmailRoutes( $ppp, $type ); + + return view( 'patch-panel-port/email-form' )->with([ + 'ppp' => $ppp, + 'emailType' => $type, + 'body' => $mailable->getBody() + ]); + } + + /** + * Send an email to the customer (connected, ceased, info, loa PDF) + * + * @param EmailPatchPanelPortRequest $r + * @param PatchPanelPort $ppp patch panel port id + * @param int $type Email type to send + * + * @return RedirectResponse|View + */ + public function send( EmailPatchPanelPortRequest $r, PatchPanelPort $ppp, int $type ): RedirectResponse|View + { + $mailable = $this->setupEmailRoutes( $ppp, $type ); + + $mailable->prepareFromRequest( $r ); + $mailable->prepareBody( $r ); + + try { + $mailable->checkIfSendable(); + } catch( MailableException $e ) { + AlertContainer::push( $e->getMessage(), Alert::DANGER ); + + return view( 'patch-panel-port/email-form' )->with([ + 'ppp' => $ppp, + 'emailType' => $type, + 'mailable' => $mailable + ]); + } + + if( $type === PatchPanelPort::EMAIL_LOA || $r->loa ) { + /** @var PDF $pdf */ + [ $pdf, $pdfname ] = $this->createLoAPDF( $ppp ); + $mailable->attachData( $pdf->output(), $pdfname, [ + 'mime' => 'application/pdf' + ]); + } + + // should we also attach public files? + if( in_array( $type, [ PatchPanelPort::EMAIL_CEASE, PatchPanelPort::EMAIL_INFO ], true ) ) { + foreach( $ppp->patchPanelPortFilesPublic as $file ) { + $mailable->attach( storage_path() . '/files/' . $file->path(), [ + 'as' => $file->name, + 'mime' => $file->type + ]); + } + } + + Mail::send( $mailable ); + + AlertContainer::push( "Email sent.", Alert::SUCCESS ); + + return Redirect::to( route( 'patch-panel-port@list-for-patch-panel', [ "pp" => $ppp->patch_panel_id ] ) ); + } + + /** + * Setup / validation for composing and sending emails + * + * @param PatchPanelPort $ppp + * @param int $type Email type to send + * @param Email|null $mailable + * + * @return Email + */ + private function setupEmailRoutes( PatchPanelPort $ppp, int $type, Email $mailable = null ): Email + { + if( !array_key_exists( $type, PatchPanelPort::$EMAIL_CLASSES ) ) { + abort(404, 'Email type not found'); + } + + if( !$mailable ) { + $mailable = new PatchPanelPort::$EMAIL_CLASSES[ $type ]( $ppp ); + } + + Former::populate([ + 'email_to' => implode( ',', $mailable->getRecipientEmails('to') ), + 'email_subject' => $mailable->getSubject(), + 'email_bcc' => implode( ',', $mailable->getRecipientEmails('bcc') ) + ]); + + return $mailable; + } +} \ No newline at end of file diff --git a/app/Http/Controllers/PatchPanel/Port/FileController.php b/app/Http/Controllers/PatchPanel/Port/FileController.php new file mode 100644 index 000000000..4dddb3694 --- /dev/null +++ b/app/Http/Controllers/PatchPanel/Port/FileController.php @@ -0,0 +1,165 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers\PatchPanel\Port + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class FileController extends Controller +{ + /** + * Make a patch panel port file private or public + * + * @param PatchPanelPortFile $file patch panel port file ID + * + * @return JsonResponse + */ + public function togglePrivacy( PatchPanelPortFile $file ): JsonResponse + { + $file->update( [ 'is_private' => !$file->is_private ] ); + return response()->json( [ 'success' => true, 'isPrivate' => $file->is_private ] ); + } + + /** + * Delete a patch panel port file + * + * @param Request $r + * @param PatchPanelPortFile $file patch panel port file + * + * @return RedirectResponse|JsonResponse + * + * @throws Exception + */ + public function delete( Request $r, PatchPanelPortFile $file ): RedirectResponse|JsonResponse + { + $path = 'files/' . $file->path(); + + if( Storage::exists( $path ) && Storage::delete( $path ) ) { + $file->delete(); + $message = 'Patch Panel Port File deleted.'; $success = true; + } else { + $message = 'Patch Panel Port File could not be deleted.'; $success = false; + } + + if( (bool)$r->jsonResponse ) { + return response()->json( ['success' => true, 'message' => 'File deleted' ] ); + } + + AlertContainer::push( $message, $success ? Alert::SUCCESS : Alert::DANGER ); + return redirect()->to( route( 'patch-panel-port@view', [ 'ppp' => $file->patch_panel_port_id ] ) ); + } + + /** + * Upload a file to a patch panel port + * + * @param PatchPanelPort $ppp patch panel port + * @param Request $r instance of the current HTTP request + * + * @return JsonResponse + */ + public function upload( Request $r, PatchPanelPort $ppp ): JsonResponse + { + if( !( $file = $r->file( 'file' ) ) ) { + return response()->json( [ 'success' => false, 'message' => 'You need to upload a file.' ] ); + } + + $hash = hash('sha256', $ppp->id . '-' . $file->getClientOriginalName() ); + $path = "files/" . PatchPanelPortFile::UPLOAD_PATH . '/' . $hash[ 0 ] . '/' + . $hash[ 1 ] . '/' . $hash; + + if( Storage::exists( $path ) ) { + return response()->json( [ 'success' => false, 'message' => 'This port already has a file with the same name.' ] ); + } + + if( Storage::put( $path, fopen( $file->getRealPath(), 'rb+' ) ) ) { + $pppf = PatchPanelPortFile::create( [ + 'patch_panel_port_id' => $ppp->id, + 'name' => $file->getClientOriginalName(), + 'type' => Storage::mimeType( $path ), + 'uploaded_at' => now(), + 'uploaded_by' => Auth::getUser()->username, + 'size' => Storage::size( $path ), + 'storage_location' => $hash + ] ); + + return response()->json( [ 'success' => true, 'message' => 'File uploaded.', 'id' => $pppf->id ] ); + } + + return response()->json( [ 'success' => false, 'message' => 'Could not save file to storage location' ] ); + } + + /** + * Download files + * + * @param PatchPanelPortFile $file the Patch panel port file + * + * @return BinaryFileResponse + */ + public function download( PatchPanelPortFile $file ): BinaryFileResponse + { + $u = User::find( Auth::id() ); + if( !$u->isSuperUser() ) { + if( !$file->patchPanelPort->customer + || $file->patchPanelPort->customer_id !== $u->custid + || $file->is_private ) { + Log::alert($u->username . ' tried to access a PPP file with ID:' . $file->id . ' but does not have permission'); + abort(401); + } + } + + return response()->file( storage_path() . '/files/' . $file->path(), [ 'Content-Type' => $file->type ] ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/PatchPanel/Port/HistoryFileController.php b/app/Http/Controllers/PatchPanel/Port/HistoryFileController.php new file mode 100644 index 000000000..0acdf5ccf --- /dev/null +++ b/app/Http/Controllers/PatchPanel/Port/HistoryFileController.php @@ -0,0 +1,118 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers\PatchPanel\Port + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class HistoryFileController extends Controller +{ + /** + * Make a patch panel port file private or public + * + * @param PatchPanelPortHistoryFile $file patch panel port file ID + * + * @return JsonResponse + */ + public function togglePrivacy( PatchPanelPortHistoryFile $file ): JsonResponse + { + $file->update( [ 'is_private' => !$file->is_private ] ); + return response()->json( [ 'success' => true, 'isPrivate' => $file->is_private ] ); + } + + /** + * Delete a patch panel port history file + * + * @param PatchPanelPortHistoryFile $file patch panel port history file + * + * @return RedirectResponse + * + * @throws Exception + */ + public function delete( PatchPanelPortHistoryFile $file ): RedirectResponse + { + $path = 'files/' . $file->path(); + + if( Storage::exists( $path ) && Storage::delete( $path ) ) { + $file->delete(); + AlertContainer::push( 'Patch Panel Port File deleted.', Alert::SUCCESS ); + } else { + AlertContainer::push( 'Patch Panel Port File could not be deleted.', Alert::DANGER ); + } + + return redirect( route( 'patch-panel-port@view', [ 'ppp' => $file->patchPanelPortHistory->patch_panel_port_id ] ) . '#ppp-' . $file->patch_panel_port_history_id ); + } + + /** + * Download history files + * + * @param PatchPanelPortHistoryFile $file the Patch panel port file + * + * @return BinaryFileResponse + */ + public function download( PatchPanelPortHistoryFile $file ): BinaryFileResponse + { + $u = User::find( Auth::id() ); + if( !$u->isSuperUser() ) { + if( !$file->patchPanelPortHistory->cust_id + || $file->patchPanelPortHistory->cust_id !== $u->custid + || $file->is_private ) { + Log::alert($u->username . ' tried to access a PPP history file with ID:' . $file->id . ' but does not have permission'); + abort(401); + } + } + + return response()->file( storage_path() . '/files/' . $file->path(), [ 'Content-Type' => $file->type ] ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/PatchPanel/Port/LoaController.php b/app/Http/Controllers/PatchPanel/Port/LoaController.php new file mode 100644 index 000000000..17431eb16 --- /dev/null +++ b/app/Http/Controllers/PatchPanel/Port/LoaController.php @@ -0,0 +1,115 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers\PatchPanel\Port + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class LoaController extends Common +{ + /** + * Download a Letter of Authority file - LoA + * + * @param PatchPanelPort $ppp the patch panel port + * + * @return Response + */ + public function download( PatchPanelPort $ppp ): Response + { + $this->setupLoA( $ppp ); + [ $pdf, $pdfname ] = $this->createLoaPDF( $ppp ); + return $pdf->download( $pdfname ); + } + + /** + * View a Letter of Authority file - LoA + * + * @param PatchPanelPort $ppp the patch panel port + * + * @return Response + */ + public function view( PatchPanelPort $ppp ): Response + { + $this->setupLoA( $ppp ); + [ $pdf, $pdfname ] = $this->createLoaPDF( $ppp ); + return $pdf->stream( $pdfname ); + } + + /** + * Bootstrap LoA request + * + * @param PatchPanelPort $ppp + * + * @return void + */ + private function setupLoA( PatchPanelPort $ppp ): void + { + $u = User::find( Auth::id() ); + if( $ppp->customer_id !== $u->custid && !$u->isSuperUser() ) { + Log::alert($u->username . ' tried to create a PPP LoA for PPP:' . $ppp->id . ' but does not have permission'); + abort(401); + } + } + + /** + * Allow to access to the Loa with the patch panel port ID and the LoA code + * + * @param PatchPanelPort $ppp The patch panel port + * @param string $loaCode LoA Code + * + * @return View + */ + public function verify( PatchPanelPort $ppp, string $loaCode ): View + { + if( $ppp->loa_code !== $loaCode ) { + Log::alert( "Failed PPP LoA verification for port {$ppp->id} from {$_SERVER['REMOTE_ADDR']} - invalid LoA code presented" ); + } else if( !$ppp->stateAwaitingXConnect() ) { + Log::alert( "PPP LoA verification denied for port {$ppp->id} from {$_SERVER['REMOTE_ADDR']} - port status is not AwaitingXConnect" ); + } + return view( 'patch-panel-port/verify-loa' )->with([ + 'ppp' => $ppp, + 'loaCode' => $loaCode + ]); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/PatchPanel/Port/PortController.php b/app/Http/Controllers/PatchPanel/Port/PortController.php new file mode 100644 index 000000000..3a8585cf4 --- /dev/null +++ b/app/Http/Controllers/PatchPanel/Port/PortController.php @@ -0,0 +1,510 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers\PatchPanel\Port + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class PortController extends Controller +{ + /** + * Display all the patch panel ports (optionally limited to a specific patch panel) + * + * @param PatchPanel|null $pp Show all ports for a patch panel + * + * @return View + */ + public function index( PatchPanel $pp = null ): View + { + return view( 'patch-panel-port/index' )->with([ + 'patchPanelPorts' => PatchPanelPortAggregator::list( $pp->id ?? null ), + 'pp' => $pp ?: false, + ]); + } + + /** + * Display all the patch panel ports based on a search + * + * @param Request $r + * + * @return View + */ + public function advancedIndex( Request $r ): View + { + $location = is_numeric( $r->location ) ? (int)$r->location : 0; + $cabinet = is_numeric( $r->cabinet ) ? (int) $r->cabinet : 0; + $cabletype = is_numeric( $r->type ) ? (int) $r->type : 0; + $availableForUse = (bool) $r->available; + $prewiredOnly = (bool) $r->prewiredOnly; + + $summary = "Filtered for: "; + $summary .= $location ? Location::find( $location )->name : 'all locations'; + $summary .= ', ' . ( $cabinet ? Cabinet::find( $cabinet )->name : 'all cabinets' ); + $summary .= ', ' . ( $cabletype ? PatchPanel::$CABLE_TYPES[ $cabletype ] : 'all cable types' ); + $summary .= ( $availableForUse ? ', available for use' : '' ); + $summary .= ( $prewiredOnly ? ', prewired only' : '' ); + $summary .= '.'; + + return view( 'patch-panel-port/index' )->with([ + 'patchPanelPorts' => PatchPanelPortAggregator::list( + null, true, $location, $cabinet, $cabletype, $availableForUse, $prewiredOnly + ), + 'pp' => false, + 'summary' => $summary, + ]); + } + + /** + * Display the form to edit allocate a patch panel port + * + * @param PatchPanelPort $ppp patch panel port that need to be edited + * + * @return View + */ + public function editAllocate( PatchPanelPort $ppp ): View + { + return $this->edit( request(), $ppp, 'allocate' ); + } + + /** + * Display the form to edit prewired a patch panel port + * + * @param PatchPanelPort $ppp patch panel port that need to be edited + * + * @return View + */ + public function editPrewired( PatchPanelPort $ppp ): View + { + return $this->edit( request(), $ppp, 'prewired' ); + } + + /** + * Display the form to edit a patch panel port + * + * @param Request $r + * @param PatchPanelPort $ppp Patch panel port that need to be edited + * @param string|null $formType Which type of form to show + * + * @return View + */ + public function edit( Request $r, PatchPanelPort $ppp, string $formType = null ): View + { + // if this is a slave port in a duplex port, swap for the master: + $ppp = $ppp->duplexMasterPort ?? $ppp; + + switch ( $formType ) { + case 'allocate' : + $allocating = true; + $prewired = false; + $states = PatchPanelPort::$ALLOCATED_STATES_TEXT; + break; + case 'prewired' : + $allocating = false; + $prewired = true; + $states = [ PatchPanelPort::STATE_PREWIRED => PatchPanelPort::$STATES[ PatchPanelPort::STATE_PREWIRED ] ]; + break; + default : + $allocating = false; + $prewired = false; + $states = PatchPanelPort::$STATES; + break; + } + + // If we're allocating this port, set the chargable flag to the patch panel's default: + $chargeable = $allocating && $ppp->stateAvailable() ? $ppp->patchPanel->chargeable : $ppp->isChargeable(); + + if( $sp = $ppp->switchPort ) { + $switchPorts = SwitchPort::selectRaw( 'sp.name, sp.type, sp.id' ) + ->from( 'switchport AS sp' ) + ->leftJoin( 'switch AS s', 's.id', 'sp.switchid' ) + ->leftJoin( 'physicalinterface AS pi', 'pi.switchportid', 'sp.id' ) + ->where( 's.id', $sp->switchid ) + ->whereNull( 'pi.id' )->orWhere( 'sp.id', $sp->id ) + ->orderBy( 'sp.id' )->get()->keyBy( 'id' )->toArray(); + } + + $duplexSlaveId = $ppp->duplexSlavePorts()->exists() ? $ppp->duplexSlavePorts()->first()->id : null; + // fill the form with patch panel port data + Former::populate( [ + 'switch_port_id' => $r->old( 'switch_port_id', (string)$ppp->switch_port_id ), + 'patch_panel' => $ppp->patchPanel->name, + 'customer_id' => $r->old( 'customer_id', (string)$ppp->customer_id ), + 'state' => $r->old( 'state', (string)$ppp->state ), + 'notes' => $r->old( 'notes', $ppp->notes ), + 'assigned_at' => $r->old( 'assigned_at', $ppp->assigned_at ), + 'connected_at' => $r->old( 'connected_at', $ppp->connected_at ), + 'cease_requested_at' => $r->old( 'cease_requested_at', $ppp->cease_requested_at ), + 'ceased_at' => $r->old( 'ceased_at', $ppp->ceased_at ), + 'last_state_change' => $r->old( 'last_state_change', $ppp->last_state_change ), + 'chargeable' => $r->old( 'chargeable', $chargeable ), + 'number' => $ppp->number, + 'colo_circuit_ref' => $r->old( 'colo_circuit_ref', $ppp->colo_circuit_ref ), + 'ticket_ref' => $r->old( 'ticket_ref', $ppp->ticket_ref ), + 'private_notes' => $r->old( 'private_notes', $ppp->private_notes ), + 'owned_by' => $r->old( 'owned_by', (string)$ppp->owned_by ), + 'description' => $r->old( 'description', $ppp->description ), + 'colo_billing_ref' => $r->old( 'colo_billing_ref', $ppp->colo_billing_ref ), + 'cabinet_name' => $ppp->patchPanel->cabinet->name, + 'colocation_centre' => $ppp->patchPanel->cabinet->location->name, + 'switch' => $r->old( 'switch', $ppp->switchPort->switchid ?? null ), + 'partner_port' => $r->old( 'partner_port', $duplexSlaveId ), + ]); + + return view( 'patch-panel-port/edit' )->with([ + 'states' => $states, + 'customers' => CustomerAggregator::reformatNameWithDetail( Customer::trafficking()->orderBy( 'name' )->get() ), + 'switches' => Switcher::select( [ 'switch.id', 'switch.name' ] ) + ->leftJoin( 'cabinet AS cab', 'cab.id', 'switch.cabinetid' ) + ->where( 'active', true ) + ->where( 'cab.locationid', $ppp->patchPanel->cabinet->locationid ) + ->orderBy( 'name' )->get()->keyBy( 'id' ), + 'switchPorts' => $switchPorts ?? [], + 'ppp' => $ppp, + 'partnerPorts' => PatchPanelPortAggregator::getAvailablePorts( $ppp->patch_panel_id, [ $ppp->id ], $duplexSlaveId ), + 'hasDuplex' => (bool) $duplexSlaveId, + 'allocating' => $allocating, + 'prewired' => $prewired, + ]); + } + + /** + * Update a patch panel port + * + * @param StorePatchPanelPortRequest $r instance of the current HTTP request + * @param PatchPanelPort $ppp + * + * @return RedirectResponse + * + * @throws + */ + public function update( StorePatchPanelPortRequest $r, PatchPanelPort $ppp ): RedirectResponse + { + if( $r->switch_port_id ) { + $sp = SwitchPort::findOrFail( $r->switch_port_id ); + + if( $sp->id !== $ppp->switch_port_id ){ + // check if the switch port is available + if( PatchPanelPort::where( 'switch_port_id', $sp->id )->doesntExist() ){ + $ppp->update( [ 'switch_port_id' => $sp->id ] ); + } else { + AlertContainer::push( 'The switch port selected is already used by an other patch panel port.', Alert::DANGER ); + return redirect()->back()->withInput( $r->all() ); + } + } + + if( $r->customer_id ) { + // check if the switch port can be linked to the customer + $cid = SwitchPort::select( 'vi.custid' ) + ->from( 'switchport AS sp' ) + ->leftJoin( 'physicalinterface AS pi', 'pi.switchportid', 'sp.id' ) + ->leftJoin( 'virtualinterface AS vi', 'vi.id', 'pi.virtualinterfaceid' ) + ->where( 'sp.id', $sp->id )->get()->pluck( 'custid' )->first(); + + if( $cid !== null && $cid !== (int)$r->customer_id ) { + AlertContainer::push( 'The selected customer does not have a relationship with the switch port', Alert::DANGER ); + return redirect()->back()->withInput( $r->all() ); + } + } + } else { + if( $r->customer_id && $r->switch ) { + AlertContainer::push( 'You need to select a switch port when a switch is selected', Alert::DANGER ); + return redirect()->back()->withInput( $r->all() ); + } + $ppp->update( [ 'switch_port_id' => null ] ); + } + + if( $ppp->state !== (int)$r->state ) { + $ppp->update( [ + 'state' => $r->state, + 'last_state_change' => now() + ] ); + } + + if( ( $r->customer_id && !$r->assigned_at ) || $r->allocated ) { + $assigned_at = now(); + } else { + $assigned_at = $r->assigned_at; + } + + if( (int)$r->state === PatchPanelPort::STATE_CONNECTED && !$r->connected_at ) { + $connected_at = now(); + } else { + $connected_at = $r->connected_at; + } + + if( (int)$r->state === PatchPanelPort::STATE_AWAITING_CEASE && !$r->cease_requested_at ) { + $cease_requested_at = now(); + } else { + $cease_requested_at = $r->cease_requested_at; + } + + if( (int)$r->state === PatchPanelPort::STATE_CEASED && !$r->ceased_at ) { + $ceased_at = now(); + } else { + $ceased_at = $r->ceased_at; + } + + $loa_code = $ppp->loa_code; + if( $r->customer_id && !$ppp->loa_code ) { + $loa_code = Str::random(25); + } else if( !$r->customer_id ) { + $loa_code = ''; + } + + $ppp->update([ + 'customer_id' => $r->customer_id, + 'notes' => clean( $r->notes ), + 'assigned_at' => $assigned_at, + 'connected_at' => $connected_at, + 'cease_requested_at' => $cease_requested_at, + 'ceased_at' => $ceased_at, + 'internal_use' => (bool)$r->internal_use, + 'chargeable' => $r->chargeable, + 'colo_circuit_ref' => $r->colo_circuit_ref, + 'ticket_ref' => $r->ticket_ref, + 'private_notes' => clean( $r->private_notes ), + 'owned_by' => $r->owned_by, + 'loa_code' => $loa_code, + 'description' => clean( $r->description ), + 'colo_billing_ref' => $r->colo_billing_ref, + ]); + + if( $r->duplex ) { + $partnerPort = PatchPanelPort::find( $r->partner_port ); + + if( $ppp->id !== $partnerPort->duplex_master_id ) { + foreach( $ppp->duplexSlavePorts as $slave ) { + $slave->reset(); + } + $partnerPort->update( [ 'duplex_master_id' => $ppp->id ] ); + } + + } else { + // if ppp has a slave port and duplex port is uncheck => unlink the slave port and reset it + foreach( $ppp->duplexSlavePorts as $slave ){ + $slave->reset(); + } + } + + // create a history and reset the patch panel port + if( $ppp->stateCeased() ) { + $ppp->archive(); + $ppp->reset(); + } + + // set physical interface status if available + if( $r->allocated && $r->pi_status && $r->pi_status > 0 + && $ppp->switchPort && $ppp->switchPort->physicalInterface ) { + $ppp->switchPort->physicalInterface->update( [ 'status' => $r->pi_status ] ); + } + + return redirect( route( 'patch-panel-port@list-for-patch-panel', [ "pp" => $ppp->patch_panel_id ] ) ); + } + + /** + * Display the patch panel port and the patch panel for history (if any). + * + * @param PatchPanelPort $ppp the patch panel + * + * @return View + */ + public function view( PatchPanelPort $ppp ): View + { + /** @var User $us */ + $us = Auth::getUser(); + + $listHistory[] = $ppp->load( [ 'patchPanel', 'duplexSlavePorts', + 'switchPort', 'customer' + ] ); + + if( !$us->isSuperUser() ) { + if( !$ppp->customer || $ppp->customer_id !== $us->custid ) { + abort(404); + } + } else { + // only load history if we're a super user + // get the patch panel port histories + foreach( $ppp->patchPanelPortHistories as $history ){ + $listHistory[] = $history->load( [ + 'patchPanelPort.patchPanel' + ] ); + } + } + + return view( 'patch-panel-port/view' )->with([ + 'ppp' => $ppp, + 'listHistory' => $listHistory, + ]); + } + + /** + * Set the public and private notes of a patch panel + * + * @param Request $r instance of the current HTTP request + * @param PatchPanelPort $ppp The patch panel port to query + * + * @return JsonResponse JSON customer object + * + * @throws + */ + public function setNotes( Request $r, PatchPanelPort $ppp ) : JsonResponse + { + $ppp->update( + [ + 'notes' => clean( $r->notes ), + 'private_notes' => clean( $r->private_notes ), + ] + ); + + // we may also pass a new state for a physical interface with this request + // (because we call this function from set connected / set ceased / etc) + if( $r->pi_status && $ppp->switchPort && ( $pi = $ppp->switchPort->physicalInterface ) ) { + $pi->update( [ 'status' => $r->pi_status ] ); + } + + if( $r->colo_circuit_ref ) { + $ppp->update( [ 'colo_circuit_ref' => clean( $r->colo_circuit_ref ) ] ); + } + + return response()->json( [ 'success' => true ] ); + } + + /** + * Change the status of a patch panel port and set the date value related to the status + * + * @param PatchPanelPort $ppp + * @param int $status + * + * @return RedirectResponse + * + * @throws + */ + public function changeStatus( PatchPanelPort $ppp, int $status ): RedirectResponse + { + if( !array_key_exists( $status, PatchPanelPort::$STATES ) ){ + AlertContainer::push( 'The status is invalid.', Alert::DANGER ); + return redirect::back(); + } + + switch ( $status ) { + case PatchPanelPort::STATE_AVAILABLE: + if( $ppp->statePrewired() ) { + // we get here via 'Unset Prewired' + if( $ppp->isDuplexPort() ) { + foreach( $ppp->duplexSlavePorts as $slave ){ + $slave->update( [ + 'state' => PatchPanelPort::STATE_AVAILABLE, + 'duplex_master_id' => null + ] ); + } + } + $ppp->update( [ 'switch_port_id' => null ] ); + } + $ppp->update( ['state' => PatchPanelPort::STATE_AVAILABLE ] ); + break; + case PatchPanelPort::STATE_CONNECTED : + $ppp->update( [ + 'state' => PatchPanelPort::STATE_CONNECTED, + 'connected_at' => now(), + ] ); + break; + case PatchPanelPort::STATE_AWAITING_CEASE : + $ppp->update( [ + 'state' => PatchPanelPort::STATE_AWAITING_CEASE, + 'cease_requested_at' => now(), + ] ); + break; + case PatchPanelPort::STATE_CEASED : + $ppp->update( [ + 'state' => PatchPanelPort::STATE_CEASED, + 'ceased_at' => now(), + ] ); + break; + default: + $ppp->update( [ + 'state' => $status, + ] ); + break; + } + + $ppp->update( [ + 'last_state_change' => now(), + ] ); + + if( $status === PatchPanelPort::STATE_CEASED ) { + if( $sp = $ppp->switchPort ) { + AlertContainer::push( 'The patch panel port has been set to available again. Consider ' + . 'setting it as prewired if the cable is still in place. It was connected to ' + . $sp->switcher->name . ' :: ' . $sp->name, + Alert::SUCCESS ); + } + + // create a history and reset the patch panel port + if( $ppp->stateCeased() ) { + $ppp->archive(); + $ppp->reset(); + } + } + + AlertContainer::push( 'The patch panel port has been set to: ' . $ppp->states(), Alert::SUCCESS ); + return redirect::back(); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/PeeringManagerController.php b/app/Http/Controllers/PeeringManagerController.php new file mode 100644 index 000000000..5775533e1 --- /dev/null +++ b/app/Http/Controllers/PeeringManagerController.php @@ -0,0 +1,317 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class PeeringManagerController extends Controller +{ + /** + * Display dashboard + * + * @return View|RedirectResponse + * + * @throws + */ + public function index(): View|RedirectResponse + { + if( config( 'ixp_fe.frontend.disabled.peering-manager', false ) ) { + AlertContainer::push( 'The peering manager has been disabled.', Alert::DANGER ); + return Redirect::to(''); + } + + $protos = [ 4, 6 ]; + $c = Customer::find( Auth::getUser()->custid ); + $vlans = Vlan::peeringManager()->orderBy( 'number' )->get(); + /** @psalm-suppress InvalidArgument */ + $peers = CustomerAggregator::getPeeringManagerArrayByType( $c , $vlans, $protos ) ?? []; + + if( !count( $peers ) ) { + AlertContainer::push( 'No peers have been found for the peering manager. Please see these instructions' + . ' / ensure your database is populating with peering information.', Alert::DANGER ); + return redirect( '' ); + } + + return view( 'peering-manager/index' )->with([ + 'bilat' => $peers[ "bilat" ], + 'vlans' => $vlans, + 'protos' => $protos, + 'peers' => $peers[ "peers" ], + 'me' => $peers[ "me" ], + 'c' => $c, + 'custs' => $peers[ "custs" ], + 'potential' => $peers[ "potential" ], + 'potential_bilat' => $peers[ "potential_bilat" ], + 'peered' => $peers[ "peered" ], + 'rejected' => $peers[ "rejected" ], + ]); + } + + /** + * Display the form to send email + * + * @param Request $r instance of the current HTTP request + * + * @return JsonResponse + * + * @throws + */ + public function formEmailFrag( Request $r ): JsonResponse + { + $success = true; + $pp = $peer = $peeringManager = false; + + if( !( $peer = Customer::find( $r->peerid ) ) ){ + $success = false; + } elseif( $r->form === "email" ) { + // potential peerings + $pp = []; + $count = 0; + $cust = Customer::find( Auth::getUser()->custid ); + + foreach( $cust->virtualInterfaces as $myvis ) { + foreach( $myvis->vlanInterfaces as $vli ) { + // does b member have one (or more than one)? + foreach( $peer->virtualInterfaces as $pvis ) { + foreach( $pvis->vlanInterfaces as $pvli ) { + if( $vli->vlan->id === $pvli->vlan->id ) { + $pp[ $count ][ 'my' ] = $vli; + $pp[ $count ][ 'your' ] = $pvli; + $count++; + } + } + } + } + } + + Former::populate( [ + 'to' => $peer->peeringemail, + 'cc' => $cust->peeringemail, + 'bcc' => User::find( Auth::id() )->email, + 'subject' => "[" . config( "identity.orgname" ) . "] Peering Request from " . $cust->name . " (ASN" . $cust->autsys . ")", + ] ); + } else { + $peeringManager = $this->loadPeeringManager( Customer::find( Auth::getUser()->custid ), $peer ); + } + + $returnHTML = view('peering-manager/form-email')->with([ + 'peer' => $peer, + 'pp' => $pp, + 'peeringManager' => $peeringManager, + 'form' => $r->form ?? "email", + ])->render(); + + return response()->json( ['success' => $success, 'htmlFrag' => $returnHTML ] ); + } + + /** + * @param PeeringManagerRequest $r + * + * @return JsonResponse + * + * @throws + */ + public function sendPeeringEmail( PeeringManagerRequest $r ) : JsonResponse + { + $peer = Customer::findOrFail( $r->peerid ); + $mailable = new RequestPeeringManager( $peer, $r ); + $marksent = $r->marksent; + $sendtome = $r->sendtome; + $user = User::find( Auth::id() ); + $cust = Customer::find( Auth::getUser()->custid ); + + try { + if( !$marksent ){ + $mailable->checkIfSendable( $sendtome ); + } + + } catch( Exception $e ) { + return response()->json( [ 'error' => true, "message" => $e->getMessage() ] ); + } + + if( !$marksent ) { + Mail::send( $mailable ); + } + + if( !$sendtome ) { + // get this customer/peer peering manager table entry + $pm = $this->loadPeeringManager( $cust , $peer ); + + if( !( config( "ixp.peering_manager.testmode" ) ) || config( "ixp.peering_manager.testdate" ) ) { + $pm->email_last_sent = now(); + ++$pm->emails_sent; + } + + if( !( config( "ixp.peering_manager.testmode" ) ) || config( "ixp.peering_manager.testnote" ) ) { + $pm->notes = '### ' . date( 'Y-m-d' ) . " - " . $user->username . "\n\nPeering request " . ( $r->marksent ? 'marked ' : '' ) . "sent\n\n" . $pm->notes; + } + + $pm->save(); + } + + if( $sendtome ){ + $message = "Peering request sample sent to your own email address (" . $user->email . ")."; + } else if( $marksent ) { + $message = "Peering request marked as sent in your Peering Manager."; + } else { + $message = "Peering request sent to ". $peer->name . " Peering Team."; + } + + return response()->json( [ 'error' => false, "message" => $message ] ); + } + + /** + * Set a note for a dedicated peering manager + * + * @param Request $r + * + * @return JsonResponse + * + * @throws + */ + public function peeringNotes( Request $r ): JsonResponse + { + $peer = Customer::findOrFail( $r->peerid ); + $cust = Customer::find( Auth::getUser()->custid ); + $pm = $this->loadPeeringManager( $cust , $peer ); + + if( trim( stripslashes( $r->notes ) ) ) { + $pm->notes = trim( stripslashes( $r->notes ) ); + $pm->save(); + } + return response()->json( [ 'error' => false, "message" => "Peering notes updated for " . $peer->name ] ); + } + + /** + * Mark the peering manager as "peered" or "rejected" + * + * @param integer $custid + * @param string $status + * + * @return RedirectResponse + * + * @throws + */ + public function markPeering( int $custid, string $status ): RedirectResponse + { + $peer = Customer::findOrFail( $custid ); + $pm = $this->loadPeeringManager( Customer::find( Auth::getUser()->custid ), $peer ); + + if( $status === "peered" ) { + $pm->peered = $pm->peered === 0 ? 1 : 0; + if( $pm->peered && $pm->rejected ){ + $pm->rejected = false; + } + } else{ + $pm->rejected = $pm->rejected === 0 ? 1 : 0; + if( $pm->peered && $pm->rejected ){ + $pm->peered = false; + } + } + + $pm->save(); + + if( $status === "peered" ) { + AlertContainer::push( "Peered flag " . ( $pm->peered ? 'set' : 'cleared' ) . " for " . $peer->name . "." , Alert::SUCCESS ); + } else { + AlertContainer::push( "Ignored / rejected flag " . ( $pm->rejected ? 'set' : 'cleared' ) . " for " . $peer->name . "." , Alert::SUCCESS ); + } + + return Redirect::to( route( "peering-manager@index" ) ); + } + + /** + * Utility function to load a PeeringManager entity and initialise one if not found + * + * @param Customer $cust + * @param Customer $peer + * + * @return PeeringManager + * + * @throws + */ + private function loadPeeringManager( Customer $cust, Customer $peer ): PeeringManager + { + $pm = PeeringManager::where( 'custid' , $cust->id )->where( 'peerid' , $peer->id )->first(); + + if( !$pm ){ + $pm = PeeringManager::create( + [ + 'note' => '', + 'peered' => false, + 'rejected' => false, + ] + ); + + $pm->custid = $cust->id; + $pm->peerid = $peer->id; + $pm->save(); + return $pm; + } + + return $pm; + } +} \ No newline at end of file diff --git a/app/Http/Controllers/PeeringMatrixController.php b/app/Http/Controllers/PeeringMatrixController.php new file mode 100644 index 000000000..02f0545a6 --- /dev/null +++ b/app/Http/Controllers/PeeringMatrixController.php @@ -0,0 +1,276 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class PeeringMatrixController extends Controller +{ + /** + * Display dashboard + * + * @param Request $r + * + * @return View|RedirectResponse + * + * @throws + */ + public function index( Request $r ): View|RedirectResponse + { + if( config( 'ixp_fe.frontend.disabled.peering-matrix', false ) ) { + AlertContainer::push( 'The peering matrix has been disabled.', Alert::DANGER ); + return redirect(''); + } + + if( !ixp_min_auth( config( 'ixp.peering-matrix.min-auth' ) ) ) { + AlertContainer::push( 'You do not have the required privileges to access the peering matrix', Alert::DANGER ); + return redirect(''); + } + + $protos = [ + 4 => 'IPv4', + 6 => 'IPv6' + ]; + + if( $r->vlan !== null ) { + if( $vlan = Vlan::find( $r->vlan ) ) { + $vl = $vlan->id; + $r->session()->put( "peering-matrix-vlan", $vl ); + } else { + $r->session()->remove( "peering-matrix-vlan" ); + $vl = false; + } + } else if( $r->session()->exists( "peering-matrix-vlan" ) ) { + $vl = $r->session()->get( "peering-matrix-vlan" ); + } else { + $vl = Vlan::where( 'peering_matrix', 1 )->first(); + + if( !$vl ) { + AlertContainer::push( 'There are no VLANs configured to be included in the peering matrix.', Alert::DANGER ); + return redirect(''); + } + + $vl = $vl->id; + } + + if( $r->proto !== null ) { + if( array_key_exists( $r->proto , $protos ) ) { + $proto = $r->proto; + $r->session()->put( "peering-matrix-proto", $proto ); + } else { + $r->session()->remove( "peering-matrix-proto" ); + $proto = 4; + } + } else if( $r->session()->exists( "peering-matrix-proto" ) ) { + $proto = $r->session()->get( "peering-matrix-proto" ); + } else { + $proto = 4; + } + + // Find all VLANs marked for inclusion in the peering matrices. + $vlans = Vlan::select( [ 'id', 'name' ] ) + ->where( 'peering_matrix', 1 ) + ->orderBy( 'number' )->get() + ->keyBy( 'id' )->toArray(); + + if( !count( $vlans ) ) { + AlertContainer::push( 'No VLANs have been enabled for the peering matrix. Please see these instructions' + . ' / contact our support team.', Alert::DANGER ); + return redirect( ''); + } + + + if( !isset( $vlans[ $vl ] ) ) { + AlertContainer::push( 'There are no VLANs set for the peering matrix. Please ' + . 'edit your VLAN(s) and set the "Peering Matrix" option to "Yes" for at least one of them.', + Alert::DANGER ); + + return redirect( ''); + } + + /** @var User $us */ + $us = Auth::getUser(); + $restrictActivePeeringMatrix = true; + if( Auth::check() && $us->isSuperUser() ){ + $restrictActivePeeringMatrix = false; + } + + //Return all active, trafficking and external customers on a given VLAN for a given protocol + //(indexed by ASN) + $cust = Vlan::select( [ 'cust.autsys', 'cust.name', 'cust.shortname', 'vli.rsclient', 'cust.activepeeringmatrix', 'cust.id' ] ) + ->leftJoin( 'vlaninterface AS vli', 'vli.vlanid', 'vlan.id' ) + ->leftJoin( 'virtualinterface AS vi', 'vi.id', 'vli.virtualinterfaceid' ) + ->leftJoin( 'cust', 'cust.id', 'vi.custid' ) + ->whereRaw( Customer::SQL_CUST_CURRENT )->whereRaw( Customer::SQL_CUST_TRAFFICING ) + ->whereRaw( Customer::SQL_CUST_EXTERNAL )->where( 'vlan.id', $vl ) + ->where( "vli.ipv{$proto}enabled", 1 )->orderBy( 'cust.autsys' ) + ->when( $restrictActivePeeringMatrix, function( Builder $q ) { + return $q->where( 'cust.activepeeringmatrix', 1 ); + } ) + ->get()->keyBy( 'autsys' )->toArray(); + + $asns = array_keys( $cust ); + + return view( 'peering-matrix/index' )->with([ + 'sessions' => $this->getPeers( $vl, $proto, $restrictActivePeeringMatrix ), + 'custs' => $cust, + 'vlans' => $vlans, + 'protos' => $protos, + 'proto' => $proto, + 'vl' => $vl, + 'asnStringFormat' => count($asns) > 0 ? "% " . strlen( $asns[ count( $asns ) - 1 ] ) . "s" : "% 0s", + ]); + } + + /** + * Get all the BGP peers of all peers + * + * This function is for generating the peering matrix based on data contained in the + * `bgpsession` table which is updated based on detected BGP sessions between + * routers on the peering LAN(s) from sflow data. + * + * It returns an array of all BGP peers show their peers, such as: + * + * array(57) { + * [42] => array(3) { + * ["shortname"] => string(10) "pchanycast" + * ["name"] => string(25) "Packet Clearing House DNS" + * ["peers"] => array(17) { + * [2110] => string(4) "2110" + * [2128] => string(4) "2128" + * ... + * } + * } + * [112] => array(3) { + * ["shortname"] => string(5) "as112" + * ["name"] => string(17) "AS112 Reverse DNS" + * ["peers"] => array(20) { + * [1213] => string(4) "1213" + * [2110] => string(4) "2110" + * ... + * } + * } + * ... + * } + * + * It also caches the results on a per VLAN, per protocol basis. + * + * @param int $vlan The VLAN ID of the peering LAN to query + * @param int $protocol The IP protocol to query (4 or 6) + * @param bool $restrictActivePeeringMatrix + * + * @return array Array of peerings (as described above) + * + */ + private function getPeers( int $vlan, int $protocol = 6, bool $restrictActivePeeringMatrix = true ): array + { + $key = "pm_sessions_{$vlan}_{$protocol}"; + + if( $apeers = Cache::get( $key ) ) { + return $apeers; + } + + // the number of days back we look is not a perfect science. generally, the bigger the interface / more + // traffic, the less likely we'll find bgp sessions recently via sflow sampling. + // 28 days seems good in practice but his can be changed in config/ixp.php + $lookback_days = (int)config( 'ixp.peering-matrix.lookback_days' ); + if( !is_int( $lookback_days ) || $lookback_days < 1 ) { + $lookback_days = 30; + } + + // we've added "bs.timestamp >= NOW() - INTERVAL 7 DAY" below as we don't + // dump old date (yet) and the time to run the query is O(n) on number + // of rows... + $peers = BgpSession::selectRaw( + 'cs.shortname AS csshortname, + cs.name AS csname, + cs.autsys AS csautsys, + cs.activepeeringmatrix AS csactivepeeringmatrix, + cd.autsys AS cdautsys' + )->from( 'bgp_sessions AS bs' ) + ->leftJoin( "ipv{$protocol}address AS srcip", 'srcip.id', 'bs.srcipaddressid' ) + ->leftJoin( "ipv{$protocol}address AS dstip", 'dstip.id', 'bs.dstipaddressid' ) + ->leftJoin( 'vlaninterface AS vlis', "vlis.ipv{$protocol}addressid", 'srcip.id' ) + ->leftJoin( 'vlaninterface AS vlid', "vlid.ipv{$protocol}addressid", 'dstip.id' ) + ->leftJoin( 'virtualinterface AS vis', 'vis.id', 'vlis.virtualinterfaceid' ) + ->leftJoin( 'virtualinterface AS vid', 'vid.id', 'vlid.virtualinterfaceid' ) + ->leftJoin( 'cust AS cs', 'cs.id', 'vis.custid' ) + ->leftJoin( 'cust AS cd', 'cd.id', 'vid.custid' ) + ->leftJoin( 'vlan', 'vlan.id', 'srcip.vlanid' ) + ->whereRaw( 'bs.last_seen >= NOW() - INTERVAL ' . $lookback_days . ' DAY' ) + ->where( 'bs.protocol', $protocol ) + ->where( 'bs.packetcount', '>=', 1 ) + ->where( 'vlan.id', $vlan ) + ->when( $restrictActivePeeringMatrix, function( Builder $q ) { + return $q->where( 'cs.activepeeringmatrix', 1 ) + ->where( 'cd.activepeeringmatrix', 1); + } ) + ->groupByRaw( 'bs.srcipaddressid, bs.dstipaddressid, bs.id, vlis.virtualinterfaceid, vlid.virtualinterfaceid' ) + ->orderBy( 'csautsys' )->get()->toArray(); + + $apeers = []; + + foreach( $peers as $p ) { + if( !isset( $apeers[ $p['csautsys'] ] ) ) { + $apeers[ $p['csautsys'] ] = []; + $apeers[ $p['csautsys'] ]['shortname'] = $p['csshortname']; + $apeers[ $p['csautsys'] ]['name'] = $p['csname']; + $apeers[ $p['csautsys'] ]['activepeeringmatrix'] = $p['csactivepeeringmatrix']; + $apeers[ $p['csautsys'] ]['peers'] = []; + } + $apeers[ $p['csautsys'] ]['peers'][ $p['cdautsys'] ] = $p['cdautsys']; + } + + Cache::put( $key, $apeers, 3600 ); + return $apeers; + } +} \ No newline at end of file diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php new file mode 100644 index 000000000..6f823e1cb --- /dev/null +++ b/app/Http/Controllers/ProfileController.php @@ -0,0 +1,213 @@ + + * @author Barry O'Donovan + * @category IXP + * @package IXP\Http\Controllers + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class ProfileController extends Controller +{ + /** + * Display the form to edit a profile + * + * @return View + */ + public function edit(): View + { + /** @var User $user */ + $user = Auth::getUser(); + + // array used to populate the form to modify user information. + // former doesn't allow us to populate a form the classic way when there is many forms on the same view. + $details = [ + 'name' => $user->name, + 'username' => $user->username, + 'email' => $user->email, + 'authorisedMobile' => $user->authorisedMobile, + ]; + + $notesNotifications = [ + 'notify' => Auth::getUser()->prefs[ 'notes' ][ 'global_notifs' ] ?? 'none', + ]; + + $mailingListSubscriptions = []; + + // are we using mailing lists? + if( config( 'mailinglists.enabled', false ) ) { + $mailingListSubscriptions = $user->prefs[ 'mailinglist' ] ?? []; + } + + return view( 'profile/edit' )->with([ + "details" => $details, + "notesNotifications" => $notesNotifications, + "mailingListSubscriptions" => $mailingListSubscriptions, + ]); + } + + /** + * Update password form + * + * @param PasswordRequest $r instance of the current HTTP request + * + * @return RedirectResponse + * + * @throws + */ + public function updatePassword( PasswordRequest $r ): RedirectResponse + { + /** @var User $user */ + $user = Auth::getUser(); + + // get the token of the current session + if( $recallerName = $r->cookies->get( Auth::getRecallerName() ) ) { + $recaller = new Recaller( $recallerName ); + $token = $recaller->token(); + } + + $user->password = Hash::make( $r->new_password ); + $user->lastupdatedby = $user->id; + $user->save(); + + // Logout all the active session except the current one + UserRememberToken::where( 'user_id', $user->id ) + ->where( 'token', '!=', $token ?? null ) + ->delete(); + + AlertContainer::push( 'Password updated.', Alert::SUCCESS ); + return redirect( route( "profile@edit" ) ); + } + + /** + * Update the user's profile (name, email, etc..) + * + * @param ProfileRequest $r instance of the current HTTP request + * + * @return RedirectResponse + * + * @throws + */ + public function updateProfile( ProfileRequest $r ): RedirectResponse + { + /** @var User $user */ + $user = Auth::getUser(); + + $user->name = $r->name; + $user->username = $r->username; + $user->email = $r->email; + $user->authorisedMobile = $r->authorisedMobile; + $user->lastupdatedby = $user->id; + $user->save(); + + AlertContainer::push( 'Profile details updated.', Alert::SUCCESS ); + return redirect( route( "profile@edit" ) ); + } + + /** + * Update the user's customer notes notification preference + * + * @param NotificationRequest $r instance of the current HTTP request + * + * @return RedirectResponse + * + * @throws + */ + public function updateNotificationPreference( NotificationRequest $r ) : RedirectResponse + { + /** @var User $user */ + $user = Auth::getUser(); + $prefs = $user->prefs; + + $prefs[ 'notes' ][ 'global_notifs' ] = $r->notify; + + $user->prefs = $prefs; + $user->save(); + + AlertContainer::push( 'Notification preference updated.', Alert::SUCCESS ); + return Redirect::to( route( "profile@edit" ) ); + } + + /** + * Update the mailing list preferences of the user + * + * @param Request $r instance of the current HTTP request + * + * @return RedirectResponse + * + * @throws + */ + public function updateMailingLists( Request $r ) : RedirectResponse + { + if( config( 'mailinglists.enabled', false ) ) { + /** @var User $user */ + $user = Auth::getUser(); + $prefs = $user->prefs; + $mailintLists = []; + + foreach( config( 'mailinglists.lists' ) as $name => $ml ) { + $mailintLists[ $name ] = $r->input( "ml_" . $name ); + } + + $prefs[ 'mailinglist' ] = $mailintLists; + $user->prefs = $prefs; + $user->save(); + AlertContainer::push( 'Mailing list subscriptions updated and will take effect within 12 hours.', Alert::SUCCESS ); + return Redirect::to( route( "profile@edit" ) ); + } + + AlertContainer::push( 'Mailing list subscriptions is not enabled.', Alert::DANGER ); + return Redirect::to( route( "profile@edit" ) ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/RipeAtlas/MeasurementController.php b/app/Http/Controllers/RipeAtlas/MeasurementController.php new file mode 100644 index 000000000..22e9836db --- /dev/null +++ b/app/Http/Controllers/RipeAtlas/MeasurementController.php @@ -0,0 +1,333 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers\RipeAtlas + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class MeasurementController extends Eloquent2Frontend +{ + /** + * The object being created / edited + * + * @var AtlasMeasurement + */ + protected $object = null; + + /** + * Is this a read only controller? + * + * @var boolean + */ + public static $read_only = true; + + /** + * The URL prefix to use. + * + * Automatically determined based on the controller name if not set. + * + * @var string + */ + protected static $route_prefix = "ripe-atlas/measurements"; + + /** + * This function sets up the frontend controller + * + * @return void + */ + #[\Override] + public function feInit() + { + $this->feParams = (object)[ + 'model' => AtlasMeasurement::class, + 'pagetitle' => 'Ripe Atlas :: Measurements', + 'titleSingular' => 'Measurement', + 'nameSingular' => 'an atlas measurement', + 'listOrderBy' => 'id', + 'listOrderByDir' => 'DESC', + 'viewFolderName' => 'ripe-atlas/measurement', + 'readonly' => self::$read_only, + 'listColumns' => [ + 'run_id' => [ + 'title' => 'ID Run', + 'type' => self::$FE_COL_TYPES[ 'HAS_ONE' ], + 'controller' => 'ripe-atlas/runs', + 'action' => 'view', + 'idField' => 'run_id', + ], + 'cs_name' => [ + 'title' => 'Customer Source', + 'type' => self::$FE_COL_TYPES[ 'HAS_ONE' ], + 'controller' => 'customer', + 'action' => 'overview', + 'idField' => 'cust_source', + ], + 'cd_name' => [ + 'title' => 'Customer Destination', + 'type' => self::$FE_COL_TYPES[ 'HAS_ONE' ], + 'controller' => 'customer', + 'action' => 'overview', + 'idField' => 'cust_dest', + ], + 'atlas_id' => [ + 'title' => 'Atlas ID', + 'type' => self::$FE_COL_TYPES[ 'JSON' ], + 'displayAs' => 'text', + 'valueFrom' => 'url', + 'value' => url( '/api/v4/ripe-atlas/measurement/' ) . '/%%COL%%/info', + ], + 'atlas_request' => [ + 'title' => 'Atlas Request', + 'type' => self::$FE_COL_TYPES[ 'JSON' ], + 'displayAs' => 'btn', + 'valueFrom' => 'DB', + ], + 'atlas_state' => 'State', + ], + ]; + + // display the same information in the view as the list + $this->feParams->viewColumns = array_merge( + $this->feParams->listColumns, + [ + 'atlas_data' => [ + 'title' => 'Atlas Data', + 'type' => self::$FE_COL_TYPES[ 'JSON' ], + 'displayAs' => 'btn', + 'valueFrom' => 'DB', + ], + 'atlas_start' => 'Atlas Start', + 'atlas_stop' => 'Atlas Stop', + 'created_at' => [ + 'title' => 'Created at', + 'type' => self::$FE_COL_TYPES[ 'DATETIME' ], + ], + 'updated_at' => [ + 'title' => 'Updated at', + 'type' => self::$FE_COL_TYPES[ 'DATETIME' ], + ], + ] + ); + } + + /** + * Function which can be over-ridden to add additional routes + * + * If you don't want to use the defaults as well as some additionals, override + * `routes()` instead. + * + * @param string $route_prefix + * + * @return void + */ + #[\Override] + protected static function additionalRoutes( string $route_prefix ): void + { + Route::group( [ 'prefix' => $route_prefix ], function() use ( $route_prefix ) { + Route::get( 'matrix/{atlasRun}', 'RipeAtlas\MeasurementController@matrix' )->name( $route_prefix . '@matrix' ); + Route::post('run/{atlasrun}', 'RipeAtlas\MeasurementController@runMeasurements' )->name( $route_prefix . '@run-measurements' ); + Route::put( 'update/{atlasrun}', 'RipeAtlas\MeasurementController@updateMeasurements' )->name( $route_prefix . '@update-measurements' ); + Route::put( 'stop-measurements/{atlasrun}', 'RipeAtlas\MeasurementController@stopMeasurements' )->name( $route_prefix . '@stop-measurements' ); + }); + } + + /** + * List the contents of a database table. + * + * @param Request $param + * + * @return View + */ + #[\Override] + public function list( Request $param ) : View + { + $rid = false; + if( $run = AtlasRun::find( $param->atlasrun ) ) { + $rid = $run->id; + } + + $this->data[ 'params' ][ 'rid' ] = $rid; + $this->data[ 'rows' ] = $this->listGetData(); + + $this->listIncludeTemplates(); + $this->preList(); + + return $this->display( 'list' ); + } + + /** + * Provide array of rows for the list action and view action + * + * @param int $id The `id` of the row to load for `view` action`. `null` if `listAction` + * + * @return array + */ + #[\Override] + protected function listGetData( $id = null ): array + { + $rid = $this->data[ 'params' ][ 'rid' ] ?? null; + $feParams = $this->feParams; + + return AtlasMeasurement::selectRaw( + 'atlas_measurements.*, + cs.abbreviatedName as cs_name, + cd.abbreviatedName as cd_name, + atlas_results.id as atlas_result_id' + ) + ->leftJoin( 'cust as cs', 'atlas_measurements.cust_source', 'cs.id' ) + ->leftJoin( 'cust as cd', 'atlas_measurements.cust_dest', 'cd.id' ) + ->leftJoin('atlas_results', 'atlas_measurements.id', 'atlas_results.measurement_id' ) + ->when( $id, function( Builder $q, $id ) { + return $q->where( 'atlas_measurements.id', $id ); + } )->when( $rid, function( Builder $q, $rid ) { + return $q->where( 'atlas_measurements.run_id', $rid ); + } )->when( $feParams->listOrderBy , function( Builder $q ) use( $feParams ) { + return $q->orderBy( $feParams->listOrderBy, $feParams->listOrderByDir ?? 'ASC'); + })->get()->toArray(); + } + + /** + * Function that run measurements for an atlas run + * + * @param AtlasRun $atlasrun + * + * @return RedirectResponse + */ + public function runMeasurements( AtlasRun $atlasrun ): RedirectResponse + { + $atlasrun->atlasMeasurements()->whereNull( 'atlas_id' )->each( function ( $am ) { + RunMeasurementsJob::dispatchAfterResponse( $am ); + }); + + $atlasrun->update( [ 'started_at' => now() ] ); + + AlertContainer::push( 'The command run atlas measurements is processing.', Alert::SUCCESS ); + return redirect( route( RunController::route_prefix() . "@list" ) ); + } + + /** + * Function that update measurements for an atlas run + * + * @param AtlasRun $atlasrun + * + * @return RedirectResponse + * + */ + public function updateMeasurements( AtlasRun $atlasrun ): RedirectResponse + { + $ams = $atlasrun->atlasMeasurements()->whereNull( 'atlas_stop' )->get(); + + if( $ams->isEmpty() ) { + AlertContainer::push( 'All the atlas measurements have been executed already.', Alert::DANGER ); + return redirect( route( RunController::route_prefix() . "@list" ) ); + } + + $ams->each( function ( $am ) { + UpdateMeasurementsJob::dispatchAfterResponse( $am ); + }); + + AlertContainer::push( 'The command update atlas measurements is processing.', Alert::SUCCESS ); + return redirect( route( RunController::route_prefix() . "@list" ) ); + } + + /** + * Function that stop all measurements for an atlas run + * + * @param AtlasRun $atlasrun + * + * @return RedirectResponse + * + */ + public function stopMeasurements( AtlasRun $atlasrun ): RedirectResponse + { + $atlasrun->atlasMeasurements()->each( function( $am ) {/** @var $am AtlasMeasurement */ + StopAllMeasurementsJob::dispatchAfterResponse( $am->atlas_id ); + }); + + AlertContainer::push( 'The command Stop atlas measurements is processing.', Alert::SUCCESS ); + return redirect( route( RunController::route_prefix() . "@list" ) ); + } + + /** + * Display the ripe atlas measurements matrix for a run + * + * @param AtlasRun $atlasRun ID of the atlas run + */ + public function matrix( AtlasRun $atlasRun ): \Illuminate\Contracts\View\View|Factory|View|Application + { + $custs = AtlasMeasurement::select( [ + 'cust_dest.*', + 'cust_source.*' + ] ) + ->join( 'atlas_runs', 'atlas_measurements.run_id', 'atlas_runs.id' ) + ->join( 'cust as cust_dest', 'atlas_measurements.cust_dest', 'cust_dest.id' ) + ->join( 'cust as cust_source', 'atlas_measurements.cust_source', 'cust_source.id') + ->where('atlas_measurements.run_id', $atlasRun->id ) + ->get()->keyBy( 'autsys' )->sortKeys(); + + $asns = $custs->keys(); + + return view( $this->feParams->viewFolderName . '/matrix' )->with( [ + 'custs' => $custs, + 'routePrefix' => self::$route_prefix, + 'asnStringFormat' => $asns->isNotEmpty() ? "% " . strlen( $asns->last() ) . "s" : "% 0s" , + ] ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/RipeAtlas/ProbesController.php b/app/Http/Controllers/RipeAtlas/ProbesController.php new file mode 100644 index 000000000..6b88aa208 --- /dev/null +++ b/app/Http/Controllers/RipeAtlas/ProbesController.php @@ -0,0 +1,208 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers\RipeAtlas + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class ProbesController extends Eloquent2Frontend +{ + /** + * The object being created / edited + * + * @var AtlasProbe + */ + protected $object = null; + + /** + * The URL prefix to use. + * + * Automatically determined based on the controller name if not set. + * + * @var string + */ + protected static $route_prefix = "ripe-atlas/probes"; + + /** + * This function sets up the frontend controller + * + * @return void + */ + #[\Override] + public function feInit() + { + $this->feParams = (object)[ + 'model' => AtlasProbe::class, + 'pagetitle' => 'Ripe Atlas :: Probes', + 'titleSingular' => 'Probe', + 'nameSingular' => 'a probe', + 'listOrderBy' => 'updated_at', + 'listOrderByDir' => 'ASC', + 'viewFolderName' => 'atlas-probes', + 'readonly' => 'true', + 'listColumns' => [ + 'name' => [ + 'title' => 'Customer', + 'type' => self::$FE_COL_TYPES[ 'HAS_ONE' ], + 'controller' => 'customer', + 'action' => 'overview', + 'idField' => 'cust_id' + ], + 'address_v4' => [ + 'title' => 'IPv4', + 'type' => self::$FE_COL_TYPES[ 'WHO_IS_PREFIX' ] + ], + 'address_v6' => 'IPv6', + 'v4_enabled' => [ + 'title' => 'IPv4 Enabled', + 'type' => self::$FE_COL_TYPES[ 'YES_NO' ] + ], + 'v6_enabled' => [ + 'title' => 'IPv6 Enabled', + 'type' => self::$FE_COL_TYPES[ 'YES_NO' ] + ], + 'last_connected' => [ + 'title' => 'Last Connected', + 'type' => self::$FE_COL_TYPES[ 'DATETIME' ], + ], + 'updated_at' => [ + 'title' => 'Updated at', + 'type' => self::$FE_COL_TYPES[ 'DATETIME' ], + ], + ] + ]; + + // display the same information in the view as the list + $this->feParams->viewColumns = array_merge( + $this->feParams->listColumns, + [ + 'asn' => 'ASN', + 'atlas_id' => [ + 'title' => 'Atlas ID', + 'type' => self::$FE_COL_TYPES[ 'JSON' ], + 'displayAs' => 'text', + 'valueFrom' => 'url', + 'value' => url( '/api/v4/ripe-atlas/probe/' ) . '/%%COL%%/info', + ], + 'is_anchor' => [ + 'title' => 'Is Anchor', + 'type' => self::$FE_COL_TYPES[ 'YES_NO' ] + ], + 'is_public' => [ + 'title' => 'Is Public', + 'type' => self::$FE_COL_TYPES[ 'YES_NO' ] + ], + 'api_data' => [ + 'title' => 'API Data', + 'type' => self::$FE_COL_TYPES[ 'JSON' ], + 'displayAs' => 'btn', + 'valueFrom' => 'DB', + ], + ] + ); + } + + /** + * List the contents of a database table. + * + * @param Request $param + * + * @return View + */ + #[\Override] + public function list( Request $param ): View + { + $cid = $protocol = false; + + if( $cust = Customer::find( $param->cust ) ) { + $cid = $cust->id; + } + + if( array_key_exists( $param->protocol, Router::$PROTOCOLS ) ) { + $protocol = $param->protocol; + } + + $this->data[ 'params' ][ 'cid' ] = $cid; + $this->data[ 'params' ][ 'protocol' ] = $protocol; + + $this->data[ 'rows' ] = $this->listGetData(); + + $this->listIncludeTemplates(); + + $this->preList(); + + return $this->display( 'list' ); + } + + /** + * Provide array of rows for the list action and view action + * + * @param int $id The `id` of the row to load for `view` action`. `null` if `listAction` + * + * @return array + */ + #[\Override] + protected function listGetData( $id = null ): array + { + $feParams = $this->feParams; + $cid = $this->data[ 'params' ][ 'cid' ] ?? null; + $protocol = $this->data[ 'params' ][ 'protocol' ] ?? null; + + return AtlasProbe::select( [ + 'atlas_probes.*', + 'cust.name' + ] )->leftJoin( 'cust', 'atlas_probes.cust_id', 'cust.id' ) + ->when( $id, function( Builder $q, $id ) { + return $q->where( 'atlas_probes.id', $id ); + } )->when( $cid , function( Builder $q, $cid ) { + return $q->where('atlas_probes.cust_id', $cid ); + })->when( $protocol , function( Builder $q, $protocol ) { + return $q->where('atlas_probes.v' . $protocol . "_enabled", $protocol, true ); + })->when( $feParams->listOrderBy , function( Builder $q ) use( $feParams ) { + return $q->orderBy( $feParams->listOrderBy, $feParams->listOrderByDir ?? 'ASC'); + })->get()->toArray(); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/RipeAtlas/ResultController.php b/app/Http/Controllers/RipeAtlas/ResultController.php new file mode 100644 index 000000000..04925a8b9 --- /dev/null +++ b/app/Http/Controllers/RipeAtlas/ResultController.php @@ -0,0 +1,113 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers\RipeAtlas + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class ResultController extends Eloquent2Frontend +{ + /** + * The object being created / edited + * + * @var AtlasResult + */ + protected $object = null; + + /** + * Is this a read only controller? + * + * @var boolean + */ + public static $read_only = true; + + /** + * The URL prefix to use. + * + * Automatically determined based on the controller name if not set. + * + * @var string + */ + protected static $route_prefix = "ripe-atlas/results"; + + /** + * This function sets up the frontend controller + * + * @return void + */ + #[\Override] + public function feInit() + { + $this->feParams = (object)[ + 'model' => AtlasResult::class, + 'pagetitle' => 'Ripe Atlas :: Results', + 'titleSingular' => 'Result', + 'nameSingular' => 'an atlas result', + 'listOrderBy' => 'id', + 'listOrderByDir' => 'DESC', + 'viewFolderName' => 'atlas-result', + 'readonly' => self::$read_only, + 'listColumns' => [ + 'routing' => 'Routing', + ], + ]; + + // display the same information in the view as the list + $this->feParams->viewColumns = array_merge( + $this->feParams->listColumns, + [ + 'path' => 'Path' + ] + ); + } + + /** + * Provide array of rows for the list action and view action + * + * @param int $id The `id` of the row to load for `view` action`. `null` if `listAction` + * + * @return array + */ + #[\Override] + protected function listGetData( $id = null ): array + { + $feParams = $this->feParams; + return AtlasResult::when( $id, function( Builder $q, $id ) { + return $q->where( 'atlas_results.id', $id ); + } )->when( $feParams->listOrderBy , function( Builder $q ) use( $feParams ) { + return $q->orderBy( $feParams->listOrderBy, $feParams->listOrderByDir ?? 'ASC'); + })->get()->toArray(); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/RipeAtlas/RunController.php b/app/Http/Controllers/RipeAtlas/RunController.php new file mode 100644 index 000000000..0f8c8658c --- /dev/null +++ b/app/Http/Controllers/RipeAtlas/RunController.php @@ -0,0 +1,338 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers\RipeAtlas + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class RunController extends Eloquent2Frontend +{ + /** + * The object being created/edited + * + * @var AtlasRun + */ + protected $object = null; + + /** + * The URL prefix to use. + * + * Automatically determined based on the controller name if not set. + * + * @var string + */ + protected static $route_prefix = "ripe-atlas/runs"; + + /** + * Do we disable to edit? + * + * @var boolean + */ + public static $disable_edit = true; + + /** + * This function sets up the frontend controller + * + * @return void + */ + #[\Override] + public function feInit() + { + $this->feParams = (object)[ + 'model' => AtlasRun::class, + 'pagetitle' => 'Ripe Atlas :: Runs', + 'titleSingular' => 'Run', + 'nameSingular' => 'atlas run', + 'listOrderBy' => 'created_at', + 'listOrderByDir' => 'DESC', + 'disableEdit' => self::$disable_edit, + 'viewFolderName' => 'ripe-atlas/run', + 'listColumns' => [ + 'vlan_name' => [ + 'title' => 'Vlan', + 'type' => self::$FE_COL_TYPES[ 'HAS_ONE' ], + 'controller' => 'vlan', + 'action' => 'view', + 'idField' => 'vlan_id', + ], + 'protocol' => [ + 'title' => 'Protocol', + 'type' => self::$FE_COL_TYPES[ 'CONST' ], + 'const' => Router::$PROTOCOLS, + ], + 'nb_run' => 'Total', + 'created_at' => 'Created At', + 'scheduled_at' => 'Scheduled At', + 'started_at' => 'Started At', + 'completed_at' => 'Completed At', + ], + ]; + // display the same information in the view as the list + $this->feParams->viewColumns = $this->feParams->listColumns; + } + + /** + * Function which can be over-ridden to add additional routes + * + * If you don't want to use the defaults as well as some additionals, override + * `routes()` instead. + * + * @param string $route_prefix + * + * @return void + */ + #[\Override] + protected static function additionalRoutes( string $route_prefix ): void + { + Route::group( [ 'prefix' => $route_prefix ], function() use ( $route_prefix ) { + Route::get( 'add-step-2', 'RipeAtlas\RunController@addStep2' )->name( $route_prefix . '@add-step-2' ); + Route::put( 'run/complete/{atlasrun}', 'RipeAtlas\RunController@completeRun' )->name( $route_prefix . '@complete-run' ); + }); + } + + /** + * Provide array of rows for the list action and view action + * + * @param int $id The `id` of the row to load for `view` action`. `null` if `listAction` + * + * @return array + */ + #[\Override] + protected function listGetData( $id = null ): array + { + $feParams = $this->feParams; + + return AtlasRun::select( [ + 'atlas_runs.*', + 'vlan.name as vlan_name', + DB::raw('COUNT( am.run_id ) as nb_am'), + DB::raw('COUNT( am.atlas_create ) as nb_am_created'), + DB::raw('COUNT( am.atlas_start ) as nb_am_started'), + DB::raw('COUNT( am.atlas_stop ) as nb_am_stopped') + ]) + ->leftJoin( 'vlan', 'atlas_runs.vlan_id','vlan.id' ) + ->leftJoin( 'atlas_measurements AS am' , 'atlas_runs.id', 'am.run_id' ) + ->when( $id, function( Builder $q, $id ) { + return $q->where( 'atlas_runs.id', $id ); + } ) + ->when( $feParams->listOrderBy , function( Builder $q, $orderby ) use ( $feParams ) { + return $q->orderBy( $orderby, $feParams->listOrderByDir ?? 'ASC'); + })->groupBy( 'atlas_runs.id' )->get()->toArray(); + } + + /** + * Display the form to create an object + * + * @return (\Illuminate\Database\Eloquent\Collection|null|true)[] + * + * @psalm-return array{object: null, vlans: \Illuminate\Database\Eloquent\Collection, preAddForm: true} + */ + #[\Override] + protected function createPrepareForm(): array + { + Former::populate( [ + 'protocol' => request()->old( 'protocol', Router::PROTOCOL_IPV4 ), + 'scheduled_at' => request()->old( 'scheduled_at', (string)AtlasRun::SCHEDULED_AT_NOW ), + 'scheduled_date' => request()->old( 'scheduled_date' ), + 'scheduled_time' => request()->old( 'scheduled_time' ), + ] ); + + return [ + 'object' => null, + 'vlans' => Vlan::publicOnly()->get(), + 'preAddForm' => true + ]; + } + + /** + * Second step to create an Atlas run + * + * @param Request $r + * + * @return View + */ + public function addStep2( Request $r ): View + { + $this->checkForm( $r ); + + $this->feParams->titleSingular = "Step 2"; + $this->addEditSetup(); + + $this->data[ 'params' ][ 'isAdd' ] = true; + $this->data[ 'params' ][ 'preAddForm' ] = false; + $this->data[ 'params' ][ 'object' ] = null; + $this->data[ 'params' ][ 'custs' ] = CustomerAggregator::WithProbesForProtocol( $r->protocol, $r->vlan_id ); + $this->data[ 'params' ][ 'vlans' ] = Vlan::publicOnly()->get(); + $this->data[ 'params' ][ 'protocol' ] = $r->protocol; + $this->data[ 'params' ][ 'vlanid' ] = $r->vlan_id; + $this->data[ 'params' ][ 'scheduled_at' ] = $r->scheduled_at; + $this->data[ 'params' ][ 'scheduled_date' ] = $r->scheduled_date; + $this->data[ 'params' ][ 'scheduled_time' ] = $r->scheduled_time; + + return $this->display( 'edit' ); + } + + /** + * Function to do the actual validation and storing of the submitted object. + * + * @param Request $r + * + * @return RedirectResponse|true + */ + #[\Override] + public function doStore( Request $r ): bool|RedirectResponse + { + if( !$r->selected_custs || !count( $r->selected_custs ) ) { + AlertContainer::push( "You need to select at least one " . config( "ixp_fe.lang.customer.one" ) . ".", Alert::DANGER ); + return Redirect::back()->withInput(); + } + + $this->checkForm( $r ); + + $this->object = AtlasRun::create( [ + 'protocol' => $r->protocol, + 'scheduled_at' => $r->scheduled_at === AtlasRun::SCHEDULED_AT_NOW ? now() : new Carbon( $r->scheduled_date . $r->scheduled_time ), + 'vlan_id' => $r->vlan_id + ] ); + + CreateMeasurementsJob::dispatchSync( $this->object, $r->selected_custs ); + + if( (int)$r->scheduled_at === AtlasRun::SCHEDULED_AT_NOW ) { + $this->object->atlasMeasurements()->each( function( $am ) { + RunMeasurementsJob::dispatchAfterResponse( $am ); + } ); + + $this->object->update( [ 'started_at' => now() ] ); + AlertContainer::push( 'The command atlas run is processing.', Alert::SUCCESS ); + } + + return true; + } + + /** + * Run the Atlas complete job + * + * @param AtlasRun $atlasrun + * + * @return RedirectResponse + */ + public function completeRun( AtlasRun $atlasrun ): RedirectResponse + { + if( $atlasrun->completed_at ) { + AlertContainer::push( 'The command complete atlas run have already executed.', Alert::DANGER ); + } elseif( CompleteRequestsJob::dispatchSync( $atlasrun ) ) { + AlertContainer::push( 'The command complete atlas run executed with success.', Alert::SUCCESS ); + } else { + AlertContainer::push( 'The command complete atlas run cannot be executed, some atlas measurements are not ended.', Alert::DANGER ); + } + + return redirect( route( self::$route_prefix . "@list" ) ); + } + + /** + * Delete all atlas measurements and atlas result before deleting the atlas run + * + * @inheritdoc + * + * @return bool Return false to stop / cancel the deletion + */ + #[\Override] + protected function preDelete(): bool + { + if( !$this->object->completed_at ) { + AlertContainer::push( 'Impossible to delete, the atlas run must be completed or stopped to do this action.', Alert::DANGER ); + return false; + } + + // Delete All Atlas results and Atlas measurements before deleting the Atlas run + // Delete on cascade for the atlas result linked to the atlas measurements + $this->object->atlasMeasurements()->delete(); + + return true; + } + + /** + * Check if the form is valid + * + * @param Request $r + */ + #[\Override] + public function checkForm( Request $r ): void + { + $rules = [ + 'vlan_id' => 'required|integer|exists:Vlan,id', + 'protocol' => 'required|integer|in:' . implode( ',', array_keys( Router::$PROTOCOLS ) ), + 'scheduled_at' => 'required|integer|in:' . implode( ',', array_keys( AtlasRun::$SCHEDULED_AT ) ), + ]; + + if( $r->scheduled_at === AtlasRun::SCHEDULED_AT_DATETIME ) { + $validateScheduled = + [ + 'scheduled_date' => 'required|date', + 'scheduled_time' => 'required|date_format:H:i', + ]; + + $rules = array_merge( $rules, $validateScheduled ); + } + + $r->validate( $rules ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/RouterController.php b/app/Http/Controllers/RouterController.php new file mode 100644 index 000000000..1abc00961 --- /dev/null +++ b/app/Http/Controllers/RouterController.php @@ -0,0 +1,317 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class RouterController extends Controller +{ + /** + * Display all the routers + * + * @return View + */ + public function list(): View + { + $routers = Router::with( 'vlan' )->get(); + + $birdv1Templates = []; + + foreach( $routers as $router ) { + if( str_contains( $router->template, 'router/as112/bird/' ) ) { + $birdv1Templates[] = $router->handle; + } + } + + if( $birdv1Templates !== [] ) { + AlertContainer::push( 'Bird v1 went end of life at the end of 2023, however the following routers are using a Bird v1 templates: ' + . implode( ', ', $birdv1Templates ) . '. You should migrate to Bird v2 or v3 as support for Bird v1 will be ' + . 'removed from IXP Manager in a future release.', Alert::WARNING + ); + } + + return view( 'router/index' )->with([ + 'routers' => Router::with( 'vlan' )->get(), + ]); + } + + /** + * Status page for routers + * + * @return View + */ + public function status(): View + { + $routers = $routersWithApi = []; + $lgEnabled = !config('ixp_fe.frontend.disabled.lg' ); + + if( $lgEnabled ) { + $routers = Router::all(); + // Get routers with API + $routersWithApi = Router::whereNotNull( 'api' ) + ->where( 'api_type', '!=', 0 )->get() + ->pluck( 'handle' )->toArray(); + } + + return view( 'router/status' )->with([ + 'routers' => $routers, + 'routersWithApi' => $routersWithApi, + 'lgEnabled' => $lgEnabled, + ]); + } + + /** + * Display the form to create a router + * + * @return View + */ + public function create(): View + { + return view( 'router/edit' )->with([ + 'rt' => false, + 'vlans' => Vlan::publicOnly()->orderBy( 'number' )->get(), + ]); + } + + /** + * Create a router + * + * @param StoreRouter $r instance of the current HTTP request + * + * @return RedirectResponse + * + * @throws + */ + public function store( StoreRouter $r ): RedirectResponse + { + if( !FacadeView::exists( $r->template ) ) { + AlertContainer::push( 'The template you entered cannot be found. Please check the help message for more information.', Alert::DANGER ); + return Redirect::to( route( 'router@create' ) )->withInput( $r->all() ); + } + + $router = Router::create( $r->all() ); + + $this->checkASN32( $router ); + + AlertContainer::push( 'Router created.', Alert::SUCCESS ); + return Redirect::to( route( "router@list" ) ); + } + + /** + * Display the form to edit a router + * + * @param Router $router router that need to be edited + * + * @return View + */ + public function edit( Router $router ): View + { + Former::populate([ + 'handle' => request()->old( 'handle', $router->handle ), + 'vlan_id' => request()->old( 'vlan_id', (string)$router->vlan_id ), + 'protocol' => request()->old( 'protocol', $router->protocol ), + 'type' => request()->old( 'type', $router->type ), + 'name' => request()->old( 'name', $router->name ), + 'shortname' => request()->old( 'shortname', $router->shortname ), + 'router_id' => request()->old( 'router_id', $router->router_id ), + 'peering_ip' => request()->old( 'peering_ip', $router->peering_ip ), + 'asn' => request()->old( 'asn', (string)$router->asn ), + 'software' => request()->old( 'software', $router->software ), + 'software_version' => request()->old( 'software_version', $router->software_version ), + 'operating_system' => request()->old( 'operating_system', $router->operating_system ), + 'operating_system_version' => request()->old( 'operating_system_version', $router->operating_system_version ), + 'mgmt_host' => request()->old( 'mgmt_host', $router->mgmt_host ), + 'api_type' => request()->old( 'api_type', (string)$router->api_type ), + 'api' => request()->old( 'api', $router->api ), + 'lg_access' => request()->old( 'lg_access', (string)$router->lg_access ), + 'quarantine' => request()->old( 'quarantine', (string)$router->quarantine ), + 'bgp_lc' => request()->old( 'bgp_lc', (string)$router->bgp_lc ), + 'rpki' => request()->old( 'rpki', (string)$router->rpki ), + 'rfc1997_passthru' => request()->old( 'rfc1997_passthru', (string)$router->rfc1997_passthru ), + 'skip_md5' => request()->old( 'skip_md5', (string)$router->skip_md5 ), + 'pair_id' => request()->old( 'pair_id', (string)$router->pair_id ), + 'template' => request()->old( 'template', $router->template ), + ]); + + return view( 'router/edit' )->with([ + 'rt' => $router, + 'vlans' => Vlan::publicOnly()->orderBy( 'number' )->get(), + ]); + } + + /** + * Update a router (set all the data needed) + * + * @param StoreRouter $r instance of the current HTTP request + * @param Router $router + * + * @return RedirectResponse + * + * @throws + */ + public function update( StoreRouter $r, Router $router ): RedirectResponse + { + $router->update( $r->all() ); + + $this->checkASN32( $router ); + + AlertContainer::push( 'Router updated.', Alert::SUCCESS ); + return Redirect::to( route( "router@list" ) ); + } + + /** + * Display the details of a router + * + * @param Router $router router that need to be displayed + * + * @return View + */ + public function view( Router $router ): View + { + return view( 'router/view' )->with([ + 'rt' => $router->load( 'vlan' ) + ]); + } + + /** + * Delete a router + * + * @param Router $router + * + * @return redirectresponse + * + * @throws + */ + public function delete( Router $router): RedirectResponse + { + $router->delete(); + AlertContainer::push( 'Router deleted.', Alert::SUCCESS ); + return Redirect::to( route( "router@list" ) ); + } + + /** + * Pause a router + * + * @param Router $router + * + * @return redirectresponse + * + * @throws + */ + public function pause( Router $router): RedirectResponse + { + $router->pause_updates = 1; + $router->save(); + + AlertContainer::push( 'Automatic updates for router ' . $router->handle . ' paused.', Alert::SUCCESS ); + return Redirect::to( route( "router@list" ) ); + } + + /** + * Resume a router + * + * @param Router $router + * + * @return redirectresponse + * + * @throws + */ + public function resume( Router $router): RedirectResponse + { + $router->pause_updates = false; + $router->save(); + + AlertContainer::push( 'Automatic updates for router ' . $router->handle . ' resumed.', Alert::SUCCESS ); + return Redirect::to( route( "router@list" ) ); + } + + /** + * Reset update timestamps + * + * @param Router $router + * + * @return redirectresponse + * + * @throws + */ + public function resetUpdateTimestamps( Router $router): RedirectResponse + { + $router->last_update_started = null; + $router->last_updated = null; + $router->save(); + + AlertContainer::push( 'Update timestamps for router ' . $router->handle . ' set to null.', Alert::SUCCESS ); + return Redirect::to( route( "router@list" ) ); + } + + /** + * Warning about asn32's with route servers. + * + * + * + * @param Router $router + * @return void + */ + private function checkASN32( Router $router ) + { + if( $router->type == Router::TYPE_ROUTE_SERVER && $router->asn > 65535 ) { + AlertContainer::push( 'You are strongly advised to use / request a dedicated 16-bit ASN from your RIR ' + . 'for route server use and in our experience, all RIRs understand this and accommodate it. The route server ' + . 'configurations will support an asn32 but to our knowledge, this has never been used in production. ' + . 'Also, withouot an asn16, you will be unable to offer your members standard community based filtering. ', + Alert::WARNING + ); + } + } + +} \ No newline at end of file diff --git a/app/Http/Controllers/RsFilterController.php b/app/Http/Controllers/RsFilterController.php new file mode 100644 index 000000000..a8c9f1ecd --- /dev/null +++ b/app/Http/Controllers/RsFilterController.php @@ -0,0 +1,416 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class RsFilterController extends Controller +{ + /** + * Display the list of customers with Route Server Filters + * + * @return View + * + * @throws AuthorizationException + */ + public function listCustomers(): View + { + $this->authorize( 'checkListCustomers', [ RouteServerFilter::class ] ); + + return view( 'rs-filter/list-customers' )->with([ + "customers" => Customer::select( DB::raw('cust.id, cust.name, COUNT("route_server_filters_prod.id") as prod_rules') ) + ->join('route_server_filters_prod', 'cust.id', '=', 'route_server_filters_prod.customer_id') + ->groupBy( DB::raw('cust.id, cust.name') ) + ->orderBy('cust.name') + ->get() + ]); + } + + + /** + * Display the list of Route Server Filter + * + * @param Customer $cust + * + * @return View + * + * @throws AuthorizationException + */ + public function list( Customer $cust ): View + { + $this->authorize( 'checkCustObject', [ RouteServerFilter::class, $cust ] ); + + return view( 'rs-filter/list' )->with([ + "rsFilters" => RouteServerFilter::where( "customer_id" , $cust->id ) + ->orderBy( 'order_by' )->get(), + "rsFiltersProd" => RouteServerFilterProd::where( "customer_id" , $cust->id ) + ->orderBy( 'order_by' )->get(), + "c" => $cust, + 'in_sync' => RouteServerFilterAggregator::inSync( $cust ), + ]); + } + + + + /** + * Revert changes + */ + public function revert( Customer $cust ): RedirectResponse + { + $this->authorize( 'checkCustObject', [ RouteServerFilter::class, $cust ] ); + + RouteServerFilterAggregator::revert( $cust ); + + AlertContainer::push( "Staged changes reverted.", Alert::SUCCESS ); + return redirect( route( 'rs-filter@list', [ 'cust' => $cust->id ] ) ); + } + + /** + * Commit changes + */ + public function commit( Customer $cust ): RedirectResponse + { + $this->authorize( 'checkCustObject', [ RouteServerFilter::class, $cust ] ); + + RouteServerFilterAggregator::commit( $cust ); + + Log::notice( Auth::getUser()->username . ' commited route server filter changes' ); + AlertContainer::push( "Staged changes commited. " . config('ixp_fe.rs-filters.ttl' ), Alert::SUCCESS ); + return redirect( route( 'rs-filter@list', [ 'cust' => $cust->id ] ) ); + } + + + /** + * Allow to display the form to create a route server filter + * + * @param Request $r + * @param Customer $cust + * + * @return View + * + * @throws AuthorizationException + */ + public function create( Request $r, Customer $cust ): View + { + $this->authorize( 'checkCustObject', [ RouteServerFilter::class, $cust ] ); + + $vlanid = $r->old( 'vlan_id' ); + $protocol = $r->old( 'protocol'); + $peer = $r->old( 'peer_id' ); + + Former::populate( [ + 'peer_id' => $peer ?? "Null", + 'vlan_id' => $vlanid ?? "Null", + 'protocol' => $protocol ?? "Null", + 'action_advertise' => $r->old( 'action_advertise', "Null" ), + 'action_receive' => $r->old( 'action_receive', "Null" ), + 'received_prefix' => $r->old( 'received_prefix', "*" ), + 'advertised_prefix' => $r->old( 'advertised_prefix', "*" ), + ] ); + + $advertisedPrefixes = []; + if( $cust->maxprefixes < 2000 ) { + $advertisedPrefixes = IrrdbPrefix::where( 'customer_id', $cust->id ) + ->where( 'protocol', $protocol )->get()->toArray(); + } + + $peers = array_merge( [ '0' => [ 'id' => '0', 'name' => "All Peers" ] ], + CustomerAggregator::getByVlanAndProtocol( (int)$vlanid , (int)$protocol ) ); + + // exclude this network + foreach( $peers as $i => $p ) { + if( $p['id'] === $cust->id ) { + unset( $peers[$i] ); + break; + } + } + + return view( 'rs-filter/edit' )->with( [ + 'rsf' => false, + 'c' => $cust, + 'vlans' => array_merge( [ '0' => [ 'id' => '0', 'name' => "All LANs" ] ], $this->getPublicPeeringVLANs( $cust->id ) ), + 'protocols' => Router::$PROTOCOLS, + 'peers' => $peers, + 'advertisedPrefixes' => $advertisedPrefixes + ] ); + } + + /** + * Function to store A Route Server Filter object + * + * @param Store $r + * + * @return RedirectResponse + * + * @throws AuthorizationException + */ + public function store( Store $r ): RedirectResponse + { + $cust = Customer::findOrFail( $r->custid ); + + $this->authorize( 'checkCustObject', [ RouteServerFilter::class, $cust ] ); + + $rsf = RouteServerFilter::make( array_merge( $r->except( [ 'enabled', 'order_by' ] ), + [ + 'enabled' => true, + 'order_by' => RouteServerFilter::where( 'customer_id', $cust->id ) + ->get()->max( 'order_by' ) +1, + ] + )); + + $rsf->customer_id = $cust->id; + $rsf->save(); + + Log::notice( Auth::getUser()->username . ' created a route server filter with ID ' . $rsf->id ); + AlertContainer::push( "Route Server Filter created", Alert::SUCCESS ); + return redirect( route( "rs-filter@list", [ "cust" => $cust->id ] ) ); + } + + /** + * Allow to display the form to edit a route server filter + * + * @param Request $r + * @param RouteServerFilter $rsf + * + * @return View + * + * @throws AuthorizationException + */ + public function edit( Request $r, RouteServerFilter $rsf ): View + { + $this->authorize( 'checkRsfObject', [ RouteServerFilter::class, $rsf ] ); + + $vlanid = $r->old( 'vlan_id', (string)$rsf->vlan_id ?? null ); + $protocol = $r->old( 'protocol', $rsf->protocol ?? null ); + $peerid = $r->old( 'peer_id', (string)$rsf->peer_id ?? null ); + + Former::populate( [ + 'vlan_id' => $vlanid ?? "null", + 'protocol' => $protocol ?? "null", + 'peer_id' => $peerid ?? 'null', + 'received_prefix_val' => $r->old( 'received_prefix', $rsf->received_prefix ), + 'advertised_prefix_val' => $r->old( 'advertised_prefix', $rsf->advertised_prefix ), + 'action_advertise' => $r->old( 'action_advertise', $rsf->action_advertise ?? 'Null' ), + 'action_receive' => $r->old( 'action_receive', $rsf->action_receive ?? 'Null' ), + ] ); + + return view( 'rs-filter/edit' )->with( [ + 'rsf' => $rsf, + 'c' => $rsf->customer, + 'vlans' => array_merge( [ '0' => [ 'id' => '0', 'name' => "All LANs" ] ], $this->getPublicPeeringVLANs( $rsf->customer_id ) ), + 'protocols' => Router::$PROTOCOLS, + 'peers' => array_merge( [ '0' => [ 'id' => '0', 'name' => "All Peers" ] ], CustomerAggregator::getByVlanAndProtocol( (int)$vlanid , (int)$protocol ) ), + ] ); + } + + /** + * Function to update A Route Server Filter object + * + * @param Store $r + * @param RouteServerFilter $rsf + * + * @return RedirectResponse + * + * @throws AuthorizationException + */ + public function update( Store $r, RouteServerFilter $rsf ): RedirectResponse + { + $this->authorize( 'checkRsfObject', [ RouteServerFilter::class, $rsf ] ); + + $rsf->update( $r->all() ); + + Log::notice( Auth::getUser()->username . ' updated a route server filter with ID ' . $rsf->id ); + AlertContainer::push( "Route Server Filter updated", Alert::SUCCESS ); + return redirect( route( "rs-filter@list", [ "cust" => $rsf->customer_id ] ) ); + } + + /** + * Display the details of a Route Server Filter + * + * @param RouteServerFilter $rsf + * + * @return View + * + * @throws AuthorizationException + */ + public function view( RouteServerFilter $rsf ): View + { + $this->authorize( 'checkRsfObject', [ RouteServerFilter::class, $rsf ] ); + + return view( 'rs-filter/view' )->with( [ + 'rsf' => $rsf + ] ); + } + + /** + * Enable or disable a route server filter + * + * @param RouteServerFilter $rsf + * @param int $enable + * + * @return RedirectResponse + * + * @throws AuthorizationException + */ + public function toggleEnable( RouteServerFilter $rsf, int $enable ): RedirectResponse + { + $this->authorize( 'checkRsfObject', [ RouteServerFilter::class, $rsf ] ); + + $status = $enable ? 'enabled' : 'disabled'; + + $rsf->enabled = $enable; + $rsf->save(); + + Log::notice( Auth::getUser()->username . ' ' . $status . ' a route server filter with ID ' . $rsf->id ); + AlertContainer::push( 'Route server filter ' . $status . '.', Alert::SUCCESS ); + return redirect( route( "rs-filter@list", [ "cust" => $rsf->customer_id ] ) ); + } + + /** + * Change the order by of a route server filter (up/down) + * + * @param RouteServerFilter $rsf + * @param int $up If true move up if false move down + * + * @return RedirectResponse + * + * @throws AuthorizationException + */ + public function changeOrderBy( RouteServerFilter $rsf, int $up ): RedirectResponse + { + $this->authorize( 'checkRsfObject', [ RouteServerFilter::class, $rsf ] ); + + // Getting the list of all the route server filters for the customer + $listRsf = RouteServerFilter::where( "customer_id", $rsf->customer_id ) + ->orderBy( 'order_by' )->get(); + + // Getting the index of the requested route server filter within the list + $index = $listRsf->search( function ( $value, $key ) use ( $rsf ) { + return $value->id === $rsf->id; + }); + + // Adding +1 (moving up) or -1 (moving down) to the index of the route serve filter + $newIndex = $up ? $index-1 : $index+1; + $upText = $up ? 'up' : 'down'; + + // Check if the new index exist in the list + if( !$listRsf->get( $newIndex ) ) { + AlertContainer::push( "Not possible to move that route server filter " . $upText , Alert::DANGER ); + return redirect( route( "rs-filter@list", [ "cust" => $rsf->customer_id ] ) ); + } + + // Getting the route server filter object that we will have to switch + $rsfToMove = $listRsf->get( $newIndex ); + + $newOrder = $rsfToMove->order_by; + $oldOrder = $rsf->order_by; + + // temporary order to avoid unique constrain violation + $rsfToMove->order_by = 0; + $rsfToMove->save(); + + $rsf->order_by = $newOrder; + $rsf->save(); + + $rsfToMove->order_by = $oldOrder; + $rsfToMove->save(); + + AlertContainer::push( 'Route server filter moved ' . $upText, Alert::SUCCESS ); + return redirect( route( "rs-filter@list", [ "cust" => $rsf->customer_id ] ) ); + } + + /** + * Function to Delete a route serve filter + * + * @param RouteServerFilter $rsf + * + * @return RedirectResponse + * + * @throws AuthorizationException + */ + public function delete( RouteServerFilter $rsf ): RedirectResponse + { + $this->authorize( 'checkRsfObject', [ RouteServerFilter::class, $rsf ] ); + $rsf->delete(); + + Log::notice( Auth::getUser()->username." deleted the route server filter with the ID:" . $rsf->id ); + AlertContainer::push( 'Route server filter deleted.', Alert::SUCCESS ); + return Redirect::to( route( "rs-filter@list", [ "cust" => $rsf->customer_id ] ) ); + } + + /** + * Return an array of all public peering vlans names where the array key is the vlan id. + * + * @param int $custid + * + * @return array + */ + private function getPublicPeeringVLANs( int $custid ): array + { + return Vlan::select( [ 'vlan.id AS id', 'vlan.name' ] ) + ->leftJoin( 'vlaninterface AS vli', 'vli.vlanid', 'vlan.id' ) + ->leftJoin( 'virtualinterface AS vi', 'vi.id', 'vli.virtualinterfaceid' ) + ->where( 'vi.custid', $custid ) + ->where( 'vlan.private', false ) + ->where( 'vlan.peering_manager', true ) + ->where( 'vli.rsclient', true ) + ->orderBy( 'vlan.name' )->get()->keyBy( 'id' )->toArray(); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/RsPrefixesController.php b/app/Http/Controllers/RsPrefixesController.php new file mode 100644 index 000000000..180cfb7e4 --- /dev/null +++ b/app/Http/Controllers/RsPrefixesController.php @@ -0,0 +1,109 @@ + + * @author Yann Robin + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class RsPrefixesController extends Controller +{ + /** + * Display all the RsPrefixes + * + * @return View + */ + public function list(): View + { + return view( 'rs-prefixes/list' )->with([ + 'types' => RsPrefix::$SUMMARY_TYPES_FNS, + 'rsRouteTypes' => array_keys( RsPrefix::$ROUTES_TYPES_FNS ), + 'cPrefixes' => RsPrefixAggregator:: aggregateRouteSummaries() + ]); + } + + /** + * Display all the RsPrefixes for a Customer + * + * Optional get parameters: + * + * type type of Rs prefix (adv_nacc|adv_acc|nadv_acc) + * protocol protocol selected (4/6) + * + * @param Request $r + * @param Customer $cust customer + * + * @return View + */ + public function view( Request $r, Customer $cust ) : View + { + if( !in_array( $type = $r->input( 'type', false ), array_merge( [ false ], array_keys( RsPrefix::$SUMMARY_TYPES_FNS ) ), false ) ) { + abort( 404 ); + } + + if( !in_array( $protocol = $r->protocol, [ null, 4, 6 ], false ) ) { + abort( 404 ); + } + + // does the customer have VLAN interfaces that filtering is disabled on? + $totalVlanInts = $filteredVlanInts = 0; + + foreach( $cust->virtualInterfaces as $vi ) { + foreach( $vi->vlanInterfaces as $vli ) { + if( !$vli->vlan->private ){ + if( $vli->irrdbfilter ){ + $filteredVlanInts++; + } + $totalVlanInts++; + } + } + } + + return view( 'rs-prefixes/view' )->with([ + 'totalVl' => $totalVlanInts, + 'filteredVl' => $filteredVlanInts , + 'protocol' => $protocol ?? false, + 'type' => $type, + 'rsRouteTypes' => array_keys( RsPrefix::$ROUTES_TYPES_FNS ), + 'c' => $cust, + 'aggRoutes' => RsPrefixAggregator::aggregateRoutes( $cust->id, $protocol ) + ]); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/SearchController.php b/app/Http/Controllers/SearchController.php new file mode 100644 index 000000000..7c2e86764 --- /dev/null +++ b/app/Http/Controllers/SearchController.php @@ -0,0 +1,265 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class SearchController extends Controller +{ + /** + * Search different type of objects ( IP, User, Mac address) + * + * @param Request $r instance of the current HTTP request + * + * @return RedirectResponse|View + */ + public function do( Request $r ): RedirectResponse|View + { + $type = ''; + $results = $interfaces = []; + + if( $search = trim( trim( htmlspecialchars( $r->search ) ), '%' ) ) { + // what kind of search are we doing? + if( preg_match( '/^PPP\-(\d+)$/', $search, $matches ) ) { + // patch panel port search + if( $ppp = PatchPanelPort::find( $matches[1] ) ) { + return redirect( route( 'patch-panel-port@view', [ 'ppp' => $ppp->id ] ) ); + } + } + else if( preg_match( '/^xc:\s*(.*)\s*$/', $search, $matches ) ) { + // patch panel x-connect ID search + // wild card search + $type = 'ppp-xc'; + $results = PatchPanelPort::where( function( $query ) use( $matches ){ + $query->where( 'colo_circuit_ref', 'LIKE', '%' . $matches[1] . '%' ) + ->orWhere( 'colo_billing_ref', 'LIKE', '%' . $matches[1] . '%' ); + } )->orderByRaw( 'id ASC' ) + ->with( [ 'patchPanel.cabinet.location', 'customer' ] ) + ->get(); + + if( count( $results ) === 1 ) { + return redirect( route( 'patch-panel-port@view', [ 'ppp' => $results[0]->id ] ) ); + } + } + else if( preg_match( '/^\.\d{1,3}$/', $search ) || preg_match( '/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/', $search ) ) { + // IPv4 search + $type = 'ipv4'; + $result = VlanInterface::select( 'vlaninterface.*' ) + ->leftJoin( 'ipv4address AS ip', 'ip.id', 'vlaninterface.ipv4addressid' ) + ->where( 'ip.address', 'LIKE', strtolower( '%' . $search ) ) + ->with( 'virtualInterface.customer' )->get(); + + $ips = $this->processIPSearch( $result ); + $results = $ips[ 'results' ]; + $interfaces = $ips[ 'interfaces' ]; + } + else if( preg_match( '/^[a-f0-9]{12}$/', strtolower( $search ) ) || preg_match( '/^[a-f0-9]{2}:[a-f0-9]{2}:[a-f0-9]{2}:[a-f0-9]{2}:[a-f0-9]{2}:[a-f0-9]{2}$/', strtolower( $search ) ) ) { + // mac address search + $type = 'mac'; + $search = preg_replace( '/[^a-f0-9]/', '', strtolower( $search ) ); + + $vis = VirtualInterface::select( 'virtualinterface.*' ) + ->leftJoin( 'macaddress', 'macaddress.virtualinterfaceid', 'virtualinterface.id' ) + ->where( 'macaddress.mac', $search ) + ->distinct() + ->with( 'customer' )->get(); + + $vlis = VlanInterface::select( 'vlaninterface.*' ) + ->leftJoin( 'l2address', 'l2address.vlan_interface_id', 'vlaninterface.id' ) + ->where( 'l2address.mac', $search ) + ->with( 'virtualInterface.customer' )->get(); + + $discoveredMACs = $this->processMACSearch( $vis ); + $configuredMACs = $this->processMACSearch( $vlis ); + $macs = $this->mergeMacs( $discoveredMACs, $configuredMACs ); + $results = $macs[ 'results' ]; + $interfaces = $macs[ 'interfaces' ]; + } + else if( preg_match( '/^:[0-9a-fA-F]{1,4}$/', $search ) || preg_match( '/^[0-9a-fA-F]{1,4}:.*:[0-9a-fA-F]{1,4}$/', $search ) ) { + // IPv6 search + $type = 'ipv6'; + + $result = VlanInterface::select( 'vlaninterface.*' ) + ->leftJoin( 'ipv6address AS ip', 'ip.id', 'vlaninterface.ipv6addressid' ) + ->where( 'ip.address', 'LIKE', strtolower( '%' . $search ) ) + ->with( 'ipv4address' , 'ipv6address' )->get(); + + $ips = $this->processIPSearch( $result ); + $results = $ips[ 'results' ]; + $interfaces = $ips[ 'interfaces' ]; + } + else if( preg_match( '/^as(\d+)$/', strtolower( $search ), $matches ) || preg_match( '/^(\d+)$/', $search, $matches ) ) { + // user by ASN search + $type = 'asn'; + $results = Customer::where('autsys', $matches[1] )->get(); + } + else if( preg_match( '/^AS-(.*)$/', strtoupper( $search ) ) ) { + // user by ASN macro search + $type = 'asmacro'; + $results = Customer::where( 'peeringmacro', $search ) + ->orWhere( 'peeringmacrov6', $search )->get(); + } + else if( preg_match( '/^@([a-zA-Z0-9]+)$/', $search, $matches ) ) { + // user by username search + $type = 'username'; + $results[ 'users' ] = User::where( 'username', 'LIKE' , '%' . $matches[1] . '%' ) + ->with( 'customers' )->get(); + } + else if( filter_var( $search, FILTER_VALIDATE_EMAIL ) !== false ) { + // user by email search + $type = 'email'; + $results[ 'users' ] = User::where( 'email', $search ) + ->with( 'customers' )->get(); + $results[ 'contacts' ] = Contact::where( 'email', $search )->get(); + } + else if( preg_match( '/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\/\d{1,2}$/', $search ) || preg_match( '/^[0-9a-fA-F]{1,4}:.*:[0-9a-fA-F]{0,4}\/\d{1,3}$/', $search ) ) { + // rsprefix search + $type = 'rsprefix'; + $results = RsPrefix::wherePrefix( $search )->with( 'customer' )->get(); + } + else { + // wild card search + $type = 'cust_wild'; + $wildsearch = '%' . $search . '%'; + $results = Customer::select( 'cust.*' ) + ->leftJoin( 'company_registration_detail AS r', 'r.id', 'cust.company_registered_detail_id' ) + ->where( 'cust.name', 'LIKE' , $wildsearch )->orWhere( 'cust.shortname', 'LIKE' , $wildsearch ) + ->orWhere( 'cust.abbreviatedName', 'LIKE' , $wildsearch )->orWhere( 'r.registeredName', 'LIKE' , $wildsearch ) + ->orderBy( 'cust.name' )->get(); + } + } + + return view( 'search/do' )->with([ + 'results' => $results, + 'interfaces' => $interfaces, + 'type' => $type, + 'search' => $search + ]); + } + + /** + * Process the IP search (IPv4 and IPv6) + * + * @param Collection $vlis vlan interfaces list + * + * @return (\Illuminate\Database\Eloquent\Model[]|mixed)[][] + * + * @psalm-return array{results: array, interfaces: array>} + */ + private function processIpSearch( Collection $vlis ): array + { + $results = $interfaces = []; + foreach( $vlis as $vli ) { + /** @var $vli VlanInterface */ + $results[ $vli->virtualInterface->custid ] = $vli->virtualInterface->customer; + $interfaces[ $vli->virtualInterface->custid ][] = $vli; + } + return [ 'results' => $results, 'interfaces' => $interfaces ]; + } + + /** + * Process the mac address search + * + * @param Collection|null $is virtual interfaces list + * + * @return ((VirtualInterface|mixed)[]|Customer|mixed)[][] + * + * @psalm-return array{results: array, interfaces: array>} + */ + private function processMACSearch( Collection $is = null ): array + { + $results = $interfaces = []; + + foreach( $is as $i ) { + if( $i instanceof VlanInterface ) { + $c = $i->virtualInterface->customer; + $vi = $i->virtualInterface; + } else { + $c = $i->customer; + $vi = $i; + } + + $results[ $c->id ] = $c; + $interfaces[ $c->id ][] = $vi; + } + return [ 'results' => $results, 'interfaces' => $interfaces ]; + } + + /** + * Merge configured and discovered mac address results + * + * @param array $discovered + * @param array $configured + * + * @return array[] + * + * @psalm-return array{results: array, interfaces: array} + */ + private function mergeMacs( array $discovered, array $configured ): array + { + $results = $interfaces = []; + + foreach( [ $discovered, $configured ] as $a ) { + foreach( $a['results'] as $cid => $c ) { + if( !isset( $results[ $cid ] ) ) { + $results[ $cid ] = $c; + } + } + + foreach( $a['interfaces'] as $viid => $vi ) { + if( !isset( $interfaces[ $viid ] ) ) { + $interfaces[ $viid ] = $vi; + } + } + } + + return [ 'results' => $results, 'interfaces' => $interfaces ]; + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Services/Grapher.php b/app/Http/Controllers/Services/Grapher.php new file mode 100644 index 000000000..eb9d81ea7 --- /dev/null +++ b/app/Http/Controllers/Services/Grapher.php @@ -0,0 +1,214 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Services\Grapher + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Grapher extends Controller +{ + /** + * the grapher service + * + * @var GrapherService + */ + private $grapher; + + /** + * The graph object + * + * @var Graph + */ + private $graph = null; + + /** + * The request object + * + * @var Request $request + */ + private $request = null; + + + /** + * Constructor + * + * @var Request $request + * @var GrapherService $grapher + */ + public function __construct( Request $request, GrapherService $grapher ) + { + $this->grapher = $grapher; + $this->request = $request; + // NB: Construtcor happens before middleware... + } + + /** + * Grapher accessor + * + * @return GrapherService + */ + private function grapher(): GrapherService + { + return $this->grapher; + } + + /** + * Graph accessor + * + * @return Graph + * + * @throws + */ + private function graph(): Graph + { + if( $this->graph === null ) { + $this->graph = $this->request()->attributes->get('graph'); + + // if there's no graph then the middleware went wrong... safety net: + if( $this->graph === null ) { + throw new GrapherGeneralException('Middleware could not load graph but did not throw a 404'); + } + } + return $this->graph; + } + + /** + * Request accessor + * + * @return Request + */ + private function request(): Request + { + return $this->request; + } + + private function simpleResponse( Request $request ): Response + { + return (new Response( call_user_func( [ $this->graph(), $this->graph()->type() ] ) ) ) + ->header('Content-Type', Graph::CONTENT_TYPES[ $this->graph()->type() ] ) + ->header('Content-Disposition', sprintf( 'inline; filename="grapher-%s-%s-%s-%s.%s"', + $this->graph()->backend()->name(), $this->graph()->category(), + $this->graph()->period(), $this->graph()->protocol(), $this->graph()->type() ) + ) + ->header( 'Expires', Carbon::now()->addMinutes(5)->toRfc1123String() ); + } + + public function ixp( Request $request ): Response + { + return $this->simpleResponse( $request ); + } + + public function infrastructure( Request $request ): Response + { + return $this->simpleResponse( $request ); + } + + public function vlan( Request $request ): Response + { + return $this->simpleResponse( $request ); + } + + public function location( Request $request ): Response + { + return $this->simpleResponse( $request ); + } + + public function switch( Request $request ): Response + { + return $this->simpleResponse( $request ); + } + + public function corebundle( Request $request ): Response + { + return $this->simpleResponse( $request ); + } + + public function trunk( Request $request ): Response + { + return $this->simpleResponse( $request ); + } + + public function physicalInterface( Request $request ): Response + { + return $this->simpleResponse( $request ); + } + + public function virtualInterface( Request $request ): Response + { + return $this->simpleResponse( $request ); + } + + public function customer( Request $request ): Response + { + return $this->simpleResponse( $request ); + } + + public function vlanInterface( Request $request ): Response + { + return $this->simpleResponse( $request ); + } + + public function latency( Request $request ): Response + { + return $this->simpleResponse( $request ); + } + + public function p2p( Request $request ): Response + { + return $this->simpleResponse( $request ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Services/Grapher/Api.php b/app/Http/Controllers/Services/Grapher/Api.php new file mode 100644 index 000000000..84f1e696a --- /dev/null +++ b/app/Http/Controllers/Services/Grapher/Api.php @@ -0,0 +1,94 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Services\Grapher + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Api extends Controller +{ + /** + * The grapher service + * + * @var GrapherService + */ + private GrapherService $grapher; + + /** + * Constructor + * @param Request $request + * @param GrapherService $grapher + */ + public function __construct( Request $request, GrapherService $grapher ) + { + $this->grapher = $grapher; + } + + /** + * Grapher accessor + * + * @return GrapherService + */ + private function grapher(): GrapherService + { + return $this->grapher; + } + + public function generateConfiguration( Request $request ): Response + { + // get the appropriate backend + $grapher = $this->grapher()->backend( $request->input( 'backend', 'mrtg' ) ); + + if( !$grapher->isConfigurationRequired() ) { + abort( 404, "This grapher backend (" . $grapher->name() . ") does not require any configuration to be generated" ); + } + + if( !$grapher->isMonolithicConfigurationSupported() ) { + abort( 404, "This backend ({$grapher->name()}) does not support single configuration files" ); + } + + $config = $grapher->generateConfiguration( GrapherBackendContract::GENERATED_CONFIG_TYPE_MONOLITHIC, $request->input() )[0]; + + + return (new Response( $config ) ) + ->header('Content-Type', "text/plain" ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Services/LookingGlass.php b/app/Http/Controllers/Services/LookingGlass.php new file mode 100644 index 000000000..dacceda2b --- /dev/null +++ b/app/Http/Controllers/Services/LookingGlass.php @@ -0,0 +1,373 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Services\LookingGlass + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class LookingGlass extends Controller +{ + /** + * the LookingGlass + * + * @var LookingGlassContract + */ + private $lg = null; + + /** + * The request object + * + * @var Request $request + */ + private $request = null; + + /** + * Constructor + * + * @param Request $request + */ + public function __construct( Request $request ) + { + // NB: Constructor happens before middleware... + $this->request = $request; + } + + /** + * Looking glass accessor + * + * @return LookingGlassContract + * + * @throws + */ + private function lg(): LookingGlassContract + { + if( $this->lg === null ) { + $this->lg = $this->request()->attributes->get('lg' ); + // if there's no graph then the middleware went wrong... safety net: + if( $this->lg === null ) { + throw new LookingGlassGeneralException('Middleware could not load looking glass but did not throw a 404' ); + } + } + return $this->lg; + } + + /** + * Request accessor + * + * @return Request + */ + private function request(): Request + { + return $this->request; + } + + /** + * Add view parameters common for all requests. + * + * @param View $view + * + * @return View + * + * @throws + */ + private function addCommonParams( View $view ): View + { + $cust = Auth::check() ? Customer::find( Auth::getUser()->custid ) : null; + $user = Auth::check() ? User::find( Auth::id() ) : null; + + $view->with( 'status', json_decode( $this->lg()->status(), false, 512, JSON_THROW_ON_ERROR)); + $view->with( 'lg', $this->lg() ); + $view->with( 'routers', RouterAggregator::forDropdown( $cust, $user ) ); + $view->with( 'tabRouters', RouterAggregator::forTab( $cust, $user ) ); + return $view; + } + + /** + * Index page + * + * @return View + * + * @throws + */ + public function index(): View + { + $cust = Auth::check() ? Customer::find( Auth::getUser()->custid ) : null; + $user = Auth::check() ? User::find( Auth::id() ) : null; + + return view('services/lg/index' )->with( [ + 'lg' => false, + 'routers' => RouterAggregator::forDropdown( $cust, $user ), + 'tabRouters' => RouterAggregator::forTab( $cust, $user ) + ] ); + } + + /** + * Returns the router's status as JSON + * + * @param string $handle + * + * @return Response JSON of status + * + * @throws + */ + public function status( string $handle ): Response + { + // get the router status + return response() + ->make( $this->lg()->status() ) + ->header('Content-Type', 'application/json' ); + } + + /** + * Returns the router's "bgp summary" as JSON + * + * @param string $handle + * @return Response JSON of status + * + * @throws + */ + public function bgpSummaryApi( string $handle ): Response + { + // get the router status + return response() + ->make( $this->lg()->bgpSummary() ) + ->header('Content-Type', 'application/json'); + } + + /** + * @param string $handle + * + * @return View + * + * @throws + */ + public function bgpSummary(string $handle ): View + { + // get bgp protocol summary + $view = view('services/lg/bgp-summary' )->with([ + 'content' => json_decode( $this->lg()->bgpSummary(), false, 512, JSON_THROW_ON_ERROR), + ]); + + return $this->addCommonParams( $view ); + } + + /** + * @param string $handle + * @param string $table + * + * @return RedirectResponse|View + * + * @throws + */ + public function routesForTable( string $handle, string $table ): RedirectResponse|View + { + $tooManyRoutesMsg = "The routing table {$table} has too many routes to display in the web interface. Please use " + . " $this->lg()->router()->handle ] ) + . "\">the route search tool to query this table."; + + try{ + $routes = $this->lg()->routesForTable( $table ); + } catch( ErrorException $e ) { + if( strpos( $e->getMessage(), 'HTTP/1.0 403' ) !== false ) { + return redirect( 'lg/' . $handle )->with( 'msg', $tooManyRoutesMsg ); + } + return redirect( 'lg/' . $handle )->with('msg', 'An error occurred - please contact our support team if you wish.' ); + } + + if( $routes === "" ) { + return redirect( 'lg/' . $handle )->with( 'msg', $tooManyRoutesMsg ); + } + + $view = view('services/lg/routes' )->with([ + 'content' => json_decode($routes, false, 512, JSON_THROW_ON_ERROR), + 'source' => 'table', 'name' => $table + ]); + + return $this->addCommonParams( $view ); + } + + /** + * @param string $handle + * @param string $protocol + * + * @throws + */ + public function routesForProtocol( string $handle, string $protocol ): RedirectResponse|View + { + try{ + // get bgp protocol summary + $view = view('services/lg/routes' )->with([ + 'content' => json_decode( $this->lg()->routesForProtocol( $protocol ), false, 512, JSON_THROW_ON_ERROR), + 'source' => 'protocol', 'name' => $protocol + ]); + return $this->addCommonParams( $view ); + } catch( \Exception $e ){ + AlertContainer::push( 'The available resource is not available. Most likely the amount of routes exceed the APIs configured maximum threshold.', Alert::DANGER ); + return redirect( route( "lg::bgp-sum", [ 'handle' => $handle ] ) ); + } + } + + /** + * @param string $handle + * @param string $protocol + * + * @return View + * + * @throws + */ + public function routesForExport( string $handle, string $protocol ): View + { + // get bgp protocol summary + $view = view('services/lg/routes' )->with([ + 'content' => json_decode( $this->lg()->routesForExport( $protocol ), false, 512, JSON_THROW_ON_ERROR), + 'source' => 'export to protocol', + 'name' => $protocol + ]); + return $this->addCommonParams( $view ); + } + + /** + * @param string $handle + * @param string $network + * @param string $mask + * @param string $protocol + * + * @return View + * + * @throws + */ + public function routeProtocol( string $handle, string $network, string $mask, string $protocol ): View + { + return view('services/lg/route' )->with([ + 'content' => json_decode($this->lg()->protocolRoute($protocol, $network, (int) $mask), false, 512, + JSON_THROW_ON_ERROR), + 'source' => 'protocol', + 'name' => $protocol, + 'lg' => $this->lg(), + 'net' => urldecode( $network.'/'.$mask ), + ]); + } + + /** + * @param string $handle + * @param string $network + * @param string $mask + * @param string $table + * + * @return View + * + * @throws + */ + public function routeTable( string $handle, string $network, string $mask, string $table ): View + { + return view('services/lg/route')->with( [ + 'content' => json_decode( $this->lg()->protocolTable( $table, $network, (int)$mask ), false ), + 'source' => 'table', + 'name' => $table, + 'lg' => $this->lg(), + 'net' => urldecode($network . '/' . $mask), + ]); + } + + /** + * @param string $handle + * @param string $network + * @param string $mask + * @param string $protocol + * + * @return View + * + * @throws + */ + public function routeExport( string $handle, string $network, string $mask, string $protocol ): View + { + return view('services/lg/route' )->with([ + 'content' => json_decode( $this->lg()->exportRoute( $protocol, $network, (int)$mask ), false ), + 'source' => 'export', + 'name' => $protocol, + 'lg' => $this->lg(), + 'net' => urldecode( $network . '/' . $mask ), + ]); + } + + /** + * @param string $handle + * + * @return View + * + * @throws + */ + public function routeSearch( string $handle ): View + { + $view = view('services/lg/route-search' )->with( [ + 'content' => json_decode( $this->lg()->symbols(), false ), + ]); + return $this->addCommonParams( $view ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/SettingsController.php b/app/Http/Controllers/SettingsController.php new file mode 100644 index 000000000..c11d9b9b0 --- /dev/null +++ b/app/Http/Controllers/SettingsController.php @@ -0,0 +1,210 @@ + + * @author Barry O'Donovan + * @category IXP + * @package IXP\Http\Controllers + * @copyright Copyright (C) 2009 - 2025 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class SettingsController extends Controller +{ + protected array $fe_settings; + + + public function __construct() + { + $this->fe_settings = config( 'ixp_fe_settings' ); + } + + /** + * Collect rules from the fe_settings configuration file which contains + * arrays of 'panels' for the UI. + * + * @return array + */ + private function gatherRules(): array + { + $rules = []; + + foreach( $this->fe_settings[ "panels" ] as $panel ) { + foreach( $panel[ "fields" ] as $label => $field ) { + if( isset( $field[ "rules" ] ) && $field[ "rules" ] !== '' ) { + $rules[ $label ] = $field[ "rules" ]; + } + } + } + + return $rules; + } + + + /** + * Display the form to edit an object + */ + protected function index(): View + { + try { + $this->checkIfDotEnvIsCompatible(); + } catch( Exception $e ) { + + AlertContainer::push( $e->getMessage(), Alert::DANGER ); + + return view( 'settings.compatibility' )->with( [ + 'exception' => $e, + ] ); + } + + return view( 'settings.index' )->with( [ + 'settings' => $this->fe_settings, + 'rules' => [], //$this->gatherRules(), + ] ); + } + + + /** + * @throws DotEnvParserException + * @throws Exception + */ + private function checkIfDotEnvIsCompatible() + { + if( !file_exists( base_path( '.env' ) ) ) { + throw new Exception( "The .env file is missing. Please create it and try again." ); + } + + if( !is_writable( base_path( '.env' ) ) ) { + throw new Exception( "The .env file is can not be written to. Please check the file permissions and try again." ); + } + + if( !( $env = file_get_contents( base_path( '.env' ) ) ) ) { + throw new Exception( "The .env file is empty. Please add some settings and try again." ); + } + + new DotEnvParser( $env )->parse(); + } + + /** + * @throws DotEnvParserException + * @throws Exception + */ + private function loadDotEnv(): DotEnvContainer + { + if( !file_exists( base_path( '.env' ) ) ) { + throw new Exception( "The .env file is missing. Please create it and try again." ); + } + + if( !( $env = file_get_contents( base_path( '.env' ) ) ) ) { + throw new Exception( "The .env file is empty. Please add some settings and try again." ); + } + + + return new DotEnvContainer( new DotEnvParser( $env )->parse()->settings() ); + } + + /** + * @throws Exception + */ + private function saveDotEnv( string $dotEnv ): void + { + if( !file_exists( base_path( '.env' ) ) ) { + throw new Exception( "The .env file is missing. Please create it and try again." ); + } + + if( !is_writable( base_path( '.env' ) ) ) { + throw new Exception( "The .env file is can not be written to. Please check the file permissions and try again." ); + } + + if( !( file_put_contents( base_path( '.env' ), $dotEnv ) ) ) { + throw new Exception( "Could not write to the .env file. Please check the file permissions and try again." ); + } + } + + /** + * Function to do the actual validation and storing of the submitted object. + * + * @param Request $request + * @return RedirectResponse + */ + public function update( Request $request ): RedirectResponse + { + $validated = $request->validate( $this->gatherRules() ); + + try { + // only interested in saving settings where the value has changed + $dotenv = $this->loadDotEnv(); + + foreach( $this->fe_settings[ "panels" ] as $panel ) { + foreach( $panel[ "fields" ] as $fname => $fconfig ) { + + $orig = config( $fconfig[ "config_key" ] ); + if( isset( $fconfig[ "invert" ] ) && $fconfig[ "invert" ] ) { + $orig = !$orig; + } + + if( !isset( $validated[ $fname ] ) || $validated[ $fname ] == $orig ) { + continue; + } + + // update dotenv container + if( $dotenv->isset( $fconfig[ 'dotenv_key' ] ) ) { + $dotenv->updateValue( $fconfig[ 'dotenv_key' ], $validated[ $fname ] ); + } else { + // include blank line + $dotenv->set( null, null, null ); + $dotenv->set( $fconfig[ 'dotenv_key' ], $validated[ $fname ] ); + } + + } + } + + $this->saveDotEnv( new DotEnvWriter( $dotenv->settings() )->generateContent() ); + + } catch( DotEnvParserException|Exception $e ) { + AlertContainer::push( $e->getMessage(), Alert::DANGER ); + return redirect()->back(); + } + + AlertContainer::push( 'Settings have been successfully updated', Alert::SUCCESS ); + return redirect( route( 'settings@index') ); + } + +} diff --git a/app/Http/Controllers/StatisticsController.php b/app/Http/Controllers/StatisticsController.php new file mode 100644 index 000000000..7892c3acd --- /dev/null +++ b/app/Http/Controllers/StatisticsController.php @@ -0,0 +1,915 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers + * @copyright Copyright (C) 2009 - 2024 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class StatisticsController extends Controller +{ + /** + * Process and update request parameters for standard graph attributes: period, category, protocol, type. + * + * These are safe for use from the request. + * + * @param StatisticsRequest $request + * + * @return void + */ + private function processGraphParams( StatisticsRequest $request ): void + { + $request->period = Graph::processParameterPeriod( $request->period ); + $request->category = Graph::processParameterCategory( $request->category ); + $request->protocol = Graph::processParameterProtocol( $request->protocol ); + $request->type = Graph::processParameterType( $request->type ); + } + + /** + * Show overall IXP graphs + * + * @param string $category Category of graph to show (e.g. bits / pkts) + * + * @return View + * + * @throws + */ + public function ixp( string $category = Graph::CATEGORY_BITS ) : View + { + $category = Graph::processParameterCategory( $category, true ); + + $graph = App::make( Grapher::class ) + ->ixp()->setType( Graph::TYPE_PNG ) + ->setProtocol( Graph::PROTOCOL_ALL )->setCategory( $category ); + + $graph->authorise(); + + return view( 'statistics/ixp' )->with( [ + 'graph' => $graph, + 'category' => $category, + ] ); + } + + /** + * Show IXP infrastructure graphs + * + * @param Infrastructure|null $infra Infrastructure to show the graph of + * @param string $category Category of graph to show (e.g. bits / pkts) + * + * @return View + * + * @throws + */ + public function infrastructure( ?Infrastructure $infra = null, string $category = Graph::CATEGORY_BITS ) : View + { + $infras = Infrastructure::select( [ 'name', 'id' ] ) + ->orderBy( 'name' )->get(); + + $inf = $infra ?? $infras->first(); + $category = Graph::processParameterCategory( $category, true ); + + $graph = App::make( Grapher::class ) + ->infrastructure( $inf )->setType( Graph::TYPE_PNG ) + ->setProtocol( Graph::PROTOCOL_ALL )->setCategory( $category ); + + $graph->authorise(); + + return view( 'statistics/infrastructure' )->with( [ + 'infras' => $infras, + 'infra' => $inf, + 'graph' => $graph, + 'category' => $category, + ] ); + } + + /** + * Show Vlan (sflow) graphs + * + * @param Vlan|null $vlan VLAN to show the graph of + * @param string $protocol IPv4/6 + * @param string $category + * + * @return View + * + * @throws ParameterException + * @throws BindingResolutionException + */ + public function vlan( ?Vlan $vlan = null, string $protocol = Graph::PROTOCOL_IPV4, string $category = Graph::CATEGORY_BITS ) : View + { + $vlans = Vlan::publicOnly() + ->where( 'peering_matrix', true ) + ->orWhere( 'peering_manager', true ) + ->orderBy( 'name' )->get(); + + if( !$vlans->count() ) { + abort( 404, 'No VLANs available for graphing' ); + } + + $protocol = Graph::processParameterRealProtocol( $protocol ); + $category = Graph::processParameterCategory( $category, true ); + $vl = $vlan ?? $vlans->first(); + $graph = App::make( Grapher::class ) + ->vlan( $vl )->setType( Graph::TYPE_PNG ) + ->setProtocol( $protocol )->setCategory( $category ); + + try { + $graph->backend(); + } catch( GraphCannotBeProcessedException $e ) { + abort( 404, 'No backend available to process VLAN graphs' ); + } + + $graph->authorise(); + + return view( 'statistics/vlan' )->with( [ + 'vlans' => $vlans, + 'vlan' => $vl, + 'graph' => $graph, + 'protocol' => $protocol, + 'category' => $category, + ] ); + } + + /** + * Show IXP switch graphs + * + * @param Switcher|null $switch Switch to show the graph of + * @param string $category Category of graph to show (e.g. bits / pkts) + * + * @return View + * @throws BindingResolutionException + * @throws ParameterException + */ + public function switch( ?Switcher $switch = null, string $category = Graph::CATEGORY_BITS ) : View + { + $switches = Switcher::where( 'active', true ) + ->orderBy( 'name' )->get() + ->keyBy( 'id' ); + + $category = Graph::processParameterCategory( $category, true ); + $s = $switch ?? $switches->first(); + $graph = App::make( Grapher::class ) + ->switch( $s )->setType( Graph::TYPE_PNG ) + ->setProtocol( Graph::PROTOCOL_ALL )->setCategory( $category ); + + $graph->authorise(); + + return view( 'statistics/switch' )->with([ + 'switches' => $switches, + 'switch' => $s, + 'graph' => $graph, + 'category' => $category, + ]); + } + + /** + * Show IXP location graphs + * + * @param Location|null $location Location§ to show the graph of + * @param string $category Category of graph to show (e.g. bits / pkts) + * + * @return View + * @throws BindingResolutionException + * @throws ParameterException + */ + public function location( ?Location $location = null, string $category = Graph::CATEGORY_BITS ) : View + { + $locations = Location::orderBy( 'name' )->get() + ->keyBy( 'id' ); + + $category = Graph::processParameterCategory( $category, true ); + $l = $location ?? $locations->first(); + $graph = App::make( Grapher::class ) + ->location( $l )->setType( Graph::TYPE_PNG ) + ->setProtocol( Graph::PROTOCOL_ALL )->setCategory( $category ); + + $graph->authorise(); + + return view( 'statistics/location' )->with([ + 'locations' => $locations, + 'location' => $l, + 'graph' => $graph, + 'category' => $category, + ]); + } + + /** + * Show IXP trunk graphs + * + * @param string|null $trunk ID of the trunk to show the graph of + * @param string $category Category of graph to show (e.g. bits / pkts) + * + * @return RedirectResponse|View + * + * @throws ParameterException + */ + public function trunk( ?string $trunk = null, string $category = Graph::CATEGORY_BITS ): RedirectResponse|View + { + if( !is_array( config('grapher.backends.mrtg.trunks') ) || !count( config('grapher.backends.mrtg.trunks') ) ) { + AlertContainer::push( + "Trunk graphs have not been configured. Please see this documentation for instructions.", + Alert::DANGER + ); + return redirect(''); + } + + // get the available graphs + $images = []; + $graphs = []; + foreach( config('grapher.backends.mrtg.trunks') as $g ) { + $images[] = $g[ 'name' ]; + $graphs[ $g['name'] ] = $g[ 'title' ]; + } + + if( !in_array( $trunk, $images, true ) ) { + $trunk = $images[ 0 ]; + } + + $graph = App::make( Grapher::class ) + ->trunk( $trunk )->setType( Graph::TYPE_PNG ) + ->setProtocol( Graph::PROTOCOL_ALL )->setCategory( Graph::CATEGORY_BITS ); + + $graph->authorise(); + + return view( 'statistics/trunk' )->with( [ + 'graphs' => $graphs, + 'trunkid' => $trunk, + 'graph' => $graph, + 'category' => $category, + ] ); + } + + /** + * Display all member graphs + * + * @param StatisticsRequest $r + * + * @return View + * + * @throws ParameterException + */ + public function members( StatisticsRequest $r ): View + { + if( !CustomerGraph::authorisedForAllCustomers() ) { + abort( 403, "You are not authorised to view this member's graphs." ); + } + + $grapher = App::make( Grapher::class ); + $this->processGraphParams( $r ); + + // do we have an infrastructure or vlan? + $vlan = $infra = false; + + if( $r->infra ) { + if( $infra = Infrastructure::find( $r->infra ) ) { + $targets = VirtualInterfaceAggregator::getForInfrastructure( $infra ); + } else { + $targets = Customer::currentActive( true, false )->get(); + } + $r->protocol = Graph::PROTOCOL_ALL; + } else if( $r->vlan && ( $vlan = Vlan::find( $r->vlan ) ) ) { + if( !in_array( $r->protocol, Graph::PROTOCOLS_REAL, true ) ) { + $r->protocol = Graph::PROTOCOL_IPV4; + } + $targets = VlanInterfaceAggregator::forVlan( $vlan, $r->protocol ); + } else { + $targets = []; + } + + $graphs = []; + foreach( $targets as $t ) { + if( !$t->isGraphable() ) { + continue; + } + + if( $infra ) { + $g = $grapher->virtint( $t ); + } else if( $vlan ) { + $g = $grapher->vlanint( $t ); + } else { + $g = $grapher->customer( $t ); + } + + /** @var Graph $g */ + $g->setType( Graph::TYPE_PNG ) + ->setProtocol( $r->protocol ) + ->setCategory( $r->category ) + ->setPeriod( $r->period ); + + $g->authorise(); + $graphs[] = $g; + } + + return view( 'statistics/members' )->with([ + 'graph' => $graphs[ 0 ] ?? false, // sample graph as all types/protocols/categories/periods will be the same + 'graphs' => $graphs, + 'r' => $r, + 'infras' => Infrastructure::orderBy( 'name' )->get(), + 'infra' => $infra ?? false, + 'vlans' => Vlan::publicOnly()->orderBy('number')->get(), + 'vlan' => $vlan ?? false, + ]); + } + + /** + * Display all graphs for a member + * + * @param StatisticsRequest $r + * @param Customer|null $cust the member + * + * @return RedirectResponse|View + * + */ + public function member( StatisticsRequest $r, ?Customer $cust = null ): RedirectResponse|View + { + if( !$cust && Auth::check() ) { + $cust = Auth::getUser()->customer; + } + + if( $cust == null ) { + abort( 403, "You are not authorised to view this member's graphs." ); + } + + $grapher = App::make( Grapher::class ); + + // if the customer is authorised, then so too are all of their virtual and physical interfaces: + try { + $grapher->customer( $cust )->authorise(); + } catch( AuthorizationException $e ) { + abort( 403, "You are not authorised to view this member's graphs." ); + } + + if( !$cust->hasInterfacesConnectedOrInQuarantine() ) { + AlertContainer::push("This customer has no graphable interfaces (i.e. no physical interfaces in quarantine or connected)", Alert::WARNING); + return redirect()->back(); + } + + return view( 'statistics/member' )->with([ + "c" => $cust->load( [ + 'virtualinterfaces.physicalInterfaces.switchPort.switcher', + 'virtualinterfaces.physicalInterfaces.switchPort.switcher.cabinet.location', + ] ), + "grapher" => $grapher, + "category" => Graph::processParameterCategory( $r->category ), + "period" => Graph::processParameterPeriod( $r->period ), + ]); + } + + /** + * Display Aggregate/LAG/Port for all periods (day/week/month/year) + * + * @param StatisticsRequest $r + * @param string $type type + * @param integer $typeid ID of type + * + * @return View + * + * @throws ParameterException + */ + public function memberDrilldown( StatisticsRequest $r, string $type, int $typeid ): View + { + switch( strtolower( $type ) ) { + case 'agg': + $c = Customer::findOrFail( $typeid ); + $graph = App::make( Grapher::class )->customer( $c ); + break; + case 'vi': + $vi = VirtualInterface::findOrFail( $typeid ); + $c = $vi->customer; + $graph = App::make( Grapher::class )->virtint( $vi ); + break; + case 'pi': + $pi = PhysicalInterface::findOrFail( $typeid ); + $c = $pi->virtualInterface->customer; + $graph = App::make( Grapher::class )->physint( $pi ); + break; + default: + abort( 404, 'Unknown graph type' ); + } + + /** @var Graph $graph */ + $graph->setCategory( Graph::processParameterCategory( $r->category ) ); + $graph->authorise(); + + return view( 'statistics/member-drilldown' )->with( [ + 'c' => $c, + 'graph' => $graph, + ]); + } + + /** + * Show latency graphs + * + * @param VlanInterface $vli + * @param string $protocol + * + * @return View|RedirectResponse + * + * @throws ParameterException|AuthorizationException + */ + public function latency( VlanInterface $vli, string $protocol ): View|RedirectResponse + { + $protocol = Graph::processParameterProtocol( $protocol ); + + $graph = App::make( Grapher::class )->latency( $vli )->setProtocol( $protocol ); + $graph->authorise(); + + $fnEnabled = strtolower( $protocol ) . 'enabled'; + $fnCanping = strtolower( $protocol ) . 'canping'; + $fnAddress = strtolower( $protocol ) . 'address'; + + if( !$vli->$fnEnabled || !$vli->$fnCanping ) { + AlertContainer::push( + "Protocol or ping not enabled on the requested interface", + Alert::WARNING + ); + return redirect( route( "statistics@member", [ "id" => $vli->virtualInterface->customer->id ] ) ); + } + + return view( 'statistics/latency' )->with([ + 'c' => $vli->virtualInterface->customer, + 'vli' => $vli, + 'ip' => $vli->$fnAddress->address, + 'protocol' => $protocol, + 'graph' => $graph, + ]); + } + + + + + /** + * sFlow Peer to Peer statistics + * + * @param Request $request + * @param Customer|null $customer + * + * @return (Customer|VlanInterface|\Illuminate\Support\Collection|mixed|null|string)[] + * + * @throws ParameterException + * + * @psalm-return array{c: Customer|null, category: null|string, period: null|string, protocol: null|string, srcVlis: mixed, srcVli: VlanInterface|mixed, dstVlis: \Illuminate\Support\Collection, dstVli: VlanInterface|null} + */ + public function p2pPrepare( Request $request, ?Customer $customer, ?VlanInterface $dstVli = null ): array + { + if( !$customer ) { + $customer = Auth::user()->customer; + } + + $requestCategory = Graph::processParameterCategory( $request->category, true ); + $requestPeriod = Graph::processParameterPeriod( $request->period ); + $requestProtocol = Graph::processParameterRealProtocol( $request->protocol ); + + // on the p2ps page, we need to provide a dropdown of all possible source vlis for this customer + $srcVlis = VlanInterface::select( [ 'vli.*' ] ) + ->from( 'vlaninterface AS vli' ) + ->join( 'virtualinterface AS vi', 'vi.id', 'vli.virtualinterfaceid' ) + ->join( 'cust AS c', 'c.id', 'vi.custid' ) + ->join( 'vlan AS v', 'v.id', 'vli.vlanid' ) + ->where( 'c.id', $customer->id ) + ->where( 'vli.' . $requestProtocol . 'enabled', '1' ) + ->with( [ 'vlan' ] ) + ->orderBy( 'v.number' )->get()->keyBy( 'id' ); + + // Find the possible VLAN interfaces that this customer has for the given IXP + if( !count( $srcVlis ) ) { + throw new ParameterException("There were no interfaces available for the given criteria."); + } + + // svli will hold a source vli selected by the user on the p2ps page or else a default + /** @var VlanInterface $srcVli */ + $srcVli = $srcVlis[ $srcVlis->first()->id ]; + if( ( $svlid = $request->svli ) && isset( $srcVlis[ $svlid ] ) ) { + $srcVli = $srcVlis[ $svlid ]; + } + + // is the requested protocol supported? (we have no protocols on private vlans) + if( !$srcVli->vlan->private && !$srcVli->ipvxEnabled( $requestProtocol ) ) { + throw new ParameterException(Graph::resolveProtocol( $requestProtocol ) . " is not supported on the requested VLAN interface." ); + } + + // Now find the possible other VLAN interfaces that this customer could exchange traffic with + // (as well as removing the source vli) + $dstVlis = VlanInterfaceAggregator::forVlan( $srcVli->vlan ); + unset( $dstVlis[ $srcVli->id ] ); + + if( !$dstVlis->count() ) { + throw new ParameterException("There were no destination interfaces available for traffic exchange for the given criteria." ); + } + + if( $dstVli ) { + if( $srcVli->vlan->id !== $dstVli->vlan->id ) { + // otherwise, is there an appropriate dstVLi on srcVli->vlan? + foreach( $dstVli->virtualInterface->customer->virtualInterfaces as $vi ) { + foreach( $vi->vlanInterfaces as $vli ) { + if( $vli->vlan->id === $srcVli->vlan->id ) { + $dstVli = $vli; + } + } + } + } + + if( $srcVli->vlan->id !== $dstVli->vlan->id ) { + // okay, bounce back to p2p overview page + throw new ParameterException( "No valid destination customer in this peering LAN" ); + } + } + + + return [ + 'c' => $customer, + 'category' => $requestCategory, + 'period' => $requestPeriod, + 'protocol' => $requestProtocol, + 'srcVlis' => $srcVlis, + 'srcVli' => $srcVli, + 'dstVlis' => $dstVlis, + 'dstVli' => $dstVli, + ]; + } + + + + /** + * sFlow Peer to Peer statistics + * + * @param Request $request + * @param Customer|null $customer + * + * @return RedirectResponse|View + * + * @throws ParameterException + */ + public function p2ps( Request $request, ?Customer $customer = null ): RedirectResponse|View + { + try { + $data = $this->p2pPrepare( $request, $customer ); + } catch( ParameterException $e ) { + AlertContainer::push( $e->getMessage(), Alert::WARNING ); + return redirect()->back(); + } + + // for larger IXPs, it's quite intensive to display all the graphs - decide if we need to do this or not + $showGraphs = $request->input( 'show_graphs', config('grapher.backends.sflow.show_graphs_on_index_page') ? 'show' : 'hide' ) === 'show'; + $orderBy = $request->input( 'order_by', 'traffic' ); + + // ordering is by customer name by default due to VlanInterfaceAggregator::forVlan() + if( $orderBy !== 'name' ) { + $trafficByPeerId = P2pDailyStats::latestTotalTraffic( $data['c']); + + /** @var VlanInterface $dstVli */ + foreach( $data['dstVlis'] as $idx => $dstVli) { + $data['dstVlis'][$idx]->total_traffic = $trafficByPeerId[ $dstVli->virtualInterface->custid ] ?? 0; + } + + $data['dstVlis'] = $data['dstVlis']->sortByDesc( 'total_traffic', SORT_NUMERIC ); + } + + + // authenticate on one of the graphs + $graph = App::make( Grapher::class ) + ->p2p( $data['srcVli'], $data['dstVlis'][ $data['dstVlis']->first()->id ] ) + ->setProtocol( $data['protocol'] ) + ->setCategory( $data['category'] ) + ->setPeriod( $data['period'] ); + $graph->authorise(); + + return view( 'statistics/p2ps', array_merge( $data, [ + 'graph' => $graph, + 'orderBy' => $orderBy, + 'showGraphs' => $showGraphs, + ]) ); + } + + /** + * sFlow Peer to Peer statistics + * + * @param Request $request + * @param VlanInterface $srcVli + * @param VlanInterface $dstVli + * @return RedirectResponse|View + * @throws ParameterException + */ + public function p2pPost( Request $request ): RedirectResponse|View + { + // svli and dvli via top of page post form. + // svli may have changed and so dvli may not be appropriate anymore + $srcVli = VlanInterface::findOrFail($request->svli); + $dstVli = VlanInterface::findOrFail($request->dvli); + + return $this->p2p( $request, $srcVli, $dstVli ); + } + + /** + * sFlow Peer to Peer statistics + * + * @param Request $request + * @param VlanInterface $srcVli + * @param VlanInterface $dstVli + * @return RedirectResponse|View + * @throws ParameterException + */ + public function p2p( Request $request, VlanInterface $srcVli, VlanInterface $dstVli ): RedirectResponse|View + { + try { + $data = $this->p2pPrepare( $request, $srcVli->virtualInterface->customer, $dstVli ); + $srcVli = $data['srcVli']; + $dstVli = $data['dstVli']; + } catch( ParameterException $e ) { + AlertContainer::push( $e->getMessage(), Alert::WARNING ); + return redirect()->back(); + } + + if( !$srcVli->ipvxEnabled( $data['protocol'] ) || !$dstVli->ipvxEnabled( $data['protocol'] ) ) { + AlertContainer::push( Graph::resolveProtocol( $data['protocol'] ) . " is not supported on the requested VLAN interfaces.", Alert::WARNING ); + return redirect( route('statistics@p2ps-get', ['customer' => $srcVli->virtualInterface->custid ] ) ); + } + + $possibleProtocols = []; + foreach( Graph::PROTOCOL_REAL_DESCS as $p => $desc ) { + if( $srcVli->ipvxEnabled($p) && $dstVli->ipvxEnabled($p) ) { + $possibleProtocols[$p] = $desc; + } + } + + $graph = App::make( Grapher::class ) + ->p2p( $srcVli, $dstVli ) + ->setProtocol( $data['protocol'] ) + ->setCategory( $data['category'] ) + ->setPeriod( $data['period'] ); + + $graph->authorise(); + + return view( 'statistics/p2p-single', array_merge( $data, [ + 'graph' => $graph, + 'dstVli' => $dstVli, + 'possibleProtocols' => $possibleProtocols, + ]) ); + } + + + + /** + * Show p2p stats for a given customer and day. + * + * @param Request $r + * + * @return \Illuminate\Foundation\Application|\Illuminate\Routing\Redirector|RedirectResponse + * + * @throws + */ + public function p2pTable( Request $r ): View|RedirectResponse + { + if( !Auth::check() ) { + abort( 403, "You are not authorised to view this page." ); + } + + if( !Auth::user()->isSuperUser() ) { + $r->merge( [ 'custid' => Auth::user()->custid ] ); + } + + $days = P2pDailyStats::select('day')->distinct('day')->orderBy('day','desc')->get()->pluck('day')->toArray(); + + if( empty( $days ) ) { + AlertContainer::push( "The P2P daily stats database table is empty.", Alert::WARNING ); + return redirect( route('statistics@member', ['cust' => $r->custid ] ) ); + } + + if( !$r->day || !in_array( $r->day, $days ) ) { + $r->merge( [ 'day' => $days[0] ] ); + } + + $customers = Customer::currentActive(true,true,true) + ->get()->keyBy('id'); + + $stats = []; + if( $r->custid ) { + $stats = P2pDailyStats::with('peer') + ->where('day', $r->day)->where('cust_id', $r->custid )->get(); + } + + return view( 'statistics/p2p-table' )->with( [ + 'day' => $r->day, + 'days' => $days, + 'stats' => $stats, + 'customers' => $customers, + 'c' => $r->custid ? $customers[$r->custid] : false, + ] ); + } + + + + + + + /** + * Show daily traffic for customers in a table. + * + * @param Request $r + * + * @return View + * + * @throws + */ + public function leagueTable( Request $r ): View + { + $metrics = [ + 'Total' => 'data', + 'Max' => 'max', + 'Average' => 'average', + ]; + + $metric = $r->input( 'metric', $metrics['Total'] ); + if( !in_array( $metric, $metrics, true ) ) { + $metric = $metrics[ 'Total' ]; + } + + $tday = $r->day; + if( !preg_match( '/^\d\d\d\d\-\d\d\-\d\d$/', $tday ) ) { + $tday = Carbon::now()->format( 'Y-m-d'); + } + + $day = Carbon::createFromFormat( 'Y-m-d', $tday ); + $category = Graph::processParameterCategory( $r->category ); + + return view( 'statistics/league-table' )->with( [ + 'metric' => $metric, + 'metrics' => $metrics, + 'day' => $day, + 'category' => $category, + 'trafficDaily' => TrafficDaily::loadTraffic( $day, $category ), + ] ); + } + + + + + + + + + /** + * Display graphs for a core bundle + * + * @param StatisticsRequest $r + * @param CoreBundle $cb the core bundle + * + * @return RedirectResponse|View + * + * @throws ParameterException + */ + public function coreBundle( StatisticsRequest $r, CoreBundle $cb ): RedirectResponse|View + { + /** @var User $us */ + $us = Auth::getUser(); + + $category = Graph::processParameterCategory( $r->input( 'category' ) ); + $graph = App::make( Grapher::class ) + ->coreBundle( $cb )->setCategory( $category ) + ->setSide( $r->input( 'side', 'a' ) ); + + // if the customer is authorised, then so too are all of their virtual and physical interfaces: + try { + $graph->authorise(); + } catch( AuthorizationException $e ) { + abort( 403, "You are not authorised to view this graph." ); + } + + return view( 'statistics/core-bundle' )->with([ + "cbs" => CoreBundle::active()->get(), + "cb" => $cb, + "graph" => $graph, + "category" => $category, + "categories" => Auth::check() && $us && $us->isSuperUser() ? Graph::CATEGORY_DESCS : Graph::CATEGORIES_BITS_PKTS_DESCS, + ]); + } + + /** + * Show utilisation of member ports + * + * @param StatisticsRequest $r + * + * @return View + */ + public function utilisation( StatisticsRequest $r ): View + { + $metrics = [ + 'Max' => 'max', + 'Total' => 'data', + 'Average' => 'average', + ]; + + $metric = $r->input( 'metric', $metrics['Max'] ); + + if( !in_array( $metric, $metrics, true ) ) { + $metric = $metrics[ 'Max' ]; + } + + $days = TrafficDailyPhysInt::select( [ 'day' ] ) + ->distinct( 'day' ) + ->orderBy( 'day', 'desc')->get()->pluck( 'day' )->toArray(); + + if( count( $days ) ) { + $day = $r->day; + if( !in_array( $day, $days, true ) ) { + $day = $days[ 0 ]; + } + } else { + $day = null; + } + + $vid = false; + if( $r->vlan && ( $vlan = Vlan::find( $r->vlan ) ) ) { + $vid = $vlan->id; + } + + $category = Graph::processParameterCategory( $r->category ); + $period = Graph::processParameterPeriod( $r->period, Graph::PERIOD_MONTH ); + + return view( 'statistics/utilisation' )->with( [ + 'metric' => $metric, + 'metrics' => $metrics, + 'day' => $day, + 'days' => $days, + 'category' => $category, + 'period' => $period, + 'tdpis' => $day ? TrafficDailyPhysIntAggregator::loadTraffic( $day, $category, $period, $vid ) : [], + 'vlans' => Vlan::publicOnly()->orderBy('number')->get(), + 'vlan' => $vid, + ] ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Switches/SwitchController.php b/app/Http/Controllers/Switches/SwitchController.php new file mode 100644 index 000000000..db490ce41 --- /dev/null +++ b/app/Http/Controllers/Switches/SwitchController.php @@ -0,0 +1,797 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers\Switches + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class SwitchController extends EloquentController +{ + /** + * The object being created / edited + * + * @var Switcher + */ + protected $object = null; + + /** + * This function sets up the frontend controller + */ + #[\Override] + public function feInit(): void + { + /** @var User $us */ + $us = Auth::getUser(); + + $this->feParams = (object)[ + 'model' => Switcher::class, + 'pagetitle' => 'Switches', + 'titleSingular' => 'Switch', + 'nameSingular' => 'switch', + 'listOrderBy' => 'name', + 'listOrderByDir' => 'ASC', + 'viewFolderName' => 'switches', + 'addRoute' => route( static::route_prefix() . '@create-by-snmp' ), + 'documentation' => 'https://docs.ixpmanager.org/latest/usage/switches/', + 'listColumns' => [ + 'name' => 'Name', + 'cabinet' => [ + 'title' => 'Rack', + 'type' => self::$FE_COL_TYPES[ 'HAS_ONE' ], + 'controller' => 'rack', + 'action' => 'view', + 'idField' => 'cabinetid' + ], + 'vendor' => [ + 'title' => 'Vendor', + 'type' => self::$FE_COL_TYPES[ 'HAS_ONE' ], + 'controller' => 'vendor', + 'action' => 'view', + 'idField' => 'vendorid' + ], + 'infrastructure' => 'Infrastructure', + 'active' => [ + 'title' => 'Active', + 'type' => self::$FE_COL_TYPES[ 'YES_NO' ] + ], + 'poll' => [ + 'title' => 'Poll', + 'type' => self::$FE_COL_TYPES[ 'YES_NO' ] + ], + 'model' => 'Model', + 'ipv4addr' => 'IPv4 Address', + ] + ]; + + // display the same information in the view as the list + $this->feParams->viewColumns = array_merge( + $this->feParams->listColumns, + [ + 'ipv6addr' => 'IPv6 Address', + 'hostname' => 'Hostname', + 'snmppasswd' => 'SNMP Community', + 'os' => 'OS', + 'osVersion' => 'OS Version', + 'osDate' => [ + 'title' => 'OS Date', + 'type' => self::$FE_COL_TYPES[ 'DATETIME' ] + ], + 'lastPolled' => [ + 'title' => 'Last Polled', + 'type' => self::$FE_COL_TYPES[ 'DATETIME' ] + ], + 'serialNumber' => 'Serial Number', + 'mauSupported' => [ + 'title' => 'MAU Supported', + 'type' => self::$FE_COL_TYPES[ 'YES_NO_NULL' ] + ], + 'asn' => 'ASN', + 'loopback_ip' => 'Loopback IP', + 'loopback_name' => 'Loopback Name', + 'mgmt_mac_address' => 'Mgmt MAC Address', + 'notes' => [ + 'title' => 'Notes', + 'type' => self::$FE_COL_TYPES[ 'PARSDOWN' ] + ], + 'created_at' => [ + 'title' => 'Created', + 'type' => self::$FE_COL_TYPES[ 'DATETIME' ] + ], + 'updated_at' => [ + 'title' => 'Updated', + 'type' => self::$FE_COL_TYPES[ 'DATETIME' ] + ] + + ] + ); + + // phpunit / artisan trips up here without the cli test: + if( PHP_SAPI !== 'cli' ) { + // custom access controls: + switch( Auth::check() ? $us->privs() : User::AUTH_PUBLIC ) { + case User::AUTH_SUPERUSER: + break; + case User::AUTH_CUSTUSER || User::AUTH_CUSTADMIN: + switch( Route::current()->getName() ) { + case 'switch@configuration': + break; + + default: + $this->unauthorized(); + } + break; + default: + $this->unauthorized(); + } + } + } + + /** + * Additional routes + * + * @param string $route_prefix + * + * @return void + */ + #[\Override] + protected static function additionalRoutes( string $route_prefix ): void + { + // NB: this route is marked as 'read-only' to disable normal CRUD operations. It's not really read-only. + Route::group( [ 'prefix' => $route_prefix ], function() { + Route::get( 'create-by-snmp', 'Switches\SwitchController@addBySnmp' )->name( 'switch@create-by-snmp' ); + Route::get( 'port-report/{switch}','Switches\SwitchController@portReport' )->name( "switch@port-report" ); + Route::get( 'configuration', 'Switches\SwitchController@configuration' )->name( "switch@configuration" ); + Route::post( 'store-by-snmp', 'Switches\SwitchController@storeBySmtp' )->name( "switch@store-by-snmp" ); + }); + } + + /** + * List the contents of a database table. + * + * @param Request $param + * + * @return View + */ + #[\Override] + public function list( Request $param ) : View + { + if( ( $showActiveOnly = $param->activeOnly ) !== null ) { + $param->session()->put( "switch-list-active-only", $showActiveOnly ); + } else if( $param->session()->exists( "switch-list-active-only" ) ) { + $showActiveOnly = $param->session()->get( "switch-list-active-only" ); + } else { + $showActiveOnly = false; + } + + if( $vtype = $param->vtype ) { + $param->session()->put( "switch-list-vtype", $vtype ); + } elseif( $param->session()->exists( "switch-list-vtype" ) ) { + $vtype = $param->session()->get( "switch-list-vtype" ); + } else { + $param->session()->remove( "switch-list-vtype" ); + $vtype = Switcher::VIEW_MODE_DEFAULT; + } + + if( $param->infra ) { + if( $infra = Infrastructure::find( $param->infra ) ) { + $param->session()->put( "switch-list-infra", $infra ); + } else { + $param->session()->remove( "switch-list-infra" ); + $infra = false; + } + } else if( $param->session()->exists( "switch-list-infra" ) ) { + $infra = $param->session()->get( "switch-list-infra" ); + } else { + $infra = false; + } + + if( $vtype === Switcher::VIEW_MODE_OS ) { + $this->setUpOsView(); + } else if( $vtype === Switcher::VIEW_MODE_L3 ){ + $this->setUpL3View(); + } + + $this->data[ 'params' ][ 'activeOnly' ] = $showActiveOnly; + $this->data[ 'params' ][ 'vtype' ] = $vtype; + $this->data[ 'params' ][ 'infra' ] = $infra; + $this->data[ 'rows' ] = $this->listGetData(); + + $this->listIncludeTemplates(); + $this->preList(); + + return $this->display( 'list' ); + } + + /** + * Set Up the the table to display the OS VIEW + * + * @return void + */ + private function setUpOsView(): void + { + $this->feParams->listColumns = [ + 'id' => + [ 'title' => 'UID', + 'display' => false + ], + 'name' => 'Name', + 'vendor' => [ + 'title' => 'Vendor', + 'type' => self::$FE_COL_TYPES[ 'HAS_ONE' ], + 'controller' => 'vendor', + 'action' => 'view', + 'idField' => 'vendorid' + ], + 'model' => 'Model', + 'os' => 'OS', + 'osVersion' => 'OS Version', + 'serialNumber' => 'Serial Number', + 'osDate' => [ + 'title' => 'OS Date', + 'type' => self::$FE_COL_TYPES[ 'DATETIME' ] + ], + 'lastPolled' => [ + 'title' => 'Last Polled', + 'type' => self::$FE_COL_TYPES[ 'DATETIME' ] + ], + 'active' => [ + 'title' => 'Active', + 'type' => self::$FE_COL_TYPES[ 'YES_NO' ] + ] + ]; + } + + /** + * Set Up the the table to display the OS VIEW + * + * @return void + */ + private function setUpL3View(): void + { + $this->feParams->listColumns = [ + 'id' => [ + 'title' => 'UID', + 'display' => false + ], + 'name' => 'Name', + 'hostname' => 'Hostname', + 'asn' => 'ASN', + 'loopback_ip' => 'Loopback', + 'mgmt_mac_address' => 'Mgmt Mac', + 'active' => [ + 'title' => 'Active', + 'type' => self::$FE_COL_TYPES[ 'YES_NO' ] + ] + ]; + } + + /** + * Provide array of rows for the list action and view action + * + * @param int|null $id The `id` of the row to load for `view` action`. `null` if `listAction` + * + * @return array + */ + #[\Override] + protected function listGetData( ?int $id = null ): array + { + $feParams = $this->feParams; + $data = $this->data; + return Switcher::select( [ + 'switch.*', + 'i.name AS infrastructure', + 'v.id AS vendorid', 'v.name AS vendor', + 'c.id AS cabinetid', 'c.name AS cabinet' + ] )->leftJoin( 'infrastructure AS i', 'i.id', 'switch.infrastructure') + ->leftJoin( 'cabinet AS c', 'c.id', 'switch.cabinetid') + ->leftJoin( 'vendor AS v', 'v.id', 'switch.vendorid') + ->when( $id , function( Builder $q, $id ) { + return $q->where('switch.id', $id ); + } )->when( isset( $data[ 'params' ][ 'activeOnly' ] ) && $data[ 'params' ][ 'activeOnly' ] , function( Builder $q ) { + return $q->where('switch.active', true ); + } )->when( $feParams->listOrderBy , function( Builder $q, $orderby ) use ( $feParams ) { + return $q->orderBy( $orderby, $feParams->listOrderByDir ?? 'ASC'); + })->get()->toArray(); + } + + /** + * Display the form to create an object + * + * @return (Switcher|array|false|mixed|null|string)[] + * + * @psalm-return array{object: Switcher, addBySnmp: array|null|string, preAddForm: false, cabinets: array, infra: mixed, vendors: mixed} + */ + #[\Override] + protected function createPrepareForm(): array + { + return [ + 'object' => $this->object, + 'addBySnmp' => request()->old( 'add_by_snnp', null ), + 'preAddForm' => false, + 'cabinets' => Location::with( 'cabinets' ) + ->has( 'cabinets' )->get()->toArray(),// getting the cabinets via the location to build the grouped options dropdown + 'infra' => Infrastructure::orderBy( 'name' )->get(), + 'vendors' => Vendor::orderBy( 'name' )->get(), + ]; + } + + /** + * Function to do the actual validation and storing of the submitted object. + * + * @param Request $r + * + * @return true + */ + #[\Override] + public function doStore( Request $r ): bool|RedirectResponse + { + $this->checkForm( $r ); + + if( $r->asn && Switcher::where( 'asn', $r->asn )->exists() ) { + AlertContainer::push( "Note: this ASN is already is use by at least one other switch. If you are using eBGP, this may cause prefixes to be black-holed.", Alert::WARNING ); + } + + $r->merge( [ 'mgmt_mac_address' => preg_replace( "/[^a-f0-9]/i", '', strtolower( $r->mgmt_mac_address ) ) ] ); + + $this->object = Switcher::create( $r->all() ); + $this->extraAttributes( $r ); + return true; + } + + /** + * Display the form to edit an object + * + * @param int $id ID of the row to edit + * + * @return (array|false|mixed|null|string)[] + * + * @psalm-return array{object: mixed, addBySnmp: array|null|string, preAddForm: false, cabinets: array, infra: mixed, vendors: mixed} + */ + #[\Override] + protected function editPrepareForm( int $id ): array + { + $this->object = Switcher::findOrFail( $id ); + + Former::populate([ + 'name' => request()->old( 'name', $this->object->name ), + 'hostname' => request()->old( 'hostname', $this->object->hostname ), + 'infrastructure' => request()->old( 'infrastructure', $this->object->infrastructure ), + 'ipv4addr' => request()->old( 'ipv4addr', $this->object->ipv4addr ), + 'ipv6addr' => request()->old( 'ipv6addr', $this->object->ipv6addr ), + 'snmppasswd' => request()->old( 'snmppasswd', $this->object->snmppasswd ), + 'vendorid' => request()->old( 'vendorid', $this->object->vendorid ), + 'model' => request()->old( 'model', $this->object->model ), + 'active' => request()->old( 'active', ( $this->object->active ? '1' : '0' ) ), + 'poll' => request()->old( 'poll', ( $this->object->poll ? '1' : '0' ) ), + 'asn' => request()->old( 'asn', $this->object->asn ), + 'loopback_ip' => request()->old( 'loopback_ip', $this->object->loopback_ip ), + 'loopback_name' => request()->old( 'loopback_name', $this->object->loopback_name ), + 'mgmt_mac_address' => request()->old( 'mgmt_mac_address', $this->object->mgmt_mac_address ) , + 'notes' => request()->old( 'notes', $this->object->notes ) , + ]); + + return [ + 'object' => $this->object, + 'addBySnmp' => request()->old( 'add_by_snnp', null ), + 'preAddForm' => false, + 'cabinets' => Location::with( 'cabinets' ) + ->has( 'cabinets' )->get()->toArray(),// getting the cabinets via the location to build the grouped options dropdown + 'infra' => Infrastructure::orderBy( 'name' )->get(), + 'vendors' => Vendor::orderBy( 'name' )->get() + ]; + } + + /** + * Function to do the actual validation and editing of the submitted object. + * + * @param Request $r + * @param int $id + * + * @return true + * + * @throws + */ + #[\Override] + public function doUpdate( Request $r, int $id ): bool|RedirectResponse + { + $this->object = Switcher::findOrFail( $id ); + + $this->checkForm( $r ); + + if( $r->asn && Switcher::where('asn', $r->asn )->where( 'id', '!=', $this->object->id )->exists() ){ + AlertContainer::push( "Note: this ASN is already is use by at least one other switch. If you are using eBGP, this may cause prefixes to be black-holed.", Alert::WARNING ); + } + + $r->merge( [ 'mgmt_mac_address' => preg_replace( "/[^a-f0-9]/i", '', strtolower( $r->mgmt_mac_address ) ) ] ); + + $this->object->update( $r->all() ); + $this->extraAttributes( $r ); + return true; + } + + /** + * Display the form to add by SNMP + * + * @return View + */ + public function addBySnmp(): View + { + // wipe any preexisting cached switch platform entry: + session()->remove( "snmp-platform" ); + $this->addEditSetup(); + return $this->display( 'add-by-smtp-form' ); + } + + /** + * Process the hostname and SNMP community, poll the switch and set up the proper add/edit form + * + * @param StoreBySmtpRequest $r + * + * @return View + */ + public function storeBySmtp( StoreBySmtpRequest $r ): View + { + $vendorid = null; + + // can we get it by SNMP and discover some basic details? + try { + $snmp = new SNMP( $r->hostname, $r->snmppasswd ); + $vendor = $snmp->getPlatform()->getVendor(); + + // Store the platform in session to be able to get back the information when we will create the object + $r->session()->put( "snmp-platform", $snmp->getPlatform() ); + + if( $v = Vendor::where('name', $vendor )->first() ) { + $vendorid = $v->id; + } + } catch( SNMPException $e ) { + $snmp = null; + } + + $sp = strpos( $r->hostname, '.' ); + + Former::populate([ + 'name' => substr( $r->hostname, 0, $sp ?: strlen( $r->hostname ) ), + 'snmppasswd' => $r->snmppasswd, + 'hostname' => $r->hostname, + 'ipv4addr' => resolve_dns_a( $r->hostname ) ?? '', + 'ipv6addr' => resolve_dns_aaaa( $r->hostname ) ?? '', + 'vendorid' => $vendorid ?? "", + 'model' => $snmp ? $snmp->getPlatform()->getModel() : "", + ]); + + $this->feParams->titleSingular = "Switch via SNMP"; + $this->addEditSetup(); + + $this->data[ 'params' ]['isAdd'] = true; + $this->data[ 'params' ]['addBySnmp'] = true; + $this->data[ 'params' ]['preAddForm'] = false; + $this->data[ 'params' ]['object'] = null; + $this->data[ 'params' ]['cabinets'] = Location::with( 'cabinets' ) + ->has( 'cabinets' )->get()->toArray(); + $this->data[ 'params' ]['infra'] = Infrastructure::orderBy( 'name' )->get(); + $this->data[ 'params' ]['vendors'] = Vendor::orderBy( 'name' )->get(); + + return $this->display( 'edit' ); + } + + /** + * @inheritdoc + */ + #[\Override] + protected function preDelete() : bool + { + $okay = true; + + if( $this->object->getPhysicalInterfaces()->count() ) { + $okay = false; + AlertContainer::push( "Cannot delete switch: there are switch ports assigned to one or more physical interfaces.", Alert::DANGER ); + } + + if( $this->object->getPatchPanelPorts()->count() ) { + $okay = false; + AlertContainer::push( "Cannot delete switch: there are switch ports assigned to patch panel ports", Alert::DANGER ); + } + + if( $okay ){ + $this->object->switchPorts()->delete(); + } + + return $okay; + } + + /** + * Display the Port report for a switch + * + * @param Switcher $switch ID for the switch + * + * @return view + */ + public function portReport( Switcher $switch ) : View + { + $allPorts = SwitchPortAggregator::getAllPortsForSwitch( $switch->id, [] , [], false ); + + $ports = SwitchPort::select( [ + 'sp.id AS id', 'sp.name AS name', 'sp.type AS porttype', + 'pi.speed AS speed', 'pi.duplex AS duplex', + 'c.name AS custname' + ] )->from( 'switchport AS sp' ) + ->join( 'physicalinterface AS pi', 'pi.switchportid', 'sp.id' ) + ->join( 'virtualinterface AS vi', 'vi.id', 'pi.virtualinterfaceid' ) + ->join( 'cust AS c', 'c.id', 'vi.custid' ) + ->where( 'sp.switchid', $switch->id ) + ->orderBy( 'id' ) + ->get()->keyBy( 'id' )->toArray(); + + $matchingValues = array_uintersect( $ports, $allPorts , static function ( $val1, $val2 ){ + return strcmp( $val1['name'], $val2['name'] ); + }); + + $diffValues = array_udiff( $allPorts, $ports , static function ( $val1, $val2 ){ + return strcmp( $val1['name'], $val2['name'] ); + }); + + return view( 'switches/port-report' )->with([ + 'switches' => Switcher::orderBy( 'name' ) + ->get()->keyBy( 'id' ), + 's' => $switch, + 'ports' => array_merge( $matchingValues, $diffValues ), + ]); + } + + /** + * Display the switch configurations + * + * @param Request $r + * + * @return view + * + * @throws + */ + public function configuration( Request $r ) : View + { + $infra = $location = $speed = $switch = $vlan = $summary = false; + + $speeds = PhysicalInterface::selectRaw( 'DISTINCT physicalinterface.speed AS speed' ) + ->orderBy( 'speed' )->get()->pluck( 'speed' )->toArray(); + + $rate_limits = PhysicalInterface::selectRaw( 'DISTINCT physicalinterface.rate_limit AS rate_limit' ) + ->whereNotNull( 'rate_limit' ) + ->orderBy( 'rate_limit' )->get()->pluck( 'rate_limit' )->toArray(); + + $speeds = array_merge( $speeds, $rate_limits ); + asort( $speeds, SORT_NUMERIC ); + $speeds = array_values($speeds); + + if( $r->switch !== null ) { + if( $switch = Switcher::find( $r->switch ) ) { + $r->session()->put( "switch-configuration-switch", $switch ); + } else { + $r->session()->remove( "switch-configuration-switch" ); + $switch = false; + } + } else if( $r->session()->exists( "switch-configuration-switch" ) ) { + $switch = $r->session()->get( "switch-configuration-switch" ); + } + + if( $r->infra !== null ) { + if( $infra = Infrastructure::find( $r->infra ) ) { + $r->session()->put( "switch-configuration-infra", $infra ); + } else { + $r->session()->remove( "switch-configuration-infra" ); + $infra = false; + } + } else if( $r->session()->exists( "switch-configuration-infra" ) ) { + $infra = $r->session()->get( "switch-configuration-infra" ); + } + + if( $r->location !== null ) { + if( $location = Location::find( $r->location ) ) { + $r->session()->put( "switch-configuration-location", $location ); + } else { + $r->session()->remove( "switch-configuration-location" ); + $location = false; + } + } else if( $r->session()->exists( "switch-configuration-location" ) ) { + $location = $r->session()->get( "switch-configuration-location" ); + } + + if( $r->speed !== null ) { + $speed = (int)$r->speed; + if( in_array( $r->speed, $speeds, false ) ) { + $r->session()->put( "switch-configuration-speed", $r->speed ); + } else { + $r->session()->remove( "switch-configuration-speed" ); + $speed = false; + } + } else if( $r->session()->exists( "switch-configuration-speed" ) ) { + $speed = $r->session()->get( "switch-configuration-speed" ); + } + + if( $r->vlan !== null ) { + if( $vlan = Vlan::find( $r->vlan ) ) { + $r->session()->put( "switch-configuration-vlan", $vlan ); + } else { + $r->session()->remove( "switch-configuration-vlan" ); + $vlan = false; + } + } else if( $r->session()->exists( "switch-configuration-vlan" ) ) { + $vlan = $r->session()->get( "switch-configuration-vlan" ); + } + + if( $switch || $infra || $location || $vlan ) { + $summary = "Connections details for: "; + + if( $switch ) { + $summary .= $switch->name . " (on " . $switch->infrastructureModel->name. " at " . $switch->cabinet->location->name . ")"; + } else { + if( $infra ){ + $summary .= $infra->name . ' (infrastructure); '; + } + if( $location ){ + $summary .= $location->name . ' (facility); '; + } + if( $vlan ) { + $summary .= $vlan->name . ' (VLAN); '; + } + if( $speed ) { + $summary .= PhysicalInterface::$SPEED[ $speed ] . '; '; + } + } + } + + $config = SwitcherAggregator::getConfiguration( + $switch ? $switch->id : null, + $infra ? $infra->id : null, + $location ? $location->id : null, + $speed, + $vlan ? $vlan->id : null, + (bool) $r->input('rs-client'), + (bool) $r->input('ipv6-enabled') + ); + + return view( 'switches/configuration' )->with([ + 's' => $switch, + 'speed' => $speed, + 'infra' => $infra, + 'vlan' => $vlan, + 'location' => $location, + 'summary' => $summary, + 'speeds' => $speeds, + 'infras' => $switch ? [ Infrastructure::find( $switch->infrastructure ) ] : Infrastructure::orderBy( 'name' )->get(), + 'vlans' => Vlan::orderBy( 'name' )->get(), + 'locations' => $switch ? [ Location::find( $switch->cabinet->locationid ) ] : Location::orderBy( 'name' )->get(), + 'switches' => SwitcherAggregator::getByLocationInfrastructureSpeed( $infra ? $infra->id : null, $location ? $location->id : null, $speed ?: null ), + 'config' => $config, + ]); + } + + /** + * Check if the form is valid + * + * @param Request $r + */ + #[\Override] + public function checkForm( Request $r ): void + { + $r->validate( [ + 'name' => 'required|string|max:255|unique:switch,name' . ( $r->id ? ',' . $r->id : '' ), + 'hostname' => [ 'required', 'string', 'max:255', new IdnValidate(), 'unique:switch,hostname' . ( $r->id ? ',' . $r->id : '' ) ], + 'cabinetid' => 'required|integer|exists:cabinet,id', + 'infrastructure' => 'required|integer|exists:infrastructure,id', + 'snmppasswd' => 'nullable|string|max:255', + 'vendorid' => 'required|integer|exists:vendor,id', + 'ipv4addr' => 'nullable|ipv4', + 'ipv6addr' => 'nullable|ipv6', + 'model' => 'nullable|string|max:255', + 'asn' => 'nullable|integer|min:1', + 'loopback_ip' => 'nullable|string|max:255|unique:switch,loopback_ip' . ( $r->id ? ',' . $r->id : '' ), + 'loopback_name' => 'nullable|string|max:255', + 'mgmt_mac_address' => 'nullable|string|max:17|regex:/^[a-f0-9:\.\-]{12,17}$/i', + ] ); + } + + /** + * Add some extra attributes to the object + * + * @param Request $r + * + * @return void + * + * @throws + */ + private function extraAttributes( Request $r ): void + { + if( $r->session()->exists( "snmp-platform" ) ) { + /** @var Platform $platform */ + $platform = $r->session()->get( "snmp-platform" ); + $osDate = null; + + if( $platform->getOsDate() instanceof DateTime ) { + $osDate = $platform->getOsDate(); + } else if( is_string( $platform->getOsDate() ) ) { + $osDate = new DateTime( $platform->getOsDate() ); + } + + $this->object->os = $platform->getOs(); + $this->object->osDate = $osDate; + $this->object->osVersion = $platform->getOsVersion(); + $this->object->serialNumber = $platform->getSerialNumber() ?? '(not implemented)'; + $this->object->save(); + $r->session()->remove( "snmp-platform" ); + } + + if( $r->add_by_snnp ) { + $this->object->lastPolled = now(); + $this->object->save(); + } + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Switches/SwitchPortController.php b/app/Http/Controllers/Switches/SwitchPortController.php new file mode 100644 index 000000000..c366de9d8 --- /dev/null +++ b/app/Http/Controllers/Switches/SwitchPortController.php @@ -0,0 +1,880 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers\Switches + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class SwitchPortController extends EloquentController +{ + /** + * The object being created / edited + * + * @var SwitchPort + */ + protected $object = null; + + /** + * This function sets up the frontend controller + */ + #[\Override] + public function feInit(): void + { + $this->feParams = ( object )[ + 'model' => SwitchPort::class, + 'pagetitle' => 'Switch Ports', + 'titleSingular' => 'Switch Port', + 'nameSingular' => 'a switch port', + 'listOrderBy' => 'name', + 'listOrderByDir' => 'ASC', + 'viewFolderName' => 'switch-port', + 'route_action' => 'list', + 'route_prefix_page_title' => 'switch', + 'pagetitlepostamble' => 'Switch Port', + 'listColumns' => [ + 'switchname' => [ + 'title' => 'Switch', + 'type' => self::$FE_COL_TYPES[ 'HAS_ONE' ], + 'controller' => 'switch', + 'action' => 'view', + 'idField' => 'switchid' + ], + 'name' => 'Description', + 'ifName' => 'Name', + 'ifAlias' => 'Alias', + 'active' => [ + 'title' => 'Active', + 'type' => self::$FE_COL_TYPES[ 'YES_NO' ], + ], + 'type' => [ + 'title' => 'Type', + 'type' => self::$FE_COL_TYPES[ 'XLATE' ], + 'xlator' => SwitchPort::$TYPES + ] + ] + ]; + + // display the same information in the view as the list + $this->feParams->viewColumns = $this->feParams->listColumns; + } + + /** + * Additional routes + * + * @param string $route_prefix + * + * @return void + */ + #[\Override] + protected static function additionalRoutes( string $route_prefix ): void + { + // NB: this route is marked as 'read-only' to disable normal CRUD operations. It's not really read-only. + Route::group( [ 'prefix' => $route_prefix ], static function() { + Route::get( 'unused-optics', 'Switches\SwitchPortController@unusedOptics' )->name( 'switch-port@unused-optics' ); + Route::get( 'optic-inventory', 'Switches\SwitchPortController@opticInventory' )->name( 'switch-port@optic-inventory' ); + Route::get( 'optic-list', 'Switches\SwitchPortController@opticList' )->name( 'switch-port@optic-list' ); + Route::get( 'list-mau/{switch}', 'Switches\SwitchPortController@listMau' )->name( 'switch-port@list-mau' ); + Route::get( 'op-status/{switch}', 'Switches\SwitchPortController@listOpStatus' )->name( 'switch-port@list-op-status' ); + Route::get( 'snmp-poll/{switch}', 'Switches\SwitchPortController@snmpPoll' )->name( 'switch-port@snmp-poll' ); + + Route::post( 'set-type', 'Switches\SwitchPortController@setType' )->name( 'switch-port@set-type' ); + Route::post( 'change-status', 'Switches\SwitchPortController@changeStatus' )->name( 'switch-port@change-status' ); + Route::delete( 'delete-snmp-poll', 'Switches\SwitchPortController@deleteSnmpPoll' )->name( 'switch-port@delete-snmp-poll' ); + }); + } + + /** + * Provide array of rows for the list action and view action + * + * @param int|null $id The `id` of the row to load for `view` action`. `null` if `listAction` + * + * @return array + */ + #[\Override] + protected function listGetData( ?int $id = null ): array + { + $feParams = $this->feParams; + $params = $this->data; + return SwitchPort::select( [ + 'sp.*', + 's.id AS switchid', 's.name AS switchname' + ] ) + ->from( 'switchport AS sp' ) + ->leftJoin( 'switch AS s', 's.id', 'sp.switchid') + ->when( $id , function( Builder $q, $id ) { + return $q->selectRaw( 'c.id AS cid, c.name AS cname' ) + ->leftJoin( 'physicalinterface AS pi', 'pi.switchportid', 'sp.id' ) + ->leftJoin( 'virtualinterface AS vi', 'vi.id', 'pi.virtualinterfaceid' ) + ->leftJoin( 'cust AS c', 'c.id', 'vi.custid' ) + ->where('sp.id', $id ); + } )->when( isset( $params[ 'params' ][ 'switch' ] ) && $params[ 'params' ][ 'switch' ] , function( Builder $q ) use ( $params ) { + return $q->where('s.id', $params[ 'params' ][ 'switch' ]->id ); + } )->when( isset( $feParams->listOrderBy ) , function( Builder $q ) use ( $feParams ) { + return $q->orderBy( $feParams->listOrderBy, $feParams->listOrderByDir ?? 'ASC'); + })->get()->toArray(); + } + + /** + * List the contents of a database table. + * + * @param Request $param + * + * @return View + */ + #[\Override] + public function list( Request $param ) : View + { + $switch = false; + if( $param->switchid !== null ) { + if( $switch = Switcher::find( $param->switchid ) ) { + $param->session()->put( "switch-port-list", $switch->id ); + } else { + $param->session()->remove( "switch-port-list" ); + $switch = false; + } + } else if( $param->session()->exists( "switch-port-list" ) ) { + $switch = Switcher::find( $param->session()->get( "switch-port-list" ) ); + } + + $this->data[ 'params' ][ 'switch' ] = $switch; + $this->data[ 'params' ][ 'switches' ] = Switcher::orderBy( 'name' )->get()->keyBy( 'id' ); + + $this->data[ 'rows' ] = $this->listGetData(); + + $this->listIncludeTemplates(); + + return $this->display( 'list' ); + } + + /** + * Display the form to create an object + * + * @return (SwitchPort|mixed)[] + * + * @psalm-return array{object: SwitchPort, switches: mixed} + */ + #[\Override] + protected function createPrepareForm(): array + { + return [ + 'object' => $this->object, + 'switches' => Switcher::orderBy( 'name' )->get()->toArray() + ]; + } + + /** + * Function to do the actual validation and storing of the submitted object. + * + * @param Request $r + * + * @return true + */ + #[\Override] + public function doStore( Request $r ): bool|RedirectResponse + { + $rules = [ + 'switchid' => 'required|integer|exists:switch,id', + 'numfirst' => 'required|integer|min:0', + 'numports' => 'required|integer|min:1|max:48', + 'type' => 'required|integer|in:' . implode( ',', array_keys( SwitchPort::$TYPES ) ), + ]; + + for( $i = 0; $i < $r->numports; $i++ ) { + $rules[ 'portName'.$i ] = 'required|string|max:255'; + $rules[ 'portType'.$i ] = 'required|integer|in:' . implode( ',', array_keys( SwitchPort::$TYPES ) ); + } + + $r->validate( $rules ); + + for( $i = 0; $i < $r->numports; $i++ ) { + $this->object = SwitchPort::create( [ + 'switchid' => $r->switchid, + 'type' => $r->input('portType' . $i ), + 'name' => $r->input('portName' . $i ), + 'active' => true, + ] ); + } + return true; + } + + /** + * Display the form to edit an object + * + * @param int $id ID of the row to edit + * + * @return array + * + * @psalm-return array{object: mixed, switches: mixed} + */ + #[\Override] + protected function editPrepareForm( $id = null ): array + { + $this->object = SwitchPort::findOrFail( $id ); + + Former::populate([ + 'switchid' => request()->old( 'switchid', $this->object->switchid ), + 'name' => request()->old( 'name', $this->object->name ), + 'type' => request()->old( 'type', $this->object->type ), + 'active' => request()->old( 'active', $this->object->active ), + ]); + + return [ + 'object' => $this->object, + 'switches' => Switcher::orderBy( 'name' )->get()->keyBy( 'id' )->toArray(), + ]; + } + + /** + * Function to do the actual validation and storing of the submitted object. + * + * @param Request $r + * @param int $id + * + * @return true + */ + #[\Override] + public function doUpdate( Request $r, int $id ): bool|RedirectResponse + { + $this->object = SwitchPort::findOrFail( $id ); + + $r->validate( [ + 'switchid' => 'required|integer|exists:switch,id', + 'name' => 'required|string|max:255', + 'type' => 'required|integer|in:' . implode( ',', array_keys( SwitchPort::$TYPES ) ), + ] ); + + $this->object->update( $r->all() ); + return true; + } + + /** + * Allow D2F implementations to override where the post-store redirect goes. + * + * To implement this, have it return a valid route name + * + * @return string|null + */ + #[\Override] + protected function postStoreRedirect(): ?string + { + if( request()->isAdd ) { + return route( "switch-port@list", [ "switch" => request()->switchid ] ); + } + return null; + } + + /** + * @inheritdoc + */ + #[\Override] + protected function preDelete(): bool + { + if( $pi = $this->object->physicalInterface ) { + $c = $pi->virtualInterface->customer; + AlertContainer::push( "You cannot delete the switch port {$this->object->name} as it is assigned to a physical interface for " + . " $c->id, "tab" => "ports" ]) . "\">{$c->name}.", Alert::DANGER ); + return false; + } + + if( $ppp = $this->object->patchPanelPort ) { + AlertContainer::push( "You cannot delete the switch port {$this->object->name} as it is assigned to a patch panel port for " + . " $ppp->patch_panel_id ] ) . "\">{$ppp->name()}.", Alert::DANGER ); + return false; + } + + return true; + } + + /** + * Set up all the information to display the Unused optics list + * + * @bool + * + * @return true + */ + public function setUpUnusedOptics(): bool + { + $this->feParams->listOrderBy = 'switchname'; + $this->feParams->pagetitle = 'Switches'; + $this->feParams->pagetitlepostamble = 'Unused Optics'; + $this->feParams->route_prefix_page_title = 'switch'; + $this->feParams->readonly = true; + $this->feParams->hideactioncolumn = true; + $this->feParams->listColumns = [ + 'switchname' => 'Switch', + 'ifName' => 'Port', + 'type' => 'Type', + 'mauType' => 'MAU Type', + 'mauState' => 'MAU State', + 'mauJacktype' => 'Jack Type', + ]; + + return true; + } + + /** + * Display the unused optics + * + * @return view + * + * @throws + */ + public function unusedOptics() : View + { + $this->setUpUnusedOptics(); + + $feParams = $this->feParams; + $this->data[ 'rows' ] = SwitchPort::select( [ + 'sp.ifIndex AS ifIndex', 'sp.ifName AS ifName', 'sp.type AS type', 'sp.mauType AS mauType', 'sp.mauState AS mauState', 'sp.mauJacktype AS mauJacktype', + 's.id AS switchid', 's.name AS switchname' + ] )->from( 'switchport AS sp' ) + ->leftJoin( 'switch AS s', 's.id', 'sp.switchid') + ->where( 's.mauSupported', 1 ) + ->where( 's.active', 1 ) + ->where( 's.poll', 1 ) + ->where( 'sp.ifOperStatus', '!=', 1 ) + ->where( 'sp.mauType', '!=', '(empty)' ) + ->where( 'sp.type', '!=', SwitchPort::TYPE_MANAGEMENT ) + ->when( $feParams->listOrderBy , function( Builder $q, $orderby ) use ( $feParams ) { + return $q->orderBy( $orderby, $feParams->listOrderByDir ?? 'ASC'); + })->get()->toArray(); + + $this->listIncludeTemplates(); + + AlertContainer::push( "A list of ports from switches that support the IANA MAU MIB where the operational status + is down, the port is populated with an optic / SFP and the port type is not management. + Data valid at time of last SNMP poll.", Alert::INFO ); + + return $this->display( 'list' ); + } + + /** + * Set up all the information to display the list MAU + * + * @bool + * + * @return true + */ + public function setUpListMau(): bool + { + $this->feParams->pagetitle = 'Switches'; + $this->feParams->route_prefix_page_title = 'switch'; + $this->feParams->route_action = 'list-mau'; + $this->feParams->listOrderBy = 'name'; + $this->feParams->readonly = true; + $this->feParams->hideactioncolumn = true; + + $this->feParams->listColumns = [ + 'ifName' => [ + 'title' => 'Name', + 'data-sort' => 'id' + ], + 'type' => [ + 'title' => 'Type', + 'type' => self::$FE_COL_TYPES[ 'CONST' ], + 'const' => SwitchPort::$TYPES, + ], + 'state' => [ + 'title' => 'State (Admin/Op)', + 'type' => self::$FE_COL_TYPES[ 'SCRIPT' ], + 'script' => 'switch-port/port-admin-status', + 'params' => [ + "adminState" => "ifAdminStatus", + "operState" => "ifOperStatus", + ], + ], + 'mauType' => 'MAU Type', + 'mauState' => 'MAU State', + 'mauAvailability' => 'MAU Availability', + 'mauJacktype' => 'Jack Type', + 'mauAutoNegAdminState' => [ + 'title' => 'Auto Neg', + 'type' => self::$FE_COL_TYPES[ 'SCRIPT' ], + 'script' => 'switch-port/port-auto-neg', + 'params' => [ + "mauAutoNegAdminState" => "mauAutoNegAdminState", + ], + ] + ]; + + return true; + } + + /** + * Display the List MAU + * + * @param Switcher $switch + * + * @return RedirectResponse|View + */ + public function listMau( Switcher $switch ): RedirectResponse|View + { + if( !$switch->mauSupported ) { + return redirect( route( "switch@list" ) ); + } + + $this->setUpListMau(); + $this->feParams->pagetitlepostamble = 'MAU Interface Detail for ' . $switch->name ; + $this->data[ 'params' ][ 'switches' ] = Switcher::where( 'mauSupported', true ) + ->get()->keyBy( 'id' )->toArray(); + $this->data[ 'params' ][ 'switch' ] = $switch; + + $feParams = $this->feParams; + $this->data[ 'rows' ] = SwitchPort::select( [ + 'sp.*', + 's.id AS switchid' + ] ) + ->from( 'switchport AS sp' ) + ->leftJoin( 'switch AS s', 's.id', 'sp.switchid') + ->when( $switch->id , function( Builder $q, $id ) { + return $q->where('s.id', $id ); + } ) + ->when( $feParams->listOrderBy , function( Builder $q, $orderby ) use ( $feParams ) { + return $q->orderBy( $orderby, $feParams->listOrderByDir ?? 'ASC'); + })->get()->toArray(); + + $this->listIncludeTemplates(); + + AlertContainer::push( "Data valid at time of last SNMP poll: " . $switch->lastPolled, Alert::INFO ); + + return $this->display( 'list' ); + } + + /** + * Set up all the information to display the operation Status + * + * @bool + * + * @return true + */ + public function setUpOpStatus(): bool + { + $this->feParams->listOrderBy = 'ifIndex'; + $this->feParams->pagetitle = 'Switches'; + $this->feParams->route_prefix_page_title = 'switch'; + $this->feParams->route_action = 'list-op-status'; + + $this->feParams->listColumns = [ + 'ifIndex' => 'Index', + 'name' => 'Description', + 'ifName' => 'Name', + 'ifAlias' => 'Alias', + 'lagIfIndex' => 'LAG', + 'ifHighSpeed' => 'Speed', + 'ifMtu' => 'MTU', + 'ifAdminStatus' => [ + 'title' => 'Admin State', + 'type' => self::$FE_COL_TYPES[ 'SCRIPT' ], + 'script' => 'switch-port/port-status', + 'params' => [ + "state" => "ifAdminStatus", + ] + ], + 'ifOperStatus' => [ + 'title' => 'Operational State', + 'type' => self::$FE_COL_TYPES[ 'SCRIPT' ], + 'script' => 'switch-port/port-status', + 'params' => [ + "state" => "ifOperStatus", + ], + 'active' => [ + 'title' => 'Active', + 'type' => self::$FE_COL_TYPES[ 'YES_NO' ], + ], + ] + ]; + + return true; + } + + /** + * Display the switch ports operation status for a switch + * + * @param Switcher $switch + * + * @return view + */ + public function listOpStatus( Switcher $switch ): view + { + // to refresh switch and switch port details via SNMP + try { + $result = false; + $host = new SNMP( $switch->hostname, $switch->snmppasswd ); + $switch->snmpPoll( $host, true ); + $switch->snmpPollSwitchPorts( $host, true, $result, false ); + $switch->save(); + + AlertContainer::push( "The below is live information gathered via SNMP", Alert::INFO ); + } catch( Exception $e ) { + $lastpolled = is_null( $switch->lastPolled) ? "never" : $switch->lastPolled; + + AlertContainer::push( "Could not update switch and switch port details via SNMP poll. " . + "Last successful poll: " . $lastpolled . ".", Alert::DANGER ); + } + + $this->setUpOpStatus(); + + $this->data[ 'params' ][ 'portStates' ] = Iface::$IF_OPER_STATES; + $this->data[ 'params' ][ 'switch' ] = $switch; + $this->data[ 'params' ][ 'switchid' ] = $switch->id; + $this->data[ 'params' ][ 'switches'] = Switcher::orderBy( 'name' )->get()->keyBy( 'id' ); + + $this->data[ 'rows' ] = $this->listGetData(); + + $this->feParams->pagetitlepostamble = 'List Live Port State for ' . $switch->name ; + + $this->listIncludeTemplates(); + + return $this->display( 'list' ); + } + + + /** + * This action will find all ports on a switch, match them (where possible) to existing + * ports of that switch in the database and allow the user to: + * + * - view name (ifDescr), ifName and ifAlias + * - set the switchport type in bulk + * - remove port(s) + * - manage these actions in bulk (e.g. phpMyAdmin type row management) + * + * Should this be in the SwitchController? Possibly... + * + * @param Switcher $switch Switch + * + * @return view + */ + public function snmpPoll( Switcher $switch ): view + { + if( !$switch->active ) { + AlertContainer::push( "SNMP Polling of ports is only valid for switches that are active", Alert::DANGER ); + redirect::to( route( "switch@list" ) ); + } + + $results = []; + try { + $host = new SNMP( $switch->hostname, $switch->snmppasswd ); + $switch->snmpPoll( $host, true ); + $switch->snmpPollSwitchPorts( $host, true, $results, false ); + $switch->save(); + } catch( Exception $e ) { + AlertContainer::push( "Error polling switch via SNMP.", Alert::DANGER ); + redirect::to( route( "switch@list" ) ); + } + + return view( 'switch-port/snmp-poll' )->with([ + 'switches' => Switcher::orderBy( 'name' )->get()->keyBy( 'id' ), + 's' => $switch, + 'ports' => $results, + ]); + } + + /** + * Sets port type for port loaded + * + * @param Request $r HTTP instance + * + * @return JsonResponse + */ + public function setType( Request $r ): JsonResponse + { + if( !$r->spid ) { + return response()->json( [ 'success' => false ] ); + } + + foreach( $r->spid as $id ) { + $sp = SwitchPort::findOrFail( $id ); + + if( !array_key_exists( $r->type, SwitchPort::$TYPES ) ){ + return response()->json( [ 'success' => false ] ); + } + + $sp->type = $r->type; + $sp->save(); + } + + if( $r->returnMessage ) { + AlertContainer::push( "The selected switch ports have been updated", Alert::SUCCESS ); + } + + return response()->json( [ 'success' => true ] ); + } + + /** + * Change the port status for the Switch Ports + * + * @param Request $r HTTP instance + * + * @return JsonResponse + */ + public function changeStatus( Request $r ): JsonResponse + { + if( $r->spid ) { + foreach( $r->spid as $id ) { + $sp = SwitchPort::findOrFail( $id ); + $sp->active = $r->active ? 1 : 0; + $sp->save(); + } + + AlertContainer::push( "The selected switch ports have been updated.", Alert::SUCCESS ); + return response()->json( [ 'success' => true ] ); + } + return response()->json( [ 'success' => false ] ); + } + + /** + * Delete port + * + * @param Request $r HTTP instance + * + * @return JsonResponse + * + * @throws \Exception + */ + public function deleteSnmpPoll( Request $r ): JsonResponse + { + if( !$r->spid ){ + return response()->json( [ 'success' => false ] ); + } + + foreach( $r->spid as $id ) { + $error = false; + Log::debug( 'Html\Controllers\Switches\SwitchPort::deleteSnmpPoll() - Processing switch port ID: ' . $id ); + + $sp = SwitchPort::findOrFail( $id ); + + if( $sp->physicalInterface()->exists() ) { + $cust = $sp->physicalInterface->virtualInterface->customer; + AlertContainer::push( "Could not delete switch port {$sp->name} as it is assigned to a physical interface for " + . " $cust->id, 'tab' => 'ports' ] ) + . "\">{$cust->name}.", Alert::DANGER + ); + + $error = true; + } + + if( $sp->patchPanelPort()->exists() ) { + $ppp = $sp->patchPanelPort; + AlertContainer::push( "Could not delete switch port {$sp->name} as it is assigned to a patch panel port for " + . " $ppp->id ] ) + . "\">{$ppp->getName()}.", Alert::DANGER + ); + + $error = true; + } + + if( !$error ) { + $sp->delete(); + } + } + + AlertContainer::push( + "Please Note: It is not possible to delete real physical Ethernet switch ports as " + . "the switch is re-polled and these ports are added back into the system as new ports automatically. " + . "The purpose of delete is to remove ports that were manually added to the database that do not match " + . "up with physical ports on the switch. You can, however, deactivate switch ports.", + Alert::INFO + ); + + AlertContainer::push( "The selected switch ports - where possible - have been deleted", Alert::SUCCESS ); + return response()->json( [ 'success' => true ] ); + } + + /** + * Set up all the information to display the optic inventory + * + * @bool + * + * @return true + */ + public function setUpOpticInventory(): bool + { + $this->feParams->listOrderBy = 'cnt'; + $this->feParams->pagetitle = 'Switches'; + $this->feParams->pagetitlepostamble = 'Optic Inventory'; + $this->feParams->route_prefix_page_title = 'switch'; + $this->feParams->route_action = 'optic-inventory'; + $this->feParams->hideactioncolumn = true; + $this->feParams->listOrderByDir = "DESC"; + + $this->feParams->listColumns = [ + 'mauType' => 'Type', + 'cnt' => [ + 'title' => 'Count', + 'type' => self::$FE_COL_TYPES[ 'HAS_ONE' ], + 'controller' => 'switch-port', + 'action' => 'optic-list', + 'nameIdOptionalParam' => 'mau-type', + 'idField' => 'mauType' + ], + ]; + + return true; + } + + /** + * Display the Optic Inventory + * + * @return view + */ + public function opticInventory(): view + { + $this->setUpOpticInventory(); + + $feParams = $this->feParams; + $this->data[ 'rows' ] = SwitchPort::selectRaw( + 'switchport.mauType AS mauType, + COUNT( switchport.mauType ) AS cnt' + ) + ->when( $feParams->listOrderBy , function( Builder $q, $orderby ) use ( $feParams ) { + return $q->orderBy( $orderby, $feParams->listOrderByDir ?? 'ASC'); + }) + ->groupBy( 'switchport.mauType' ) + ->having( 'cnt', '>', '0' ) + ->get()->toArray(); + + $this->listIncludeTemplates(); + return $this->display( 'list' ); + } + + /** + * Set up all the information to display the optics list + * + * @bool + * + * @return true + */ + public function setUpOpticList(): bool + { + $this->feParams->pagetitle = 'Switches'; + $this->feParams->pagetitlepostamble = 'Optic List'; + $this->feParams->route_prefix_page_title = 'switch'; + $this->feParams->listOrderBy = 'ifName'; + $this->feParams->readonly = true; + $this->feParams->hideactioncolumn = true; + + $this->feParams->listColumns = [ + 'ifName' => 'Name', + 'switch' => 'Switch', + 'custname' => [ + 'title' => 'Customer', + 'type' => self::$FE_COL_TYPES[ 'HAS_ONE' ], + 'controller' => 'customer', + 'action' => 'overview', + 'idField' => 'custid' + ], + 'type' => [ + 'title' => 'Type', + 'type' => self::$FE_COL_TYPES[ 'CONST' ], + 'const' => SwitchPort::$TYPES, + ], + 'state' => [ + 'title' => 'State (Admin/Op)', + 'type' => self::$FE_COL_TYPES[ 'SCRIPT' ], + 'script' => 'switch-port/port-admin-status', + 'params' => [ + "adminState" => "ifAdminStatus", + "operState" => "ifOperStatus", + ], + ], + 'mauType' => 'MAU Type', + 'mauState' => 'MAU State', + ]; + + return true; + } + + /** + * Display the Optic list + * + * @return view + */ + public function opticList(): view + { + $this->setUpOpticList(); + + $feParams = $this->feParams; + $this->data[ 'rows' ] = SwitchPort::select( [ + 'sp.*', + 'c.name AS custname', 'c.id AS custid', + 's.id AS switchid', 's.name AS switch' + ] ) + ->from( 'switchport AS sp' ) + ->leftjoin( 'physicalinterface AS pi', 'pi.switchportid', 'sp.id' ) + ->leftjoin( 'virtualinterface AS vi', 'vi.id', 'pi.virtualinterfaceid' ) + ->leftjoin( 'cust AS c', 'c.id', 'vi.custid' ) + ->leftJoin( 'switch AS s', 's.id', 'sp.switchid') + ->when( request()->input( "mau-type" ) , function( Builder $q, $mautype ) { + return $q->where( 'sp.mauType', $mautype); + }, function ($query) { + return $query->where( 'sp.mauType', '!=', NULL); + }) + ->when( $feParams->listOrderBy , function( Builder $q, $orderby ) use ( $feParams ) { + return $q->orderBy( $orderby, $feParams->listOrderByDir ?? 'ASC'); + })->get()->toArray(); + + $this->listIncludeTemplates(); + $this->preList(); + return $this->display( 'list' ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/TestController.php b/app/Http/Controllers/TestController.php new file mode 100644 index 000000000..d3e053b4f --- /dev/null +++ b/app/Http/Controllers/TestController.php @@ -0,0 +1,11 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers\User + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class CustomerToUserController extends Controller +{ + /** + * Allow to display the form to add a customer to user object + * + * @param string $email + * @param Request $r + * + * @return View + */ + public function create( Request $r, string $email ): View + { + /** @var User $us */ + $us = Auth::getUser(); + if( $us->isCustUser() ){ + abort( 403, 'Action no allowed' ); + } + // search user via email address + if( !( $listUsers = User::where( 'email', strtolower( trim( $email ) ) )->get() ) ) { + abort(404, 'User not found'); + } + + Former::populate([ + 'customer_id' => $r->old( 'custid', $r->cust ), + 'linkCancel' => $r->old( 'linkCancel', $r->headers->get( 'referer', "" ) ), + ]); + + return view( 'customer2user/add' )->with([ + 'listUsers' => $listUsers, + 'custs' => Customer::orderBy( 'name' )->get(), + 'privs' => $this->getAllowedPrivs(), + 'c' => Customer::find( $r->cust ) ?: false, + ]); + } + + /** + * Function to store A customerToUser object + * + * @param StoreCustomerToUser $r + * + * @return RedirectResponse + */ + public function store( StoreCustomerToUser $r ): RedirectResponse + { + $c2u = new CustomerToUser; + $c2u->customer_id = $r->cust->id; + $c2u->user_id = $r->user_id; + $c2u->privs = $r->privs; + $c2u->extra_attributes = [ "created_by" => [ "type" => "user" , "user_id" => $r->user_id ] ]; + $c2u->save(); + + event( new UserAddedToCustomerEvent( $c2u ) ); + + $redirect = session()->get( "user_post_store_redirect" ); + session()->remove( "user_post_store_redirect" ); + + $user = $c2u->user; + + Log::notice( Auth::getUser()->username . ' created ' . $user->username . ' via CustomerToUser ID [' . $c2u->id . '] to ' . $c2u->customer->name ); + AlertContainer::push( $user->name . '/' . $user->username . ' has been created to ' . $c2u->customer->name, Alert::SUCCESS ); + + // retrieve the customer ID + if( strpos( $redirect, "customer/overview" ) ) { + return redirect( route( 'customer@overview' , [ 'cust' => $c2u->customer_id , 'tab' => 'users' ] ) ); + } + + return redirect( route( "user@list" ) ); + } + + /** + * Function to Update privs for a CustomerToUser + * + * @param Request $r + * + * @return JsonResponse + */ + public function updatePrivs( Request $r ): JsonResponse + { + /** @var User $us */ + $us = Auth::getUser(); + + /** @var CustomerToUser $c2u */ + $c2u = CustomerToUser::findOrFail( $r->id ); + + if( in_array( (int)$r->privs , User::$PRIVILEGES_ALL, true ) ) { + return response()->json( [ 'success' => false, 'message' => "Unknown privilege requested" ] ); + } + + if( (int)$r->privs === User::AUTH_SUPERUSER ) { + if( !$us->isSuperUser() ) { + return response()->json( [ 'success' => false, 'message' => "You are not allowed to set the super user privilege" ] ); + } + + if( !$c2u->customer->typeInternal() ) { + return response()->json( [ 'success' => false, 'message' => "You are not allowed to set super user privileges for non-internal (IXP) customer types" ] ); + } + + $extraMessage = "Please note that you have given this user full administrative access (super user privilege)."; + } + + $c2u->privs = $r->privs; + $c2u->save(); + + return response()->json( [ 'success' => true, 'message' => "The user's privilege has been updated.", "extraMessage" => $extraMessage ?? null ] ); + } + + /** + * Function to Delete a customer to user link + * + * @param DeleteCustomerToUser $r + * @param CustomerToUser $c2u + * + * @return RedirectResponse + * + * @throws Exception + */ + public function delete( DeleteCustomerToUser $r, CustomerToUser $c2u ): RedirectResponse + { + $disassociatedUser = $c2u->user; + $disassociatedCust = $c2u->customer; + + // Store the initial Customer before the deletion + $initialCust = Auth::user()->customer; + + // Delete User login history + $c2u->userLoginHistories()->delete(); + $c2u->delete(); + + // then reset default customer + if( $disassociatedUser->custid === $disassociatedCust->id ){ + $disassociatedUser->custid = $disassociatedUser->customers() ? $disassociatedUser->customers()->first()->id : null; + $disassociatedUser->save(); + } + + AlertContainer::push( $disassociatedUser->name . '/' . $disassociatedUser->username . ' deleted from ' . $disassociatedCust->name, Alert::SUCCESS ); + Log::notice( Auth::getUser()->username." deleted customer2user" . $disassociatedCust->name . '/' . $disassociatedUser->name ); + + // If the user deleted itself and is logged in as the same customer: + if( $r->user()->id === $disassociatedUser->id && $initialCust->id === $disassociatedCust->id ) { + Auth::logout(); + return redirect( route( "login@showForm" ) ); + } + + // retrieve the customer ID + if( strpos( $r->headers->get( 'referer', "" ), "customer/overview" ) !== false ) { + return redirect( route( "customer@overview" , [ 'cust' => $disassociatedCust->id , "tab" => "users" ] ) ); + } + + return redirect( route( "user@list" ) ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/User/User2FAController.php b/app/Http/Controllers/User/User2FAController.php new file mode 100644 index 000000000..ce13f2299 --- /dev/null +++ b/app/Http/Controllers/User/User2FAController.php @@ -0,0 +1,251 @@ + + * @author Barry O'Donovan + * @category IXP + * @package IXP\Http\Controllers\User + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class User2FAController extends Controller +{ + /** + * Configure 2FA + * + * @return View + */ + public function configure(): View + { + /** @var User $user */ + $user = Auth::getUser(); + + if( !$user->user2FA ) { + $this->generateUser2FA( $user ); + // Refresh to get the new User2FA created above + $user->refresh(); + } + + return view( 'user/2fa/configure' )->with([ + 'user' => $user, + 'qrcode' => $this->generateQRCode( $user ), + ]); + } + + /** + * Enable 2FA for a user + * + * @param Request $r + * + * @return RedirectResponse + * + * @throws User2FAException + */ + public function enable( Request $r ): RedirectResponse + { + if( !$this->checkUserPassword( $r ) || !$this->testOneTimeCode( $r ) ) { + return redirect( route('2fa@configure' ) ); + } + + $r->user()->user2FA->update( [ 'enabled' => true ] ); + + // We also need to mark the current session as 2fa complete: + if( $recallerName = $r->cookies->get( Auth::getRecallerName() ) ) { + $recaller = new Recaller( $recallerName ); + + if( $urt = UserRememberToken::where( 'token', $recaller->token() )->first() ) { + $urt->update( [ 'is_2fa_complete' => true ] ); + } + } + + $this->google2faLogin( $r ); + AlertContainer::push( "2FA successfully enabled.", Alert::SUCCESS ); + return redirect(''); + } + + /** + * Disable 2FA for a user + * + * @param Request $r + * + * @return RedirectResponse + */ + public function disable( Request $r ): RedirectResponse + { + if( !$this->checkUserPassword( $r ) ) { + return redirect( route('2fa@configure' ) ); + } + $r->user()->user2FA->delete(); + + $this->google2faLogin( $r, false ); + AlertContainer::push( "2FA successfully disabled.", Alert::SUCCESS ); + return redirect( route('profile@edit' ) ); + } + + /** + * Remove 2FA for a user + * + * @param Request $r + * + * @return RedirectResponse + */ + public function delete( Request $r, User $user ): RedirectResponse + { + $user?->user2FA?->delete(); + + AlertContainer::push( "2FA successfully deleted for {$user->username}.", Alert::SUCCESS ); + return redirect( route('user@list' ) ); + } + + + + /** + * Create Password Security if needed and generate a QR code + * + * @param User $user + * + * @return void + */ + private function generateUser2FA( User $user ): void + { + $google2fa = app( 'pragmarx.google2fa' ); + + if( !$user->user2FA ) { + User2FA::create([ + 'user_id' => $user->id, + 'enabled' => false, + 'secret' => $google2fa->generateSecretKey( 32 ), + ]); + } + } + + /** + * Get a QR Code object for the user's 2fa settings + * + * @param User $user + * + * @return mixed + */ + private function generateQRCode( User $user ): mixed + { + $google2fa = app( 'pragmarx.google2fa' ); + + return $google2fa->getQRCodeInline( + config( 'identity.sitename' ), + $user->email, + $user->user2FA->secret + ); + } + + /** + * Check if the user password is valid + * + * @param Request $r + * + * @return bool + */ + private function checkUserPassword( Request $r ): bool + { + if( !Hash::check( $r->password, $r->user()->password ) ) { + AlertContainer::push( 'Incorrect user password - please check your password and try again.', Alert::DANGER ); + return false; + } + + return true; + } + + /** + * Test if the one time code is valid + * + * @param Request $r + * + * @return bool + * + * @throws User2FAException + */ + private function testOneTimeCode( Request $r ): bool + { + if( !$r->user()->user2FA ) { + throw new User2FAException('Attempt to test OTC but no 2FA record exists'); + } + + $google2fa = app( 'pragmarx.google2fa' ); + + if( !$google2fa->verifyKey( $r->user()->user2FA->secret, $r->one_time_password ) ) { + AlertContainer::push( "Incorrect one time code - please check your code and try again.", Alert::DANGER ); + return false; + } + + return true; + } + + /** + * Login or logout via the 2FA authenticator. + * + * This essentially decides whether the 2fa middleware will look for a 2fa code on the next request. + * + * @param Request $r + * @param bool $login + * + * @return void + */ + private function google2faLogin( Request $r, bool $login = true ): void + { + $authenticator = new GoogleAuthenticator( $r ); + $login ? $authenticator->login() : $authenticator->logout(); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/User/UserController.php b/app/Http/Controllers/User/UserController.php new file mode 100644 index 000000000..2bf522bfd --- /dev/null +++ b/app/Http/Controllers/User/UserController.php @@ -0,0 +1,514 @@ + + * @author Barry O'Donovan + * @category IXP + * @package IXP\Http\Controllers\User + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class UserController extends Controller +{ + /** + * Set as a session the redirect link + * + * @return Void + */ + private function redirectLink(): void + { + if( !request()->old() ) { + request()->session()->remove( "user_post_store_redirect" ); + session()->put( 'user_post_store_redirect', request()->headers->get( 'referer', "" ) ); + } + } + + /** + * Get tge list of user Depending on the Privilege + * + * @param User|null $u + * + * @return array + */ + private function getListData( User $u = null ): array + { + return User::selectRaw( + 'u.id as id, + u.name AS name, + u.username as username, + u.email as email, + u.created_at as created, + u.disabled as disabled, + u.peeringdb_id as peeringdb_id, + u.updated_at AS lastupdated, + u.creator as creator, + c.id as custid, + c.name as customer, + COUNT( c2u.id ) as nbC2U, + MAX( c2u.privs ) as privileges, + c2u.id as c2uid, + u2fa.enabled as u2fa_enabled, + u2fa.id as psid ' ) + ->from( 'user AS u' ) + ->leftJoin( 'cust AS c', 'c.id', 'u.custid' ) + ->leftJoin( 'customer_to_users AS c2u', 'c2u.user_id', 'u.id' ) + ->leftJoin( 'user_2fa AS u2fa', 'u2fa.user_id', 'u.id' ) + ->when( !Auth::user()->isSuperUser(), function( Builder $q ) { + return $q->where( 'c2u.customer_id', Auth::user()->custid ) + ->where( 'c2u.privs', '<=', User::AUTH_CUSTADMIN ); + } ) + ->when( $u, function( Builder $q, $u ) { + return $q->where( 'u.id', $u->id ); + } )->groupBy( 'id' ) + ->orderBy( 'username' )->get()->toArray(); + } + + /** + * Display the User list + * + * @return view + * + * @throws AuthorizationException + */ + public function list(): View + { + $this->authorize( 'any', User::class ); + + return view( 'user/index' )->with([ + 'users' => $this->getListData(), + 'nbC2u' => CustomerToUser::selectRaw( 'user_id, COUNT( id ) AS nbC2U' ) + ->groupBy( 'user_id' )->get()->keyBy( 'user_id' )->toArray() + ]); + } + + /** + * Display the first step form to Add a User object via email address + * + * @param Request $r + * + * @param Customer|null $cust + * + * @return View + * + * @throws AuthorizationException + */ + public function createForm( Request $r, Customer $cust = null ): View + { + $this->authorize( 'any', User::class ); + + $this->redirectLink(); + + Former::populate([ + 'cancelBtn' => $r->old( 'cancelBtn', $r->headers->get( 'referer', "" ) ), + ]); + + return view( 'user/create-wizard' )->with([ + 'custid' => $cust->id ?? false, + ]); + } + + /** + * Function to check if the Email address is already used by a User + * + * if yes we get the user information and display the information of the user + * if no we display the create/edit form + * + * @param CheckEmailRequest $r + * + * @return RedirectResponse + * + * @throws AuthorizationException + */ + public function createCheckEmail( CheckEmailRequest $r ): RedirectResponse + { + $this->authorize( 'any', User::class ); + + $custid = null; + + if( $r->custid && ( $cust = Customer::find( $r->custid ) ) ){ + $custid = $cust->id; + } + + if( $user = User::where( 'email', $r->email )->first() ){ + return redirect( route( "customer-to-user@create", [ 'email' => $user->email ] ) . ( $custid ? "?cust=" . $custid : '' ) ); + } + + return redirect( route("user@create" , [ 'custid' => $custid, 'email' => $r->email ] ) ); + } + + /** + * Allow to display the form to create a user + * + * @param Request $r + * + * @return View + * + * @throws AuthorizationException + */ + public function create( Request $r ): View + { + $this->authorize( 'any', User::class ); + + if( !$r->session()->exists( 'user_post_store_redirect' ) ) { + $this->redirectLink(); + } + + Former::populate([ + 'name' => $r->old( 'name', $r->name ), + 'username' => $r->old( 'username', $r->username ), + 'email' => $r->old( 'email', $r->email ), + 'authorisedMobile' => $r->old( 'authorisedMobile', $r->authorisedMobile ), + 'disabled' => $r->old( 'disabled', $r->disabled ), + 'cust' => $r->old( 'cust', $r->cust ), + 'linkCancel' => $r->old( 'linkCancel', $r->headers->get( 'referer', "" ) ) + ]); + + return view( 'user/edit' )->with( [ + 'user' => false, + 'disableInputs' => false, + 'isAdd' => true, + 'custs' => Customer::orderBy( 'name' )->get(), + 'privs' => $this->getAllowedPrivs(), + 'c' => Customer::find( $r->custid ) ?? false, + ]); + } + + /** + * Allow to create a User + * + * @param StoreUser $r instance of the current HTTP request + * + * @throws AuthorizationException + */ + public function store( StoreUser $r ): RedirectResponse|Redirector + { + /** @var User $us */ + $us = Auth::getUser(); + + $this->authorize( 'any', User::class ); + + // Creating the User object + $user = new User; + $user->creator = $us->username; + $user->password = Hash::make( Str::random(16) ); + $user->name = $r->name; + $user->authorisedMobile = $r->authorisedMobile; + $user->username = strtolower( $r->username ); + $user->email = strtolower( $r->email ); + $user->disabled = $r->disabled ? 0 : 1; // input as enable in the view + $user->lastupdatedby = Auth::id(); + $user->privs = $r->privs; + $user->custid = $us->isSuperUser() ? $r->custid : $us->custid; + $user->save(); + + // Creating the CustomerToUser object + $c2u = new CustomerToUser; + $c2u->customer_id = $user->custid; + $c2u->user_id = $user->id; + $c2u->privs = $r->privs; + $c2u->extra_attributes = [ "created_by" => [ "type" => "user" , "user_id" => $user->id ] ]; + $c2u->save(); + + if( (int)$r->privs === User::AUTH_SUPERUSER ) { + AlertContainer::push( 'Please note that you have given this user full administrative access.', Alert::WARNING ); + } + + // Send Email related to the event + event( new UserCreatedEvent( $user ) ); + + Log::notice( Auth::user()->username . ' Created a User with ID ' . $user->id ); + + AlertContainer::push( "User created. A welcome email is being sent to {$user->email} with " + . "instructions on how to set their password. ", Alert::SUCCESS ); + + return redirect( $this->postStoreRedirect() ); + } + + /** + * Allow to display the form to Edit a user + * + * @param User $u + * @param Request $r + * + * @return View + * + * @throws AuthorizationException + */ + public function edit( Request $r, User $u ): View + { + /** @var User $us */ + $us = Auth::user(); + $this->authorize( 'access', $u ); + + $isSuperUser = $us->isSuperUser(); + + if( !request()->session()->exists( 'user_post_store_redirect' ) ) { + $this->redirectLink(); + } + + $dataCust = [ + 'name' => $r->old( 'name', $u->name ), + 'username' => $r->old( 'username', $u->username ), + 'email' => $r->old( 'email', $u->email ), + 'authorisedMobile' => $r->old( 'authorisedMobile', $u->authorisedMobile ), + 'disabled' => $r->old( 'disabled', $u->disabled ? '0' : '1' ), + 'linkCancel' => $r->old( 'linkCancel', $r->headers->get( 'referer', "" ) ), + ]; + + $datac2u = []; + + $listC2u = $isSuperUser ? $u->customerToUser : $u->customerToUser()->where( 'customer_id', Auth::user()->custid )->get(); + + foreach( $listC2u as $c2u ) { + if( $isSuperUser ) { + $datac2u[ 'privs_' . $c2u->id ] = $r->old( 'privs_' . $c2u->id , $c2u->privs ); + } else { + $datac2u[ 'privs' ] = $r->old( 'privs', $c2u->privs ); + } + } + + Former::populate( array_merge( $dataCust, $datac2u ) ); + + return view( 'user/edit' )->with([ + 'user' => $u, + 'disableInputs' => !$isSuperUser, + 'isAdd' => false, + 'custs' => Customer::orderBy( 'name' )->get(), + 'privs' => $this->getAllowedPrivs(), + 'c' => Customer::find( $r->custid ) ?? false, + ]); + } + + /** + * Allow to update a User + * + * @param UpdateUser $r instance of the current HTTP request + * @param User $u + * + * @throws AuthorizationException + */ + public function update( UpdateUser $r, User $u ): RedirectResponse|Redirector + { + /** @var User $us */ + $us = Auth::user(); + $this->authorize( 'access', $u ); + + // Superuser OR Logged User edit his own user + if( ( $isSuperUser = $us->isSuperUser() ) || $u->id === Auth::id() ) { + $u->name = $r->name; + $u->authorisedMobile = $r->authorisedMobile; + } + + if( $isSuperUser ) { + $u->username = strtolower( $r->username ); + $u->email = $r->email; + $u->disabled = $r->disabled ? 0 : 1;// displayed as enabled in the view + + // Delete Remember Token for the user if disabled + if(!$r->disabled){ + $u->userRememberTokens()->delete(); + } + } + + $u->lastupdatedby = Auth::id(); + $u->save(); + + if( !$isSuperUser ) { + /** @var $c2u CustomerToUser */ + if( !( $c2u = $u->customerToUser()->where( 'customer_id', Auth::user()->custid )->first() ) ) { + abort(404, 'UserToCustomer not found'); + } + + $c2u->privs = $r->privs; + $c2u->save(); + } + + Log::notice( Auth::user()->username . ' updated a User with ID ' . $u->id ); + AlertContainer::push( 'User updated', Alert::SUCCESS ); + return redirect( $this->postStoreRedirect() ); + } + + /** + * Redirect the user post store + */ + protected function postStoreRedirect(): string + { + /** @var User $us */ + $us = Auth::user(); + + if( $us->isSuperUser() ) { + $redirect = session( "user_post_store_redirect" ); + session()->forget( "user_post_store_redirect" ); + + if( strpos( $redirect, "customer/overview" ) ) { + return $redirect; + } + } + + if( $us->isCustUser() ) { + return ''; + } + + return route( 'user@list' ); + } + + /** + * Display the patch panel information + * + * @param User $u ID of the patch panel + * + * @return view + * + * @throws AuthorizationException + */ + public function view( User $u ): View + { + $this->authorize( 'access', $u ); + + $user = $this->getListData( $u )[ 0 ] ?? null; + + if( !$user ){ + abort( 404,'User does not exist'); + } + + return view( 'user/view' )->with([ + 'u' => $user, + 'c2us' => $u->customerToUser + ]); + } + + /** + * Delete a user and everything related !! + * + * @param DeleteRequest $r + * @param User $u + * + * @return RedirectResponse + * + * @throws AuthorizationException + */ + public function delete( DeleteRequest $r, User $u ) : RedirectResponse + { + /** @var User $us */ + $us = Auth::user(); + + $this->authorize( 'any', User::class ); + + // delete all the user's API keys + $u->apiKeys()->delete(); + + // delete all the C2U for the user + foreach( $u->customerToUser as $c2u ) { + // delete all the user's login records + $c2u->userLoginHistories()->delete(); + $c2u->delete(); + } + + // preserve and delete logs + foreach( \IXP\Models\Log::whereUserId( $u->id )->orderBy( 'id', 'ASC' )->get() as $l ) { + Log::info( "[USER DEL - PRESERVING LOG {$l->id}] {$l->model}:{$l->model_id}:{$l->action} ::: {$l->message} ::: " . json_encode( $l->models ) . " ::: {$l->created_at->format('Y-m-d H:i:s')} :::ENDS:::" ); + $l->delete(); + } + + $u->delete(); + + AlertContainer::push('User deleted.', Alert::SUCCESS ); + Log::notice( $us->username." deleted user" . $u->username ); + + // If the user delete itself and is loggued as the same customer logout + if( Auth::id() === $u->id ) { + Auth::logout(); + return redirect( route( "login@showForm" ) ); + } + + if( $us->isSuperUser() && strpos( request()->headers->get('referer', "" ), "customer/overview" ) ) { + return redirect( route( "customer@overview", [ 'cust' => $u->custid , "tab" => "users"] ) ); + } + + return redirect( route( "user@list" ) ); + } + + /** + * Send or resend the welcome email to a new user + * + * @param User $u + * + * @return RedirectResponse + */ + public function resendWelcomeEmail( User $u ): RedirectResponse + { + /** @var User $us */ + $us = Auth::user(); + + Mail::to( $u->email )->send( new UserCreatedeMailable( $u, true ) ); + AlertContainer::push( sprintf( 'The welcome email has been resent' ), Alert::SUCCESS ); + + if( $us->isSuperUser() && strpos( request()->headers->get('referer', "" ), "customer/overview" ) ) { + return redirect( route( "customer@overview", [ 'cust' => $u->custid , "tab" => "users"] ) ); + } + + return redirect( route( "user@list" ) ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/User/UserRememberTokenController.php b/app/Http/Controllers/User/UserRememberTokenController.php new file mode 100644 index 000000000..3a46b0fa7 --- /dev/null +++ b/app/Http/Controllers/User/UserRememberTokenController.php @@ -0,0 +1,212 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers\User + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class UserRememberTokenController extends EloquentController +{ + /** + * The object being created / edited + * + * @var UserRememberToken + */ + protected $object = null; + + /** + * The URL prefix to use. + * + * Automatically determined based on the controller name if not set. + * + * @var string|null + */ + protected static $route_prefix = "active-sessions"; + + /** + * The minimum privileges required to access this controller. + * + * If you set this to less than the superuser, you need to manage privileges and access + * within your own implementation yourself. + * + * @var int + */ + public static $minimum_privilege = User::AUTH_CUSTUSER; + + /** + * Is this a read only controller? + * + * @var boolean + */ + public static $read_only = true; + + /** + * Should we allow a read only controller to delete + * + * @var boolean + */ + public static $allow_delete_for_read_only = true; + + /** + * This function sets up the frontend controller + */ + #[\Override] + public function feInit(): void + { + $this->feParams = (object)[ + 'model' => UserRememberToken::class, + 'pagetitle' => 'Your Active Login Sessions', + 'titleSingular' => 'Active Login Session', + 'nameSingular' => 'active login session', + 'listOrderBy' => 'created_at', + 'listOrderByDir' => 'ASC', + 'readonly' => self::$read_only, + 'allowDeleteForReadOnly' => self::$allow_delete_for_read_only, + 'viewFolderName' => 'user-remember-token', + 'listColumns' => [ + 'device' => 'Device', + 'ip' => 'IP', + 'created_at' => [ + 'title' => 'Created', + 'type' => self::$FE_COL_TYPES[ 'DATETIME' ] + ], + 'expires' => [ + 'title' => 'Expires', + 'type' => self::$FE_COL_TYPES[ 'DATETIME' ] + ], + ] + ]; + + // display the same information in the view as the list + $this->feParams->viewColumns = $this->feParams->listColumns; + } + + /** + * Additional routes + * + * @param string $route_prefix + * + * @return void + */ + #[\Override] + protected static function additionalRoutes( string $route_prefix ): void + { + // NB: this route is marked as 'read-only' to disable normal CRUD operations. It's not really read-only. + Route::group( [ 'prefix' => $route_prefix ], static function() use ( $route_prefix ) { + Route::delete( 'delete', 'User\UserRememberTokenController@delete' )->name( $route_prefix."@delete" ); + }); + } + + /** + * Function which can be over-ridden to perform any pre-list tasks + * + * E.g. adding elements to $this->view for the pre/post-amble templates. + * + * @return void + */ + #[\Override] + protected function preList(): void + { + // We want to indicate which session is the user's //current// session so they can avoid logging themselves out. + // We identify it by matching the remember me cookie token with the database token: + $token = null; + + if( $r = request()->cookies->get( Auth::getRecallerName() ) ) { + $recaller = new Recaller( $r ); + $token = $recaller->token(); + } + + $this->data['session_token'] = $token; + } + + /** + * Provide array of rows for the list and view + * + * @param int|null $id The `id` of the row to load for `view`. `null` if `list` + * + * @return array + */ + #[\Override] + protected function listGetData( ?int $id = null ): array + { + $feParams = $this->feParams; + return UserRememberToken::where( 'user_id', request()->user()->id ) + ->when( $id , function( Builder $q, $id ) { + return $q->where('id', $id ); + } )->when( $feParams->listOrderBy , static function( Builder $q, $orderby ) use ( $feParams ) { + return $q->orderBy( $orderby, $feParams->listOrderByDir ?? 'ASC'); + })->get()->toArray(); + } + + /** + * @inheritdoc + */ + #[\Override] + protected function preDelete(): bool + { + // ensure a user can only delete their own sessions: + return $this->object->user_id === Auth::id(); + } + + /** + * Allow D2F implementations to override where the post-delete redirect goes. + * + * To implement this, have it return a valid route url (e.g. `return route( "route-name" );` + * + * For UserRememberToken, we need to log the user out if they deleted the current sessions remember me token. + * + * @return null|string + */ + #[\Override] + protected function postDeleteRedirect(): ?string + { + if( $r = request()->cookies->get( Auth::getRecallerName() ) ) { + $recaller = new Recaller( $r ); + if( $this->object->token === $recaller->token() ) { + return route('login@logout'); + } + } + return null; + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Utils/IxfCompareController.php b/app/Http/Controllers/Utils/IxfCompareController.php new file mode 100644 index 000000000..c8acda387 --- /dev/null +++ b/app/Http/Controllers/Utils/IxfCompareController.php @@ -0,0 +1,150 @@ + + * @category IXP + * @package IXP\Http\Controllers\Customer + * @copyright Copyright (C) 2009 - 2022 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class IxfCompareController extends Controller +{ + public function index(): View + { + Former::populate([ + 'sourcea_dd' => request()->old( 'sourcea_dd' ), + 'sourcea_tf' => request()->old( 'sourcea_tf' ), + 'sourceb_dd' => request()->old( 'sourcea_dd' ), + 'sourceb_tf' => request()->old( 'sourcea_tf' ), + ]); + + + return view( 'utils/ixf-compare' )->with([ + 'sources' => array_combine( array_keys( config('ixp_fe.ixfsources')), array_keys( config('ixp_fe.ixfsources')) ), + 'results' => false, + ]); + } + + + public function compare( Request $r ): View|RedirectResponse + { + $sources = config('ixp_fe.ixfsources'); + + if( !isset( $sources[ $r->sourcea_dd ] ) || !isset( $sources[ $r->sourceb_dd ] ) || $r->sourcea_dd === $r->sourceb_dd ) { + AlertContainer::push( 'Bad sources for IX-F data.', Alert::DANGER ); + return redirect()->back(); + } + + // get API data + $a = Cache::remember( 'ixf_compare_' . $r->sourcea_dd, 3600, function () use ( $r, $sources ) { return json_decode( file_get_contents( $sources[ $r->sourcea_dd ][ 'url' ] ) ); } ); + $b = Cache::remember( 'ixf_compare_' . $r->sourceb_dd, 3600, function () use ( $r, $sources ) { return json_decode( file_get_contents( $sources[ $r->sourceb_dd ][ 'url' ] ) ); } ); + + $netsa = $this->getNetworks( $a, $sources[ $r->sourcea_dd ][ 'ixid' ] ); + $netsb = $this->getNetworks( $b, $sources[ $r->sourceb_dd ][ 'ixid' ] ); + + return view( 'utils/ixf-compare' )->with([ + 'sources' => array_combine( array_keys( config('ixp_fe.ixfsources')), array_keys( config('ixp_fe.ixfsources')) ), + 'results' => $this->compareIXs( $netsa, $netsb ), + ]); + } + + /** + * @return (int|mixed)[][] + * + * @psalm-return array + */ + private function getNetworks( object $ixf, int $ixid ): array + { + $nets = []; + + foreach( $ixf->member_list as $net ) { + foreach( $net->connection_list as $cl ) { + if( $cl->ixp_id != $ixid ) { + continue; + } + + if( !isset( $nets[ $net->asnum ] ) ) { + $nets[ $net->asnum ]['name'] = $net->name; + $nets[ $net->asnum ]['speed'] = 0; + } + + foreach( $cl->if_list as $ifl ) { + $nets[ $net->asnum ]['speed'] += $ifl->if_speed; + } + } + } + + return $nets; + } + + /** + * @return (array|mixed)[][] + * + * @psalm-return array{shared: array, aonly: array, bonly: array} + */ + private function compareIXs( array $a, array $b ): array + { + $shared = []; + + foreach( $a as $as => $details ) { + if( isset( $b[ $as ] ) ) { + $shared[ $as ] = [ + 'name' => $details['name'], + 'aspeed' => $details['speed'], + 'bspeed' => $b[$as]['speed'], + ]; + unset( $a[$as] ); + unset( $b[$as] ); + } + } + + ksort( $a, SORT_NUMERIC ); + ksort( $b, SORT_NUMERIC ); + ksort( $shared, SORT_NUMERIC ); + + return [ 'shared' => $shared, 'aonly' => $a, 'bonly' => $b ]; + } + +} \ No newline at end of file diff --git a/app/Http/Controllers/VendorController.php b/app/Http/Controllers/VendorController.php new file mode 100644 index 000000000..4196e91ce --- /dev/null +++ b/app/Http/Controllers/VendorController.php @@ -0,0 +1,198 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class VendorController extends EloquentController +{ + /** + * The object being created / edited + * + * @var Vendor + */ + protected $object = null; + + /** + * This function sets up the frontend controller + */ + #[\Override] + public function feInit(): void + { + $this->feParams = (object)[ + 'model' => Vendor::class, + 'pagetitle' => 'Vendors', + 'titleSingular' => 'Vendor', + 'nameSingular' => 'a vendor', + 'listOrderBy' => 'name', + 'listOrderByDir' => 'ASC', + 'viewFolderName' => 'vendor-e2f', + 'listColumns' => [ + 'name' => 'Name', + 'shortname' => 'Short Name', + 'bundle_name' => 'Bundle Name' + ] + ]; + + // display the same information in the view as the list + $this->feParams->viewColumns = array_merge( + $this->feParams->listColumns, + [ + 'created_at' => [ + 'title' => 'Created', + 'type' => self::$FE_COL_TYPES[ 'DATETIME' ] + ], + 'updated_at' => [ + 'title' => 'Updated', + 'type' => self::$FE_COL_TYPES[ 'DATETIME' ] + ] + ] + ); + + } + + /** + * Provide array of rows for the list and view + * + * @param int|null $id The `id` of the row to load for `view`. `null` if `list` + * + * @return array + */ + #[\Override] + protected function listGetData( ?int $id = null ): array + { + $feParams = $this->feParams; + return Vendor::when( $id , function( Builder $q, $id ) { + return $q->where('id', $id ); + } )->when( $feParams->listOrderBy , function( Builder $q, $orderby ) use ( $feParams ) { + return $q->orderBy( $orderby, $feParams->listOrderByDir ?? 'ASC'); + })->get()->toArray(); + } + + /** + * Display the form to create an object + * + * @return Vendor[] + * + * @psalm-return array{object: Vendor} + */ + #[\Override] + protected function createPrepareForm(): array + { + return [ + 'object' => $this->object, + ]; + } + + /** + * Display the form to edit an object + * + * @param int $id ID of the row to edit + * + * @return array + * + * @psalm-return array{object: mixed} + */ + #[\Override] + protected function editPrepareForm( int $id ): array + { + $this->object = Vendor::findOrFail( $id ); + + Former::populate([ + 'name' => request()->old( 'name', $this->object->name ), + 'shortname' => request()->old( 'shortname', $this->object->shortname ), + 'bundle_name' => request()->old( 'bundle_name', $this->object->bundle_name ), + ]); + + return [ + 'object' => $this->object, + ]; + } + + /** + * Function to do the actual validation and storing of the submitted object. + * + * @param Request $r + * + * @return true + */ + #[\Override] + public function doStore( Request $r ): bool|RedirectResponse + { + $this->checkForm( $r ); + $this->object = Vendor::create( $r->all() ); + return true; + } + + /** + * Function to do the actual validation and updating of the submitted object. + * + * @param Request $r + * @param int $id + * + * @return true + */ + #[\Override] + public function doUpdate( Request $r, int $id ): bool|RedirectResponse + { + $this->object = Vendor::findOrFail( $id ); + $this->checkForm( $r ); + $this->object->update( $r->all() ); + return true; + } + + /** + * Check if the form is valid + * + * @param Request $r + */ + #[\Override] + public function checkForm( Request $r ): void + { + $r->validate( [ + 'name' => 'required|string|max:255', + 'shortname' => 'required|string|max:255', + ] ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/VlanController.php b/app/Http/Controllers/VlanController.php new file mode 100644 index 000000000..edd86ecc6 --- /dev/null +++ b/app/Http/Controllers/VlanController.php @@ -0,0 +1,371 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Controllers + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class VlanController extends EloquentController +{ + /** + * The object being created / edited + * + * @var Vlan + */ + protected $object = null; + + /** + * This function sets up the frontend controller + */ + #[\Override] + public function feInit(): void + { + $this->feParams = (object)[ + 'model' => Vlan::class, + 'pagetitle' => 'VLANs', + 'titleSingular' => 'VLAN', + 'nameSingular' => 'VLAN', + 'listOrderBy' => 'number', + 'listOrderByDir' => 'ASC', + 'viewFolderName' => 'vlan', + 'listColumns' => [ + 'name' => 'Description', + 'config_name' => 'Config Name', + 'number' => '802.1q Tag', + 'infrastructure_name' => 'Infrastructure', + 'private' => [ + 'title' => 'Private', + 'type' => self::$FE_COL_TYPES[ 'YES_NO' ] + ], + ] + ]; + + // display the same information in the view as the list + $this->feParams->viewColumns = array_merge( + $this->feParams->listColumns, + [ + 'peering_matrix' => [ + 'title' => 'Peering Matrix', + 'type' => self::$FE_COL_TYPES[ 'YES_NO' ], + ], + 'peering_manager' => [ + 'title' => 'Peering Manager', + 'type' => self::$FE_COL_TYPES[ 'YES_NO' ], + ], + 'export_to_ixf' => [ + 'title' => 'Export to IX-F', + 'type' => self::$FE_COL_TYPES[ 'YES_NO' ], + ], + 'notes' => [ + 'title' => 'Notes', + 'type' => self::$FE_COL_TYPES[ 'PARSDOWN' ] + ], + 'created_at' => [ + 'title' => 'Created', + 'type' => self::$FE_COL_TYPES[ 'DATETIME' ] + ], + 'updated_at' => [ + 'title' => 'Updated', + 'type' => self::$FE_COL_TYPES[ 'DATETIME' ] + ] + ] + ); + } + + /** + * Additional routes + * + * @param string $route_prefix + * + * @return void + */ + #[\Override] + protected static function additionalRoutes( string $route_prefix ): void + { + Route::group( [ 'prefix' => $route_prefix ], static function() use ( $route_prefix ) { + Route::get( 'private', 'VlanController@listPrivate' )->name( $route_prefix . '@private' ); + Route::get( 'private/infra/{infra}', 'VlanController@listPrivate' )->name( $route_prefix . '@privateInfra' ); + Route::get( 'list/infra/{infra}', 'VlanController@listInfra' )->name( $route_prefix . '@infra' ); + Route::get( 'list/infra/{infra}/public/{public}', 'VlanController@listInfra' )->name( $route_prefix . '@infraPublic' ); + }); + } + + /** + * Provide array of rows for the list and view + * + * @param int|null $id The `id` of the row to load for `view`. `null` if `list` + * + * @return array + */ + #[\Override] + protected function listGetData( ?int $id = null ): array + { + $param = $this->feParams; + return Vlan::select( [ 'vlan.*', 'i.shortname AS infrastructure_name' ] ) + ->leftJoin( 'infrastructure AS i', 'i.id', 'vlan.infrastructureid' ) + ->when( $id , function( Builder $q, $id ) { + return $q->where('vlan.id', $id ); + } ) + ->when( $this->feParams->privateList ?? false, function( Builder $q ) { + return $q->where( 'private', 1 ); + }) + ->when( $this->feParams->publicOnly ?? false, function( Builder $q ) { + return $q->where( 'private', 0 ); + }) + ->when( $this->feParams->infra ?? false, function( Builder $q, $infra ) { + return $q->where( 'infrastructureid', $infra->id ); + }) + ->when( $this->feParams->listOrderBy ?? false, function( Builder $q, $orderby ) use ( $param ) { + return $q->orderBy( $orderby, $param->listOrderByDir ?? 'asc' ); + }) + ->get()->toArray(); + } + + /** + * Display the form to create an object + * + * @return (Vlan|mixed)[] + * + * @psalm-return array{object: Vlan, infrastructure: mixed} + */ + #[\Override] + protected function createPrepareForm(): array + { + return [ + 'object' => $this->object, + 'infrastructure' => Infrastructure::orderBy( 'name' )->get(), + ]; + } + + /** + * Function to do the actual validation and storing of the submitted object. + * + * @param Request $r + * + * @return RedirectResponse|true + */ + #[\Override] + public function doStore( Request $r ): bool|RedirectResponse + { + $this->checkForm( $r ); + if( $this->checkIsDuplicate( $r, null ) ) { + return Redirect::back()->withInput(); + } + $this->object = Vlan::create( $r->all() ); + return true; + } + + /** + * Display the form to edit an object + * + * @param null $id ID of the row to edit + * + * @return array + * + * @psalm-return array{object: mixed, infrastructure: mixed} + */ + #[\Override] + protected function editPrepareForm( $id = null ): array + { + $this->object = Vlan::findOrFail( $id ); + + Former::populate([ + 'name' => request()->old( 'name', $this->object->name ), + 'number' => request()->old( 'number', $this->object->number ), + 'infrastructureid' => request()->old( 'infrastructureid', $this->object->infrastructureid ), + 'config_name' => request()->old( 'config_name', $this->object->config_name ), + 'private' => request()->old( 'private', $this->object->private ), + 'peering_matrix' => request()->old( 'peering_matrix', $this->object->peering_matrix ), + 'peering_manager' => request()->old( 'peering_manager', $this->object->peering_manager ), + 'export_to_ixf' => request()->old( 'export_to_ixf', $this->object->export_to_ixf ), + 'notes' => request()->old( 'notes', $this->object->notes ), + ]); + + return [ + 'object' => $this->object, + 'infrastructure' => Infrastructure::orderBy( 'name' )->get(), + ]; + } + + /** + * Function to do the actual validation and updating of the submitted object. + * + * @param Request $r + * @param int $id + * + * @return RedirectResponse|true + */ + #[\Override] + public function doUpdate( Request $r, int $id ): bool|RedirectResponse + { + $this->object = Vlan::findOrFail( $id ); + $this->checkForm( $r ); + + if( $this->checkIsDuplicate( $r, $this->object->id ) ){ + return Redirect::back()->withInput(); + } + $this->object->update( $r->all() ); + return true; + } + + /** + * @inheritdoc + */ + #[\Override] + protected function preDelete(): bool + { + $okay = true; + if( ( $cnt = $this->object->routers()->count() ) ) { + AlertContainer::push( "Could not delete this VLAN as {$cnt} router(s) are assigned to it", Alert::DANGER ); + $okay = false; + } + + if( ( $cnt = $this->object->ipv4addresses()->count() ) ) { + AlertContainer::push( "Could not delete this VLAN as {$cnt} IPv4 address(es) are assigned to it", Alert::DANGER ); + $okay = false; + } + + if( ( $cnt = $this->object->ipv6addresses()->count() ) ) { + AlertContainer::push( "Could not delete this VLAN as {$cnt} IPv6 address(es) are assigned to it", Alert::DANGER ); + $okay = false; + } + + if( ( $cnt = $this->object->vlanInterfaces()->count() ) ) { + AlertContainer::push( "Could not delete this VLAN as {$cnt} VLAN Interfaces are assigned to it", Alert::DANGER ); + $okay = false; + } + + return $okay; + } + + /** + * Display the private Vlan + * + * @param Infrastructure|null $infra ID of the vlan to display + * + * @return View + */ + public function listPrivate( ?Infrastructure $infra = null ): View + { + $this->data[ 'rows' ] = Vlan::where( 'private', 1 ) + ->when( $infra, function( $q, $infra ) { + return $q->where('infrastructureid', $infra->id); + }) + ->with([ + 'vlanInterfaces.virtualInterface.customer', + 'vlanInterfaces.virtualInterface.physicalInterfaces.switchport.switcher.cabinet.location' + ])->get(); + + $this->data[ 'params' ] = [ 'infra' => $infra ]; + return $this->display( 'private' ); + } + + /** + * Display the Vlan for an Infrastructure + * + * @param Infrastructure $infra + * @param null $public only the public vlan ? + * + * @return View|RedirectResponse + */ + public function listInfra( Infrastructure $infra, $public = null ): View|RedirectResponse + { + if( $public ) { + $this->feParams->publicOnly = true; + } + + $this->feParams->infra = $infra; + return $this->list( request() ); + } + + /** + * Check if the form is valid + * + * @param Request $r + */ + #[\Override] + public function checkForm( Request $r ): void + { + $r->validate( [ + 'name' => 'required|string|max:255', + 'number' => 'required|integer|min:1|max:4096', + 'config_name' => 'required|string|max:32|alpha_dash', + 'infrastructureid' => 'required|integer|exists:infrastructure,id', + ] ); + } + + /** + * Check if there is a duplicate vlan object with those values + * + * @param int|null $objectid + * @param Request $r + * + * @return bool + */ + private function checkIsDuplicate( Request $r, ?int $objectid = null ): bool + { + $exist = Vlan::where( 'infrastructureid', $r->infrastructureid ) + ->where( 'config_name', $r->config_name ) + ->when( $objectid , function( Builder $q, $objectid ) { + return $q->where( 'id', '!=', $objectid ); + })->count(); + + if( $exist ) { + AlertContainer::push( "The couple Infrastructure and config name already exist.", Alert::DANGER ); + return true; + } + + return false; + } +} \ No newline at end of file diff --git a/app/Http/Controllers/WeatherMapController.php b/app/Http/Controllers/WeatherMapController.php new file mode 100644 index 000000000..ecd0b1b21 --- /dev/null +++ b/app/Http/Controllers/WeatherMapController.php @@ -0,0 +1,57 @@ + + * @author Barry O'Donovan + * @category IXP + * @package IXP\Http\Controllers + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class WeatherMapController extends Controller +{ + /** + * Display the weather map + * + * @param int $id ID of the weather map + * + * @return view + */ + public function index( int $id ): View + { + if( !is_numeric( $id ) || !isset( config( 'ixp_tools.weathermap' )[ $id ] ) ) { + abort( 404,'Unknown weathermap requested'); + } + return view( 'weather-map/index' )->with([ + 'wm' => config( 'ixp_tools.weathermap' )[ $id ], + 'wms' => config( 'ixp_tools.weathermap' ), + ]); + } +} \ No newline at end of file diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php new file mode 100644 index 000000000..3a4660044 --- /dev/null +++ b/app/Http/Kernel.php @@ -0,0 +1,185 @@ + [ + Middleware\EncryptCookies::class, + AddQueuedCookiesToResponse::class, + StartSession::class, + ShareErrorsFromSession::class, + Middleware\VerifyCsrfToken::class, + SubstituteBindings::class, + Middleware\ControllerEnabled::class, + ], + + 'apibase' => [ + Middleware\EncryptCookies::class, + AddQueuedCookiesToResponse::class, + StartSession::class, + ShareErrorsFromSession::class, + SubstituteBindings::class, + Middleware\ControllerEnabled::class, + //'throttle:60,1', + //'bindings', + Middleware\ControllerEnabled::class, + ], + + 'public/api/v4' => [ + 'apibase', + 'apimaybeauth', + ], + + 'api/v4' => [ + 'apibase', + 'apiauth', + ], + + 'e2frontend' => [ + 'web', + 'eloquent2Frontend', + ], + + 'grapher' => [ + Middleware\EncryptCookies::class, + AddQueuedCookiesToResponse::class, + StartSession::class, + Middleware\ControllerEnabled::class, + 'apimaybeauth', + Middleware\Services\Grapher::class, + ], + + 'lookingglass' => [ + 'web', + Middleware\ControllerEnabled::class, + Middleware\Services\LookingGlass::class, + ], + + // Middleware group for simple APIs where we do not want to create cookies. + 'publicapi' => [ + SubstituteBindings::class, + Middleware\ControllerEnabled::class, + ], + ]; + + /** + * The application's route middleware. + * + * @var array + */ + protected $routeMiddleware = [ + 'auth' => Middleware\Authenticate::class, + 'auth.basic' => AuthenticateWithBasicAuth::class, + 'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class, + //'bindings' => SubstituteBindings::class, + 'can' => Authorize::class, + 'cache.headers' => SetCacheHeaders::class, + 'guest' => Middleware\RedirectIfAuthenticated::class, + 'signed' => Middleware\ValidateSignature::class, + 'throttle' => ThrottleRequests::class, + 'verified' => EnsureEmailIsVerified::class, + 'apiauth' => Middleware\ApiAuthenticate::class, + 'apimaybeauth' => Middleware\ApiMaybeAuthenticate::class, + 'assert.privilege' => Middleware\AssertUserPrivilege::class, + 'controller-enabled' => Middleware\ControllerEnabled::class, + 'eloquent2Frontend' => Middleware\Eloquent2Frontend::class, + 'grapher' => Middleware\Services\Grapher::class, + 'rs-prefixes' => Middleware\RsPrefixes::class, + '2fa' => Middleware\Google2FA::class, + ]; + + /** + * The priority-sorted list of middleware. + * + * This forces non-global middleware to always be in the given order. + * + * @var array + */ + protected $middlewarePriority = [ + StartSession::class, + ShareErrorsFromSession::class, + Authenticate::class, + ThrottleRequests::class, + AuthenticateSession::class, + SubstituteBindings::class, + Authorize::class, + ]; +} \ No newline at end of file diff --git a/app/Http/Middleware/ApiAuthenticate.php b/app/Http/Middleware/ApiAuthenticate.php new file mode 100644 index 000000000..e7a1d38e7 --- /dev/null +++ b/app/Http/Middleware/ApiAuthenticate.php @@ -0,0 +1,116 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Middleware + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class ApiAuthenticate +{ + /** + * Authenticate protected APIv4 calls + * + * API key can be passed in the header (preferred) or on the URL. + * + * curl -X GET -H "X-IXP-Manager-API-Key: mySuperSecretApiKey" http://ixpv.dev/api/v4/test + * wget http://ixpv.dev/api/v4/test?apikey=mySuperSecretApiKey + * + * @param Request $r + * @param Closure $next + * + * @return mixed + * + * @throws + */ + public function handle( Request $r, Closure $next ) + { + // are we already logged in? + if( !Auth::check() ) { + // find API key. Prefer header to URL: + $apikey = false; + if( $r->header('X-IXP-Manager-API-Key') ) { + $apikey = $r->header('X-IXP-Manager-API-Key'); + } else if( $r->apikey ) { + $apikey = $r->apikey; + } + + if( !$apikey ) { + return response('Unauthorized.', 401); + } + + if( !( $key = ApiKey::where( 'apiKey', $apikey )->with( 'user.customer' )->first() ) ) { + return response( 'Valid API key required', 403 ); + } + + if( $key->expires && now() > $key->expires ) { + return response( 'API key expired', 403 ); + } + + // Check if user is disabled + if( $key->user->disabled ){ + return response( 'User is disabled', 403 ); + } + + // Check if default customer is disabled + if( $key->user->customer()->active()->notDeleted()->doesntExist() ){ + return response( ucfirst( config( 'ixp_fe.lang.customer.one' ) ) . ' of the user is disabled', 403 ); + } + + + Auth::onceUsingId( $key->user_id ); + + $key->update( [ + 'lastseenAt' => now(), + 'lastseenFrom' => ixp_get_client_ip(), + ] ); + } else { + /** @var User $us */ + $us = Auth::user(); + + if( $us->disabled ){ + return response( 'User is disabled', 403 ); + } elseif( $us->customer()->active()->notDeleted()->doesntExist() ){// Check if default customer is disabled + return response( ucfirst( config( 'ixp_fe.lang.customer.one' ) ) . ' of the user is disabled', 403 ); + } + } + + return $next( $r ); + } +} \ No newline at end of file diff --git a/app/Http/Middleware/ApiMaybeAuthenticate.php b/app/Http/Middleware/ApiMaybeAuthenticate.php new file mode 100644 index 000000000..c3d421af9 --- /dev/null +++ b/app/Http/Middleware/ApiMaybeAuthenticate.php @@ -0,0 +1,111 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Middleware + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class ApiMaybeAuthenticate +{ + /** + * Authenticate if credentials present and valid + * + * API key can be passed in the header (preferred) or on the URL. + * + * curl -X GET -H "X-IXP-Manager-API-Key: mySuperSecretApiKey" http://ixpv.dev/api/v4/test + * wget http://ixpv.dev/api/v4/test?apikey=mySuperSecretApiKey + * + * @param Request $r + * @param Closure $next + * + * @return mixed + * + * @throws + */ + public function handle( Request $r, Closure $next ) + { + /** @var User $us */ + $us = Auth::user(); + + // are we already logged in? + if( !Auth::check() ) { + // find API key. Prefer header to URL: + $apikey = false; + if( $r->header('X-IXP-Manager-API-Key') ) { + $apikey = $r->header('X-IXP-Manager-API-Key'); + } else if( $r->apikey ) { + $apikey = $r->apikey; + } + + if( $apikey ) { + if( !( $key = ApiKey::where( 'apiKey', $apikey )->with( 'user.customer' )->first() ) ) { + return response( 'Valid API key required', 403 ); + } + + if( $key->expires && now() > $key->expires ) { + return response( 'API key expired', 403 ); + } + + // Check if user is disabled + if( $key->user->disabled ){ + return response( 'User is disabled', 403 ); + } + + // Check if default customer is disabled + if( $key->user->customer()->active()->notDeleted()->doesntExist() ){ + return response( ucfirst( config( 'ixp_fe.lang.customer.one' ) ) . ' of the user is disabled', 403 ); + } + + Auth::onceUsingId( $key->user_id ); + $us = Auth::user(); + + $key->update( [ + 'lastseenAt' => now(), + 'lastseenFrom' => ixp_get_client_ip(), + ] ); + } + } elseif( $us->disabled ){ + return response( 'User is disabled', 403 ); + } + + return $next( $r ); + } +} \ No newline at end of file diff --git a/app/Http/Middleware/AssertUserPrivilege.php b/app/Http/Middleware/AssertUserPrivilege.php new file mode 100644 index 000000000..9312fa248 --- /dev/null +++ b/app/Http/Middleware/AssertUserPrivilege.php @@ -0,0 +1,64 @@ + + * @category IXP + * @package IXP\Http\Middleware + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class AssertUserPrivilege +{ + /** + * Handle an incoming request. + * + * @param Request $r + * @param Closure $next + * @param int $privilege + * + * @return mixed + */ + public function handle( Request $r, Closure $next, int $privilege ) + { + /** @var User $us */ + $us = Auth::getUser(); + + if( $us->privs() !== $privilege ) { + return response( 'Insufficient permissions', 403 ); + } + + return $next( $r ); + } +} \ No newline at end of file diff --git a/app/Http/Middleware/Authenticate.php b/app/Http/Middleware/Authenticate.php new file mode 100644 index 000000000..28af6e1b7 --- /dev/null +++ b/app/Http/Middleware/Authenticate.php @@ -0,0 +1,108 @@ + + * @author Yannr Robin + * @category IXP + * @package IXP\Http\Middleware + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Authenticate +{ + /** + * The Guard implementation. + * + * @var Guard + */ + protected $auth; + + /** + * Create a new filter instance. + * + * @param Guard $auth + * + * @return void + */ + public function __construct( Guard $auth ) + { + $this->auth = $auth; + } + + /** + * Handle an incoming request. + * + * @param Request $r + * @param Closure $next + * + * @return mixed + */ + public function handle( Request $r, Closure $next ) + { + if( $this->auth->guest() ) { + if( $r->ajax() ) { + return response('Unauthorized.', 401); + } + return redirect()->guest(route( "login@showForm" ) ); + } + + /** @var User $us */ + $us = Auth::user(); + + // Check if use has at least one customer linked, if not logout + if( !$us->custid || !CustomerToUser::where( [ 'user_id' => Auth::id() ] )->where( [ 'customer_id' => $us->custid ] )->first() ){ + Auth::logout(); + return redirect()->guest( route( "login@showForm" ) ); + } + + // Check if user is disabled + if( $us->disabled ){ + AlertContainer::push( 'You account is disabled.', Alert::DANGER ); + Auth::logout(); + return redirect()->guest( route( "login@showForm" ) ); + } + + // Check if default customer is disabled + if( $us->customer()->active()->notDeleted()->doesntExist() ){ + Auth::logout(); + return redirect()->guest( route( "login@showForm" ) ); + } + + return $next( $r ); + } +} \ No newline at end of file diff --git a/app/Http/Middleware/ControllerEnabled.php b/app/Http/Middleware/ControllerEnabled.php new file mode 100644 index 000000000..a48620ab0 --- /dev/null +++ b/app/Http/Middleware/ControllerEnabled.php @@ -0,0 +1,105 @@ + + * @author Yannr Robin + * @category IXP + * @package IXP\Http\Middleware + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class ControllerEnabled +{ + /** + * Handle an incoming request. + * + * @param Request $r + * @param Closure $next + * @return mixed + */ + public function handle( Request $r, Closure $next ) + { + // not everything belongs here: + if( !strpos( Route::currentRouteAction(), '@' ) ) { + return $next( $r ); + } + + // get the class and method that has been called: + [ $controller, $method ] = explode('@', Route::currentRouteAction() ); + + // reformat controller name to exclude 'IXP\Http\Controllers' and then replace remaining '\' with - + if( strpos( $controller, 'IXP\\Http\\Controllers' ) === 0 ) { + $controller = substr( $controller, 21 ); + } else if( strpos( $controller, '\\IXP\\Http\\Controllers' ) === 0 ) { + $controller = substr( $controller, 22 ); + } + + if( substr( $controller, -10 ) === 'Controller' ) { + $controller = substr( $controller, 0, -10 ); + } + + $bits = explode( '\\', $controller ); + + $name = ''; + foreach( $bits as $b ) { + if( $b === '' ) { + continue; + } + + $name .= Str::kebab( strtolower( $b ) ) . '-'; + } + $name = substr( $name, 0, -1 ); + + $enabled = true; + + // is the controller enabled? + if( config( 'ixp_fe.frontend.disabled.' . $name, false ) ) { + $enabled = false; + } else if( str_starts_with( $controller, 'ConsoleServer' ) && config( 'ixp_fe.frontend.disabled.console-server-connection', false ) ) { + dump('here'); + $enabled = false; + } + + if( !$enabled ) { + AlertContainer::push( "This controller has been disabled (see: config/ixp_fe.php).", Alert::DANGER ); + return redirect( '' ); + } + + return $next( $r ); + } +} \ No newline at end of file diff --git a/app/Http/Middleware/Eloquent2Frontend.php b/app/Http/Middleware/Eloquent2Frontend.php new file mode 100644 index 000000000..24993f898 --- /dev/null +++ b/app/Http/Middleware/Eloquent2Frontend.php @@ -0,0 +1,79 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Middleware + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Eloquent2Frontend +{ + /** + * Handle an incoming request. + * + * @param Request $r + * @param Closure $next + * + * @return mixed + */ + public function handle( Request $r, Closure $next ) + { + /** @var User $us */ + $us = Auth::user(); + + // get the class and method that has been called: + [ $controller, $method ] = explode('@', Route::currentRouteAction() ); + + // what's the user's privilege? + $user_priv = Auth::check() ? $us->privs() : User::AUTH_PUBLIC; + + // first check - do we have the necessary privileges to access this? + /** @psalm-suppress InvalidPropertyFetch */ + if( $user_priv < $controller::$minimum_privilege ) { + AlertContainer::push( "You do not have the required privileges to access this function.", Alert::DANGER ); + Log::info( ( Auth::check() ? $us->username : 'Anonymous user' ) . " tried to access {$controller}@{$method} but does not have the required privileges" ); + return redirect( '' ); + } + + return $next( $r ); + } +} \ No newline at end of file diff --git a/app/Http/Middleware/EncryptCookies.php b/app/Http/Middleware/EncryptCookies.php new file mode 100644 index 000000000..71260a2c1 --- /dev/null +++ b/app/Http/Middleware/EncryptCookies.php @@ -0,0 +1,46 @@ + + * @author Yannr Robin + * @category IXP + * @package IXP\Http\Middleware + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class EncryptCookies extends Middleware +{ + /** + * The names of the cookies that should not be encrypted. + * + * @var array + */ + protected $except = []; +} \ No newline at end of file diff --git a/app/Http/Middleware/Google2FA.php b/app/Http/Middleware/Google2FA.php new file mode 100644 index 000000000..51ce0d737 --- /dev/null +++ b/app/Http/Middleware/Google2FA.php @@ -0,0 +1,84 @@ +route()->getName(), $this->excludes, true ) ) { + return $next( $r ); + } + + // Force the user to enable 2FA? + if( $r->user()->is2faEnforced() ) { + return redirect( route( '2fa@configure' ) ); + } + + $authenticator = new GoogleAuthenticator( $r ); + + if( !Auth::getUser()->user2FA || !Auth::getUser()->user2FA->enabled || $authenticator->isAuthenticated() ) { + return $next( $r ); + } + + return $authenticator->makeRequestOneTimePasswordResponse(); + } +} \ No newline at end of file diff --git a/app/Http/Middleware/PreventRequestsDuringMaintenance.php b/app/Http/Middleware/PreventRequestsDuringMaintenance.php new file mode 100644 index 000000000..65d01e27c --- /dev/null +++ b/app/Http/Middleware/PreventRequestsDuringMaintenance.php @@ -0,0 +1,48 @@ + + * @author Yannr Robin + * @category IXP + * @package IXP\Http\Middleware + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class PreventRequestsDuringMaintenance extends Middleware +{ + /** + * The URIs that should be reachable while maintenance mode is enabled. + * + * @var array + */ + protected $except = [ + // + ]; +} \ No newline at end of file diff --git a/app/Http/Middleware/RedirectIfAuthenticated.php b/app/Http/Middleware/RedirectIfAuthenticated.php new file mode 100644 index 000000000..cc3e9dc45 --- /dev/null +++ b/app/Http/Middleware/RedirectIfAuthenticated.php @@ -0,0 +1,72 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Middleware + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class RedirectIfAuthenticated +{ + /** + * The Guard implementation. + * + * @var Guard + */ + protected $auth; + + /** + * Handle an incoming request. + * + * @param Request $request + * @param Closure $next + * @param string|null ...$guards + * @return mixed + */ + public function handle(Request $request, Closure $next, ...$guards) + { + $guards = empty($guards) ? [null] : $guards; + + foreach ( $guards as $guard ) { + if ( Auth::guard( $guard )->check() ) { + return redirect(RouteServiceProvider::HOME ); + } + } + + return $next($request); + } +} \ No newline at end of file diff --git a/app/Http/Middleware/RsPrefixes.php b/app/Http/Middleware/RsPrefixes.php new file mode 100644 index 000000000..f826e63c0 --- /dev/null +++ b/app/Http/Middleware/RsPrefixes.php @@ -0,0 +1,99 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Middleware + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class RsPrefixes +{ + /** + * Handle an incoming request. + * + * @param Request $r + * @param Closure $next + * + * @return mixed + * + * @throws + */ + public function handle( Request $r, Closure $next ) + { + /** @var User $us */ + $us = Auth::getUser(); + + // there are only two routes for rs prefixes - authorise each one as follows: + if( $r->is( 'rs-prefixes/list' ) ) { + if( (int)config( 'ixp_fe.rs-prefixes.access' ) === User::AUTH_PUBLIC ) { + return $next( $r ); + } + + if( Auth::guest() || config( 'ixp_fe.rs-prefixes.access' ) > $us->privs() ) { + AlertContainer::push( "You do not have the required privileges to access this function.", Alert::DANGER ); + return redirect( '' ); + } + } else if( $r->is( 'rs-prefixes/view/*' ) ) { + if( (int)config( 'ixp_fe.rs-prefixes.access' ) === User::AUTH_PUBLIC ) { + return $next( $r ); + } + + if( Auth::check() ) { + if( (int)config( 'ixp_fe.rs-prefixes.access' ) <= $us->privs() ) { + return $next( $r ); + } + + if( $us->custid === $r->cust->id ) { + return $next( $r ); + } + } + + AlertContainer::push( "You do not have the required privileges to access this function.", Alert::DANGER ); + return redirect( '' ); + } else { + throw new GeneralException( 'Unknown route server prefix route in middleware' ); + } + + return $next( $r ); + } +} \ No newline at end of file diff --git a/app/Http/Middleware/Services/Grapher.php b/app/Http/Middleware/Services/Grapher.php new file mode 100644 index 000000000..f2147aeeb --- /dev/null +++ b/app/Http/Middleware/Services/Grapher.php @@ -0,0 +1,186 @@ + + * @author Yann Robon + * @category Grapher + * @package IXP\Http\Middleware\Grapher + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Grapher +{ + /** + * Handle an incoming request. + * + * @param Request $r + * @param Closure $next + * + * @return mixed + */ + public function handle( Request $r, Closure $next ) + { + // get the grapher service + $grapher = App::make( GrapherService::class ); + + // all graph requests require a certain basic set of parameters / defaults. + // let's take care of that here + $graph = $this->processParameters( $r, $grapher ); + + // so we know what graph we need and who's looking for it + // let's authorise for access (this throws an exception) + $graph->authorise(); + + // For PHP unit tests, we're currently just testing for authorisation. + // The $request->attributes->add(['graph' => $graph]); doesn't work in the tests + // for an unknown reason so just abort here if in test mode: + if( env( 'IXP_PHPUNIT_RUNNING', false ) ) { + abort( 200 ); + } + + $r->attributes->add( ['graph' => $graph] ); + + return $next( $r ); + } + + /** + * All graphs have common parameters. We process these here for every request - and set sensible defaults. + * + * @param Request $request + * @param GrapherService $grapher + * + * @return Graph + */ + private function processParameters( Request $request, GrapherService $grapher ): Graph + { + // while the Grapher service stores the processed parameters in its own object, we update the $request + // parameters here also just in case we need to final versions later in the request. + + $target = explode( '/', $request->path() ); + $target = array_pop( $target ); + + switch( $target ) { + case 'ixp': + $graph = $grapher->ixp(); + break; + + case 'infrastructure': + $infra = InfrastructureGraph::processParameterInfrastructure( (int)$request->input( 'id', 0 ) ); + $graph = $grapher->infrastructure( $infra ); + break; + + case 'vlan': + $vlan = VlanGraph::processParameterVlan( (int)$request->input( 'id', 0 ) ); + $graph = $grapher->vlan( $vlan ); + break; + + case 'trunk': + $trunkname = TrunkGraph::processParameterTrunkname( (string)$request->input( 'id', '' ) ); + $graph = $grapher->trunk( $trunkname ); + break; + + case 'corebundle': + $corebundle = CoreBundleGraph::processParameterCoreBundle( (int)$request->input( 'id', 0 ) ); + $side = CoreBundleGraph::processParameterSide( $request->input( 'side', 'a' ) ); + $graph = $grapher->coreBundle( $corebundle, $side ); + break; + + case 'location': + $location = LocationGraph::processParameterLocation( (int)$request->input( 'id', 0 ) ); + $graph = $grapher->location( $location ); + break; + + case 'switch': + $switch = SwitchGraph::processParameterSwitch( (int)$request->input( 'id', 0 ) ); + $graph = $grapher->switch( $switch ); + break; + + case 'physicalinterface': + $physint = PhysIntGraph::processParameterPhysicalInterface( (int)$request->input( 'id', 0 ) ); + $graph = $grapher->physint( $physint ); + break; + + case 'virtualinterface': + $virtint = VirtIntGraph::processParameterVirtualInterface( (int)$request->input( 'id', 0 ) ); + $graph = $grapher->virtint( $virtint ); + break; + + case 'customer': + $customer = CustomerGraph::processParameterCustomer( (int)$request->input( 'id', 0 ) ); + $graph = $grapher->customer( $customer ); + break; + + case 'vlaninterface': + $vlanint = VlanIntGraph::processParameterVlanInterface( (int)$request->input( 'id', 0 ) ); + $graph = $grapher->vlanint( $vlanint ); + break; + + case 'latency': + $vli = LatencyGraph::processParameterVlanInterface( (int)$request->input( 'id', 0 ) ); + $graph = $grapher->latency( $vli ); + break; + + case 'p2p': + $srcvlanint = P2pGraph::processParameterSourceVlanInterface( (int)$request->input( 'svli', 0 ) ); + $dstvlanint = P2pGraph::processParameterDestinationVlanInterface( (int)$request->input( 'dvli', 0 ) ); + $graph = $grapher->p2p( $srcvlanint, $dstvlanint ); + break; + + default: + abort(404, 'No such graph type'); + } + + $graph->setPeriod( $graph->processParameterPeriod( $request->period ) ); + $graph->setCategory( $graph->processParameterCategory( $request->category ) ); + $graph->setProtocol( $graph->processParameterProtocol( $request->protocol ) ); + $graph->setType( $graph->processParameterType( $request->type ) ); + + /** @var Graph $graph */ + return $graph; + } +} \ No newline at end of file diff --git a/app/Http/Middleware/Services/LookingGlass.php b/app/Http/Middleware/Services/LookingGlass.php new file mode 100644 index 000000000..a37a45a29 --- /dev/null +++ b/app/Http/Middleware/Services/LookingGlass.php @@ -0,0 +1,159 @@ + + * @author Yann Robin + * @category LookingGlass + * @package IXP\Services\Middleware\LookingGlass + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class LookingGlass +{ + /** + * Check if the symbols is valid + * + * @param string $symbol + * + * @return bool + */ + private function validateSymbol( string $symbol ): bool + { + return $symbol !== '' && preg_match( '/^[a-zA-Z_]?[a-zA-Z0-9_]*$/', $symbol ); + } + + /** + * Check if the prefix is valid + * + * @param Request $r + * + * @return bool + */ + private function validateNetworkRoute( Request $r ): bool + { + $validator = Validator::make( + [ + 'net' => $r->net, + 'mask' => $r->mask + ], [ + 'net' => 'required|ip', + 'mask' => 'numeric|min:1|max:128', + ] + ); + + return !$validator->fails(); + } + + /** + * Handle an incoming request. + * + * @param Request $r + * @param Closure $next + * + * @return mixed + * + * @throws + */ + public function handle( Request $r, Closure $next ) + { + if( Route::currentRouteName() === 'lg::index' ) { + return $next( $r ); + } + + // get the router object + try { + $router = Router::whereHandle( $r->handle )->first(); + + if( !$router || !$router->api() ) { + AlertContainer::push( "No router with the provided handle was found", Alert::DANGER ); + return redirect( route( 'lg::index' ) ); + } + } catch( RouterException $e ) { + abort( 404, $e->getMessage() ); + } + + if( ( $r->table && !$this->validateSymbol( $r->table ) ) + || ( $r->protocol && !$this->validateSymbol( $r->protocol ) ) ) { + AlertContainer::push( "Symbol (protocol / table) invalid or not found", Alert::DANGER ); + return redirect( route( 'lg::bgp-sum', [ 'handle' => $r->handle ] ) ); + } + + if( ( $r->net || $r->mask ) && !$this->validateNetworkRoute( $r ) ) { + abort(404); + } + + // let's authorise for access (this throws an exception) + if( !$this->authorise( $router ) ) { + AlertContainer::push( "Insufficient permissions to access this looking glass", Alert::DANGER ); + return redirect( route( 'lg::index' ) ); + } + + // get the appropriate looking glass service + // (throws an exception if no appropriate Looking Glass handler) + $lg = App::make( LookingGlassService::class )->forRouter( $router ); + + $r->attributes->add( [ 'lg' => $lg ] ); + + return $next( $r ); + } + + /** + * This function controls access to a router for a looking glass + * + * @param Router $router + * + * @return bool + */ + private function authorise( Router $router ): bool + { + /** @var User $us */ + $us = Auth::user(); + + if( $router->authorise( Auth::check() ? $us->privs() : User::AUTH_PUBLIC ) ) { + return true; + } + return false; + } +} \ No newline at end of file diff --git a/app/Http/Middleware/TrimStrings.php b/app/Http/Middleware/TrimStrings.php new file mode 100644 index 000000000..258f95de7 --- /dev/null +++ b/app/Http/Middleware/TrimStrings.php @@ -0,0 +1,49 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Middleware + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class TrimStrings extends Middleware +{ + /** + * The names of the attributes that should not be trimmed. + * + * @var array + */ + protected $except = [ + 'password', + 'password_confirmation', + ]; +} \ No newline at end of file diff --git a/app/Http/Middleware/TrustHosts.php b/app/Http/Middleware/TrustHosts.php new file mode 100644 index 000000000..19761c1de --- /dev/null +++ b/app/Http/Middleware/TrustHosts.php @@ -0,0 +1,54 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Middleware + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class TrustHosts extends Middleware +{ + /** + * Get the host patterns that should be trusted. + * + * @return (null|string)[] + * + * @psalm-return list{null|string} + */ + #[\Override] + public function hosts() + { + return [ + $this->allSubdomainsOfApplicationUrl(), + ]; + } +} \ No newline at end of file diff --git a/app/Http/Middleware/TrustProxies.php b/app/Http/Middleware/TrustProxies.php new file mode 100644 index 000000000..3c67bd126 --- /dev/null +++ b/app/Http/Middleware/TrustProxies.php @@ -0,0 +1,59 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Middleware + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class TrustProxies extends Middleware +{ + /** + * The trusted proxies for this application. + * + * @var array|string|null + */ + protected $proxies; + + /** + * The current proxy header mappings. + * + * @var int + */ + protected $headers = Request::HEADER_X_FORWARDED_FOR | + Request::HEADER_X_FORWARDED_HOST | + Request::HEADER_X_FORWARDED_PORT | + Request::HEADER_X_FORWARDED_PROTO | + Request::HEADER_X_FORWARDED_AWS_ELB; +} \ No newline at end of file diff --git a/app/Http/Middleware/ValidateSignature.php b/app/Http/Middleware/ValidateSignature.php new file mode 100644 index 000000000..2de0667ea --- /dev/null +++ b/app/Http/Middleware/ValidateSignature.php @@ -0,0 +1,22 @@ + + */ + protected $except = [ + // 'fbclid', + // 'utm_campaign', + // 'utm_content', + // 'utm_medium', + // 'utm_source', + // 'utm_term', + ]; +} diff --git a/app/Http/Middleware/VerifyCsrfToken.php b/app/Http/Middleware/VerifyCsrfToken.php new file mode 100644 index 000000000..daaee428a --- /dev/null +++ b/app/Http/Middleware/VerifyCsrfToken.php @@ -0,0 +1,48 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Middleware + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class VerifyCsrfToken extends Middleware +{ + /** + * The URIs that should be excluded from CSRF verification. + * + * @var array + */ + protected $except = [ + 'login', + ]; +} \ No newline at end of file diff --git a/app/Http/Requests/Auth/ForgotUsername.php b/app/Http/Requests/Auth/ForgotUsername.php new file mode 100644 index 000000000..c3f4bdfb3 --- /dev/null +++ b/app/Http/Requests/Auth/ForgotUsername.php @@ -0,0 +1,63 @@ + + * @author Barry O'Donovan + * @category IXP + * @package IXP\Http\Requests\Auth + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class ForgotUsername extends FormRequest +{ + /** + * Determine if the user is authorized to make this request. + * + * @return true + */ + public function authorize(): bool + { + return true; + } + + /** + * Get the validation rules that apply to the request. + * + * @return string[] + * + * @psalm-return array{email: 'required|email'} + */ + public function rules(): array + { + return [ + 'email' => 'required|email' + ]; + } +} \ No newline at end of file diff --git a/app/Http/Requests/CoreBundle/Store.php b/app/Http/Requests/CoreBundle/Store.php new file mode 100644 index 000000000..1ba356d36 --- /dev/null +++ b/app/Http/Requests/CoreBundle/Store.php @@ -0,0 +1,125 @@ + + * @author Barry O'Donovan + * @category IXP + * @package IXP\Http\Requests\CoreBundle + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Store extends FormRequest +{ + /** + * Determine if the user is authorized to make this request. + * + * @return bool + */ + public function authorize(): bool + { + /** @var User $us */ + $us = Auth::getUser(); + // middleware ensures superuser access only so always authorised here: + return $us->isSuperUser(); + } + + /** + * Get the validation rules that apply to the request. + * + * @return string[] + * + * @psalm-return array{custid: 'required|integer|exists:cust,id', description: 'required|string|max:255', graph_title: 'required|string|max:255', cost: 'nullable|integer', preference: 'nullable|integer', type: string, ipv4_subnet: 'nullable'|'required', mtu?: 'required|integer|min:0|max:64000', 'vi-name-a'?: 'nullable'|'required|string|max:255', 'vi-name-b'?: 'nullable'|'required|string|max:255', 'vi-channel-number-a'?: 'nullable'|'required|integer|min:0', 'vi-channel-number-b'?: 'nullable'|'required|integer|min:0'} + */ + public function rules(): array + { + $arrayCb = [ + 'custid' => 'required|integer|exists:cust,id', + 'description' => 'required|string|max:255', + 'graph_title' => 'required|string|max:255', + 'cost' => 'nullable|integer', + 'preference' => 'nullable|integer', + 'type' => 'required|integer|in:' . implode( ',', array_keys( CoreBundle::$TYPES ) ), + 'ipv4_subnet' => $this->type === CoreBundle::TYPE_L3_LAG ? "required" : "nullable", + ]; + + $rules = ( $this->type === CoreBundle::TYPE_L2_LAG || $this->type === CoreBundle::TYPE_L3_LAG) ? 'required|string|max:255' : 'nullable'; + $rules2 = ( $this->type === CoreBundle::TYPE_L2_LAG || $this->type === CoreBundle::TYPE_L3_LAG) ? 'required|integer|min:0' : 'nullable'; + + $arrayVi = [ + 'mtu' => 'required|integer|min:0|max:64000', + 'vi-name-a' => $rules, + 'vi-name-b' => $rules, + 'vi-channel-number-a' => $rules2, + 'vi-channel-number-b' => $rules2, + ]; + + return $this->cb ? $arrayCb : array_merge( $arrayCb, $arrayVi ); + } + + /** + * @param Validator $validator + * + * @return true + */ + public function withValidator( Validator $validator ): bool + { + if( !$validator->fails() && !$this->input( 'cb' ) ) { + $validator->after( function( Validator $validator ) { + if( count( $this->input( 'cl-details' ) ) === 0 ){ + AlertContainer::push( 'You need at add least one Core link' , Alert::DANGER ); + $validator->errors()->add( "", "" ); + return false; + } + + foreach( $this->input( 'cl-details' ) as $index => $detail ) { + foreach( [ "a", "b" ] as $side ){ + if( !SwitchPort::find( $detail[ "hidden-sp-" . $side ] ) ) { + AlertContainer::push( 'Please select the switch port side ' . ucfirst( $side ) . " for the core link number " . $index , Alert::DANGER ); + $validator->errors()->add( "switch-port", "" ); + return false; + } + } + } + }); + } + return true; + } +} \ No newline at end of file diff --git a/app/Http/Requests/CoreBundle/StoreCoreLink.php b/app/Http/Requests/CoreBundle/StoreCoreLink.php new file mode 100644 index 000000000..602c9db6c --- /dev/null +++ b/app/Http/Requests/CoreBundle/StoreCoreLink.php @@ -0,0 +1,74 @@ + + * @author Barry O'Donovan + * @category IXP + * @package IXP\Http\Requests\CoreBundle + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class StoreCoreLink extends FormRequest +{ + /** + * Determine if the user is authorized to make this request. + * + * @return bool + */ + public function authorize(): bool + { + /** @var User $us */ + $us = Auth::getUser(); + // middleware ensures superuser access only so always authorised here: + return $us->isSuperUser(); + } + + /** + * Get the validation rules that apply to the request. + * + * @return string[] + * + * @psalm-return array + */ + public function rules(): array + { + $arrayCl = []; + for( $i = 1; $i <= $this->input( 'nb-core-links' ); $i++ ){ + $arrayCl = [ + "sp-a-{$i}" => 'required|integer|exists:switchport,id', + "sp-b-{$i}" => 'required|integer|exists:switchport,id', + ]; + } + return $arrayCl; + } +} \ No newline at end of file diff --git a/app/Http/Requests/Customer/BillingInformation.php b/app/Http/Requests/Customer/BillingInformation.php new file mode 100644 index 000000000..0d645b689 --- /dev/null +++ b/app/Http/Requests/Customer/BillingInformation.php @@ -0,0 +1,98 @@ + + * @author Barry O'Donovan + * @category IXP + * @package IXP\Http\Requests\Customer + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class BillingInformation extends FormRequest +{ + /** + * Determine if the user is authorized to make this request. + * + * @return bool + */ + public function authorize(): bool + { + /** @var User $us */ + $us = Auth::getUser(); + return $us->isSuperUser(); + } + + /** + * Get the validation rules that apply to the request. + * + * @return string[] + * + * @psalm-return array{registeredName: 'nullable|string|max:255', companyNumber: 'nullable|string|max:255', jurisdiction: 'nullable|string|max:255', address1: 'nullable|string|max:255', address2: 'nullable|string|max:255', address3: 'nullable|string|max:255', townCity: 'nullable|string|max:255', postcode: 'nullable|string|max:255', country: string, notes: 'nullable|string', billingContactName: 'nullable|string|max:255', billingFrequency: string, billingAddress1: 'nullable|string|max:255', billingAddress2: 'nullable|string|max:255', billingAddress3: 'nullable|string|max:255', billingTownCity: 'nullable|string|max:255', billingPostcode: 'nullable|string|max:255', billingCountry: string, billingEmail: 'nullable|email|max:255', billingTelephone: 'nullable|string|max:255', invoiceMethod: string, invoiceEmail: 'nullable|string|max:255', vatRate: 'nullable|string|max:255', vatNumber: 'nullable|string|max:255', purchaseOrderNumber: 'nullable|string|max:50', billingNotes: 'nullable|string'} + */ + public function rules(): array + { + return [ + 'registeredName' => 'nullable|string|max:255', + 'companyNumber' => 'nullable|string|max:255', + 'jurisdiction' => 'nullable|string|max:255', + 'address1' => 'nullable|string|max:255', + 'address2' => 'nullable|string|max:255', + 'address3' => 'nullable|string|max:255', + 'townCity' => 'nullable|string|max:255', + 'postcode' => 'nullable|string|max:255', + 'country' => 'nullable|string|max:255|in:' . implode( ',', array_values( Countries::getListForSelect( 'iso_3166_2' ) ) ), + 'notes' => 'nullable|string', + + 'billingContactName' => 'nullable|string|max:255', + 'billingFrequency' => 'nullable|string|max:255|in:' . implode( ',', array_keys( CompanyBillingDetail::$BILLING_FREQUENCIES ) ), + 'billingAddress1' => 'nullable|string|max:255', + 'billingAddress2' => 'nullable|string|max:255', + 'billingAddress3' => 'nullable|string|max:255', + 'billingTownCity' => 'nullable|string|max:255', + 'billingPostcode' => 'nullable|string|max:255', + 'billingCountry' => 'nullable|string|max:255|in:' . implode( ',', array_values( Countries::getListForSelect( 'iso_3166_2' ) ) ), + 'billingEmail' => 'nullable|email|max:255', + 'billingTelephone' => 'nullable|string|max:255', + 'invoiceMethod' => 'nullable|string|max:255|in:' . implode( ',', array_keys( CompanyBillingDetail::$INVOICE_METHODS ) ), + 'invoiceEmail' => 'nullable|string|max:255', + 'vatRate' => 'nullable|string|max:255', + 'vatNumber' => 'nullable|string|max:255', + 'purchaseOrderNumber' => 'nullable|string|max:50', + 'billingNotes' => 'nullable|string', + ]; + } +} \ No newline at end of file diff --git a/app/Http/Requests/Customer/Logo.php b/app/Http/Requests/Customer/Logo.php new file mode 100644 index 000000000..d245547f3 --- /dev/null +++ b/app/Http/Requests/Customer/Logo.php @@ -0,0 +1,67 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Requests\Customer + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Logo extends FormRequest +{ + /** + * Determine if the user is authorized to make this request. + * + * @return bool + */ + public function authorize(): bool + { + // This is just belt and braces. Real authorization is handled via: + // 1. routes are defined in web-auth (or web-auth-superuser) + // 2. routes are only defined if !config( 'ixp_fe.frontend.disabled.logo' ) + return Auth::check() && !config( 'ixp_fe.frontend.disabled.logo' ); + } + + /** + * Get the validation rules that apply to the request. + * + * @return string[] + * + * @psalm-return array{logo: 'required|file'} + */ + public function rules(): array + { + return [ + 'logo' => 'required|file', + ]; + } +} \ No newline at end of file diff --git a/app/Http/Requests/Customer/Store.php b/app/Http/Requests/Customer/Store.php new file mode 100644 index 000000000..26166c966 --- /dev/null +++ b/app/Http/Requests/Customer/Store.php @@ -0,0 +1,168 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Requests\Customer + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Store extends FormRequest +{ + /** + * Determine if the user is authorized to make this request. + * + * @return bool + */ + public function authorize(): bool + { + /** @var User $us */ + $us = Auth::getUser(); + // middleware ensures superuser access only so always authorised here: + return $us->isSuperUser(); + } + + /** + * Get the validation rules that apply to the request. + * + * @return (string|string[])[] + * + * @psalm-return array{name: 'required|string|max:255', type: string, shortname: string, corpwww: 'nullable|url|max:255', datejoin: 'required|date', dateleft: 'nullable|date', status: string, md5support: string, abbreviatedName: 'required|string|max:30', autsys?: 'int|min:1', maxprefixes?: 'nullable|int|min:0', peeringemail?: 'email', peeringmacro?: list{'nullable', 'string', 'max:255', 'regex:/^(AS-[A-Z0-9]+(?:-[A-Z0-9]+)*|AS[1-9]\d*(?::AS-[A-Z0-9]+(?:-[A-Z0-9]+)*)?)$/i'}, peeringmacrov6?: list{'nullable', 'string', 'max:255', 'regex:/^(AS-[A-Z0-9]+(?:-[A-Z0-9]+)*|AS[1-9]\d*(?::AS-[A-Z0-9]+(?:-[A-Z0-9]+)*)?)$/i'}, peeringpolicy?: string, irrdb?: 'nullable|integer|exists:irrdbconfig,id', nocphone?: 'nullable|string|max:255', noc24hphone?: 'nullable|string|max:255', nocemail?: 'email|max:255', nochours?: string, nocwww?: 'nullable|url|max:255', reseller?: 'nullable|integer|exists:cust,id'} + */ + public function rules(): array + { + $validateCommonDetails = [ + 'name' => 'required|string|max:255', + 'type' => 'required|integer|in:' . implode( ',', array_keys( Customer::$CUST_TYPES_TEXT ) ), + 'shortname' => 'required|string|max:30|regex:/[a-z0-9]+/|unique:cust,shortname'. ( $this->cust ? ','. $this->cust->id : '' ), + 'corpwww' => 'nullable|url|max:255', + 'datejoin' => 'required|date', + 'dateleft' => 'nullable|date', + 'status' => 'required|integer|in:' . implode( ',', array_keys( Customer::$CUST_STATUS_TEXT ) ), + 'md5support' => 'nullable|string|in:' . implode( ',', array_keys( Customer::$MD5_SUPPORT ) ), + 'abbreviatedName' => 'required|string|max:30', + ]; + + $validateOtherDetails = [ + 'autsys' => 'int|min:1', + 'maxprefixes' => 'nullable|int|min:0', + 'peeringemail' => 'email', + 'peeringmacro' => [ 'nullable', 'string', 'max:255', 'regex:/^(AS-[A-Z0-9]+(?:-[A-Z0-9]+)*|AS[1-9]\d*(?::AS-[A-Z0-9]+(?:-[A-Z0-9]+)*)?)$/i' ], + 'peeringmacrov6' => [ 'nullable', 'string', 'max:255', 'regex:/^(AS-[A-Z0-9]+(?:-[A-Z0-9]+)*|AS[1-9]\d*(?::AS-[A-Z0-9]+(?:-[A-Z0-9]+)*)?)$/i' ], + 'peeringpolicy' => 'string|in:' . implode( ',', array_keys( Customer::$PEERING_POLICIES ) ), + 'irrdb' => 'nullable|integer|exists:irrdbconfig,id', + 'nocphone' => 'nullable|string|max:255', + 'noc24hphone' => 'nullable|string|max:255', + 'nocemail' => 'email|max:255', + 'nochours' => 'nullable|string|in:' . implode( ',', array_keys( Customer::$NOC_HOURS ) ), + 'nocwww' => 'nullable|url|max:255', + 'reseller' => 'nullable|integer|exists:cust,id', + ]; + + + return $this->type == Customer::TYPE_ASSOCIATE ? $validateCommonDetails : array_merge( $validateCommonDetails, $validateOtherDetails ) ; + } + + /** + * Configure the validator instance. + * + * @param Validator $validator + * + * @return void + */ + public function withValidator( Validator $validator ): void + { + $validator->after( function( $validator ) { + if( $this->type != Customer::TYPE_ASSOCIATE ) { + $this->checkReseller( $validator ); + } + }); + } + + /** + * Checks reseller status such that: + * + * - we cannot remove reseller state from a reseller with resold customers + * - we cannot reassign a resold customer to another reseller if they still have ports belonging to the current reseller + * - we cannot unset reseller status while a resold customer has ports belonging to a reseller + * + * @param Validator $validator + * + * @return bool If false, the form is not valid + */ + private function checkReseller( Validator $validator ): bool + { + /** @var Customer $c */ + $c = $this->cust; + + if( $this->isResold ) { + if( !$this->reseller || !( $reseller = Customer::find( $this->reseller ) ) ) { + $validator->errors()->add('reseller', 'Please choose a reseller'); + return false; + } + + // are we changing reseller? + if( $c && $c->resellerObject && $c->resellerObject->id !== $reseller->id ) { + foreach( $c->virtualInterfaces as $vi ) { + foreach( $vi->physicalInterfaces as $pi ) { + if( $pi->fanoutPhysicalInterface && $pi->fanoutPhysicalInterface->virtualInterface->custid === $c->reseller ) { + $validator->errors()->add('reseller', 'You cannot change the reseller because there are still fanout ports from the current reseller' + . 'linked to this customer\'s physical interfaces. You need to reassign these first.' ); + return false; + } + } + } + } + } else if( $c && $c->resellerObject ) { + foreach( $c->virtualInterfaces as $vi ) { + foreach( $vi->physicalInterfaces as $pi ) { + if( $pi->fanoutPhysicalInterface && $pi->fanoutPhysicalInterface->virtualInterface->custid === $c->reseller ) { + $validator->errors()->add('reseller', 'You can not change this resold customer state because there are still physical interface(s) of ' + . 'this customer linked to fanout ports of the current reseller. You need to reassign these first.' ); + return false; + } + } + } + } + + if( !$this->isReseller && $c && $c->isReseller && $c->resoldCustomers()->count() ) { + $validator->errors()->add('isReseller', 'You can not change the reseller state because this customer still has resold customers. ' + . 'You need to reassign these first.' ); + return false; + } + return true; + } +} \ No newline at end of file diff --git a/app/Http/Requests/Customer/WelcomeEmail.php b/app/Http/Requests/Customer/WelcomeEmail.php new file mode 100644 index 000000000..b4748f75e --- /dev/null +++ b/app/Http/Requests/Customer/WelcomeEmail.php @@ -0,0 +1,101 @@ + 'required|email', + ]; + + $addresses = explode( ',', $value ); + + foreach( $addresses as $address ) { + $data = [ + 'email' => trim($address) + ]; + $validator = Validator::make($data, $rules); + if ($validator->fails()) { + return false; + } + } + return true; + }); + } + + /** + * Determine if the user is authorized to make this request. + * + * @return true + */ + public function authorize(): bool + { + // middleware ensures superuser access only so always authorised here: + return true; + } + + /** + * Get the validation rules that apply to the request. + * + * @return string[] + * + * @psalm-return array{to: 'required|emails', cc: 'nullable|emails', bcc: 'nullable|emails', subject: 'required|string', message: 'required'} + */ + public function rules(): array + { + return [ + 'to' => 'required|emails', + 'cc' => 'nullable|emails', + 'bcc' => 'nullable|emails', + 'subject' => 'required|string', + 'message' => 'required', + ]; + } + + /** + * Get the error messages for the defined validation rules. + * + * @return string[] + * + * @psalm-return array{'to.emails': 'One or more of the email addresses are invalid', 'cc.emails': 'One or more of the email addresses are invalid', 'bcc.emails': 'One or more of the email addresses are invalid'} + */ + #[\Override] + public function messages() + { + return [ + 'to.emails' => 'One or more of the email addresses are invalid', + 'cc.emails' => 'One or more of the email addresses are invalid', + 'bcc.emails' => 'One or more of the email addresses are invalid', + ]; + } +} \ No newline at end of file diff --git a/app/Http/Requests/Dashboard/BillingDetailsRequest.php b/app/Http/Requests/Dashboard/BillingDetailsRequest.php new file mode 100644 index 000000000..aa74a41c8 --- /dev/null +++ b/app/Http/Requests/Dashboard/BillingDetailsRequest.php @@ -0,0 +1,78 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Requests\Dashboard + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class BillingDetailsRequest extends FormRequest +{ + /** + * Determine if the user is authorized to make this request. + * + * @return bool + */ + public function authorize(): bool + { + /** @var User $us */ + $us = Auth::getUser(); + // middleware ensures custadmin access only so always authorised here: + return $us->isCustAdmin(); + } + + /** + * Get the validation rules that apply to the request. + * + * @return string[] + * + * @psalm-return array{billingContactName: 'nullable|string|max:255', billingAddress1: 'nullable|string|max:255', billingAddress2: 'nullable|string|max:255', billingAddress3: 'nullable|string|max:255', billingTownCity: 'nullable|string|max:255', billingPostcode: 'nullable|string|max:255', billingCountry: string, billingEmail: 'nullable|email|max:255', billingTelephone: 'nullable|string|max:255', invoiceEmail: 'nullable|email|max:255'} + */ + public function rules(): array + { + return [ + 'billingContactName' => 'nullable|string|max:255', + 'billingAddress1' => 'nullable|string|max:255', + 'billingAddress2' => 'nullable|string|max:255', + 'billingAddress3' => 'nullable|string|max:255', + 'billingTownCity' => 'nullable|string|max:255', + 'billingPostcode' => 'nullable|string|max:255', + 'billingCountry' => 'nullable|string|max:255|in:' . implode( ',', array_values( Countries::getListForSelect( 'iso_3166_2' ) ) ), + 'billingEmail' => 'nullable|email|max:255', + 'billingTelephone' => 'nullable|string|max:255', + 'invoiceEmail' => 'nullable|email|max:255', + ]; + } +} \ No newline at end of file diff --git a/app/Http/Requests/Dashboard/NocDetailsRequest.php b/app/Http/Requests/Dashboard/NocDetailsRequest.php new file mode 100644 index 000000000..3fb8f9ecd --- /dev/null +++ b/app/Http/Requests/Dashboard/NocDetailsRequest.php @@ -0,0 +1,75 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Requests\Dashboard + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class NocDetailsRequest extends FormRequest +{ + /** + * Determine if the user is authorized to make this request. + * + * @return bool + */ + public function authorize(): bool + { + /** @var User $us */ + $us = Auth::getUser(); + // middleware ensures custadmin access only so always authorised here: + return $us->isCustAdmin(); + } + + /** + * Get the validation rules that apply to the request. + * + * @return string[] + * + * @psalm-return array{nocphone: 'nullable|string|max:255', noc24hphone: 'nullable|string|max:255', nocemail: 'nullable|email|max:255', nochours: string, nocwww: 'nullable|url|max:255'} + */ + public function rules(): array + { + return [ + 'nocphone' => 'nullable|string|max:255', + 'noc24hphone' => 'nullable|string|max:255', + 'nocemail' => 'nullable|email|max:255', + 'nochours' => 'nullable|string|in:' . implode( ',', array_keys( Customer::$NOC_HOURS ) ), + 'nocwww' => 'nullable|url|max:255', + ]; + } +} \ No newline at end of file diff --git a/app/Http/Requests/EmailPatchPanelPort.php b/app/Http/Requests/EmailPatchPanelPort.php new file mode 100644 index 000000000..70ae1f6ee --- /dev/null +++ b/app/Http/Requests/EmailPatchPanelPort.php @@ -0,0 +1,62 @@ +isSuperUser(); + } + + /** + * Get the validation rules that apply to the request. + * + * @return string[] + * + * @psalm-return array{email_to: 'required|string', email_cc: 'nullable|string', email_bcc: 'nullable|string', email_subject: 'required|string', email_text: 'required'} + */ + public function rules(): array + { + return [ + 'email_to' => 'required|string', + 'email_cc' => 'nullable|string', + 'email_bcc' => 'nullable|string', + 'email_subject' => 'required|string', + 'email_text' => 'required', + ]; + } +} \ No newline at end of file diff --git a/app/Http/Requests/IpAddress/DeleteByNetwork.php b/app/Http/Requests/IpAddress/DeleteByNetwork.php new file mode 100644 index 000000000..1b0da19c9 --- /dev/null +++ b/app/Http/Requests/IpAddress/DeleteByNetwork.php @@ -0,0 +1,125 @@ + + * @author Barry O'Donovan + * @category IXP + * @package IXP\Http\Requests\IpAddress + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class DeleteByNetwork extends FormRequest +{ + /** + * Determine if the user is authorized to make this request. + * + * @return bool + */ + public function authorize(): bool + { + /** @var User $us */ + $us = Auth::getUser(); + // middleware ensures superuser access only so always authorised here: + return $us->isSuperUser(); + } + + /** + * Get the validation rules that apply to the request. + * + * @return string[] + * + * @psalm-return array{network: 'string|max:255'} + */ + public function rules(): array + { + return [ + 'network' => 'string|max:255' + ]; + } + + /** + * Configure the validator instance. + * + * @param Validator $validator + * + * @return void + */ + public function withValidator( Validator $validator ): void + { + $validator->after( function ( $validator ) { + if( trim( $this->network ) ) { + $netblock = trim( htmlspecialchars( $this->network ) ); + $pieces = explode( '/', $netblock ); + + // $pieces[ 0 ] => IP address, if exist $pieces[ 1 ] => subnet + if( !filter_var( $pieces[ 0 ], FILTER_VALIDATE_IP ) ) { + $validator->errors()->add( 'network', 'The IP address format is invalid' ); + return; + } + + $ip = new IP( $pieces[ 0 ] ); + + if( $ip->version === 'IPv4' ) { + if( !isset( $pieces[ 1 ] ) ) { + $pieces[ 1 ] = 32; + } + + if( $pieces[ 1 ] < 24 || $pieces[ 1 ] > 32 || !filter_var( $pieces[ 1 ], FILTER_VALIDATE_INT ) ) { + $validator->errors()->add( 'network', 'The subnet size is invalid. For IPv4, it must be an integer between between 24 and 32 inclusive.' ); + } + } else { + if( !isset( $pieces[ 1 ] ) ) { + $pieces[ 1 ] = 128; + } + + if( $pieces[ 1 ] < 120 || $pieces[ 1 ] > 128 || !filter_var( $pieces[ 1 ], FILTER_VALIDATE_INT ) ) { + $validator->errors()->add( 'network', 'The subnet size is invalid. For IPv6, it must be an integer between between 120 and 128 inclusive.' ); + } + } + + $network = Network::parse( $netblock ); + + if( substr( $pieces[ 0 ], -3 ) !== '::0' && $network->getFirstIP() != $pieces[ 0 ] ) { + $validator->errors()->add( 'network', 'The network you have specified above does not start at the correct IP address. ' + . 'While it is technically valid, we require it to be set correctly to avoid unintentional errors. ' + . 'In this instance, the correct starting address would be: ' . $network->getFirstIP() . '.' ); + } + } + }); + } +} \ No newline at end of file diff --git a/app/Http/Requests/IpAddress/Store.php b/app/Http/Requests/IpAddress/Store.php new file mode 100644 index 000000000..d260e3d68 --- /dev/null +++ b/app/Http/Requests/IpAddress/Store.php @@ -0,0 +1,127 @@ + + * @author Barry O'Donovan + * @category IXP + * @package IXP\Http\Requests\IpAddress + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Store extends FormRequest +{ + /** + * Determine if the user is authorized to make this request. + * + * @return bool + */ + public function authorize(): bool + { + /** @var User $us */ + $us = Auth::getUser(); + // middleware ensures superuser access only so always authorised here: + return $us->isSuperUser(); + } + + /** + * Get the validation rules that apply to the request. + * + * @return string[] + * + * @psalm-return array{vlan: 'required|integer|exists:vlan,id', network: 'required|string|max:255', decimal: 'bool', skip: 'bool', overflow: 'bool'} + */ + public function rules(): array + { + return [ + 'vlan' => 'required|integer|exists:vlan,id', + 'network' => 'required|string|max:255', + 'decimal' => 'bool', + 'skip' => 'bool', + 'overflow' => 'bool', + ]; + } + + /** + * Configure the validator instance. + * + * @param Validator $validator + * + * @return void + */ + public function withValidator( Validator $validator ): void + { + $validator->after( function( $validator ) { + $netblock = trim( htmlspecialchars( $this->network ) ); + $pieces = explode( '/', $netblock ); + + // $pieces[ 0 ] => IP address, if exist $pieces[ 1 ] => subnet + if( !filter_var( $pieces[0], FILTER_VALIDATE_IP ) ) { + $validator->errors()->add('network', 'The IP address format is invalid' ); + } else { + $ip = new IP( $pieces[ 0 ] ); + + if( $ip->version === 'IPv4' ) { + if( !isset( $pieces[ 1 ] ) ) { + $pieces[ 1 ] = 32; + } + + if( $pieces[ 1 ] < 24 || $pieces[ 1 ] > 32 || !filter_var( $pieces[ 1 ], FILTER_VALIDATE_INT ) ) { + $validator->errors()->add( 'network', 'The subnet size is invalid. For IPv4, it must be an integer between between 24 and 32 inclusive.' ); + } + } else { + if( !isset( $pieces[ 1 ] ) ) { + $pieces[ 1 ] = 128; + } + + if( $pieces[ 1 ] < 120 || $pieces[ 1 ] > 128 || !filter_var( $pieces[ 1 ], FILTER_VALIDATE_INT ) ) { + $validator->errors()->add( 'network', 'The subnet size is invalid. For IPv6, it must be an integer between between 120 and 128 inclusive.' ); + } + } + + $network = Network::parse( $netblock ); + + // FIXME FIXME-YR type might not match + if( $network->getFirstIP() != $pieces[ 0 ] && substr( $pieces[ 0 ], -3 ) !== '::0' ) { + $validator->errors()->add( 'network', 'The network you have specified above does not start at the correct IP address. ' + . 'While it is technically valid, we require it to be set correctly to avoid unintentional errors. ' + . 'In this instance, the correct starting address would be: ' . $network->getFirstIP() . '.' ); + } + } + }); + } +} \ No newline at end of file diff --git a/app/Http/Requests/Irrdb.php b/app/Http/Requests/Irrdb.php new file mode 100644 index 000000000..2717375bf --- /dev/null +++ b/app/Http/Requests/Irrdb.php @@ -0,0 +1,101 @@ + + * @author Barry O'Donovan + * @category IXP + * @package IXP\Http\Requests + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Irrdb extends FormRequest +{ + /** + * Determine if the user is authorized to make this request. + * + * @return bool + */ + public function authorize(): bool + { + /** @var User $us */ + $us = Auth::getUser(); + $privs = $us->privs(); + + if( $privs < User::AUTH_CUSTUSER ) { + return false; + } + + if( $privs === User::AUTH_SUPERUSER ) { + return true; + } + + return $this->user()->custid === $this->cust->id; + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + * + * @psalm-return array + */ + public function rules(): array + { + return []; + } + + /** + * Configure the validator instance. + * + * @return void + * + * @throws + */ + public function withValidator(): void + { + if( !in_array( $this->protocol, [ 4,6 ], false ) ) { + abort( 404 , 'Unknown protocol'); + } + + if( !( $this->cust->routeServerClient( $this->protocol ) && $this->cust->irrdbFiltered() ) ) { + throw new IrrdbManage( 'IRRDB only applies to customers who are route server clients which are configured for IRRDB filtering.' ); + } + + if( !in_array( $this->type, [ "asn", 'prefix' ] ) ) { + abort( 404 , 'Unknown IRRDB type'); + } + } +} \ No newline at end of file diff --git a/app/Http/Requests/MovePatchPanelPort.php b/app/Http/Requests/MovePatchPanelPort.php new file mode 100644 index 000000000..41d77a721 --- /dev/null +++ b/app/Http/Requests/MovePatchPanelPort.php @@ -0,0 +1,62 @@ +isSuperUser(); + } + + /** + * Get the validation rules that apply to the request. + * + * @return string[] + * + * @psalm-return array{id: 'required|integer|exists:patch_panel_port,id', port_id: 'required|integer|exists:patch_panel_port,id', slave_id: ''|'required|integer|different:port_id|exists:patch_panel_port,id'} + */ + public function rules(): array + { + $master = PatchPanelPort::find( $this->port_id ); + return [ + 'id' => 'required|integer|exists:patch_panel_port,id', + 'port_id' => 'required|integer|exists:patch_panel_port,id', + 'slave_id' => $master->isDuplexPort() ? 'required|integer|different:port_id|exists:patch_panel_port,id' : '', + ]; + } +} \ No newline at end of file diff --git a/app/Http/Requests/PeeringManagerRequest.php b/app/Http/Requests/PeeringManagerRequest.php new file mode 100644 index 000000000..95b8bca9d --- /dev/null +++ b/app/Http/Requests/PeeringManagerRequest.php @@ -0,0 +1,109 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Requests + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class PeeringManagerRequest extends FormRequest +{ + + /** + * Determine if the user is authorized to make this request. + * + * @return true + */ + public function authorize(): bool + { + return true; + } + + public function __construct() + { + parent::__construct(); + Validator::extend("emails", function( $attribute, $value, $parameters ) { + $rules = [ + 'email' => 'required|email', + ]; + + $addresses = explode( ',', $value ); + + foreach( $addresses as $address ) { + $data = [ + 'email' => trim( $address ) + ]; + $validator = Validator::make( $data, $rules ); + if( $validator->fails() ) { + return false; + } + } + return true; + }); + } + + /** + * Get the validation rules that apply to the request. + * + * @return string[] + * + * @psalm-return array{to: 'required|emails', cc: 'nullable|emails', bcc: 'nullable|emails', subject: 'required|string', message: 'required'} + */ + public function rules(): array + { + return [ + 'to' => 'required|emails', + 'cc' => 'nullable|emails', + 'bcc' => 'nullable|emails', + 'subject' => 'required|string', + 'message' => 'required', + ]; + } + + /** + * Get the error messages for the defined validation rules. + * + * @return string[] + * + * @psalm-return array{'to.emails': 'One or more of the email addresses are invalid', 'cc.emails': 'One or more of the email addresses are invalid', 'bcc.emails': 'One or more of the email addresses are invalid'} + */ + #[\Override] + public function messages(): array + { + return [ + 'to.emails' => 'One or more of the email addresses are invalid', + 'cc.emails' => 'One or more of the email addresses are invalid', + 'bcc.emails' => 'One or more of the email addresses are invalid', + ]; + } +} \ No newline at end of file diff --git a/app/Http/Requests/Profile/Notification.php b/app/Http/Requests/Profile/Notification.php new file mode 100644 index 000000000..b4af39f1b --- /dev/null +++ b/app/Http/Requests/Profile/Notification.php @@ -0,0 +1,65 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Requests\Profile + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Notification extends FormRequest +{ + /** + * Determine if the user is authorized to make this request. + * + * @return bool + */ + public function authorize(): bool + { + // middleware ensures the user is logged in + return Auth::check(); + } + + /** + * Get the validation rules that apply to the request. + * + * @return string[] + * + * @psalm-return array{notify: 'required|in:none,watched,all'} + */ + public function rules(): array + { + return [ + 'notify' => 'required|in:none,watched,all', + ]; + } +} \ No newline at end of file diff --git a/app/Http/Requests/Profile/Password.php b/app/Http/Requests/Profile/Password.php new file mode 100644 index 000000000..ce7091ceb --- /dev/null +++ b/app/Http/Requests/Profile/Password.php @@ -0,0 +1,87 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Requests\Profile + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Password extends FormRequest +{ + /** + * Determine if the user is authorized to make this request. + * + * @return bool + */ + public function authorize(): bool + { + // middleware ensures the user is logged in + return Auth::check(); + } + + /** + * Get the validation rules that apply to the request. + * + * @return ((\Illuminate\Validation\Rules\Password|string)[]|string)[] + * + * @psalm-return array{current_password: 'required|string', new_password: list{'required', 'max:255', 'different:current_password', \Illuminate\Validation\Rules\Password}, confirm_password: 'required|same:new_password'} + */ + public function rules(): array + { + return [ + 'current_password' => 'required|string', + 'new_password' => [ 'required', 'max:255', 'different:current_password', \Illuminate\Validation\Rules\Password::min(8)->letters()->mixedCase()->numbers() ], + 'confirm_password' => 'required|same:new_password', + ]; + } + + /** + * Configure the validator instance. + * + * @param Validator $validator + * + * @return void + */ + public function withValidator( Validator $validator ): void + { + $validator->after( function( $validator ) { + if( !Hash::check( $this->current_password, Auth::getUser()->password ) ){ + $validator->errors()->add( 'current_password', 'The current password is incorrect.' ); + return false; + } + return true; + }); + } +} \ No newline at end of file diff --git a/app/Http/Requests/Profile/Profile.php b/app/Http/Requests/Profile/Profile.php new file mode 100644 index 000000000..32929fc59 --- /dev/null +++ b/app/Http/Requests/Profile/Profile.php @@ -0,0 +1,88 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Requests\Profile + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Profile extends FormRequest +{ + /** + * Determine if the user is authorized to make this request. + * + * @return bool + */ + public function authorize(): bool + { + // middleware ensures the user is logged in + return Auth::check(); + } + + /** + * Get the validation rules that apply to the request. + * + * @return string[] + * + * @psalm-return array{name: 'required|string|min:2|max:255', username: string, email: 'required|email|max:255', authorisedMobile: 'nullable|string|max:30', actual_password: 'required|string|max:255'} + */ + public function rules(): array + { + return [ + 'name' => 'required|string|min:2|max:255', + 'username' => 'required|string|min:3|max:255|unique:user,username,' . Auth::id(), + 'email' => 'required|email|max:255', + 'authorisedMobile' => 'nullable|string|max:30', + 'actual_password' => 'required|string|max:255', + ]; + } + + /** + * Configure the validator instance. + * + * @param Validator $validator + * + * @return void + */ + public function withValidator( Validator $validator ): void + { + $validator->after( function( $validator ) { + if( !Hash::check( $this->actual_password, Auth::getUser()->password ) ) { + $validator->errors()->add( 'actual_password', 'The current password is incorrect.'); + return false; + } + return true; + }); + } +} \ No newline at end of file diff --git a/app/Http/Requests/Request.php b/app/Http/Requests/Request.php new file mode 100644 index 000000000..ce4618346 --- /dev/null +++ b/app/Http/Requests/Request.php @@ -0,0 +1,36 @@ + + * @author Yann Robin + * @category Requests + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +abstract class Request extends FormRequest {} \ No newline at end of file diff --git a/app/Http/Requests/RouteServerFilter/Store.php b/app/Http/Requests/RouteServerFilter/Store.php new file mode 100644 index 000000000..da834abaf --- /dev/null +++ b/app/Http/Requests/RouteServerFilter/Store.php @@ -0,0 +1,115 @@ + + * @author Barry O'Donovan + * @category Request\RouteServerFilter + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Store extends FormRequest +{ + /** + * Prepare the data for validation. + * + * @return void + */ + #[\Override] + protected function prepareForValidation(): void + { + // If all vlans or all peers are selected (value === 0) then reset to null to avoid conflict in DB + $vlanid = $this->vlan_id === '0' ? null : $this->vlan_id; + $peerid = $this->peer_id === '0' ? null : $this->peer_id; + $this->merge([ 'vlan_id' => $vlanid, 'peer_id' => $peerid ]); + } + + + /** + * Get the validation rules that apply to the request. + * + * @return ((IPv4Cidr|IPv6Cidr|Ipv4SubnetSize|Ipv6SubnetSize|\Closure|string)[]|string)[] + * + * @psalm-return array{peer_id: list{'nullable', 'integer', \Closure(mixed, mixed, mixed):mixed}, vlan_id: list{'nullable', 'integer', \Closure(mixed, mixed, mixed):mixed}, advertised_prefix: list{'nullable', 'max:43', 'string'|IPv4Cidr|IPv6Cidr, ''|Ipv4SubnetSize|Ipv6SubnetSize}, received_prefix: list{'nullable', 'max:43', 'string'|IPv4Cidr|IPv6Cidr, ''|Ipv4SubnetSize|Ipv6SubnetSize}, protocol: string, action_advertise: string, action_receive: string} + */ + public function rules(): array + { + if( $this->received_prefix !== '*'){ + $ipvCheckRec = $this->protocol === '4' ? new IPv4Cidr() : new IPv6Cidr(); + $subnetCheckRec = $this->protocol === '4' ? new Ipv4SubnetSize() : new Ipv6SubnetSize(); + } else { + $this->merge(['received_prefix' => null]); + $ipvCheckRec = "string"; + $subnetCheckRec = ""; + } + + if( $this->advertised_prefix !== '*'){ + $ipvCheckAdv = $this->protocol === '4' ? new IPv4Cidr() : new IPv6Cidr(); + $subnetCheckAdv = $this->protocol === '4' ? new Ipv4SubnetSize() : new Ipv6SubnetSize(); + } else { + $this->merge(['advertised_prefix' => null]); + $ipvCheckAdv = "string"; + $subnetCheckAdv = ""; + } + + return [ + 'peer_id' => [ 'nullable', 'integer', + function( $attribute, $value, $fail ) { + if( !Customer::find( $value ) ) { + return $fail( 'Customer is invalid / does not exist.' ); + } + } + ], + 'vlan_id' => [ 'nullable', 'integer', + function( $attribute, $value, $fail ) { + if( !Vlan::find( $value ) ) { + return $fail( 'Vlan is invalid / does not exist.' ); + } + } + ], + 'advertised_prefix' => [ 'nullable', 'max:43', $ipvCheckAdv, $subnetCheckAdv ], + 'received_prefix' => [ 'nullable', 'max:43', $ipvCheckRec, $subnetCheckRec ], + 'protocol' => 'nullable|integer|in:' . implode( ',', array_keys( Router::$PROTOCOLS ) ), + 'action_advertise' => 'nullable|string|max:250|in:' . implode( ',', array_keys( RouteServerFilter::$ADVERTISE_ACTION_TEXT ) ), + 'action_receive' => 'nullable|string|max:250|in:' . implode( ',', array_keys( RouteServerFilter::$RECEIVE_ACTION_TEXT ) ), + ]; + } +} \ No newline at end of file diff --git a/app/Http/Requests/StatisticsRequest.php b/app/Http/Requests/StatisticsRequest.php new file mode 100644 index 000000000..777310c4c --- /dev/null +++ b/app/Http/Requests/StatisticsRequest.php @@ -0,0 +1,69 @@ + + * @author Barry O'Donovan + * @category IXP + * @package IXP\Http\Requests + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class StatisticsRequest extends FormRequest +{ + public ?string $period = null; + public ?string $category = null; + public ?string $protocol = null; + public ?string $type = null; + + /** + * Determine if the user is authorized to make this request. + * + * @return true + */ + public function authorize(): bool + { + // authorisation done on a per action basis using Grapher::Graph->authorise() in the controller + return true; + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + * + * @psalm-return array + */ + public function rules(): array + { + return []; + } +} \ No newline at end of file diff --git a/app/Http/Requests/StorePatchPanel.php b/app/Http/Requests/StorePatchPanel.php new file mode 100644 index 000000000..573b5e859 --- /dev/null +++ b/app/Http/Requests/StorePatchPanel.php @@ -0,0 +1,81 @@ + + * @author Barry O'Donovan + * @category IXP + * @package IXP\Http\Requests + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class StorePatchPanel extends FormRequest +{ + /** + * Determine if the user is authorized to make this request. + * + * @return bool + */ + public function authorize(): bool + { + /** @var User $us */ + $us = Auth::getUser(); + // middleware ensures superuser access only so always authorised here: + return $us->isSuperUser(); + } + + /** + * Get the validation rules that apply to the request. + * + * @return string[] + * + * @psalm-return array{name: 'required|string|max:255', colo_reference: 'required|string|max:255', cabinet_id: 'required|integer|exists:cabinet,id', cable_type: 'required|integer', connector_type: 'required|integer', installation_date: 'date', port_prefix: 'string|nullable', u_position: 'numeric|nullable', colo_pp_type: 'numeric', mounted_at: string, numberOfPorts: 'required|integer'} + */ + public function rules(): array + { + return [ + 'name' => 'required|string|max:255', + 'colo_reference' => 'required|string|max:255', + 'cabinet_id' => 'required|integer|exists:cabinet,id', + 'cable_type' => 'required|integer', + 'connector_type' => 'required|integer', + 'installation_date' => 'date', + 'port_prefix' => 'string|nullable', + 'u_position' => 'numeric|nullable', + 'colo_pp_type' => 'numeric', + 'mounted_at' => 'string|nullable|in:' . implode( ',', array_keys( PatchPanel::$MOUNTED_AT ) ), + 'numberOfPorts' => 'required|integer', + ]; + } +} \ No newline at end of file diff --git a/app/Http/Requests/StorePatchPanelPort.php b/app/Http/Requests/StorePatchPanelPort.php new file mode 100644 index 000000000..0e7ecd95a --- /dev/null +++ b/app/Http/Requests/StorePatchPanelPort.php @@ -0,0 +1,88 @@ + + * @author Barry O'Donovan + * @category IXP + * @package IXP\Http\Requests + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class StorePatchPanelPort extends FormRequest +{ + /** + * Determine if the user is authorized to make this request. + * + * @return bool + */ + public function authorize(): bool + { + /** @var User $us */ + $us = Auth::getUser(); + // middleware ensures superuser access only so always authorised here: + return $us->isSuperUser(); + } + + /** + * Get the validation rules that apply to the request. + * + * @return string[] + * + * @psalm-return array{switch_port_id: 'nullable|integer|exists:switchport,id', customer_id: 'nullable|integer|exists:cust,id', partner_port: 'nullable|integer|exists:patch_panel_port,id'|'required|integer|exists:patch_panel_port,id', number: 'nullable|string|max:255'|'required|string|max:255', patch_panel: 'nullable'|'required', description: 'nullable|string|max:255', colo_circuit_ref: 'nullable|string|max:255', colo_billing_ref: 'nullable|string|max:255', ticket_ref: 'nullable|string|max:255', state: string, chargeable: string, assigned_at: 'nullable|date', connected_at: 'nullable|date', cease_requested_at: 'nullable|date', ceased_at: 'nullable|date', last_state_change_at: 'nullable|date'} + */ + public function rules(): array + { + $required = ( $this->allocated || $this->prewired ) ? 'nullable' : 'required'; + $required2 = $this->duplex ? 'required' : 'nullable'; + return [ + 'switch_port_id' => 'nullable|integer|exists:switchport,id', + 'customer_id' => 'nullable|integer|exists:cust,id', + 'partner_port' => $required2 . '|integer|exists:patch_panel_port,id', + 'number' => $required . '|string|max:255', + 'patch_panel' => $required, + 'description' => 'nullable|string|max:255', + 'colo_circuit_ref' => 'nullable|string|max:255', + 'colo_billing_ref' => 'nullable|string|max:255', + 'ticket_ref' => 'nullable|string|max:255', + 'state' => 'nullable|integer|in:' . implode( ',', array_keys( PatchPanelPort::$STATES ) ), + 'chargeable' => 'nullable|integer|in:' . implode( ',', array_keys( PatchPanelPort::$CHARGEABLES ) ), + 'assigned_at' => 'nullable|date', + 'connected_at' => 'nullable|date', + 'cease_requested_at' => 'nullable|date', + 'ceased_at' => 'nullable|date', + 'last_state_change_at' => 'nullable|date', + ]; + } +} \ No newline at end of file diff --git a/app/Http/Requests/StorePhysicalInterface.php b/app/Http/Requests/StorePhysicalInterface.php new file mode 100644 index 000000000..470330a70 --- /dev/null +++ b/app/Http/Requests/StorePhysicalInterface.php @@ -0,0 +1,80 @@ + + * @author Barry O'Donovan + * @category IXP + * @package IXP\Http\Requests + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class StorePhysicalInterface extends FormRequest +{ + /** + * Determine if the user is authorized to make this request. + * + * @return bool + */ + public function authorize(): bool + { + /** @var User $us */ + $us = Auth::getUser(); + // middleware ensures superuser access only so always authorised here: + return $us->isSuperUser(); + } + + /** + * Get the validation rules that apply to the request. + * + * @return string[] + * + * @psalm-return array{virtualinterfaceid: 'required|integer|exists:virtualinterface,id', switch: 'required|integer|exists:switch,id', switchportid: 'required|integer|exists:switchport,id', status: string, speed: string, duplex: string, rate_limit: 'nullable|integer|min:0', notes: 'string|nullable', 'switch-fanout': 'integer|nullable'|'integer|required|exists:switch,id', 'switch-port-fanout': 'integer|nullable'|'integer|required|exists:switchport,id'} + */ + public function rules(): array + { + return [ + 'virtualinterfaceid' => 'required|integer|exists:virtualinterface,id', + 'switch' => 'required|integer|exists:switch,id', + 'switchportid' => 'required|integer|exists:switchport,id', + 'status' => 'required|integer|in:' . implode( ',', array_keys( PhysicalInterface::$STATES ) ), + 'speed' => 'required|integer|in:' . implode( ',', array_keys( PhysicalInterface::$SPEED ) ), + 'duplex' => 'required|string|in:' . implode( ',', array_keys( PhysicalInterface::$DUPLEX ) ), + 'rate_limit' => 'nullable|integer|min:0', + 'notes' => 'string|nullable', + 'switch-fanout' => 'integer' . ( $this->fanout ? '|required|exists:switch,id' : '|nullable' ), + 'switch-port-fanout' => 'integer' . ( $this->fanout ? '|required|exists:switchport,id' : '|nullable' ), + ]; + } +} \ No newline at end of file diff --git a/app/Http/Requests/StoreRouter.php b/app/Http/Requests/StoreRouter.php new file mode 100644 index 000000000..8bd705b19 --- /dev/null +++ b/app/Http/Requests/StoreRouter.php @@ -0,0 +1,108 @@ + + * @author Yann Robin + * @category IPX + * @package IXP\Http\Requests + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class StoreRouter extends FormRequest +{ + /** + * Determine if the user is authorized to make this request. + * + * @return bool + */ + public function authorize(): bool + { + /** @var User $us */ + $us = Auth::getUser(); + // middleware ensures superuser access only so always authorised here: + return $us->isSuperUser(); + } + + /** + * Get the validation rules that apply to the request. + * + * @return string[] + * + * @psalm-return array{handle: string, pair_id: string, vlan_id: 'required|integer|exists:vlan,id', protocol: string, type: string, name: 'required|string|max:255', shortname: 'required|string|max:30', router_id: 'required|ipv4', peering_ip: string, asn: 'required|integer', software: string, software_version: 'nullable|string|max:255', operating_system: 'nullable|string|max:255', operating_system_version: 'nullable|string|max:255', mgmt_host: 'required|string|max:255', api_type: string, api: ''|'url|required|regex:/.*[^\/]$/', lg_access: string} + */ + public function rules(): array + { + $this->merge( [ 'handle' => preg_replace( "/[^a-z0-9\-]/", '' , strtolower( $this->input( 'handle', '' ) ) ) ] ); + + return [ + 'handle' => 'required|string|max:255|unique:routers,handle' . ( $this->router ? ','. $this->router->id : '' ), + 'pair_id' => 'nullable|integer' . ( $this->db_id ? '|not_in:' . $this->db_id : '' ) . '|exists:routers,id', + 'vlan_id' => 'required|integer|exists:vlan,id', + 'protocol' => 'required|integer|in:' . implode( ',', array_keys( Router::$PROTOCOLS ) ), + 'type' => 'required|integer|in:' . implode( ',', array_keys( Router::$TYPES ) ), + 'name' => 'required|string|max:255', + 'shortname' => 'required|string|max:30', + 'router_id' => 'required|ipv4', + 'peering_ip' => 'required|ipv' . $this->protocol, + 'asn' => 'required|integer', + 'software' => 'required|integer|in:' . implode( ',', array_keys( Router::$SOFTWARES ) ), + 'software_version' => 'nullable|string|max:255', + 'operating_system' => 'nullable|string|max:255', + 'operating_system_version' => 'nullable|string|max:255', + 'mgmt_host' => 'required|string|max:255', + 'api_type' => 'required|integer|in:' . implode( ',', array_keys( Router::$API_TYPES ) ), + 'api' => ( $this->api_type != Router::API_TYPE_NONE ? 'url|required|regex:/.*[^\/]$/' : '' ), + 'lg_access' => 'integer' . ( $this->api ? '|required|in:' . implode( ',', array_keys( User::$PRIVILEGES_ALL ) ) : '' ), + ]; + } + + /** + * Get the error messages for the defined validation rules. + * + * @return string[] + * + * @psalm-return array{'api.regex': 'The API URL must not end with a trailing slash', 'pair_id.not_in': 'Cannot pair a router with itself'} + */ + #[\Override] + public function messages(): array + { + return [ + 'api.regex' => 'The API URL must not end with a trailing slash', + 'pair_id.not_in' => 'Cannot pair a router with itself', + ]; + } +} \ No newline at end of file diff --git a/app/Http/Requests/StoreSflowReceiver.php b/app/Http/Requests/StoreSflowReceiver.php new file mode 100644 index 000000000..69d742316 --- /dev/null +++ b/app/Http/Requests/StoreSflowReceiver.php @@ -0,0 +1,71 @@ + + * @author Barry O'Donovan + * @category IXP + * @package IXP\Http\Requests + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class StoreSflowReceiver extends FormRequest +{ + /** + * Determine if the user is authorized to make this request. + * + * @return bool + */ + public function authorize(): bool + { + /** @var User $us */ + $us = Auth::getUser(); + // middleware ensures superuser access only so always authorised here: + return $us->isSuperUser(); + } + + /** + * Get the validation rules that apply to the request. + * + * @return string[] + * + * @psalm-return array{virtual_interface_id: 'required|integer|exists:virtualinterface,id', dst_ip: 'required|ip', dst_port: 'required|integer'} + */ + public function rules(): array + { + return [ + 'virtual_interface_id' => 'required|integer|exists:virtualinterface,id', + 'dst_ip' => 'required|ip', + 'dst_port' => 'required|integer', + ]; + } +} \ No newline at end of file diff --git a/app/Http/Requests/StoreVirtualInterface.php b/app/Http/Requests/StoreVirtualInterface.php new file mode 100644 index 000000000..3ca028085 --- /dev/null +++ b/app/Http/Requests/StoreVirtualInterface.php @@ -0,0 +1,73 @@ + + * @author Barry O'Donovan + * @category IXP + * @package IXP\Http\Requests + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class StoreVirtualInterface extends FormRequest +{ + /** + * Determine if the user is authorized to make this request. + * + * @return bool + */ + public function authorize(): bool + { + /** @var User $us */ + $us = Auth::getUser(); + // middleware ensures superuser access only so always authorised here: + return $us->isSuperUser(); + } + + /** + * Get the validation rules that apply to the request. + * + * @return string[] + * + * @psalm-return array{custid: 'integer|required|exists:cust,id', name: 'string|max:255|nullable', description: 'string|max:255|nullable', channelgroup: 'integer|nullable', mtu: 'integer|min:1|max:64000|nullable'} + */ + public function rules(): array + { + return [ + 'custid' => 'integer|required|exists:cust,id', + 'name' => 'string|max:255|nullable', + 'description' => 'string|max:255|nullable', + 'channelgroup' => 'integer|nullable', + 'mtu' => 'integer|min:1|max:64000|nullable', + ]; + } +} \ No newline at end of file diff --git a/app/Http/Requests/StoreVirtualInterfaceWizard.php b/app/Http/Requests/StoreVirtualInterfaceWizard.php new file mode 100644 index 000000000..96d9a457a --- /dev/null +++ b/app/Http/Requests/StoreVirtualInterfaceWizard.php @@ -0,0 +1,102 @@ + + * @author Barry O'Donovan + * @category IXP + * @package IXP\Http\Requests + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class StoreVirtualInterfaceWizard extends FormRequest +{ + /** + * Determine if the user is authorized to make this request. + * + * @return bool + */ + public function authorize(): bool + { + /** @var User $us */ + $us = Auth::getUser(); + // middleware ensures superuser access only so always authorised here: + return $us->isSuperUser(); + } + + /** + * Get the validation rules that apply to the request. + * + * @return ((IdnValidate|string)[]|string)[] + * + * @psalm-return array{custid: 'required|integer|exists:cust,id', vlanid: 'required|integer|exists:vlan,id', trunk: 'boolean', switch: 'required|integer|exists:switch,id', switchportid: 'required|integer|exists:switchport,id', status: string, speed: string, duplex: string, maxbgpprefix: 'integer|nullable', mcastenabled: 'boolean', rsclient: 'boolean', irrdbfilter: 'boolean', rsmorespecifics: 'boolean', as112client: 'boolean', ipv4enabled: 'boolean', ipv4address: 'ipv4|nullable'|'ipv4|required', ipv4hostname: list{'string', 'max:255', 'nullable'|'required', IdnValidate}, ipv4bgpmd5secret: 'string|max:255|nullable', ipv4canping: 'boolean', ipv4monitorrcbgp: 'boolean', ipv6enabled: 'boolean', ipv6address: 'ipv6|nullable'|'ipv6|required', ipv6hostname: list{'string', 'max:255', 'nullable'|'required', IdnValidate}, ipv6bgpmd5secret: 'string|max:255|nullable', ipv6canping: 'boolean', ipv6monitorrcbgp: 'boolean'} + */ + public function rules(): array + { + return [ + 'custid' => 'required|integer|exists:cust,id', + 'vlanid' => 'required|integer|exists:vlan,id', + 'trunk' => 'boolean', + + 'switch' => 'required|integer|exists:switch,id', + 'switchportid' => 'required|integer|exists:switchport,id', + 'status' => 'required|integer|in:' . implode( ',', array_keys( PhysicalInterface::$STATES ) ), + 'speed' => 'required|integer|in:' . implode( ',', array_keys( PhysicalInterface::$SPEED ) ), + 'duplex' => 'required|string|in:' . implode( ',', array_keys( PhysicalInterface::$DUPLEX ) ), + + 'maxbgpprefix' => 'integer|nullable', + 'mcastenabled' => 'boolean', + 'rsclient' => 'boolean', + 'irrdbfilter' => 'boolean', + 'rsmorespecifics' => 'boolean', + 'as112client' => 'boolean', + + 'ipv4enabled' => 'boolean', + 'ipv4address' => 'ipv4|' . ( $this->ipv4enabled ? 'required' : 'nullable' ), + 'ipv4hostname' => [ 'string', 'max:255' , ( ( config('ixp_fe.vlaninterfaces.hostname_required' ) && $this->ipv4enabled ) ? 'required' : 'nullable' ), new IdnValidate() ], + 'ipv4bgpmd5secret' => 'string|max:255|nullable', + 'ipv4canping' => 'boolean', + 'ipv4monitorrcbgp' => 'boolean', + + 'ipv6enabled' => 'boolean', + 'ipv6address' => 'ipv6|' . ( $this->ipv6enabled ? 'required' : 'nullable' ), + 'ipv6hostname' => [ 'string', 'max:255' , ( ( config('ixp_fe.vlaninterfaces.hostname_required' ) && $this->ipv6enabled ) ? 'required' : 'nullable' ), new IdnValidate() ], + 'ipv6bgpmd5secret' => 'string|max:255|nullable', + 'ipv6canping' => 'boolean', + 'ipv6monitorrcbgp' => 'boolean', + ]; + } +} \ No newline at end of file diff --git a/app/Http/Requests/StoreVlanInterface.php b/app/Http/Requests/StoreVlanInterface.php new file mode 100644 index 000000000..0f810dcf0 --- /dev/null +++ b/app/Http/Requests/StoreVlanInterface.php @@ -0,0 +1,91 @@ + + * @author Barry O'Donovan + * @category IXP + * @package IXP\Http\Requests + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class StoreVlanInterface extends FormRequest +{ + /** + * Determine if the user is authorized to make this request. + * + * @return bool + */ + public function authorize(): bool + { + /** @var User $us */ + $us = Auth::getUser(); + // middleware ensures superuser access only so always authorised here: + return $us->isSuperUser(); + } + + /** + * Get the validation rules that apply to the request. + * + * @return ((IdnValidate|string)[]|string)[] + * + * @psalm-return array{vlanid: 'required|integer|exists:vlan,id', virtualinterfaceid: 'required|integer|exists:virtualinterface,id', irrdbfilter: 'boolean', mcastenabled: 'boolean', ipv4enabled: 'boolean', ipv4address: 'ipv4|nullable'|'ipv4|required', ipv4hostname: list{'string', 'max:255', 'nullable'|'required', IdnValidate}, ipv4bgpmd5secret: 'string|max:255|nullable', ipv4canping: 'boolean', ipv4monitorrcbgp: 'boolean', maxbgpprefix: 'integer|nullable', rsclient: 'boolean', rsmorespecifics: 'boolean', as112client: 'boolean', busyhost: 'boolean', ipv6enabled: 'boolean', ipv6address: 'ipv6|nullable'|'ipv6|required', ipv6hostname: list{'string', 'max:255', 'nullable'|'required', IdnValidate}, ipv6bgpmd5secret: 'string|max:255|nullable', ipv6canping: 'boolean', ipv6monitorrcbgp: 'boolean'} + */ + public function rules(): array + { + return [ + 'vlanid' => 'required|integer|exists:vlan,id', + 'virtualinterfaceid' => 'required|integer|exists:virtualinterface,id', + 'irrdbfilter' => 'boolean', + 'mcastenabled' => 'boolean', + 'ipv4enabled' => 'boolean', + 'ipv4address' => 'ipv4' . ( $this->ipv4enabled ? '|required' : '|nullable' ), + 'ipv4hostname' => [ 'string', 'max:255' , ( ( config('ixp_fe.vlaninterfaces.hostname_required' ) && $this->ipv4enabled ) ? 'required' : 'nullable' ), new IdnValidate() ], + 'ipv4bgpmd5secret' => 'string|max:255|nullable', + 'ipv4canping' => 'boolean', + 'ipv4monitorrcbgp' => 'boolean', + 'maxbgpprefix' => 'integer|nullable', + 'rsclient' => 'boolean', + 'rsmorespecifics' => 'boolean', + 'as112client' => 'boolean', + 'busyhost' => 'boolean', + 'ipv6enabled' => 'boolean', + 'ipv6address' => 'ipv6' . ( $this->ipv6enabled ? '|required' : '|nullable' ), + 'ipv6hostname' => [ 'string', 'max:255' , ( ( config('ixp_fe.vlaninterfaces.hostname_required' ) && $this->ipv6enabled ) ? 'required' : 'nullable' ), new IdnValidate() ], + 'ipv6bgpmd5secret' => 'string|max:255|nullable', + 'ipv6canping' => 'boolean', + 'ipv6monitorrcbgp' => 'boolean', + ]; + } +} \ No newline at end of file diff --git a/app/Http/Requests/Switches/Store.php b/app/Http/Requests/Switches/Store.php new file mode 100644 index 000000000..d0df83c87 --- /dev/null +++ b/app/Http/Requests/Switches/Store.php @@ -0,0 +1,75 @@ +isSuperUser(); + } + + /** + * Get the validation rules that apply to the request. + * + * @return ((IdnValidate|string)[]|string)[] + * + * @psalm-return array{name: string, hostname: list{'required', 'string', 'max:255', string, IdnValidate}, cabinetid: 'required|integer|exists:cabinet,id', infrastructure: 'required|integer|exists:infrastructure,id', snmppasswd: 'nullable|string|max:255', vendorid: 'required|integer|exists:vendor,id', ipv4addr: 'nullable|ipv4', ipv6addr: 'nullable|ipv6', model: 'nullable|string|max:255', asn: 'nullable|integer|min:1', loopback_ip: string, loopback_name: 'nullable|string|max:255', mgmt_mac_address: 'nullable|string|max:17|regex:/^[a-f0-9:\.\-]{12,17}$/i'} + */ + public function rules(): array + { + return [ + 'name' => 'required|string|max:255|unique:switch,name' . ( $this->input('id') ? ','. $this->input('id') : '' ), + 'hostname' => [ 'required', 'string', 'max:255', 'unique:switch,hostname' . ( $this->input('id') ? ','. $this->input('id') : '' ), new IdnValidate() ], + 'cabinetid' => 'required|integer|exists:cabinet,id', + 'infrastructure' => 'required|integer|exists:infrastructure,id', + 'snmppasswd' => 'nullable|string|max:255', + 'vendorid' => 'required|integer|exists:vendor,id', + 'ipv4addr' => 'nullable|ipv4', + 'ipv6addr' => 'nullable|ipv6', + 'model' => 'nullable|string|max:255', + 'asn' => 'nullable|integer|min:1', + 'loopback_ip' => 'nullable|string|max:255|unique:switch,loopback_ip' . ( $this->input('id') ? ','. $this->input('id') : '' ), + 'loopback_name' => 'nullable|string|max:255', + 'mgmt_mac_address' => 'nullable|string|max:17|regex:/^[a-f0-9:\.\-]{12,17}$/i', + ]; + } + +} diff --git a/app/Http/Requests/Switches/StoreBySmtp.php b/app/Http/Requests/Switches/StoreBySmtp.php new file mode 100644 index 000000000..3f25ae5cb --- /dev/null +++ b/app/Http/Requests/Switches/StoreBySmtp.php @@ -0,0 +1,73 @@ + + * @author Barry O'Donovan + * @category IXP + * @package IXP\Http\Swtiches + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class StoreBySmtp extends FormRequest +{ + /** + * Determine if the user is authorized to make this request. + * + * @return bool + */ + public function authorize(): bool + { + /** @var User $us */ + $us = Auth::getUser(); + // middleware ensures superuser access only so always authorised here: + return $us->isSuperUser(); + } + + /** + * Get the validation rules that apply to the request. + * + * @return ((IdnValidate|string)[]|string)[] + * + * @psalm-return array{snmppasswd: 'required|string|max:255', hostname: list{'required', 'string', 'max:255', IdnValidate, string}} + */ + public function rules(): array + { + return [ + 'snmppasswd' => 'required|string|max:255', + 'hostname' => [ 'required', 'string', 'max:255', new IdnValidate(), 'unique:switch,hostname' . ( $this->id ? ',' . $this->id : '' ) ], + ]; + } + +} diff --git a/app/Http/Requests/User/CheckEmail.php b/app/Http/Requests/User/CheckEmail.php new file mode 100644 index 000000000..7021ebec2 --- /dev/null +++ b/app/Http/Requests/User/CheckEmail.php @@ -0,0 +1,97 @@ + + * @author Barry O'Donovan + * @category IXP + * @package IXP\Http\Requests\User + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class CheckEmail extends FormRequest +{ + /** + * Determine if the user is authorized to make this request. + * + * @return bool + */ + public function authorize(): bool + { + if( $this->user()->isCustUser() ){ + return false; + } + return true; + } + + /** + * Get the validation rules that apply to the request. + * + * @return string[] + * + * @psalm-return array{email: 'required|email|max:255'} + */ + public function rules(): array + { + return [ + 'email' => 'required|email|max:255', + ]; + } + + /** + * @param Validator $validator + */ + public function withValidator( Validator $validator ): void + { + /** @var User $us */ + $us = Auth::user(); + $validator->after( function( Validator $validator ) use ( $us ) { + if( !$us->isSuperUser() && User::leftJoin( 'customer_to_users AS c2u', 'c2u.user_id', 'user.id' ) + ->where( 'email', $this->email ) + ->where( 'customer_id', $us->custid )->exists() ) { + + AlertContainer::push( "A user already exists with that email address for your company." , Alert::DANGER ); + $validator->errors()->add( 'email', " " ); + return false; + } + return true; + }); + } +} \ No newline at end of file diff --git a/app/Http/Requests/User/CustomerToUser/Delete.php b/app/Http/Requests/User/CustomerToUser/Delete.php new file mode 100644 index 000000000..ae05f1ca4 --- /dev/null +++ b/app/Http/Requests/User/CustomerToUser/Delete.php @@ -0,0 +1,83 @@ + + * @author Barry O'Donovan + * @category IXP + * @package IXP\Http\Requests\CustomerToUser + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Delete extends FormRequest +{ + /** + * Determine if the user is authorized to make this request. + * + * @return bool + */ + public function authorize(): bool + { + if( $this->user()->isCustUser() ) { + return false; + } + return true; + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + * + * @psalm-return array + */ + public function rules(): array + { + return []; + } + + /** + * @param Validator $validator + * + * @return true + */ + public function withValidator( Validator $validator ): bool + { + $validator->after( function( ) { + if( $this->c2u->customer_id !== $this->user()->custid && !$this->user()->isSuperUser() ) { + Log::notice( $this->user()->username . " tried to delete another customer's user: " . $this->c2u->user->name . " from " . $this->c2u->customer->name ); + abort( 403, 'You are not authorised to delete this user. The administrators have been notified.' ); + } + }); + return true; + } +} \ No newline at end of file diff --git a/app/Http/Requests/User/CustomerToUser/Store.php b/app/Http/Requests/User/CustomerToUser/Store.php new file mode 100644 index 000000000..f23047d57 --- /dev/null +++ b/app/Http/Requests/User/CustomerToUser/Store.php @@ -0,0 +1,124 @@ +user()->isCustUser() ) { + return false; + } + return true; + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules(): array + { + /** @var User $us */ + $us = Auth::user(); + $rules = [ + 'user_id' => 'required|integer|exists:user,id', + 'privs' => 'required|integer|in:' . implode( ',', array_keys( User::$PRIVILEGES_ALL ) ), + ]; + + if( $us->isSuperUser() ) { + $extraRules = [ + 'customer_id' => 'required|integer|exists:cust,id', + ]; + } + + return array_merge( $rules, $extraRules ?? [] ); + } + + /** + * @param Validator $validator + */ + public function withValidator( Validator $validator ): void + { + /** @var User $us */ + $us = Auth::user(); + $validator->after( function( Validator $validator ) use ($us) { + if( !$this->user_id ) { + AlertContainer::push( "You must select one user from the list." , Alert::DANGER ); + return false; + } + + $this->cust = $us->isSuperUser() ? Customer::find( $this->customer_id ) : $us->customer; + + if( CustomerToUser::where( 'customer_id', $this->cust->id )->where( 'user_id', $this->user_id )->get()->isNotEmpty() ) { + AlertContainer::push( "This user is already associated with " . $this->cust->name, Alert::DANGER ); + $validator->errors()->add( 'customer_id', " " ); + return false; + } + + if( (int)$this->privs === User::AUTH_SUPERUSER ) { + if( !$this->user()->isSuperUser() ) { + $validator->errors()->add( 'privs', "You are not allowed to set any user as a super user." ); + return false; + } + + if( !$this->cust->typeInternal() ) { + $validator->errors()->add( 'privs', "You are not allowed to set super user privileges for non-internal (IXP) customer types." ); + return false; + } + + AlertContainer::push( 'Please note that you have given this user full administrative access (super user privilege).', Alert::WARNING ); + } + + return true; + }); + } +} \ No newline at end of file diff --git a/app/Http/Requests/User/Delete.php b/app/Http/Requests/User/Delete.php new file mode 100644 index 000000000..b2d8a72c8 --- /dev/null +++ b/app/Http/Requests/User/Delete.php @@ -0,0 +1,105 @@ + + * @author Barry O'Donovan + * @category IXP + * @package IXP\Http\Requests\User + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ + +class Delete extends FormRequest +{ + /** + * The User object + * @var User + */ + public $user = null; + + /** + * Determine if the user is authorized to make this request. + * + * @return bool + */ + public function authorize(): bool + { + if( $this->user()->isCustUser() ){ + return false; + } + return true; + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + * + * @psalm-return array + */ + public function rules(): array + { + return []; + } + + /** + * @param Validator $validator + * + * @return true + */ + public function withValidator( Validator $validator ): bool + { + /** @var User $us */ + $us = Auth::getUser(); + if( !$validator->fails() && !$us->isSuperUser() ) { + $validator->after( function( ) use ($us) { + // Check if the custadmin try to delete a user from an other Customer + if( CustomerToUser::where( 'customer_id', $us->custid )->where( 'user_id', $this->u->id )->doesntExist() ) { + Log::notice( Auth::user()->username . " tried to delete other customer user " . $this->u->username ); + abort( 401, 'You are not authorised to delete this user. The administrators have been notified.' ); + } + + // Check if a custadmin try to delete a User that has more than 1 customer to User (this should never happen) + if( $this->u->customers()->count() > 1 ) { + abort( 401, 'You are not authorised to delete this user. The administrators have been notified.' ); + } + }); + } + return true; + } +} \ No newline at end of file diff --git a/app/Http/Requests/User/Store.php b/app/Http/Requests/User/Store.php new file mode 100644 index 000000000..f343b85cf --- /dev/null +++ b/app/Http/Requests/User/Store.php @@ -0,0 +1,106 @@ + + * @author Barry O'Donovan + * @category IXP + * @package IXP\Http\Requests\User + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Store extends FormRequest +{ + /** + * Determine if the user is authorized to make this request. + * + * @return bool + */ + public function authorize(): bool + { + if( $this->user()->isCustUser() ){ + return false; + } + return true; + } + + /** + * Get the validation rules that apply to the request. + * + * @return string[] + * + * @psalm-return array{name: 'required|string|max:255', username: string, email: 'required|email|max:255', authorisedMobile: 'nullable|string|max:50', custid: 'required|integer|exists:cust,id', privs: string} + */ + public function rules(): array + { + return [ + 'name' => 'required|string|max:255', + 'username' => 'required|string|min:3|max:255|regex:/^[a-z0-9\-_\.]{3,255}$/|unique:user,username' . ( $this->id ? ',' . $this->id : '' ), + 'email' => 'required|email|max:255', + 'authorisedMobile' => 'nullable|string|max:50', + 'custid' => 'required|integer|exists:cust,id', + 'privs' => 'required|integer|in:' . implode( ',', array_keys( User::$PRIVILEGES ) ), + ]; + } + + /** + * @param Validator $validator + * + * @return false + */ + public function withValidator( Validator $validator ): bool + { + /** @var User $us */ + $us = Auth::getUser(); + if( !$validator->fails() ) { + $validator->after( function( Validator $validator ) use( $us ) { + $isSuperUser = $us->isSuperUser(); + $cust = $isSuperUser ? Customer::find( $this->custid ) : $us->customer; + + if( (int)$this->privs === User::AUTH_SUPERUSER ) { + if( !$isSuperUser || ( $isSuperUser && !$cust->typeInternal() ) ) { + $validator->errors()->add( 'privs', "You are not allowed to set this User as a Super User for " . $cust->name ); + return false; + } + } + return true; + }); + } + return false; + } +} \ No newline at end of file diff --git a/app/Http/Requests/User/Update.php b/app/Http/Requests/User/Update.php new file mode 100644 index 000000000..aa2643ca4 --- /dev/null +++ b/app/Http/Requests/User/Update.php @@ -0,0 +1,125 @@ + + * @author Barry O'Donovan + * @category IXP + * @package IXP\Http\Requests\User + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ + +class Update extends FormRequest +{ + /** + * Determine if the user is authorized to make this request. + * + * @return bool + */ + public function authorize(): bool + { + if( $this->user()->isCustUser() ){ + return false; + } + return true; + } + + /** + * Get the validation rules that apply to the request. + * + * @return string[] + * + * @psalm-return array{name?: 'required|string|max:255', username?: string, email?: 'required|email|max:255', authorisedMobile?: 'nullable|string|max:50', privs?: string} + */ + public function rules(): array + { + /** @var User $us */ + $us = Auth::user(); + $addUserInfo = []; + + // If its a superuser + if( $us->isSuperUser() ) { + $infoArray = [ + 'name' => 'required|string|max:255', + 'username' => 'required|string|min:3|max:255|regex:/^[a-z0-9\-_\.]{3,255}$/|unique:user,username,' . $this->u->id, + 'email' => 'required|email|max:255', + 'authorisedMobile' => 'nullable|string|max:50', + ]; + } else { + $infoArray = [ + 'privs' => 'required|integer|in:' . implode( ',', array_keys( User::$PRIVILEGES_ALL ) ) + ]; + + // If the User edit himself + if( Auth::id() === (int)$this->u->id ) { + $addUserInfo = [ + 'name' => 'required|string|max:255', + 'authorisedMobile' => 'nullable|string|max:50' + ]; + } + $infoArray = array_merge( $infoArray, $addUserInfo ); + } + return $infoArray ; + } + + /** + * @param Validator $validator + * + * @return false + */ + public function withValidator( Validator $validator ): bool + { + /** @var User $us */ + $us = Auth::user(); + $isSuperUser = $us->isSuperUser(); + if( !$validator->fails() && !$isSuperUser ) { + $validator->after( function( Validator $validator ) use ( $us, $isSuperUser ) { + $cust = $isSuperUser ? Customer::find( $this->custid ) : $us->customer; + + if( (int)$this->privs === User::AUTH_SUPERUSER ) { + if( !$isSuperUser || ( $isSuperUser && !$cust->typeInternal() ) ) { + $validator->errors()->add( 'privs', "You are not allowed to set this User as a Super User for " . $cust->name ); + return false; + } + } + return true; + }); + } + return false; + } +} \ No newline at end of file diff --git a/app/IXP.php b/app/IXP.php new file mode 100644 index 000000000..ac22f84a9 --- /dev/null +++ b/app/IXP.php @@ -0,0 +1,179 @@ + + * @category IXP + * @package IXP + * @copyright Copyright (C) 2009 - 2024 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ + +class IXP +{ + public const int IPv4 = 4; + public const int IPv6 = 6; + + public static array $PROTOCOLS = [ + self::IPv4 => 'IPv4', + self::IPv6 => 'IPv6', + ]; + + public static function protocol( int $p ): string { + return self::$PROTOCOLS[$p] ?? ''; + } + + + + /** + * Scale function + * + * This function will scale a number to (for example for traffic + * measured in bits/second) to Kbps, Mbps, Gbps or Tbps; or data. + * measured in bytes to KB, MB, GB or TB. + * + * Valid string formats ($strFormats) and what they return are: + * bytes => Bytes, KBytes, MBytes, GBytes, TBytes + * pkts / errs / discs => pps, Kpps, Mpps, Gpps, Tpps + * bits / * => bits, Kbits, Mbits, Gbits, Tbits + * + * Valid return types ($format) are: + * 0 => fully formatted and scaled value. E.g. 12,354.235 Tbits + * 1 => scaled value without string. E.g. 12,354.235 + * 2 => just the string. E.g. Tbits + * + * @param float $v The value to scale + * @param string $format The format to sue (as above: bytes / pkts / errs / etc ) + * @param int $decs Number of decimals after the decimal point. Defaults to 3. + * @param int $returnType Type of string to return. Valid values are listed above. Defaults to 0. + * + * @return string Scaled / formatted number / type. + */ + public static function scale( float $v, string $format, int $decs = 3, int $returnType = 0 ): string + { + if( $format === 'bytes' ) { + $formats = [ + 'Bytes', 'KBytes', 'MBytes', 'GBytes', 'TBytes' + ]; + } else if( in_array( $format, [ 'pkts', 'errs', 'discs', 'bcasts' ] ) ) { + $formats = [ + 'pps', 'Kpps', 'Mpps', 'Gpps', 'Tpps' + ]; + } else if( $format === 'speed' ) { + $formats = [ + 'Mbps', 'Gbps', 'Tbps' + ]; + } else { + $formats = [ + 'bits', 'Kbits', 'Mbits', 'Gbits', 'Tbits' + ]; + } + + $num_formats = count( $formats ); + for( $i = 0; $i < $num_formats; $i++ ) { + $format = $i > 4 ? $formats[ 4 ] : $formats[ $i ]; + if( ( $v / 1000.0 < 1.0 ) || ( $num_formats === $i + 1 ) ) { + if( $returnType === 0 ) { + return number_format( $v, $decs ) . ' ' . $format; + } + + if( $returnType === 1 ) { + return number_format( $v, $decs ); + } + + return $format; + } + + $v /= 1000.0; + } + + return (string)$v; + } + + /** + * See scale above + * @param float $v + * @param int $decs + * + * @return string + */ + public static function scaleBits( float $v, int $decs = 3 ): string + { + return self::scale( $v, 'bits', $decs ); + } + + /** + * See scale above + * @param float $v + * @param int $decs + * + * @return string + */ + public static function scaleSpeed( float $v, int $decs = 0 ): string + { + return self::scale( $v, 'speed', $decs ); + } + + + /** + * See scale above + * @param float $v + * @param int $decs + * + * @return string + */ + public static function scaleBytes( float $v, int $decs = 3 ): string + { + return self::scale( $v, 'bytes', $decs ); + } + + /** + * Scale a size in bytes in human style filesize + * + * @param int $bytes The value to scale + * + * @return string Scaled / formatted number / type. + */ + public static function scaleFilesize( int $bytes ): string + { + if( $bytes >= 1073741824 ) { + return number_format( $bytes / 1073741824, 2 ) . ' GB'; + } + + if( $bytes >= 1048576 ) { + return number_format( $bytes / 1048576, 2 ) . ' MB'; + } + + if( $bytes >= 1024 ) { + return number_format( $bytes / 1024, 2 ) . ' KB'; + } + + return $bytes . ' bytes'; + } + + +} \ No newline at end of file diff --git a/app/Jobs/FetchFilteredPrefixesForCustomer.php b/app/Jobs/FetchFilteredPrefixesForCustomer.php new file mode 100644 index 000000000..0182be6ca --- /dev/null +++ b/app/Jobs/FetchFilteredPrefixesForCustomer.php @@ -0,0 +1,186 @@ + + * @author Barry O'Donovan + * @category Jobs + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class FetchFilteredPrefixesForCustomer extends Job implements ShouldQueue +{ + use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; + + /** + * @var Customer + */ + protected $customer; + + /** + * @var array + */ + protected $filteredPrefixes = []; + + /** + * Create a new job instance. + * + * @param Customer $customer + * + * @return void + */ + public function __construct( Customer $customer ) + { + $this->customer = $customer; + } + + /** + * Execute the job. + * + * @param LookingGlass $lg + * + * @return void + * + * @throws GeneralException + */ + public function handle( LookingGlass $lg ): void + { + if( !$this->havePersistentCache() ) { + throw new GeneralException('A persistent cache is required to fetch filtered prefixes' ); + } + + + // so, find all vlan interfaces where this customer is configured for route server client + // them, for each router, get a list of filtered prefixes and record reason(s) and routers + + /** @var VlanInterface[] $vlis */ + $vlis = VlanInterface::select('vli.*') + ->from( 'vlaninterface AS vli' ) + ->leftJoin( 'virtualinterface AS vi', 'vi.id', 'vli.virtualinterfaceid' ) + ->leftJoin( 'cust AS c', 'c.id', 'vi.custid' ) + ->leftJoin( 'vlan AS v', 'v.id', 'vli.vlanid' ) + ->where( 'vli.rsclient', true ) + ->where( 'v.private', false ) + ->where( 'c.id', $this->customer->id ) + ->get(); + + foreach( $vlis as $vli ) { + // query routers for this VLAN + foreach( array_keys( Router::$PROTOCOLS ) as $ipproto ) { + if( $vli->ipvxEnabled( $ipproto ) ) { + $this->queryRouteServer( $lg, $vli, $ipproto ); + } + } + } + + // and jam the results into the cache + Cache::put('filtered-prefixes-' . $this->customer->id, $this->filteredPrefixes, 900); + } + + /** + * Query the various route servers for filtered prefixes and add them to the $this->filteredPrefixes array + * + * @param LookingGlass $lg + * @param VlanInterface $vli + * @param int $ipproto + * + * @return void + * + * @throws + */ + private function queryRouteServer( LookingGlass $lg, VlanInterface $vli, int $ipproto ): void + { + $bird_protocol = sprintf( "pb_%04d_as%d", $vli->id, $vli->virtualInterface->customer->autsys ); + + /** @var Router[] $routers */ + $routers = Router::where( 'vlan_id', $vli->vlan->id ) + ->where( 'protocol', $ipproto ) + ->notQuarantine() + ->hasApi() + ->routeServer() + ->largeCommunities() + ->get(); + + foreach( $routers as $router ) { + if( !( $resp = \json_decode( $lg->forRouter( $router )->routesProtocolLargeCommunityWildXYRoutes( $bird_protocol, $router->asn, 1101 ) ) ) || !isset( $resp->routes ) ) { + continue; + } + + foreach( $resp->routes as $route ) { + if( !isset( $this->filteredPrefixes[ $route->network ] ) ) { + $this->filteredPrefixes[ $route->network ] = []; + $this->filteredPrefixes[ $route->network ]['found_at'] = now(); + $this->filteredPrefixes[ $route->network ]['reasons'] = []; + } + + $this->filteredPrefixes[ $route->network ]['routers'][ $router->handle ] = $bird_protocol; + $this->filteredPrefixes[ $route->network ]['age'][ $router->handle ] = Carbon::parse( $route->age ); + $this->filteredPrefixes[ $route->network ]['primary'][ $router->handle ] = $route->primary; + $this->filteredPrefixes[ $route->network ]['as_path'][ $router->handle ] = $route->bgp->as_path; + $this->filteredPrefixes[ $route->network ]['gateway'][ $router->handle ] = $route->gateway; + $this->filteredPrefixes[ $route->network ]['learnt_from'][ $router->handle ] = $route->learnt_from; + $this->filteredPrefixes[ $route->network ]['communities'][ $router->handle ] = $route->bgp->communities ?? []; + $this->filteredPrefixes[ $route->network ]['lcommunities'][ $router->handle ] = $route->bgp->large_communities ?? []; + + foreach( $route->bgp->large_communities as $lc ) { + if( $lc[0] !== $router->asn || $lc[1] !== 1101 ) { + continue; + } + + $lc = implode( ':', $lc ); + if( !in_array( $lc, $this->filteredPrefixes[ $route->network ]['reasons'] ) ) { + $this->filteredPrefixes[ $route->network ]['reasons'][] = $lc; + } + } + } + } + } +} \ No newline at end of file diff --git a/app/Jobs/Job.php b/app/Jobs/Job.php new file mode 100644 index 000000000..8cac9c918 --- /dev/null +++ b/app/Jobs/Job.php @@ -0,0 +1,61 @@ + + * @author Barry O'Donovan + * @category Jobs + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +abstract class Job +{ + /* + |-------------------------------------------------------------------------- + | Queueable Jobs + |-------------------------------------------------------------------------- + | + | This job base class provides a central location to place any logic that + | is shared across all of your jobs. The trait included with the class + | provides access to the "onQueue" and "delay" queue helper methods. + | + */ + use Queueable; + + /** + * Check if we have access to a persistent cache + * + * @return bool + */ + protected function havePersistentCache(): bool + { + // we need a persistent cache or this is a waste of time + return !in_array( config( 'cache.default' ), [ 'array', 'none' ] ); + } +} \ No newline at end of file diff --git a/app/Jobs/RipeAtlas/CompleteRequests.php b/app/Jobs/RipeAtlas/CompleteRequests.php new file mode 100644 index 000000000..2b72fc38e --- /dev/null +++ b/app/Jobs/RipeAtlas/CompleteRequests.php @@ -0,0 +1,90 @@ + + * @author Barry O'Donovan + * @category IXP + * @package IXP\Jobs\RipeAtlas + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class CompleteRequests extends Job implements ShouldQueue +{ + use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; + + /** + * @var AtlasRun + */ + protected $atlasRun; + + /** + * Create a new job instance. + * + * @param AtlasRun $atlasRun + * @return void + */ + public function __construct( AtlasRun $atlasRun ) + { + $this->atlasRun = $atlasRun; + } + + /** + * Execute the job. + * + * @throws + * + * @psalm-return 0|1 + */ + public function handle(): int + { + $ams = $this->atlasRun->atlasMeasurements()->get(); + + // Does All the measurements are stopped ? + if( $ams->contains( 'atlas_stop', null ) ) { + return 0; + } + + $this->atlasRun->update( [ 'completed_at' => now() ] ); + return 1; + } +} \ No newline at end of file diff --git a/app/Jobs/RipeAtlas/CreateMeasurements.php b/app/Jobs/RipeAtlas/CreateMeasurements.php new file mode 100644 index 000000000..25a4a2afe --- /dev/null +++ b/app/Jobs/RipeAtlas/CreateMeasurements.php @@ -0,0 +1,101 @@ + + * @author Barry O'Donovan + * @category IXP + * @package IXP\Jobs\RipeAtlas + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class CreateMeasurements extends Job implements ShouldQueue +{ + use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; + + /** + * @var AtlasRun + */ + protected $atlasRun; + + /** + * @var array of customer + */ + protected $customers; + + /** + * Create a new job instance. + * + * @param AtlasRun $atlasRun + * @param array $customers + * + * @return void + */ + public function __construct( AtlasRun $atlasRun, array $customers = [] ) + { + $this->atlasRun = $atlasRun; + $this->customers = $customers; + } + + /** + * Execute the job. + * + * @return void + * + * @throws + */ + public function handle(): void + { + // find all candidate networks. I.e. networks with probes for this protocol + $networks = CustomerAggregator::withProbesForProtocol( $this->atlasRun->protocol, $this->atlasRun->vlan->id , $this->customers ); + + $networks->each( function ( $src ) use ( $networks ) { + $networks->each( function ( $dest ) use ( $src ) { + if( $src->id !== $dest->id ) { + AtlasMeasurement::create( [ 'run_id' => $this->atlasRun->id, 'cust_source' => $src->id, 'cust_dest' => $dest->id ] ); + } + }); + }); + } +} \ No newline at end of file diff --git a/app/Jobs/RipeAtlas/RunMeasurements.php b/app/Jobs/RipeAtlas/RunMeasurements.php new file mode 100644 index 000000000..91869b3c4 --- /dev/null +++ b/app/Jobs/RipeAtlas/RunMeasurements.php @@ -0,0 +1,103 @@ + + * @author Barry O'Donovan + * @category IXP + * @package IXP\Jobs\RipeAtlas + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class RunMeasurements extends Job implements ShouldQueue +{ + use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; + + /** + * @var AtlasMeasurement + */ + protected $atlasMeasurement; + + /** + * Create a new job instance. + * + * @param AtlasMeasurement $atlasMeasurement + * d + * @return void + */ + public function __construct( AtlasMeasurement $atlasMeasurement ) + { + $this->atlasMeasurement = $atlasMeasurement; + } + + /** + * Execute the job. + * + * @return void + * + * @throws + */ + public function handle(): void + { + $atlasRun = $this->atlasMeasurement->atlasRun; + $getAddress = $atlasRun->protocol === 4 ? 'address_v4' : 'address_v6'; + + // get a random atlas probe for the protocol and customer + $dprobe = AtlasProbe::forActiveProtocol( $atlasRun->protocol )->forCustomer( $this->atlasMeasurement->custDest->id )->get()->random(); + + $sourceAS = $this->atlasMeasurement->custSource->autsys; + $targetIP = $dprobe->$getAddress; + + +// if( $this->isVerbose() ) { +// $this->info( "Requesting measurement for {$this->atlasMeasurement->custSource->name} / {$this->atlasMeasurement->custDest->name}: {$sourceAS}/{$targetIP} IPv{$atlasRun->protocol}" ); +// } + + if( !$this->atlasMeasurement->atlas_id && ( $id = App::make(ApiCall::class )->requestAtlasTraceroute( $sourceAS, $targetIP, $atlasRun->protocol ) ) ) { + $this->atlasMeasurement->update( [ 'atlas_id' => $id, 'atlas_create' => now() ] ); + } + } +} \ No newline at end of file diff --git a/app/Jobs/RipeAtlas/StopAllMeasurements.php b/app/Jobs/RipeAtlas/StopAllMeasurements.php new file mode 100644 index 000000000..f1db1694b --- /dev/null +++ b/app/Jobs/RipeAtlas/StopAllMeasurements.php @@ -0,0 +1,86 @@ + + * @author Barry O'Donovan + * @category IXP + * @package IXP\Jobs\RipeAtlas + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class StopAllMeasurements extends Job implements ShouldQueue +{ + use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; + + /** + * @var int + */ + protected $atlas_id; + + /** + * Create a new job instance. + * + * @param int $atlas_id + * + * @return void + */ + public function __construct( int $atlas_id ) + { + $this->atlas_id = $atlas_id; + } + + /** + * Execute the job. + * + * @return void + * + * @throws + */ + public function handle(): void + { + App::make(ApiCall::class )->atlasStopMeasurement( $this->atlas_id ); + } +} \ No newline at end of file diff --git a/app/Jobs/RipeAtlas/UpdateMeasurements.php b/app/Jobs/RipeAtlas/UpdateMeasurements.php new file mode 100644 index 000000000..bbf4e2616 --- /dev/null +++ b/app/Jobs/RipeAtlas/UpdateMeasurements.php @@ -0,0 +1,141 @@ + + * @author Barry O'Donovan + * @category IXP + * @package IXP\Jobs\RipeAtlas + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class UpdateMeasurements extends Job implements ShouldQueue +{ + use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; + + /** + * @var AtlasMeasurement + */ + protected $atlasMeasurement; + + /** + * Create a new job instance. + * + * @param AtlasMeasurement $atlasMeasurement + * @return void + */ + public function __construct( AtlasMeasurement $atlasMeasurement ) + { + $this->atlasMeasurement = $atlasMeasurement; + } + + /** + * Execute the job. + * + * @return int + * + * @throws + * + * @psalm-return 0|1 + */ + public function handle(): int + { + if( !( $atlasId = $this->atlasMeasurement->atlas_id ) ) { + return 0; + } + + $measurement = App::make(ApiCall::class )->updateAtlasMeasurement( $atlasId )[ "response" ]; + + $this->atlasMeasurement->update( [ 'atlas_request' => json_encode( $measurement, JSON_THROW_ON_ERROR ) ] ); + + if( !$this->atlasMeasurement->atlas_start ){ + $this->atlasMeasurement->update( [ 'atlas_start' => now() ] ); + } + + if( isset( $measurement->status->name ) ) { + $this->atlasMeasurement->update( [ 'atlas_state' => $measurement->status->name ] ); + + if( $measurement->status->name === "Stopped" ) { + $this->atlasMeasurement->update( [ + 'atlas_stop' => now(), + 'atlas_data' => file_get_contents( "https://atlas.ripe.net/api/v2/measurements/" . $atlasId . '/results' ) + ] ); + + } else if( in_array( $measurement->status->name, [ "Failed", "No suitable probes" ] ) ) { + $this->atlasMeasurement->update( [ 'atlas_stop' => now() ] ); + } + } + + // if both in about out is complete with data, emit an event + if( $this->atlasMeasurement->atlas_stop && $this->atlasMeasurement->atlas_data ) { + // if( $this->isVerbose() ) { + // $this->info( 'Emitting measurement complete event for measurement ' . $atlasId ); + // } + + event( new MeasurementCompleteEvent( $this->atlasMeasurement ) ); + return 1; + } + + // after an hour, consider outstanding measurements as dead + if( $this->atlasMeasurement->atlas_start && !$this->atlasMeasurement->atlas_stop && Carbon::parse( $this->atlasMeasurement->atlas_start )->diffInMinutes( now() ) >= 120 ) { + // if( $this->isVerbose() ) { + // $this->info( 'Expiring in measurement ' . $atlasId ); + // } + + $this->atlasMeasurement->update( [ + 'atlas_stop' => now(), + 'atlas_state' => 'ABANDONNED' + ] ); + + App::make(ApiCall::class )->atlasStopMeasurement( $atlasId ); + } + + return 0; + } +} \ No newline at end of file diff --git a/app/Jobs/RipeAtlas/UpdateProbes.php b/app/Jobs/RipeAtlas/UpdateProbes.php new file mode 100644 index 000000000..00f7958a7 --- /dev/null +++ b/app/Jobs/RipeAtlas/UpdateProbes.php @@ -0,0 +1,162 @@ + + * @author Barry O'Donovan + * @category IXP + * @package IXP\Jobs\RipeAtlas + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class UpdateProbes extends Job implements ShouldQueue +{ + use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; + + /** + * @var Customer + */ + protected $customer; + + /** + * Create a new job instance. + * + * @param Customer $customer + * @return void + */ + public function __construct( Customer $customer ) + { + $this->customer = $customer; + } + + /** + * Execute the job. + * + * @return void + * + * @throws + */ + public function handle(): void + { + foreach( [ 4, 6 ] as $protocol ) { + $probes = App::make(ApiCall::class )->queryAtlasForProbes( $this->customer, $protocol ); + + if( $probes[ 'error' ] === true ) { + throw new GeneralException( $probes[ 'content' ] ); + } + + $probes = $probes[ 'response' ]; + + // fn names for later: + $attrVxEnabled = "v{$protocol}_enabled"; + $attrVxIp = "address_v{$protocol}"; + + if( !$probes->results ) { + // no probes - delete any if they exist + $this->customer->AtlasProbes->each( function( $ap ) use( $attrVxEnabled ) { + if( $ap->$attrVxEnabled ) { + $ap->update( [ $attrVxEnabled => false ] ); + //$this->comment( "Removed 'gone away' probe {$p->atlas_id} for { $this->customer->getName()} - IPv{$protocol}" ); + } + + if( !$ap->v4_enabled && !$ap->v6_enabled ) { + $ap->delete(); + } + }); + + } else { + $isNew = false; + foreach( $probes->results as $probe ) { + if( !$ap = AtlasProbe::forCustomer( $this->customer->id )->forAtlas( $probe->id )->first() ) { + // probe not in database + $ap = AtlasProbe::create([ + 'cust_id' => $this->customer->id, + 'atlas_id' => $probe->id, + 'v4_enabled' => false, + 'v6_enabled' => false, + ]); + + $isNew = true; + + //$this->info("Adding probe {$p->getAtlasId()} for {$network->getName()} - IPv{$protocol}" ); + } + + $old = $ap->$attrVxEnabled; + $key = 'address_v' . $protocol; + + $ap->update([ + $attrVxIp => $probe->$key, + 'address_v4' => $probe->address_v4, + 'address_v6' => $probe->address_v6, + 'is_anchor' => $probe->is_anchor, + 'is_public' => $probe->is_public, + 'status' => $probe->status->name, + 'api_data' => json_encode($probe, JSON_THROW_ON_ERROR), + $attrVxEnabled => true, + 'asn' => $probe->asn_v4, + 'last_connected' => new Carbon( $probe->last_connected ), + ]); + + // now make sure it /really/ works + foreach( $probe->tags as $tag ) { + if( $tag->slug === "system-ipv{$protocol}-doesnt-work" ) { + $ap->update( [ $attrVxEnabled => false ] ); + break; + } + } + + if( $old !== $ap->$attrVxEnabled && !$isNew ) { + //$this->comment("Updated probe {$ap->atlas_id} for {$this->customer->getName()} - IPv{$protocol}" ); + } + } + } + } + } +} \ No newline at end of file diff --git a/app/Jobs/UpdateIrrdb.php b/app/Jobs/UpdateIrrdb.php new file mode 100644 index 000000000..7b03fa4f0 --- /dev/null +++ b/app/Jobs/UpdateIrrdb.php @@ -0,0 +1,115 @@ + + * @author Barry O'Donovan + * @category Jobs + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class UpdateIrrdb extends Job implements ShouldQueue +{ + use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; + + /** + * @var CustomerModel + */ + protected $customer; + + /** + * @var String + */ + protected $type; + + /** + * @var int + */ + protected $proto; + + /** + * Create a new job instance. + * + * @param CustomerModel $customer + * @param string $type + * @param int $proto + * + * @return void + */ + public function __construct( CustomerModel $customer, string $type, int $proto ) + { + $this->customer = $customer; + $this->type = $type; + $this->proto = $proto; + } + + /** + * Execute the job. + * + * @return void + * + * @throws + */ + public function handle(): void + { + if( !$this->havePersistentCache() ) { + throw new GeneralException('A persistent cache is required to fetch filtered prefixes' ); + } + + Cache::put( 'updating-irrdb-' . $this->type . '-' . $this->proto . '-' . $this->customer->id, true, 3600 ); + + $updater = $this->type === "asn" ? new UpdateAsnDb( $this->customer ) : new UpdatePrefixDb( $this->customer ); + + $result = $updater->update(); + $result[ "found_at" ] = now(); + + Cache::put( 'updated-irrdb-' . $this->type . '-' . $this->proto . '-' . $this->customer->id, $result , 900 ); + Cache::put( 'updating-irrdb-' . $this->type . '-' . $this->proto . '-' . $this->customer->id, false, 3600 ); + } +} \ No newline at end of file diff --git a/app/Listeners/Auth/ForgotPassword.php b/app/Listeners/Auth/ForgotPassword.php new file mode 100644 index 000000000..3c96cbd02 --- /dev/null +++ b/app/Listeners/Auth/ForgotPassword.php @@ -0,0 +1,59 @@ + + * @author Yann Robin + * @category Listeners + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class ForgotPassword +{ + /** + * Create the event listener. + * + * @return void + */ + public function __construct(){} + + /** + * Handle the event. + * + * @param ForgotPasswordEvent $e + * + * @return void + */ + public function handle( ForgotPasswordEvent $e ): void + { + Mail::to( $e->user->email )->send( new ForgotPasswordMailable( $e->token, $e->user ) ); + } +} \ No newline at end of file diff --git a/app/Listeners/Auth/ForgotUsername.php b/app/Listeners/Auth/ForgotUsername.php new file mode 100644 index 000000000..efc8d47e3 --- /dev/null +++ b/app/Listeners/Auth/ForgotUsername.php @@ -0,0 +1,60 @@ + + * @author Yann Robin + * @category Listeners\Auth + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class ForgotUsername +{ + /** + * Create the event listener. + * + * @return void + */ + public function __construct(){} + + /** + * Handle the event. + * + * @param ForgotUsernameEvent $e + * + * @return void + */ + public function handle( ForgotUsernameEvent $e ): void + { + Mail::to( $e->email ) + ->send( new ForgotPasswordMailable( $e->users ) ); + } +} \ No newline at end of file diff --git a/app/Listeners/Auth/Google2FALoginSucceeded.php b/app/Listeners/Auth/Google2FALoginSucceeded.php new file mode 100644 index 000000000..92c0822f5 --- /dev/null +++ b/app/Listeners/Auth/Google2FALoginSucceeded.php @@ -0,0 +1,65 @@ + + * @author Yann Robin + * @category Listeners\Auth + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Google2FALoginSucceeded +{ + /** + * Handle a Google2FA login event. + * + * What we need to do here is, if this is a 'remember me' session, store the fact that the user has + * authenticated with Google2FA in the database. If we do not record this (and subsequently check it) it + * is possibly for a user to avoid 2da by deleting the session cookie and forcing a remember me login. + * + * @param LoginSucceeded $e + * + * @return void + */ + public function handle( LoginSucceeded $e ): void + { + if( $r = request()->cookies->get( Auth::getRecallerName() ) ) { + $recaller = new Recaller( $r ); + $urt = UserRememberToken::where( 'token', $recaller->token() )->first(); + + if( $urt && !$urt->is_2fa_complete ) { + $urt->update( [ 'is_2fa_complete' => true ] ); + } + } + } +} \ No newline at end of file diff --git a/app/Listeners/Auth/LoginFailed.php b/app/Listeners/Auth/LoginFailed.php new file mode 100644 index 000000000..c162bc08b --- /dev/null +++ b/app/Listeners/Auth/LoginFailed.php @@ -0,0 +1,51 @@ + + * @author Yann Robin + * @category Listeners + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class LoginFailed +{ + /** + * Handle a failed login event. + * + * @param FailedEvent $e + * + * @return void + */ + public function handle( FailedEvent $e ): void + { + // TODO: Maybe we should persist failed logs into the DB instead and create a view in the backend + Log::warning( 'Login failed for user [' . $e->credentials[ 'username' ] . '] from IP [' . ixp_get_client_ip() . ']' ); + } +} \ No newline at end of file diff --git a/app/Listeners/Auth/LoginSuccessful.php b/app/Listeners/Auth/LoginSuccessful.php new file mode 100644 index 000000000..de1093845 --- /dev/null +++ b/app/Listeners/Auth/LoginSuccessful.php @@ -0,0 +1,73 @@ + + * @author Yann Robin + * @category Listeners + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class LoginSuccessful +{ + /** + * Handle a login event. + * + * @param LoginEvent $e + * + * @return void + */ + public function handle( LoginEvent $e ): void + { + /** @var User $user */ + $user = $e->user; + + Log::notice( 'Login successful for user "' . $user->username. '" from IP ' . ixp_get_client_ip() . '.' ); + + if( !session()->exists( "switched_user_from" ) && ( $c2u = $user->currentCustomerToUser() ) ) { + $c2u->update( [ + 'last_login_date' => now(), + 'last_login_from' => ixp_get_client_ip(), + 'last_login_via' => Auth::viaRemember() ? 'RememberMe' : 'Login', + ] ); + + if( config( "ixp_fe.login_history.enabled" ) ) { + UserLoginHistory::create( [ + 'ip' => ixp_get_client_ip(), + 'at' => now(), + 'via' => Auth::viaRemember() ? 'RememberMe' : 'Login', + 'customer_to_user_id' => $c2u->id, + ] ); + } + } + } +} \ No newline at end of file diff --git a/app/Listeners/Auth/PasswordReset.php b/app/Listeners/Auth/PasswordReset.php new file mode 100644 index 000000000..2457da9e4 --- /dev/null +++ b/app/Listeners/Auth/PasswordReset.php @@ -0,0 +1,59 @@ + + * @author Yann Robin + * @category Listeners + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class PasswordReset +{ + /** + * Create the event listener. + * + * @return void + */ + public function __construct() {} + + /** + * Handle the event. + * + * @param PasswordResetEvent $e + * + * @return void + */ + public function handle( PasswordResetEvent $e ): void + { + Mail::to( $e->user->email )->send( new PasswordResetMailable( $e->user ) ); + } +} \ No newline at end of file diff --git a/app/Listeners/Customer/BillingDetailsChanged.php b/app/Listeners/Customer/BillingDetailsChanged.php new file mode 100644 index 000000000..de0a066c7 --- /dev/null +++ b/app/Listeners/Customer/BillingDetailsChanged.php @@ -0,0 +1,55 @@ +ocbd->customer->resellerObject()->exists() ) { + return; + } + + Mail::to( config( 'ixp_fe.customer.billing_updates_notify' ) )->send( new BillingDetailsChangedMailable( $e->ocbd, $e->cbd ) ); + } +} \ No newline at end of file diff --git a/app/Listeners/Customer/Note/EmailOnChange.php b/app/Listeners/Customer/Note/EmailOnChange.php new file mode 100644 index 000000000..138cc8462 --- /dev/null +++ b/app/Listeners/Customer/Note/EmailOnChange.php @@ -0,0 +1,174 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Listener\Customer\Note + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class EmailOnChange +{ + /** + * Handle customer note added + * + * @param $event + * + * @return void + */ + public function onCreatedNote( $event ) : void + { + $this->handle( $event ); + } + + /** + * Handle customer note edited + * + * @param $event + * + * @return void + */ + public function onEditedNote( $event ): void + { + $this->handle( $event ); + } + + /** + * Handle customer note deleted + * + * @param $event + * + * @return void + */ + public function onDeletedNote( $event ): void + { + $this->handle( $event ); + } + + /** + * Register the listeners for the subscriber. + * + * @param Dispatcher $events + */ + public function subscribe( Dispatcher $events ): void + { + $events->listen( + Created::class, + 'IXP\Listeners\Customer\Note\EmailOnChange@onCreatedNote' + ); + + $events->listen( + Edited::class, + 'IXP\Listeners\Customer\Note\EmailOnChange@onEditedNote' + ); + + $events->listen( + Deleted::class, + 'IXP\Listeners\Customer\Note\EmailOnChange@onDeletedNote' + ); + } + + + /** + * Create the event listener. + * + * @return void + */ + public function __construct(){} + + /** + * Handle the event. + * + * @param NoteChangedEvent $e + * + * @return void + */ + public function handle( $e ): void + { + if( config( 'ixp_fe.customer.notes.only_send_to' ) ) { + $to = [ config( 'ixp_fe.customer.notes.only_send_to' ) ]; + } else { + // get admin users + $c2us = CustomerToUser::from( 'customer_to_users AS c2u' ) + ->leftJoin( 'user AS u', 'u.id', 'c2u.user_id' ) + ->where( 'c2u.privs', User::AUTH_SUPERUSER ) + ->where( 'u.disabled', false ) + ->get(); + $to = []; + + foreach( $c2us as $c2u ) { + $user = $c2u->user;/** @var $user User */ + if( isset( $user->prefs[ 'notes' ][ 'global_notifs' ] ) && $user->prefs[ 'notes' ][ 'global_notifs' ] === 'none' ){ + continue; + } + + if( !$user->email || filter_var( $user->email , FILTER_VALIDATE_EMAIL ) === false ) { + continue; + } + + if( !isset( $user->prefs[ 'notes' ][ 'global_notifs' ] ) || $user->prefs[ 'notes' ][ 'global_notifs' ] === 'default' || $user->prefs[ 'notes' ][ 'global_notifs' ] === 'all' ) { + $to[] = [ 'name' => $user->username, 'email' => $user->email ]; + continue; + } + + // watching a whole customer + if( isset( $user->prefs[ 'notes' ][ 'customer_watching' ][ $e->customer()->id ] ) ) { + $to[] = [ 'name' => $user->username, 'email' => $user->email ]; + continue; + } + + // watching a specific note: customer-notes.watching.{note id} + if( isset( $user->prefs[ 'notes' ][ 'note_watching' ][ $e->eitherNote()->id ] ) ) { + $to[] = [ 'name' => $user->username, 'email' => $user->email ]; + continue; + } + // so, skip this user then. + } + } + + if( count( $to ) ) { + Mail::to( $to )->send( new CustomerNoteChangedMailable( $e ) ); + } + } +} \ No newline at end of file diff --git a/app/Listeners/Layer2Address/Changed.php b/app/Listeners/Layer2Address/Changed.php new file mode 100644 index 000000000..dd1141183 --- /dev/null +++ b/app/Listeners/Layer2Address/Changed.php @@ -0,0 +1,73 @@ + + * @author Yann Robin + * @category Layer2Address\Listeners + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Changed implements ShouldQueue +{ + /** + * Create the event listener. + * + * @return void + */ + public function __construct(){} + + /** + * Handle the event. + * + * @param Layer2AddressAddedEvent|Layer2AddressDeletedEvent $e + * + * @return void + */ + public function handle( $e ): void + { + if( !( config( 'ixp_fe.layer2-addresses.email_on_superuser_change' ) || config( 'ixp_fe.layer2-addresses.email_on_customer_change' ) ) ) { + return; + } + + if( !config( 'ixp_fe.layer2-addresses.email_on_superuser_change' ) && $e->user->isSuperUser() ) { + return; + } + + Mail::to( config( 'ixp_fe.layer2-addresses.email_on_change_dest' ) )->send( new Layer2AddressChangedMail( $e ) ); + } +} \ No newline at end of file diff --git a/app/Listeners/RipeAtlas/MeasurementComplete.php b/app/Listeners/RipeAtlas/MeasurementComplete.php new file mode 100644 index 000000000..2ccc96a35 --- /dev/null +++ b/app/Listeners/RipeAtlas/MeasurementComplete.php @@ -0,0 +1,64 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Listeners\RipeAtlas + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class MeasurementComplete +{ + /** + * Create the event listener. + * + * @return void + */ + public function __construct(){} + + public function handle( MeasurementCompleteEvent $event ): void + { + // get the measurement ORM entity + $am = $event->atlasMeasurement; + + // if there's already a result, delete it as this is a re-run + if( $r = $am->atlasResult() ) { + $r->delete(); + } + + // interpret the measurement + $result = App::make(Interpretor::class )->interpret( $am ); + + $result->update( [ 'measurement_id' => $am->id ] ); + } +} \ No newline at end of file diff --git a/app/Listeners/User/SendNewUserWelcomeEmail.php b/app/Listeners/User/SendNewUserWelcomeEmail.php new file mode 100644 index 000000000..40f527eb0 --- /dev/null +++ b/app/Listeners/User/SendNewUserWelcomeEmail.php @@ -0,0 +1,59 @@ + + * @author Yann Robin + * @category Listeners\User + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class SendNewUserWelcomeEmail +{ + /** + * Create the event listener. + * + * @return void + */ + public function __construct(){} + + /** + * Handle the event. + * + * @param UserCreatedEvent $e + * + * @return void + */ + public function handle( UserCreatedEvent $e ): void + { + Mail::to( $e->user->email )->send( new UserCreatedMailable( $e->user, false ) ); + } +} \ No newline at end of file diff --git a/app/Listeners/User/SendUserAddedToCustomerWelcomeEmail.php b/app/Listeners/User/SendUserAddedToCustomerWelcomeEmail.php new file mode 100644 index 000000000..f7e07df53 --- /dev/null +++ b/app/Listeners/User/SendUserAddedToCustomerWelcomeEmail.php @@ -0,0 +1,59 @@ + + * @author Yann Robin + * @category Listeners\User + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class SendUserAddedToCustomerWelcomeEmail +{ + /** + * Create the event listener. + * + * @return void + */ + public function __construct() {} + + /** + * Handle the event. + * + * @param UserAddedToCustomerEvent $e + * + * @return void + */ + public function handle( UserAddedToCustomerEvent $e ): void + { + Mail::to( $e->c2u->user->email )->send( new UserAddedToCustomerMailable( $e->c2u ) ); + } +} \ No newline at end of file diff --git a/app/Mail/Alert.php b/app/Mail/Alert.php new file mode 100644 index 000000000..2d2a88055 --- /dev/null +++ b/app/Mail/Alert.php @@ -0,0 +1,61 @@ + + * @author Laszlo Kiss + * @category User + * @package IXP\Mail + * @copyright Copyright (C) 2009 - 2025 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Alert extends Mailable +{ + use Queueable, SerializesModels; + + /** + * Create a new message instance. + **/ + public function __construct( + public string $alert, + public \Exception $exception, + ) {} + + /** + * Build the message. + * + * @return $this + */ + public function build(): self + { + return $this->markdown( 'user.emails.alert' ) + ->subject( config('identity.sitename' ) . " - Alert" ); + } +} \ No newline at end of file diff --git a/app/Mail/Auth/ForgotPassword.php b/app/Mail/Auth/ForgotPassword.php new file mode 100644 index 000000000..dee39e73c --- /dev/null +++ b/app/Mail/Auth/ForgotPassword.php @@ -0,0 +1,79 @@ + + * @author Yann Robin + * @category Customer + * @package IXP\Mail\Auth + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class ForgotPassword extends Mailable +{ + use Queueable, SerializesModels; + + /** + * @var User + */ + public $user; + + /** + * @var string + */ + public $token; + + /** + * Create a new message instance. + * + * @param string $token + * @param User $user + * + */ + public function __construct( string $token, User $user ) + { + $this->token = $token; + $this->user = $user; + } + + /** + * Build the message. + * + * @return $this + */ + public function build(): self + { + return $this->markdown( 'auth/emails/forgot-password' ) + ->subject( config('identity.sitename' ) . " - Password Reset Information " ); + } +} \ No newline at end of file diff --git a/app/Mail/Auth/ForgotUsername.php b/app/Mail/Auth/ForgotUsername.php new file mode 100644 index 000000000..594964f18 --- /dev/null +++ b/app/Mail/Auth/ForgotUsername.php @@ -0,0 +1,70 @@ + + * @author Yann Robin + * @category Customer + * @package IXP\Mail\Auth + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class ForgotUsername extends Mailable +{ + use Queueable, SerializesModels; + + /** + * @var [] + */ + public $users; + + /** + * Create a new message instance. + * + * @param Collection $users + * + */ + public function __construct( Collection $users ) + { + $this->users = $users; + } + + /** + * Build the message. + * + * @return $this + */ + public function build(): self + { + return $this->markdown( 'auth/emails/forgot-username' ) + ->subject( config('identity.sitename' ) . " - Your Accounts " ); + } +} \ No newline at end of file diff --git a/app/Mail/Auth/PasswordReset.php b/app/Mail/Auth/PasswordReset.php new file mode 100644 index 000000000..3c815b043 --- /dev/null +++ b/app/Mail/Auth/PasswordReset.php @@ -0,0 +1,71 @@ + + * @author Yann Robin + * @category Customer + * @package IXP\Mail\Auth + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class PasswordReset extends Mailable +{ + use Queueable, SerializesModels; + + /** + * @var User + */ + public $user; + + /** + * Create a new message instance. + * + * @param User $user + * + */ + public function __construct( User $user ) + { + $this->user = $user; + } + + /** + * Build the message. + * + * @return $this + */ + public function build(): self + { + return $this->markdown( 'auth/emails/password-reset' ) + ->subject( config('identity.sitename' ) . " - Your Password Has Been Reset" ); + } +} \ No newline at end of file diff --git a/app/Mail/Customer/BillingDetailsChanged.php b/app/Mail/Customer/BillingDetailsChanged.php new file mode 100644 index 000000000..55e02f2cb --- /dev/null +++ b/app/Mail/Customer/BillingDetailsChanged.php @@ -0,0 +1,83 @@ + + * @author Yann Robin + * @category Customer + * @package IXP\Mail\Customer + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class BillingDetailsChanged extends Mailable +{ + use Queueable, SerializesModels; + + /** + * Old/original details + * + * @var CompanyBillingDetail + */ + public $ocbd; + + /** + * New details + * + * @var CompanyBillingDetail + */ + public $cbd; + + /** + * Create a new message instance. + * + * @param CompanyBillingDetail $ocbd + * @param CompanyBillingDetail $cbd + * + * @return void + */ + public function __construct( CompanyBillingDetail $ocbd, CompanyBillingDetail $cbd ) + { + $this->ocbd = $ocbd; + $this->cbd = $cbd; + } + + /** + * Build the message. + * + * @return $this + */ + public function build(): self + { + return $this->markdown( 'customer.emails.billing-details-changed' ) + ->subject( env('IDENTITY_NAME') . " :: Updated Billing Details for " . $this->cbd->customer->getFormattedName() ); + } +} \ No newline at end of file diff --git a/app/Mail/Customer/Note/Changed.php b/app/Mail/Customer/Note/Changed.php new file mode 100644 index 000000000..3c0cfbbaa --- /dev/null +++ b/app/Mail/Customer/Note/Changed.php @@ -0,0 +1,75 @@ + + * @author Yann Robin + * @category Customer + * @package IXP\Mail\Customer + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Changed extends Mailable +{ + use Queueable, SerializesModels; + + /** + * + * @var CustomerNoteChangedEvent + */ + public $event; + + /** + * Create a new message instance. + * + * @param CustomerNoteChangedEvent $e + * + * @return void + */ + public function __construct( CustomerNoteChangedEvent $e ) + { + $this->event = $e; + } + + /** + * Build the message. + * + * @return $this + */ + public function build() + { + $cust = $this->event->note() ? $this->event->note()->customer : $this->event->oldNote()->customer; + return $this->markdown( 'customer.emails.note-changed' ) + ->subject( env('IDENTITY_NAME') . " :: Customer Notes :: " . $cust->getFormattedName() ); + } +} \ No newline at end of file diff --git a/app/Mail/Customer/WelcomeEmail.php b/app/Mail/Customer/WelcomeEmail.php new file mode 100644 index 000000000..b44d78437 --- /dev/null +++ b/app/Mail/Customer/WelcomeEmail.php @@ -0,0 +1,169 @@ + + * @author Yanm Robin + * @category Customer + * @package IXP\Mail\Customer + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class WelcomeEmail extends Mailable +{ + use Queueable, SerializesModels; + + /** + * @var Customer + */ + public $c; + + /** + * @var WelcomeEmailRequest + */ + public $r; + + /** + * @var string The template to use to create the email + */ + protected $tmpl; + + /** + * @var string Temporary view file + */ + protected $tmpfile; + + /** + * @var string Temporary view name + */ + protected $tmpname; + + /** + * Create a new message instance. + * + * @param Customer $c + * @param WelcomeEmailRequest $r + */ + public function __construct( Customer $c, WelcomeEmailRequest $r ) + { + $this->c = $c; + $this->r = $r; + $this->prepareFromRequest($r); + $this->prepareBody($r); + } + + /** + * Destructor + */ + public function __destruct() + { + // remove temporary file if it exists + if( $this->tmpfile && file_exists( $this->tmpfile ) ){ + @unlink( $this->tmpfile ); + } + } + + /** + * Set up recipients and subject from a POST request. + * + * @param WelcomeEmailRequest $r + */ + protected function prepareFromRequest( WelcomeEmailRequest $r ): static + { + // recipients + foreach( [ 'to', 'cc', 'bcc' ] as $p ) { + $hasFn = 'has' . ucfirst( $p ); + foreach( explode(',', $r->input( $p ) ) as $emaddr ) { + $email = trim( $emaddr ); + if( filter_var( $email, FILTER_VALIDATE_EMAIL ) ) { + $this->$p($email); + } + } + } + + $this->subject( $r->subject ); + return $this; + } + + /** + * Set up Markdown body from a POST request. + * + * @param WelcomeEmailRequest $r + */ + public function prepareBody( WelcomeEmailRequest $r ): static + { + // Templating is slightly awkward here as Laravel's Mailable is built around reading the + // body from a template file be we have it via post. + // + // To work around this, we'll use a temporary file in a new view namespace. + + $body = $r->message; + $this->tmpfile = tempnam( sys_get_temp_dir(), 'welcome_email_' ); + $this->tmpname = basename( $this->tmpfile ); + $this->tmpfile = $this->tmpfile . '.blade.php'; + file_put_contents( $this->tmpfile, "@component('mail::message')\n\n" . $body . "\n\n@endcomponent\n" ); + view()->addNamespace('welcome_emails', sys_get_temp_dir() ); + $this->markdown( 'welcome_emails::' . $this->tmpname ); + return $this; + } + + /** + * Build the message. + * + * @return $this + */ + public function build(): self + { + return $this; + } + + /** + * Checks if we can send the email + * + * @throws MailableException + */ + public function checkIfSendable(): void + { + if( !count( $this->to ) ) { + throw new MailableException( "No valid recipients" ); + } + + if( !view()->exists( $this->markdown ) ) { + throw new MailableException( "Could not create / load temporary template" ); + } + } +} \ No newline at end of file diff --git a/app/Mail/Grapher/PortUtilisation.php b/app/Mail/Grapher/PortUtilisation.php new file mode 100644 index 000000000..5707625bc --- /dev/null +++ b/app/Mail/Grapher/PortUtilisation.php @@ -0,0 +1,74 @@ + + * @category Grapher + * @package IXP\Mail\Grapher + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class PortUtilisation extends Mailable +{ + use Queueable, SerializesModels; + + /** + * @var array + */ + public $excess = []; + + /** + * @var float + */ + public $threshold; + + /** + * Create a new message instance. + * + * @param array $excess + * @param float $threshold + */ + public function __construct( array $excess, float $threshold ) + { + $this->excess = $excess; + $this->threshold = $threshold; + } + + /** + * Build the message. + * + * @return $this + */ + public function build(): self + { + return $this->view('services.grapher.email.port-utilisation') + ->subject( env('IDENTITY_NAME') . " :: Ports Utilisation Report" ); + } +} \ No newline at end of file diff --git a/app/Mail/Grapher/PortsWithCounts.php b/app/Mail/Grapher/PortsWithCounts.php new file mode 100644 index 000000000..581238072 --- /dev/null +++ b/app/Mail/Grapher/PortsWithCounts.php @@ -0,0 +1,77 @@ + + * @category Grapher + * @package IXP\Mail\Grapher + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class PortsWithCounts extends Mailable +{ + use Queueable, SerializesModels; + + /** + * @var array + */ + public $ports; + + /** + * @var string + */ + public $category; + + /** + * Create a new message instance. + * + * @param array $ports + * @param string $category + */ + public function __construct( array $ports, string $category ) + { + $this->ports = $ports; + $this->category = $category; + } + + /** + * Build the message. + * + * @return $this + */ + public function build(): self + { + $c = Graph::resolveCategory( $this->category ); + return $this->view('services.grapher.email.ports-with-counts') + ->subject( env('IDENTITY_NAME') . " :: Ports with " . $c ) + ->with( 'categoryDesc', $c ); + } +} \ No newline at end of file diff --git a/app/Mail/Grapher/TrafficDeltas.php b/app/Mail/Grapher/TrafficDeltas.php new file mode 100644 index 000000000..c3d055275 --- /dev/null +++ b/app/Mail/Grapher/TrafficDeltas.php @@ -0,0 +1,84 @@ + + * @category Grapher + * @package IXP\Mail\Grapher + * @copyright Copyright (C) 2009 - 2019 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class TrafficDeltas extends Mailable +{ + use Queueable, SerializesModels; + + /** + * @var array + */ + public $ports; + + /** + * @var float + */ + public $stddev; + + /** + * @var Carbon + */ + public $day; + + /** + * Create a new message instance. + * + * @param array $ports + * @param float $stddev + * @param Carbon $day + */ + public function __construct( array $ports, float $stddev, Carbon $day ) + { + $this->ports = $ports; + $this->stddev = $stddev; + $this->day = $day; + } + + /** + * Build the message. + * + * @return $this + */ + public function build(): self + { + return $this->view('services.grapher.email.traffic-deltas') + ->subject( env('IDENTITY_NAME') . " :: Traffic Deltas Report" ); + } +} diff --git a/app/Mail/Layer2Address/ChangedMail.php b/app/Mail/Layer2Address/ChangedMail.php new file mode 100644 index 000000000..9d2057057 --- /dev/null +++ b/app/Mail/Layer2Address/ChangedMail.php @@ -0,0 +1,74 @@ + + * @author Yanm Robin + * @category Customer + * @package IXP\Mail\Customer + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class ChangedMail extends Mailable +{ + use Queueable, SerializesModels; + + /** + * @var Layer2AddressAddedEvent|Layer2AddressDeletedEvent + */ + public $event; + + /** + * Create a new message instance. + * + * @param Layer2AddressAddedEvent|Layer2AddressDeletedEvent $e + */ + public function __construct( $e ) + { + $this->event = $e; + } + + /** + * Build the message. + * + * @return $this + */ + public function build() + { + return $this->markdown( 'layer2-address.emails.changed' ) + ->subject( env('IDENTITY_NAME') . " :: Layer2 / MAC Address Changed for " . $this->event->customer ); + } +} \ No newline at end of file diff --git a/app/Mail/PatchPanelPort/Cease.php b/app/Mail/PatchPanelPort/Cease.php new file mode 100644 index 000000000..9f0055033 --- /dev/null +++ b/app/Mail/PatchPanelPort/Cease.php @@ -0,0 +1,52 @@ + + * @author Yann Robin + * @category PatchPanel + * @package IXP\Mail\PatchPanelPort + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Cease extends Email +{ + /** + * Create a new message instance. + * + * @param PatchPanelPort $ppp + */ + public function __construct( PatchPanelPort $ppp ) + { + parent::__construct( $ppp ) ; + $this->subject = "Cease cross connect to " . env('IDENTITY_ORGNAME') . " [" . $ppp->patchPanel->colo_reference . " / " . $ppp->name() . "]"; + $this->tmpl = 'patch-panel-port/emails/cease'; + } +} diff --git a/app/Mail/PatchPanelPort/Connect.php b/app/Mail/PatchPanelPort/Connect.php new file mode 100644 index 000000000..e174cc450 --- /dev/null +++ b/app/Mail/PatchPanelPort/Connect.php @@ -0,0 +1,51 @@ + + * @author Yann Robin + * @category PatchPanel + * @package IXP\Mail\PatchPanelPort + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Connect extends Email +{ + /** + * Create a new message instance. + * + * @param PatchPanelPort $ppp + */ + public function __construct( PatchPanelPort $ppp ) + { + parent::__construct( $ppp ); + $this->subject = "Cross connect to " . env('IDENTITY_ORGNAME' ) . " [" . $ppp->patchPanel->colo_reference . " / " . $ppp->name() . "]"; + $this->tmpl = 'patch-panel-port/emails/connect'; + } +} diff --git a/app/Mail/PatchPanelPort/Email.php b/app/Mail/PatchPanelPort/Email.php new file mode 100644 index 000000000..3170bfd44 --- /dev/null +++ b/app/Mail/PatchPanelPort/Email.php @@ -0,0 +1,224 @@ + + * @author Yann Robin + * @category PatchPanel + * @package IXP\Mail\PatchPanelPort + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +abstract class Email extends Mailable +{ + use Queueable, SerializesModels; + + /** + * @var PatchPanelPort + */ + public $ppp; + + /** + * @var string + */ + public $subject; + + /** + * @var string The template to use to create the email + */ + protected $tmpl; + + /** + * @var string Temporary view file + */ + protected $tmpfile; + + /** + * @var string Temporary view name + */ + protected $tmpname; + + /** + * Create a new message instance. + * + * @param PatchPanelPort $ppp + */ + public function __construct( PatchPanelPort $ppp ) + { + $this->ppp = $ppp; + + if( $c = $this->ppp->customer ) { + $this->to( $c->nocemail, $c->abbreviatedName . ' NOC' ); + } + + $this->bcc( env( 'IDENTITY_SUPPORT_EMAIL' ), env( 'IDENTITY_NAME') . ' Operations' ); + } + + /** + * Destructor + */ + public function __destruct() + { + // remove temporary file if it exists + if( $this->tmpfile && file_exists( $this->tmpfile ) ){ + @unlink( $this->tmpfile ); + } + } + + /** + * Build the message. + * + * @return $this + */ + public function build(): self + { + return $this; + } + + /** + * Get the email addresses for to / cc / bcc + * + * @param string $recipientClass + * + * @return array Array of email addresses + * + * @psalm-return list{0?: mixed} + */ + public function getRecipientEmails( string $recipientClass ): array + { + assert( in_array( $recipientClass, ['to','cc','bcc'] ) ); + + $a = []; + foreach( $this->$recipientClass as $t ) { + $a = [ $t['address'] ]; + } + + return $a; + } + + /** + * Get the subject for an email + * + * @return string The subject for an email + */ + public function getSubject(): string + { + return $this->subject; + } + + /** + * Get the email's body + * + * For this we assume Markdown and return the template as is (with + * rendered data). + * + * @return string The Email's body + */ + public function getBody(): string + { + return view( $this->tmpl )->with( $this->buildViewData() )->render(); + } + + /** + * Set up recipients and subject from a POST request. + * + * @param EmailPatchPanelPortRequest $request + * + * @return void + */ + public function prepareFromRequest( EmailPatchPanelPortRequest $request ): void + { + // in the constructor, we add the NOC address of the customer (if we have one) + // to the recipients. This is for presetting the To: field in the HTML form. + // we wipe there here and just use what was submitted in the form. + $this->to = []; + + // recipients + foreach( [ 'to', 'cc', 'bcc' ] as $r ) { + $hasFn = 'has' . ucfirst( $r ); + foreach( explode(',', $request->input('email_' . $r ) ) as $emaddr ) { + $email = trim( $emaddr ); + if( filter_var( $email, FILTER_VALIDATE_EMAIL ) && !$this->$hasFn( $email ) ) { + $this->$r($email); + } + } + } + + $this->subject( $request->email_subject ); + } + + /** + * Set up Markdown body from a POST request. + * + * @param EmailPatchPanelPortRequest $request + * + * @return void + */ + public function prepareBody( EmailPatchPanelPortRequest $request ): void + { + // Templating is slightly awkward here as Laravel's Mailable is built around reading the + // body from a template file be we have it via post. + // + // To work around this, we'll use a temporary file in a new view namespace. + + $body = $request->email_text; + $this->tmpfile = tempnam( sys_get_temp_dir(), 'ppp_email_' ); + $this->tmpname = basename( $this->tmpfile ); + $this->tmpfile .= '.blade.php'; + file_put_contents( $this->tmpfile, "@component('mail::message')\n\n" . $body . "\n\n@endcomponent\n" ); + view()->addNamespace('ppp_emails', sys_get_temp_dir() ); + $this->markdown( 'ppp_emails::' . $this->tmpname ); + } + + /** + * Checks if we can send the email + * + * @return void + * + * @throws MailableException + */ + public function checkIfSendable(): void + { + if( !count( $this->to ) ) { + throw new MailableException( "No valid recipients" ); + } + + if( !view()->exists( $this->markdown ) ) { + throw new MailableException( "Could not create / load temporary template" ); + } + } +} \ No newline at end of file diff --git a/app/Mail/PatchPanelPort/Info.php b/app/Mail/PatchPanelPort/Info.php new file mode 100644 index 000000000..057978d0a --- /dev/null +++ b/app/Mail/PatchPanelPort/Info.php @@ -0,0 +1,52 @@ + + * @author Yann Robin + * @category PatchPanel + * @package IXP\Mail\PatchPanelPort + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Info extends Email +{ + + /** + * Create a new message instance. + * + * @param PatchPanelPort $ppp + */ + public function __construct( PatchPanelPort $ppp ) + { + parent::__construct( $ppp ); + $this->subject = "Cross connect details for " .env('IDENTITY_ORGNAME') . " [" . $ppp->patchPanel->colo_reference . " / " . $ppp->name() . "]"; + $this->tmpl = 'patch-panel-port/emails/info'; + } +} \ No newline at end of file diff --git a/app/Mail/PatchPanelPort/Loa.php b/app/Mail/PatchPanelPort/Loa.php new file mode 100644 index 000000000..30f608dad --- /dev/null +++ b/app/Mail/PatchPanelPort/Loa.php @@ -0,0 +1,51 @@ + + * @author Yann Robin + * @category PatchPanel + * @package IXP\Mail\PatchPanelPort + * @copyright Copyright (C) 2009 - 2019 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Loa extends Email +{ + /** + * Create a new message instance. + * + * @param PatchPanelPort $ppp + */ + public function __construct( PatchPanelPort $ppp ) + { + parent::__construct( $ppp ); + $this->subject = "Cross connect LoA details for " . env('IDENTITY_ORGNAME') . " [" . $ppp->patchPanel->colo_reference . " / " . $ppp->name() . "]"; + $this->tmpl = 'patch-panel-port/emails/loa'; + } +} diff --git a/app/Mail/PeeringManager/RequestPeeringManager.php b/app/Mail/PeeringManager/RequestPeeringManager.php new file mode 100644 index 000000000..d526a0519 --- /dev/null +++ b/app/Mail/PeeringManager/RequestPeeringManager.php @@ -0,0 +1,209 @@ + + * @author Yanm Robin + * @category Customer + * @package IXP\Mail\Customer + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class RequestPeeringManager extends Mailable +{ + use Queueable, SerializesModels; + + /** + * @var Customer + */ + public $peer; + + /** + * @var PeeringManagerRequest + */ + public $r; + + /** + * @var string The template to use to create the email + */ + protected $tmpl; + + /** + * @var string Temporary view file + */ + protected $tmpfile; + + /** + * @var string Temporary view name + */ + protected $tmpname; + + /** + * Create a new message instance. + * + * @param Customer $peer + * @param PeeringManagerRequest $r + * + */ + public function __construct( Customer $peer, PeeringManagerRequest $r ) + { + $this->peer = $peer; + $this->prepareFromRequest( $r ); + $this->prepareBody( $r ); + } + + /** + * Destructor + */ + public function __destruct() + { + // remove temporary file if it exists + if( $this->tmpfile && file_exists( $this->tmpfile ) ){ + @unlink( $this->tmpfile ); + } + } + + + /** + * Set up recipients and subject from a POST request. + * + * @param PeeringManagerRequest $r + */ + protected function prepareFromRequest( PeeringManagerRequest $r ): static + { + if( !$r->sendtome ) { + // recipients + foreach( [ "to", "cc", "bcc" ] as $p ) { + foreach( explode(',', $r->input( $p ) ) as $emaddr ) { + $email = trim( $emaddr ); + if( filter_var( $email, FILTER_VALIDATE_EMAIL ) ) { + $this->$p( $email ); + } + } + } + } + + $this->subject( $r->subject ); + + return $this; + } + + /** + * Set up Markdown body from a POST request. + * + * @param PeeringManagerRequest $r + */ + public function prepareBody( PeeringManagerRequest $r ): static + { + // Templating is slightly awkward here as Laravel's Mailable is built around reading the + // body from a template file be we have it via post. + // + // To work around this, we'll use a temporary file in a new view namespace. + + $body = $r->message; + $this->tmpfile = tempnam( sys_get_temp_dir(), 'request_peering_email_' ); + $this->tmpname = basename( $this->tmpfile ); + $this->tmpfile .= '.blade.php'; + file_put_contents( $this->tmpfile, "@component('mail::message')\n\n" . $body . "\n\n@endcomponent\n" ); + view()->addNamespace('request_peering_emails', sys_get_temp_dir() ); + $this->markdown( 'request_peering_emails::' . $this->tmpname ); + return $this; + } + + /** + * Build the message. + * + * @return $this + */ + public function build(): self + { + return $this; + } + + /** + * Checks if we can send the email + * + * @param bool $sendtome + * + * @return void + * + * @throws + */ + public function checkIfSendable( bool $sendtome ): void + { + if( config( "ixp.peering_manager.testmode" ) ) { + if( !config( "ixp.peering_manager.testemail" ) ) { + throw new MailableException( "Peering Manager test mode enabled but testemail not defined in config file." ); + } + + if( !filter_var( config( "ixp.peering_manager.testemail" ) , FILTER_VALIDATE_EMAIL ) ) { + throw new MailableException( "Peering Manager testemail not a valid email." ); + } + + $this->to = []; + $this->cc = []; + $this->bcc = []; + + $this->to( config( "ixp.peering_manager.testemail" ), "Test Email" ); + + } else { + $user = User::find( Auth::id() ); + if( $sendtome ) { + $this->to = []; + $this->cc = []; + $this->bcc = []; + $this->to( $user->email, $user->username ); + } else { + $this->to( $this->peer->peeringemail, $this->peer->name . " Peering Team" ); + $this->cc( $user->customer->peeringemail, $user->customer->name . " Peering Team" ); + $this->replyTo( $user->customer->peeringemail, $user->customer->name . " Peering Team" ); + } + } + + if( !count( $this->to ) ) { + throw new MailableException( "No valid recipients" ); + } + + if( !view()->exists( $this->markdown ) ) { + throw new MailableException( "Could not create / load temporary template" ); + } + } +} \ No newline at end of file diff --git a/app/Mail/User/UserAddedToCustomer.php b/app/Mail/User/UserAddedToCustomer.php new file mode 100644 index 000000000..54cc12750 --- /dev/null +++ b/app/Mail/User/UserAddedToCustomer.php @@ -0,0 +1,69 @@ + + * @author Yann Robin + * @category Customer + * @package IXP\Mail\Customer + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class UserAddedToCustomer extends Mailable +{ + use Queueable, SerializesModels; + + /** + * @var CustomerToUser + */ + public $c2u; + + /** + * Create a new message instance. + * + * @param CustomerToUser $c2u + */ + public function __construct( CustomerToUser $c2u ) + { + $this->c2u = $c2u; + } + + /** + * Build the message. + * + * @return $this + */ + public function build(): self + { + return $this->markdown( 'user.emails.welcome-existing' )->subject( config('identity.sitename' ) . " - Your Access Details" ); + } +} \ No newline at end of file diff --git a/app/Mail/User/UserCreated.php b/app/Mail/User/UserCreated.php new file mode 100644 index 000000000..c4c488276 --- /dev/null +++ b/app/Mail/User/UserCreated.php @@ -0,0 +1,86 @@ + + * @author Yann Robin + * @category Customer + * @package IXP\Mail\Customer + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class UserCreated extends Mailable +{ + use Queueable, SerializesModels; + + /** + * @var User + */ + public $user; + + /** + * Resend? + * @var bool + */ + public $resend; + + /** + * Existing? + * @var mixed + */ + public $token = null; + + + /** + * Create a new message instance. + * + * @param User $user + * @param bool $resend + */ + public function __construct( User $user, bool $resend = false ) + { + $this->user = $user; + $this->resend = $resend; + } + + /** + * Build the message. + * + * @return $this + */ + public function build(): self + { + $this->token = app('auth.password.broker')->createToken( $this->user ); + + return $this->markdown( 'user.emails.welcome' )->subject( config('identity.sitename' ) . " - Your Access Details" ); + } +} \ No newline at end of file diff --git a/app/Mail/Utils/SmtpTest.php b/app/Mail/Utils/SmtpTest.php new file mode 100644 index 000000000..b5d702b5b --- /dev/null +++ b/app/Mail/Utils/SmtpTest.php @@ -0,0 +1,51 @@ + + * @package IXP\Mail\Utils + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class SmtpTest extends Mailable +{ + use Queueable, SerializesModels; + + /** + * Build the message. + * + * @return $this + */ + public function build(): self + { + return $this->markdown( 'utils.emails.smtp-test' ) + ->subject( 'SMTP test email from IXP Manager' ); + } +} diff --git a/app/Models/Aggregators/BgpSessionDataAggregator.php b/app/Models/Aggregators/BgpSessionDataAggregator.php new file mode 100644 index 000000000..17e885422 --- /dev/null +++ b/app/Models/Aggregators/BgpSessionDataAggregator.php @@ -0,0 +1,155 @@ + array(3) { + * ["shortname"] => string(10) "pchanycast" + * ["name"] => string(25) "Packet Clearing House DNS" + * ["peers"] => array(17) { + * [2110] => string(4) "2110" + * [2128] => string(4) "2128" + * ... + * } + * } + * [112] => array(3) { + * ["shortname"] => string(5) "as112" + * ["name"] => string(17) "AS112 Reverse DNS" + * ["peers"] => array(20) { + * [1213] => string(4) "1213" + * [2110] => string(4) "2110" + * ... + * } + * } + * ... + * } + * + * It also caches the results on a per VLAN, per protocol basis. + * + * @param int|null $vlan The VLAN ID of the peering LAN to query + * @param int $protocol The IP protocol to query (4 or 6) + * @param int|null $asn Optional ASN to limit the query to + * @param bool $forceDb Set to true to ignore the cache and force the query to the database + * + * @return array|string Array of peerings (as described above) + * + * @throws + */ + public static function getPeers( ?int $vlan = null, int $protocol = 6, ?int $asn = null, bool $forceDb = false ): array|string + { + $key = "pm_sessions_{$vlan}_{$protocol}"; + + if( !$forceDb && ( $apeers = Cache::get( $key ) ) ){ + return $apeers; + } + + if( !in_array( $protocol, [ 4, 6 ] ) ){ + throw new IXP_Exception( 'Invalid protocol' ); + } + + if( $vlan !== null && !( $evlan = Vlan::find( $vlan ) ) ) + throw new IXP_Exception( 'Invalid VLAN' ); + + // we've added "bs.timestamp >= NOW() - INTERVAL 7 DAY" below as we don't + // dump old date (yet) and the time to run the query is O(n) on number + // of rows... + // also: CREATE INDEX idx_timestamp ON bgpsessiondata (timestamp) + + // need to construct a raw SQL here due to the schema design by NH + $peers = self::selectRaw( + 'bs.*, srcip.*, dstip.*, + vlis.virtualinterfaceid as visid, vlid.virtualinterfaceid as vidid, + cs.shortname AS csshortname, cs.name AS csname, cs.autsys AS csautsys, + cd.shortname AS cdshortname, cd.name AS cdname, cd.autsys AS cdautsys, + vlan.id AS vlanid, vlan.name AS vlanname, vlan.number AS vlantag, + COUNT( bs.packetcount ) AS packetcount' + )->from( 'bgpsessiondata AS bs' ) + ->leftJoin( "ipv{$protocol}address AS srcip", 'srcip.id', 'bs.srcipaddressid' ) + ->leftJoin( "ipv{$protocol}address AS dstip", 'dstip.id', 'bs.dstipaddressid' ) + ->leftJoin( 'vlaninterface AS vlis', "vlis.ipv{$protocol}addressid", 'srcip.id' ) + ->leftJoin( 'vlaninterface AS vlid', "vlid.ipv{$protocol}addressid", 'dstip.id' ) + ->leftJoin( 'virtualinterface AS vis', 'vis.id', 'vlis.virtualinterfaceid' ) + ->leftJoin( 'virtualinterface AS vid', 'vid.id', 'vlid.virtualinterfaceid' ) + ->leftJoin( 'cust AS cs', 'cs.id', 'vis.custid' ) + ->leftJoin( 'cust AS cd', 'cd.id', 'vid.custid' ) + ->leftJoin( 'vlan AS vlan', 'vlan.number', 'bs.vlan' ) + ->whereRaw( 'bs.timestamp >= NOW() - INTERVAL 7 DAY' ) + ->where( 'bs.protocol', $protocol ) + ->where( 'packetcount', '>=', 1 ) + ->when( $vlan !== null && $evlan, function( Builder $q ) use ( $evlan ) { + return $q->where( 'vlan.id', $evlan->id ); + } ) + ->when( $asn !== null, function( Builder $q, $asn ) { + return $q->where( 'cs.autsys', (int)$asn ); + } ) + ->groupBy( [ 'bs.srcipaddressid', 'bs.dstipaddressid', 'bs.id', 'vlis.virtualinterfaceid', 'vlid.virtualinterfaceid' ] ) + ->get()->toArray(); + + + $apeers = []; + + foreach( $peers as $p ) { + if( !isset( $apeers[ $p['csautsys'] ] ) ) { + $apeers[ $p[ 'csautsys' ] ] = []; + $apeers[ $p[ 'csautsys' ] ][ 'shortname' ] = $p[ 'csshortname' ]; + $apeers[ $p[ 'csautsys' ] ][ 'name' ] = $p[ 'csname' ]; + $apeers[ $p[ 'csautsys' ] ][ 'peers' ] = []; + } + + $apeers[ $p[ 'csautsys' ] ][ 'peers' ][ $p[ 'cdautsys' ] ] = $p[ 'cdautsys' ]; + } + + ksort( $apeers, SORT_NUMERIC ); + + foreach( $apeers as $asn => $p ) { + ksort( $apeers[ $asn ][ 'peers' ], SORT_NUMERIC ); + } + + Cache::put( $key, $apeers, 3600 ); + + return $apeers; + } +} diff --git a/app/Models/Aggregators/ConsoleServerConnectionAggregatore.php b/app/Models/Aggregators/ConsoleServerConnectionAggregatore.php new file mode 100644 index 000000000..fe7eddcd6 --- /dev/null +++ b/app/Models/Aggregators/ConsoleServerConnectionAggregatore.php @@ -0,0 +1,105 @@ +from( 'consoleserverconnection AS csc' ) + ->leftJoin( 'console_server AS cs', 'cs.id', 'csc.console_server_id') + ->leftJoin( 'cust AS c', 'c.id', 'csc.custid') + ->when( $id , function( Builder $q, $id ) { + return $q->where('csc.id', $id ); + } ) + ->when( $port , function( Builder $q, $port ) { + return $q->where('csc.console_server_id', $port ); + } ) + ->when( $feParams->listOrderBy , function( Builder $q, $orderby ) use ( $feParams ) { + foreach( $orderby as $order ) { + return $q->orderBy( $order, $feParams->listOrderByDir ?? 'ASC'); + } + })->get()->toArray(); + } +} \ No newline at end of file diff --git a/app/Models/Aggregators/ContactGroupAggregator.php b/app/Models/Aggregators/ContactGroupAggregator.php new file mode 100644 index 000000000..13ff8540f --- /dev/null +++ b/app/Models/Aggregators/ContactGroupAggregator.php @@ -0,0 +1,107 @@ + $contacts + * @property-read int|null $contacts_count + * @method static Builder|ContactGroupAggregator newModelQuery() + * @method static Builder|ContactGroupAggregator newQuery() + * @method static Builder|ContactGroupAggregator query() + * @method static Builder|ContactGroupAggregator whereActive($value) + * @method static Builder|ContactGroupAggregator whereCreatedAt($value) + * @method static Builder|ContactGroupAggregator whereDescription($value) + * @method static Builder|ContactGroupAggregator whereId($value) + * @method static Builder|ContactGroupAggregator whereLimitedTo($value) + * @method static Builder|ContactGroupAggregator whereName($value) + * @method static Builder|ContactGroupAggregator whereType($value) + * @method static Builder|ContactGroupAggregator whereUpdatedAt($value) + * @property string $created + * @method static Builder|ContactGroupAggregator whereCreated($value) + * @mixin \Eloquent + */ +class ContactGroupAggregator extends ContactGroup +{ + + /** + * Get contact group names as an array grouped by group type. + * + * Returned array structure: + * + * $arr = [ + * 'ROLE' => [ + * [ 'id' => 1, 'name' => 'Billing' ], + * [ 'id' => 2, 'name' => 'Admin'] + * ] + * 'OTHER' => [ + * [ 'id' => n, 'name' => 'Other group' ] + * ] + * ]; + * + * @param string|null $type Optionally limit to a specific type + * @param int|null $cid Contact id to filter for a particular contact + * @param bool $active Filter active + * + * @return array[][] + * + * @psalm-return array> + */ + public static function getGroupNamesTypeArray( ?string $type = null, ?int $cid = null, bool $active = false ): array + { + $result = self::when( $cid , function( Builder $q, $cid ) { + return $q->leftJoin( 'contact_to_group', function( $join ) use( $cid ) { + $join->on( 'contact_group.id', 'contact_to_group.contact_group_id') + ->where('contact_to_group.contact_id', $cid ); + }); + })->when( $type , function( Builder $q, $type ) { + return $q->where( 'type', $type ); + })->when( $active , function( Builder $q, $active ) { + return $q->where('active', $active ); + } )->get(); + + $groups = []; + + foreach( $result as $r ){ + $groups[ $r->type ][ $r->id ] = [ 'id' => $r->id, 'name' => $r->name ]; + } + + return $groups; + } +} \ No newline at end of file diff --git a/app/Models/Aggregators/CustomerAggregator.php b/app/Models/Aggregators/CustomerAggregator.php new file mode 100644 index 000000000..515f0e287 --- /dev/null +++ b/app/Models/Aggregators/CustomerAggregator.php @@ -0,0 +1,589 @@ + $AtlasMeasurementsDest + * @property-read int|null $atlas_measurements_dest_count + * @property-read \Illuminate\Database\Eloquent\Collection $AtlasMeasurementsSource + * @property-read int|null $atlas_measurements_source_count + * @property-read \Illuminate\Database\Eloquent\Collection $AtlasProbes + * @property-read int|null $atlas_probes_count + * @property-read \IXP\Models\CompanyBillingDetail|null $companyBillingDetail + * @property-read \IXP\Models\CompanyRegisteredDetail|null $companyRegisteredDetail + * @property-read \Illuminate\Database\Eloquent\Collection $consoleServerConnections + * @property-read int|null $console_server_connections_count + * @property-read \Illuminate\Database\Eloquent\Collection $contacts + * @property-read int|null $contacts_count + * @property-read \Illuminate\Database\Eloquent\Collection $customerEquipments + * @property-read int|null $customer_equipments_count + * @property-read \Illuminate\Database\Eloquent\Collection $customerNotes + * @property-read int|null $customer_notes_count + * @property-read \Illuminate\Database\Eloquent\Collection $customerToUser + * @property-read int|null $customer_to_user_count + * @property-read \Illuminate\Database\Eloquent\Collection $docstoreCustomerDirectories + * @property-read int|null $docstore_customer_directories_count + * @property-read \Illuminate\Database\Eloquent\Collection $docstoreCustomerFiles + * @property-read int|null $docstore_customer_files_count + * @property-read \IXP\Models\IrrdbConfig|null $irrdbConfig + * @property-read \Illuminate\Database\Eloquent\Collection $irrdbPrefixes + * @property-read int|null $irrdb_prefixes_count + * @property-read \IXP\Models\Logo|null $logo + * @property-read \Illuminate\Database\Eloquent\Collection $patchPanelPortHistories + * @property-read int|null $patch_panel_port_histories_count + * @property-read \Illuminate\Database\Eloquent\Collection $patchPanelPorts + * @property-read int|null $patch_panel_ports_count + * @property-read \Illuminate\Database\Eloquent\Collection $peerRouteServerFilters + * @property-read int|null $peer_route_server_filters_count + * @property-read \Illuminate\Database\Eloquent\Collection $peers + * @property-read int|null $peers_count + * @property-read \Illuminate\Database\Eloquent\Collection $peersWith + * @property-read int|null $peers_with_count + * @property-read Customer|null $resellerObject + * @property-read \Illuminate\Database\Eloquent\Collection $resoldCustomers + * @property-read int|null $resold_customers_count + * @property-read \Illuminate\Database\Eloquent\Collection $routeServerFilters + * @property-read int|null $route_server_filters_count + * @property-read \Illuminate\Database\Eloquent\Collection $routeServerFiltersInProduction + * @property-read int|null $route_server_filters_in_production_count + * @property-read \Illuminate\Database\Eloquent\Collection $rsPrefixes + * @property-read int|null $rs_prefixes_count + * @property-read \Illuminate\Database\Eloquent\Collection $tags + * @property-read int|null $tags_count + * @property-read \Illuminate\Database\Eloquent\Collection $trafficDailies + * @property-read int|null $traffic_dailies_count + * @property-read \Illuminate\Database\Eloquent\Collection $users + * @property-read int|null $users_count + * @property-read \Illuminate\Database\Eloquent\Collection $virtualInterfaces + * @property-read int|null $virtual_interfaces_count + * @property-read \Illuminate\Database\Eloquent\Collection $vlanInterfaces + * @property-read int|null $vlan_interfaces_count + * @method static Builder|Customer active() + * @method static Builder|Customer addressesForVlan(int $vlanid, int $cust, int $protocol) + * @method static Builder|Customer associate() + * @method static Builder|Customer current() + * @method static Builder|Customer currentActive(bool $trafficing = false, bool $externalOnly = false, bool $connected = true) + * @method static Builder|Customer internal() + * @method static Builder|CustomerAggregator newModelQuery() + * @method static Builder|CustomerAggregator newQuery() + * @method static Builder|Customer notDeleted() + * @method static Builder|CustomerAggregator query() + * @method static Builder|Customer resellerOnly() + * @method static Builder|Customer trafficking() + * @method static Builder|CustomerAggregator whereAbbreviatedName($value) + * @method static Builder|CustomerAggregator whereActivepeeringmatrix($value) + * @method static Builder|CustomerAggregator whereAutsys($value) + * @method static Builder|CustomerAggregator whereCompanyBillingDetailsId($value) + * @method static Builder|CustomerAggregator whereCompanyRegisteredDetailId($value) + * @method static Builder|CustomerAggregator whereCorpwww($value) + * @method static Builder|CustomerAggregator whereCreatedAt($value) + * @method static Builder|CustomerAggregator whereCreator($value) + * @method static Builder|CustomerAggregator whereDatejoin($value) + * @method static Builder|CustomerAggregator whereDateleave($value) + * @method static Builder|CustomerAggregator whereId($value) + * @method static Builder|CustomerAggregator whereInManrs($value) + * @method static Builder|CustomerAggregator whereInPeeringdb($value) + * @method static Builder|CustomerAggregator whereIrrdb($value) + * @method static Builder|CustomerAggregator whereIsReseller($value) + * @method static Builder|CustomerAggregator whereLastupdatedby($value) + * @method static Builder|CustomerAggregator whereMD5Support($value) + * @method static Builder|CustomerAggregator whereMaxprefixes($value) + * @method static Builder|CustomerAggregator whereName($value) + * @method static Builder|CustomerAggregator whereNoc24hphone($value) + * @method static Builder|CustomerAggregator whereNocemail($value) + * @method static Builder|CustomerAggregator whereNocfax($value) + * @method static Builder|CustomerAggregator whereNochours($value) + * @method static Builder|CustomerAggregator whereNocphone($value) + * @method static Builder|CustomerAggregator whereNocwww($value) + * @method static Builder|CustomerAggregator wherePeeringdbOauth($value) + * @method static Builder|CustomerAggregator wherePeeringemail($value) + * @method static Builder|CustomerAggregator wherePeeringmacro($value) + * @method static Builder|CustomerAggregator wherePeeringmacrov6($value) + * @method static Builder|CustomerAggregator wherePeeringpolicy($value) + * @method static Builder|CustomerAggregator whereReseller($value) + * @method static Builder|CustomerAggregator whereShortname($value) + * @method static Builder|CustomerAggregator whereStatus($value) + * @method static Builder|CustomerAggregator whereType($value) + * @method static Builder|CustomerAggregator whereUpdatedAt($value) + * @property string|null $lastupdated + * @property string|null $created + * @method static Builder|CustomerAggregator whereCreated($value) + * @method static Builder|CustomerAggregator whereLastupdated($value) + * @property-read \IXP\Models\IrrdbUpdateLog|null $irrdbUpdateLog + * @mixin \Eloquent + */ +class CustomerAggregator extends Customer +{ + + /** + * Get All customer by vlan and protocol + * + * @param int|null $vlanid + * @param int|null $protocol + * + * @return array + */ + public static function getByVlanAndProtocol( ?int $vlanid = null, ?int $protocol = null ): array + { + return self::select( [ 'c.id', 'c.name' ] ) + ->from( 'cust AS c' ) + ->leftJoin( 'virtualinterface AS vi', 'vi.custid', 'c.id' ) + ->leftJoin( 'vlaninterface AS vli', 'vli.virtualinterfaceid', 'vi.id' ) + ->leftJoin( 'vlan AS v', 'v.id', 'vli.vlanid' ) + ->leftJoin( 'routers AS r', 'r.vlan_id', 'v.id' ) + ->where( 'vli.rsclient', true ) + ->when( $protocol, function( Builder $q, $protocol ) { + return $q->where( 'r.protocol', $protocol ) + ->where( "vli.ipv{$protocol}enabled", true ); + }, function( $query ) { + return $query->where( function( $q ) { + $q->where( 'r.protocol', 4 ) + ->orWhere( 'r.protocol', 6 ); + } )->where( function( $q ) { + $q->where( 'vli.ipv4enabled', true ) + ->orWhere( 'vli.ipv6enabled', true ); + } ); + } )->when( $vlanid, function( Builder $q, $vlanid ) { + return $q->where( 'v.id', $vlanid ); + } )->distinct( 'c.id' )->orderBy( 'c.name', 'asc' )->get()->toArray(); + } + + /** + * Build an array of data for the peering matrice + * + * Sample return: + * + * [ + * "me" => [ "id" => 69 + * "name" => "3 Ireland's" + * "shortname" => "three" + * "autsys" => 34218 + * "maxprefixes" => 100 + * "peeringemail" => "io.ip@three.co.uk" + * "peeringpolicy" => "open" + * "vlaninterfaces" => [ + * 10 => [ + * 0 => [ + * "ipv4enabled" => true + * "ipv6enabled" => false + * "rsclient" => true + * ] + * ] + * + * ] + * + * ] + * + * "potential" => [ + * 12041 => false + * 56767 => false + * 196737 => false + * ] + * + * "potential_bilat" => [ + * 12041 => true + * 56767 => true + * 196737 => false + * ] + * + * "peered" => [ + * 12041 => true + * 56767 => true + * 196737 => false + * ] + * + * "peered" => [ + * 12041 => false + * 56767 => false + * 196737 => false + * ] + * + * "peers" => [ + * 60 => [ + * "id" => 44 + * "custid" => 69 + * "peerid" => 60 + * "email_last_sent" => null + * "emails_sent" => 0 + * "peered" => false + * "rejected" => false + * "notes" => "" + * "created" => DateTime + * "updated" => DateTime + * "email_days" => -1 + * ] + * ] + * + * "custs" => [ + * 12041 => [ + * "id" => 146 + * "name" => "Afilias" + * "shortname" => "afilias" + * "autsys" => 12041 + * "maxprefixes" => 500 + * "peeringemail" => "peering@afilias-nst.info" + * "peeringpolicy" => "open" + * "vlaninterfaces" => [...] + * "ispotential" => true + * 10 => [ + * 4 => 1 + * ] + * ] + * 56767 => [...] + * 196737 => [...] + * + * ] + * + * @param Customer $cust Current customer + * @param Vlan[] $vlans Array of Vlans + * @param array $protos Array of protos + * + * @return ((((int|mixed)[]|mixed|string|true)[]|Vlan|bool|mixed)[]|mixed|null)[]|null + * + * @psalm-return array{me: mixed|null, potential: array|mixed, bool>, potential_bilat: array|mixed, bool>, peered: array|mixed, bool>, rejected: array|mixed, bool>, peers: array|mixed, custs: array|mixed|true>|mixed>, bilat: array>, vlan: array, protos: array}|null + */ + public static function getPeeringManagerArrayByType( Customer $cust, $vlans, array $protos ): array|null + { + if( !count( $vlans ) ) { + return null; + } + + $bilat = []; + foreach( $vlans as $vlan ) { + foreach( $protos as $proto ) { + $bilat[ $vlan['number'] ][ $proto ] = BgpSessionDataAggregator::getPeers( $vlan['id'], $proto ); + } + } + $vlanNumbers = Vlan::select( ['id', 'number'] )->get()->keyBy( 'id' )->toArray(); + + $custs = Customer::currentActive( true, true, false )->with( 'vlanInterfaces' )->get()->keyBy( 'autsys' )->toArray(); + + $potential = $potential_bilat = $peered = $rejected = []; + + foreach( $custs as $index => $value ){ + $vlanInterface = []; + foreach( $value[ 'vlan_interfaces' ] as $i => $vli ){ + $vlanInterface[ $vlanNumbers[ $vli[ 'vlanid' ] ][ 'number' ] ][] = $vli; + } + + $custs[ $index ][ 'vlan_interfaces' ] = $vlanInterface; + } + + if( isset( $custs[ $cust->autsys ] ) ){ + $me = $custs[ $cust->autsys ]; + unset( $custs[ $cust->autsys ] ); + } else { + $me = null; + } + + foreach( $custs as $c ) { + $custs[ $c[ 'autsys' ] ][ 'ispotential' ] = false; + foreach( $vlans as $vlan ) { + if( isset( $me[ 'vlan_interfaces' ][ $vlan['number'] ] ) ) { + if( isset( $c[ 'vlan_interfaces' ][$vlan['number']] ) ) { + foreach( $protos as $proto ) { + if( $me[ 'vlan_interfaces' ][ $vlan['number'] ][ 0 ][ "ipv{$proto}enabled" ] && $c[ 'vlan_interfaces' ][ $vlan['number'] ][ 0 ][ "ipv{$proto}enabled" ] ) { + if( isset( $bilat[ $vlan['number'] ][ 4 ][ $me['autsys' ] ][ 'peers' ] ) && in_array( $c[ 'autsys' ], $bilat[ $vlan['number'] ][ 4 ][ $me[ 'autsys' ] ][ 'peers' ] ) ){ + $custs[ $c[ 'autsys' ] ][ $vlan['number'] ][$proto] = 2; + } else if( $me[ 'vlan_interfaces' ][ $vlan['number'] ][ 0 ][ 'rsclient' ] && $c[ 'vlan_interfaces' ][ $vlan['number'] ][ 0 ][ 'rsclient' ] ){ + $custs[ $c[ 'autsys' ] ][ $vlan['number'] ][ $proto ] = 1; + $custs[ $c[ 'autsys' ] ][ 'ispotential' ] = true; + } else { + $custs[ $c[ 'autsys' ] ][ $vlan['number'] ][ $proto ] = 0; + $custs[ $c[ 'autsys' ] ][ 'ispotential' ] = true; + } + } + } + } + } + } + } + + foreach( $custs as $c ) { + $peered[ $c[ 'autsys' ] ] = false; + $potential_bilat[ $c[ 'autsys' ] ] = false; + $potential[ $c[ 'autsys' ] ] = false; + $rejected[ $c[ 'autsys' ] ] = false; + + foreach( $vlans as $vlan ) { + foreach( $protos as $proto ) { + if( isset( $c[ $vlan['number'] ][ $proto ] ) ) { + switch( $c[ $vlan['number'] ][ $proto ] ) { + case 2: + $peered[ $c[ 'autsys' ] ] = true; + break; + case 1: + $peered[ $c[ 'autsys' ] ] = true; + $potential_bilat[ $c[ 'autsys' ] ] = true; + break; + case 0: + $potential[ $c[ 'autsys' ] ] = true; + $potential_bilat[ $c[ 'autsys' ] ] = true; + break; + } + } + } + } + } + $peers = PeeringManager::selectRaw( + 'pm.id AS id, c.id AS custid, p.id AS peerid, + pm.email_last_sent AS email_last_sent, pm.emails_sent AS emails_sent, + pm.peered AS peered, pm.rejected AS rejected, pm.notes AS notes, + pm.created_at AS created_at, pm.updated_at AS updated_at' + )->from( 'peering_manager AS pm' ) + ->leftJoin( 'cust AS c', 'c.id', 'pm.custid') + ->leftJoin( 'cust AS p', 'p.id', 'pm.peerid') + ->where( 'c.id', $cust->id ) + ->get()->keyBy( 'peerid' )->toArray(); + + foreach( $peers as $i => $p ) { + // days since last peering request email sent + if( !$p[ 'email_last_sent' ] ){ + $peers[ $i ][ 'email_days' ] = -1; + } else { + $email_last_sent = new Carbon( $p['email_last_sent']); + $peers[ $i ][ 'email_days' ] = floor( ( time() - $email_last_sent->getTimestamp() ) / 86400 ); + } + } + + foreach( $custs as $c ) { + if( isset( $peers[ $c[ 'id' ] ] ) ) { + if( isset( $peers[ $c[ 'id' ] ][ 'peered' ] ) && $peers[ $c[ 'id' ] ][ 'peered' ] ) { + $peered[ $c[ 'autsys' ] ] = true; + $rejected[ $c[ 'autsys' ] ] = false; + $potential[ $c[ 'autsys' ] ] = false; + $potential_bilat[ $c[ 'autsys' ] ] = false; + } else if( isset( $peers[ $c[ 'id' ] ][ 'rejected' ] ) && $peers[ $c[ 'id' ] ][ 'rejected' ] ) { + $peered[ $c['autsys' ] ] = false; + $rejected[ $c['autsys' ] ] = true; + $potential[ $c['autsys' ] ] = false; + $potential_bilat[ $c['autsys' ] ] = false; + } + } + } + + return [ "me" => $me, + "potential" => $potential, + "potential_bilat" => $potential_bilat, + "peered" => $peered, + "rejected" => $rejected, + "peers" => $peers, + "custs" => $custs, + "bilat" => $bilat, + "vlan" => $vlans , + "protos" => $protos, + ]; + } + + /** + * Delete the customer. + * + * Related models are mostly handled by 'ON DELETE CASCADE'. + * + * @param Customer $cust The customer Object + * + * @return true + * + * @throws + */ + public static function deleteObject( Customer $cust ): bool + { + try { + DB::beginTransaction(); + + // Delete Customer Logo + if( $logo = $cust->logo ) { + if( file_exists( $logo->fullPath() ) ) { + @unlink( $logo->fullPath() ); + } + $logo->delete(); + } + + // delete contact to contact group links + $contacts = $cust->contacts(); + + DB::table( 'contact_to_group' ) + ->whereIn( 'contact_id', $contacts->get()->pluck( 'id' )->toArray() ) + ->delete(); + + $cust->contacts()->delete(); + + foreach( $cust->customerToUser as $c2u ) { + + // Delete User Logins + $c2u->userLoginHistories()->delete(); + + $user = $c2u->user; + $nbCust = $user->customerToUser()->count(); + // Delete Customer2User + $c2u->delete(); + + // Delete User, if that user only have the customer that we want to delete + if( $nbCust === 1 ) { + $user->delete(); + $user->refresh(); + } elseif( $user->custid === $cust->id ) { + if( $c = $user->customerToUser()->where( 'customer_id', '!=', $cust->id )->first() ){ + $newAssignCust = $c->customer_id; + } + + $user->custid = $newAssignCust ?? null; + $user->save(); + } + } + + // Delete the Core Bundle + $cbs = CoreBundle::select( 'cb.*' )->from( 'corebundles AS cb' ) + ->leftJoin( 'corelinks AS cl', 'cl.core_bundle_id', 'cb.id' ) + ->leftJoin( 'coreinterfaces AS ci', 'ci.id', 'cl.core_interface_sidea_id' ) + ->leftJoin( 'physicalinterface AS pi', 'pi.id', 'ci.physical_interface_id' ) + ->leftJoin( 'virtualinterface AS vi', 'vi.id', 'pi.virtualinterfaceid' ) + ->where( 'vi.custid', $cust->id )->distinct()->get(); + + foreach( $cbs as $cb ){ + /** @var $cb CoreBundle */ + $cb->deleteObject(); + } + $cust->tags()->detach(); + + $cust->delete(); + + $cust->companyBillingDetail()->delete(); + $cust->companyRegisteredDetail()->delete(); + + DB::commit(); + } catch( Exception $e ) { + DB::rollBack(); + throw $e; + } + + return true; + } + + /** + * Reformat the name of the customers with additional details such as their date of leaving, + * if they have left, appended to the name. + * + * @param \Illuminate\Database\Eloquent\Collection $custs Collection of customers + * @return \Illuminate\Database\Eloquent\Collection Updated collection with reformatted names + */ + public static function reformatNameWithDetail( EloquentCollection $custs ): EloquentCollection { + + /** @var Customer $cust */ + foreach( $custs as $id => $cust ) { + + if( $cust->hasLeft() ) { + $custs[$id]->name .= " [Left $cust->dateleave]"; + } + + } + + return $custs; + } + + + /** + * Get atlas probes for a given customer and protocol. + * + * @param int $protocol + * @param int|null $vlanid + * @param array $includeCust + * @param string $orderBy + * @param int|null $limit + * + * @return Collection + */ + public static function withProbesForProtocol( int $protocol = 4, ?int $vlanid = null, array $includeCust = [], string $orderBy = 'name', ?int $limit = null ): Collection + { + $enabled = $protocol === 4 ? 'v4_enabled' : 'v6_enabled'; + + return self::select('cust.*' ) + ->LeftJoin( 'atlas_probes', 'cust.id', 'atlas_probes.cust_id' ) + ->when( $vlanid , function( Builder $q, $vlanid ) { + return $q->join( 'virtualinterface AS vi', 'cust.id', 'vi.custid' ) + ->join( 'vlaninterface AS vli', 'vi.id','vli.virtualinterfaceid' ) + ->where( 'vli.vlanid', $vlanid ); + }) + ->where( 'atlas_probes.' . $enabled , true ) + ->when( count( $includeCust ) , function( Builder $q) use ( $includeCust ) { + return $q->whereIn( 'atlas_probes.cust_id', $includeCust ); + })->when( $limit , function( Builder $q, $limit ) { + return $q->limit( $limit ); + })->distinct()->orderBy( 'cust.' . $orderBy ) + ->get()->keyBy( 'autsys' ); + } +} \ No newline at end of file diff --git a/app/Models/Aggregators/IpAddressAggregator.php b/app/Models/Aggregators/IpAddressAggregator.php new file mode 100644 index 000000000..b627f1e48 --- /dev/null +++ b/app/Models/Aggregators/IpAddressAggregator.php @@ -0,0 +1,95 @@ + addresses that already existed in the database. + * `new` => addresses added (if `skip == true`) or addresses that would have been added. + * + * @param array $addresses + * @param Vlan $vlan + * @param IPv4Address|IPv6Address $model + * @param bool $skip If the address already exists, then skip over it (default). Otherwise, do not add any addresses. + * + * @return array[] + * + * @throws + * + * @psalm-param IPv4Address::class|IPv6Address::class $model + * + * @psalm-return array{preexisting: list, new: list} + */ + public static function bulkAdd( array $addresses, Vlan $vlan, string $model, bool $skip = true ): array + { + $results = [ + 'preexisting' => [], + 'new' => [] + ]; + + DB::beginTransaction(); + + try { + + foreach( $addresses as $a ) { + // does the address already exist? + $ipAddress = $model::where( 'address', $a )->where( 'vlanid', $vlan->id )->first(); + + if( $ipAddress ) { + $results[ 'preexisting' ][] = $ipAddress; + } else { + $ipAddress = new $model; + $ipAddress->vlanid = $vlan->id; + $ipAddress->address = $a; + $ipAddress->save(); + $results['new'][] = $a; + } + } + + if( !$skip && count( $results['preexisting'] ) ) { + DB::rollBack(); + } else { + DB::commit(); + } + + } catch ( Exception $e ) { + DB::rollBack(); + throw $e; + } + + return $results; + } + +} \ No newline at end of file diff --git a/app/Models/Aggregators/IrrdbAggregator.php b/app/Models/Aggregators/IrrdbAggregator.php new file mode 100644 index 000000000..5888df829 --- /dev/null +++ b/app/Models/Aggregators/IrrdbAggregator.php @@ -0,0 +1,145 @@ +where( 'customer_id', $custid ) + ->where( 'protocol', $protocol ) + ->orderByRaw( $orderby ) + ->get(); + + if( !$flatten ) { + return $results->toArray(); + } + + return $results->keyBy( 'id' )->pluck( $field, 'id' )->toArray(); + } + + /** + * Utility function to get the prefixes/ASN a customer has for a given protocol + * for the purpose of generating router configuration + * + * Returns an array of prefixes. + * + * @param int|Customer $cust The customer entity | id + * @param int $protocol The IP protocol (4/6) + * @param bool $resetCache If true, delete and reseed the cache + * + * @return array The prefixes found + */ + public static function prefixesForRouterConfiguration( int|Customer $cust, int $protocol, bool $resetCache = false ): array + { + if( is_int( $cust ) ) { + $cust = Customer::find( $cust ); + } + + if( $resetCache ) { + Cache::store()->forget( 'irrdb:prefix:ipv' . $protocol . ':' . $cust->asMacro( $protocol ) ); + } + + // Pull these out of the cache if possible, otherwise the database. + return Cache::store()->rememberForever( 'irrdb:prefix:ipv' . $protocol . ':' . $cust->asMacro( $protocol ), function() use ($cust,$protocol) { + return IrrdbPrefix::select('prefix') + ->where( 'customer_id', $cust->id ) + ->where('protocol', $protocol ) + ->orderByRaw( 'INET' . ( $protocol === 6 ? '6' : '' ) . '_ATON( prefix ) ASC' ) + ->orderBy( 'id', 'ASC' ) + ->pluck('prefix') + ->toArray(); + }); + } + + + /** + * Utility function to get the prefixes/ASN a customer has for a given protocol + * for the purpose of generating router configuration + * + * Returns an array of prefixes. + * + * @param int|Customer $cust The customer entity | id + * @param int $protocol The IP protocol (4/6) + * @param bool $resetCache If true, delete and reseed the cache + * + * @return array The prefixes found + */ + public static function asnsForRouterConfiguration( int|Customer $cust, int $protocol, bool $resetCache = false ): array + { + if( is_int( $cust ) ) { + $cust = Customer::find( $cust ); + } + + if( $resetCache ) { + Cache::store()->forget( 'irrdb:asn:ipv' . $protocol . ':' . $cust->asMacro( $protocol ) ); + } + + // Pull these out of the cache if possible, otherwise the database. + return Cache::store()->rememberForever( 'irrdb:asn:ipv' . $protocol . ':' . $cust->asMacro( $protocol ), function() use ($cust,$protocol) { + return IrrdbAsn::select('asn') + ->where( 'customer_id', $cust->id ) + ->where('protocol', $protocol ) + ->orderBy( 'asn', 'ASC' ) + ->orderBy( 'id', 'ASC' ) + ->pluck('asn') + ->toArray(); + }); + } +} \ No newline at end of file diff --git a/app/Models/Aggregators/PatchPanelPortAggregator.php b/app/Models/Aggregators/PatchPanelPortAggregator.php new file mode 100644 index 000000000..75de639c2 --- /dev/null +++ b/app/Models/Aggregators/PatchPanelPortAggregator.php @@ -0,0 +1,177 @@ + $duplexSlavePorts + * @property-read int|null $duplex_slave_ports_count + * @property-read \IXP\Models\PatchPanel|null $patchPanel + * @property-read Collection $patchPanelPortFiles + * @property-read int|null $patch_panel_port_files_count + * @property-read Collection $patchPanelPortFilesPublic + * @property-read int|null $patch_panel_port_files_public_count + * @property-read Collection $patchPanelPortHistories + * @property-read int|null $patch_panel_port_histories_count + * @property-read \IXP\Models\SwitchPort|null $switchPort + * @method static Builder|PatchPanelPort masterPort() + * @method static Builder|PatchPanelPortAggregator newModelQuery() + * @method static Builder|PatchPanelPortAggregator newQuery() + * @method static Builder|PatchPanelPortAggregator query() + * @mixin \Eloquent + */ +class PatchPanelPortAggregator extends PatchPanelPort +{ + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'patch_panel_port AS ppp'; + + /** + * Return the list of patch panel ports + * + * @param int|null $ppid + * @param bool $advanced + * @param int|null $location + * @param int|null $cabinet + * @param int|null $cabletype + * @param bool $availableForUse + * + * @return Collection + */ + public static function list( ?int $ppid = null, bool $advanced = false, ?int $location = null, ?int $cabinet = null, ?int $cabletype = null, bool $availableForUse = false, bool $prewiredOnly = false ): Collection + { + return self::selectRaw( ' + ppp.*, + count( pppf.id ) AS files, count( ppph.id ) AS histories, + pp.name as ppname, + pp.colo_reference as ppcolo_reference, + pp.port_prefix AS prefix, + pp.cable_type AS ppcable_type, + pp.colo_pp_type, + sp.name AS spname, + s.name AS sname, c.abbreviatedName AS cname, + count( ppps.id ) AS nbslave, + max( ppps.number ) AS slavenumber' ) // only one slave port! + ->from( 'patch_panel_port AS ppp' ) + ->join( 'patch_panel AS pp', 'pp.id', 'ppp.patch_panel_id') + ->leftJoin( 'switchport AS sp', 'sp.id', 'ppp.switch_port_id') + ->leftJoin( 'switch AS s', 's.id', 'sp.switchid') + ->leftJoin( 'cust AS c', 'c.id', 'ppp.customer_id') + ->leftJoin( 'patch_panel_port_file AS pppf', 'pppf.patch_panel_port_id', 'ppp.id') + ->leftJoin( 'patch_panel_port_history AS ppph', 'ppph.patch_panel_port_id', 'ppp.id') + ->leftJoin( 'patch_panel_port AS ppps', 'ppps.duplex_master_id', 'ppp.id') + ->when( !$advanced , function( Builder $q ) { + return $q->whereNull( 'ppp.duplex_master_id' ); + } ) + ->when( $ppid , function( Builder $q, $ppid ) { + return $q->where('ppp.patch_panel_id', $ppid ); + } ) + ->when( $cabinet || $location , function( Builder $q ) use( $location, $cabinet ) { + return $q->leftJoin( 'cabinet as cab', 'cab.id', 'pp.cabinet_id' ) + ->when( $cabinet , function( Builder $q, $cabinet ) { + return $q->where( 'pp.cabinet_id', $cabinet ); + })->when( $location , function( Builder $q, $location ) { + return $q->where('cab.locationid', $location ); + } ); + } ) + ->when( $cabletype , function( Builder $q, $cabletype ) { + return $q->where('pp.cable_type', $cabletype ); + } ) + ->when( $prewiredOnly, function( Builder $q, $prewired ) { + return $q->where( 'ppp.state', '=', PatchPanelPort::STATE_PREWIRED ); + } ) + ->when( $availableForUse , function( Builder $q ) { + return $q->whereIn('ppp.state', PatchPanelPort::$AVAILABLE_STATES ); + } ) + ->groupByRaw( 'ppp.id, ppp.number' ) + ->orderBy( 'ppp.number' )->get()->keyBy( 'id' ); + } + + /** + * Get all the patch panel ports available for a patch panel ID + * + * Possibility to exclude some ppp id from the list + * + * port available => PatchPanelPort::$AVAILABLE_FOR_ALLOCATION_STATES + * + * @param int $ppid ID of the patch panel + * @param array $excludeIds Patch Panel Port ID that we want to exclude from the list + * @param int|null $includeSlave Patch Panel Port ID of the slave to include + * @param bool $excludeDuplex Should we exclude the duplex port ? + * + * @return array list of patch panel form key => pppId , value => ppp name + */ + public static function getAvailablePorts( int $ppid, $excludeIds = [], ?int $includeSlave = null, bool $excludeDuplex = true ) + { + $ppps = PatchPanelPort::selectRaw( + 'ppp.id, ppp.number, + ppp.patch_panel_id, ppp.duplex_master_id, + GROUP_CONCAT( pp.port_prefix, ppp.number ) AS name' ) + ->from( 'patch_panel_port AS ppp' ) + ->join( 'patch_panel AS pp', 'pp.id', 'ppp.patch_panel_id' ) + ->leftJoin( 'patch_panel_port AS ppps', 'ppps.duplex_master_id', 'ppp.id' ) + ->where( 'pp.id', $ppid ) + ->when( $excludeDuplex , function( Builder $q ) { + return $q->whereNull( 'ppps.duplex_master_id' ) + ->whereNull( 'ppp.duplex_master_id' ); + }, function( $q ) { + return $q->whereNull( 'ppp.duplex_master_id' ); + } ) + ->whereIn( 'ppp.state', PatchPanelPort::$AVAILABLE_FOR_ALLOCATION_STATES ) + ->when( $includeSlave , function( Builder $q, $includeSlave ) { + return $q->orWhere('ppp.id', $includeSlave ); + } ) + ->when( count( $excludeIds ) > 0 , function( Builder $q ) use( $excludeIds ) { + return $q->whereNotIn('ppp.id', $excludeIds ); + } ) + ->groupBy( 'ppp.id' )->orderBy( 'ppp.number' ) + ->get(); + + if( $excludeDuplex ){ + return $ppps->toArray(); + } + + $result = []; + foreach ( $ppps as $ppp ){ + /** @var $ppp PatchPanelPort */ + $result[ $ppp->id ] = [ + 'id' => $ppp->id, + 'name' => $ppp->name(), + 'isDuplex' => $ppp->duplexSlavePorts()->exists() + ]; + } + return $result; + } +} \ No newline at end of file diff --git a/app/Models/Aggregators/README.md b/app/Models/Aggregators/README.md new file mode 100644 index 000000000..2bb68369a --- /dev/null +++ b/app/Models/Aggregators/README.md @@ -0,0 +1,13 @@ + # Aggregators + + An *aggregator* is a class comprising functions that aggregate data from one or mode database tables in such a way that cannot be done cleanly or easily with SQL. + + The concept is similar to Doctrine's Repository classes but not a direct replacement: + + * functions that easily operator on multiple rows still belong in the model class; + * scopes and similar filtering functions belong in the model class; + * aggregation type functions that are ~ <10 lines long and are used once in a single controller are better placed in the controller action or a private function in the controller. + + So an aggregator is really a library of model functions that typically involve more than one database table and/or are complex and/or are used in more than one place in the code (e.g. multiple controllers with no parent/child relationship). + + \ No newline at end of file diff --git a/app/Models/Aggregators/RouteServerFilterAggregator.php b/app/Models/Aggregators/RouteServerFilterAggregator.php new file mode 100644 index 000000000..b2b5a3b08 --- /dev/null +++ b/app/Models/Aggregators/RouteServerFilterAggregator.php @@ -0,0 +1,184 @@ +|RouteServerFilterAggregator whereActionAdvertise($value) + * @mixin \Eloquent + */ +class RouteServerFilterAggregator extends RouteServerFilter +{ + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'route_server_filters'; + + + /** + * Most functions will need the set of user and production filters + * + * @return Collection[] [ ufilters, pfilters ] + * + * @psalm-return list{Collection, Collection} + */ + private static function filters( Customer $c ): array + { + return [ + RouteServerFilter::whereCustomerId($c->id)->orderBy('order_by')->get(), + RouteServerFilterProd::whereCustomerId($c->id)->orderBy('order_by')->get() + ]; + } + + /** + * Check if the production filters are in sync with the user's editable filters + * @param Customer $c + * @return bool + */ + public static function inSync( Customer $c ): bool + { + [ $ufilters, $pfilters ] = self::filters($c); + + // both empty -> not in sync + if( !$ufilters->isEmpty() && $pfilters->isEmpty() ) { + return false; + } + + // unequal number of rules -> not in sync + if( $ufilters->count() !== $pfilters->count() ) { + return false; + } + + // we're sorting by order so rules should be equal on a rule by rule basis: + foreach( $ufilters as $id => $uf ) { + foreach( $uf->getAttributes() as $a => $v ) { + if( in_array( $a, [ 'id', 'created_at', 'updated_at', 'live' ] ) ) { + continue; // skip these attributes + } + + if( $pfilters[$id]->$a !== $v ) { + return false; + } + } + } + + return true; + } + + + /** + * Revert - reverse sync where we move production to user's rules + */ + public static function revert( Customer $c ): void + { + [ , $pfilters ] = self::filters($c); + + DB::transaction(function () use($pfilters, $c) { + // start by clearing out the user's filters + RouteServerFilter::whereCustomerId( $c->id )->delete(); + + // pfilters empty -> all done + if( !$pfilters->isEmpty() ) { + // unequal number of rules -> copy pfilters to ufilters + foreach( $pfilters as $pf ) { + RouteServerFilter::forceCreate( array_merge( + $pf->getAttributes(), + [ 'id' => null ] + ) ); + } + } + }); + } + + + /** + * Commit - sync staged / user's rules to production + */ + public static function commit( Customer $c ): void + { + [ $ufilters , ] = self::filters($c); + + DB::transaction(function () use($ufilters, $c) { + // start by clearing out the production + RouteServerFilterProd::whereCustomerId( $c->id )->delete(); + + // ufilters empty -> all done + if( !$ufilters->isEmpty() ) { + // unequal number of rules -> copy ufilters to pfilters + foreach( $ufilters as $pf ) { + RouteServerFilterProd::forceCreate( array_merge( + $pf->getAttributes(), + [ 'id' => null ] + ) ); + } + } + }); + } + + +} \ No newline at end of file diff --git a/app/Models/Aggregators/RouterAggregator.php b/app/Models/Aggregators/RouterAggregator.php new file mode 100644 index 000000000..4e78dd4bc --- /dev/null +++ b/app/Models/Aggregators/RouterAggregator.php @@ -0,0 +1,194 @@ + + */ + public static function forDropdown( ?Customer $cust = null, ?User $user = null ): array + { + $privs = $user ? $user->privs() : User::AUTH_PUBLIC; + $routers = self::whereNotNull( 'api' ) + ->where( 'api_type', 1 ) + ->where( 'lg_access', '<=', $privs ) + ->when( !$user, function( Builder $q ) { + return $q->where( 'quarantine', false ); + } ) + ->orderBy( 'handle' ) + ->get()->keyBy( 'handle' ); + + $result = []; + foreach( $routers as $key => $r ) { + if( $r->quarantine && $privs !== User::AUTH_SUPERUSER && !$cust->hasInterfacesInQuarantine() ) { + continue; + } + $result[ $r->type() ][ $key ] = $r->name; + } + + return $result; + } + + /** + * Gather the data for looking glass dropdowns + * + * This is the dropdown on the top right of the IXP Manager looking glass interface. + * + * @param Customer|null $cust + * @param User|null $user + * + * @return array[][] + * + * @psalm-return array>> + */ + public static function forTab( ?Customer $cust = null, ?User $user = null ): array + { + $privs = $user ? $user->privs() : User::AUTH_PUBLIC; + $routers = self:: +// select( [ +// 'routers.handle', 'routers.name', 'routers.updated_at' +// ] ) +// leftJoin( 'vlan as v', 'v.id', 'routers.vlan_id' ) +// ->leftJoin( 'infrastructure as i', 'i.id', 'v.infrastructureid' ) + whereNotNull( 'api' ) + ->where( 'api_type', 1 ) + ->where( 'lg_access', '<=', $privs ) + ->when( !$user, function( Builder $q ) { + return $q->where( 'quarantine', false ); + } ) + ->get(); + + $result = []; + foreach( $routers as $key => $r ) { + if( $r->quarantine && $privs !== User::AUTH_SUPERUSER && !$cust->hasInterfacesInQuarantine() ) { + continue; + } + $result[ $r->vlan->infrastructure->name ][ $r->protocol ][] = $r; + } + + return $result; + } +} \ No newline at end of file diff --git a/app/Models/Aggregators/RsPrefixAggregator.php b/app/Models/Aggregators/RsPrefixAggregator.php new file mode 100644 index 000000000..d3929675f --- /dev/null +++ b/app/Models/Aggregators/RsPrefixAggregator.php @@ -0,0 +1,390 @@ + + */ + private static function initialiseAggregateRouteSummariesArray(): array + { + $init = [ + 'total' => 0 + ]; + + foreach( RsPrefix::$SUMMARY_TYPES_FNS as $type => $fn ) + { + $init[ $type ] = [ + 4 => 0, + 6 => 0, + 'total' => 0 + ]; + } + + return $init; + } + + /** + * Return route acceptance counts for a specific customers as an aggregated array. + * + * A sample element of the array is (RS = Route Server): + * + * [ + * [total] => 10 // total routes of all types + * [adv_acc] => [ // routes advertised to the RS and accepted by the RS + * [4] => 6 // IPv4 + * [6] => 2 // IPv6 + * [total] => 8 // total + * ] + * [adv_nacc] => [ // routes advertised but not accepted (not in IRRDB) + * [4] => 0 + * [6] => 1 + * [total] => 1 + * ] + * [nadv_acc] => [ // routes not advertised but that would be accepted + * [4] => 0 + * [6] => 1 + * [total] => 1 + * ] + * ] + * + * + * @return array Route acceptance counts for all customers as an aggregated array + */ + public static function aggregateRouteSummariesForCustomer( $custid ): array + { + $summary = self::initialiseAggregateRouteSummariesArray(); + + foreach( RsPrefix::$SUMMARY_TYPES_FNS as $type => $fn ) { + foreach( [ 4, 6 ] as $protocol ) { + if( $sum = self::$fn( $protocol, $custid ) ) { + $summary[ $type ][ $protocol ] = $sum[0]['prefixes']; + $summary[ $type ]['total'] += $sum[0]['prefixes']; + $summary[ 'total' ] += $sum[0]['prefixes']; + } + } + } + + return $summary; + } + + /** + * Return route acceptance counts for all customers as an aggregated array. + * + * A sample element of the array is (RS = Route Server): + * + * [64] => [ // customer ID + * [total] => 10 // total routes of all types + * [adv_acc] => [ // routes advertised to the RS and accepted by the RS + * [4] => 6 // IPv4 + * [6] => 2 // IPv6 + * [total] => 8 // total + * ] + * [adv_nacc] => [ // routes advertised but not accepted (not in IRRDB) + * [4] => 0 + * [6] => 1 + * [total] => 1 + * ] + * [nadv_acc] => [ // routes not advertised but that would be accepted + * [4] => 0 + * [6] => 1 + * [total] => 1 + * ] + * [name] => Customer Name + * ] + * + * @return array[] Route acceptance counts for all customers as an aggregated array + * + * @psalm-return array + */ + public static function aggregateRouteSummaries(): array + { + $summary = []; + foreach( RsPrefix::$SUMMARY_TYPES_FNS as $type => $fn ) { + foreach( [ 4, 6 ] as $protocol ) { + foreach( self::$fn( $protocol ) as $route ) { + // initialise customer's summary array if necessary + if( !isset( $summary[ $route[ 'id' ] ] ) ) { + $summary[ $route['id'] ] = self::initialiseAggregateRouteSummariesArray(); + $summary[ $route['id'] ][ 'name' ] = $route['name']; + } + + $summary[ $route['id'] ][ $type ][ $protocol ] = $route['prefixes']; + $summary[ $route['id'] ][ $type ]['total'] += $route['prefixes']; + $summary[ $route['id'] ][ 'total' ] += $route['prefixes']; + } + } + } + + return $summary; + } + + /** + * Return categorised routes for a given customer as an aggregated array. + * + * A sample element of the array is (RS = Route Server): + * + * [ + * [adv_acc] => [ // Routes advertised and accepted + * [0] => [ + * [id] => 64 // Customer ID + * [name] => ABC Limited // Customer Name + * [protocol] => 4 // protocol (4,6) + * [irrdb] => 1 // 1 if the route is in IRRDB + * [prefix] => 192.0.2.0/24 // prefix + * [timestamp] => DateTime Object + * [rsorigin] => 65500 // origin AS + * ] + * ... + * ] + * [adv_nacc] => [ // Routes advertised but not accepted + * ... + * ] + * [nadv_acc] => [ // Routes not advertised but acceptable + * ... + * ] + * ] + * + * @param int $cust The customer ID to return routes for + * @param int|null protocol The (optional) protocol to limit results to (''4'', ''6'', ''NULL'') + * + * @return array Categorised routes for a given customer as an aggregated array. + */ + public static function aggregateRoutes( int $cust, ?int $protocol = null ): array + { + $aggRoutes = []; + + foreach( RsPrefix::$ROUTES_TYPES_FNS as $type => $fn ){ + $aggRoutes[ $type ] = self::$fn( $protocol, $cust ); + } + return $aggRoutes; + } + /** + * Returns a count of all routes advertised to the route server and accepted by + * it for all customers / a specific customer. + * + * @param int $protocol The protocol to count routes for (accepts ''4'' or ''6'') + * @param int|null $cust The customer ID to limit the results for + * + * @return bool|array Count of all routes advertised to the route server and accepted by it + */ + public static function summaryRoutesAdvertisedAndAccepted( $protocol, $cust = null ) + { + return self::getSummaryRoutes( $protocol, 1, false, $cust ); + } + + /** + * Returns a count of all routes not advertised to the route server but that would + * be accepted by it for all customers / a specific customer. + * + * @param int $protocol The protocol to count routes for (accepts ''4'' or ''6'') + * @param int|null $cust The customer ID to limit the results for + * + * @return bool|array Count of all routes not advertised to the route server but would be accepted by it + */ + public static function summaryRoutesAdvertisedAndNotAccepted( int $protocol, ?int $cust = null ) + { + return self::getSummaryRoutes( $protocol, 0, false, $cust ); + } + + /** + * Returns a count of all routes advertised to the route server but not accepted by + * it for all customers / a specific customer. + * + * @param int $protocol The protocol to count routes for (accepts ''4'' or ''6'') + * @param int|null $cust The customer ID to limit the results for + * + * @return bool|array Count of all routes advertised to the route server but not accepted by it + */ + public static function summaryRoutesNotAdvertisedButAcceptable( int $protocol, ?int $cust = null ) + { + return self::getSummaryRoutes( $protocol, 1, true, $cust ); + } + + /** + * Utility function used by the ''getSummaryRoutesXXX()'' function to query the database. + * + * The rules for routes are: + * + * * Advertised & Accepted: irrdb = 1 AND rs_origin IS NOT NULL + * * Advertised & NOT Accepted: irrdb = 0 AND rs_origin IS NOT NULL + * * Not Advertised & Acceptable: irrdb = 1 AND rs_origin IS NULL + * + * @param int $protocol The IP protocol to limit results to (accepts ''4'' or ''6'') + * @param int $irrdb Limit results to ''irrdb = 1'' or ''irrdb = 0'' + * @param bool $rsOriginIsNull Limit results depending on whether the rs_origin is null or not + * @param int|null $cust The customer ID to limit the results to + * + * @return bool|array The database query result (or false if none) + */ + public static function getSummaryRoutes( int $protocol, int $irrdb, bool $rsOriginIsNull, ?int $cust = null ) + { + $result = self::selectRaw( + 'cust.id AS id, cust.name AS name, + rs_prefixes.protocol AS protocol, rs_prefixes.irrdb AS irrdb, + count( rs_prefixes.protocol ) AS prefixes' + ) + ->from( 'rs_prefixes' ) + ->join( 'cust', 'cust.id', 'rs_prefixes.custid') + ->where( 'rs_prefixes.rs_origin', $rsOriginIsNull ? '=' : '!=', null ) + ->where( 'rs_prefixes.irrdb', $irrdb ) + ->where( 'rs_prefixes.protocol', $protocol ) + ->when( $cust, function( Builder $q, $cust ) { + return $q->where( 'cust.id', $cust ); + } ) + ->groupBy( 'cust.id' ) + ->orderByRaw( 'cust.name ASC, rs_prefixes.protocol ASC, rs_prefixes.irrdb ASC' ) + ->get(); + + + + if( $cust !== null ) + { + $result ? $result->first() : false; + } + + return $result->isNotEmpty() ? $result->toArray() : false; + } + + /** + * Returns all routes advertised to the route server and accepted by + * it for all customers / a specific customer. + * + * @param int|null $protocol The protocol to count routes for (accepts ''null'', ''4'' or ''6'') + * @param int|null $cust The customer ID to limit the results for + * + * @return array All routes advertised to the route server and accepted by it + */ + public static function routesAdvertisedAndAccepted( ?int $protocol = null, ?int $cust = null ): array + { + return self::getRoutes( 1, false, $protocol, $cust ); + } + + /** + * Returns all routes not advertised to the route server but that would + * be accepted by it for all customers / a specific customer. + * + * @param int|null $protocol The protocol to count routes for (accepts ''null'', ''4'' or ''6'') + * @param int|null $cust The customer ID to limit the results for + * + * @return array All routes not advertised to the route server but would be accepted by it + */ + public static function routesAdvertisedAndNotAccepted( $protocol = null, $cust = null ): array + { + return self::getRoutes( 0, false, $protocol, $cust ); + } + + /** + * Returns all routes not advertised to the route server but that would + * be accepted by it for all customers / a specific customer. + * + * @param int|null $protocol The protocol to count routes for (accepts ''null'', ''4'' or ''6'') + * @param int|null $cust The customer ID to limit the results for + * + * @return array All routes not advertised to the route server but would be accepted by it + */ + public static function routesNotAdvertisedButAcceptable( $protocol = null, $cust = null ): array + { + return self::getRoutes( 1, true, $protocol, $cust ); + } + + /** + * Utility function used by the ''getRoutesXXX()'' function to query the database. + * + * The rules for routes are: + * + * * Advertised & Accepted: irrdb = 1 AND rs_origin IS NOT NULL + * * Advertised & NOT Accepted: irrdb = 0 AND rs_origin IS NOT NULL + * * Not Advertised & Acceptable: irrdb = 1 AND rs_origin IS NULL + * + * @param int $irrdb Limit results to ''irrdb = 1'' or ''irrdb = 0'' + * @param bool $rsOriginIsNull Limit results depending on whether the rs_origin is null or not + * @param int|null $protocol The IP protocol to limit results to (accepts ''null'', ''4'' or ''6'') + * @param int|null $cust The customer ID to limit the results to + * + * @return array The database query result + */ + public static function getRoutes( int $irrdb, bool $rsOriginIsNull, ?int $protocol = null, ?int $cust = null ): array + { + return self::selectRaw( + 'cust.id AS id, cust.name AS name, rs_prefixes.protocol AS protocol, + rs_prefixes.irrdb AS irrdb, rs_prefixes.prefix AS prefix, + rs_prefixes.timestamp AS timestamp, rs_prefixes.rs_origin AS rsorigin' + ) + ->leftJoin( 'cust', 'cust.id', 'rs_prefixes.custid') + ->where( 'rs_prefixes.rs_origin', $rsOriginIsNull ? '=' : '!=', null ) + ->where( 'rs_prefixes.irrdb', $irrdb ) + ->when( $protocol, function( Builder $q, $protocol ) { + return $q->where( 'rs_prefixes.protocol', $protocol ); + } ) + ->when( $cust, function( Builder $q, $cust ) { + return $q->where( 'cust.id', $cust ); + } ) + ->orderByRaw( 'cust.name ASC, rs_prefixes.protocol ASC, rs_prefixes.irrdb ASC' ) + ->get()->toArray(); + } +} \ No newline at end of file diff --git a/app/Models/Aggregators/SwitchPortAggregator.php b/app/Models/Aggregators/SwitchPortAggregator.php new file mode 100644 index 000000000..de194219e --- /dev/null +++ b/app/Models/Aggregators/SwitchPortAggregator.php @@ -0,0 +1,77 @@ +from( 'switchport AS sp' ) + ->when( $notAssignToPI , function( Builder $q ) { + return $q->leftJoin( 'physicalinterface AS pi', 'pi.switchportid', 'sp.id' ) + ->where( 'pi.id', NULL); + }) + ->when( count( $types ) > 0 , function( Builder $q ) use( $types ) { + return $q->whereIn( 'sp.type', $types ); + }) + ->when( count( $excludedSpid ) > 0 , function( Builder $q ) use( $excludedSpid ) { + return $q->whereNotIn( 'sp.id', $excludedSpid ); + }) + ->where( 'sp.switchid', $switchid ) + ->orderBy( 'id', 'ASC' ) + ->get()->keyBy( 'id' )->toArray(); + } + + +} \ No newline at end of file diff --git a/app/Models/Aggregators/SwitcherAggregator.php b/app/Models/Aggregators/SwitcherAggregator.php new file mode 100644 index 000000000..8114051ac --- /dev/null +++ b/app/Models/Aggregators/SwitcherAggregator.php @@ -0,0 +1,324 @@ + $consoleServerConnections + * @property-read int|null $console_server_connections_count + * @property-read Infrastructure|null $infrastructureModel + * @property-read \Illuminate\Database\Eloquent\Collection $switchPorts + * @property-read int|null $switch_ports_count + * @property-read \IXP\Models\Vendor|null $vendor + * @method static Builder|SwitcherAggregator newModelQuery() + * @method static Builder|SwitcherAggregator newQuery() + * @method static Builder|SwitcherAggregator query() + * @method static Builder|SwitcherAggregator whereActive($value) + * @method static Builder|SwitcherAggregator whereAsn($value) + * @method static Builder|SwitcherAggregator whereCabinetid($value) + * @method static Builder|SwitcherAggregator whereCreatedAt($value) + * @method static Builder|SwitcherAggregator whereHostname($value) + * @method static Builder|SwitcherAggregator whereId($value) + * @method static Builder|SwitcherAggregator whereInfrastructure($value) + * @method static Builder|SwitcherAggregator whereIpv4addr($value) + * @method static Builder|SwitcherAggregator whereIpv6addr($value) + * @method static Builder|SwitcherAggregator whereLastPolled($value) + * @method static Builder|SwitcherAggregator whereLoopbackIp($value) + * @method static Builder|SwitcherAggregator whereLoopbackName($value) + * @method static Builder|SwitcherAggregator whereMauSupported($value) + * @method static Builder|SwitcherAggregator whereMgmtMacAddress($value) + * @method static Builder|SwitcherAggregator whereModel($value) + * @method static Builder|SwitcherAggregator whereName($value) + * @method static Builder|SwitcherAggregator whereNotes($value) + * @method static Builder|SwitcherAggregator whereOs($value) + * @method static Builder|SwitcherAggregator whereOsDate($value) + * @method static Builder|SwitcherAggregator whereOsVersion($value) + * @method static Builder|SwitcherAggregator wherePoll($value) + * @method static Builder|SwitcherAggregator whereSerialNumber($value) + * @method static Builder|SwitcherAggregator whereSnmpEngineBoots($value) + * @method static Builder|SwitcherAggregator whereSnmpEngineTime($value) + * @method static Builder|SwitcherAggregator whereSnmpSystemUptime($value) + * @method static Builder|SwitcherAggregator whereSnmppasswd($value) + * @method static Builder|SwitcherAggregator whereUpdatedAt($value) + * @method static Builder|SwitcherAggregator whereVendorid($value) + * @method static Builder|SwitcherAggregator whereMauSupported($value) + * @mixin \Eloquent + */ +class SwitcherAggregator extends Switcher +{ + /** + * Return an array of all switch names where the array key is the switch id + * + * @param int|null $infra + * @param int|null $location + * @param int|null $speed + + * @return Collection + */ + public static function getByLocationInfrastructureSpeed( ?int $infra = null, ?int $location = null, ?int $speed = null ): Collection + { + /** @psalm-suppress InvalidArgument - psalm can not recognise eloquent grouped conditions */ + return self::select( 'switch.*' ) + ->when( $location , function( Builder $q, $location ) { + return $q->leftJoin( 'cabinet AS c', 'c.id', 'switch.cabinetid' ) + ->where( 'c.locationid', $location ); + }) + ->when( $infra , function( Builder $q, $infra ) { + return $q->where( 'switch.infrastructure', $infra ); + }) + ->when( $speed , function( Builder $q, $speed ) { + return $q->leftjoin( 'switchport AS sp', 'sp.switchid', 'switch.id' ) + ->leftjoin( 'physicalinterface AS pi', 'pi.switchportid','sp.id' ) + ->leftjoin( 'virtualinterface AS vi', 'vi.id','pi.virtualinterfaceid' ) + ->leftjoin( 'vlaninterface AS vli', 'vli.virtualinterfaceid','vi.id' ) + ->leftjoin( 'ipv4address AS ipv4', 'ipv4.id', '=', 'vli.ipv4addressid' ) + ->leftjoin( 'ipv6address AS ipv6', 'ipv4.id', '=', 'vli.ipv6addressid' ) + ->where( function( $query ) use ( $speed ) { + $query->where( 'pi.speed', $speed ) + ->orWhere( 'pi.rate_limit', $speed ); + }); + }) + ->where( 'switch.active', true ) + ->orderBy( 'switch.name' )->distinct()->get(); + } + + /** + * Return an array of configurations + * + * @param int|null $switchid Switcher id for filtering results + * @param int|null $infraid Infrastructure id for filtering results + * @param int|null $facilityid Facility id for filtering results + * @param int|null $speed Speed filtering results + * @param int|null $vlanid Vlan id for filtering results + * @param bool $rsclient + * @param bool $ipv6enabled + * + * @return array + */ + public static function getConfiguration( ?int $switchid = null, ?int $infraid = null, ?int $facilityid = null, ?int $speed = null, ?int $vlanid = null, bool $rsclient = false, bool $ipv6enabled = false ): array + { + // BUGLET: see https://github.com/inex/IXP-Manager/issues/757 + // "Switch configuration port list erroneously lists non-rate limited port as rate limited" + + /** @psalm-suppress InvalidArgument - psalm can not recognise eloquent grouped conditions */ + return self::selectRaw( + 's.name AS switchname, + s.id AS switchid, + GROUP_CONCAT( sp.ifName ) AS ifName, + GROUP_CONCAT( pi.speed ) AS speed, + GROUP_CONCAT( pi.rate_limit ) AS rate_limit, + GROUP_CONCAT( pi.status ) AS portstatus, + cust.name AS customer, + cust.id AS custid, + cust.autsys AS asn, + MAX( vli.rsclient ) AS rsclient, + MAX( vli.ipv4enabled ) AS ipv4enabled, + MAX( vli.ipv6enabled ) AS ipv6enabled, + v.name AS vlan, + GROUP_CONCAT( DISTINCT ipv4.address ) AS ipv4address, + GROUP_CONCAT( DISTINCT ipv6.address ) AS ipv6address' + ) + ->from( 'vlaninterface AS vli' ) + ->leftjoin( 'ipv4address AS ipv4', 'ipv4.id', 'vli.ipv4addressid' ) + ->leftjoin( 'ipv6address AS ipv6', 'ipv6.id', 'vli.ipv6addressid' ) + ->leftjoin( 'vlan AS v', 'v.id', '=', 'vli.vlanid' ) + ->leftjoin( 'virtualinterface AS vi', 'vi.id','vli.virtualinterfaceid' ) + ->leftjoin( 'cust', 'cust.id','vi.custid' ) + ->leftjoin( 'physicalinterface AS pi', 'pi.virtualinterfaceid','vi.id' ) + ->leftjoin( 'switchport AS sp', 'sp.id', 'pi.switchportid' ) + ->leftjoin( 'switch AS s', 's.id', 'sp.switchid' ) + ->leftjoin( 'cabinet AS cab', 'cab.id', 's.cabinetid' ) + ->whereRaw( Customer::SQL_CUST_CURRENT ) + ->when( $switchid , function( Builder $q, $switchid ) { + return $q->where( 's.id', $switchid); + }) + ->when( $infraid , function( Builder $q, $infraid ) { + return $q->where( 's.infrastructure', $infraid ); + }) + ->when( $facilityid , function( Builder $q, $facilityid ) { + return $q->where( 'cab.locationid', $facilityid ); + }) + ->when( $speed , function( Builder $q, $speed ) { + return $q->where( function($query ) use ($speed) { + $query->where( 'pi.speed', $speed ) + ->orWhere( 'pi.rate_limit', $speed ); + } ); + }) + ->when( $vlanid , function( Builder $q, $vlanid ) { + return $q->where( 'vli.vlanid', $vlanid ); + }, function ( $query) { + return $query->where( 'v.private', false ); + }) + ->when( $rsclient , function( Builder $q ) { + return $q->where( 'vli.rsclient', true ); + }) + ->when( $ipv6enabled , function( Builder $q ) { + return $q->where( 'vli.ipv6enabled', true ); + }) + ->groupBy( 'customer', 'custid', 'asn', 'switchname', 'switchid', 'vlan', 'vi.id' ) + ->orderBy( 'customer', 'ASC' ) + ->get()->toArray(); + } + + + /** + * Returns all available switch ports for a switch. + * + * Restrict to only some types of switch port + * Exclude switch port ids from the list + * + * Suitable for other generic use. + * + * @param int $id Switch ID - switch to query + * @param array $types Switch port type restrict to some types only + * @param array $spid Switch port IDs, if set, those ports are excluded from the results + * @param bool $notAssignToPI + * @param bool $piNull + * @return array + */ + public static function allPorts( int $id, $types = [], $spid = [], bool $notAssignToPI = true, bool $piNull = true ): array + { + $ports = self::select( [ 'sp.name AS name', 'sp.type AS porttype', 'sp.id AS id' ] ) + ->leftJoin( 'switchport AS sp', 'sp.switchid', 'switch.id' ) + ->when( $notAssignToPI, function( Builder $q ) use( $piNull ) { + return $q->addSelect( [ 'pi.id AS pi_id' ] ) + ->leftJoin( 'physicalinterface as pi', 'pi.switchportid', 'sp.id' ) + ->when( $piNull , function( Builder $q ) { + return $q->whereNull( 'pi.id' ); + } ); + } )->where( 'switch.id', $id ) + ->when( count( $types ) > 0, function( Builder $q ) use( $types ) { + return $q->whereIn( 'sp.type', $types ); + } ) + ->when( $spid !== null && count( $spid ) > 0, function( Builder $q ) use( $spid ) { + return $q->whereNotIn( 'sp.id', $spid ); + } ) + ->orderBy( 'sp.id' ) + ->get()->keyBy( 'id' )->toArray(); + + foreach( $ports as $index => $port ){ + $ports[ $index ][ 'type' ] = SwitchPort::$TYPES[ $port[ 'porttype' ] ]; + $ports[ $index ][ 'spname-sptype' ] = $port[ "name" ] . ' (' . SwitchPort::$TYPES[ $port[ 'porttype' ] ] . ')'; + } + + return $ports; + } + + /** + * @param string $net + * @param string $side + * @param bool $maskneeded + * + * @return string + */ + public static function linkAddr( string $net, string $side, bool $maskneeded = true ): string + { + $ip = explode("/", $net )[ 0 ]; + $mask = explode("/", $net )[ 1 ]; + + $net = ip2long( $ip ) & ( 0xffffffff << ( 32 - $mask ) ); + $firstip = (int)$mask === 31 ? $net : $net + 1; + + $ip = strtolower( $side ) === 'a' ? long2ip( $firstip ) : long2ip($firstip + 1 ); + + if( $maskneeded ) { + $ip .= "/" . $mask; + } + + return $ip; + } + + /** + * Returns core bundle routing info for the specified switch ID + * + * @param Switcher $switch switch to query + * + * @return array + * + * @psalm-return array + */ + public static function coreBundleNeighbors( Switcher $switch ): array + { + /** @psalm-suppress InvalidArgument - psalm can not recognise eloquent grouped conditions */ + return self::query()->selectRaw( 'cb.type, cb.ipv4_subnet as cbSubnet, + cb.cost, cb.preference, cl.ipv4_subnet as clSubnet, sA.id as sAid, + sB.id as sBid, sA.name as sAname , sB.name as sBname, sA.asn as sAasn , + sB.asn as sBasn' ) + ->from( 'corelinks AS cl' ) + ->leftJoin( 'corebundles AS cb', 'cb.id', 'cl.core_bundle_id' ) + ->leftJoin( 'coreinterfaces AS ciA', 'ciA.id', 'cl.core_interface_sidea_id' ) + ->leftJoin( 'coreinterfaces AS ciB', 'ciB.id', 'cl.core_interface_sideb_id' ) + ->leftJoin( 'physicalinterface AS piA', 'piA.id', 'ciA.physical_interface_id' ) + ->leftJoin( 'physicalinterface AS piB', 'piB.id', 'ciB.physical_interface_id' ) + ->leftJoin( 'switchport AS spA', 'spA.id', 'piA.switchportid' ) + ->leftJoin( 'switchport AS spB', 'spB.id', 'piB.switchportid' ) + ->leftJoin( 'switch AS sA', 'sA.id', 'spA.switchid' ) + ->leftJoin( 'switch AS sB', 'sB.id', 'spB.switchid' ) + ->where( function ($query) use( $switch ) { + $query->where( 'sA.id', $switch->id ) + ->orWhere( 'sB.id', $switch->id ); + }) + ->whereIn( 'cb.type', [ CoreBundle::TYPE_ECMP, CoreBundle::TYPE_L3_LAG ] ) + ->get()->toArray(); + } +} diff --git a/app/Models/Aggregators/TrafficDailyPhysIntAggregator.php b/app/Models/Aggregators/TrafficDailyPhysIntAggregator.php new file mode 100644 index 000000000..63017431e --- /dev/null +++ b/app/Models/Aggregators/TrafficDailyPhysIntAggregator.php @@ -0,0 +1,179 @@ + array:8 [ + * "cid" => 1, + * "cname" => "ABC Ltd" + * "vname" => "INEX LAN1" + * "viid" => 1, + * "switch" => "swi1-kcp1-1" + * "in" => "9929169056" + * "out" => "348408392" + * "num_ports_in_lag" => "5" + * "vi_speed" => "50000" + * "util" => "99.29" + * ], + * ... + * } + * + * @see \IXP_Mrtg::$CATEGORIES + * @param string $day The day to load records for + * @param string $category The category of records to load (one of \IXP_Mrtg::$CATEGORIES) + * @param string $period + * @param int $vid + * + * @return array An array of all switch objects + */ + public static function loadTraffic( string $day, string $category, string $period, int $vid ): array + { + $period = Graph::processParameterPeriod( $period ); + + return self::selectRaw( + "c.id AS cid, c.abbreviatedName AS cname, ANY_VALUE( s.name ) as switch, + vi.id AS viid, + SUM( tdpi.{$period}_max_in ) AS max_in, + SUM( tdpi.{$period}_max_out ) AS max_out, + COUNT( pi.id ) AS num_ports_in_lag, + SUM( COALESCE( pi.rate_limit, pi.speed ) ) AS vi_speed, + ROUND( GREATEST( (MAX( tdpi.{$period}_max_in )/1000000/MAX( COALESCE( pi.rate_limit, pi.speed ) ))*100, (MAX( tdpi.{$period}_max_out )/1000000/MAX( COALESCE( pi.rate_limit, pi.speed ) ))*100 ), 2) AS util" + ) + ->from( 'traffic_daily_phys_ints AS tdpi' ) + ->leftJoin( 'physicalinterface AS pi', 'pi.id', 'tdpi.physicalinterface_id' ) + ->leftJoin( 'virtualinterface AS vi', 'vi.id', 'pi.virtualinterfaceid' ) + ->leftJoin( 'cust AS c', 'c.id', 'vi.custid' ) + ->leftJoin( 'switchport AS sp', 'sp.id', 'pi.switchportid' ) + ->leftJoin( 'switch AS s', 's.id', 'sp.switchid' ) + ->when( $vid , function( Builder $q, $vid ) { + return $q->leftJoin( 'vlaninterface AS vli', 'vli.virtualinterfaceid', 'vi.id' ) + ->leftJoin( 'vlan AS v', 'v.id', 'vli.vlanid' ) + ->where( 'v.id', $vid ); + } ) + ->where( 'tdpi.day', $day ) + ->where( 'tdpi.category', $category ) + ->groupBy( 'vi.id' )->get()->toArray(); + } +} diff --git a/app/Models/Aggregators/UserAggregator.php b/app/Models/Aggregators/UserAggregator.php new file mode 100644 index 000000000..49512e0d2 --- /dev/null +++ b/app/Models/Aggregators/UserAggregator.php @@ -0,0 +1,294 @@ + $apiKeys + * @property-read int|null $api_keys_count + * @property-read Customer|null $customer + * @property-read \Illuminate\Database\Eloquent\Collection $customerToUser + * @property-read int|null $customer_to_user_count + * @property-read \Illuminate\Database\Eloquent\Collection $customers + * @property-read int|null $customers_count + * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications + * @property-read int|null $notifications_count + * @property-read \IXP\Models\User2FA|null $user2FA + * @property-read \Illuminate\Database\Eloquent\Collection $userRememberTokens + * @property-read int|null $user_remember_tokens_count + * @method static Builder|User activeOnly() + * @method static Builder|User byPrivs(?int $priv = null) + * @method static \Illuminate\Database\Eloquent\Builder|UserAggregator newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|UserAggregator newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|UserAggregator query() + * @method static \Illuminate\Database\Eloquent\Builder|UserAggregator whereAuthorisedMobile($value) + * @method static \Illuminate\Database\Eloquent\Builder|UserAggregator whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|UserAggregator whereCreator($value) + * @method static \Illuminate\Database\Eloquent\Builder|UserAggregator whereCustid($value) + * @method static \Illuminate\Database\Eloquent\Builder|UserAggregator whereDisabled($value) + * @method static \Illuminate\Database\Eloquent\Builder|UserAggregator whereEmail($value) + * @method static \Illuminate\Database\Eloquent\Builder|UserAggregator whereExtraAttributes($value) + * @method static \Illuminate\Database\Eloquent\Builder|UserAggregator whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|UserAggregator whereLastupdatedby($value) + * @method static \Illuminate\Database\Eloquent\Builder|UserAggregator whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|UserAggregator wherePassword($value) + * @method static \Illuminate\Database\Eloquent\Builder|UserAggregator wherePeeringdbId($value) + * @method static \Illuminate\Database\Eloquent\Builder|UserAggregator wherePrefs($value) + * @method static \Illuminate\Database\Eloquent\Builder|UserAggregator wherePrivs($value) + * @method static \Illuminate\Database\Eloquent\Builder|UserAggregator whereUid($value) + * @method static \Illuminate\Database\Eloquent\Builder|UserAggregator whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|UserAggregator whereUsername($value) + * @property string|null $lastupdated + * @property string|null $created + * @method static \Illuminate\Database\Eloquent\Builder|UserAggregator whereCreated($value) + * @method static \Illuminate\Database\Eloquent\Builder|UserAggregator whereLastupdated($value) + * @mixin \Eloquent + */ +class UserAggregator extends User +{ + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'user'; + + /** + * Find or create a user from PeeringDB information. + * + * + * +pdbuser: array:8 [▼ + * "family_name" => "Bloggs" + * "email" => "ixpmanager@example.com" + * "name" => "Joe Bloggs" + * "verified_user" => true + * "verified_email" => true + * "networks" => array:2 [▼ + * 0 => array:4 [▼ + * "perms" => 1 + * "id" => 888 + * "name" => "INEX Route Collectors" + * "asn" => 65501 + * ] + * 1 => array:4 [▼ + * "perms" => 1 + * "id" => 777 + * "name" => "INEX Route Servers" + * "asn" => 65500 + * ] + * ] + * "id" => 666 + * "given_name" => "Joe" + * ] + * } + * + * @param array $pdbuser + * + * @return ((Customer|mixed)[]|User|mixed|null)[] + * + * @psalm-return array{user: User|mixed|null, added_to: list, removed_from: list} + */ + public static function findOrCreateFromPeeringDb( array $pdbuser ): array + { + // results to pass back: + $result = [ + 'user' => null, + 'added_to' => [], + 'removed_from' => [], + ]; + + // let's make sure we have a reason to do any work before we start: + $asns = []; + foreach( $pdbuser[ 'networks' ] as $nw ) { + if( is_numeric( $nw[ 'asn' ] ) && (int)$nw[ 'asn' ] > 0 ) { + $asns[] = (int)$nw[ 'asn' ]; + } + } + + if( !count( $asns ) ) { + Log::info( 'PeeringDB OAuth: no valid affiliated networks for ' . $pdbuser[ 'name' ] . '/' . $pdbuser[ 'email' ] ); + return $result; + } + + + if( Customer::whereIn( 'autsys', $asns )->count() === 0 ) { + Log::info( 'PeeringDB OAuth: no customers for attempted login from ' . $pdbuser[ 'name' ] . '/' . $pdbuser[ 'email' ] . ' with networks: ' . implode( ',', $asns ) ); + return $result; + } + + + // what privilege do we use? + $priv = isset( User::$PRIVILEGES_TEXT_NONSUPERUSER[ config( 'auth.peeringdb.privs' ) ] ) ? config( 'auth.peeringdb.privs' ) : User::AUTH_CUSTUSER; + + + // if we don't have a user already, create one with unique username + if( !( $user = User::where( 'peeringdb_id', $pdbuser['id'] )->first() ) ) { + + $un = strtolower( $pdbuser['name'] ?? 'unknownpdbuser' ); + $un = preg_replace( '/[^a-z0-9\._\-]/', '.', $un ); + $int = 0; + + do { + $int++; + $uname = $un . ( $int === 1 ? '' : "{$int}" ); + } while( User::where( 'username', $uname )->first() ); + $user = new User(); + $user->peeringdb_id = $pdbuser['id']; + $user->username = $uname; + $user->password = Hash::make( Str::random() ); + $user->privs = $priv; + $user->creator = 'OAuth-PeeringDB'; + $user->save(); + + $user_created = true; + Log::info( 'PeeringDB OAuth: created new user ' . $user->id . '/' . $user->username . ' for PeeringDB user: ' . $pdbuser[ 'name' ] . '/' . $pdbuser[ 'email' ] ); + } else { + $user_created = false; + Log::info( 'PeeringDB OAuth: found existing user ' . $user->id . '/' . $user->username . ' for PeeringDB user: ' . $pdbuser[ 'name' ] . '/' . $pdbuser[ 'email' ] ); + } + + $user->name = $pdbuser[ 'name' ]; + $user->email = $pdbuser[ 'email' ]; + $user->save(); + + $result[ 'user' ] = $user; + + // user updated or created now. + // we still need to link to customers + + // let's start with removing any customers that are no longer in the peeringdb networks list + foreach( $user->customerToUser as $c2u ) { + $cust = $c2u->customer; + $key = array_search( $cust->autsys, $asns ); + + if( $key === false || ( $key && !$cust->peeringdb_oauth ) ) { + // either user has a network that's not in the current peeringdb list of affiliated networks + // or user has a network that (now) indicates PeeringDB OAuth should be disabled + // then => if it came from peeringdb, remove it + $ea = $c2u->extra_attributes; + if( $ea && isset( $ea[ 'created_by' ][ 'type' ] ) && $ea[ 'created_by' ][ 'type' ] === 'PeeringDB' ) { + UserLoginHistory::where( 'customer_to_user_id', $c2u->id )->delete(); + // if this is the user's default / last logged in as customer, reset it: + if( !$user->customer || $user->custid === $c2u->customer_id ) { + $user->custid = null; + $user->save(); + } + + $result['removed_from'][] = $c2u->customer; + Log::info( 'PeeringDB OAuth: removing user ' . $user->id . '/' . $user->username . ' from ' . $cust->getFormattedName() ); + $c2u->delete(); + } + } else { + // we already have a link so take it out of the array + Log::info( 'PeeringDB OAuth: user ' . $user->id . '/' . $user->username . ' already linked to AS' . $asns[ $key ] ); + unset( $asns[ $key ] ); + } + } + + // what's left in $asns is potential new customers: + foreach( $asns as $asn ) { + if( $cust = Customer::where( 'autsys', $asn )->first() ) { + Log::info( 'PeeringDB OAuth: user ' . $user->id . '/' . $user->username . ' has PeeringDB affiliation with ' . $cust->getFormattedName() ); + + // is this a valid customer? + if( !( $cust->typeFull() || $cust->typeProBono() ) || $cust->statusSuspended() || $cust->hasLeft() || !$cust->peeringdb_oauth ) { + Log::info( 'PeeringDB OAuth: ' . $cust->getFormattedName() . ' not a suitable IXP Manager customer for PeeringDB, skipping.' ); + continue; + } + + $c2u = CustomerToUser::create([ + 'customer_id' => $cust->id, + 'user_id' => $user->id, + 'privs' => $priv, + 'extra_attributes' => [ "created_by" => [ "type" => "PeeringDB" ] ], + ]); + + $result['added_to'][] = $c2u->customer; + Log::info( 'PeeringDB OAuth: user ' . $user->id . '/' . $user->username . ' linked with with ' . $cust->getFormattedName() ); + + if( $user_created ) { + // should not emit any more UserCreatedEvent events + $user_created = false; + event( new UserCreatedEvent( $user ) ); + } else { + event( new UserAddedToCustomerEvent( $c2u ) ); + } + } + } + + // refresh from database + $user->refresh(); + + // do we actually have any customers after all this? + if( !$user->customers()->count() ) { + Log::info( 'PeeringDB OAuth: user ' . $user->id . '/' . $user->username . ' has no customers - deleting...' ); + + // delete all the user's API keys + $user->apiKeys()->delete(); + $user->delete(); + $result['user'] = null; + + } else if( $user->customer()->doesntExist() ) { + // set a default customer if we do not have one + Log::info( 'PeeringDB OAuth: user ' . $user->id . '/' . $user->username . ' given default customer: ' . $user->customers()->first()->getFormattedName() ); + $user->custid = $user->customers()->first()->id; + $user->save(); + } + + return $result; + } +} \ No newline at end of file diff --git a/app/Models/Aggregators/VirtualInterfaceAggregator.php b/app/Models/Aggregators/VirtualInterfaceAggregator.php new file mode 100644 index 000000000..bc73cbbcc --- /dev/null +++ b/app/Models/Aggregators/VirtualInterfaceAggregator.php @@ -0,0 +1,231 @@ + $macAddresses + * @property-read int|null $mac_addresses_count + * @property-read \Illuminate\Database\Eloquent\Collection $physicalInterfaces + * @property-read int|null $physical_interfaces_count + * @property-read \Illuminate\Database\Eloquent\Collection $sflowReceivers + * @property-read int|null $sflow_receivers_count + * @property-read \Illuminate\Database\Eloquent\Collection $vlanInterfaces + * @property-read int|null $vlan_interfaces_count + * @method static Builder|VirtualInterfaceAggregator newModelQuery() + * @method static Builder|VirtualInterfaceAggregator newQuery() + * @method static Builder|VirtualInterfaceAggregator query() + * @method static Builder|VirtualInterfaceAggregator whereChannelgroup($value) + * @method static Builder|VirtualInterfaceAggregator whereCreatedAt($value) + * @method static Builder|VirtualInterfaceAggregator whereCustid($value) + * @method static Builder|VirtualInterfaceAggregator whereDescription($value) + * @method static Builder|VirtualInterfaceAggregator whereFastlacp($value) + * @method static Builder|VirtualInterfaceAggregator whereId($value) + * @method static Builder|VirtualInterfaceAggregator whereLagFraming($value) + * @method static Builder|VirtualInterfaceAggregator whereMtu($value) + * @method static Builder|VirtualInterfaceAggregator whereName($value) + * @method static Builder|VirtualInterfaceAggregator whereTrunk($value) + * @method static Builder|VirtualInterfaceAggregator whereUpdatedAt($value) + * @mixin \Eloquent + */ +class VirtualInterfaceAggregator extends VirtualInterface +{ + /** + * Utility function to provide an array of all virtual interface objects on a given + * infrastructure + * + * @param Infrastructure $infra The infrastructure to gather VirtualInterfaces for + * @param int|bool $proto Either 4 or 6 to limit the results to interface with IPv4 / IPv6 + * @param bool $externalOnly If true (default) then only external (non-internal) interfaces will be returned + * + * @return Collection + * + * @throws + */ + public static function getForInfrastructure( Infrastructure $infra, $proto = false, $externalOnly = true ): Collection + { + return self::select( [ 'vi.*' ] ) + ->from( 'virtualinterface AS vi' ) + ->Join( 'cust AS cust', 'cust.id', 'vi.custid' ) + ->Join( 'vlaninterface AS vli', 'vli.virtualinterfaceid', 'vi.id' ) + ->Join( 'physicalinterface AS pi', 'pi.virtualinterfaceid', 'vi.id' ) + ->Join( 'switchport AS sp', 'sp.id', 'pi.switchportid' ) + ->Join( 'switch AS s', 's.id', 'sp.switchid' ) + ->Join( 'infrastructure AS i', 'i.id', 's.infrastructure' ) + ->where( 'i.id', $infra->id ) + ->whereRaw( Customer::SQL_CUST_ACTIVE ) + ->whereRaw( Customer::SQL_CUST_CURRENT ) + ->whereRaw( Customer::SQL_CUST_TRAFFICING ) + ->where( 'pi.status', PhysicalInterface::STATUS_CONNECTED ) + ->when( $proto , function( Builder $q, $proto ) { + $p = in_array( $proto, [ 4, 6 ], true ) ? $proto : 4; + return $q->whereRaw( "vli.ipv{$p}enabled = 1" ); + }) + ->when( $externalOnly , static function( Builder $q ) { + return $q->whereRaw( Customer::SQL_CUST_EXTERNAL ); + })->orderBy( 'cust.name' )->get()->keyBy( 'id' ); + } + + + /** + * Utility function to provide an array of all virtual interface objects on a given + * infrastructure + * + * @return array + * + * @throws + */ + public static function getByLocation(): array + { + return self::select( [ + 'cust.id AS customerid', + 'vi.id AS id', + 'pi.speed AS speed', + 'pi.rate_limit AS rlspeed', + 'i.id AS infrastructureid', + 'i.name AS infrastructure', + 'l.id AS locationid', + 'l.name AS locationname', + 'ca.id AS cabinetid', + 'ca.name AS cabinetname', + ] ) + ->from( 'virtualinterface AS vi' ) + ->Join( 'cust AS cust', 'cust.id', 'vi.custid' ) + ->Join( 'physicalinterface AS pi', 'pi.virtualinterfaceid', 'vi.id' ) + ->Join( 'switchport AS sp', 'sp.id', 'pi.switchportid' ) + ->Join( 'switch AS s', 's.id', 'sp.switchid' ) + ->Join( 'infrastructure AS i', 'i.id', 's.infrastructure' ) + ->Join( 'cabinet AS ca', 'ca.id', 's.cabinetid' ) + ->Join( 'location AS l', 'l.id', 'ca.locationid' ) + ->whereRaw( Customer::SQL_CUST_EXTERNAL ) + ->orderBy( 'cust.name' )->get()->toArray(); + } + + + /** + * Get statistics/percentage of connected customer by VLAN + * + * - customer type full or probono + * - at least one pi connected + * + * Returns an array of objects such as: + * + * [ + * {#4344 + * "vlanname": "INEX LAN1", + * "count": 105, + * "percent": "96.3303", + * }, + * ] + * + * @return array + */ + public static function getPercentageCustomersByVlan(): array + { + return self::selectRaw( + "v.name AS vlanname, + v.id AS vlanid, + COUNT( DISTINCT vi.custid ) AS count, + COUNT( DISTINCT vi.custid ) / ( + SELECT COUNT( DISTINCT vi.custid ) FROM virtualinterface AS vi + JOIN vlaninterface as vli ON vli.virtualinterfaceid = vi.id + JOIN vlan AS v ON v.id = vli.vlanid + JOIN cust AS c ON c.id = vi.custid + LEFT JOIN physicalinterface AS pi ON pi.virtualinterfaceid = vi.id + WHERE v.private = 0 AND pi.status = 1 AND c.`type`IN (1,4) ) * 100 AS percent" + ) + ->from( 'virtualinterface AS vi' ) + ->join( 'vlaninterface AS vli', 'vli.virtualinterfaceid', 'vi.id' ) + ->join( 'vlan AS v', 'v.id', 'vli.vlanid' ) + ->leftJoin( 'physicalinterface AS pi', 'pi.virtualinterfaceid', 'vi.id' ) + ->join( 'cust AS c', 'c.id', 'vi.custid' ) + ->where( 'v.private', false ) + ->where( 'pi.status', PhysicalInterface::STATUS_CONNECTED ) + ->whereIn( 'c.type', [1,4] ) + ->groupBy( 'v.name' ) + ->orderByDesc( 'count' )->get()->toArray(); + } + + /** + * For the given $vi, we want to ensure its channel group is unique + * within a switch + * + * @param VirtualInterface $vi + * + * @return bool + * + * @throws + */ + public static function validateChannelGroup( VirtualInterface $vi ): bool + { + if( $vi->channelgroup === null ) { + throw new GeneralException("Should not be testing a null channel group number"); + } + + if( $vi->physicalInterfaces()->count() === 0 ) { + throw new GeneralException("Channel group number is only relevant when there is at least one physical interface"); + } + + $vis = VirtualInterface::select( [ 'vi.id AS id' ] ) + ->from( 'virtualinterface AS vi' ) + ->join( 'physicalinterface AS pi', 'pi.virtualinterfaceid', 'vi.id' ) + ->join( 'switchport as sp', 'sp.id', 'pi.switchportid' ) + ->where( 'vi.channelgroup', $vi->channelgroup ) + ->whereIn( 'sp.switchid', function( $query ) use( $vi ) { + $query->select( [ 's.id' ] ) + ->from( 'switch AS s' ) + ->leftJoin( 'switchport AS sp', 'sp.switchid', 's.id') + ->leftJoin( 'physicalinterface AS pi', 'pi.switchportid', 'sp.id' ) + ->where( 'pi.virtualinterfaceid', $vi->id ); + } )->distinct()->get()->pluck( 'id' )->toArray(); + + return count( $vis ) === 0 || ( count( $vis ) === 1 && in_array( $vi->id, $vis, false ) ); + } +} \ No newline at end of file diff --git a/app/Models/Aggregators/VlanAggregator.php b/app/Models/Aggregators/VlanAggregator.php new file mode 100644 index 000000000..609434ca8 --- /dev/null +++ b/app/Models/Aggregators/VlanAggregator.php @@ -0,0 +1,234 @@ + $atlasRun + * @property-read int|null $atlas_run_count + * @property-read \IXP\Models\Infrastructure $infrastructure + * @property-read \Illuminate\Database\Eloquent\Collection $ipv4Addresses + * @property-read int|null $ipv4_addresses_count + * @property-read \Illuminate\Database\Eloquent\Collection $ipv6Addresses + * @property-read int|null $ipv6_addresses_count + * @property-read \Illuminate\Database\Eloquent\Collection $networksInfo + * @property-read int|null $networks_info_count + * @property-read \Illuminate\Database\Eloquent\Collection $routeServerFilters + * @property-read int|null $route_server_filters_count + * @property-read \Illuminate\Database\Eloquent\Collection $routers + * @property-read int|null $routers_count + * @property-read \Illuminate\Database\Eloquent\Collection $vlanInterfaces + * @property-read int|null $vlan_interfaces_count + * @method static \Illuminate\Database\Eloquent\Builder|VlanAggregator newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|VlanAggregator newQuery() + * @method static Builder|Vlan peeringManager() + * @method static Builder|Vlan privateOnly() + * @method static Builder|Vlan publicOnly() + * @method static \Illuminate\Database\Eloquent\Builder|VlanAggregator query() + * @method static \Illuminate\Database\Eloquent\Builder|VlanAggregator whereConfigName($value) + * @method static \Illuminate\Database\Eloquent\Builder|VlanAggregator whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|VlanAggregator whereExportToIxf($value) + * @method static \Illuminate\Database\Eloquent\Builder|VlanAggregator whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|VlanAggregator whereInfrastructureid($value) + * @method static \Illuminate\Database\Eloquent\Builder|VlanAggregator whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|VlanAggregator whereNotes($value) + * @method static \Illuminate\Database\Eloquent\Builder|VlanAggregator whereNumber($value) + * @method static \Illuminate\Database\Eloquent\Builder|VlanAggregator wherePeeringManager($value) + * @method static \Illuminate\Database\Eloquent\Builder|VlanAggregator wherePeeringMatrix($value) + * @method static \Illuminate\Database\Eloquent\Builder|VlanAggregator wherePrivate($value) + * @method static \Illuminate\Database\Eloquent\Builder|VlanAggregator whereUpdatedAt($value) + * @method static Builder|VlanAggregator publicProductionPeeringLan() + * @mixin \Eloquent + */ +class VlanAggregator extends Vlan +{ + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'vlan'; + + /** + * Get the IPv4 or IPv6 list for a vlan as an array. + * + * Returns a array sorted by IP address with elements: + * + * { + * id: "1040", // address ID from the IPv4/6 table + * address: "2001:7f8:18::20", // address + * v_id: "2", // VLAN id + * vli_id: "16" // VlanInterface ID (or null if not assigned / in use) + * }, + * + * @param int $vid The ID of the VLAN to query + * @param int $proto The IP protocol to get addresses for (one of RouterEntity::PROTOCOL_IPV4/6) + * + * @return array Array of addresses as defined above. + * + * @throws + */ + public static function ipAddresses( int $vid, int $proto ) : array + { + if( $proto === (int)Router::PROTOCOL_IPV6 ) { + $orderBy = 'INET6_ATON'; $table = 'ipv6address'; + } else if( $proto === (int)Router::PROTOCOL_IPV4 ) { + $orderBy = 'INET_ATON'; $table = 'ipv4address'; + } else { + throw new Exception('Invalid protocol' ); + } + + return Vlan::select( [ + "{$table}.id", "{$table}.address", + 'vlan.id AS vid', 'vli.id as vliid' + ] ) + ->leftJoin( $table, "{$table}.vlanid", 'vlan.id' ) + ->leftJoin( 'vlaninterface as vli', "vli.{$table}id", "{$table}.id" ) + ->where( 'vlan.id', $vid ) + ->orderByRaw( "{$orderBy}(address) ASC" ) + ->get()->toArray(); + + } + + /** + * Determine is an IP address /really/ free by checking across all vlans + * + * Returns a array of objects as follows (or empty array if not user): + * + * [ + * [ + * customer: { + * id: x, + * name: "", + * autsys: x, + * abbreviated_name: "" + * }, + * virtualinterface: { + * id: x + * }, + * vlaninterface: { + * id: x + * }, + * vlan: { + * id: x, + * name: "", + * number: x + * } + * }, + * { + * + * ] + * ] + * + * @param string $ip The IPv6/4 address to check + * @return array Array of object + */ + public static function usedAcrossVlans( string $ip ) : array + { + $table = strpos( $ip, ':' ) !== false ? 'ipv6address' : 'ipv4address' ; + + return Vlan::select( [ + 'c.id AS cid', 'c.name AS cname', 'c.autsys AS cautsys', 'c.abbreviatedName AS cabbreviatedname', + 'vi.id AS viid', 'vli.id AS vliid', + 'v.id AS vid', 'v.name AS vname', 'v.number AS vnumber' + ] ) + ->from( 'vlaninterface AS vli' ) + ->leftJoin( $table, "{$table}.id", "vli.{$table}id" ) + ->leftJoin( 'virtualinterface AS vi', 'vi.id', 'vli.virtualinterfaceid') + ->leftJoin( 'cust AS c', 'c.id', 'vi.custid' ) + ->leftJoin( 'vlan AS v', 'v.id', 'vli.vlanid') + ->where( "{$table}.address", $ip )->get()->toArray(); + } + + /** + * Utility function to provide an array of all VLAN interface IP addresses + * and hostnames on a given VLAN for a given protocol for the purpose of generating + * an ARPA DNS zone. + * + * Returns an array of elements such as: + * + * [ + * [enabled] => 1/0 + * [hostname] => ixp.rtr.example.com + * [address] => 192.0.2.0 / 2001:db8:67::56f + * ] + * + * @param Vlan $vlan The VLAN + * @param int $proto Either 4 or 6 + * + * @return array + * + * @throws + */ + public static function arpaDetails( Vlan $vlan, int $proto ): array + { + if( $proto === (int)Router::PROTOCOL_IPV6 ) { + $address = 'HEX(INET6_ATON( addr.address ))'; $table = 'ipv6address'; + } else if( $proto === (int)Router::PROTOCOL_IPV4 ) { + $address = 'INET_ATON( addr.address )'; $table = 'ipv4address'; + } else { + throw new Exception( 'Invalid protocol specified' ); + } + + $results = Vlan::selectRaw( + "vli.ipv{$proto}enabled as enabled, + addr.address AS address, + vli.ipv{$proto}hostname AS hostname, + {$address} AS aton" + ) + ->from( 'vlan AS v' ) + ->join( 'vlaninterface AS vli', 'vli.vlanid', 'v.id' ) + ->join( "{$table} AS addr", 'addr.id', "vli.{$table}id" ) + ->where( 'v.id', $vlan->id ) + ->whereNotNull( "vli.ipv{$proto}hostname" ) + ->where( "vli.ipv{$proto}hostname", '!=', '' ) + ->orderBy( 'aton' )->get()->toArray(); + + // to cast 0 or 1 to a boolean + return array_map( function( $e ) { + $e[ 'enabled' ] = (bool)$e[ 'enabled' ];$e[ 'aton' ] = (string)$e[ 'aton' ];return $e; + }, $results ); + } +} \ No newline at end of file diff --git a/app/Models/Aggregators/VlanInterfaceAggregator.php b/app/Models/Aggregators/VlanInterfaceAggregator.php new file mode 100644 index 000000000..ff19cda44 --- /dev/null +++ b/app/Models/Aggregators/VlanInterfaceAggregator.php @@ -0,0 +1,399 @@ + $layer2addresses + * @property-read int|null $layer2addresses_count + * @property-read \IXP\Models\VirtualInterface|null $virtualInterface + * @property-read Vlan|null $vlan + * @method static Builder|VlanInterfaceAggregator newModelQuery() + * @method static Builder|VlanInterfaceAggregator newQuery() + * @method static Builder|VlanInterfaceAggregator query() + * @method static Builder|VlanInterfaceAggregator whereAs112client($value) + * @method static Builder|VlanInterfaceAggregator whereBgpmd5secret($value) + * @method static Builder|VlanInterfaceAggregator whereBusyhost($value) + * @method static Builder|VlanInterfaceAggregator whereCreatedAt($value) + * @method static Builder|VlanInterfaceAggregator whereId($value) + * @method static Builder|VlanInterfaceAggregator whereIpv4addressid($value) + * @method static Builder|VlanInterfaceAggregator whereIpv4bgpmd5secret($value) + * @method static Builder|VlanInterfaceAggregator whereIpv4canping($value) + * @method static Builder|VlanInterfaceAggregator whereIpv4enabled($value) + * @method static Builder|VlanInterfaceAggregator whereIpv4hostname($value) + * @method static Builder|VlanInterfaceAggregator whereIpv4monitorrcbgp($value) + * @method static Builder|VlanInterfaceAggregator whereIpv6addressid($value) + * @method static Builder|VlanInterfaceAggregator whereIpv6bgpmd5secret($value) + * @method static Builder|VlanInterfaceAggregator whereIpv6canping($value) + * @method static Builder|VlanInterfaceAggregator whereIpv6enabled($value) + * @method static Builder|VlanInterfaceAggregator whereIpv6hostname($value) + * @method static Builder|VlanInterfaceAggregator whereIpv6monitorrcbgp($value) + * @method static Builder|VlanInterfaceAggregator whereIrrdbfilter($value) + * @method static Builder|VlanInterfaceAggregator whereMaxbgpprefix($value) + * @method static Builder|VlanInterfaceAggregator whereMcastenabled($value) + * @method static Builder|VlanInterfaceAggregator whereNotes($value) + * @method static Builder|VlanInterfaceAggregator whereRsclient($value) + * @method static Builder|VlanInterfaceAggregator whereRsmorespecifics($value) + * @method static Builder|VlanInterfaceAggregator whereUpdatedAt($value) + * @method static Builder|VlanInterfaceAggregator whereVirtualinterfaceid($value) + * @method static Builder|VlanInterfaceAggregator whereVlanid($value) + * @mixin \Eloquent + */ +class VlanInterfaceAggregator extends VlanInterface +{ + + /** + * Utility function to provide an array of VLAN interface objects on a given VLAN. + * + * @param Vlan $vlan The VLAN to gather VlanInterfaces for + * @param bool|mixed $protocol Either 4 or 6 to limit the results to interface with IPv4 / IPv6 + * + * @return Collection + * + */ + public static function forVlan( Vlan $vlan, $protocol = false ) + { + return self::select( [ 'vli.*' ] ) + ->from( 'vlaninterface AS vli' ) + ->join( 'vlan AS v', 'v.id', 'vli.vlanid' ) + ->join( 'virtualinterface AS vi', 'vi.id', 'vli.virtualinterfaceid' ) + ->join( 'physicalinterface AS pi', 'pi.virtualinterfaceid', 'vi.id' ) + ->join( 'cust', 'cust.id', 'vi.custid' ) + ->where( 'v.id', $vlan->id ) + ->whereRaw( Customer::SQL_CUST_ACTIVE ) + ->whereRaw( Customer::SQL_CUST_CURRENT ) + ->whereRaw( Customer::SQL_CUST_TRAFFICING ) + ->whereRaw( Customer::SQL_CUST_EXTERNAL ) + ->where( 'pi.status', PhysicalInterface::STATUS_CONNECTED ) + ->when( $protocol , function( Builder $q, $proto ) { + $p = in_array( $proto, [ 4, 6 ], true ) ? $proto : 4; + return $q->whereRaw( "vli.ipv{$p}enabled = 1" ); + }) + ->orderBy( 'cust.name' )->get()->keyBy( 'id' ); + } + + /** + * Utility function to provide an array of all VLAN interfaces on a given + * VLAN for a given protocol. + * + * Returns an array of elements such as: + * + * [ + * [cid] => 999 + * [cname] => Customer Name + * [abrevcname] => Abbreviated Customer Name + * [cshortname] => shortname + * [autsys] => 65500 + * [gmaxprefixes] => 20 // from cust table (global) + * [peeringmacro] => ABC + * [peeringmacrov6] => ABC + * [vid] => 2 + * [vtag] => 10, + * [vname] => "Peering LAN #1 + * [viid] => 120 + * [vliid] => 159 + * [canping] => 1 + * [enabled] => 1 // VLAN interface enabled for requested protocol? + * [address] => 192.0.2.123 // assigned address for requested protocol? + * [monitorrcbgp] => 1 + * [bgpmd5secret] => qwertyui // MD5 for requested protocol + * [hostname] => hostname // Hostname + * [maxbgpprefix] => 20 // VLAN interface max prefixes + * [as112client] => 1 // if the member is an as112 client or not + * [rsclient] => 1 // if the member is a route server client or not + * [rsmorespecifics] => 0/1 // if IRRDB filtering should allow more specifics + * [busyhost] + * [sid] + * [sname] + * [cabid] + * [cabname] + * [location_name] + * [location_tag] + * [location_shortname] + * ] + * + * @param Vlan $vlan The VLAN + * @param int $proto Either 4 or 6 + * @param ?int $pistatus The status of the physical interface + * + * @return array + * + * @throws + */ + public static function forProto( Vlan $vlan, int $proto, ?int $pistatus = PhysicalInterface::STATUS_CONNECTED ) : array + { + if( !in_array( $proto, [ 4, 6 ] ) ){ + $proto = 4; + } + + $q = self::select( [ + 'cust.id AS cid', 'cust.name AS cname', + 'cust.abbreviatedName AS abrevcname', + 'cust.shortname AS cshortname', + 'cust.autsys AS autsys', 'cust.maxprefixes AS gmaxprefixes', + 'cust.peeringmacro AS peeringmacro', 'cust.peeringmacrov6 AS peeringmacrov6', + + 'v.id AS vid', 'v.number AS vtag', 'v.name AS vname', 'vi.id AS viid', + + 'vli.id AS vliid', + + "vli.ipv{$proto}enabled AS enabled" , + "vli.ipv{$proto}hostname AS hostname" , + "vli.ipv{$proto}monitorrcbgp AS monitorrcbgp" , + "vli.ipv{$proto}bgpmd5secret AS bgpmd5secret" , + 'vli.maxbgpprefix AS maxbgpprefix' , + 'vli.as112client AS as112client' , + 'vli.rsclient AS rsclient' , + 'vli.busyhost AS busyhost' , + 'vli.irrdbfilter AS irrdbfilter' , + 'vli.rsmorespecifics AS rsmorespecifics' , + "vli.ipv{$proto}canping AS canping" , + + 'addr.address AS address', + + 's.id AS sid' , + 's.name AS sname' , + + 'cab.id AS cabid' , + 'cab.name AS cabname' , + + 'l.id AS location_id' , + 'l.name AS location_name' , + 'l.shortname AS location_shortname' , + 'l.tag AS location_tag' + ] ) + ->from( 'vlaninterface AS vli' ) + ->leftJoin( 'virtualinterface AS vi', 'vi.id', 'vli.virtualinterfaceid' ) + ->leftJoin( "ipv{$proto}address AS addr", 'addr.id', "vli.ipv{$proto}addressid" ) + ->leftJoin( 'cust', 'cust.id', 'vi.custid' ) + ->leftJoin( 'physicalinterface AS pi', 'pi.virtualinterfaceid', 'vi.id' ) + ->leftjoin( 'switchport AS sp', 'sp.id', 'pi.switchportid' ) + ->leftJoin( 'switch AS s', 's.id', 'sp.switchid') + ->leftJoin( 'cabinet AS cab', 'cab.id', 's.cabinetid') + ->leftJoin( 'location AS l', 'l.id', 'cab.locationid') + ->leftJoin( 'vlan AS v', 'v.id', 'vli.vlanid' ) + ->where( 'v.id', $vlan->id ) + ->whereRaw( Customer::SQL_CUST_ACTIVE ) + ->whereRaw( Customer::SQL_CUST_CURRENT ) + ->whereRaw( Customer::SQL_CUST_TRAFFICING ); + + if( $pistatus !== null ) { + $q->where( 'pi.status', $pistatus ); + } + + $q->groupByRaw( "vli.id, cust.id, cust.name, cust.abbreviatedName, cust.shortname, cust.autsys, + cust.maxprefixes, cust.peeringmacro, cust.peeringmacrov6, + vli.ipv{$proto}enabled, addr.address, vli.ipv{$proto}bgpmd5secret, vli.maxbgpprefix, + vli.ipv{$proto}hostname, vli.ipv{$proto}monitorrcbgp, vli.busyhost, + vli.as112client, vli.rsclient, vli.irrdbfilter, vli.ipv{$proto}canping, + s.id, s.name, + cab.id, cab.name, + l.name, l.shortname, l.tag" ) + ->orderByRaw( 'cust.autsys ASC, vli.id ASC' ); + + return $q->get()->toArray(); + } + + + + /** + * Find all IP addresses on a given VLAN for a given ASN and protocol. + * + * This is used (for example) when generating router configuration + * which prevents next-hop hijacking but allows the same ASN to + * set its other IPs as the next hop. + * + * @param Vlan $v + * @param int $asn + * @param int $proto + * + * @throws + * + * @psalm-return list + */ + public static function getAllIPsForASN( Vlan $v, int $asn, int $proto ): array + { + if( !in_array( $proto, [ 4,6 ] , true ) ) { + throw new \Exception( 'Invalid inet protocol' ); + } + + $ips = Vlan::select( [ 'ip.address' ] ) + ->from( 'vlaninterface AS vli' ) + ->leftJoin( 'vlan AS v', 'v.id', 'vli.vlanid' ) + ->leftJoin( "ipv{$proto}address AS ip", 'ip.id', "vli.ipv{$proto}addressid" ) + ->leftJoin( 'virtualinterface AS vi', 'vi.id', 'vli.virtualinterfaceid') + ->leftJoin( 'cust', 'cust.id', 'vi.custid') + ->where( 'cust.autsys', $asn )->where( 'v.id', $v->id ) + ->get()->pluck( 'address' )->toArray(); + + $vips = []; + foreach( $ips as $ip ) { + if( filter_var( $ip, FILTER_VALIDATE_IP ) ) { + $vips[] = $ip; + } + } + + return $vips; + } + + /** + * Utility function to get and return active VLAN interfaces on the requested protocol + * suitable for route collector / server configuration. + * + * Sample return: + * + * [ + * [cid] => 999 + * [cname] => Customer Name + * [cshortname] => shortname + * [autsys] => 65000 + * [peeringmacro] => QWE // or AS65500 if not defined + * [vliid] => 159 + * [fvliid] => 00159 // formatted %05d + * [address] => 192.0.2.123 + * [bgpmd5secret] => qwertyui // or false + * [as112client] => 1 // if the member is an as112 client or not + * [rsclient] => 1 // if the member is a route server client or not + * [maxprefixes] => 20 + * [irrdbfilter] => 0/1 // if IRRDB filtering should be applied + * [rsmorespecifics] => 0/1 // if IRRDB filtering should allow more specifics + * [location_name] => Interxion DUB1 + * [location_shortname] => IX-DUB1 + * [location_tag] => ix1 + * [vlanid] => 2 + * ] + * + * @param Vlan $vlan + * @param int $protocol + * @param int $target + * @param bool $quarantine + * + * @return array As defined above + * + */ + public static function sanitiseVlanInterfaces( Vlan $vlan, int $protocol = 4, int $target = Router::TYPE_ROUTE_SERVER, bool $quarantine = false ): array + { + $ints = self::forProto( $vlan, $protocol, $quarantine ? null : PhysicalInterface::STATUS_CONNECTED ); + + $newints = []; + + foreach( $ints as $index => $int ) { + + if( !$int['enabled'] ) { + continue; + } + + $int['protocol'] = $protocol; + $int['vlanid'] = $int['vid']; + + // don't need this anymore: + unset( $int['enabled'] ); + + if( $target === Router::TYPE_ROUTE_SERVER && !$int['rsclient'] ) { + continue; + } + + if( $target === Router::TYPE_AS112 && !$int['as112client'] ) { + continue; + } + + $int['fvliid'] = sprintf( '%04d', $int['vliid'] ); + + if( $int['maxbgpprefix'] && $int['maxbgpprefix'] > $int['gmaxprefixes'] ) { + $int['maxprefixes'] = $int['maxbgpprefix']; + } else { + $int['maxprefixes'] = $int['gmaxprefixes']; + } + + if( !$int['maxprefixes'] ) { + $int['maxprefixes'] = 250; + } + + unset( $int['gmaxprefixes'] ); + unset( $int['maxbgpprefix'] ); + + if( $protocol === 6 && $int['peeringmacrov6'] ) { + $int['peeringmacro'] = $int['peeringmacrov6']; + } + + if( !$int['peeringmacro'] ) { + $int['peeringmacro'] = 'AS' . $int['autsys']; + } + + unset( $int['peeringmacrov6'] ); + + if( !$int['bgpmd5secret'] ) { + $int['bgpmd5secret'] = false; + } + + $int['allpeeringips'] = self::getAllIPsForASN( $vlan, $int['autsys'], $protocol ); + + // 2021-09 We now load these dynamically on a per neighbour basis in the configuration templates. + // if( $int['irrdbfilter'] ) { + // $int['irrdbfilter_prefixes'] = IrrdbAggregator::prefixesForRouterConfiguration( $int[ 'cid' ], $protocol ); + // $int['irrdbfilter_asns' ] = IrrdbAggregator::asnsForRouterConfiguration( $int[ 'cid' ], $protocol ); + // } + + $newints[ $int['address'] ] = $int; + } + + return $newints; + } +} \ No newline at end of file diff --git a/app/Models/ApiKey.php b/app/Models/ApiKey.php new file mode 100644 index 000000000..6b6ecaadf --- /dev/null +++ b/app/Models/ApiKey.php @@ -0,0 +1,90 @@ + + */ + public function user(): BelongsTo + { + return $this->belongsTo(User::class, 'user_id' ); + } +} \ No newline at end of file diff --git a/app/Models/AtlasMeasurement.php b/app/Models/AtlasMeasurement.php new file mode 100644 index 000000000..7e12177e0 --- /dev/null +++ b/app/Models/AtlasMeasurement.php @@ -0,0 +1,143 @@ +|AtlasMeasurement newQuery() + * @mixin Eloquent + */ + +class AtlasMeasurement extends Model +{ + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'atlas_measurements'; + + /** + * The attributes that aren't mass assignable. + * + * @var array + */ + protected $fillable = [ + 'run_id', + 'cust_source', + 'cust_dest', + 'atlas_id', + 'atlas_create', + 'atlas_request', + 'atlas_start', + 'atlas_stop', + 'atlas_data', + 'atlas_state', + ]; + + /** + * Get the atlas run + * + * @psalm-return BelongsTo + */ + public function atlasRun(): BelongsTo + { + return $this->belongsTo( AtlasRun::class, 'run_id'); + } + + /** + * Get the customer source + * + * @psalm-return BelongsTo + */ + public function custSource(): BelongsTo + { + return $this->belongsTo(Customer::class, 'cust_source'); + } + + /** + * Get the customer destination + * + * @psalm-return BelongsTo + */ + public function custDest(): BelongsTo + { + return $this->belongsTo(Customer::class, 'cust_dest'); + } + + /** + * Get the atlas result for a measurement + * + * @psalm-return HasOne + */ + public function atlasResult(): HasOne + { + return $this->hasOne( AtlasResult::class, 'measurement_id'); + } +} \ No newline at end of file diff --git a/app/Models/AtlasProbe.php b/app/Models/AtlasProbe.php new file mode 100644 index 000000000..c48bc8db1 --- /dev/null +++ b/app/Models/AtlasProbe.php @@ -0,0 +1,158 @@ +|AtlasProbe newQuery() + * @mixin Eloquent + */ +class AtlasProbe extends Model +{ + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'atlas_probes'; + + /** + * The attributes that aren't mass assignable. + * + * @var array + */ + protected $fillable = [ + 'cust_id', + 'atlas_id', + 'v4_enabled', + 'v6_enabled', + 'address_v4', + 'address_v6', + 'is_anchor', + 'is_public', + 'status', + 'api_data', + 'asn', + 'last_connected', + ]; + + /** + * Get the customer that owns the Atlas probe + * + * @psalm-return BelongsTo + */ + public function customer(): BelongsTo + { + return $this->belongsTo(Customer::class, 'cust_id'); + } + + /** + * Scope a query to only include atlas probes for a given customer. + * + * @param Builder $query + * @param int $custid + * + * @return Builder + */ + public function scopeForCustomer( Builder $query, int $custid ): Builder + { + return $query->where('cust_id', $custid ); + } + + /** + * Scope a query to only include atlas probes for a given atlas id. + * + * @param Builder $query + * @param int $atlasid + * + * @return Builder + */ + public function scopeForAtlas( Builder $query, int $atlasid ): Builder + { + return $query->where('atlas_id', $atlasid ); + } + + /** + * Scope a query to only include atlas probes for a given protocol. + * + * @param Builder $query + * @param int $protocol + * + * @return Builder + */ + public function scopeForActiveProtocol( Builder $query, int $protocol ): Builder + { + return $query->where( 'v' . $protocol . '_enabled', 1 ); + } +} \ No newline at end of file diff --git a/app/Models/AtlasResult.php b/app/Models/AtlasResult.php new file mode 100644 index 000000000..bbd7f5aa8 --- /dev/null +++ b/app/Models/AtlasResult.php @@ -0,0 +1,85 @@ + + */ + public function atlasMeasurement(): BelongsTo + { + return $this->belongsTo(AtlasMeasurement::class ); + } +} \ No newline at end of file diff --git a/app/Models/AtlasRun.php b/app/Models/AtlasRun.php new file mode 100644 index 000000000..43c678198 --- /dev/null +++ b/app/Models/AtlasRun.php @@ -0,0 +1,122 @@ + $atlasMeasurements + * @property-read int|null $atlas_measurements_count + * @property-read \IXP\Models\Vlan|null $vlan + * @method static Builder|AtlasRun newModelQuery() + * @method static Builder|AtlasRun newQuery() + * @method static Builder|AtlasRun query() + * @method static Builder|AtlasRun whereCompletedAt($value) + * @method static Builder|AtlasRun whereCreatedAt($value) + * @method static Builder|AtlasRun whereId($value) + * @method static Builder|AtlasRun whereProtocol($value) + * @method static Builder|AtlasRun whereScheduledAt($value) + * @method static Builder|AtlasRun whereStartedAt($value) + * @method static Builder|AtlasRun whereUpdatedAt($value) + * @method static Builder|AtlasRun whereVlanId($value) + * @mixin Eloquent + */ +class AtlasRun extends Model +{ + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'atlas_runs'; + + /** + * The attributes that aren't mass assignable. + * + * @var array + */ + protected $fillable = [ + 'protocol', + 'started_at', + 'scheduled_at', + 'created_at', + 'completed_at', + 'vlan_id' + ]; + + /** + * CONST SCHEDULED + */ + public const SCHEDULED_AT_NOW = 1; + public const SCHEDULED_AT_DATETIME = 2; + + /** + * @var array Email ids to classes + */ + public static $SCHEDULED_AT = [ + self::SCHEDULED_AT_NOW => 'Run immediately', + self::SCHEDULED_AT_DATETIME => 'Run scheduled at' + ]; + + /** + * Get the atlas measurements + * + * @psalm-return HasMany + */ + public function atlasMeasurements(): HasMany + { + return $this->hasMany( AtlasMeasurement::class, 'run_id'); + } + + /** + * Get the vlan + * + * @psalm-return BelongsTo + */ + public function vlan(): BelongsTo + { + return $this->belongsTo(Vlan::class, 'vlan_id'); + } +} \ No newline at end of file diff --git a/app/Models/BgpSession.php b/app/Models/BgpSession.php new file mode 100644 index 000000000..f0cba0c8e --- /dev/null +++ b/app/Models/BgpSession.php @@ -0,0 +1,54 @@ + $consoleServers + * @property-read int|null $console_servers_count + * @property-read Collection $customerEquipment + * @property-read int|null $customer_equipment_count + * @property-read \IXP\Models\Location|null $location + * @property-read Collection $patchPanels + * @property-read int|null $patch_panels_count + * @property-read Collection $switchers + * @property-read int|null $switchers_count + * @method static Builder|Cabinet newModelQuery() + * @method static Builder|Cabinet newQuery() + * @method static Builder|Cabinet query() + * @method static Builder|Cabinet whereColocation($value) + * @method static Builder|Cabinet whereCreatedAt($value) + * @method static Builder|Cabinet whereHeight($value) + * @method static Builder|Cabinet whereId($value) + * @method static Builder|Cabinet whereLocationid($value) + * @method static Builder|Cabinet whereName($value) + * @method static Builder|Cabinet whereNotes($value) + * @method static Builder|Cabinet whereType($value) + * @method static Builder|Cabinet whereUCountsFrom($value) + * @method static Builder|Cabinet whereUpdatedAt($value) + * @property string|null $cololocation + * @method static Builder|Cabinet whereCololocation($value) + * @mixin \Eloquent + */ +class Cabinet extends Model +{ + use Observable; + + /** + * Constants to indicate whether 'u' positions count from top or bottom + */ + public const U_COUNTS_FROM_TOP = 1; + public const U_COUNTS_FROM_BOTTOM = 2; + + /** + * @var array Textual representations of where u's count from + */ + public static $U_COUNTS_FROM = [ + self::U_COUNTS_FROM_TOP => 'Top', + self::U_COUNTS_FROM_BOTTOM => 'Bottom', + ]; + + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'cabinet'; + + /** + * The attributes that are mass assignable. + * + * @var array + */ + protected $fillable = [ + 'locationid', + 'name', + 'colocation', + 'height', + 'type', + 'notes', + 'u_counts_from' + ]; + + /** + * Get the switchers for the cabinet + * + * @psalm-return HasMany + */ + public function switchers(): HasMany + { + return $this->hasMany(Switcher::class, 'cabinetid' ); + } + + /** + * Get the customerEquipments for the cabinet + * + * @psalm-return HasMany + */ + public function customerEquipment(): HasMany + { + return $this->hasMany(CustomerEquipment::class, 'cabinetid' ); + } + + /** + * Get the console servers for the cabinet + * + * @psalm-return HasMany + */ + public function consoleServers(): HasMany + { + return $this->hasMany(ConsoleServer::class, 'cabinet_id' ); + } + + /** + * Get the patch panels for the cabinet + * + * @psalm-return HasMany + */ + public function patchPanels(): HasMany + { + return $this->hasMany(PatchPanel::class, 'cabinet_id' ); + } + + /** + * Get the location for the cabinet + * + * @psalm-return BelongsTo + */ + public function location(): BelongsTo + { + return $this->belongsTo(Location::class, 'locationid' ); + } + + /** + * String to describe the model being updated / deleted / created + * + * @param Model $model + * + * @return string + */ + public static function logSubject( Model $model ): string + { + return sprintf( + "Rack (Cabinet) [id:%d] '%s'", + $model->id, + $model->name, + ); + } +} \ No newline at end of file diff --git a/app/Models/CompanyBillingDetail.php b/app/Models/CompanyBillingDetail.php new file mode 100644 index 000000000..453a3a207 --- /dev/null +++ b/app/Models/CompanyBillingDetail.php @@ -0,0 +1,167 @@ + 'Email', + self::INVOICE_METHOD_POST => 'Post' + ]; + + public const BILLING_FREQUENCY_MONTHLY = 'MONTHLY'; + public const BILLING_FREQUENCY_2MONTHLY = '2MONTHLY'; + public const BILLING_FREQUENCY_QUARTERLY = 'QUARTERLY'; + public const BILLING_FREQUENCY_HALFYEARLY = 'HALFYEARLY'; + public const BILLING_FREQUENCY_ANNUALLY = 'ANNUALLY'; + public const BILLING_FREQUENCY_NOBILLING = 'NOBILLING'; + + public static $BILLING_FREQUENCIES = [ + self::BILLING_FREQUENCY_MONTHLY => 'Monthly', + self::BILLING_FREQUENCY_2MONTHLY => 'Every 2 Months', + self::BILLING_FREQUENCY_QUARTERLY => 'Quarterly', + self::BILLING_FREQUENCY_HALFYEARLY => 'Half-Yearly', + self::BILLING_FREQUENCY_ANNUALLY => 'Annually', + self::BILLING_FREQUENCY_NOBILLING => 'No Billing' + ]; + + /** + * Get the customer for the company billing detail + * + * @psalm-return HasOne + */ + public function customer(): HasOne + { + return $this->hasOne(Customer::class, 'company_billing_details_id' ); + } + + /** + * String to describe the model being updated / deleted / created + * + * @param Model $model + * + * @return string + */ + public static function logSubject( Model $model ): string + { + return sprintf( + "Billing Detail [id:%d] belonging to %s [id:%d] '%s'", + $model->id, + config( 'ixp_fe.lang.customer.one' ), + $model->customer->id ?? null, + $model->customer->shortname ?? null + ); + } +} diff --git a/app/Models/CompanyRegisteredDetail.php b/app/Models/CompanyRegisteredDetail.php new file mode 100644 index 000000000..821a53f04 --- /dev/null +++ b/app/Models/CompanyRegisteredDetail.php @@ -0,0 +1,124 @@ + + */ + public function customer(): HasOne + { + return $this->hasOne(Customer::class, 'company_registered_detail_id' ); + } + + /** + * String to describe the model being updated / deleted / created + * + * @param Model $model + * + * @return string + */ + public static function logSubject( Model $model ): string + { + return sprintf( + "Registered Detail [id:%d] belonging to %s [id:%d] '%s'", + $model->id, + config( 'ixp_fe.lang.customer.one' ), + $model->customer->id ?? null, + $model->customer->shortname ?? null + ); + } +} diff --git a/app/Models/ConsoleServer.php b/app/Models/ConsoleServer.php new file mode 100644 index 000000000..dd5a7b955 --- /dev/null +++ b/app/Models/ConsoleServer.php @@ -0,0 +1,141 @@ + $consoleServerConnections + * @property-read int|null $console_server_connections_count + * @property-read \IXP\Models\Vendor|null $vendor + * @method static Builder|ConsoleServer newModelQuery() + * @method static Builder|ConsoleServer newQuery() + * @method static Builder|ConsoleServer query() + * @method static Builder|ConsoleServer whereActive($value) + * @method static Builder|ConsoleServer whereCabinetId($value) + * @method static Builder|ConsoleServer whereCreatedAt($value) + * @method static Builder|ConsoleServer whereHostname($value) + * @method static Builder|ConsoleServer whereId($value) + * @method static Builder|ConsoleServer whereModel($value) + * @method static Builder|ConsoleServer whereName($value) + * @method static Builder|ConsoleServer whereNotes($value) + * @method static Builder|ConsoleServer whereSerialNumber($value) + * @method static Builder|ConsoleServer whereUpdatedAt($value) + * @method static Builder|ConsoleServer whereVendorId($value) + * @mixin \Eloquent + */ +class ConsoleServer extends Model +{ + use Observable; + + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'console_server'; + + /** + * The attributes that are mass assignable. + * + * @var array + */ + protected $fillable = [ + 'vendor_id', + 'cabinet_id', + 'name', + 'hostname', + 'model', + 'serialNumber', + 'active', + 'notes', + ]; + + /** + * Get the vendor that own the console server + * + * @psalm-return BelongsTo + */ + public function vendor(): BelongsTo + { + return $this->belongsTo(Vendor::class, 'vendor_id' ); + } + + /** + * Get the cabinet that own the console server + * + * @psalm-return BelongsTo + */ + public function cabinet(): BelongsTo + { + return $this->belongsTo(Cabinet::class, 'cabinet_id' ); + } + + /** + * Get the console server connections for the console server + * + * @psalm-return HasMany + */ + public function consoleServerConnections(): HasMany + { + return $this->hasMany(ConsoleServerConnection::class, 'console_server_id'); + } + + /** + * String to describe the model being updated / deleted / created + * + * @param Model $model + * + * @return string + */ + public static function logSubject( Model $model ): string + { + return sprintf( + "Console Server [id:%d] '%s'", + $model->id, + $model->name + ); + } +} \ No newline at end of file diff --git a/app/Models/ConsoleServerConnection.php b/app/Models/ConsoleServerConnection.php new file mode 100644 index 000000000..f9a3895e4 --- /dev/null +++ b/app/Models/ConsoleServerConnection.php @@ -0,0 +1,189 @@ + 300, + 600 => 600, + 1200 => 1200, + 2400 => 2400, + 4800 => 4800, + 9600 => 9600, + 14400 => 14400, + 19200 => 19200, + 28800 => 28800, + 38400 => 38400, + 57600 => 57600, + 115200 => 115200, + 230400 => 230400 + ]; + + public const PARITY_EVEN = 1; + public const PARITY_ODD = 2; + public const PARITY_NONE = 3; + + public static $PARITY = [ + self::PARITY_EVEN => "even", + self::PARITY_ODD => "odd", + self::PARITY_NONE => "none" + ]; + + public const FLOW_CONTROL_NONE = 1; + public const FLOW_CONTROL_RTS_CTS = 2; + public const FLOW_CONTROL_XON_XOFF = 3; + + public static $FLOW_CONTROL = [ + self::FLOW_CONTROL_NONE => "none", + self::FLOW_CONTROL_RTS_CTS => "rts/cts", + self::FLOW_CONTROL_XON_XOFF => "xon/xoff" + ]; + + public static $STOP_BITS = [ + 1 => 1, + 2 => 2, + ]; + + + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'consoleserverconnection'; + + /** + * The attributes that are mass assignable. + * + * @var array + */ + protected $fillable = [ + 'description', + 'port', + 'speed', + 'parity', + 'stopbits', + 'flowcontrol', + 'autobaud', + 'notes', + 'console_server_id', + ]; + + /** + * Get the customer that own the console server connection + * + * @psalm-return BelongsTo + */ + public function customer(): BelongsTo + { + return $this->belongsTo(CustomerTag::class, 'custid' ); + } + + /** + * Get the customer that own the console server connection + * + * @psalm-return BelongsTo + */ + public function switcher(): BelongsTo + { + return $this->belongsTo(Switcher::class, 'switchid' ); + } + + /** + * Get the console server that own the console server connection + * + * @psalm-return BelongsTo + */ + public function consoleServer(): BelongsTo + { + return $this->belongsTo(ConsoleServer::class, 'console_server_id' ); + } + + /** + * String to describe the model being updated / deleted / created + * + * @param Model $model + * + * @return string + */ + public static function logSubject( Model $model ): string + { + return sprintf( + "Console Server Connection [id:%d] belonging to Console Server [id:%d] '%s'", + $model->id, + $model->console_server_id, + $model->consoleServer->name + ); + } +} \ No newline at end of file diff --git a/app/Models/Contact.php b/app/Models/Contact.php new file mode 100644 index 000000000..32b497650 --- /dev/null +++ b/app/Models/Contact.php @@ -0,0 +1,179 @@ + $contactGroups + * @property-read int|null $contact_groups_count + * @property-read \Illuminate\Database\Eloquent\Collection $contactGroupsAll + * @property-read int|null $contact_groups_all_count + * @property-read \Illuminate\Database\Eloquent\Collection $contactRoles + * @property-read int|null $contact_roles_count + * @property-read \IXP\Models\Customer|null $customer + * @method static Builder|Contact newModelQuery() + * @method static Builder|Contact newQuery() + * @method static Builder|Contact query() + * @method static Builder|Contact whereCreatedAt($value) + * @method static Builder|Contact whereCreator($value) + * @method static Builder|Contact whereCustid($value) + * @method static Builder|Contact whereEmail($value) + * @method static Builder|Contact whereFacilityaccess($value) + * @method static Builder|Contact whereId($value) + * @method static Builder|Contact whereLastupdatedby($value) + * @method static Builder|Contact whereMayauthorize($value) + * @method static Builder|Contact whereMobile($value) + * @method static Builder|Contact whereName($value) + * @method static Builder|Contact whereNotes($value) + * @method static Builder|Contact wherePhone($value) + * @method static Builder|Contact wherePosition($value) + * @method static Builder|Contact whereUpdatedAt($value) + * @property int|null $user_id + * @property string|null $lastupdated + * @property string|null $created + * @method static Builder|Contact whereCreated($value) + * @method static Builder|Contact whereLastupdated($value) + * @method static Builder|Contact whereUserId($value) + * @mixin \Eloquent + */ +class Contact extends Model +{ + use Observable; + + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'contact'; + + /** + * The attributes that are mass assignable. + * + * @var array + */ + protected $fillable = [ + 'name', + 'email', + 'phone', + 'mobile', + 'facilityaccess', + 'mayauthorize', + 'lastupdatedby', + 'creator', + 'position', + 'notes', + ]; + + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + 'facilityaccess' => 'boolean', + 'mayauthorize' => 'boolean', + ]; + + /** + * Get the contact groups that are type role for the contact + */ + public function contactRoles(): BelongsToMany + { + return $this->belongsToMany(ContactGroup::class, 'contact_to_group', 'contact_id' ) + ->where( 'type', ContactGroup::TYPE_ROLE ) + ->orderBy( 'name' )->withTimestamps(); + } + + /** + * Get the contact groups that are not type role for the contact + */ + public function contactGroups(): BelongsToMany + { + return $this->belongsToMany(ContactGroup::class, 'contact_to_group', 'contact_id' ) + ->where( 'type', '!=', ContactGroup::TYPE_ROLE ) + ->orderBy( 'name' )->withTimestamps(); + } + + /** + * Get all the contact groups for the contact + */ + public function contactGroupsAll(): BelongsToMany + { + return $this->belongsToMany(ContactGroup::class, 'contact_to_group', 'contact_id' ) + ->orderBy( 'name' ); + } + + /** + * Get the customer that own the contact + * + * @psalm-return BelongsTo + */ + public function customer(): BelongsTo + { + return $this->belongsTo(Customer::class, 'custid' ); + } + + /** + * String to describe the model being updated / deleted / created + * + * @param Model $model + * + * @return string + */ + public static function logSubject( Model $model ): string + { + return sprintf( + "Contact [id:%d] belonging to %s [id:%d] '%s'", + $model->id, + config( 'ixp_fe.lang.customer.one' ), + $model->customer->id, + $model->customer->shortname + ); + } +} \ No newline at end of file diff --git a/app/Models/ContactGroup.php b/app/Models/ContactGroup.php new file mode 100644 index 000000000..e52b184b3 --- /dev/null +++ b/app/Models/ContactGroup.php @@ -0,0 +1,96 @@ + $contacts + * @property-read int|null $contacts_count + * @method static Builder|ContactGroup newModelQuery() + * @method static Builder|ContactGroup newQuery() + * @method static Builder|ContactGroup query() + * @method static Builder|ContactGroup whereActive($value) + * @method static Builder|ContactGroup whereCreatedAt($value) + * @method static Builder|ContactGroup whereDescription($value) + * @method static Builder|ContactGroup whereId($value) + * @method static Builder|ContactGroup whereLimitedTo($value) + * @method static Builder|ContactGroup whereName($value) + * @method static Builder|ContactGroup whereType($value) + * @method static Builder|ContactGroup whereUpdatedAt($value) + * @property string $created + * @method static Builder|ContactGroup whereCreated($value) + * @mixin \Eloquent + */ +class ContactGroup extends Model +{ + public const TYPE_ROLE = 'ROLE'; + + public static $TYPES = [ + self::TYPE_ROLE => 'Role' + ]; + + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'contact_group'; + + /** + * The attributes that are mass assignable. + * + * @var array + */ + protected $fillable = [ + 'name', + 'description', + 'type', + 'active', + 'limited_to' + ]; + + /** + * @psalm-return BelongsToMany + */ + public function contacts(): BelongsToMany + { + return $this->belongsToMany(Contact::class )->withPivot( 'contact_to_group', 'contact_group_id' ); + } +} \ No newline at end of file diff --git a/app/Models/CoreBundle.php b/app/Models/CoreBundle.php new file mode 100644 index 000000000..8d7a8b77f --- /dev/null +++ b/app/Models/CoreBundle.php @@ -0,0 +1,391 @@ + $corelinks + * @property-read int|null $corelinks_count + * @method static Builder|CoreBundle active() + * @method static Builder|CoreBundle newModelQuery() + * @method static Builder|CoreBundle newQuery() + * @method static Builder|CoreBundle query() + * @method static Builder|CoreBundle whereBfd($value) + * @method static Builder|CoreBundle whereCost($value) + * @method static Builder|CoreBundle whereCreatedAt($value) + * @method static Builder|CoreBundle whereDescription($value) + * @method static Builder|CoreBundle whereEnabled($value) + * @method static Builder|CoreBundle whereGraphTitle($value) + * @method static Builder|CoreBundle whereId($value) + * @method static Builder|CoreBundle whereIpv4Subnet($value) + * @method static Builder|CoreBundle whereIpv6Subnet($value) + * @method static Builder|CoreBundle wherePreference($value) + * @method static Builder|CoreBundle whereStp($value) + * @method static Builder|CoreBundle whereType($value) + * @method static Builder|CoreBundle whereUpdatedAt($value) + * @mixin \Eloquent + */ +class CoreBundle extends Model +{ + use Observable; + + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'corebundles'; + + /** + * The attributes that are mass assignable. + * + * @var array + */ + protected $fillable = [ + 'description', + 'type', + 'graph_title', + 'bfd', + 'ipv4_subnet', + 'stp', + 'cost', + 'preference', + 'enabled' + ]; + + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + 'stp' => 'boolean', + ]; + + /** + * CONST TYPES + */ + public const TYPE_ECMP = 1; + public const TYPE_L2_LAG = 2; + public const TYPE_L3_LAG = 3; + + /** + * Array STATES + */ + public static $TYPES = [ + self::TYPE_ECMP => "ECMP", + self::TYPE_L2_LAG => "L2-LAG (e.g. LACP)", + self::TYPE_L3_LAG => "L3-LAG", + ]; + + /** + * Get the corelinks that belong to the corebundle + * + * @psalm-return HasMany + */ + public function corelinks(): HasMany + { + return $this->HasMany(CoreLink::class, 'core_bundle_id' ); + } + + /** + * Is the type TYPE_ECMP? + * + * @return bool + */ + public function typeECMP(): bool + { + return $this->type === self::TYPE_ECMP; + } + + /** + * Is the type isTypeL2Lag? + * + * @return bool + */ + public function typeL2Lag(): bool + { + return $this->type === self::TYPE_L2_LAG; + } + + /** + * Is the type isTypeL3Lag? + * + * @return bool + */ + public function typeL3Lag(): bool + { + return $this->type === self::TYPE_L3_LAG; + } + + /** + * Turn the database integer representation of the type into text as + * defined in the self::$TYPES array (or 'Unknown') + * + * @return string + */ + public function typeText(): string + { + return self::$TYPES[ $this->type ] ?? 'Unknown'; + } + + /** + * Return all active core bundles + * + * @param Builder $query + * + * @return Builder + * + * @psalm-return Builder + */ + public function scopeActive( Builder $query ): Builder + { + return $query->where( 'enabled' , true ) + ->orderBy( 'description' ); + } + + /** + * get switch from side A or B + * + * @param bool $sideA if true get the side A if false Side B + * + * @return Switcher|bool + */ + public function switchSideX( bool $sideA = true ): Switcher|bool + { + $cl = $this->corelinks->first() ?? false; + + if( $cl ){ + /** @var Switcher $side */ + $side = $sideA ? $cl->coreInterfaceSideA : $cl->coreInterfaceSideB; + return $side->physicalinterface->switchPort->switcher; + } + + return false; + } + + /** + * Check if all the core links for the core bundle are enabled + * + * @return boolean + */ + public function allCoreLinksEnabled(): bool + { + return $this->corelinks->where( 'enabled', false )->count() <= 0; + } + + /** + * get the speed of the Physical interface + */ + public function speedPi(): int|null + { + $cl = $this->corelinks->first() ?? false; + if( $cl ){ + return $cl->coreInterfaceSideA->physicalinterface->speed; + } + return 0; + } + + /** + * get the duplex of the Physical interface + * + * @return int|false + */ + public function duplexPi() + { + if( $cl = $this->corelinks()->first() ){ + return $cl->coreInterfaceSideA->physicalinterface->duplex; + } + + return false; + } + + /** + * get the auto neg of the Physical interface + * + * @return int|false + */ + public function autoNegPi() + { + if( $cl = $this->corelinks()->first() ){ + return $cl->coreInterfaceSideA->physicalinterface->autoneg; + } + + return false; + } + + /** + * get the customer associated virtual interface of the core bundle + */ + public function customer(): Customer|false + { + $cl = $this->corelinks[ 0 ] ?? false; + if( $cl ){ + return $cl->coreInterfaceSideA->physicalinterface->virtualInterface->customer; + } + return false; + } + + /** + * get the virtual interfaces linked to the core links of the side A and B + * + * @return array + * + * @psalm-return array{a?: mixed, b?: mixed} + */ + public function virtualInterfaces(): array + { + $vis = []; + if( $cl = $this->corelinks()->first() ){ + $vis[ 'a' ] = $cl->coreInterfaceSideA->physicalInterface->virtualInterface; + $vis[ 'b' ] = $cl->coreInterfaceSideB->physicalInterface->virtualInterface; + } + return $vis; + } + + /** + * Get all core links where each side of the link has an SNMP IF Oper State as provided + * (defaults to operational state: UP). + * + * @param int $operstate + * @param bool $onlyEnabled + * + * @return CoreLink[] + */ + public function coreLinksWithIfOperStateX( int $operstate = Iface::IF_ADMIN_STATUS_UP, bool $onlyEnabled = true ): array { + return self::select( 'cl.*' ) + ->from( 'corebundles AS cb' ) + ->leftJoin( 'corelinks AS cl', 'cl.core_bundle_id', 'cb.id' ) + ->leftJoin( 'coreinterfaces AS cia', 'cia.id', 'cl.core_interface_sidea_id' ) + ->leftJoin( 'coreinterfaces AS cib', 'cib.id', 'cl.core_interface_sideb_id' ) + ->leftJoin( 'physicalinterface AS pia', 'pia.id', 'cia.physical_interface_id' ) + ->leftJoin( 'physicalinterface AS pib', 'pib.id', 'cib.physical_interface_id' ) + ->leftJoin( 'switchport AS spa', 'spa.id', 'pia.switchportid' ) + ->leftJoin( 'switchport AS spb', 'spb.id', 'pib.switchportid' ) + ->where( 'spa.ifOperStatus', $operstate ) + ->where( 'spb.ifOperStatus', $operstate ) + ->where( 'cb.id', $this->id ) + ->when( $onlyEnabled , function( Builder $q ) { + return $q->where( 'cl.enabled', true ); + })->get()->toArray(); + } + + + /** + * Check if the switch is the same for the Physical interfaces of the core links associated to the core bundle + * + * @param bool $sideA if true get the side A if false Side B + * + * @return bool + */ + public function sameSwitchForEachPIFromCL( bool $sideA = true ): bool + { + $switches = []; + + foreach( $this->corelinks as $cl ) { + /** @var CoreInterface $side */ + $side = $sideA ? $cl->coreInterfaceSideA : $cl->coreInterfaceSideB ; + $switches[] = $side->physicalInterface->switchPort->switcher->id; + } + + return count(array_unique($switches)) == 1; + } + + /** + * Delete the Core Bundle and everything related. + * + * @return true + * + * @throws + */ + public function deleteObject(): bool + { + try { + DB::beginTransaction(); + $vis = []; + foreach( $this->corelinks as $cl ){ + $cl->delete(); + foreach( $cl->coreInterfaces() as $ci ){ + /** @var CoreInterface $ci */ + $ci->delete(); + $vis[] = $ci->physicalInterface->virtualInterface; + $ci->physicalInterface->delete(); + } + } + + foreach( $vis as $vi ){ + $vi->delete(); + } + + $this->delete(); + DB::commit(); + } catch( Exception $e ) { + DB::rollBack(); + throw $e; + } + return true; + } + + /** + * String to describe the model being updated / deleted / created + * + * @param Model $model + * + * @return string + */ + public static function logSubject( Model $model ): string + { + return sprintf( + "Core Bundle [id:%d] '%s'", + $model->id, + $model->description, + ); + } +} \ No newline at end of file diff --git a/app/Models/CoreInterface.php b/app/Models/CoreInterface.php new file mode 100644 index 000000000..d4d92551f --- /dev/null +++ b/app/Models/CoreInterface.php @@ -0,0 +1,100 @@ + + */ + public function physicalInterface(): BelongsTo + { + return $this->belongsTo(PhysicalInterface::class, 'physical_interface_id' ); + } + + /** + * Get the corelink associated with the core interface side A. + * + * @psalm-return HasOne + */ + public function coreLinkSideA(): HasOne + { + return $this->hasOne(CoreLink::class, 'core_interface_sidea_id' ); + } + + /** + * Get the corelink associated with the core interface side B. + * + * @psalm-return HasOne + */ + public function coreLinkSideB(): HasOne + { + return $this->hasOne(CoreLink::class, 'core_interface_sideb_id' ); + } + + /** + * Check which side has a core link linked + */ + public function coreLink(): CoreLink|null + { + if( $this->coreLinkSideA()->exists() ) { + return $this->coreLinkSideA; + } + return $this->coreLinkSideB; + } +} \ No newline at end of file diff --git a/app/Models/CoreLink.php b/app/Models/CoreLink.php new file mode 100644 index 000000000..0fb3e4201 --- /dev/null +++ b/app/Models/CoreLink.php @@ -0,0 +1,147 @@ + + */ + public function coreInterfaceSideA(): BelongsTo + { + return $this->belongsTo(CoreInterface::class, 'core_interface_sidea_id' ); + } + + /** + * Get the core interface side B associated with the corelink. + * + * @psalm-return BelongsTo + */ + public function coreInterfaceSideB(): BelongsTo + { + return $this->belongsTo(CoreInterface::class, 'core_interface_sideb_id' ); + } + + /** + * Get the core interface (A/B) + * + * @return CoreInterface[] + * + * @psalm-return list{CoreInterface, CoreInterface} + */ + public function coreInterfaces(): array + { + return [ $this->coreInterfaceSideA, $this->coreInterfaceSideB ]; + } + /** + * Get the corebundle that own the corelink + * + * @psalm-return BelongsTo + */ + public function coreBundle(): BelongsTo + { + return $this->belongsTo(CoreBundle::class, 'core_bundle_id' ); + } + + /** + * Return all active core link + * + * @param Builder $query + * + * @return Builder + */ + + public function scopeActive( Builder $query ): Builder + { + return $query->where( 'enabled' , true ); + } + + /** + * String to describe the model being updated / deleted / created + * + * @param Model $model + * + * @return string + */ + public static function logSubject( Model $model ): string + { + return sprintf( + "Core Link [id:%d] belonging to Core Bundle [id:%d] '%s'", + $model->id, + $model->core_bundle_id, + $model->coreBundle->description + ); + } +} \ No newline at end of file diff --git a/app/Models/Customer.php b/app/Models/Customer.php new file mode 100644 index 000000000..77ee3e416 --- /dev/null +++ b/app/Models/Customer.php @@ -0,0 +1,1256 @@ + $AtlasMeasurementsDest + * @property-read int|null $atlas_measurements_dest_count + * @property-read \Illuminate\Database\Eloquent\Collection $AtlasMeasurementsSource + * @property-read int|null $atlas_measurements_source_count + * @property-read \Illuminate\Database\Eloquent\Collection $AtlasProbes + * @property-read int|null $atlas_probes_count + * @property-read \IXP\Models\CompanyBillingDetail|null $companyBillingDetail + * @property-read \IXP\Models\CompanyRegisteredDetail|null $companyRegisteredDetail + * @property-read \Illuminate\Database\Eloquent\Collection $consoleServerConnections + * @property-read int|null $console_server_connections_count + * @property-read \Illuminate\Database\Eloquent\Collection $contacts + * @property-read int|null $contacts_count + * @property-read \Illuminate\Database\Eloquent\Collection $customerEquipments + * @property-read int|null $customer_equipments_count + * @property-read \Illuminate\Database\Eloquent\Collection $customerNotes + * @property-read int|null $customer_notes_count + * @property-read \Illuminate\Database\Eloquent\Collection $customerToUser + * @property-read int|null $customer_to_user_count + * @property-read \Illuminate\Database\Eloquent\Collection $docstoreCustomerDirectories + * @property-read int|null $docstore_customer_directories_count + * @property-read \Illuminate\Database\Eloquent\Collection $docstoreCustomerFiles + * @property-read int|null $docstore_customer_files_count + * @property-read \IXP\Models\IrrdbConfig|null $irrdbConfig + * @property-read \Illuminate\Database\Eloquent\Collection $irrdbPrefixes + * @property-read int|null $irrdb_prefixes_count + * @property-read \IXP\Models\Logo|null $logo + * @property-read \Illuminate\Database\Eloquent\Collection $patchPanelPortHistories + * @property-read int|null $patch_panel_port_histories_count + * @property-read \Illuminate\Database\Eloquent\Collection $patchPanelPorts + * @property-read int|null $patch_panel_ports_count + * @property-read \Illuminate\Database\Eloquent\Collection $peerRouteServerFilters + * @property-read int|null $peer_route_server_filters_count + * @property-read \Illuminate\Database\Eloquent\Collection $peers + * @property-read int|null $peers_count + * @property-read \Illuminate\Database\Eloquent\Collection $peersWith + * @property-read int|null $peers_with_count + * @property-read Customer|null $resellerObject + * @property-read \Illuminate\Database\Eloquent\Collection $resoldCustomers + * @property-read int|null $resold_customers_count + * @property-read \Illuminate\Database\Eloquent\Collection $routeServerFilters + * @property-read int|null $route_server_filters_count + * @property-read \Illuminate\Database\Eloquent\Collection $routeServerFiltersInProduction + * @property-read int|null $route_server_filters_in_production_count + * @property-read \Illuminate\Database\Eloquent\Collection $rsPrefixes + * @property-read int|null $rs_prefixes_count + * @property-read \Illuminate\Database\Eloquent\Collection $tags + * @property-read int|null $tags_count + * @property-read \Illuminate\Database\Eloquent\Collection $trafficDailies + * @property-read int|null $traffic_dailies_count + * @property-read \Illuminate\Database\Eloquent\Collection $users + * @property-read int|null $users_count + * @property-read \Illuminate\Database\Eloquent\Collection $virtualInterfaces + * @property-read int|null $virtual_interfaces_count + * @property-read \Illuminate\Database\Eloquent\Collection $vlanInterfaces + * @property-read int|null $vlan_interfaces_count + * @method static Builder|Customer active() + * @method static Builder|Customer addressesForVlan(int $vlanid, int $cust, int $protocol) + * @method static Builder|Customer associate() + * @method static Builder|Customer current() + * @method static Builder|Customer currentActive(bool $trafficing = false, bool $externalOnly = false, bool $connected = true) + * @method static Builder|Customer internal() + * @method static Builder|Customer newModelQuery() + * @method static Builder|Customer newQuery() + * @method static Builder|Customer notDeleted() + * @method static Builder|Customer query() + * @method static Builder|Customer resellerOnly() + * @method static Builder|Customer trafficking() + * @method static Builder|Customer whereAbbreviatedName($value) + * @method static Builder|Customer whereActivepeeringmatrix($value) + * @method static Builder|Customer whereAutsys($value) + * @method static Builder|Customer whereCompanyBillingDetailsId($value) + * @method static Builder|Customer whereCompanyRegisteredDetailId($value) + * @method static Builder|Customer whereCorpwww($value) + * @method static Builder|Customer whereCreatedAt($value) + * @method static Builder|Customer whereCreator($value) + * @method static Builder|Customer whereDatejoin($value) + * @method static Builder|Customer whereDateleave($value) + * @method static Builder|Customer whereId($value) + * @method static Builder|Customer whereInManrs($value) + * @method static Builder|Customer whereInPeeringdb($value) + * @method static Builder|Customer whereIrrdb($value) + * @method static Builder|Customer whereIsReseller($value) + * @method static Builder|Customer whereLastupdatedby($value) + * @method static Builder|Customer whereMD5Support($value) + * @method static Builder|Customer whereMaxprefixes($value) + * @method static Builder|Customer whereName($value) + * @method static Builder|Customer whereNoc24hphone($value) + * @method static Builder|Customer whereNocemail($value) + * @method static Builder|Customer whereNocfax($value) + * @method static Builder|Customer whereNochours($value) + * @method static Builder|Customer whereNocphone($value) + * @method static Builder|Customer whereNocwww($value) + * @method static Builder|Customer wherePeeringdbOauth($value) + * @method static Builder|Customer wherePeeringemail($value) + * @method static Builder|Customer wherePeeringmacro($value) + * @method static Builder|Customer wherePeeringmacrov6($value) + * @method static Builder|Customer wherePeeringpolicy($value) + * @method static Builder|Customer whereReseller($value) + * @method static Builder|Customer whereShortname($value) + * @method static Builder|Customer whereStatus($value) + * @method static Builder|Customer whereType($value) + * @method static Builder|Customer whereUpdatedAt($value) + * @property string|null $lastupdated + * @property string|null $created + * @method static Builder|Customer whereCreated($value) + * @method static Builder|Customer whereLastupdated($value) + * @property-read \IXP\Models\IrrdbUpdateLog|null $irrdbUpdateLog + * @mixin Eloquent + */ +class Customer extends Model +{ + use Observable; + + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'cust'; + + /** + * The attributes that are mass assignable. + * + * @var array + */ + protected $fillable = [ + 'name', + 'type', + 'shortname', + 'autsys', + 'maxprefixes', + 'peeringemail', + 'nocphone', + 'noc24hphone', + 'nocemail', + 'nochours', + 'nocwww', + 'irrdb', + 'peeringmacro', + 'peeringpolicy', + 'corpwww', + 'datejoin', + 'dateleave', + 'status', + 'activepeeringmatrix', + 'creator', + 'company_registered_detail_id', + 'company_billing_details_id', + 'peeringmacrov6', + 'abbreviatedName', + 'MD5Support', + 'reseller', + 'isReseller', + 'peeringdb_oauth', + 'lastupdatedby', + + //'nocfax', + //'in_manrs', + //'in_peeringdb', + + ]; + + /** + * The attributes that should be mutated to dates. + * + * @var array + */ + protected $dates = [ + 'datejoin', + 'dateleave' + ]; + + /** + * DQL for selecting customers that are current in terms of `datejoin` and `dateleave` + * + * @var string DQL for selecting customers that are current in terms of `datejoin` and `dateleave` + */ + public const SQL_CUST_CURRENT = "cust.datejoin <= CURRENT_DATE() AND ( cust.dateleave IS NULL OR cust.dateleave >= CURRENT_DATE() )"; + + /** + * DQL for selecting customers that are active (i.e. not suspended) + * + * @var string DQL for selecting customers that are active (i.e. not suspended) + */ + public const SQL_CUST_ACTIVE = "cust.status IN ( 1, 2 )"; + + /** + * DQL for selecting all trafficing customers + * + * @var string DQL for selecting all trafficing customers + */ + public const SQL_CUST_TRAFFICING = "cust.type != 2"; + + /** + * DQL for selecting all customers except for internal / dummy customers + * + * @var string DQL for selecting all customers except for internal / dummy customers + */ + public const SQL_CUST_EXTERNAL = "cust.type != 3"; + + /** + * DQL for selecting all "connected" customers + * + * @var string DQL for selecting all "connected" customers + */ + public const SQL_CUST_CONNECTED = "cust.status = 1"; + + + public const TYPE_FULL = 1; + public const TYPE_ASSOCIATE = 2; + public const TYPE_INTERNAL = 3; + public const TYPE_PROBONO = 4; + + public static $CUST_TYPES_TEXT = [ + self::TYPE_FULL => 'Full', + self::TYPE_ASSOCIATE => 'Associate', + self::TYPE_INTERNAL => 'Internal', + self::TYPE_PROBONO => 'Pro-bono', + ]; + + public const STATUS_NORMAL = 1; + public const STATUS_NOTCONNECTED = 2; + public const STATUS_SUSPENDED = 3; + + public static $CUST_STATUS_TEXT = [ + self::STATUS_NORMAL => 'Normal', + self::STATUS_NOTCONNECTED => 'Not Connected', + self::STATUS_SUSPENDED => 'Suspended', + ]; + + public const PEERING_POLICY_OPEN = 'open'; + public const PEERING_POLICY_SELECTIVE = 'selective'; + public const PEERING_POLICY_MANDATORY = 'mandatory'; + public const PEERING_POLICY_CLOSED = 'closed'; + + public static $PEERING_POLICIES = [ + self::PEERING_POLICY_OPEN => 'open', + self::PEERING_POLICY_SELECTIVE => 'selective', + self::PEERING_POLICY_MANDATORY => 'mandatory', + self::PEERING_POLICY_CLOSED => 'closed' + ]; + + public const NOC_HOURS_24x7 = '24x7'; + public const NOC_HOURS_8x5 = '8x5'; + public const NOC_HOURS_8x7 = '8x7'; + public const NOC_HOURS_12x5 = '12x5'; + public const NOC_HOURS_12x7 = '12x7'; + + public static $NOC_HOURS = [ + self::NOC_HOURS_24x7 => '24x7', + self::NOC_HOURS_8x5 => '8x5', + self::NOC_HOURS_8x7 => '8x7', + self::NOC_HOURS_12x5 => '12x5', + self::NOC_HOURS_12x7 => '12x7' + ]; + + public const MD5_SUPPORT_UNKNOWN = 'UNKNOWN'; + public const MD5_SUPPORT_YES = 'YES'; + public const MD5_SUPPORT_MANDATORY = 'MANDATORY'; + public const MD5_SUPPORT_PREFERRED = 'PREFERRED'; + public const MD5_SUPPORT_NO = 'NO'; + + public static $MD5_SUPPORT = [ + self::MD5_SUPPORT_UNKNOWN => 'Unknown', + self::MD5_SUPPORT_YES => 'Yes', + self::MD5_SUPPORT_MANDATORY => 'Yes - Mandatory', + self::MD5_SUPPORT_PREFERRED => 'Yes - Preferred', + self::MD5_SUPPORT_NO => 'No' + ]; + + /** + * Get the customer equipments for the customer + * + * @psalm-return HasMany + */ + public function customerEquipments(): HasMany + { + return $this->hasMany(CustomerEquipment::class, 'custid'); + } + + /** + * Get the virtual interfaces for the customer + * + * @psalm-return HasMany + */ + public function virtualInterfaces(): HasMany + { + return $this->hasMany(VirtualInterface::class, 'custid'); + } + + /** + * Get the virtual interfaces for the customer + * + * @psalm-return HasOne + */ + public function irrdbUpdateLog(): HasOne + { + return $this->hasOne(IrrdbUpdateLog::class, 'cust_id'); + } + + + + /** + * Get the peers for the customer + * + * @psalm-return HasMany + */ + public function peers(): HasMany + { + return $this->hasMany(PeeringManager::class, 'custid'); + } + + /** + * Get the peers with for the customer + * + * @psalm-return HasMany + */ + public function peersWith(): HasMany + { + return $this->hasMany(PeeringManager::class, 'peerid'); + } + + /** + * Get the virtual interfaces for the customer + * + * @psalm-return HasManyThrough + */ + public function vlanInterfaces(): HasManyThrough + { + return $this->hasManyThrough( VlanInterface::class, VirtualInterface::class, + 'custid', 'virtualinterfaceid' + ); + } + + /** + * Get the docstore customer directories for the customer + * + * @psalm-return HasMany + */ + public function docstoreCustomerDirectories(): HasMany + { + return $this->hasMany(DocstoreCustomerDirectory::class, 'cust_id'); + } + + /** + * Get the docstore customer files for the customer + * + * @psalm-return HasMany + */ + public function docstoreCustomerFiles(): HasMany + { + return $this->hasMany(DocstoreCustomerFile::class, 'cust_id'); + } + + /** + * Get the contacts for the customer + * + * @psalm-return HasMany + */ + public function contacts(): HasMany + { + return $this->hasMany(Contact::class, 'custid' ); + } + + /** + * Get the console server connections for the customer + * + * @psalm-return HasMany + */ + public function consoleServerConnections(): HasMany + { + return $this->hasMany(ConsoleServerConnection::class, 'custid'); + } + + /** + * Get the route server filters for the customer + * + * @psalm-return HasMany + */ + public function routeServerFilters(): HasMany + { + return $this->hasMany(RouteServerFilter::class, 'customer_id' ); + } + + /** + * Get the route server filters for the customer (in production) + * + * @psalm-return HasMany + */ + public function routeServerFiltersInProduction(): HasMany + { + return $this->hasMany(RouteServerFilterProd::class, 'customer_id' ); + } + + /** + * Get the peer route server filters for the customer + * + * @psalm-return HasMany + */ + public function peerRouteServerFilters(): HasMany + { + return $this->hasMany(RouteServerFilter::class, 'peer_id' ); + } + + /** + * Get the irrdb Prefixes for the customer + * + * @psalm-return HasMany + */ + public function irrdbPrefixes(): HasMany + { + return $this->hasMany(IrrdbPrefix::class, 'customer_id' ); + } + + /** + * Get the traffic dailies for the customer + * + * @psalm-return HasMany + */ + public function trafficDailies(): HasMany + { + return $this->hasMany(TrafficDaily::class, 'cust_id' ); + } + + /** + * Get the patch panel portss for the customer + * + * @psalm-return HasMany + */ + public function patchPanelPorts(): HasMany + { + return $this->hasMany(PatchPanelPort::class, 'customer_id' ); + } + + /** + * Get the patch panel port histories for the customer + * + * @psalm-return HasMany + */ + public function patchPanelPortHistories(): HasMany + { + return $this->hasMany(PatchPanelPortHistory::class, 'cust_id' ); + } + + /** + * Get the rsPrefixes for the customer + * + * @psalm-return HasMany + */ + public function rsPrefixes(): HasMany + { + return $this->hasMany(RsPrefix::class, 'custid' ); + } + + /** + * Get the customer notes for the customer + * + * @psalm-return HasMany + */ + public function customerNotes(): HasMany + { + return $this->hasMany(CustomerNote::class, 'customer_id' ); + } + + /** + * Get the atlas probes for the customer + * + * @psalm-return HasMany + */ + public function AtlasProbes(): HasMany + { + return $this->hasMany( AtlasProbe::class, 'cust_id'); + } + + /** + * Get the atlas measurement source for the customer + * + * @psalm-return HasMany + */ + public function AtlasMeasurementsSource(): HasMany + { + return $this->hasMany( AtlasMeasurement::class, 'cust_source'); + } + + /** + * Get the atlas measurement destination for the customer + * + * @psalm-return HasMany + */ + public function AtlasMeasurementsDest(): HasMany + { + return $this->hasMany( AtlasMeasurement::class, 'cust_dest'); + } + + /** + * Get the logo for the customer + * + * @psalm-return HasOne + */ + public function logo(): HasOne + { + return $this->hasOne(Logo::class, 'customer_id' ); + } + + /** + * Get the billing details for the customer + * + * @psalm-return BelongsTo + */ + public function companyBillingDetail(): BelongsTo + { + return $this->belongsTo(CompanyBillingDetail::class, 'company_billing_details_id' ); + } + + /** + * Get the registered detail for the customer + * + * @psalm-return BelongsTo + */ + public function companyRegisteredDetail(): BelongsTo + { + return $this->belongsTo(CompanyRegisteredDetail::class, 'company_registered_detail_id' ); + } + + /** + * Get the resold customers for the customer + * + * @psalm-return HasMany + */ + public function resoldCustomers(): HasMany + { + return $this->hasMany( __CLASS__, 'reseller' ); + } + + /** + * Get the reseller for the customer + * + * @psalm-return BelongsTo + */ + public function resellerObject(): BelongsTo + { + return $this->belongsTo( __CLASS__, 'reseller' ); + } + + /** + * Get the irrdbconfig that own the customer + * + * @psalm-return BelongsTo + */ + public function irrdbConfig(): BelongsTo + { + return $this->belongsTo(IrrdbConfig::class, 'irrdb' ); + } + + /** + * Get all the users for the customer + * + * @psalm-return BelongsToMany + */ + public function users(): BelongsToMany + { + return $this->belongsToMany(User::class , 'customer_to_users', 'customer_id' ); + } + + /** + * Get all the customer to user for the customer + * + * @psalm-return HasMany + */ + public function customerToUser(): HasMany + { + return $this->HasMany(CustomerToUser::class, 'customer_id' ); + } + + /** + * The tags that belong to the customer. + * + * @psalm-return BelongsToMany + */ + public function tags(): BelongsToMany + { + return $this->belongsToMany(CustomerTag::class , 'cust_to_cust_tag', 'customer_id' ); + } + + /** + * Check if this customer is of the named type + * @return boolean + */ + public function typeInternal(): bool + { + return $this->type === self::TYPE_INTERNAL; + } + + /** + * Check if this customer is of the named type + * + * @return boolean + */ + public function typeFull(): bool + { + return $this->type === self::TYPE_FULL; + } + + /** + * Check if this customer is of the named type + * + * @return boolean + */ + public function typeProBono(): bool + { + return $this->type === self::TYPE_PROBONO; + } + + /** + * Check if this customer is of the named type + * + * @return boolean + */ + public function typeAssociate(): bool + { + return $this->type === self::TYPE_ASSOCIATE; + } + + /** + * Returns true if the customer's status is NORMAL + * + * @return bool True if the customer's status is NORMAL + */ + public function statusNormal(): bool + { + return $this->status === self::STATUS_NORMAL; + } + + /** + * Returns true if the customer's status is NOTCONNECTED + * + * @return bool True if the customer's status is NOTCONNECTED + */ + public function statusNotConnected(): bool + { + return $this->status === self::STATUS_NOTCONNECTED; + } + + /** + * Returns true if the customer's status is SUSPENDED + * + * @return bool True if the customer's status is SUSPENDED + */ + public function statusSuspended(): bool + { + return $this->status === self::STATUS_SUSPENDED; + } + + /** + * Returns true if the customer has left + * + * @return bool + */ + public function hasLeft(): bool + { + // sigh. Using a date field to determine if an account is closed or not is a + // very bad idea and should be changed => FIXME + return $this->dateleave != null; + } + + /** + * Scope a query to only include reseller members. + * + * @param Builder $query + * + * @return Builder + */ + public function scopeResellerOnly( Builder $query ): Builder + { + return $query->where('isReseller', true ); + } + + /** + * Scope a query to only include type associate + * + * @param Builder $query + * + * @return Builder + */ + public function scopeAssociate( Builder $query ): Builder + { + return $query->where('type', self::TYPE_ASSOCIATE ); + } + + /** + * Scope a query to only include type internal + * + * @param Builder $query + * + * @return Builder + */ + public function scopeInternal( Builder $query ): Builder + { + return $query->where('type', self::TYPE_INTERNAL ); + } + + /** + * Scope a query to only include trafficking members. + * + * Not that the IXP's own internal customers are included in this. + * + * @param Builder $query + * @return Builder + */ + public function scopeTrafficking( Builder $query ): Builder + { + return $query->where('type', '!=', self::TYPE_ASSOCIATE ); + } + + /** + * Scope a query to only include current members + * + * @param Builder $query + * + * @return Builder + * + * @psalm-return Builder + */ + public function scopeCurrent( Builder $query): Builder + { + return $query->whereRaw( self::SQL_CUST_CURRENT ); + } + + /** + * Utility function to provide a array of all active and current customers. + * + * @param Builder $query + * @param bool $trafficing If `true`, only include trafficing customers (i.e. no associates) + * @param bool $externalOnly If `true`, only include external customers (i.e. no internal types) + * @param bool $connected If `true`, only include connected customers + * + * @return Builder + */ + public static function scopeCurrentActive( Builder $query, bool $trafficing = false, bool $externalOnly = false, bool $connected = true ): Builder + { + return $query->whereRaw( self::SQL_CUST_CURRENT ) + ->whereRaw( self::SQL_CUST_ACTIVE ) + ->when( $trafficing , function( Builder $q ) use( $connected ) { + return $q->whereRaw(self::SQL_CUST_TRAFFICING ) + ->when( $connected , function( Builder $q ) { + return $q->whereRaw( self::SQL_CUST_CONNECTED ); + }); + } )->when( $externalOnly , function( Builder $q ) { + return $q->whereRaw( self::SQL_CUST_EXTERNAL ); + })->orderBy( 'name' ); + } + + /** + * Scope a query to only include active members (not suspended) + * + * @param Builder $query + * + * @return Builder + * + * @psalm-return Builder + */ + public function scopeActive( Builder $query ): Builder + { + return $query->whereIn( 'status', [ self::STATUS_NORMAL, self::STATUS_NOTCONNECTED ] ); + } + + /** + * Scope a query to only include not deleted members + * + * @param Builder $query + * + * @return Builder + */ + public function scopeNotDeleted( Builder $query ): Builder + { + return $query->whereNull( 'dateleave' ) + ->orWhere( 'dateleave', '>=', now() ); + } + + /** + * Useful function to get the appropriate AS macro or ASN for a customer + * for a given protocol. + * + * One example usage is in IrrdbCli for bgpq3. bgpq3 requires ASNs to + * be formatted as `asxxxx` so we set `$asnPrefix = 'as'` in this case. + * + * By default, the function will return some format of the ASN if no macro is + * defined. To return null in this case, set `$nullIfNoMacro` to true. + * + * @param int $protocol One of 4 or 6 (defaults to 4) + * @param string $asnPrefix A prefix for the ASN if no macro is present. See above. + * @param bool $nullIfNoMacro + * + * @return string|null The ASN / AS macro as appropriate + * + * @throws \Exception + */ + public function asMacro( int $protocol = 4, string $asnPrefix = '',bool $nullIfNoMacro = false ): ?string + { + if( !in_array( $protocol, [ 4, 6 ], true ) ) + throw new \Exception( 'Invalid / unknown protocol. 4/6 accepted only.' ); + + // find the appropriate ASN or macro + if( $protocol === 6 && strlen( $this->peeringmacrov6 ) > 3 ) { + $asmacro = $this->peeringmacrov6; + } else if( strlen( $this->peeringmacro ) > 3 ) { + $asmacro = $this->peeringmacro; + } else if( $nullIfNoMacro ) { + $asmacro = null; + } else { + $asmacro = $asnPrefix . $this->autsys; + } + + return $asmacro; + } + + /** + * Get formatted name + * + * @param null $fmt + * + * @return null|string + */ + public function getFormattedName( $fmt = null ): ?string + { + if( $this->type === self::TYPE_ASSOCIATE ) { + return $this->abbreviatedName ?? $this->name; + } + + if( $fmt === null || ( $fmt = config('ixp_fe.customer_name_format') ) === null ) { + $fmt = "%a %j"; + } + + $as = (string)$this->autsys; + + return (string) str_replace( + [ '%n', '%a', '%s', '%i', '%j', '%k', '%l' ], + [ + $this->name ?? '', + $this->abbreviatedName ?? ( $this->name ?? '' ), + $this->shortname ?? '', + $as, + "[AS{$as}]", + "AS{$as}", + " - AS{$as}" + ], + $fmt + ); + } + + /** + * Return the given type as string + * + * @param int $t + * + * @return string + */ + public static function givenType( int $t ): string + { + return self::$CUST_TYPES_TEXT[ $t ] ?? 'Unknown'; + } + + /** + * Turn the database integer representation of the status into text + * + * @return string + */ + public function status(): string + { + return self::$CUST_STATUS_TEXT[ $this->status ] ?? 'Unknown'; + } + + /** + * Turn the database integer representation of the type into text + * + * @return string + */ + public function type(): string + { + return self::$CUST_TYPES_TEXT[ $this->type ] ?? 'Unknown'; + } + + /** + * Is the customer a route server client on any of their VLAN interfaces? + * + * @param int $proto One of [4,6]. Defaults to 4. + * + * @return boolean + * + * @throws + */ + public function routeServerClient( ?int $proto = null ): bool + { + if( !in_array( $proto, [ null, 4, 6 ] ) ) { + throw new IXP_Exception( 'Invalid protocol' ); + } + + $protos = $proto === null ? [ 4, 6 ] : [ $proto ]; + + foreach( $protos as $p ) { + if( self::leftJoin('virtualinterface AS vi', 'vi.custid', 'cust.id') + ->leftJoin('vlaninterface AS vli', 'vli.virtualinterfaceid', 'vi.id') + ->where('vli.rsclient', true ) + ->where('cust.id', $this->id) + ->where("ipv{$p}enabled", true)->count() ) { + return true; + } + } + + return false; + } + + /** + * Is the customer IRRDB filtered (usually for route server clients) on any of their VLAN interfaces? + * + * @return boolean + */ + public function irrdbFiltered(): bool + { + return (bool)self::leftJoin( 'virtualinterface AS vi', 'vi.custid', 'cust.id' ) + ->leftJoin( 'vlaninterface AS vli', 'vli.virtualinterfaceid', 'vi.id' ) + ->where( 'cust.id', $this->id )->where( 'rsclient', true ) + ->where( 'irrdbfilter', true ) + ->get()->count(); + } + + /** + * Is the customer IRRDB filtered (usually for route server clients) on ALL of their rsclient VLAN interfaces? + * + * @return boolean + */ + public function fullyIrrdbFiltered(): bool + { + return !(bool)self::leftJoin( 'virtualinterface AS vi', 'vi.custid', 'cust.id' ) + ->leftJoin( 'vlaninterface AS vli', 'vli.virtualinterfaceid', 'vi.id' ) + ->where( 'cust.id', $this->id )->where( 'rsclient', true ) + ->where( 'irrdbfilter', false ) + ->get()->count(); + } + + + /** + * If the customer is IRRDB filtered on any of their VLAN interfaces, are more specifics allowed? + * + * @return boolean + */ + public function irrdbMoreSpecificsAllowed(): bool + { + if( !$this->irrdbFiltered() ) { + return false; + } + + return (bool)self::leftJoin( 'virtualinterface AS vi', 'vi.custid', 'cust.id' ) + ->leftJoin( 'vlaninterface AS vli', 'vli.virtualinterfaceid', 'vi.id' ) + ->where( 'cust.id', $this->id )->where( 'rsmorespecifics', true ) + ->get()->count(); + } + + /** + * Is the customer an AS112 client on any of their VLAN interfaces? + * + * @return boolean + */ + public function isAS112Client(): bool + { + return (bool)self::leftJoin( 'virtualinterface AS vi', 'vi.custid', 'cust.id' ) + ->leftJoin( 'vlaninterface AS vli', 'vli.virtualinterfaceid', 'vi.id' ) + ->where( 'cust.id', $this->id )->where( 'as112client', true ) + ->get()->count(); + } + + /** + * Is the customer IPvX enabled on any of their VLAN interfaces? + * @param int $proto One of [4,6]. Defaults to 4. + * @return boolean + * @throws IXP_Exception + */ + public function isIPvXEnabled( int $proto = 4 ): bool + { + if( !in_array( $proto, [ 4, 6 ] ) ) { + throw new IXP_Exception( 'Invalid protocol' ); + } + + foreach( $this->virtualInterfaces as $vi ) { + foreach( $vi->vlanInterfaces as $vli ) { + if( $vli->ipvxEnabled( $proto ) ) { + return true; + } + } + } + + return false; + } + + + + /** + * Is this customer graphable? + * + * @return bool + */ + public function isGraphable(): bool + { + foreach( $this->virtualInterfaces as $vi ) { + if( $vi->isGraphable() ) { + return true; + } + } + + return false; + } + + /** + * Does the customer have any interfaces in quarantine/connected? + * + * I.e. does the customer have graphable interfaces? + * + * @return bool + */ + public function hasInterfacesConnectedOrInQuarantine(): bool + { + foreach( $this->virtualInterfaces as $vi ) { + foreach( $vi->physicalInterfaces as $pi ) { + if( $pi->isConnectedOrQuarantine() ) { + return true; + } + } + } + + return false; + } + + /** + * Utility function to provide a array of all members connected to the exchange (including at + * least one physical interface with status 'CONNECTED'). + * + * @param bool $externalOnly If `true`, only include external customers (i.e. no internal types) + * @param bool $active If `true`, only include active customers + * @param string $orderBy + * @param string $direction Direction for the order by + * + * @return Collection + */ + public static function getConnected( $externalOnly = false, $active = false , string $orderBy = 'name', string $direction = 'asc' ): Collection + { + return self::select( 'cust.*' ) + ->leftJoin( 'virtualinterface AS vi', 'vi.custid', 'cust.id' ) + ->leftJoin( 'physicalinterface AS pi', 'pi.virtualinterfaceid', 'vi.id' ) + ->whereRaw( self::SQL_CUST_CURRENT ) + ->whereRaw(self::SQL_CUST_TRAFFICING ) + ->where('pi.status', PhysicalInterface::STATUS_CONNECTED ) + ->when( $externalOnly , function( Builder $q ) { + return $q->whereRaw( self::SQL_CUST_EXTERNAL ); + }) + ->when( $active , function( Builder $q ) { + return $q->whereRaw( self::SQL_CUST_ACTIVE ); + }) + ->with( [ 'tags', 'virtualInterfaces', 'virtualInterfaces.physicalInterfaces.switchPort.switcher', 'virtualInterfaces.vlanInterfaces.ipv4address', 'virtualInterfaces.vlanInterfaces.ipv6address', 'virtualInterfaces.vlanInterfaces.layer2addresses' ] ) + ->orderBy( 'cust.' . $orderBy , $direction )->distinct()->get(); + } + + /** + * Does the customer have any interfaces in quarantine? + * + * @return bool + */ + public function hasInterfacesInQuarantine(): bool + { + return (bool)self::leftJoin( 'virtualinterface AS vi', 'vi.custid', 'cust.id' ) + ->leftJoin( 'physicalinterface AS pi', 'pi.virtualinterfaceid', 'vi.id' ) + ->where( 'cust.id', $this->id )->where( 'pi.status', PhysicalInterface::STATUS_QUARANTINE ) + ->get()->count(); + } + + /** + * Does the customer have private VLANs? + * + * A private VLAN is a VLAN between a subset of members (usually + * just two). + * + * @return bool + */ + public function hasPrivateVlans(): bool + { + return (bool)self::leftJoin( 'virtualinterface AS vi', 'vi.custid', 'cust.id' ) + ->leftJoin( 'vlaninterface AS vli', 'vli.virtualinterfaceid', 'vi.id' ) + ->leftJoin( 'vlan AS v', 'v.id', 'vli.vlanid' ) + ->where( 'v.private', true )->where( 'cust.id', $this->id ) + ->get()->count(); + } + + /** + * Get private VLAN information as an associate array + * + * Useful utility function for displaying a customers private VLANs in the + * overview page and the customer's own portal. + * + * Response is an array such as: + * + * [8] => [ // VLAN ID + * [vlis] => [ + * // VlanInterface objects for the customer that are on this private VLAN + * ], + * [members] => [ + * // Customer objects for all customers (including this one) that share this VLAN + * ] + * ] + * + * @return (VlanInterface|self)[][][] + * + * @psalm-return array<''|int, array{vlis: non-empty-list, members?: array<''|int, self>}> + */ + public function privateVlanDetails(): array + { + $pvlans = []; + + foreach( $this->vlanInterfaces as $vli ){ + if( $vli->vlan->private ) { + $pvlans[ $vli->vlanid ][ 'vlis' ][] = $vli; + + foreach( $vli->vlan->vlanInterfaces as $vli2 ) { + $pvlans[ $vli->vlanid ][ 'members' ][ $vli2->virtualInterface->custid ] + = $vli2->virtualInterface->customer; + } + } + } + return $pvlans; + } + + /** + * String to describe the model being updated / deleted / created + * + * @param Model $model + * + * @return string + */ + public static function logSubject( Model $model ): string + { + return sprintf( + "%s [id:%d] '%s'", + config( 'ixp_fe.lang.customer.one' ), + $model->id, + $model->name, + ); + } + + /** + * Scope a query to get the list of IP address for a customer on a vlan and a protocol + * + * @param Builder $query + * @param int $vlanid + * @param int $cust + * @param int $protocol + * + * @psalm-return \Illuminate\Database\Eloquent\Collection + */ + public function scopeAddressesForVlan( $query, int $vlanid, int $cust, int $protocol ): \Illuminate\Database\Eloquent\Collection + { + $enabled = $protocol === 4 ? 'ipv4enabled' : 'ipv6enabled'; + $field = $protocol === 4 ? 'ipv4addressid' : 'ipv6addressid'; + $table = $protocol === 4 ? 'ipv4address' : 'ipv6address'; + + return $query->select($table.'.address' ) + ->join( 'virtualinterface AS vi', 'cust.id', 'vi.custid' ) + ->join( 'vlaninterface AS vli', 'vi.id','vli.virtualinterfaceid' ) + ->join( $table, 'vli.' . $field, $table . '.id' ) + ->where( 'vli.vlanid', $vlanid ) + ->where( 'vi.custid', $cust ) + ->where( 'vli.' . $enabled, true ) + ->get(); + } +} \ No newline at end of file diff --git a/app/Models/CustomerEquipment.php b/app/Models/CustomerEquipment.php new file mode 100644 index 000000000..d1ca5ac6a --- /dev/null +++ b/app/Models/CustomerEquipment.php @@ -0,0 +1,105 @@ + + */ + public function cabinet(): BelongsTo + { + return $this->belongsTo(Cabinet::class, 'cabinetid' ); + } + + /** + * String to describe the model being updated / deleted / created + * + * @param Model $model + * + * @return string + */ + public static function logSubject( Model $model ): string + { + return sprintf( + "Colocated Equipment (Customer Equipment) [id:%d] belonging to Rack (Cabinet) [id:%d] '%s'", + $model->id, + $model->cabinetid, + $model->cabinet->name + ); + } +} \ No newline at end of file diff --git a/app/Models/CustomerNote.php b/app/Models/CustomerNote.php new file mode 100644 index 000000000..0cfb70738 --- /dev/null +++ b/app/Models/CustomerNote.php @@ -0,0 +1,158 @@ + + */ + public function customer(): BelongsTo + { + return $this->belongsTo(Customer::class, 'customer_id' ); + } + + /** + * Scope a query to only include private notes + * + * @param Builder $query + * + * @return Builder + */ + public function scopePrivateOnly( Builder $query ): Builder + { + return $query->where( 'private', 1 ); + } + + /** + * Scope a query to only public notes + * + * @param Builder $query + * + * @return Builder + */ + public function scopePublicOnly( Builder $query ): Builder + { + return $query->where( 'private', 0 ); + } + + /** + * String to describe the model being updated / deleted / created + * + * @param Model $model + * + * @return string + */ + public static function logSubject( Model $model ): string + { + return sprintf( + "%s Note [id:%d] belonging to %s [id:%d] '%s'", + ucfirst( config( 'ixp_fe.lang.customer.one' ) ), + $model->id, + config( 'ixp_fe.lang.customer.one' ), + $model->customer->id, + $model->customer->name + ); + } + + /** + * Get note read statistics for a given set of notes and a user + * + * Returns an associate array with keys: + * + * `notesReadUpto` - UNIX timestamp of when the user last read all notes / marked them as read + * `notesLastRead` - UNIX timestamp of when the user last read this customer's notes + * `unreadNotes` - number of unread notes for this customer + * + * @param Collection $notes + * @param Customer $c The customer + * @param User $u Optional user + * + * @return (int|mixed|null)[] + * + * @psalm-return array{notesReadUpto: mixed|null, notesLastRead: mixed|null, unreadNotes: int} + */ + public static function analyseForUser( Collection $notes, Customer $c, User $u ): array + { + $unreadNotes = 0; + $rut = $u->prefs[ 'notes' ][ 'read_upto' ] ?? null; + $lastRead = $u->prefs[ 'notes' ][ 'last_read' ][ $c->id ] ?? null; + + if( $lastRead || $rut ) { + foreach( $notes as $note ) {/** @var CustomerNote $note */ + if( ( !$rut || $rut < $note->updated_at ) && ( !$lastRead || $lastRead < $note->updated_at ) ) { + $unreadNotes++; + } + } + } else { + $unreadNotes = $notes->count(); + } + + return [ "notesReadUpto" => $rut , "notesLastRead" => $lastRead, "unreadNotes" => $unreadNotes ]; + } +} diff --git a/app/Models/CustomerTag.php b/app/Models/CustomerTag.php new file mode 100644 index 000000000..8db18dea3 --- /dev/null +++ b/app/Models/CustomerTag.php @@ -0,0 +1,90 @@ + $customers + * @property-read int|null $customers_count + * @method static Builder|CustomerTag newModelQuery() + * @method static Builder|CustomerTag newQuery() + * @method static Builder|CustomerTag query() + * @method static Builder|CustomerTag whereCreatedAt($value) + * @method static Builder|CustomerTag whereDescription($value) + * @method static Builder|CustomerTag whereDisplayAs($value) + * @method static Builder|CustomerTag whereId($value) + * @method static Builder|CustomerTag whereInternalOnly($value) + * @method static Builder|CustomerTag whereTag($value) + * @method static Builder|CustomerTag whereUpdatedAt($value) + * @property string $created + * @property string $updated + * @method static Builder|CustomerTag whereCreated($value) + * @method static Builder|CustomerTag whereUpdated($value) + * @mixin \Eloquent + */ +class CustomerTag extends Model +{ + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'cust_tag'; + + /** + * The attributes that are mass assignable. + * + * @var array + */ + protected $fillable = [ + 'tag', + 'display_as', + 'description', + 'internal_only', + ]; + + /** + * Get all the customers for the tag + * + * @psalm-return BelongsToMany + */ + public function customers(): BelongsToMany + { + return $this->belongsToMany(Customer::class, 'cust_to_cust_tag', 'customer_tag_id' ); + } +} diff --git a/app/Models/CustomerToCustomerTag.php b/app/Models/CustomerToCustomerTag.php new file mode 100644 index 000000000..776f2bbbd --- /dev/null +++ b/app/Models/CustomerToCustomerTag.php @@ -0,0 +1,107 @@ + + */ + public function customer(): BelongsTo + { + return $this->belongsTo(Customer::class, 'customer_id' ); + } + + /** + * Get the tag that own the customer to contact + * + * @psalm-return BelongsTo + */ + public function tag(): BelongsTo + { + return $this->belongsTo(CustomerTag::class, 'customer_tag_id' ); + } + /** + * String to describe the model being updated / deleted / created + * + * @param Model $model + * + * @return string + */ + public static function logSubject( Model $model ): string + { + return sprintf( + "%s Tag [id:%d] '%s' associated to %s [id:%d] '%s'", + ucfirst( config( 'ixp_fe.lang.customer.one' ) ), + $model->customer_tag_id, + $model->tag->tag, + config( 'ixp_fe.lang.customer.one' ), + $model->customer_id, + $model->customer->name + ); + } +} diff --git a/app/Models/CustomerToUser.php b/app/Models/CustomerToUser.php new file mode 100644 index 000000000..93cbfc57f --- /dev/null +++ b/app/Models/CustomerToUser.php @@ -0,0 +1,167 @@ + $userLoginHistories + * @property-read int|null $user_login_histories_count + * @method static Builder|CustomerToUser custAdmin() + * @method static Builder|CustomerToUser newModelQuery() + * @method static Builder|CustomerToUser newQuery() + * @method static Builder|CustomerToUser query() + * @method static Builder|CustomerToUser whereCreatedAt($value) + * @method static Builder|CustomerToUser whereCustomerId($value) + * @method static Builder|CustomerToUser whereExtraAttributes($value) + * @method static Builder|CustomerToUser whereId($value) + * @method static Builder|CustomerToUser whereLastLoginDate($value) + * @method static Builder|CustomerToUser whereLastLoginFrom($value) + * @method static Builder|CustomerToUser whereLastLoginVia($value) + * @method static Builder|CustomerToUser wherePrivs($value) + * @method static Builder|CustomerToUser whereUpdatedAt($value) + * @method static Builder|CustomerToUser whereUserId($value) + * @mixin \Eloquent + */ +class CustomerToUser extends Model +{ + use Observable; + /** + * The attributes that are mass assignable. + * + * @var array + */ + protected $fillable = [ + 'customer_id', + 'user_id', + 'privs', + 'extra_attributes', + 'last_login_date', + 'last_login_from', + 'last_login_via', + ]; + + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + 'extra_attributes' => 'json', + ]; + + /** + * The attributes that should not be logged + * + * @var array + */ + public $field_log_exception = [ + 'last_login_date', + 'updated_at', + ]; + + /** + * Get the customer for the customertouser + * + * @psalm-return BelongsTo + */ + public function customer(): BelongsTo + { + return $this->belongsTo(Customer::class, 'customer_id' ); + } + + /** + * Get the user for the customertouser + * + * @psalm-return BelongsTo + */ + public function user(): BelongsTo + { + return $this->belongsTo(User::class, 'user_id' ); + } + + /** + * Get the user Login Histories for the customertouser + * + * @psalm-return HasMany + */ + public function userLoginHistories(): HasMany + { + return $this->hasMany(UserLoginHistory::class, 'customer_to_user_id'); + } + + /** + * Scope a query to only include cust admins. + * + * @param Builder $query + * + * @return Builder + */ + public function scopeCustAdmin( Builder $query ): Builder + { + return $query->where('privs', User::AUTH_CUSTADMIN ); + } + + /** + * String to describe the model being updated / deleted / created + * + * @param Model $model + * + * @return string + */ + public static function logSubject( Model $model ): string + { + return sprintf( + "%s To User [id:%d] belonging to %s [id:%d] '%s' and User [id:%d] '%s'", + ucfirst( config( 'ixp_fe.lang.customer.one' ) ), + $model->id, + ucfirst( config( 'ixp_fe.lang.customer.one' ) ), + $model->customer_id, + $model->customer->name, + $model->user_id, + $model->user->username, + ); + } +} \ No newline at end of file diff --git a/app/Models/DocstoreCustomerDirectory.php b/app/Models/DocstoreCustomerDirectory.php new file mode 100644 index 000000000..cc1a326b0 --- /dev/null +++ b/app/Models/DocstoreCustomerDirectory.php @@ -0,0 +1,346 @@ + $files + * @property-read int|null $files_count + * @property-read DocstoreCustomerDirectory|null $parentDirectory + * @property-read Collection $subDirectories + * @property-read int|null $sub_directories_count + * @method static Builder|DocstoreCustomerDirectory newModelQuery() + * @method static Builder|DocstoreCustomerDirectory newQuery() + * @method static Builder|DocstoreCustomerDirectory query() + * @method static Builder|DocstoreCustomerDirectory whereCreatedAt($value) + * @method static Builder|DocstoreCustomerDirectory whereCustId($value) + * @method static Builder|DocstoreCustomerDirectory whereDescription($value) + * @method static Builder|DocstoreCustomerDirectory whereId($value) + * @method static Builder|DocstoreCustomerDirectory whereName($value) + * @method static Builder|DocstoreCustomerDirectory whereParentDirId($value) + * @method static Builder|DocstoreCustomerDirectory whereUpdatedAt($value) + * @mixin Eloquent + */ + +class DocstoreCustomerDirectory extends Model +{ + /** + * The attributes that are mass assignable. + * + * @var array + */ + protected $fillable = [ + 'name', + 'description', + 'parent_dir_id', + 'cust_id' + ]; + + /** + * Static property used by getHierarchyForUserClass() and recurseForHierarchyForUserClass() + * to build the hierarchy. + * + * Yields an array of the following form (i.e. an array of subdirs in each dir): + * + * ``` + * [ + * directory_id => [ [ 'id' => subdir_id, 'name' => subdir_name ], [ ... ], ... ], + * ... + * ] + * ``` + * + * @var array + */ + public static $dirs = []; + + /** + * The cache key used in getHierarchyForUserClass() + * @var string + */ + public const CACHE_KEY_FOR_CUSTOMER_USER_CLASS_HIERARCHY = 'docstore_customer_directory_hierarchy_for_user_class_'; + + /** + * The "booting" method of the model. + * + * @return void + */ + #[\Override] + protected static function boot(): void + { + parent::boot(); + + static::addGlobalScope('privs', function ( Builder $builder ) { + /** @var User $us */ + $us = Auth::getUser(); + + if( !Auth::check() ) { + // if public user make sure that no records are returned + $builder->where('id', null ); + } elseif( !$us->isSuperUser() ) { + // if not superuser make sure only records from the same customer are returned + $builder->where('cust_id', $us->custid ); + } + }); + } + + /** + * Get the customer + * + * @psalm-return BelongsTo + */ + public function customer(): BelongsTo + { + return $this->belongsTo(Customer::class, 'cust_id', 'id' ); + } + + /** + * Get the subdirectories for this directory + */ + public function subDirectories(): HasMany + { + return $this->hasMany( __CLASS__, 'parent_dir_id', 'id' )->orderBy('name'); + } + + /** + * Get the parent directory + * + * @psalm-return BelongsTo + */ + public function parentDirectory(): BelongsTo + { + return $this->belongsTo( __CLASS__, 'parent_dir_id', 'id' ); + } + + /** + * Get the files in this directory + * + * @psalm-return HasMany + */ + public function files(): HasMany + { + return $this->hasMany(DocstoreCustomerFile::class); + } + + /** + * Gets a listing of directories for the given (or root) directory and as + * appropriate for the user (or public access) + * + * @param Customer $cust + * @param DocstoreDirectory|null $dir + * @param User $user + * + * @return Collection + */ + public static function getListing( Customer $cust, User $user, ?DocstoreDirectory $dir = null ): Collection + { + return self::where( 'cust_id', $cust->id )->where('parent_dir_id', $dir->id ?? null) + ->when( !$user->isSuperUser() , function( Builder $q ) use ( $user ) { + $q->whereHas( 'files', function( Builder $q ) use ( $user ) { + return $q->where( 'min_privs', '<=', $user->privs() ); + } ); + })->orderBy('name')->get(); + } + + /** + * Create an array of directories keeping the hierarchy root/subfolder + * + * [ + * [ "id" => 1, "name" => "Folder 1" ], + * [ "id" => 2, "name" => " - Sub Folder 1" ], + * [ "id" => 3, "name" => " - Sub Folder 2" ], + * [ "id" => 4, "name" => "Folder 2" ], + * ] + * + * @param $dirs Collection + * @param $depth int + * + * @return ((mixed|string)[]|mixed)[] + * + * @psalm-return list{0: array{id: ''|mixed, name: string}|mixed, 1?: array{id: ''|mixed, name: string}|mixed,...} + */ + public static function getListingForDropdown( Collection $dirs, int $depth = 5 ): array + { + $data = []; + $data[] = [ 'id' => '', 'name' => 'Root Directory' ]; + + foreach( $dirs as $dir ) { + $data[] = [ 'id' => $dir->id, 'name' => str_repeat( ' ', $depth ) . '- ' . $dir->name ]; + foreach( self::getListingForDropdown( $dir->subDirectories, $depth + 5 ) as $sub ) { + $data[] = $sub; + } + } + return $data; + } + + /** + * Build a hierarchy of all subdirs that this user class should be able to see. + * + * This also caches the results as it can be query intensive for large structures and, + * for choosing to display the menu options, would be ran per page hit. + * + * @param Customer $cust + * @param int $priv + * @param bool $showRoot Should we show the Root directory ('Root Directory') + * + * @return mixed + */ + public static function getHierarchyForCustomerAndUserClass( Customer $cust, int $priv = User::AUTH_SUPERUSER, bool $showRoot = true ) + { + return Cache::remember( self::CACHE_KEY_FOR_CUSTOMER_USER_CLASS_HIERARCHY . $cust->id . '_' . $priv, 86400, function() use ( $cust, $priv, $showRoot ) { + self::where( 'cust_id', $cust->id )->whereNull('parent_dir_id' ) + ->orderBy('name')->get()->each( function( $sd ) use ( $priv ) { + if( self::recurseForHierarchyForCustomerAndUserClass( $sd, $priv ) ) { + self::$dirs[ $sd->parent_dir_id ][] = [ 'id' => $sd->id, 'name' => $sd->name ]; + } + }); + + if( self::$dirs === [] ) { + // nothing for this user class in subdirectories. Should they see the document store at all? + // Yes, they should, if it itself contains files they should be able to see: + $rootDirVisible = self::where('parent_dir_id', null ) + ->whereHas( 'files', function( Builder $query ) use ( $priv ) { + $query->where( 'min_privs', '<=', $priv ); + } )->get()->isNotEmpty(); + + if( $rootDirVisible && $showRoot ) { + self::$dirs[] = [ 'id' => null, 'name' => 'Root Directory' ]; + } + } + + return self::$dirs; + } ); + } + + /** + * Companion recursive function for getHierarchyForUserClass() to recurse to the leaf node + * of a directory hierarchy and return uo through the parent nodes whether it should be + * included or not. + * + * @param DocstoreCustomerDirectory $subdir + * @param int $priv User class to test for + * + * @return bool + */ + private static function recurseForHierarchyForCustomerAndUserClass( DocstoreCustomerDirectory $subdir, int $priv ): bool + { + $includeSubdir = false; + foreach( $subdir->subDirectories as $sd ) { + if( $shouldInclude = self::recurseForHierarchyForCustomerAndUserClass( $sd, $priv ) ) { + self::$dirs[$sd->parent_dir_id][] = [ 'id' => $sd->id, 'name' => $sd->name ]; + $includeSubdir = true; + } + } + + // we have recursed all the subdirectories above. Have we decided to include this one? + if( $includeSubdir ) { + return true; + } + + if( $priv === User::AUTH_SUPERUSER ) { + return true; + } + + return self::where( 'id', $subdir->id ) + ->whereHas( 'files', function( Builder $query ) use ( $priv ) { + $query->where( 'min_privs', '<=', $priv ); + } ) + ->get()->isNotEmpty(); + } + + /** + * Delete all the files and subdirectories for a given customer folder + * + * @param DocstoreCustomerDirectory $dir + * + * @throws + */ + public static function recursiveDelete( DocstoreCustomerDirectory $dir ): void + { + $dir->subDirectories->each( function( DocstoreCustomerDirectory $subdir ) { + self::recursiveDelete( $subdir ); + }); + + $dir->files->each( function( DocstoreCustomerFile $file ) use ( $dir ) { + Storage::disk( $file->disk )->delete( $file->path ); + $file->delete(); + Log::info( sprintf( "Docstore: file [%d|%s] for the customer [%d|%s] deleted", $file->id, $file->name, $dir->customer->id, $dir->customer->name ) ); + }); + + Log::info( sprintf( "Docstore: directory [%d|%s] for the customer [%d|%s] deleted", $dir->id, $dir->name, $dir->customer->id, $dir->customer->name ) ); + $dir->delete(); + } + + /** + * Delete all the files and directories for a given customer + * + * @param Customer $cust + * + * @throws + */ + public static function deleteAllForCustomer( Customer $cust ): void + { + // Getting all the root directories for the customer + $rootDirs = self::where( 'cust_id', $cust->id )->where( 'parent_dir_id', null )->get(); + + $rootDirs->each( function( DocstoreCustomerDirectory $dir ) { + self::recursiveDelete( $dir ); + }); + + // Do we have files at the root that was not belonging to a directory ? + $cust->docstoreCustomerFiles()->each( function( DocstoreCustomerFile $file ) use( $cust ) { + Storage::disk( $file->disk )->delete( $file->path ); + $file->delete(); + Log::info( sprintf( "Docstore: file [%d|%s] for the customer [%d|%s] deleted", $file->id, $file->name, $cust->id, $cust->name ) ); + }); + } +} \ No newline at end of file diff --git a/app/Models/DocstoreCustomerFile.php b/app/Models/DocstoreCustomerFile.php new file mode 100644 index 000000000..502a37d7e --- /dev/null +++ b/app/Models/DocstoreCustomerFile.php @@ -0,0 +1,248 @@ +where('id', null ); + } elseif( !$us->isSuperUser() ) { + // If not super user make sure only allowed files are returned + $builder->where('min_privs', '<=', $us->privs() ); + } + }); + } + + /** + * Get the directory that owns the file. + * + * @psalm-return BelongsTo + */ + public function directory(): BelongsTo + { + return $this->belongsTo(DocstoreCustomerDirectory::class, 'docstore_customer_directory_id' ); + } + + /** + * Get the customer that owns the file + * + * @psalm-return BelongsTo + */ + public function customer(): BelongsTo + { + return $this->belongsTo(Customer::class, 'cust_id' ); + } + + /** + * Can we view that file? + * + * @return bool + */ + public function isViewable(): bool + { + return in_array( '.' . pathinfo( strtolower( $this->name ), PATHINFO_EXTENSION ), self::$extensionViewable, true ); + } + + /** + * Can we edit that file? + * + * @return bool + */ + public function isEditable(): bool + { + return in_array( '.' . pathinfo( strtolower( $this->name ), PATHINFO_EXTENSION ), self::$extensionEditable, true ); + } + + /** + * Get the extension of the file + * + * @return string + */ + public function extension(): string + { + return pathinfo( $this->name, PATHINFO_EXTENSION ); + } + + /** + * Gets a directory listing of files for the given Customer and directory and as + * appropriate for the user + * + * @param Customer $cust + * @param DocstoreCustomerDirectory|null $dir + * @param User $user + * + * @return Collection + */ + public static function getListing( Customer $cust, User $user, ?DocstoreCustomerDirectory $dir = null ) + { + return self::where('min_privs', '<=', $user->privs() ) + ->where('cust_id', $cust->id ) + ->where('docstore_customer_directory_id', $dir ? $dir->id : null ) + ->orderBy('name')->get(); + } + + /** + * Gets listing of files for the given Customer and all the directories and as + * appropriate for the user + * + * @param int $cust_id + * @param int $privs + * + * @return Collection + */ + public static function getListingForAllDirectories( int $cust_id, int $privs ): Collection + { + return self::where('min_privs', '<=', $privs ) + ->where('cust_id', $cust_id ) + ->orderBy('name')->get(); + } + + /** + * String to describe the model being updated / deleted / created + * + * @param Model $model + * + * @return string + */ + public static function logSubject( Model $model ): string + { + return sprintf( + "Docstore File [id:%d] '%s' belonging to %s [id:%d] '%s'", + $model->id, + $model->name, + ucfirst( config( 'ixp_fe.lang.customer.one' ) ), + $model->cust_id, + $model->customer->name + ); + } +} \ No newline at end of file diff --git a/app/Models/DocstoreDirectory.php b/app/Models/DocstoreDirectory.php new file mode 100644 index 000000000..d3d67b7de --- /dev/null +++ b/app/Models/DocstoreDirectory.php @@ -0,0 +1,284 @@ + $files + * @property-read int|null $files_count + * @property-read DocstoreDirectory|null $parentDirectory + * @property-read EloquentCollection $subDirectories + * @property-read int|null $sub_directories_count + * @method static Builder|DocstoreDirectory newModelQuery() + * @method static Builder|DocstoreDirectory newQuery() + * @method static Builder|DocstoreDirectory query() + * @method static Builder|DocstoreDirectory whereCreatedAt($value) + * @method static Builder|DocstoreDirectory whereDescription($value) + * @method static Builder|DocstoreDirectory whereId($value) + * @method static Builder|DocstoreDirectory whereName($value) + * @method static Builder|DocstoreDirectory whereParentDirId($value) + * @method static Builder|DocstoreDirectory whereUpdatedAt($value) + * @mixin Eloquent + */ + +class DocstoreDirectory extends Model +{ + /** + * The attributes that are mass assignable. + * + * @var array + */ + protected $fillable = [ + 'name', + 'description', + 'parent_dir_id' + ]; + + /** + * Get the subdirectories for this directory + */ + public function subDirectories(): HasMany + { + return $this->hasMany( __CLASS__, 'parent_dir_id', 'id' )->orderBy('name'); + } + + /** + * Get the parent directory + * + * @psalm-return BelongsTo + */ + public function parentDirectory(): BelongsTo + { + return $this->belongsTo( __CLASS__, 'parent_dir_id', 'id' ); + } + + /** + * Get the files in this directory + * + * @psalm-return HasMany + */ + public function files(): HasMany + { + return $this->hasMany(DocstoreFile::class ); + } + + /** + * Gets a listing of directories for the given (or root) directory and as + * appropriate for the user (or public access) + * + * @param DocstoreDirectory|null $dir + * @param User|null $user + * + * @return EloquentCollection + */ + public static function getListing( ?DocstoreDirectory $dir, ?User $user ): EloquentCollection + { + return self::where('parent_dir_id', $dir->id ?? null ) + ->when( !$user || !$user->isSuperUser() , function( Builder $q ) use ( $user) { + $q->whereHas( 'files', function( Builder $q ) use ( $user ) { + return $q->where( 'min_privs', '<=', $user ? $user->privs() : User::AUTH_PUBLIC ); + } ); + })->orderBy('name')->get(); + + } + + /** + * Create an array of directories keeping the hierarchy root/subfolder + * + * [ + * [ "id" => 1, "name" => "Folder 1" ], + * [ "id" => 2, "name" => " - Sub Folder 1" ], + * [ "id" => 3, "name" => " - Sub Folder 2" ], + * [ "id" => 4, "name" => "Folder 2" ], + * ] + * + * @param $dirs EloquentCollection + * @param $depth int + * + * @return ((mixed|string)[]|mixed)[] + * + * @psalm-return list{0: array{id: ''|mixed, name: string}|mixed, 1?: array{id: ''|mixed, name: string}|mixed,...} + */ + public static function getListingForDropdown( EloquentCollection $dirs, int $depth = 5 ): array + { + $data = []; + $data[] = [ 'id' => '', 'name' => 'Root Directory' ]; + + foreach( $dirs as $dir ) { + $data[] = [ 'id' => $dir->id, 'name' => str_repeat( ' ', $depth ) . '- ' . $dir->name ]; + + foreach( self::getListingForDropdown( $dir->subDirectories, $depth + 5 ) as $sub ) { + $data[] = $sub; + } + } + return $data; + } + + /** + * Static property used by getHierarchyForUserClass() and recurseForHierarchyForUserClass() + * to build the hierarchy. + * + * Yields an array of the following form (i.e. an array of subdirs in each dir): + * + * ``` + * [ + * directory_id => [ [ 'id' => subdir_id, 'name' => subdir_name ], [ ... ], ... ], + * ... + * ] + * ``` + * + * @var array + */ + public static $dirs = []; + + /** + * The cache key used in getHierarchyForUserClass() + * @var string + */ + public const CACHE_KEY_FOR_USER_CLASS_HIERARCHY = 'docstore_directory_hierarchy_for_user_class_'; + + /** + * Build a hierarchy of all subdirs that this user class should be able to see. + * + * This also caches the results as it can be query intensive for large structures and, + * for choosing to display the menu options, would be ran per page hit. + * + * @param int $priv + * + * @return mixed + */ + public static function getHierarchyForUserClass( int $priv = User::AUTH_SUPERUSER ) + { + return Cache::remember( self::CACHE_KEY_FOR_USER_CLASS_HIERARCHY . $priv, 86400, function() use ( $priv ) { + self::where('parent_dir_id', null )->orderBy('name' )->get()->each( function( $sd ) use ( $priv ) { + if( self::recurseForHierarchyForUserClass( $sd, $priv ) ) { + self::$dirs[ $sd->parent_dir_id ][] = [ 'id' => $sd->id, 'name' => $sd->name ]; + } + }); + + if( self::$dirs === [] ) { + // nothing for this user class in subdirectories. Should they see the document store at all? + // Yes, they should, if it itself contains files they should be able to see: + $rootDirVisible = self::where('parent_dir_id', null ) + ->whereHas( 'files', function( Builder $query ) use ( $priv ) { + $query->where( 'min_privs', '<=', $priv ); + } )->get()->isNotEmpty(); + + if( $rootDirVisible ) { + self::$dirs[] = [ 'id' => null, 'name' => 'Root Directory' ]; + } + } + return self::$dirs; + } ); + } + + /** + * Companion recursive function for getHierarchyForUserClass() to recurse to the leaf node + * of a directory hierarchy and return uo through the parent nodes whether it should be + * included or not. + * + * @param DocstoreDirectory $subdir + * @param int $priv User class to test for + * + * @return bool + */ + private static function recurseForHierarchyForUserClass( DocstoreDirectory $subdir, int $priv ): bool + { + $includeSubdir = false; + foreach( $subdir->subDirectories as $sd ) { + if( $shouldInclude = self::recurseForHierarchyForUserClass( $sd, $priv ) ) { + self::$dirs[ $sd->parent_dir_id ][] = [ 'id' => $sd->id, 'name' => $sd->name ]; + $includeSubdir = true; + } + } + + // we have recursed all the subdirectories above. Have we decided to include this one? + if( $includeSubdir ) { + return true; + } + + if( $priv === User::AUTH_SUPERUSER ) { + return true; + } + + return self::where( 'id', $subdir->id ) + ->whereHas( 'files', function( Builder $query ) use ( $priv ) { + $query->where( 'min_privs', '<=', $priv ); + } ) + ->get()->isNotEmpty(); + } + + /** + * Delete all the files and subdirectories for a given folder + * + * @param DocstoreDirectory $dir + * + * @return void + * + * @throws + */ + public static function recursiveDelete( DocstoreDirectory $dir ): void + { + $dir->subDirectories->each( function( DocstoreDirectory $subdir ) { + self::recursiveDelete( $subdir ); + }); + + $dir->files->each( function( DocstoreFile $file ) { + $file->logs()->delete(); + Storage::disk( $file->disk )->delete( $file->path ); + $file->delete(); + Log::info( sprintf( "Docstore: file [%d|%s] deleted", $file->id, $file->name ) ); + }); + + Log::info( sprintf( "Docstore: directory [%d|%s] deleted", $dir->id, $dir->name ) ); + $dir->delete(); + } +} \ No newline at end of file diff --git a/app/Models/DocstoreFile.php b/app/Models/DocstoreFile.php new file mode 100644 index 000000000..c26437e9d --- /dev/null +++ b/app/Models/DocstoreFile.php @@ -0,0 +1,207 @@ + $logs + * @property-read int|null $logs_count + * @method static Builder|DocstoreFile newModelQuery() + * @method static Builder|DocstoreFile newQuery() + * @method static Builder|DocstoreFile query() + * @method static Builder|DocstoreFile whereCreatedAt($value) + * @method static Builder|DocstoreFile whereCreatedBy($value) + * @method static Builder|DocstoreFile whereDescription($value) + * @method static Builder|DocstoreFile whereDisk($value) + * @method static Builder|DocstoreFile whereDocstoreDirectoryId($value) + * @method static Builder|DocstoreFile whereFileLastUpdated($value) + * @method static Builder|DocstoreFile whereId($value) + * @method static Builder|DocstoreFile whereMinPrivs($value) + * @method static Builder|DocstoreFile whereName($value) + * @method static Builder|DocstoreFile wherePath($value) + * @method static Builder|DocstoreFile whereSha256($value) + * @method static Builder|DocstoreFile whereUpdatedAt($value) + * @mixin Eloquent + */ + +class DocstoreFile extends Model +{ + use Observable; + + /** + * The attributes that are mass assignable. + * + * @var array + */ + protected $fillable = [ + 'name', + 'description', + 'docstore_directory_id', + 'path', + 'sha256', + 'min_privs', + 'file_last_updated', + 'created_by', + ]; + + /** + * The attributes that should be mutated to dates. + * + * @var array + */ + protected $dates = [ + 'file_last_updated', + ]; + + + /** + * File extension allowed to be viewed + * + * @var array + */ + public static $extensionViewable = [ '.txt', '.md' ]; + + /** + * File extension allowed to be edited + * + * @var array + */ + public static $extensionEditable = [ '.txt', '.md' ]; + + /** + * Get the directory that owns the file. + * + * @psalm-return BelongsTo + */ + public function directory(): BelongsTo + { + return $this->belongsTo(DocstoreDirectory::class, 'docstore_directory_id' ); + } + + /** + * Get the access logs for this file + * + * @psalm-return HasMany + */ + public function logs(): HasMany + { + return $this->hasMany(DocstoreLog::class ); + } + + /** + * Can we view that file? + * + * @return bool + */ + public function isViewable(): bool + { + return in_array( '.' . pathinfo( $this->name, PATHINFO_EXTENSION ), self::$extensionViewable, true ); + } + + /** + * Can we edit that file? + * + * @return bool + */ + public function isEditable(): bool + { + return in_array( '.' . pathinfo( $this->name, PATHINFO_EXTENSION ), self::$extensionEditable, true ); + } + + /** + * Get the extension of the file + * + * @return string + */ + public function extension(): string + { + return pathinfo( $this->name, PATHINFO_EXTENSION ); + } + + /** + * Gets a directory listing of files for the given (or root) directory and as + * appropriate for the user (or public access) + * + * @param DocstoreDirectory|null $dir + * @param int $privs + * + * @return Collection + */ + public static function getListing( ?DocstoreDirectory $dir = null, int $privs = User::AUTH_PUBLIC ): Collection + { + return self::where('min_privs', '<=', $privs ) + ->where('docstore_directory_id', $dir->id ?? null ) + ->withCount([ 'logs as downloads_count', 'logs as unique_downloads_count' => function( Builder $query ) { + $query->select( DB::raw('COUNT( DISTINCT downloaded_by )' ) ); + }]) + ->orderBy('name')->get(); + } + + /** + * String to describe the model being updated / deleted / created + * + * @param Model $model + * + * @return string + */ + public static function logSubject( Model $model ): string + { + return sprintf( + "Docstore File [id:%d] '%s'", + $model->id, + $model->name, + ); + } +} \ No newline at end of file diff --git a/app/Models/DocstoreLog.php b/app/Models/DocstoreLog.php new file mode 100644 index 000000000..55be56bfa --- /dev/null +++ b/app/Models/DocstoreLog.php @@ -0,0 +1,115 @@ + + */ + public function file(): BelongsTo + { + return $this->belongsTo(DocstoreFile::class , 'docstore_file_id' ); + } + + /** + * Gets a listing of logs for the given file + * + * @param DocstoreFile $file Display logs from file + * + * @return Collection + */ + public static function getListing( DocstoreFile $file ): Collection + { + return self::select([ 'docstore_logs.*', 'user.name AS name', 'user.username AS username' ]) + ->where('docstore_file_id', $file->id ) + ->leftJoin( 'user', 'user.id', '=', 'docstore_logs.downloaded_by' ) + ->orderBy('created_at', 'desc')->get(); + } + + /** + * Gets a listing of logs for the given file + * + * @param DocstoreFile $file Display logs from file + * + * @return Collection + */ + public static function getUniqueUserListing( DocstoreFile $file ): Collection + { + return self::select([ 'docstore_logs.id', 'docstore_logs.downloaded_by', + DB::raw( 'COUNT(docstore_logs.downloaded_by) AS downloads' ), + DB::raw( 'MAX(docstore_logs.created_at) AS last_downloaded' ), + DB::raw( 'MIN(docstore_logs.created_at) AS first_downloaded' ), + 'user.name AS name', 'user.username AS username' + ]) + ->where('docstore_file_id', $file->id ) + ->leftJoin( 'user', 'user.id', '=', 'docstore_logs.downloaded_by' ) + ->groupBy( 'downloaded_by' ) + ->orderBy('downloaded_by')->get(); + } +} \ No newline at end of file diff --git a/app/Models/IPv4Address.php b/app/Models/IPv4Address.php new file mode 100644 index 000000000..8e3c30d07 --- /dev/null +++ b/app/Models/IPv4Address.php @@ -0,0 +1,82 @@ + + */ + public function vlan(): BelongsTo + { + return $this->belongsTo(Vlan::class, 'vlanid' ); + } + + /** + * Get the vlan interface associated with the ipv4. + * + * @psalm-return HasOne + */ + public function vlanInterface(): HasOne + { + return $this->hasOne(VlanInterface::class, 'ipv4addressid' ); + } +} diff --git a/app/Models/IPv6Address.php b/app/Models/IPv6Address.php new file mode 100644 index 000000000..972d12a8b --- /dev/null +++ b/app/Models/IPv6Address.php @@ -0,0 +1,82 @@ + + */ + public function vlan(): BelongsTo + { + return $this->belongsTo(Vlan::class, 'vlanid' ); + } + + /** + * Get the vlan interface associated with the ipv6. + * + * @psalm-return HasOne + */ + public function vlanInterface(): HasOne + { + return $this->hasOne(VlanInterface::class, 'ipv6addressid' ); + } + +} diff --git a/app/Models/Infrastructure.php b/app/Models/Infrastructure.php new file mode 100644 index 000000000..a16b32a9f --- /dev/null +++ b/app/Models/Infrastructure.php @@ -0,0 +1,134 @@ + $switchers + * @property-read int|null $switchers_count + * @property-read Collection $vlans + * @property-read int|null $vlans_count + * @method static Builder|Infrastructure newModelQuery() + * @method static Builder|Infrastructure newQuery() + * @method static Builder|Infrastructure query() + * @method static Builder|Infrastructure whereCountry($value) + * @method static Builder|Infrastructure whereCreatedAt($value) + * @method static Builder|Infrastructure whereId($value) + * @method static Builder|Infrastructure whereIsPrimary($value) + * @method static Builder|Infrastructure whereIxfIxId($value) + * @method static Builder|Infrastructure whereName($value) + * @method static Builder|Infrastructure whereNotes($value) + * @method static Builder|Infrastructure wherePeeringdbIxId($value) + * @method static Builder|Infrastructure whereShortname($value) + * @method static Builder|Infrastructure whereUpdatedAt($value) + * @property int $ixp_id + * @method static Builder|Infrastructure whereIxpId($value) + * @property-read Collection $vlans + * @mixin Eloquent + */ +class Infrastructure extends Model +{ + use Observable; + + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'infrastructure'; + + /** + * The attributes that are mass assignable. + * + * @var array + */ + protected $fillable = [ + 'ixp_id', + 'name', + 'shortname', + 'isPrimary', + 'peeringdb_ix_id', + 'ixf_ix_id', + 'country', + 'notes', + ]; + + /** + * Get the vlans for the infrastructure + * + * @psalm-return HasMany + */ + public function vlans(): HasMany + { + return $this->hasMany(Vlan::class, 'infrastructureid' ); + } + + /** + * Get the switchers for the infrastructure + * + * @psalm-return HasMany + */ + public function switchers(): HasMany + { + return $this->hasMany(Switcher::class, 'infrastructure' ); + } + + /** + * String to describe the model being updated / deleted / created + * + * @param Model $model + * + * @return string + */ + public static function logSubject( Model $model ): string + { + return sprintf( + "Infrastructure [id:%d] '%s'", + $model->id, + $model->name + ); + } +} \ No newline at end of file diff --git a/app/Models/IrrdbAsn.php b/app/Models/IrrdbAsn.php new file mode 100644 index 000000000..8b6677d01 --- /dev/null +++ b/app/Models/IrrdbAsn.php @@ -0,0 +1,75 @@ +|IrrdbAsn newQuery() + * @mixin \Eloquent + */ +class IrrdbAsn extends Model +{ + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'irrdb_asn'; + + /** + * The attributes that are mass assignable. + * + * @var array + */ + protected $fillable = [ + 'customer_id', + 'asn', + 'protocol', + 'first_seen', + 'last_seen', + ]; + +} diff --git a/app/Models/IrrdbConfig.php b/app/Models/IrrdbConfig.php new file mode 100644 index 000000000..e70c0716b --- /dev/null +++ b/app/Models/IrrdbConfig.php @@ -0,0 +1,107 @@ + $customers + * @property-read int|null $customers_count + * @method static Builder|IrrdbConfig newModelQuery() + * @method static Builder|IrrdbConfig newQuery() + * @method static Builder|IrrdbConfig query() + * @method static Builder|IrrdbConfig whereCreatedAt($value) + * @method static Builder|IrrdbConfig whereHost($value) + * @method static Builder|IrrdbConfig whereId($value) + * @method static Builder|IrrdbConfig whereNotes($value) + * @method static Builder|IrrdbConfig whereSource($value) + * @method static Builder|IrrdbConfig whereUpdatedAt($value) + * @property string|null $protocol + * @method static Builder|IrrdbConfig whereProtocol($value) + * @property string|null $source + * @mixin \Eloquent + */ +class IrrdbConfig extends Model +{ + use Observable; + + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'irrdbconfig'; + + /** + * The attributes that are mass assignable. + * + * @var array + */ + protected $fillable = [ + 'host', + 'source', + 'notes', + ]; + + /** + * Get the customers for the Irrdb Config + * + * @psalm-return HasMany + */ + public function customers(): HasMany + { + return $this->hasMany(Customer::class, 'irrdb'); + } + + /** + * String to describe the model being updated / deleted / created + * + * @param Model $model + * + * @return string + */ + public static function logSubject( Model $model ): string + { + return sprintf( + "IRRDB Config [id:%d] '%s', '%s'", + $model->id, + $model->host, + $model->source, + ); + } +} \ No newline at end of file diff --git a/app/Models/IrrdbPrefix.php b/app/Models/IrrdbPrefix.php new file mode 100644 index 000000000..40c1e3600 --- /dev/null +++ b/app/Models/IrrdbPrefix.php @@ -0,0 +1,90 @@ +|IrrdbPrefix newQuery() + * @mixin \Eloquent + */ +class IrrdbPrefix extends Model +{ + + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'irrdb_prefix'; + + /** + * The attributes that are mass assignable. + * + * @var array + */ + protected $fillable = [ + 'customer_id', + 'prefix', + 'protocol', + 'first_seen', + 'last_seen', + ]; + + /** + * Get the customer for the irrdb prefix + * + * @psalm-return BelongsTo + */ + public function customer(): BelongsTo + { + return $this->belongsTo(Customer::class, 'customer_id' ); + } + +} diff --git a/app/Models/IrrdbUpdateLog.php b/app/Models/IrrdbUpdateLog.php new file mode 100644 index 000000000..4057bb1f5 --- /dev/null +++ b/app/Models/IrrdbUpdateLog.php @@ -0,0 +1,116 @@ +|IrrdbUpdateLog whereUpdatedAt($value) + * @mixin \Eloquent + */ +class IrrdbUpdateLog extends Model +{ + + /** + * The attributes that are mass assignable. + * + * @var array + */ + protected $fillable = [ + 'cust_id', + 'prefix_v4', + 'prefix_v6', + 'asn_v4', + 'asn_v6', + ]; + + + /** + * Get the attributes that should be cast. + * + * @return string[] + * + * @psalm-return array{prefix_v4: 'datetime', prefix_v6: 'datetime', asn_v4: 'datetime', asn_v6: 'datetime'} + */ + #[\Override] + protected function casts(): array + { + return [ + 'prefix_v4' => 'datetime', + 'prefix_v6' => 'datetime', + 'asn_v4' => 'datetime', + 'asn_v6' => 'datetime', + ]; + } + + /** + * Find the /oldest/ /relevant/ last update: + * + * - oldest: will be null if never updated + * - relevant: if customer is not v6 enabled, ignore these fields + * + * @param Customer $c + */ + public static function lastUpdatedMax( Customer $c ): \Illuminate\Support\Carbon|null + { + /** @var IrrdbUpdateLog $log */ + $log = self::firstWhere('cust_id', $c->id); + + if( !$log || !$c->irrdbFiltered() ) { + return null; + } + + $oldest = null; + + if( $c->isIPvXEnabled(4) ) { + if( !$oldest && $log->prefix_v4 ) { + $oldest = $log->prefix_v4; + } else if( $oldest && $oldest > $log->prefix_v4 ) { + $oldest = $log->prefix_v4; + } + + if( !$oldest && $log->asn_v4 ) { + $oldest = $log->asn_v4; + } else if( $oldest && $oldest > $log->asn_v4 ) { + $oldest = $log->asn_v4; + } + } + + if( $c->isIPvXEnabled(6) ) { + if( !$oldest && $log->prefix_v6 ) { + $oldest = $log->prefix_v6; + } else if( $oldest && $oldest > $log->prefix_v6 ) { + $oldest = $log->prefix_v6; + } + + if( !$oldest && $log->asn_v6 ) { + $oldest = $log->asn_v6; + } else if( $oldest && $oldest > $log->asn_v6 ) { + $oldest = $log->asn_v6; + } + } + + return $oldest; + } + +} diff --git a/app/Models/Layer2Address.php b/app/Models/Layer2Address.php new file mode 100644 index 000000000..464827de3 --- /dev/null +++ b/app/Models/Layer2Address.php @@ -0,0 +1,136 @@ + + */ + public function vlanInterface(): BelongsTo + { + return $this->belongsTo(VlanInterface::class, 'vlan_interface_id'); + } + + /** + * Get mac formatted depending on the format selected + * - with comma (xx:xx:xx:xx:xx:xx) + * - with dots (xxxx.xxxx.xxxx) + * - with dash (xx-xx-xx-xx-xx-xx) + * + * @param string $format + * + * @return null|string + */ + public function macFormatted( string $format ): ?string + { + switch( $format ) { + case ':': + return wordwrap( $this->mac, 2, ':',true ); + break; + case '.': + return wordwrap( $this->mac, 4, '.',true ); + break; + case '-': + return wordwrap($this->mac, 2, '-',true); + break; + default: + return $this->mac; + } + } + + /** + * String to describe the model being updated / deleted / created + * + * @param Model $model + * + * @return string + */ + public static function logSubject( Model $model ): string + { + return sprintf( + "Layer2Address [id:%d] '%s' belonging to VlanInterface [id:%d]", + $model->id, + $model->macFormatted( ':' ), + $model->vlan_interface_id, + ); + } +} \ No newline at end of file diff --git a/app/Models/Location.php b/app/Models/Location.php new file mode 100644 index 000000000..1d44600e7 --- /dev/null +++ b/app/Models/Location.php @@ -0,0 +1,136 @@ + $cabinets + * @property-read int|null $cabinets_count + * @method static Builder|Location newModelQuery() + * @method static Builder|Location newQuery() + * @method static Builder|Location query() + * @method static Builder|Location whereAddress($value) + * @method static Builder|Location whereCity($value) + * @method static Builder|Location whereCountry($value) + * @method static Builder|Location whereCreatedAt($value) + * @method static Builder|Location whereId($value) + * @method static Builder|Location whereName($value) + * @method static Builder|Location whereNocemail($value) + * @method static Builder|Location whereNocfax($value) + * @method static Builder|Location whereNocphone($value) + * @method static Builder|Location whereNotes($value) + * @method static Builder|Location whereOfficeemail($value) + * @method static Builder|Location whereOfficefax($value) + * @method static Builder|Location whereOfficephone($value) + * @method static Builder|Location wherePdbFacilityId($value) + * @method static Builder|Location whereShortname($value) + * @method static Builder|Location whereTag($value) + * @method static Builder|Location whereUpdatedAt($value) + * @mixin \Eloquent + */ +class Location extends Model +{ + use Observable; + + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'location'; + + /** + * The attributes that are mass assignable. + * + * @var array + */ + protected $fillable = [ + 'name', + 'shortname', + 'tag', + 'address', + 'nocphone', + 'nocfax', + 'nocemail', + 'officephone', + 'officefax', + 'officeemail', + 'notes', + 'pdb_facility_id', + 'city', + 'country', + ]; + + /** + * Get the switchers for the cabinet + * + * @psalm-return HasMany + */ + public function cabinets(): HasMany + { + return $this->hasMany(Cabinet::class, 'locationid' ); + } + + /** + * String to describe the model being updated / deleted / created + * + * @param Model $model + * + * @return string + */ + public static function logSubject( Model $model ): string + { + return sprintf( + "Facility (Location) [id:%d] '%s'", + $model->id, + $model->name, + ); + } +} \ No newline at end of file diff --git a/app/Models/Log.php b/app/Models/Log.php new file mode 100644 index 000000000..4d5840666 --- /dev/null +++ b/app/Models/Log.php @@ -0,0 +1,161 @@ + self::ACTION_CREATED, + self::ACTION_UPDATED => self::ACTION_UPDATED, + self::ACTION_DELETED => self::ACTION_DELETED, + ]; + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'log'; + + /** + * The attributes that are mass assignable. + * + * @var string[] + */ + protected $fillable = [ + 'user_id', + 'model', + 'model_id', + 'action', + 'message', + 'models', + ]; + + /** + * The attributes that should be cast to native types. + * + * @var array + */ + protected $casts = [ + 'models' => 'json', + ]; + + /** + * Get the user record associated with this log + * + * @psalm-return HasOne + */ + public function user(): HasOne + { + return $this->hasOne(User::class, 'id', 'user_id'); + } + + /** + * @param string $model + * @param int $id + * @param User|null $user limit the log entries to the actions of this user + * + * @return Builder + * + * @psalm-return Builder + */ + public static function entries(string $model, int $id, ?User $user = null): Builder + { + $query = self::with('user' ) + ->where('model', $model ) + ->where('model_id', $id ); + + if ($user) { + $query->where('user_id', $user->id ); + } + + return $query->orderByDesc('id' ); + } + + /** + * @param $query + * @param array $filters + * + * @throws \Exception + */ + public function scopeFilter( Builder $query, array $filters ): void + { + $query->when( + $filters['search'] ?? null, + function (Builder $query, $search) { + $query->whereRaw("( message like ? or models like ? )", ["%{$search}%", "%{$search}%"]); + } + )->when( + $filters['model'] ?? null, + function (Builder $query, $search) { + $query->where('model', $search); + } + )->when( + $filters['model_id'] ?? null, + function (Builder $query, $search) { + $query->where('model_id', (int)$search); + } + )->when( + $filters['user_id'] ?? null, + function (Builder $query, $search) { + $query->where('user_id', (int)$search); + } + ); + } +} \ No newline at end of file diff --git a/app/Models/Logo.php b/app/Models/Logo.php new file mode 100644 index 000000000..2fdf9d413 --- /dev/null +++ b/app/Models/Logo.php @@ -0,0 +1,128 @@ + + */ + public function customer(): BelongsTo + { + return $this->belongsTo(Customer::class, 'customer_id'); + } + + /** + * Creates a hierarchy directory structure to shard image storage + * + * @return string the/sharded/path/filename + */ + public function shardedPath(): string + { + return $this->stored_name[ 0 ] . '/' . $this->stored_name[ 1 ] . '/' . $this->stored_name; + } + + /** + * Get the full path of the a logo + * + * @return string the/full/path/filename + */ + public function fullPath(): string + { + return public_path() . '/logos/' . $this->shardedPath(); + } + + /** + * String to describe the model being updated / deleted / created + * + * @param Model $model + * + * @return string + */ + public static function logSubject( Model $model ): string + { + return sprintf( + "Logo [id:%d] belonging to %s [id:%d] '%s'", + $model->id, + ucfirst( config( 'ixp_fe.lang.customer.one' ) ), + $model->customer_id, + $model->customer->name, + ); + } +} diff --git a/app/Models/MacAddress.php b/app/Models/MacAddress.php new file mode 100644 index 000000000..60f3badab --- /dev/null +++ b/app/Models/MacAddress.php @@ -0,0 +1,83 @@ + + */ + public function virtualInterface(): BelongsTo + { + return $this->belongsTo(VirtualInterface::class, 'virtualinterfaceid'); + } + + /** + * Format the mac address with colons + * + * @return string + */ + public function macColonsFormatted(): string + { + return strtolower( implode( ':', str_split( $this->mac, 2 ) ) ); + } +} \ No newline at end of file diff --git a/app/Models/NetworkInfo.php b/app/Models/NetworkInfo.php new file mode 100644 index 000000000..147b3afff --- /dev/null +++ b/app/Models/NetworkInfo.php @@ -0,0 +1,169 @@ + + */ + public function vlan(): BelongsTo + { + return $this->belongsTo(Vlan::class, 'vlanid' ); + } + + /** + * Returns an array of the network information indexed by Vlan.id with + * sub-arrays indexed by protocol. + * + * For example (where `x` is the vlan ID): + * + * [x] => array(2) { + * [4] => array(9) { + * ["id"] => string(1) "1" + * ["protocol"] => string(1) "4" + * ["network"] => string(13) "193.242.111.0" + * ["masklen"] => string(2) "25" + * ["rs1address"] => string(13) "193.242.111.8" + * ["rs2address"] => string(13) "193.242.111.9" + * ["dnsfile"] => string(44) "/opt/bind/zones/reverse-vlan-10-ipv4.include" + * ["Vlan"] => array(5) { + * ["id"] => string(1) "2" + * ["name"] => string(15) "Peering VLAN #1" + * ["number"] => string(2) "10" + * ["rcvrfname"] => string(0) "" + * ["notes"] => string(0) "" + * } + * } + * [6] => array(9) { + * ["id"] => string(1) "2" + * ["vlanid"] => string(1) "2" + * ["protocol"] => string(1) "6" + * ["network"] => string(16) "2001:07F8:0018::" + * ["masklen"] => string(2) "64" + * ["rs1address"] => string(14) "2001:7f8:18::8" + * ["rs2address"] => string(14) "2001:7f8:18::9" + * ["dnsfile"] => string(44) "/opt/bind/zones/reverse-vlan-10-ipv6.include" + * ["Vlan"] => array(5) { + * ["id"] => string(1) "2" + * ["name"] => string(15) "Peering VLAN #1" + * ["number"] => string(2) "10" + * ["rcvrfname"] => string(0) "" + * ["notes"] => string(0) "" + * } + * } + * } + * + * @return array[] As described above + * + * @psalm-return array + */ + public static function vlanProtocol(): array + { + $result = []; + foreach( self::with( 'vlan' )->get()->toArray() as $ni ) { + $result[ $ni[ 'vlan' ][ 'id' ] ][ $ni[ 'protocol' ] ] = $ni; + } + + return $result; + } + + /** + * String to describe the model being updated / deleted / created + * + * @param Model $model + * + * @return string + */ + public static function logSubject( Model $model ): string + { + return sprintf( + "Network Info [id:%d] '%s' belonging to Vlan [id:%d] '%s'", + $model->id, + $model->network, + $model->vlanid, + $model->vlan->name, + ); + } +} \ No newline at end of file diff --git a/app/Models/Oui.php b/app/Models/Oui.php new file mode 100644 index 000000000..71e43dcce --- /dev/null +++ b/app/Models/Oui.php @@ -0,0 +1,64 @@ + + */ + public function peer(): HasOne + { + return $this->hasOne(Customer::class, 'id', 'peer_id' ); + } + + /** + * Accessor for total traffic + */ + public function total_traffic(): int + { + return $this->ipv4_total_out + $this->ipv4_total_in + $this->ipv6_total_out + $this->ipv6_total_in; + } + + + + /** + * Get the total traffic a customer exchanges with its peers for the latest day in the database. + * + * Only peers with entries on the the most recent day for this customer will be included. + * + * @param Customer $c + * @return array [ peerid => total_traffic, ... ] + */ + public static function latestTotalTraffic( Customer $c ): array + { + // latest day for which we have results + if( !( $day = self::whereCustId( $c->id )->max('day') ) ) { + return []; + } + + return self::select( DB::raw('peer_id, ipv6_total_out + ipv4_total_out + ipv6_total_in + ipv4_total_in as total_traffic') ) + ->where( 'cust_id', $c->id )->where( 'day', $day) + ->get()->pluck('total_traffic', 'peer_id')->toArray(); + } + + /** + * Get the latest n P2pDailyStats for this customer. + * + * @param Customer $c + */ + public static function latestN( Customer $c, int $n = 5 ): Collection + { + // latest day for which we have results + if( !( $day = self::whereCustId( $c->id )->max('day') ) ) { + return new Collection(); + } + + return self::select( DB::raw( '*, ipv6_total_out + ipv4_total_out + ipv6_total_in + ipv4_total_in as total_traffic' ) ) + ->where( 'cust_id', $c->id )->where( 'day', $day) + ->limit( $n )->orderBy( 'total_traffic', 'desc' )->get(); + } + + +} diff --git a/app/Models/PatchPanel.php b/app/Models/PatchPanel.php new file mode 100644 index 000000000..7e29406f4 --- /dev/null +++ b/app/Models/PatchPanel.php @@ -0,0 +1,431 @@ + $patchPanelPorts + * @property-read int|null $patch_panel_ports_count + * @method static Builder|PatchPanel newModelQuery() + * @method static Builder|PatchPanel newQuery() + * @method static Builder|PatchPanel query() + * @method static Builder|PatchPanel whereActive($value) + * @method static Builder|PatchPanel whereCabinetId($value) + * @method static Builder|PatchPanel whereCableType($value) + * @method static Builder|PatchPanel whereChargeable($value) + * @method static Builder|PatchPanel whereColoPpType($value) + * @method static Builder|PatchPanel whereColoReference($value) + * @method static Builder|PatchPanel whereConnectorType($value) + * @method static Builder|PatchPanel whereCreatedAt($value) + * @method static Builder|PatchPanel whereId($value) + * @method static Builder|PatchPanel whereInstallationDate($value) + * @method static Builder|PatchPanel whereLocationNotes($value) + * @method static Builder|PatchPanel whereMountedAt($value) + * @method static Builder|PatchPanel whereName($value) + * @method static Builder|PatchPanel wherePortPrefix($value) + * @method static Builder|PatchPanel whereUPosition($value) + * @method static Builder|PatchPanel whereUpdatedAt($value) + * @mixin Eloquent + */ + +class PatchPanel extends Model +{ + use Observable; + + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'patch_panel'; + + /** + * The attributes that are mass assignable. + * + * @var array + */ + protected $fillable = [ + 'cabinet_id', + 'name', + 'colo_reference', + 'cable_type', + 'connector_type', + 'installation_date', + 'port_prefix', + 'active', + 'colo_pp_type', + 'chargeable', + 'location_notes', + 'u_position', + 'mounted_at', + ]; + + /** + * CONST Cable types + */ + public const CABLE_TYPE_UTP = 1; + public const CABLE_TYPE_SMF = 2; + public const CABLE_TYPE_MMF = 3; + public const CABLE_TYPE_OTHER = 999; + + /** + * Array Cable types + */ + public static $CABLE_TYPES = [ + self::CABLE_TYPE_UTP => 'UTP', + self::CABLE_TYPE_SMF => 'SMF', + self::CABLE_TYPE_MMF => 'MMF', + self::CABLE_TYPE_OTHER => 'Other', + ]; + + /** + * Array 'Fibre' Cable types + */ + public static $FIBRE_CABLE_TYPES = [ + self::CABLE_TYPE_SMF, + self::CABLE_TYPE_MMF, + ]; + + /** + * CONST Co-lo simplex/duplex specification + */ + public const COLO_PP_TYPE_SIMPLEX = 0; + public const COLO_PP_TYPE_DUPLEX = 1; + + /** + * Array Co-lo simplex/duplex specification + */ + public static $COLO_PP_TYPES = [ + self::COLO_PP_TYPE_SIMPLEX => 'Simplex', + self::COLO_PP_TYPE_DUPLEX => 'Duplex', + ]; + + /** + * CONST Connector types + */ + public const CONNECTOR_TYPE_RJ45 = 1; + public const CONNECTOR_TYPE_SC = 2; + public const CONNECTOR_TYPE_LC = 3; + public const CONNECTOR_TYPE_MU = 4; + public const CONNECTOR_TYPE_OTHER = 999; + + /** + * Array Connector types + */ + public static $CONNECTOR_TYPES = [ + self::CONNECTOR_TYPE_RJ45 => 'RJ45', + self::CONNECTOR_TYPE_SC => 'SC', + self::CONNECTOR_TYPE_LC => 'LC', + self::CONNECTOR_TYPE_MU => 'MU', + self::CONNECTOR_TYPE_OTHER => 'Other', + ]; + + /** + * Counts from patch panel mount position + */ + public const MOUNTED_AT_FRONT = 1; + public const MOUNTED_AT_REAR = 2; + + /** + * Mounted at textual representations + */ + public static $MOUNTED_AT = [ + self::MOUNTED_AT_FRONT => 'Front', + self::MOUNTED_AT_REAR => 'Rear', + ]; + + /** + * Get the patch panel port files for this patch panel port + * + * @psalm-return HasMany + */ + public function patchPanelPorts(): HasMany + { + return $this->hasMany(PatchPanelPort::class, 'patch_panel_id' ); + } + + /** + * Get the cabinet that own the patch panel + * + * @psalm-return BelongsTo + */ + public function cabinet(): BelongsTo + { + return $this->belongsTo(Cabinet::class, 'cabinet_id' ); + } + + /** + * Turn the database integer representation of the cable type into text as + * defined in the self::$CABLE_TYPES array (or 'Unknown') + * + * @return string + */ + public function cableType(): string + { + return self::$CABLE_TYPES[ $this->cable_type ] ?? 'Unknown'; + } + + /** + * Identify id this patch panel is a 'fibre' patch panel + */ + public function isFibre(): bool + { + return in_array( $this->cable_type, self::$FIBRE_CABLE_TYPES ); + } + + /** + * Turn the database integer representation of the connector type into text as + * defined in the self::$CONNECTOR_TYPES array (or 'Unknown') + * + * @return string + */ + public function connectorType(): string + { + return self::$CONNECTOR_TYPES[ $this->connector_type ] ?? 'Unknown'; + } + + /** + * Turn the database integer representation of the states into text as + * defined in the PatchPanelPort::$CHARGEABLES array (or 'Unknown') + * + * @return string + */ + public function chargeable(): string + { + return PatchPanelPort::$CHARGEABLES[ $this->chargeable ] ?? 'Unknown'; + } + + /** + * Specifies whether the co-location provider uses simplex ("port 1") or + * duplex ("fibre 1 / fibre 2") notation in their cross-connect database. + * + * @return string + */ + public function coloPpType(): string + { + return self::$COLO_PP_TYPES[ $this->colo_pp_type ] ?? 'Other'; + } + + /** + * Turn the database integer representation of the states into text as + * defined in the PatchPanelPort::$CHARGEABLES array (or 'Unknown') + * + * @return string + */ + public function mountedAt(): string + { + return self::$MOUNTED_AT[ $this->mounted_at ] ?? 'Unknown'; + } + + /** + * Does this patch panel have any duplex ports? + * + * @return bool + */ + public function hasDuplexPort(): bool + { + $slave = self::select( [ 'ppps.id' ] ) + ->from( 'patch_panel AS pp' ) + ->leftJoin( 'patch_panel_port AS ppp', 'ppp.patch_panel_id', 'pp.id' ) + ->join( 'patch_panel_port AS ppps', 'ppps.id', 'ppp.duplex_master_id' ) + ->where( 'pp.id', $this->id )->get()->keyBy( 'id' )->count() > 0; + + $master = self::select( [ 'ppp.id' ] ) + ->from( 'patch_panel AS pp' ) + ->leftJoin( 'patch_panel_port AS ppp', 'ppp.patch_panel_id', 'pp.id' ) + ->whereNotNull( 'duplex_master_id' ) + ->where( 'pp.id', $this->id )->get()->keyBy( 'id' )->count() > 0; + + return $slave || $master; + } + + /** + * Get number of patch panel ports + * + * @return int + */ + public function availableForUsePortCount(): int + { + $master = self::select( [ 'pppm.*' ] ) + ->from( 'patch_panel AS pp' ) + ->leftJoin( 'patch_panel_port AS ppp', 'ppp.patch_panel_id', 'pp.id' ) + ->leftJoin( 'patch_panel_port AS pppm', 'pppm.id', 'ppp.duplex_master_id' ) + ->whereIn( 'pppm.state', PatchPanelPort::$AVAILABLE_STATES ) + ->where( 'pp.id', $this->id )->get()->keyBy( 'id' )->count(); + + $ppp = self::select( [ 'ppp.*' ] ) + ->from( 'patch_panel AS pp' ) + ->leftJoin( 'patch_panel_port AS ppp', 'ppp.patch_panel_id', 'pp.id' ) + ->whereIn( 'ppp.state', PatchPanelPort::$AVAILABLE_STATES ) + ->whereNull( 'ppp.duplex_master_id' ) + ->where( 'pp.id', $this->id )->get()->keyBy( 'id' )->count(); + + return $master + $ppp; + } + + /** + * get the css class used to display the value => available ports / total ports + * + * @param int $total + * @param int $available + * + * @return string + * + * @author Yann Robin + * + * @psalm-return 'danger'|'success'|'warning' + */ + public function cssClassPortCount( int $total, int $available): string + { + if($total !== 0): + if( ($total - $available) / $total < 0.7 ): + $class = "success"; + elseif( ($total - $available ) / $total < 0.85 ): + $class = "warning"; + else: + $class = "danger"; + endif; + else: + $class = "danger"; + endif; + + return $class; + } + + /** + * get the value available port / total port + * + * + * @param int $availableForUsePortCount + * @param int $portCount + * @param bool $divide if the value need to be divide by 2 (use when some patch panel ports have duplex port) + * + * @return string + */ + public function availableOnTotalPort( int $availableForUsePortCount, int $portCount, bool $divide = false ): string + { + $available = ($divide)? floor( $availableForUsePortCount / 2 ) : $availableForUsePortCount; + $total = ($divide)? floor( $portCount / 2 ) : $portCount; + + return $available . ' / ' . $total; + } + + /** + * Create patch panel ports for a patch panel + * + * @param int $n the number of port needed + * + * @return void + */ + public function createPorts( int $n ): void + { + // what's the current maximum port number? + // (we need this to add new ones to the end) + $max = $this->patchPanelPorts()->max( 'number' ); + + for( $i = 1; $i <= $n; $i++ ) { + PatchPanelPort::create( [ + 'number' => ( $max + $i ), + 'state' => PatchPanelPort::STATE_AVAILABLE, + 'patch_panel_id' => $this->id, + 'chargeable' => $this->chargeable, + 'last_state_change' => now(), + ] ); + } + } + + /** + * A descriptive position of the patch panel in the rack + * + * @return string + */ + public function locationDescription(): string + { + $loc = ''; + + if( $this->u_position ) { + $loc .= 'Located at U' . $this->u_position; + + if( $cf = $this->cabinet->u_counts_from ) { + $loc .= ' (counting from the ' . strtolower( Cabinet::$U_COUNTS_FROM[ $cf ] ) . ')'; + } + + if( $ma = $this->mounted_at ) { + $loc .= ' at the ' . strtolower( self::$MOUNTED_AT[ $ma ] ) . ' of the cabinet'; + } + + $loc .= '.'; + } + + return $loc; + } + + /** + * String to describe the model being updated / deleted / created + * + * @param Model $model + * + * @return string + */ + public static function logSubject( Model $model ): string + { + return sprintf( + "Patch Panel [id:%d] '%s'", + $model->id, + $model->name + ); + } +} diff --git a/app/Models/PatchPanelPort.php b/app/Models/PatchPanelPort.php new file mode 100644 index 000000000..da366c0f0 --- /dev/null +++ b/app/Models/PatchPanelPort.php @@ -0,0 +1,828 @@ + $duplexSlavePorts + * @property-read int|null $duplex_slave_ports_count + * @property-read \IXP\Models\PatchPanel|null $patchPanel + * @property-read \Illuminate\Database\Eloquent\Collection $patchPanelPortFiles + * @property-read int|null $patch_panel_port_files_count + * @property-read \Illuminate\Database\Eloquent\Collection $patchPanelPortFilesPublic + * @property-read int|null $patch_panel_port_files_public_count + * @property-read \Illuminate\Database\Eloquent\Collection $patchPanelPortHistories + * @property-read int|null $patch_panel_port_histories_count + * @property-read \IXP\Models\SwitchPort|null $switchPort + * @method static Builder|PatchPanelPort masterPort() + * @method static Builder|PatchPanelPort newModelQuery() + * @method static Builder|PatchPanelPort newQuery() + * @method static Builder|PatchPanelPort query() + * @method static Builder|PatchPanelPort whereAssignedAt($value) + * @method static Builder|PatchPanelPort whereCeaseRequestedAt($value) + * @method static Builder|PatchPanelPort whereCeasedAt($value) + * @method static Builder|PatchPanelPort whereChargeable($value) + * @method static Builder|PatchPanelPort whereColoBillingRef($value) + * @method static Builder|PatchPanelPort whereColoCircuitRef($value) + * @method static Builder|PatchPanelPort whereConnectedAt($value) + * @method static Builder|PatchPanelPort whereCreatedAt($value) + * @method static Builder|PatchPanelPort whereCustomerId($value) + * @method static Builder|PatchPanelPort whereDescription($value) + * @method static Builder|PatchPanelPort whereDuplexMasterId($value) + * @method static Builder|PatchPanelPort whereId($value) + * @method static Builder|PatchPanelPort whereInternalUse($value) + * @method static Builder|PatchPanelPort whereLastStateChange($value) + * @method static Builder|PatchPanelPort whereLoaCode($value) + * @method static Builder|PatchPanelPort whereNotes($value) + * @method static Builder|PatchPanelPort whereNumber($value) + * @method static Builder|PatchPanelPort whereOwnedBy($value) + * @method static Builder|PatchPanelPort wherePatchPanelId($value) + * @method static Builder|PatchPanelPort wherePrivateNotes($value) + * @method static Builder|PatchPanelPort whereState($value) + * @method static Builder|PatchPanelPort whereSwitchPortId($value) + * @method static Builder|PatchPanelPort whereTicketRef($value) + * @method static Builder|PatchPanelPort whereUpdatedAt($value) + * @mixin Eloquent + */ + +class PatchPanelPort extends Model +{ + use Observable; + + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'patch_panel_port'; + + /** + * The attributes that should be casted to native types. + * + * @var array + */ + protected $casts = [ + 'state' => 'integer', + ]; + + /** + * The attributes that are mass assignable. + * + * @var array + */ + protected $fillable = [ + 'switch_port_id', + 'patch_panel_id', + 'customer_id', + 'state', + 'notes', + 'assigned_at', + 'connected_at', + 'cease_requested_at', + 'ceased_at', + 'last_state_change', + 'internal_use', + 'chargeable', + 'duplex_master_id', + 'number', + 'colo_circuit_ref', + 'ticket_ref', + 'private_notes', + 'owned_by', + 'loa_code', + 'description', + 'colo_billing_ref' + ]; + + /** + * CONST STATES + */ + public const STATE_AVAILABLE = 1; + public const STATE_AWAITING_XCONNECT = 2; + public const STATE_CONNECTED = 3; + public const STATE_AWAITING_CEASE = 4; + public const STATE_CEASED = 5; + public const STATE_BROKEN = 6; + public const STATE_RESERVED = 7; + public const STATE_PREWIRED = 8; + public const STATE_OTHER = 999; + + /** + * CONST OWNED + */ + public const OWNED_CUST = 1; + public const OWNED_IXP = 2; + public const OWNED_SERV_PRO = 3; + public const OWNED_DATA_CENTER = 4; + public const OWNED_OTHER = 5; + + /** + * CONST CHARGEABLE + */ + public const CHARGEABLE_YES = 1; + public const CHARGEABLE_NO = 2; + public const CHARGEABLE_HALF = 3; + public const CHARGEABLE_OTHER = 4; + + /** + * CONST EMAIL + */ + public const EMAIL_CONNECT = 1; + public const EMAIL_CEASE = 2; + public const EMAIL_INFO = 3; + public const EMAIL_LOA = 4; + + /** + * Array STATES + */ + public static $STATES = [ + self::STATE_AVAILABLE => "Available", + self::STATE_AWAITING_XCONNECT => "Awaiting Xconnect", + self::STATE_CONNECTED => "Connected", + self::STATE_AWAITING_CEASE => "Awaiting Cease", + self::STATE_CEASED => "Ceased", + self::STATE_BROKEN => "Broken", + self::STATE_RESERVED => "Reserved", + self::STATE_PREWIRED => "Prewired", + self::STATE_OTHER => "Other" + ]; + + /** + * Array STATES for available + */ + public static $AVAILABLE_FOR_ALLOCATION_STATES = [ + self::STATE_AVAILABLE, + self::STATE_PREWIRED, + ]; + + /** + * Array STATES for allocated + */ + public static $ALLOCATED_STATES = [ + self::STATE_AWAITING_XCONNECT, + self::STATE_CONNECTED, + self::STATE_AWAITING_CEASE, + ]; + + /** + * Array STATES for allocated + */ + public static $ALLOCATED_STATES_TEXT = [ + self::STATE_AWAITING_XCONNECT => "Awaiting Xconnect", + self::STATE_CONNECTED => "Connected", + self::STATE_AWAITING_CEASE => "Awaiting Cease", + ]; + /** + * Array STATES for available + */ + public static $AVAILABLE_STATES = [ + self::STATE_AVAILABLE, + self::STATE_PREWIRED, + self::STATE_AWAITING_CEASE, + self::STATE_CEASED, + ]; + + /** + * Array STATES for cross-connects where there may be a circuit installed by the colo + */ + public static $COLO_ASSIGNED_STATES = [ + self::STATE_CONNECTED, + self::STATE_AWAITING_CEASE, + ]; + + /** + * Array $CHARGEABLES + */ + public static $OWNED_BY = [ + self::OWNED_CUST => "Customer", + self::OWNED_IXP => "IXP", + self::OWNED_SERV_PRO => "Service Provider", + self::OWNED_DATA_CENTER => "Data Center", + self::OWNED_OTHER => "Other", + ]; + + /** + * Array $CHARGEABLES + */ + public static $CHARGEABLES = [ + self::CHARGEABLE_YES => "Yes", + self::CHARGEABLE_NO => "No", + self::CHARGEABLE_HALF => "Half", + self::CHARGEABLE_OTHER => "Other" + ]; + + /** + * @var array Email ids to classes + */ + public static $EMAIL_CLASSES = [ + self::EMAIL_CEASE => CeaseMail::class, + self::EMAIL_CONNECT => ConnectMail::class, + self::EMAIL_INFO => InfoMail::class, + self::EMAIL_LOA => LoaMail::class, + ]; + + /** + * Get the Patch Panel that owns this patch panel port + * + * @psalm-return BelongsTo + */ + public function patchPanel(): BelongsTo + { + return $this->belongsTo( PatchPanel::class , 'patch_panel_id' ); + } + + /** + * Get the switch port that owns this patch panel port + * + * @psalm-return BelongsTo + */ + public function switchPort(): BelongsTo + { + return $this->belongsTo( SwitchPort::class , 'switch_port_id' ); + } + + /** + * Get the customer that owns this patch panel port + * + * @psalm-return BelongsTo + */ + public function customer(): BelongsTo + { + return $this->belongsTo( Customer::class , 'customer_id' ); + } + + /** + * Get the duplex master port that owns this patch panel port + * + * @psalm-return BelongsTo + */ + public function duplexMasterPort(): BelongsTo + { + return $this->belongsTo( __CLASS__, 'duplex_master_id' ); + } + + /** + * Get the patch panel port files for this patch panel port + * + * @psalm-return HasMany + */ + public function patchPanelPortFiles(): HasMany + { + return $this->hasMany(PatchPanelPortFile::class, 'patch_panel_port_id' ); + } + + /** + * Get the patch panel port histories for this patch panel port + * + * @psalm-return HasMany + */ + public function patchPanelPortHistories(): HasMany + { + return $this->hasMany(PatchPanelPortHistory::class, 'patch_panel_port_id' ); + } + + /** + * Get the public patch panel port files for this patch panel port + * + * @psalm-suppress all + */ + public function patchPanelPortFilesPublic(): HasMany + { + return $this->hasMany(PatchPanelPortFile::class, 'patch_panel_port_id' ) + ->where( 'is_private', 0 ); + } + + /** + * Get the duplex slaves ports for this patch panel port + * + * @psalm-return HasMany + */ + public function duplexSlavePorts(): HasMany + { + return $this->hasMany( __CLASS__ , 'duplex_master_id' ); + } + + /** + * A public facing reference for this. Essentially the ID. + * + * @return string + */ + public function circuitReference(): string + { + return sprintf( "PPP-%05d", $this->id ); + } + + /** + * Is this port part of a duplex port group? + * + * @return bool + */ + public function isDuplexPort(): bool + { + return $this->duplex_master_id !== null || $this->duplexSlavePorts()->count() > 0; + } + + /** + * Scope a query to match master ports only + * + * @param Builder $query + * + * @return Builder + */ + public function scopeMasterPort( Builder $query ): Builder + { + return $query->where('duplex_master_id', null ); + } + + + /** + * Get name + * + * @return string + */ + public function name(): string + { + $name = $this->patchPanel->port_prefix . $this->number; + + if( $duplex = $this->duplexSlavePorts->first() ) { + $name .= '/' . $duplex->name() . ' (' . ( $this->patchPanel->isFibre() ? 'Fibre, duplex port: ' : '' ); + $name .= ( $this->number % 2 ? ( floor( $this->number / 2 ) ) + 1 : $this->number / 2 ) . ')'; + } + + return $name; + } + + + /** + * Turn the database integer representation of the states into text as + * defined in the self::$STATES array (or 'Unknown') + * + * @return string + */ + public function states(): string + { + return self::$STATES[ $this->state ] ?? 'Unknown'; + } + + /** + * Get chargeable + * + * @return int + */ + public function isChargeable(): int + { + return isset( self::$CHARGEABLES[ $this->chargeable ] ) ? $this->chargeable : self::CHARGEABLE_NO; + + } + + /** + * Turn the database integer representation of the states into text as + * defined in the self::$CHARGEABLES array (or 'Unknown') + * + * @return string + */ + public function chargeable(): string + { + return self::$CHARGEABLES[ $this->chargeable ] ?? 'Unknown'; + } + + /** + * Is this port os allocated? + * + * It is if its state is one of: awaiting xconnect, connected, awaiting cease. + * + * @return bool + */ + public function allocated(): bool + { + return in_array( $this->state, self::$ALLOCATED_STATES ); + } + + /** + * Is this port available for use? + * + * It is if its state is one of: available, ceased, awaiting cease, prewired. + * + * @return bool + */ + public function availableForUse(): bool + { + return in_array( $this->state, self::$AVAILABLE_STATES ); + } + + /** + * Turn the database integer representation of the states into text as + * defined in the self::$STATES array (or 'Unknown') + * @return string + */ + public function ownedBy(): string + { + return self::$OWNED_BY[ $this->owned_by ] ?? 'Unknown'; + } + + /** + * Is the state STATE_AVAILABLE? + * + * @return bool + */ + public function stateAvailable(): bool + { + return $this->state === self::STATE_AVAILABLE; + } + + /** + * Is the state STATE_AWAITING_XCONNECT? + * + * @return bool + */ + public function stateAwaitingXConnect(): bool + { + return $this->state === self::STATE_AWAITING_XCONNECT; + } + + /** + * Is the state STATE_CONNECTED? + * + * @return bool + */ + public function stateConnected(): bool + { + return $this->state === self::STATE_CONNECTED; + } + + /** + * Is the state STATE_AWAITING_CEASE? + * + * @return bool + */ + public function stateAwaitingCease(): bool + { + return $this->state === self::STATE_AWAITING_CEASE; + } + + /** + * Is the state STATE_CEASED? + * + * @return bool + */ + public function stateCeased(): bool + { + return $this->state === self::STATE_CEASED; + } + + /** + * Is the state STATE_RESERVED? + * + * @return bool + */ + public function stateReserved(): bool + { + return $this->state === self::STATE_RESERVED; + } + + /** + * Is the state STATE_RESERVED? + * + * @return bool + */ + public function statePrewired(): bool + { + return $this->state === self::STATE_PREWIRED; + } + + /** + * Get css class state + * + * @param int $state + * @param bool $superUser + * + * @return string + */ + public static function stateCssClass( int $state, bool $superUser = false ): string + { + if( $superUser ) { + if( in_array( $state, self::$AVAILABLE_STATES ) || $state === self::STATE_PREWIRED ){ + $class = 'success'; + } elseif( $state === self::STATE_AWAITING_XCONNECT ){ + $class = 'warning'; + } elseif( $state === self::STATE_CONNECTED ){ + $class = 'danger'; + } else { + $class = 'info'; + } + + return $class; + } + + if( $state === self::STATE_CONNECTED ){ + $class = 'success'; + } elseif( $state === self::STATE_AWAITING_CEASE ) { + $class = 'warning'; + } elseif( $state === self::STATE_AWAITING_XCONNECT ) { + $class = 'danger'; + } else { + $class = 'info'; + } + + return $class; + } + + /** + * Reset the port and set status to available (including slave ports) + * + * @return void + */ + public function reset(): void + { + foreach( $this->duplexSlavePorts as $pppsp ) { + $pppsp->reset(); + $pppsp->update( [ 'duplex_master_id' => null ] ); + } + + // Attributes that are not reset + $excludedValues = [ 'id', 'patch_panel_id', 'number', 'owned_by', 'colo_billing_ref', 'updated_at', 'created_at' ]; + + // Get for each attributes their default values + $attributesToReset = DB::query()->selectRaw( 'COLUMN_NAME, COLUMN_DEFAULT, DATA_TYPE' ) + ->from( 'INFORMATION_SCHEMA.COLUMNS' ) + ->where( 'TABLE_NAME', $this->table ) + ->where( 'TABLE_SCHEMA', config( 'database.connections.mysql.database' ) ) + ->whereNotIn( 'COLUMN_NAME', $excludedValues )->get(); + + + foreach( $attributesToReset as $attr ) { + // if the types are varchar or longtext set '' instead of null + $this->{$attr->COLUMN_NAME} = in_array( $attr->DATA_TYPE, [ 'varchar', 'longtext' ] ) ? '' : $attr->COLUMN_DEFAULT; + } + + $this->state = self::STATE_AVAILABLE; + $this->last_state_change = now(); + $this->save(); + } + + /** + * Archive a patch panel port (and its slave ports) + * + * NB: does not reset the original port. + * + * @return PatchPanelPortHistory + * + * @throws + */ + public function archive(): PatchPanelPortHistory + { + $historyPort = PatchPanelPortHistory::createFromPort( $this ); + + foreach( $this->duplexSlavePorts as $slave ) { + /** @var PatchPanelPort $slave */ + PatchPanelPortHistory::create( + array_merge( + $historyPort->replicate( [ 'id', 'duplex_master_id', 'number', 'patch_panel_port_id' ] )->toArray(), + [ + 'number' => $slave->number, + 'duplex_master_id' => $historyPort->id, + 'patch_panel_port_id' => $slave->id, + ] + ) + ); + } + + foreach( $this->patchPanelPortFilesPublic as $file ) { + PatchPanelPortHistoryFile::createFromFile( $file, $historyPort ); + $file->delete(); + } + + return $historyPort; + } + + /** + * Move details / contents of a PPP to another PPP. + * + * Moves the information and files from a patch panel port to an other one + * (and also move duplex slave if there is one). This function also: + * + * * Creates history of the old patch panel port + * * Resets the old patch panel port (and duplex slave) + * + * @param PatchPanelPort $dest The destination port + * @param PatchPanelPort|null $slave If the source port is a duplex port, we need a new slave also. + * + * @return boolean + * + * @throws + */ + public function move( PatchPanelPort $dest, ?PatchPanelPort $slave = null ): bool + { + // preflight checks + if( $this->duplexSlavePorts()->count() && ( $slave === null || !$slave->availableForUse() ) ) { + throw new GeneralException( 'Source is duplex but no slave / free slave provided' ); + } + + if( !$dest->availableForUse() ) { + throw new GeneralException( 'Destination port is not available for use' ); + } + + foreach( $this->patchPanelPortFiles as $file ){ + $file->update( [ 'patch_panel_port_id' => $dest->id ] ); + } + + if( !( $history = $this->archive() ) ) { + return false; + } + + // wipe source switch port as it is a unique constraint in the db + $spid = $this->switch_port_id; + $this->update( [ 'switch_port_id' => null ] ); + + // Update the new port with the data of the old port + $dest->update( $this->replicate( + [ + 'id', + 'switch_port_id', + 'patch_panel_id', + 'duplex_master_id', + 'number', + 'private_notes', + 'colo_billing_ref' + ] + )->toArray() + ); + + $dest->update( [ + 'switch_port_id' => $spid, + 'private_notes' => "### " . now()->format('Y-m-d')." - IXP Manager\n\nMoved from " + . $this->patchPanel->name . "/" . $this->name() + . " by ". ( Auth::check() ? Auth::getUser()->username : "unknown/unauth" ) + . " on " . now()->format('Y-m-d') . ".\n\n" + . $this->private_notes, + ]); + + if( $slave ){ + $slave->update( [ 'duplex_master_id' => $dest->id ] ); + } + + // Reset the old port + $this->reset(); + + $history->update( [ + 'private_notes' => "### " . now()->format('Y-m-d' ) . " - IXP Manager\n\nMoved to " + . $dest->patchPanel->name . "/" . $dest->name() + . " by ". ( Auth::check() ? Auth::getUser()->username : "unknown/unauth" ) + . " on " . now()->format( 'Y-m-d' ) . ".\n\n" + . ( $dest->patchPanelPortFiles()->count() ? "See new port for files.\n\n" : '' ) + . $history->private_notes + ] ); + + return true; + } + + /** + * Remove a patch panel port and everything linked to it ( duplex port, files, histories, etc...) + * + * Also: + * + * * optionally deletes the linked slave port. + * * deletes all the history. + * * deletes in the database and on the disk all the files/filesHistory uploaded for this port. + * + * @throws + */ + public function remove(): void + { + // Delete slave port first + if( $this->duplexSlavePorts()->count() ){ + foreach( $this->duplexSlavePorts as $slave ){ + /** @var $slave PatchPanelPort */ + $slave->remove(); + } + } + + // Delete port histories and files + foreach( $this->patchPanelPortHistories as $history ) { + /** @var $history PatchPanelPortHistory */ + + $user = Auth::check() ? Auth::getUser()->username : 'unkown/unauth'; + foreach( PatchPanelPortHistory::whereDuplexMasterId( $history->id )->get() as $duplex ){ + $duplex->update([ + 'duplex_master_id' => null, + 'private_notes' => "### " . now()->format('Y-m-d') . " - IXP Manager \n\nHad a master port that was deleted by {$user} on " . now()->format('Y-m-d') . "\n\n" + . $duplex->private_notes + ]); + } + + foreach( $history->patchPanelPortHistoryFiles as $historyFile ) { + $path = 'files/' . $historyFile->path(); + + $historyFile->update( [ 'patch_panel_port_history_id' => null ] ); + $historyFile->delete(); + + if( Storage::exists( $path ) ){ + Storage::delete( $path ); + } + } + $history->update( [ 'patch_panel_port_id' => null ] ); + $history->delete(); + } + + // Delete port files + foreach( $this->patchPanelPortFiles as $file ){ + $path = 'files/' . $file->getPath(); + + $file->update( [ 'patch_panel_port_id' => null ] ); + $file->delete(); + if( Storage::exists( $path ) ){ + Storage::delete( $path ); + } + } + + $this->delete(); + } + + /** + * String to describe the model being updated / deleted / created + * + * @param Model $model + * + * @return string + */ + public static function logSubject( Model $model ): string + { + return sprintf( + "Patch Panel Port [id:%d] '%s' belonging to Patch Panel [id:%d] '%s'", + $model->id, + $model->name(), + $model->patch_panel_id, + $model->patchPanel->name, + ); + } +} diff --git a/app/Models/PatchPanelPortFile.php b/app/Models/PatchPanelPortFile.php new file mode 100644 index 000000000..83f81b8ae --- /dev/null +++ b/app/Models/PatchPanelPortFile.php @@ -0,0 +1,203 @@ + + */ + public function patchPanelPort(): BelongsTo + { + return $this->belongsTo( PatchPanelPort::class , 'patch_panel_port_id' ); + } + + /** + * Get name + * + * @return string + */ + public function nameTruncated(): string + { + return strlen( $this->name ) > 80 ? substr( $this->name,0,80 ) . '...' . explode('.', $this->name )[1] : $this->name; + } + + /** + * Return the formatted size + * + * @return string + */ + public function sizeFormated(): string + { + $bytes = $this->size; + if( $bytes >= 1073741824 ) { + $bytes = number_format($bytes / 1073741824, 2 ) . ' GB'; + } elseif ($bytes >= 1048576) { + $bytes = number_format($bytes / 1048576, 2) . ' MB'; + } elseif ($bytes >= 1024) { + $bytes = number_format($bytes / 1024, 2) . ' kB'; + } elseif ($bytes > 1) { + $bytes .= ' bytes'; + } elseif ($bytes === 1) { + $bytes .= ' byte'; + } else { + $bytes = '0 bytes'; + } + + return $bytes; + } + + /** + * Get type as an icon from awesome font + * + * @return string + */ + public function typeAsIcon(): string + { + switch ( $this->type ) { + case 'image/jpeg': + $icon = 'fa-file-image-o'; + break; + case 'image/png': + $icon = 'fa-file-image-o'; + break; + case 'image/bmp': + $icon = 'fa-file-image-o'; + break; + case 'application/pdf': + $icon = 'fa-file-pdf-o'; + break; + case 'application/zip': + $icon = 'fa-file-archive-o'; + break; + case 'text/plain': + $icon = 'fa-file-text'; + break; + default: + $icon = 'fa-file'; + break; + } + return $icon; + } + + /** + * Get the path for the panel port file + * + * @return string + */ + public function path(): string + { + return self::UPLOAD_PATH . '/' . $this->storage_location[ 0 ] . '/' + . $this->storage_location[ 1 ] . '/' . $this->storage_location; + } + + /** + * String to describe the model being updated / deleted / created + * + * @param Model $model + * + * @return string + */ + public static function logSubject( Model $model ): string + { + return sprintf( + "Patch Panel Port File [id:%d] '%s' belonging to Patch Panel Port [id:%d] '%s'", + $model->id, + $model->name, + $model->patch_panel_port_id, + $model->patchPanelPort->name(), + ); + } +} \ No newline at end of file diff --git a/app/Models/PatchPanelPortHistory.php b/app/Models/PatchPanelPortHistory.php new file mode 100644 index 000000000..c8fe9a2b1 --- /dev/null +++ b/app/Models/PatchPanelPortHistory.php @@ -0,0 +1,224 @@ + $patchPanelPortHistoryFiles + * @property-read int|null $patch_panel_port_history_files_count + * @method static Builder|PatchPanelPortHistory masterPort() + * @method static Builder|PatchPanelPortHistory newModelQuery() + * @method static Builder|PatchPanelPortHistory newQuery() + * @method static Builder|PatchPanelPortHistory query() + * @method static Builder|PatchPanelPortHistory whereAssignedAt($value) + * @method static Builder|PatchPanelPortHistory whereCeaseRequestedAt($value) + * @method static Builder|PatchPanelPortHistory whereCeasedAt($value) + * @method static Builder|PatchPanelPortHistory whereChargeable($value) + * @method static Builder|PatchPanelPortHistory whereColoBillingRef($value) + * @method static Builder|PatchPanelPortHistory whereColoCircuitRef($value) + * @method static Builder|PatchPanelPortHistory whereConnectedAt($value) + * @method static Builder|PatchPanelPortHistory whereCreatedAt($value) + * @method static Builder|PatchPanelPortHistory whereCustId($value) + * @method static Builder|PatchPanelPortHistory whereCustomer($value) + * @method static Builder|PatchPanelPortHistory whereDescription($value) + * @method static Builder|PatchPanelPortHistory whereDuplexMasterId($value) + * @method static Builder|PatchPanelPortHistory whereId($value) + * @method static Builder|PatchPanelPortHistory whereInternalUse($value) + * @method static Builder|PatchPanelPortHistory whereNotes($value) + * @method static Builder|PatchPanelPortHistory whereNumber($value) + * @method static Builder|PatchPanelPortHistory whereOwnedBy($value) + * @method static Builder|PatchPanelPortHistory wherePatchPanelPortId($value) + * @method static Builder|PatchPanelPortHistory wherePrivateNotes($value) + * @method static Builder|PatchPanelPortHistory whereState($value) + * @method static Builder|PatchPanelPortHistory whereSwitchport($value) + * @method static Builder|PatchPanelPortHistory whereTicketRef($value) + * @method static Builder|PatchPanelPortHistory whereUpdatedAt($value) + * @mixin Eloquent + */ + +class PatchPanelPortHistory extends Model +{ + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'patch_panel_port_history'; + + /** + * The attributes that are mass assignable. + * + * @var array + */ + protected $fillable = [ + 'switchport', + 'patch_panel_port_id', + 'customer', + 'cust_id', + 'state', + 'notes', + 'assigned_at', + 'connected_at', + 'cease_requested_at', + 'ceased_at', + 'internal_use', + 'chargeable', + 'duplex_master_id', + 'number', + 'colo_circuit_ref', + 'ticket_ref', + 'private_notes', + 'owned_by', + 'description', + 'colo_billing_ref', + ]; + + /** + * Get the Patch Panel Port that owns this patch panel port history + * + * @psalm-return BelongsTo + */ + public function patchPanelPort(): BelongsTo + { + return $this->belongsTo( PatchPanelPort::class , 'patch_panel_port_id' ); + } + + /** + * Get the patch panel port history files for this patch panel port history + * + * @psalm-return HasMany + */ + public function patchPanelPortHistoryFiles(): HasMany + { + return $this->hasMany(PatchPanelPortHistoryFile::class, 'patch_panel_port_history_id' ); + } + + /** + * Turn the database integer representation of the states into text as + * defined in the self::$CHARGEABLES array (or 'Unknown') + * + * @return string + */ + public function chargeable(): string + { + return self::$CHARGEABLES[ $this->chargeable ] ?? 'Unknown'; + } + + /** + * Turn the database integer representation of the states into text as + * defined in the self::$STATES array (or 'Unknown') + * + * @return string + */ + public function ownedBy(): string + { + return self::$OWNED_BY[ $this->owned_by ] ?? 'Unknown'; + } + + /** + * Populate the history model with details from a patch panel port. + * + * @param PatchPanelPort $ppp + * + * @return PatchPanelPortHistory + * + * @throws + */ + public static function createFromPort( PatchPanelPort $ppp ): PatchPanelPortHistory + { + return self::create( [ + 'switchport' => $ppp->switchPort ? $ppp->switchPort->switcher->name . '::' . $ppp->switchPort->name : '', + 'patch_panel_port_id' => $ppp->id, + 'customer' => $ppp->customer->name ?? '', + 'cust_id' => $ppp->customer->id ?? '', + 'state' => $ppp->state, + 'notes' => $ppp->notes, + 'assigned_at' => $ppp->assigned_at, + 'connected_at' => $ppp->connected_at, + 'cease_requested_at' => $ppp->cease_requested_at, + 'ceased_at' => $ppp->ceased_at ?? now(), + 'internal_use' => $ppp->internal_use, + 'chargeable' => $ppp->chargeable, + 'number' => $ppp->number, + 'colo_circuit_ref' => $ppp->colo_circuit_ref, + 'ticket_ref' => $ppp->ticket_ref, + 'private_notes' => $ppp->private_notes, + 'owned_by' => $ppp->owned_by, + 'description' => $ppp->description, + 'colo_billing_ref' => $ppp->colo_billing_ref, + ] ); + } + + /** + * Scope a query to match master ports only + * + * @param Builder $query + * + * @return Builder + */ + public function scopeMasterPort( Builder $query ): Builder + { + return $query->where('duplex_master_id', null ); + } +} diff --git a/app/Models/PatchPanelPortHistoryFile.php b/app/Models/PatchPanelPortHistoryFile.php new file mode 100644 index 000000000..668b19f5d --- /dev/null +++ b/app/Models/PatchPanelPortHistoryFile.php @@ -0,0 +1,200 @@ + + */ + public function patchPanelPortHistory(): BelongsTo + { + return $this->belongsTo( PatchPanelPortHistory::class , 'patch_panel_port_history_id' ); + } + + /** + * Populate this file history model with details from a patch panel port file. + * + * @param PatchPanelPortFile $file + * @param PatchPanelPortHistory $portHistory + * + * @return PatchPanelPortHistoryFile + */ + public static function createFromFile( PatchPanelPortFile $file, PatchPanelPortHistory $portHistory): PatchPanelPortHistoryFile + { + return self::create([ + 'patch_panel_port_history_id' => $portHistory->id, + 'name' => $file->name, + 'type' => $file->type, + 'uploaded_at' => $file->uploaded_at, + 'uploaded_by' => $file->uploaded_by, + 'size' => $file->size, + 'is_private' => $file->is_private, + 'storage_location' => $file->storage_location, + ]); + } + + /** + * Get name + * + * @return string + */ + public function nameTruncated(): string + { + return strlen( $this->name ) > 80 ? substr( $this->name,0,80 ) . '...' . explode('.', $this->name )[1] : $this->name; + } + + /** + * Return the formatted size + * + * @return string + */ + public function sizeFormated(): string + { + $bytes = $this->size; + if( $bytes >= 1073741824 ) { + $bytes = number_format($bytes / 1073741824, 2 ) . ' GB'; + } elseif ($bytes >= 1048576) { + $bytes = number_format($bytes / 1048576, 2) . ' MB'; + } elseif ($bytes >= 1024) { + $bytes = number_format($bytes / 1024, 2) . ' kB'; + } elseif ($bytes > 1) { + $bytes .= ' bytes'; + } elseif ($bytes === 1) { + $bytes .= ' byte'; + } else { + $bytes = '0 bytes'; + } + + return $bytes; + } + + /** + * Get type as an icon from awesome font + * + * @return string + */ + public function typeAsIcon(): string + { + switch ( $this->type ) { + case 'image/jpeg': + $icon = 'fa-file-image-o'; + break; + case 'image/png': + $icon = 'fa-file-image-o'; + break; + case 'image/bmp': + $icon = 'fa-file-image-o'; + break; + case 'application/pdf': + $icon = 'fa-file-pdf-o'; + break; + case 'application/zip': + $icon = 'fa-file-archive-o'; + break; + case 'text/plain': + $icon = 'fa-file-text'; + break; + default: + $icon = 'fa-file'; + break; + } + return $icon; + } + + /** + * get the patch for the panel port history file + * + * @return string + */ + public function path(): string + { + return PatchPanelPortFile::UPLOAD_PATH . '/' . $this->storage_location[ 0 ] . '/' + . $this->storage_location[ 1 ] . '/' . $this->storage_location; + } +} diff --git a/app/Models/PeeringManager.php b/app/Models/PeeringManager.php new file mode 100644 index 000000000..810eff34b --- /dev/null +++ b/app/Models/PeeringManager.php @@ -0,0 +1,106 @@ + + */ + public function customer(): BelongsTo + { + return $this->belongsTo(Customer::class, 'custid'); + } + + /** + * Get the peer that owns the peering manager + * + * @psalm-return BelongsTo + */ + public function peer(): BelongsTo + { + return $this->belongsTo(Customer::class, 'peerid'); + } +} diff --git a/app/Models/PhysicalInterface.php b/app/Models/PhysicalInterface.php new file mode 100644 index 000000000..805cd7c34 --- /dev/null +++ b/app/Models/PhysicalInterface.php @@ -0,0 +1,448 @@ + $trafficDailiesPhysInt + * @property-read int|null $traffic_dailies_phys_int_count + * @property-read \IXP\Models\VirtualInterface|null $virtualInterface + * @method static Builder|PhysicalInterface connected() + * @method static Builder|PhysicalInterface graphable() + * @method static Builder|PhysicalInterface newModelQuery() + * @method static Builder|PhysicalInterface newQuery() + * @method static Builder|PhysicalInterface query() + * @method static Builder|PhysicalInterface whereAutoneg($value) + * @method static Builder|PhysicalInterface whereCreatedAt($value) + * @method static Builder|PhysicalInterface whereDuplex($value) + * @method static Builder|PhysicalInterface whereFanoutPhysicalInterfaceId($value) + * @method static Builder|PhysicalInterface whereId($value) + * @method static Builder|PhysicalInterface whereNotes($value) + * @method static Builder|PhysicalInterface whereRateLimit($value) + * @method static Builder|PhysicalInterface whereSpeed($value) + * @method static Builder|PhysicalInterface whereStatus($value) + * @method static Builder|PhysicalInterface whereSwitchportid($value) + * @method static Builder|PhysicalInterface whereUpdatedAt($value) + * @method static Builder|PhysicalInterface whereVirtualinterfaceid($value) + * @mixin Eloquent + */ +class PhysicalInterface extends Model +{ + use Observable; + + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'physicalinterface'; + + /** + * The attributes that are mass assignable. + * + * @var array + */ + protected $fillable = [ + 'switchportid', + 'virtualinterfaceid', + 'status', + 'speed', + 'duplex', + 'rate_limit', + 'autoneg', + 'notes', + ]; + + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + 'autoneg' => 'boolean', + ]; + + /** + * Mutator for rate limit + * + * @param ?int $value + * @return void + */ + public function setRateLimitAttribute($value) + { + $this->attributes['rate_limit'] = $value ?: null; + } + + public const STATUS_CONNECTED = 1; + public const STATUS_DISABLED = 2; + public const STATUS_NOTCONNECTED = 3; + public const STATUS_XCONNECT = 4; + public const STATUS_QUARANTINE = 5; + + public static $STATES = [ + self::STATUS_CONNECTED => 'Connected', + self::STATUS_DISABLED => 'Disabled', + self::STATUS_NOTCONNECTED => 'Not Connected', + self::STATUS_XCONNECT => 'Awaiting X-Connect', + self::STATUS_QUARANTINE => 'Quarantine' + ]; + + public static $APISTATES = [ + self::STATUS_CONNECTED => 'connected', + self::STATUS_DISABLED => 'disabled', + self::STATUS_NOTCONNECTED => 'notconnected', + self::STATUS_XCONNECT => 'awaitingxconnect', + self::STATUS_QUARANTINE => 'quarantine' + ]; + + public static $SPEED = [ + 10 => '10 Mbps', + 100 => '100 Mbps', + 1000 => '1 Gbps', + 10000 => '10 Gbps', + 25000 => '25 Gbps', + 40000 => '40 Gbps', + 100000 => '100 Gbps', + 400000 => '400 Gbps' + ]; + + public static $DUPLEX = [ + 'full' => 'full', + 'half' => 'half' + ]; + + /** + * Get the virtual interface that owns the physical interface. + * + * @psalm-return BelongsTo + */ + public function virtualInterface(): BelongsTo + { + return $this->belongsTo(VirtualInterface::class, 'virtualinterfaceid' ); + } + + /** + * Get the switch port that owns the physical interface. + * + * @psalm-return BelongsTo + */ + public function switchPort(): BelongsTo + { + return $this->belongsTo(SwitchPort::class, 'switchportid'); + } + + /** + * Get the fanout physical interface associated with the physical interface. + * + * @psalm-return BelongsTo + */ + public function fanoutPhysicalInterface(): BelongsTo + { + return $this->belongsTo( __CLASS__, 'fanout_physical_interface_id' ); + } + + /** + * Get the core interface associated with the physical interface. + * + * @psalm-return HasOne + */ + public function coreInterface(): HasOne + { + return $this->hasOne(CoreInterface::class, 'physical_interface_id' ); + } + + /** + * Get the peering physical interface associated with the physical interface. + * + * @psalm-return HasOne + */ + public function peeringPhysicalInterface(): HasOne + { + return $this->hasOne( __CLASS__, 'fanout_physical_interface_id' ); + } + + /** + * Get the trafficDailiesPhysInt associated with the physical interface. + * + * @psalm-return HasMany + */ + public function trafficDailiesPhysInt(): HasMany + { + return $this->hasMany(TrafficDailyPhysInt::class, 'physicalinterface_id' ); + } + + /** + * Determine if the port's status is set to QUARANTINE / CONNECTED + * + * @return bool True if the port's status is QUARANTINE / CONNECTED + */ + public function isConnectedOrQuarantine(): bool + { + return $this->statusConnected() || $this->statusQuarantine(); + } + + /** + * Determine if the port's status is set to CONNECTED + * + * @return bool True if the port's status is CONNECTED + */ + public function statusConnected(): bool + { + return $this->status === self::STATUS_CONNECTED; + } + + /** + * Determine if the port's status is set to DISABLED + * + * @return bool True if the port's status is DISABLED + */ + public function statusDisabled(): bool + { + return $this->status === self::STATUS_DISABLED; + } + + /** + * Determine if the port's status is set to NOTCONNECTED + * + * @return bool True if the port's status is NOTCONNECTED + */ + public function statusNotConnected(): bool + { + return $this->status === self::STATUS_NOTCONNECTED; + } + + /** + * Determine if the port's status is set to XCONNECT + * + * @return bool True if the port's status is XCONNECT + */ + public function statusAwaitingXConnect(): bool + { + return $this->status === self::STATUS_XCONNECT; + } + + /** + * Determine if the port's status is set to QUARANTINE + * + * @return bool True if the port's status is QUARANTINE + */ + public function statusQuarantine(): bool + { + return $this->status === self::STATUS_QUARANTINE; + } + + /** + * Try to find the most accurate version of the port's speed. + * + * I.e. try the actual SNMP-discovered port speed first, otherwise use the configured speed + * + * @return int|null + */ + public function detectedSpeed(): ?int + { + // try the actual SNMP-discovered port speed first, otherwise use the configured speed: + return $this->switchPort->ifHighSpeed > 0 ? $this->switchPort->ifHighSpeed : $this->speed; + } + + /** + * Turn the database integer representation of the speed into text as + * defined in the self::$SPEEDS array (or 'Unknown') + * + * @return string + */ + public function speed(): string + { + return self::$SPEED[ $this->speed ] ?? 'Unknown'; + } + + /** + * Is this port rate limited? + */ + public function isRateLimited(): bool + { + return $this->rate_limit !== null; + } + + /** + * Get the configured speed + */ + public function configuredSpeed(): int|null + { + return $this->rate_limit ?: $this->speed; + } + + + + /** + * Turn the database integer representation of the states into text as + * defined in the self::$STATES array (or 'Unknown') + * + * @return string + */ + public function status(): string + { + return self::$STATES[ $this->status ] ?? 'Unknown'; + } + + /** + * Turn the database integer representation of the states into text suitable + * for API output as defined in the self::$STATES array (or 'unknown') + * @return string + */ + public function apiStatus(): string + { + return self::$APISTATES[ $this->status ] ?? 'unknown'; + } + + /** + * Is this port graphable? + * + * @return bool + */ + public function isGraphable(): bool + { + return $this->isConnectedOrQuarantine(); + } + + /** + * Scope to get connected virtual interface + * + * @param Builder $query + * + * @return Builder + */ + public function scopeGraphable( Builder $query ): Builder + { + return $query->where( 'status' , self::STATUS_CONNECTED ) + ->orWhere( 'status' , self::STATUS_QUARANTINE ); + } + + /** + * Gets the related peering / fanout port for the current fanout / peering port + * + * For reseller functionality, we have the option of having fanout ports connected to + * peering ports. In this case, this function will return the related peering or + * fanout port as appropriate. + * + * @return false|self The related peering / fanout port (or false for none / n/a) + */ + public function relatedInterface(): self|false + { + if( $sp = $this->switchPort ) { + if( $sp->typeFanout() && $this->peeringPhysicalInterface ){ + return $this->peeringPhysicalInterface; + } + + if( $sp->typePeering() && $this->fanoutPhysicalInterface ) { + return $this->fanoutPhysicalInterface; + } + return false; + } + return false; + } + + /** + * Get the other physical interface associated to the core link of the current Physical Interface + * + * @return false|null|self + */ + public function otherPICoreLink(): false|null|self + { + if( $ci = $this->coreInterface ){ + if( $this->id === $ci->coreLink()->coreInterfaceSideA->physical_interface_id ){ + return $ci->coreLink()->coreInterfaceSideB->physicalInterface; + } + return $ci->coreLink()->coreInterfaceSideA->physicalInterface; + } + return false; + } + + /** + * Scope to get connected virtual interface + * + * @param Builder $query + * + * @return Builder + */ + public function scopeConnected( Builder $query ): Builder + { + return $query->where( 'status' , self::STATUS_CONNECTED ); + } + + /** + * Get the core bundle if the physical interface is associated to a core bundle + */ + public function coreBundle(): CoreBundle|false|null + { + if( $ci = $this->coreInterface ){ + return $ci->coreLink()->coreBundle; + } + return false; + } + + /** + * String to describe the model being updated / deleted / created + * + * @param Model $model + * + * @return string + */ + public static function logSubject( Model $model ): string + { + return sprintf( + "Physical Interface [id:%d] belonging to Virtual Interface [id:%d]", + $model->id, + $model->virtualinterfaceid, + ); + } +} \ No newline at end of file diff --git a/app/Models/RouteServerFilter.php b/app/Models/RouteServerFilter.php new file mode 100644 index 000000000..6f5779111 --- /dev/null +++ b/app/Models/RouteServerFilter.php @@ -0,0 +1,200 @@ + 'Advertise As Is', + self::NO_ADVERTISE => 'Do Not Advertise', + self::PREPEND_ONCE => 'Prepend My ASN x1', + self::PREPEND_TWICE => 'Prepend My ASN x2', + self::PREPEND_THRICE => 'Prepend My ASN x3', + ]; + + public static $RECEIVE_ACTION_TEXT = [ + self::AS_IS => 'Receive As Is', + self::NO_ADVERTISE => "Do Not Receive (Drop)", + self::PREPEND_ONCE => "Prepend Peer's ASN x1", + self::PREPEND_TWICE => "Prepend Peer's ASN x2", + self::PREPEND_THRICE => "Prepend Peer's ASN x3", + ]; + + /** + * Get the customer that owns the route server filter. + * + * @psalm-return BelongsTo + */ + public function customer(): BelongsTo + { + return $this->belongsTo(Customer::class, 'customer_id'); + } + + /** + * Get the peer that owns the route server filter. + * + * @psalm-return BelongsTo + */ + public function peer(): BelongsTo + { + return $this->belongsTo(Customer::class, 'peer_id'); + } + + /** + * Get the vlan that owns the route server filter. + * + * @psalm-return BelongsTo + */ + public function vlan(): BelongsTo + { + return $this->belongsTo(Vlan::class, 'vlan_id'); + } + + /** + * Turn the database integer representation of the action advertise into text as + * defined in the self::$ADVERTISE_ACTION_TEXT array (or 'Unknown') + * + * @return string + */ + public function actionAdvertise(): string + { + return self::$ADVERTISE_ACTION_TEXT[ $this->action_advertise ] ?? 'Unknown'; + } + + /** + * Turn the database integer representation of the action receive into text as + * defined in the self::$RECEIVE_ACTION_TEXT array (or 'Unknown') + * + * @return string + */ + public function actionReceive(): string + { + return self::$RECEIVE_ACTION_TEXT[ $this->action_receive ] ?? 'Unknown'; + } + + /** + * Turn the database integer representation of the protocol into text as + * defined in the RouteServerFilter::$PROTOCOLS array (or 'Unknown') + * + * @return string + */ + public function protocol(): string + { + return Router::$PROTOCOLS[ $this->protocol ] ?? 'Both'; + } + + + + /** + * String to describe the model being updated / deleted / created + * + * @param Model $model + * + * @return string + */ + public static function logSubject( Model $model ): string + { + return sprintf( + "Route Server Filter [id:%d] belonging to %s [id:%d] '%s' and Peer [id:%d] '%s'", + $model->id, + ucfirst( config( 'ixp_fe.lang.customer.one' ) ), + $model->customer_id, + $model->customer->name, + $model->peer_id, + $model->peer?->name, + ); + } +} \ No newline at end of file diff --git a/app/Models/RouteServerFilterProd.php b/app/Models/RouteServerFilterProd.php new file mode 100644 index 000000000..2a9c35e8b --- /dev/null +++ b/app/Models/RouteServerFilterProd.php @@ -0,0 +1,109 @@ + cosider making changes to RouteServerFilter first! + /// + //////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////// + + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'route_server_filters_prod'; + + /** + * String to describe the model being updated / deleted / created + * + * @param Model $model + * + * @return string + */ + #[\Override] + public static function logSubject( Model $model ): string + { + return sprintf( + "Route Server Filter (Production) [id:%d] belonging to %s [id:%d] '%s' and Peer [id:%d] '%s'", + $model->id, + ucfirst( config( 'ixp_fe.lang.customer.one' ) ), + $model->customer_id, + $model->customer->name ?? Customer::find( $model->customer_id )?->name, + $model->peer_id, + $model->peer?->name, + ); + } +} diff --git a/app/Models/Router.php b/app/Models/Router.php new file mode 100644 index 000000000..fd337a204 --- /dev/null +++ b/app/Models/Router.php @@ -0,0 +1,658 @@ + 'integer', + 'quarantine' => 'boolean', + 'bgp_lc' => 'boolean', + 'skip_md5' => 'boolean', + 'rpki' => 'boolean', + 'last_updated' => 'datetime', + 'last_update_started' => 'datetime', + ]; + + + /** + * CONST PROTOCOL + */ + public const PROTOCOL_IPV4 = '4'; + public const PROTOCOL_IPV6 = '6'; + + /** + * @var array Router Protocols + */ + public static $PROTOCOLS = [ + self::PROTOCOL_IPV4 => 'IPv4', + self::PROTOCOL_IPV6 => 'IPv6' + ]; + + /** + * CONST TYPES + */ + public const TYPE_ROUTE_SERVER = 1; + public const TYPE_ROUTE_COLLECTOR = 2; + public const TYPE_AS112 = 3; + public const TYPE_OTHER = 99; + + /** + * @var array Router types textual description + */ + public static $TYPES = [ + self::TYPE_ROUTE_SERVER => 'Route Server', + self::TYPE_ROUTE_COLLECTOR => 'Route Collector', + self::TYPE_AS112 => 'AS112', + self::TYPE_OTHER => 'Other' + ]; + + /** + * @var array Router types short description + */ + public static $TYPES_SHORT = [ + self::TYPE_ROUTE_SERVER => 'RS', + self::TYPE_ROUTE_COLLECTOR => 'RC', + self::TYPE_AS112 => 'AS112', + self::TYPE_OTHER => 'Other' + ]; + + /** + * CONST SOFTWARES + */ + public const SOFTWARE_BIRD = 1; + public const SOFTWARE_BIRD2 = 6; + public const SOFTWARE_BIRD3 = 7; + public const SOFTWARE_QUAGGA = 2; + public const SOFTWARE_FRROUTING = 3; + public const SOFTWARE_OPENBGPD = 4; + public const SOFTWARE_CISCO = 5; + public const SOFTWARE_OTHER = 99; + + /** + * @var array Router softwares textual description + */ + public static $SOFTWARES = [ + self::SOFTWARE_BIRD => 'Bird v1', + self::SOFTWARE_BIRD2 => 'Bird v2', + self::SOFTWARE_BIRD3 => 'Bird v3', + self::SOFTWARE_QUAGGA => 'Quagga', + self::SOFTWARE_FRROUTING => 'FRRouting', + self::SOFTWARE_OPENBGPD => 'OpenBGPd', + self::SOFTWARE_CISCO => 'Cisco', + self::SOFTWARE_OTHER => 'Other' + ]; + + /** + * CONST SOFTWARES + */ + public const API_TYPE_NONE = 0; + public const API_TYPE_BIRDSEYE = 1; + public const API_TYPE_OTHER = 99; + + /** + * @var array Router API types + */ + public static $API_TYPES = [ + self::API_TYPE_NONE => 'None', + self::API_TYPE_BIRDSEYE => 'Birdseye', + self::API_TYPE_OTHER => 'Other' + ]; + + /** + * Get the vlan that own the router + * + * @psalm-return BelongsTo + */ + public function vlan(): BelongsTo + { + return $this->belongsTo(Vlan::class, 'vlan_id' ); + } + + /** + * Get the router's configuration / isolation pair + * + * @psalm-return BelongsTo + */ + public function pair(): BelongsTo + { + return $this->belongsTo(Router::class, 'pair_id' ); + } + + /** + * Get the API type + * + * Alias to allow Entities\Router and Models\Router to work interchangeably + */ + public function apiType(): int + { + return $this->api_type; + } + + /** + * Scope a query to only include servers with an API + * + * @param Builder $query + * + * @return Builder + */ + public function scopeHasApi( Builder $query ): Builder + { + return $query->where('api_type', '>', 0); + } + + /** + * Scope a query to only include route collectors + * + * @param Builder $query + * + * @return Builder + */ + public function scopeRouteCollector( Builder $query ): Builder + { + return $query->where('type', self::TYPE_ROUTE_COLLECTOR); + } + + /** + * Scope a query to only include route servers + * + * @param Builder $query + * + * @return Builder + */ + public function scopeRouteServer( Builder $query ): Builder + { + return $query->where('type', self::TYPE_ROUTE_SERVER); + } + + /** + * Scope a query to match IPv4 routers only + * + * @param Builder $query + * + * @return Builder + */ + public function scopeIpv4( Builder $query ): Builder + { + return $query->where('protocol', self::PROTOCOL_IPV4); + } + + /** + * Scope a query to match IPv6 routers only + * + * @param Builder $query + * + * @return Builder + */ + public function scopeIpv6( Builder $query ): Builder + { + return $query->where('protocol', self::PROTOCOL_IPV6); + } + + /** + * Scope a query to match IPvX routers only + * + * @param Builder $query + * + * @return Builder + */ + public function scopeIpProtocol( Builder $query, int $protocol ): Builder + { + return $query->where('protocol', $protocol === 4 ? self::PROTOCOL_IPV4 : self::PROTOCOL_IPV6 ); + } + + + /** + * Scope a query to match BGP Large Communities enabled + * + * @param Builder $query + * + * @return Builder + */ + public function scopeLargeCommunities( Builder $query ): Builder + { + return $query->where('bgp_lc', true); + } + + /** + * Scope a query to match against quarantine routers + * + * @param Builder $query + * + * @return Builder + */ + public function scopeNotQuarantine( Builder $query ): Builder + { + return $query->where('quarantine', false); + } + + /** + * Scope a query to match RPKI enabled + * + * @param Builder $query + * + * @return Builder + */ + public function scopeRpki( Builder $query ): Builder + { + return $query->where('rpki', true); + } + + + /** + * Turn the database integer representation of the software into text as + * defined in the self::$SOFTWARES array (or 'Unknown') + * + * @return string + */ + public function software(): string + { + return self::$SOFTWARES[ $this->software ] ?? 'Unknown'; + } + + /** + * Turn the database integer representation of the api type into text as + * defined in the self::$SOFTWARES array (or 'Unknown') + * @return string + */ + public function resolveApiType(): string + { + return self::$API_TYPES[ $this->api_type ] ?? 'Unknown'; + } + + /** + * Turn the database integer representation of the protocol into text as + * defined in the self::$PROTOCOLS array (or 'Unknown') + * + * @return string + */ + public function protocol(): string + { + return self::$PROTOCOLS[ $this->protocol ] ?? 'Unknown'; + } + + /** + * Turn the database integer representation of the type into text as + * defined in the self::$TYPES_SHORT array (or 'Unknown') + * @return string + */ + public function typeShortName(): string + { + return self::$TYPES_SHORT[ $this->type ] ?? 'Unknown'; + } + + /** + * Turn the database integer representation of the type into text as + * defined in the self::$TYPES array (or 'Unknown') + * @return string + */ + public function type(): string + { + return self::$TYPES[ $this->type ] ?? 'Unknown'; + } + + /** + * Check if the given type matches the routers's type. + * + * @param int $type The type to check against. + * @return bool True if the given type matches, false otherwise. + */ + public function isType( int $type ): bool + { + return $this->type === $type; + } + + + /** + * Turn the database integer representation of the lg access into text as + * defined in the User::$PRIVILEGES_ALL array (or 'Unknown') + * + * @return string + */ + public function lgAccess(): string + { + return User::$PRIVILEGES_ALL[ $this->lg_access ] ?? 'Unknown'; + } + + /** + * Does the router have an API? + * + * In other words, is 'api' and 'api_type' set? + * + * @return bool + */ + public function api(): bool + { + return $this->api && $this->api_type; + } + + /** + * This function controls access to a router for a looking glass + * + * @param int $privs User's privileges (see \Models\User) + * + * @return bool + */ + public function authorise( int $privs ): bool + { + return $privs >= $this->lg_access; + } + + /** + * This function check is the last updated time is greater than the given number of seconds + * + * @param int $threshold + * + * @return bool + */ + public function lastUpdatedGreaterThanSeconds( int $threshold ): ?bool + { + // TESTS: covered + + if( !$this->last_updated ) { + // if null, then, as far as we know, it has never been updated.... + return null; + } + + return $this->last_updated->diffInSeconds() > $threshold; + } + + + /** + * Is the router being updated? + * + * If null, then we can't determine this as the appropriate columns + * have not been set. + * + * @return bool|null + */ + public function isUpdating(): ?bool + { + // TESTS: covered + + if( !$this->last_updated && !$this->last_update_started ) { + return null; // don't know + } + + if( !$this->last_updated && $this->last_update_started ) { + return true; // looks like it, first time though. + } + + if( $this->last_updated >= $this->last_update_started ) { + return false; // nope, updates done + } + + return true; + } + + /** + * This function check is the last updated time is greater than the given number of seconds + * + * If null, then we can't determine this as the appropriate columns + * have not been set or it is not mid-update. + * + * Best to use this with isUpdating() first. + * + * @param int $threshold + * + * @return bool|null + */ + public function isUpdateTakingLongerThanSeconds( int $threshold ): ?bool + { + // TESTS: covered + + if( $this->isUpdating() !== true ) { + return null; + } + + return $this->last_update_started->diffInSeconds() > $threshold; + } + + + /** + * This function checks to see if this router can be updated. + * + * This uses database read/write locks to ensure it is transaction / concurrency safe. + * + * It can be updated if: + * + * 1. Fresh router - last update start and finish times are null. + * 2. It doesn't have a pair and it itself is not mid-update. + * 3. It does have a pair and neither it nor itself are mid-update. + * + * The lock parameter should be set to true to take an update lock (i.e. to + * set last_update_started). + * + * @param bool $lock As well as querying the update status, we should lock it also. + * @return bool + */ + public function canUpdate( bool $lock = false ): bool + { + // if we're paused, we don't need to check anything else + if( $this->pause_updates ) { + return false; + } + + $canUpdate = false; + + try { + // get a total lock on the table so only this thread can read and write + DB::raw( 'LOCK TABLES routers WRITE' ); + + // Got to assume yes here as we're asking the question. + if( !$this->last_updated && !$this->last_update_started ) { + $canUpdate = true; + } + + // else if I don't have a pair and I'm not updating or I have a pair but neither of us are updating + else if( ( !$this->pair || !$this->pair->isUpdating() ) && !$this->isUpdating() ) { + $canUpdate = true; + } + + } finally { + // finally is always executed even when we return() out of the try. + + if( $canUpdate && $lock ) { + + // need to avoid a same-second race condition + while( $this->last_updated?->format('Y-m-d H:i:s') == now()->format('Y-m-d H:i:s') ) { + sleep(1); + } + + $this->last_update_started = now(); + $this->save(); + } + + DB::raw('UNLOCK TABLES'); + + // do not return out of finally or any exception will be silently handled. + } + + return $canUpdate; + } + + + + + /** + * String to describe the model being updated / deleted / created + * + * @param Model $model + * + * @return string + */ + public static function logSubject( Model $model ): string + { + return sprintf( + "Router [id:%d] '%s' belonging to Vlan [id:%d] '%s'", + $model->id, + $model->handle, + $model->vlan_id, + $model->vlan->name, + ); + } + + + /** + * We don't want to log router config updates via the API + */ + public function observerSkipUpdateLogging( array $changes ): bool { + + $interesting = array_filter( array_keys($changes), function( $v ) { + return !in_array( $v, [ 'last_updated', 'updated_at', 'last_update_started' ] ); + } ); + + return count( $interesting ) === 0; + } + + +} \ No newline at end of file diff --git a/app/Models/RsPrefix.php b/app/Models/RsPrefix.php new file mode 100644 index 000000000..5e87a72f7 --- /dev/null +++ b/app/Models/RsPrefix.php @@ -0,0 +1,85 @@ + 'summaryRoutesAdvertisedAndAccepted', + 'adv_nacc' => 'summaryRoutesAdvertisedAndNotAccepted', + 'nadv_acc' => 'summaryRoutesNotAdvertisedButAcceptable' + ]; + + /** + * Map prefix acceptance types to lookup functions + * @var array Map prefix acceptance types to lookup functions + */ + public static $ROUTES_TYPES_FNS = [ + 'adv_acc' => 'routesAdvertisedAndAccepted', + 'adv_nacc' => 'routesAdvertisedAndNotAccepted', + 'nadv_acc' => 'routesNotAdvertisedButAcceptable' + ]; + + /** + * Get the the customer that own the rs prefix + * + * @psalm-return BelongsTo + */ + public function customer(): BelongsTo + { + return $this->belongsTo(Customer::class, 'custid' ); + } +} \ No newline at end of file diff --git a/app/Models/SflowReceiver.php b/app/Models/SflowReceiver.php new file mode 100644 index 000000000..5bb3a5747 --- /dev/null +++ b/app/Models/SflowReceiver.php @@ -0,0 +1,102 @@ + + */ + public function virtualInterface(): BelongsTo + { + return $this->belongsTo(VirtualInterface::class, 'virtual_interface_id'); + } + + /** + * String to describe the model being updated / deleted / created + * + * @param Model $model + * + * @return string + */ + public static function logSubject( Model $model ): string + { + return sprintf( + "Sflow Receiver [id:%d] '%s' belonging to Vlan [id:%d]", + $model->id, + $model->dst_ip, + $model->virtual_interface_id, + ); + } +} diff --git a/app/Models/SwitchPort.php b/app/Models/SwitchPort.php new file mode 100644 index 000000000..c3f92cf82 --- /dev/null +++ b/app/Models/SwitchPort.php @@ -0,0 +1,544 @@ + 'Unset / Unknown', + self::TYPE_PEERING => 'Peering', + self::TYPE_MONITOR => 'Monitor', + self::TYPE_CORE => 'Core', + self::TYPE_OTHER => 'Other', + self::TYPE_MANAGEMENT => 'Management', + self::TYPE_FANOUT => 'Fanout', + self::TYPE_RESELLER => 'Reseller' + ]; + + // This array is for matching data from OSS_SNMP to the switchport database table. + // See snmpUpdate() below + public static $SNMP_MAP = [ + 'descriptions' => 'name', + 'names' => 'ifName', + 'aliases' => 'ifAlias', + 'highSpeeds' => 'ifHighSpeed', + 'mtus' => 'ifMtu', + 'physAddresses' => 'ifPhysAddress', + 'adminStates' => 'ifAdminStatus', + 'operationStates' => 'ifOperStatus', + 'lastChanges' => 'ifLastChange' + ]; + + /** + * Mappings for OSS_SNMP functions to SwitchPort members + */ + public static $OSS_SNMP_MAU_MAP = [ + 'types' => [ 'fn' => 'mauType', 'xlate' => false ], + 'states' => [ 'fn' => 'mauState', 'xlate' => true ], + 'mediaAvailable' => [ 'fn' => 'mauAvailability', 'xlate' => true ], + 'jackTypes' => [ 'fn' => 'mauJacktype', 'xlate' => true ], + 'autonegSupported' => [ 'fn' => 'mauAutoNegSupported' ], + 'autonegAdminState' => [ 'fn' => 'mauAutoNegAdminState' ] + ]; + + + /** + * Get the switcher that own the switch port + * + * @psalm-return BelongsTo + */ + public function switcher(): BelongsTo + { + return $this->belongsTo(Switcher::class, 'switchid' ); + } + + /** + * Get the patch panel ports for this switch port + * + * @psalm-return HasOne + */ + public function physicalInterface(): HasOne + { + return $this->hasOne(PhysicalInterface::class, 'switchportid' ); + } + + /** + * Get the patch panel ports for this switch port + * + * @psalm-return HasOne + */ + public function patchPanelPort(): HasOne + { + return $this->hasOne(PatchPanelPort::class, 'switch_port_id' ); + } + + /** + * Turn the database integer representation of the type into text as + * defined in the self::$TYPES array (or 'Unknown') + * + * @return string + */ + public function type(): string + { + return self::$TYPES[ $this->type ]; + } + + /** + * Is this an unset port? + * + * @return boolean + */ + public function typeUnset():bool + { + return $this->type === self::TYPE_UNSET; + } + + /** + * Is this a peering port? + * + * @return boolean + */ + public function typePeering(): bool + { + return $this->type === self::TYPE_PEERING; + } + + /** + * Is this a reseller port? + * + * @return boolean + */ + public function typeReseller(): bool + { + return $this->type === self::TYPE_RESELLER; + } + + /** + * Is this a core port? + * + * @return boolean + */ + public function typeCore(): bool + { + return $this->type === self::TYPE_CORE; + } + + /** + * Is this a fanout port? + * + * @return boolean + */ + public function typeFanout(): bool + { + return $this->type === self::TYPE_FANOUT; + } + + /** + * Get the appropriate OID for in octets + * + * @return string + * + * @psalm-return '.1.3.6.1.2.1.31.1.1.1.6' + */ + public function oidInOctets(): string + { + return Iface::OID_IF_HC_IN_OCTETS; + } + + /** + * Get the appropriate OID for out octets + * + * @return string + * + * @psalm-return '.1.3.6.1.2.1.31.1.1.1.10' + */ + public function oidOutOctets(): string + { + return Iface::OID_IF_HC_OUT_OCTETS; + } + + /** + * Get the appropriate OID for in unicast packets + * + * @return string + * + * @psalm-return '.1.3.6.1.2.1.31.1.1.1.7' + */ + public function oidInUnicastPackets(): string + { + return Iface::OID_IF_HC_IN_UNICAST_PACKETS; + } + + /** + * Get the appropriate OID for out unicast packets + * + * @return string + * + * @psalm-return '.1.3.6.1.2.1.31.1.1.1.11' + */ + public function oidOutUnicastPackets(): string + { + return Iface::OID_IF_HC_OUT_UNICAST_PACKETS; + } + + /** + * Get the appropriate OID for in errors + * + * @return string + * + * @psalm-return '.1.3.6.1.2.1.2.2.1.14' + */ + public function oidInErrors(): string + { + return Iface::OID_IF_IN_ERRORS; + } + + /** + * Get the appropriate OID for out errors + * + * @return string + * + * @psalm-return '.1.3.6.1.2.1.2.2.1.20' + */ + public function oidOutErrors(): string + { + return Iface::OID_IF_OUT_ERRORS; + } + + /** + * Get the appropriate OID for in discards + * + * @return string + * + * @psalm-return '.1.3.6.1.2.1.2.2.1.13' + */ + public function oidInDiscards(): string + { + return Iface::OID_IF_IN_DISCARDS; + } + + /** + * Get the appropriate OID for out discards + * + * @return string + * + * @psalm-return '.1.3.6.1.2.1.2.2.1.19'|'.1.3.6.1.4.1.1916.1.4.14.1.1' + */ + public function oidOutDiscards(): string + { + switch( $this->switcher->os ) { + case 'ExtremeXOS': + return Port::OID_PORT_CONG_DROP_PKTS; + + default: + return Iface::OID_IF_OUT_DISCARDS; + + } + } + + /** + * Get the appropriate OID for in broadcasts + * + * @return string + * + * @psalm-return '.1.3.6.1.2.1.31.1.1.1.9' + */ + public function oidInBroadcasts(): string + { + return Iface::OID_IF_HC_IN_BROADCAST; + } + + /** + * Get the appropriate OID for out broadcasts + * + * @return string + * + * @psalm-return '.1.3.6.1.2.1.31.1.1.1.13' + */ + public function oidOutBroadcasts(): string + { + return Iface::OID_IF_HC_OUT_BROADCAST; + } + + public function ifnameToSNMPIdentifier(): string|null + { + # escape special characters in ifName as per + # http://oss.oetiker.ch/mrtg/doc/mrtg-reference.en.html - "Interface by Name" section + + $ifname = preg_replace( '/:/', '\\:', $this->ifName ); + $ifname = preg_replace( '/&/', '\\&', $ifname ); + $ifname = preg_replace( '/@/', '\\@', $ifname ); + $ifname = preg_replace( '/\ /', '\\\ ', $ifname ); + + return $ifname; + } + + /** + * Update switch port details from a SNMP poll of the device. + * + * Pass an instance of OSS_Logger if you want logging enabled. + * + * @link https://github.com/opensolutions/OSS_SNMP + * + * @param SNMP $host An instance of the SNMP host object + * @param bool $logger An instance of the logger or false + * + * @throws + */ + public function snmpUpdate( SNMP $host, bool $logger = false, bool $nosave = false ): static + { + foreach( self::$SNMP_MAP as $snmp => $attribute ) { + + switch( $snmp ) { + case 'lastChanges': + $n = $host->useIface()->$snmp( true )[ $this->ifIndex ]; + + // need to allow for small changes due to rounding errors + if( $logger !== false && $this->$attribute !== $n && abs( $this->$attribute - $n ) > 60 ) { + Log::info( "[{$this->switcher->name}]:{$this->name} [Index: {$this->ifIndex}] Updating {$attribute} from [{$this->$attribute}] to [{$n}]" ); + } + break; + default: + $n = null; + if( !empty($host->useIface()->$snmp()[ $this->ifIndex ]) ) { + $n = $host->useIface()->$snmp()[ $this->ifIndex ]; + } + + if( $logger !== false && $this->$attribute !== $n ) { + Log::info( "[{$this->switcher->name}]:{$this->name} [Index: {$this->ifIndex}] Updating {$attribute} from [{$this->$attribute}] to [{$n}]" ); + } + break; + } + + $this->$attribute = $n; + } + + if( $this->switcher->mauSupported ) { + foreach( self::$OSS_SNMP_MAU_MAP as $snmp => $attribute ) { + $fn = $attribute['fn']; + + try { + if( isset( $attribute['xlate'] ) ) { + $n = $host->useMAU()->$snmp( $attribute['xlate'] ); + $n = isset( $n[ $this->ifIndex ] ) ? $n[ $this->ifIndex ] : null; + } else { + $n = $host->useMAU()->$snmp(); + $n = isset( $n[ $this->ifIndex ] ) ? $n[ $this->ifIndex ] : null; + } + } catch( \Exception $e ) { + // looks like the switch supports MAU but not all of the MIBs + if( $logger !== false ) { + Log::debug( "[{$this->switcher->name}]:{$this->name} [Index: {$this->ifIndex}] MAU MIB for {$fn} not supported" ); + } + $n = null; + } + + if( $snmp === 'types' ) { + if( isset( MauMib::$TYPES[ $n ] ) ) { + $n = MauMib::$TYPES[ $n ]; + } else if( $n === null || $n === '.0.0' ) { + $n = '(empty)'; + } else { + $n = '(unknown type - oid: ' . $n . ')'; + } + } + + if ( $fn == 'mauAutoNegSupported' || $fn == 'mauAutoNegAdminState' ) { + // both these OSS_SNMP functions return a bool so really only testing for null. + $n = $n ? 1 : 0; + } + + if( $this->$fn !== $n && $logger !== false ) { + Log::info( "[{$this->switcher->name}]:{$this->name} [Index: {$this->ifIndex}] Updating {$attribute['fn']} from [{$this->$fn}] to [{$n}]" ); + } + + $this->$fn = $n; + } + } + + try { + // not all switches support this + // FIXME is there a vendor agnostic way of doing this? + + // are we a LAG port? + $isAggregatePorts = $host->useLAG()->isAggregatePorts(); + + if( isset( $isAggregatePorts[ $this->ifIndex ] ) && $isAggregatePorts[ $this->ifIndex ] ){ + $this->lagIfIndex = $host->useLAG()->portAttachedIds()[ $this->ifIndex ]; + } else { + $this->lagIfIndex = null; + } + + } catch( \Exception $e ){} + + $this->lastSnmpPoll = now(); + + if( !$nosave ) { + $this->save(); + } + + return $this; + } +} \ No newline at end of file diff --git a/app/Models/Switcher.php b/app/Models/Switcher.php new file mode 100644 index 000000000..6159fbb52 --- /dev/null +++ b/app/Models/Switcher.php @@ -0,0 +1,618 @@ + $consoleServerConnections + * @property-read int|null $console_server_connections_count + * @property-read \IXP\Models\Infrastructure|null $infrastructureModel + * @property-read \Illuminate\Database\Eloquent\Collection $switchPorts + * @property-read int|null $switch_ports_count + * @property-read \IXP\Models\Vendor|null $vendor + * @method static Builder|Switcher newModelQuery() + * @method static Builder|Switcher newQuery() + * @method static Builder|Switcher query() + * @method static Builder|Switcher whereActive($value) + * @method static Builder|Switcher whereAsn($value) + * @method static Builder|Switcher whereCabinetid($value) + * @method static Builder|Switcher whereCreatedAt($value) + * @method static Builder|Switcher whereHostname($value) + * @method static Builder|Switcher whereId($value) + * @method static Builder|Switcher whereInfrastructure($value) + * @method static Builder|Switcher whereIpv4addr($value) + * @method static Builder|Switcher whereIpv6addr($value) + * @method static Builder|Switcher whereLastPolled($value) + * @method static Builder|Switcher whereLoopbackIp($value) + * @method static Builder|Switcher whereLoopbackName($value) + * @method static Builder|Switcher whereMauSupported($value) + * @method static Builder|Switcher whereMgmtMacAddress($value) + * @method static Builder|Switcher whereModel($value) + * @method static Builder|Switcher whereName($value) + * @method static Builder|Switcher whereNotes($value) + * @method static Builder|Switcher whereOs($value) + * @method static Builder|Switcher whereOsDate($value) + * @method static Builder|Switcher whereOsVersion($value) + * @method static Builder|Switcher wherePoll($value) + * @method static Builder|Switcher whereSerialNumber($value) + * @method static Builder|Switcher whereSnmpEngineBoots($value) + * @method static Builder|Switcher whereSnmpEngineTime($value) + * @method static Builder|Switcher whereSnmpSystemUptime($value) + * @method static Builder|Switcher whereSnmppasswd($value) + * @method static Builder|Switcher whereUpdatedAt($value) + * @method static Builder|Switcher whereVendorid($value) + * @mixin \Eloquent + */ +class Switcher extends Model +{ + use Observable; + + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'switch'; + + /** + * The attributes that are mass assignable. + * + * @var array + */ + protected $fillable = [ + 'cabinetid', + 'vendorid', + 'name', + 'ipv4addr', + 'ipv6addr', + 'snmppasswd', + 'infrastructure', + 'model', + 'active', + 'poll', + 'notes', + 'hostname', + 'os', + 'osDate', + 'osVersion', + 'serialNumber', + 'lastPolled', + 'mauSupported', + 'asn', + 'loopback_ip', + 'loopback_name', + 'mgmt_mac_address', + 'snmp_engine_time', + 'snmp_system_uptime', + 'snmp_engine_boots', + ]; + + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + 'active' => 'boolean', + ]; + + /** + * Constants for the list mode dropdown in the swtiches list + */ + public const VIEW_MODE_DEFAULT = 'view_mode_default'; + public const VIEW_MODE_OS = 'view_mode_os'; + public const VIEW_MODE_L3 = 'view_mode_l3'; + + /** + * @var array Textual representations of the list mode + */ + public static $VIEW_MODES = [ + self::VIEW_MODE_DEFAULT => 'Default', + self::VIEW_MODE_OS => 'OS View', + self::VIEW_MODE_L3 => 'L3 View', + ]; + + /** + * Elements for SNMP polling via the OSS_SNMP library + * + * These are used to build function names + * + * @see snmpPoll() below + * @var array Elements for SNMP polling via the OSS_SNMP library + */ + public static $SNMP_SWITCH_ELEMENTS = [ + 'Model' => [ 'fn' => 'model' ], + 'Os' => [ 'fn' => 'os' ], + 'OsDate' => [ 'fn' => 'osDate' ], + 'OsVersion' => [ 'fn' => 'osVersion' ], + 'SerialNumber' => [ 'fn' => 'serialNumber' ], + ]; + + /** + * Get the infrastructure that own the switcher + * + * @psalm-return BelongsTo + */ + public function infrastructureModel(): BelongsTo + { + return $this->belongsTo(Infrastructure::class, 'infrastructure' ); + } + + /** + * Get the cabinet that own the switcher + * + * @psalm-return BelongsTo + */ + public function cabinet(): BelongsTo + { + return $this->belongsTo(Cabinet::class, 'cabinetid' ); + } + + /** + * Get the vendor that own the switcher + * + * @psalm-return BelongsTo + */ + public function vendor(): BelongsTo + { + return $this->belongsTo(Vendor::class, 'vendorid' ); + } + + /** + * Get the switch ports for the switcher + * + * @psalm-return HasMany + */ + public function switchPorts(): HasMany + { + return $this->hasMany(SwitchPort::class, 'switchid'); + } + + /** + * Get the console server connections for the switcher + * + * @psalm-return HasMany + */ + public function consoleServerConnections(): HasMany + { + return $this->hasMany(ConsoleServerConnection::class, 'switchid'); + } + + /** + * Gets a listing of patch panel ports for a switch + * + * @return Collection + */ + public function getPatchPanelPorts(): Collection + { + return self::select( 'ppp.*' ) + ->leftJoin( 'switchport AS sp', 'sp.switchid', 'switch.id' ) + ->leftJoin( 'patch_panel_port AS ppp', 'ppp.switch_port_id', 'sp.id' ) + ->where( 'switch.id', $this->id ) + ->where( 'ppp.id', '!=', NULL ) + ->groupBy( 'ppp.id' ) + ->get(); + } + + /** + * Gets a listing of physical interfaces for a switch + * + * @return Collection + */ + public function getPhysicalInterfaces(): Collection + { + return self::select( 'pi.*' ) + ->leftJoin( 'switchport AS sp', 'sp.switchid', 'switch.id' ) + ->leftJoin( 'physicalinterface AS pi', 'pi.switchportid', 'sp.id' ) + ->where( 'switch.id', $this->id ) + ->where( 'pi.id', '!=', NULL ) + ->get(); + } + + /** + * Update switch's details using SNMP polling + * + * @see self::$SNMP_SWITCH_ELEMENTS + * + * @param SNMP $host An instance of \OSS_SNMP\SNMP for this switch + * @param bool $logger An instance of the logger or false + * + * @return static For fluent interfaces + */ + public function snmpPoll( $host, bool $logger = false, bool $nosave = false ): static + { + // utility to format dates + $formatDate = function( $d ): string { + return $d instanceof DateTime ? $d->format( 'Y-m-d H:i:s' ) : 'Unknown'; + }; + + foreach( self::$SNMP_SWITCH_ELEMENTS as $index => $p ) { + $objfn = $p[ 'fn' ]; + $fn = "get{$index}"; + $n = $host->getPlatform()->$fn(); + + if( $logger ) { + switch( $index ) { + case 'OsDate': + if( $this->$objfn != $formatDate( $n ) ) + Log::info( " [{$this->name}] Platform: Updating {$index} from " . $this->$objfn . " to " . $formatDate( $n ) ); + else + Log::info( " [{$this->name}] Platform: Found {$index}: " . $formatDate( $n ) ); + break; + + default: + if( $logger && $this->$objfn != $n ){ + Log::info( " [{$this->name}] Platform: Updating {$index} from {$this->$objfn} to {$n}" ); + } else{ + Log::info( " [{$this->name}] Platform: Found {$index}: {$n}" ); + } + break; + } + } + + $this->$objfn = $n; + } + + // does this switch support the IANA MAU MIB? + try { + $host->useMAU()->types(); + $this->mauSupported = 1; + } catch( \OSS_SNMP\Exception $e ) { + $this->mauSupported = 0; + } + + // uptime data + try { + $this->snmp_system_uptime = $host->useSystem()->uptime(); + } catch( \OSS_SNMP\Exception $e ) { + // + } + + try { + $this->snmp_engine_time = $host->useSNMP_Engine()->time(); + $this->snmp_engine_boots = $host->useSNMP_Engine()->boots(); + } catch( \OSS_SNMP\Exception $e ) { + // + } + + $this->lastPolled = now(); + + if( !$nosave ) { + $this->save(); + } + + return $this; + } + + /** + * Update a switches ports using SNMP polling + * + * There is an optional ``$results`` array which can be passed by reference. If + * so, it will be indexed by the SNMP port index (or a decreasing negative index + * beginning -1 if the port only exists in the database). The contents of this + * associative array is: + * + * "port" => \Models\SwitchPort object + * "bullet" => + * - false for existing ports + * - "new" for newly found ports + * - "db" for ports that exist in the database only + * + * Note:** It is assumed that the Doctrine2 Entity Manager is available in the + * Zend registry as ``d2em`` in this function. + * + * @param SNMP $host An instance of \OSS_SNMP\SNMP for this switch + * @param bool $logger An instance of the logger or false + * @param bool $result + * @param bool $nosave Do we need to save the object in DB ? + * + * @throws + */ + public function snmpPollSwitchPorts( SNMP $host, bool $logger = false, bool|array &$result = false, bool $nosave = false ): static + { + // clone the ports currently known to this switch as we'll be playing with this array + $existingPorts = clone $this->switchPorts; + + $foundIfIndexes = []; + + // iterate over all the ports discovered on the switch: + foreach( $host->useIface()->indexes() as $index ) { + // Port types - see https://docs.ixpmanager.org/latest/usage/switches/#snmp-and-port-types-iftype + if( isset( $host->useIface()->types()[ $index ] ) && !in_array( $host->useIface()->types()[ $index ], config('ixp.snmp.allowed_interface_types') ) ) { + continue; + } + + // https://github.com/inex/IXP-Manager/issues/840 suggested duplicate ports were being added. + // Track found indexes to avoid this. + if( in_array( $index, $foundIfIndexes ) ) { + continue; + } + $foundIfIndexes[] = $index; + + // find the matching switch port that may already be in the database (or create a new one) + $sp = false; + foreach( $existingPorts as $ix => $ep ) { + if( $ep->ifIndex === $index ) { + $sp = SwitchPort::findOrFail( $ep->id ); + if( is_array( $result ) ){ + $result[ $index ] = [ "port" => $sp, 'bullet' => false ]; + } + + if( $logger ) { + Log::info( " - {$this->name} - found pre-existing port for ifIndex {$index}" ); + } + + // remove this from the array so later we'll know what ports exist only in the database + unset( $existingPorts[ $ix ] ); + break; + } + } + + if( !$sp ) { + // none existing port in database so we have found a new port + // Do we need to save the object ? + if( !$nosave ){ + $sp = SwitchPort::create( [ + 'switchid' => $this->id, + 'ifIndex' => $index, + 'active' => true, + 'type' => SwitchPort::TYPE_UNSET, + ]); + } + + if( is_array( $result ) ) { + $result[ $index ] = [ "port" => $sp, 'bullet' => "new" ]; + } + + if( $logger ) { + Log::info( "Found new port for {$this->name} with index $index" ); + } + } + + // update / set port details from SNMP + $sp->snmpUpdate( $host, $logger, $nosave ); + } + + if( $existingPorts->count() ) { + $i = -1; + foreach( $existingPorts as $ep ) { + if( is_array( $result ) ) { + $result[ $i-- ] = [ "port" => $ep, 'bullet' => "db" ]; + } + if( $logger ) { + Log::warning( "{$this->name} - port found in database with no matching port on the switch: [{$ep->id}] {$ep->name}" ); + } + } + } + + return $this; + } + + /** + * Return an array of core bundles + * + * @return CoreBundle[] + * + * @psalm-return list + */ + public function getCoreBundles(): array + { + $cbs = $cbids = []; + + foreach( $this->switchPorts as $sp ) { + if( $sp->physicalInterface && $sp->physicalInterface->coreinterface ) { + if( $sp->physicalInterface->coreinterface->corelinksidea ) { + if( !in_array( $sp->physicalInterface->coreinterface->corelinksidea->corebundle->id, $cbids, true ) ) { + $sideA = $sp->physicalInterface->coreinterface->corelinksidea; + $cbids[] = $sideA->corebundle->id; + $cbs[] = $sideA->corebundle; + } + } elseif( $sp->physicalInterface->coreinterface->corelinksideb ) { + if( !in_array( $sp->physicalInterface->coreinterface->corelinksideb->corebundle->id, $cbids, true ) ) { + $sideB = $sp->physicalInterface->coreinterface->corelinksideb; + $cbids[] = $sideB->corebundle->id; + $cbs[] = $sideB->corebundle; + } + } + } + } + + return $cbs; + } + + /** + * Evaluate the switches status. + * + * Checks for recent reboots and missed snmp polling. + * + * @return (bool|null|string|string[])[] + * + * @psalm-return array{name: null|string, status: bool, msgs: list{0: string, 1?: 'CRITICAL: rebooted within the last hour (probably).'|'Switch does not support reboot checks.', 2?: 'Switch does not support reboot checks.'}} + */ + public function status(): array + { + // assume we're okay + $okay = true; + $msgs = []; + + if( !$this->active ) { + return [ + 'name' => $this->name, + 'status' => true, + 'msgs' => [ 'Switch is inactive. Status tests skipped.' ], + ]; + } + + // last polled: + if( $this->lastPolled ) { + $lastPolled = Carbon::parse( $this->lastPolled ); + if( $lastPolled->diffInMinutes() > 10 ) { + $okay = false; + $msgs[] = 'WARNING: last polled ' . $lastPolled->diffForHumans() . '.'; + } else { + $msgs[] = 'Last polled ' . $lastPolled->diffForHumans() . '.';; + } + } else { + $okay = false; + $msgs[] = 'Switch has never been polled via SNMP.'; + } + + try { + if( $this->recentlyRebooted() ) { + $okay = false; + $msgs[] = 'CRITICAL: rebooted within the last hour (probably).'; + } + } catch( RebootDiscoveryNotSupported $e ) { + $msgs[] = 'Switch does not support reboot checks.'; + } + + return [ + 'name' => $this->name, + 'status' => $okay, + 'msgs' => $msgs, + ]; + } + + /** + * Indicate if the switch (probably) recently rebooted. + * + * If this returns true, switch has /most likely/ rebooted. + * + * @param int $window Window in minutes for 'recently'. Defaults to 60. + * + * @return bool + * + * @throws RebootDiscoveryNotSupported + */ + public function recentlyRebooted( int $window = 60 ): bool + { + if( $this->snmp_engine_time === null && $this->snmp_system_uptime === null ) { + throw new RebootDiscoveryNotSupported; + } + + // convert window to seconds + $window *= 60; + + // assume that the switch probably hasn't rebooted + $probably = false; + + if( ( $this->snmp_system_uptime / 100 ) < $window ) { + // Either sysuptime has rolled over or switch has rebooted. + + // try to identify rollover from snmp engine uptime. + // we're ignore engine.boots here because it's not clear what causes that to increment. + if( $this->snmp_engine_time !== null ) { + if( $this->snmp_engine_time < $window ) { + $probably = true; + } + } else { + $probably = true; + } + } + + if( $probably === true && Carbon::parse( $this->lastPolled )->diffInMinutes() < 60 ) { + // one additional check is that interface last change must be less than the sysuptime for a reboot + // to have taken place. We'll add a margin to the window here also. + $cutoff = time() - $window - 60; // 60 for some margin + + foreach( $this->switchPorts as $sp ) { + if( $sp->ifLastChange < $cutoff && $sp->ifOperStatus === SNMPIface::IF_ADMIN_STATUS_UP && $sp->physicalInterface && $sp->active ) { + $probably = false; + break; + } + } + } + + return $probably; + } + + /** + * String to describe the model being updated / deleted / created + * + * @param Model $model + * + * @return string + */ + public static function logSubject( Model $model ): string + { + return sprintf( + "Switcher [id:%d] '%s'", + $model->id, + $model->name, + ); + } +} \ No newline at end of file diff --git a/app/Models/TrafficDaily.php b/app/Models/TrafficDaily.php new file mode 100644 index 000000000..ba952fb4d --- /dev/null +++ b/app/Models/TrafficDaily.php @@ -0,0 +1,172 @@ + + */ + public function customer(): BelongsTo + { + return $this->belongsTo(Customer::class, 'cust_id'); + } + + /** + * Return an array of traffic data (joined with the customer record) for + * a given day and category. + * + * For example: + * + * array(55) { + * [0] => array(28) { + * ["day"] => object(DateTime)#286 (3) { + * .... + * } + * ["category"] => string(4) "bits" + * ["day_avg_in"] => string(8) "32732583" + * ... + * ["year_tot_out"] => string(16) "1430530473953106" + * ["id"] => string(6) "156931" + * ["Customer"] => array(31) { + * ["name"] => string(10) "A Name" + * ["type"] => int(1) + * ... + * ["id"] => int(4) + * } + * } + * [1] => array(28) { + * ["day"] => object(DateTime)#292 (3) { + * ... + * } + * ["category"] => string(4) "bits" + * ... + * } + * } + * + * @see \IXP_Mrtg::$CATEGORIES + * + * @param Carbon $day The day to load records for + * @param string $category The category of records to load (one of \IXP_Mrtg::$CATEGORIES) + * + * @return array An array of all switch objects + */ + public static function loadTraffic( Carbon $day, string $category ) + { + return self::select( [ 'td.*', 'c.*'] ) + ->from( 'traffic_daily AS td' ) + ->leftJoin( 'cust AS c', 'c.id', 'td.cust_id') + ->where( 'td.day', $day->format( 'Y-m-d' ) ) + ->where( 'td.category', $category ) + ->get()->toArray(); + } +} diff --git a/app/Models/TrafficDailyPhysInt.php b/app/Models/TrafficDailyPhysInt.php new file mode 100644 index 000000000..cb473b739 --- /dev/null +++ b/app/Models/TrafficDailyPhysInt.php @@ -0,0 +1,135 @@ + + */ + public function physicalInterface(): BelongsTo + { + return $this->belongsTo(PhysicalInterface::class, 'physicalinterface_id'); + } +} diff --git a/app/Models/User.php b/app/Models/User.php new file mode 100644 index 000000000..3f57a8b38 --- /dev/null +++ b/app/Models/User.php @@ -0,0 +1,432 @@ + $apiKeys + * @property-read int|null $api_keys_count + * @property-read \IXP\Models\Customer|null $customer + * @property-read \Illuminate\Database\Eloquent\Collection $customerToUser + * @property-read int|null $customer_to_user_count + * @property-read \Illuminate\Database\Eloquent\Collection $customers + * @property-read int|null $customers_count + * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications + * @property-read int|null $notifications_count + * @property-read \IXP\Models\User2FA|null $user2FA + * @property-read \Illuminate\Database\Eloquent\Collection $userRememberTokens + * @property-read int|null $user_remember_tokens_count + * @method static Builder|User activeOnly() + * @method static Builder|User byPrivs(?int $priv = null) + * @method static Builder|User newModelQuery() + * @method static Builder|User newQuery() + * @method static Builder|User query() + * @method static Builder|User whereAuthorisedMobile($value) + * @method static Builder|User whereCreatedAt($value) + * @method static Builder|User whereCreator($value) + * @method static Builder|User whereCustid($value) + * @method static Builder|User whereDisabled($value) + * @method static Builder|User whereEmail($value) + * @method static Builder|User whereExtraAttributes($value) + * @method static Builder|User whereId($value) + * @method static Builder|User whereLastupdatedby($value) + * @method static Builder|User whereName($value) + * @method static Builder|User wherePassword($value) + * @method static Builder|User wherePeeringdbId($value) + * @method static Builder|User wherePrefs($value) + * @method static Builder|User wherePrivs($value) + * @method static Builder|User whereUid($value) + * @method static Builder|User whereUpdatedAt($value) + * @method static Builder|User whereUsername($value) + * @property string|null $lastupdated + * @property string|null $created + * @method static Builder|User whereCreated($value) + * @method static Builder|User whereLastupdated($value) + * @mixin Eloquent + */ +class User extends Model implements AuthenticatableContract, CanResetPasswordContract +{ + + use Authenticatable, CanResetPassword, Notifiable, Observable; + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'user'; + + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + 'extra_attributes' => 'json', + 'prefs' => 'json', + ]; + + protected $attributes = [ + 'extra_attributes' => '[]', + 'prefs' => '{}', + ]; + + public const AUTH_PUBLIC = 0; + public const AUTH_CUSTUSER = 1; + public const AUTH_CUSTADMIN = 2; + public const AUTH_SUPERUSER = 3; + + public static $PRIVILEGES = [ + self::AUTH_CUSTUSER => 'CUSTUSER', + self::AUTH_CUSTADMIN => 'CUSTADMIN', + self::AUTH_SUPERUSER => 'SUPERUSER', + ]; + + public static $PRIVILEGES_ALL = [ + self::AUTH_PUBLIC => 'PUBLIC', + self::AUTH_CUSTUSER => 'CUSTUSER', + self::AUTH_CUSTADMIN => 'CUSTADMIN', + self::AUTH_SUPERUSER => 'SUPERUSER', + ]; + + public static $PRIVILEGES_TEXT = [ + self::AUTH_CUSTUSER => 'Customer User', + self::AUTH_CUSTADMIN => 'Customer Administrator', + self::AUTH_SUPERUSER => 'Superuser', + ]; + + public static $PRIVILEGES_TEXT_ALL = [ + self::AUTH_PUBLIC => 'Public / Non-User', + self::AUTH_CUSTUSER => 'Customer User', + self::AUTH_CUSTADMIN => 'Customer Administrator', + self::AUTH_SUPERUSER => 'Superuser', + ]; + + public static $PRIVILEGES_TEXT_NONSUPERUSER = [ + self::AUTH_CUSTUSER => 'Customer User', + self::AUTH_CUSTADMIN => 'Customer Administrator', + ]; + + public static $PRIVILEGES_TEXT_VSHORT = [ + self::AUTH_CUSTUSER => 'CU', + self::AUTH_CUSTADMIN => 'CA', + self::AUTH_SUPERUSER => 'SU', + ]; + + /** + * Get the remember tokens for the user + * + * @psalm-return HasMany + */ + public function userRememberTokens(): HasMany + { + return $this->hasMany(UserRememberToken::class, 'user_id' ); + } + + /** + * Get the remember tokens for the user + * + * @psalm-return HasOne + */ + public function user2FA(): HasOne + { + return $this->hasOne(User2FA::class, 'user_id' ); + } + + /** + * Get the customer + * + * @psalm-return BelongsTo + */ + public function customer(): BelongsTo + { + return $this->belongsTo(Customer::class, 'custid'); + } + + /** + * Get the api keys for the user + * + * @psalm-return HasMany + */ + public function apiKeys(): HasMany + { + return $this->hasMany(ApiKey::class, 'user_id' ); + } + + /** + * Get all the customers for the user + */ + public function customers(): BelongsToMany + { + return $this->belongsToMany(Customer::class, 'customer_to_users', 'user_id' ) + ->orderBy( 'id', 'asc' ); + } + + /** + * Get all the customers for the user + * + * @psalm-return HasMany + */ + public function customerToUser(): HasMany + { + return $this->HasMany(CustomerToUser::class, 'user_id' ); + } + + /** + * Scope a query to match active user only + * + * @param Builder $query + * + * @return Builder + */ + public function scopeActiveOnly( Builder $query ): Builder + { + return $query->where('disabled', false ); + } + + /** + * Scope a query to match user by privs + * + * @param Builder $query + * @param int|null $priv + * + * @return Builder + */ + public function scopeByPrivs( Builder $query, ?int $priv = null ): Builder + { + return $query->select( 'user.*' )->leftJoin( 'customer_to_users AS c2u', 'c2u.user_id', 'user.id' ) + ->when( $priv, function( Builder $q, $priv ){ + return $q->where( 'c2u.privs', $priv ); + } )->distinct(); + } + + /** + * Is the user of the named type? + * + * @return bool + */ + public function isCustUser(): bool + { + return $this->privs() === self::AUTH_CUSTUSER; + } + + /** + * Is the user of the named type? + * @return bool + */ + public function isCustAdmin(): bool + { + return $this->privs() === self::AUTH_CUSTADMIN; + } + + /** + * Is the user of the named type? + * + * @return bool + */ + public function isSuperUser(): bool + { + return $this->privs() === self::AUTH_SUPERUSER; + } + + /** + * Get privilege from the table CustomerToUser + * + * @return int|null + */ + public function privs(): ?int + { + $c2u = CustomerToUser::where( 'customer_id' , $this->custid )->where( 'user_id' , $this->id )->first(); + if( $c2u ) { + return $c2u->privs; + } + return null; + } + + /** + * Does 2fa need to be enforced for this user? + * + * @return bool + */ + public function is2faEnforced(): bool + { + if( !config('google2fa.enabled') ) { + return false; + } + + return $this->privs() >= config( "google2fa.ixpm_2fa_enforce_for_users" ) + && ( !$this->user2FA || !$this->user2FA->enabled ); + } + + /** + * Check if the user is required to authenticate with 2FA for the current session + * + * @return bool + */ + public function is2faAuthRequiredForSession(): bool + { + if( !config('google2fa.enabled') ) { + return false; + } + + if( !$this->user2FA || !$this->user2FA->enabled ) { + // If the user does not have 2fa configured or enabled but it is required, then return true: + if( $this->is2faEnforced() ) { + return true; + } + return false; + } + + $authenticator = new GoogleAuthenticator( request() ); + + if( $authenticator->isAuthenticated() ) { + return false; + } + return true; + } + + /** + * Get the current customer to user - if one exists. + * + * @return CustomerToUser|null + */ + public function currentCustomerToUser(): ?CustomerToUser + { + if( !$this->customer ) { + return null; + } + + $c2u = CustomerToUser::where( 'customer_id', $this->custid ) + ->where( 'user_id', $this->id )->first(); + + return $c2u ?? null; + } + + /** + * Send the password reset notification. + * + * @param string $token + * + * @return void + */ + #[\Override] + public function sendPasswordResetNotification( $token ): void + { + event( new ForgotPasswordEvent( $token, $this ) ); + } + + /** + * Get the "remember me" token value. + * + * // We have overridden Laravel's remember token functionality and do not rely on this. + * // However, some Laravel functionality if triggered on this returning a non-false value + * // to execute certain functionality. As such, we'll just return something random: + * + * @return string + */ + #[\Override] + public function getRememberToken(): string + { + // We have overridden Laravel's remember token functionality and do not rely on this. + // However, some Laravel functionality if triggered on this returning a non-false value + // to execute certain functionality. As such, we'll just return something random: + return Str::random(60); + } + + /** + * Allow direct access to the 2FA secret code + */ + #[\Override] + public function __get( $key ) + { + switch( $key ) { + // google2fa Laravel bridge looking for 2fa secret + case 'secret': + return $this->user2FA->secret ?? null; + break; + } + + return parent::__get( $key ); + } + + /** + * String to describe the model being updated / deleted / created + * + * @param Model $model + * + * @return string + */ + public static function logSubject( Model $model ): string + { + return sprintf( + "User [id:%d] '%s'", + $model->id, + $model->username, + ); + } +} \ No newline at end of file diff --git a/app/Models/User2FA.php b/app/Models/User2FA.php new file mode 100644 index 000000000..59f9f3da2 --- /dev/null +++ b/app/Models/User2FA.php @@ -0,0 +1,81 @@ + + */ + public function user(): BelongsTo + { + return $this->belongsTo(User::class, 'user_id' ); + } +} \ No newline at end of file diff --git a/app/Models/UserLoginHistory.php b/app/Models/UserLoginHistory.php new file mode 100644 index 000000000..a96d97052 --- /dev/null +++ b/app/Models/UserLoginHistory.php @@ -0,0 +1,87 @@ + + */ + public function customerToUser(): BelongsTo + { + return $this->belongsTo(CustomerToUser::class, 'customer_to_user_id'); + } +} \ No newline at end of file diff --git a/app/Models/UserRememberToken.php b/app/Models/UserRememberToken.php new file mode 100644 index 000000000..b13f638a8 --- /dev/null +++ b/app/Models/UserRememberToken.php @@ -0,0 +1,97 @@ + + */ + public function user(): BelongsTo + { + return $this->belongsTo(User::class, 'user_id'); + } + + /** + * Is this token expired? + * + * @return bool + */ + public function expired(): bool + { + return $this->expires < now(); + } +} \ No newline at end of file diff --git a/app/Models/Vendor.php b/app/Models/Vendor.php new file mode 100644 index 000000000..b19e2702d --- /dev/null +++ b/app/Models/Vendor.php @@ -0,0 +1,117 @@ + $consoleServers + * @property-read int|null $console_servers_count + * @property-read \Illuminate\Database\Eloquent\Collection $switchers + * @property-read int|null $switchers_count + * @method static Builder|Vendor newModelQuery() + * @method static Builder|Vendor newQuery() + * @method static Builder|Vendor query() + * @method static Builder|Vendor whereBundleName($value) + * @method static Builder|Vendor whereCreatedAt($value) + * @method static Builder|Vendor whereId($value) + * @method static Builder|Vendor whereNagiosName($value) + * @method static Builder|Vendor whereName($value) + * @method static Builder|Vendor whereShortname($value) + * @method static Builder|Vendor whereUpdatedAt($value) + * @mixin \Eloquent + */ +class Vendor extends Model +{ + use Observable; + + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'vendor'; + + /** + * The attributes that are mass assignable. + * + * @var array + */ + protected $fillable = [ + 'name', + 'shortname', + 'bundle_name', + ]; + + /** + * Get the console servers for the vendor + * + * @psalm-return HasMany + */ + public function consoleServers(): HasMany + { + return $this->hasMany(ConsoleServer::class, 'vendor_id' ); + } + + /** + * Get the switchers for the vendor + * + * @psalm-return HasMany + */ + public function switchers(): HasMany + { + return $this->hasMany(Switcher::class, 'vendorid' ); + } + + /** + * String to describe the model being updated / deleted / created + * + * @param Model $model + * + * @return string + */ + public static function logSubject( Model $model ): string + { + return sprintf( + "Vendor [id:%d] '%s'", + $model->id, + $model->name, + ); + } +} diff --git a/app/Models/VirtualInterface.php b/app/Models/VirtualInterface.php new file mode 100644 index 000000000..ffa67522b --- /dev/null +++ b/app/Models/VirtualInterface.php @@ -0,0 +1,384 @@ + $macAddresses + * @property-read int|null $mac_addresses_count + * @property-read Collection $physicalInterfaces + * @property-read int|null $physical_interfaces_count + * @property-read Collection $sflowReceivers + * @property-read int|null $sflow_receivers_count + * @property-read Collection $vlanInterfaces + * @property-read int|null $vlan_interfaces_count + * @method static Builder|VirtualInterface newModelQuery() + * @method static Builder|VirtualInterface newQuery() + * @method static Builder|VirtualInterface query() + * @method static Builder|VirtualInterface whereChannelgroup($value) + * @method static Builder|VirtualInterface whereCreatedAt($value) + * @method static Builder|VirtualInterface whereCustid($value) + * @method static Builder|VirtualInterface whereDescription($value) + * @method static Builder|VirtualInterface whereFastlacp($value) + * @method static Builder|VirtualInterface whereId($value) + * @method static Builder|VirtualInterface whereLagFraming($value) + * @method static Builder|VirtualInterface whereMtu($value) + * @method static Builder|VirtualInterface whereName($value) + * @method static Builder|VirtualInterface whereTrunk($value) + * @method static Builder|VirtualInterface whereUpdatedAt($value) + * @mixin Eloquent + */ +class VirtualInterface extends Model +{ + use Observable; + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'virtualinterface'; + + /** + * The attributes that are mass assignable. + * + * @var array + */ + protected $fillable = [ + 'custid', + 'name', + 'description', + 'mtu', + 'trunk', + 'channelgroup', + 'lag_framing', + 'fastlacp', + ]; + + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + 'trunk' => 'boolean', + 'lag_framing' => 'boolean', + 'fastlacp' => 'boolean', + ]; + + /** + * Get the customer that owns the virtual interfaces. + * + * @psalm-return BelongsTo + */ + public function customer(): BelongsTo + { + return $this->belongsTo(Customer::class, 'custid' ); + } + + /** + * Get the VLAN interfaces for the virtual interface + * + * @psalm-return HasMany + */ + public function vlanInterfaces(): HasMany + { + return $this->hasMany(VlanInterface::class, 'virtualinterfaceid'); + } + + /** + * Get the physical interfaces for the virtual interface + * + * @psalm-return HasMany + */ + public function physicalInterfaces(): HasMany + { + return $this->hasMany(PhysicalInterface::class, 'virtualinterfaceid'); + } + + /** + * Get the mac addresses for the virtual interface + * + * @psalm-return HasMany + */ + public function macAddresses(): HasMany + { + return $this->hasMany(MacAddress::class, 'virtualinterfaceid'); + } + + /** + * Get the sflow receivers for the virtual interface + * + * @psalm-return HasMany + */ + public function sflowReceivers(): HasMany + { + return $this->hasMany(SflowReceiver::class, 'virtual_interface_id'); + } + + /** + * Get the speed of the LAG + * + * @param bool $connectedOnly Only consider physical interfaces with 'CONNECTED' state + * + * @return int + */ + public function speed( bool $connectedOnly = true ): int + { + if( $connectedOnly ) { + return $this->physicalInterfaces()->connected()->sum('speed'); + } + + return $this->physicalInterfaces()->sum('speed'); + } + + /** + * Is this LAG graphable? + * + * @return bool + */ + public function isGraphable(): bool + { + if( $this->physicalInterfaces()->graphable()->count() ) { + return true; + } + + return false; + } + + /** + * Return the core bundle associated to the virtual interface or false + */ + public function getCoreBundle(): CoreBundle|false|null + { + foreach( $this->physicalInterfaces as $pi ) { + if( $pi->coreinterface()->exists() ) { + $ci = $pi->coreinterface; + /** @var $ci CoreInterface */ + return $ci->coreLink()->coreBundle; + } + } + return false; + } + + /** + * Get peering PhysicalInterfaces + * + * @return PhysicalInterface[] + * + * @psalm-return list + */ + public function peeringPhysicalInterface(): array + { + $ppis = []; + foreach( $this->physicalInterfaces as $ppi ) { + if( $ppi->peeringPhysicalInterface ){ + $ppis[] = $ppi->peeringPhysicalInterface; + } + } + return $ppis; + } + + /** + * Get fanout PhysicalInterfaces + * + * @return PhysicalInterface[] + * + * @psalm-return list + */ + public function fanoutPhysicalInterface(): array + { + $ppis = []; + foreach( $this->physicalInterfaces as $ppi){ + if( $ppi->fanoutPhysicalInterface ) { + $ppis[] = $ppi->peeringPhysicalInterface; + } + } + return $ppis; + } + + /** + * Get a Switch Port of a virtual interface. + * + * @return bool|SwitchPort|null The switch port or false if no switch port. + */ + public function switchPort(): bool|SwitchPort|null + { + if( $this->physicalInterfaces()->count() ){ + return $this->physicalInterfaces()->first()->switchPort; + } + return false; + } + + /** + * Get the *type* of virtual interface based on the switchport type. + * + * Actually returns type of the first physical interface's switchport. All + * switchports in a virtual interface should be the same type so just + * examining the first is sufficient to determine the *virtual interface type*. + * + * @see SwitchPortt::$TYPES + * + * @return false|int|null The virtual interface type (`\Models\SwitchPort::TYPE_XXX`) or false if no physical interfaces. + */ + public function type(): int|false|null + { + if( $this->physicalInterfaces->isNotEmpty() ) { + return $this->physicalInterfaces[ 0 ]->switchPort->type; + } + return false; + } + + /** + * Turn the database integer representation of the type into text as + * defined in the SwitchPort::$TYPES array (or 'Unknown') + * @return string + */ + public function resolveType(): string + { + return SwitchPort::$TYPES[ $this->type() ] ?? 'Unknown'; + } + + /** + * Is the type SwitchPort::TYPE_PEERING? + * + * @return bool + */ + public function typePeering(): bool + { + return $this->type() === SwitchPort::TYPE_PEERING; + } + + /** + * Is the type SwitchPort::TYPE_FANOUT? + * + * @return bool + */ + public function typeFanout(): bool + { + return $this->type() === SwitchPort::TYPE_FANOUT; + } + + /** + * Is the type SwitchPort::TYPE_RESELLER? + * + * @return bool + */ + public function typeReseller(): bool + { + return $this->type() === SwitchPort::TYPE_RESELLER; + } + + /** + * Is the type SwitchPort::TYPE_CORE? + * + * @return bool + */ + public function typeCore(): bool + { + return $this->type() === SwitchPort::TYPE_CORE; + } + + /** + * Get the bundle name if name and channel group are set. Otherwise an empty string. + * + * @return string + */ + public function bundleName(): string + { + return $this->name && $this->channelgroup ? $this->name . $this->channelgroup : ''; + } + + /** + * Check if the switch is the same for the physical interfaces of the virtual interface + * + * @return bool + */ + public function sameSwitchForEachPI(): bool + { + return self::select( 'sp.switchid AS switchid' ) + ->from( 'virtualinterface AS vi' ) + ->leftJoin( 'physicalinterface AS pi', 'pi.virtualinterfaceid', 'vi.id' ) + ->leftJoin( 'switchport AS sp', 'sp.id', 'pi.switchportid' ) + ->where( 'vi.id', $this->id )->distinct()->get()->pluck( 'switchid' )->count() === 1; + } + + /** + * Number of non-private VLANs + * + * Usually just one but we use this for labeling on the frontend if >1 + * + * @return int + */ + public function numberPublicVlans(): int + { + return self::from( 'virtualinterface AS vi' ) + ->leftJoin( 'vlaninterface AS vli', 'vli.virtualinterfaceid', 'vi.id' ) + ->leftJoin( 'vlan AS v', 'v.id', 'vli.vlanid' ) + ->where( 'vi.id', $this->id ) + ->where( 'private', false )->count(); + } + + /** + * String to describe the model being updated / deleted / created + * + * @param Model $model + * + * @return string + */ + public static function logSubject( Model $model ): string + { + return sprintf( + "Virtual Interface [id:%d] belonging to %s [id:%d] '%s'", + $model->id, + ucfirst( config( 'ixp_fe.lang.customer.one' ) ), + $model->custid, + $model->customer->name, + ); + } +} \ No newline at end of file diff --git a/app/Models/Vlan.php b/app/Models/Vlan.php new file mode 100644 index 000000000..9e8ad63d0 --- /dev/null +++ b/app/Models/Vlan.php @@ -0,0 +1,273 @@ + $atlasRun + * @property-read int|null $atlas_run_count + * @property-read \IXP\Models\Infrastructure $infrastructure + * @property-read \Illuminate\Database\Eloquent\Collection $ipv4Addresses + * @property-read int|null $ipv4_addresses_count + * @property-read \Illuminate\Database\Eloquent\Collection $ipv6Addresses + * @property-read int|null $ipv6_addresses_count + * @property-read \Illuminate\Database\Eloquent\Collection $networksInfo + * @property-read int|null $networks_info_count + * @property-read \Illuminate\Database\Eloquent\Collection $routeServerFilters + * @property-read int|null $route_server_filters_count + * @property-read \Illuminate\Database\Eloquent\Collection $routers + * @property-read int|null $routers_count + * @property-read \Illuminate\Database\Eloquent\Collection $vlanInterfaces + * @property-read int|null $vlan_interfaces_count + * @method static Builder|Vlan newModelQuery() + * @method static Builder|Vlan newQuery() + * @method static Builder|Vlan peeringManager() + * @method static Builder|Vlan privateOnly() + * @method static Builder|Vlan publicOnly() + * @method static Builder|Vlan query() + * @method static Builder|Vlan whereConfigName($value) + * @method static Builder|Vlan whereCreatedAt($value) + * @method static Builder|Vlan whereExportToIxf($value) + * @method static Builder|Vlan whereId($value) + * @method static Builder|Vlan whereInfrastructureid($value) + * @method static Builder|Vlan whereName($value) + * @method static Builder|Vlan whereNotes($value) + * @method static Builder|Vlan whereNumber($value) + * @method static Builder|Vlan wherePeeringManager($value) + * @method static Builder|Vlan wherePeeringMatrix($value) + * @method static Builder|Vlan wherePrivate($value) + * @method static Builder|Vlan whereUpdatedAt($value) + * @method static Builder|Vlan publicProductionPeeringLan() + * @mixin \Eloquent + */ +class Vlan extends Model +{ + use Observable; + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'vlan'; + + /** + * The attributes that are mass assignable. + * + * @var array + */ + protected $fillable = [ + 'name', + 'number', + 'notes', + 'private', + 'infrastructureid', + 'peering_matrix', + 'peering_manager', + 'export_to_ixf', + 'config_name', + ]; + + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + 'private' => 'boolean', + 'export_to_ixf' => 'boolean', + ]; + + /** + * Get the vlan interfaces that are in this vlan + * + * @psalm-return HasMany + */ + public function vlanInterfaces(): HasMany + { + return $this->hasMany(VlanInterface::class, 'vlanid'); + } + + /** + * Get the vlan interfaces that are in this vlan + * + * @psalm-return HasMany + */ + public function routers(): HasMany + { + return $this->hasMany(Router::class ); + } + + /** + * Get the ipv4addresses for the vlan + * + * @psalm-return HasMany + */ + public function ipv4Addresses(): HasMany + { + return $this->hasMany(IPv4Address::class, 'vlanid' ); + } + + /** + * Get the ipv6addresses for the vlan + * + * @psalm-return HasMany + */ + public function ipv6Addresses(): HasMany + { + return $this->hasMany(IPv6Address::class, 'vlanid' ); + } + + /** + * Get the route server filters for the vlan + * + * @psalm-return HasMany + */ + public function routeServerFilters(): HasMany + { + return $this->hasMany(RouteServerFilter::class, 'vlan_id' ); + } + + /** + * Get the networks info for the vlan + * + * @psalm-return HasMany + */ + public function networksInfo(): HasMany + { + return $this->hasMany(NetworkInfo::class, 'vlanid' ); + } + + /** + * Get the atlas run that are in this vlan + * + * @psalm-return HasMany + */ + public function atlasRun(): HasMany + { + return $this->hasMany( AtlasRun::class, 'vlan_id', 'id'); + } + + /** + * Get the infrastructure that own the vlan + * + * @psalm-return BelongsTo + */ + public function infrastructure(): BelongsTo + { + return $this->belongsTo(Infrastructure::class, 'infrastructureid' ); + } + + /** + * Scope a query to only include private VLANs + * + * @param Builder $query + * + * @return Builder + */ + public function scopePrivateOnly( Builder $query ): Builder + { + return $query->where( 'private', 1 ); + } + + /** + * Scope a query to only include public / non-private VLANs + * + * @param Builder $query + * + * @return Builder + */ + public function scopePublicOnly( Builder $query ): Builder + { + return $query->where( 'private', 0 ); + } + + /** + * Scope a query to only include peering manager + * + * @param Builder $query + * + * @return Builder + */ + public function scopePeeringManager( Builder $query ): Builder + { + return $query->where( 'peering_manager', 1 ); + } + + /** + * Scope a query to only include public production peering LANs + * + * This is a bit of a hack right now as there is no specific flag for + * this but we'll use export_to_ixf for now as that is as good an + * inidicator as any. + * + * @param Builder $query + * @return Builder + */ + public function scopePublicProductionPeeringLan( Builder $query ): Builder + { + return $query->where( 'export_to_ixf', 1 ); + } + + + + /** + * String to describe the model being updated / deleted / created + * + * @param Model $model + * + * @return string + */ + public static function logSubject( Model $model ): string + { + return sprintf( + "Vlan [id:%d] '%s' belonging to Infrastructure [id:%d] '%s'", + $model->id, + $model->name, + $model->infrastructureid, + $model->infrastructure->name, + ); + } +} \ No newline at end of file diff --git a/app/Models/VlanInterface.php b/app/Models/VlanInterface.php new file mode 100644 index 000000000..720cb13b5 --- /dev/null +++ b/app/Models/VlanInterface.php @@ -0,0 +1,296 @@ + $layer2addresses + * @property-read int|null $layer2addresses_count + * @property-read \IXP\Models\VirtualInterface|null $virtualInterface + * @property-read \IXP\Models\Vlan|null $vlan + * @method static Builder|VlanInterface newModelQuery() + * @method static Builder|VlanInterface newQuery() + * @method static Builder|VlanInterface query() + * @method static Builder|VlanInterface whereAs112client($value) + * @method static Builder|VlanInterface whereBgpmd5secret($value) + * @method static Builder|VlanInterface whereBusyhost($value) + * @method static Builder|VlanInterface whereCreatedAt($value) + * @method static Builder|VlanInterface whereId($value) + * @method static Builder|VlanInterface whereIpv4addressid($value) + * @method static Builder|VlanInterface whereIpv4bgpmd5secret($value) + * @method static Builder|VlanInterface whereIpv4canping($value) + * @method static Builder|VlanInterface whereIpv4enabled($value) + * @method static Builder|VlanInterface whereIpv4hostname($value) + * @method static Builder|VlanInterface whereIpv4monitorrcbgp($value) + * @method static Builder|VlanInterface whereIpv6addressid($value) + * @method static Builder|VlanInterface whereIpv6bgpmd5secret($value) + * @method static Builder|VlanInterface whereIpv6canping($value) + * @method static Builder|VlanInterface whereIpv6enabled($value) + * @method static Builder|VlanInterface whereIpv6hostname($value) + * @method static Builder|VlanInterface whereIpv6monitorrcbgp($value) + * @method static Builder|VlanInterface whereIrrdbfilter($value) + * @method static Builder|VlanInterface whereMaxbgpprefix($value) + * @method static Builder|VlanInterface whereMcastenabled($value) + * @method static Builder|VlanInterface whereNotes($value) + * @method static Builder|VlanInterface whereRsclient($value) + * @method static Builder|VlanInterface whereRsmorespecifics($value) + * @method static Builder|VlanInterface whereUpdatedAt($value) + * @method static Builder|VlanInterface whereVirtualinterfaceid($value) + * @method static Builder|VlanInterface whereVlanid($value) + * @mixin Eloquent + */ +class VlanInterface extends Model +{ + use Observable; + + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'vlaninterface'; + + /** + * The attributes that are mass assignable. + * + * @var array + */ + protected $fillable = [ + 'virtualinterfaceid', + 'vlanid', + 'irrdbfilter', + 'rsmorespecifics', + 'mcastenabled', + 'maxbgpprefix', + 'rsclient', + 'as112client', + 'busyhost', + ]; + + /** + * Get the customer that owns the virtual interfaces. + * + * @psalm-return BelongsTo + */ + public function virtualInterface(): BelongsTo + { + return $this->belongsTo(VirtualInterface::class, 'virtualinterfaceid'); + } + + /** + * Get the vlan that holds the vlan interface. + * + * @psalm-return BelongsTo + */ + public function vlan(): BelongsTo + { + return $this->belongsTo(Vlan::class, 'vlanid'); + } + + /** + * Get the layer2addresses for the vlan interface + * + * @psalm-return HasMany + */ + public function layer2addresses(): HasMany + { + return $this->hasMany(Layer2Address::class, 'vlan_interface_id' ); + } + + /** + * Get the ipv4address associated with the vlaninterface. + * + * @psalm-return BelongsTo + */ + public function ipv4address(): BelongsTo + { + return $this->belongsTo(IPv4Address::class, 'ipv4addressid' ); + } + + /** + * Get the ipv6address associated with the vlaninterface. + * + * @psalm-return BelongsTo + */ + public function ipv6address(): BelongsTo + { + return $this->belongsTo(IPv6Address::class, 'ipv6addressid' ); + } + + /** + * See if a given protocol is enabled + * + * @param int|string $proto + * + * @return bool + */ + public function ipvxEnabled( $proto ): bool + { + switch( $proto ) { + case 4: + case 'ipv4': + return (bool)$this->ipv4enabled; + break; + case 6: + case 'ipv6': + return (bool)$this->ipv6enabled; + break; + default: + return false; + } + } + + /** + * Is this VLAN interface graphable? + * + * @return bool + */ + public function isGraphable(): bool + { + return $this->virtualInterface->isGraphable(); + } + + /** + * Convenience function to see if we can graph a VLI for latency for a given protocol + * + * @param string $proto Either ipv4 / ipv6 (as defined in Grapher) + * + * @return bool + * + * @throws + */ + public function canGraphForLatency( string $proto ): bool + { + switch( $proto ) { + case 'ipv4': + return !$this->vlan->private + && $this->ipv4enabled + && $this->ipv4canping + && $this->ipv4address; + break; + case 'ipv6': + return !$this->vlan->private + && $this->ipv6enabled + && $this->ipv6canping + && $this->ipv6address; + break; + default: + return false; + } + } + + /** + * Convenience function to get an IP address based on a given protocol + * + * @param int|string $proto Either 4/ipv4 / 6/ipv6 (as defined in Grapher) + * + * @throws + */ + public function getIPAddress( int|string $proto ): IPv4Address|IPv6Address|null + { + if( is_string( $proto ) ) { + $proto = strtolower( $proto ); + } + + return match( $proto ) { + 4, 'ipv4' => $this->ipv4address, + 6, 'ipv6' => $this->ipv6address, + }; +/* + if( is_int( $proto ) ) { + return match( $proto ) { + 4 => $this->ipv4address, + 6 => $this->ipv6address, + }; + } + + switch( strtolower( $proto ) ) { + case 'ipv4': + return $this->ipv4address; + break; + case 'ipv6': + return $this->ipv6address; + break; + default: + return null; + } +*/ + } + + /** + * String to describe the model being updated / deleted / created + * + * @param Model $model + * + * @return string + */ + public static function logSubject( Model $model ): string + { + return sprintf( + "VLAN Interface [id:%d] belonging to Virtual Interface [id:%d]", + $model->id, + $model->virtualinterfaceid, + ); + } +} \ No newline at end of file diff --git a/app/Observers/DocstoreCustomerDirectoryObserver.php b/app/Observers/DocstoreCustomerDirectoryObserver.php new file mode 100644 index 000000000..54b31c23b --- /dev/null +++ b/app/Observers/DocstoreCustomerDirectoryObserver.php @@ -0,0 +1,117 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Observers + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class DocstoreCustomerDirectoryObserver +{ + /** + * @param Customer $cust + * + * @return void + */ + private function clearCacheOfHierarchiesForCustomerAndUserClasses( Customer $cust ): void + { + foreach( User::$PRIVILEGES_ALL as $priv => $privname ) { + Cache::forget( DocstoreCustomerDirectory::CACHE_KEY_FOR_CUSTOMER_USER_CLASS_HIERARCHY . $cust->id . '_' . $priv ); + } + } + + /** + * Handle the docstore directory "created" event. + * + * @param DocstoreCustomerDirectory $docstoreCustomerDirectory + * + * @return void + */ + public function created( DocstoreCustomerDirectory $docstoreCustomerDirectory ): void + { + $this->clearCacheOfHierarchiesForCustomerAndUserClasses( $docstoreCustomerDirectory->customer ); + } + + /** + * Handle the docstore directory "updated" event. + * + * @param DocstoreCustomerDirectory $docstoreCustomerDirectory + * + * @return void + */ + public function updated( DocstoreCustomerDirectory $docstoreCustomerDirectory ): void + { + $this->clearCacheOfHierarchiesForCustomerAndUserClasses( $docstoreCustomerDirectory->customer ); + } + + /** + * Handle the docstore directory "deleted" event. + * + * @param DocstoreCustomerDirectory $docstoreCustomerDirectory + * + * @return void + */ + public function deleted( DocstoreCustomerDirectory $docstoreCustomerDirectory ): void + { + $this->clearCacheOfHierarchiesForCustomerAndUserClasses( $docstoreCustomerDirectory->customer ); + } + + /** + * Handle the docstore directory "restored" event. + * + * @param DocstoreCustomerDirectory $docstoreCustomerDirectory + * + * @return void + */ + public function restored( DocstoreCustomerDirectory $docstoreCustomerDirectory ): void + { + $this->clearCacheOfHierarchiesForCustomerAndUserClasses( $docstoreCustomerDirectory->customer ); + } + + /** + * Handle the docstore directory "force deleted" event. + * + * @param DocstoreCustomerDirectory $docstoreCustomerDirectory + * + * @return void + */ + public function forceDeleted( DocstoreCustomerDirectory $docstoreCustomerDirectory ): void + { + $this->clearCacheOfHierarchiesForCustomerAndUserClasses( $docstoreCustomerDirectory->customer ); + } +} \ No newline at end of file diff --git a/app/Observers/DocstoreDirectoryObserver.php b/app/Observers/DocstoreDirectoryObserver.php new file mode 100644 index 000000000..3b54c2de1 --- /dev/null +++ b/app/Observers/DocstoreDirectoryObserver.php @@ -0,0 +1,114 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Observers + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class DocstoreDirectoryObserver +{ + /** + * @return void + */ + private function clearCacheOfHierarchiesForUserClasses(): void + { + foreach( User::$PRIVILEGES_ALL as $priv => $privname ) { + Cache::forget( DocstoreDirectory::CACHE_KEY_FOR_USER_CLASS_HIERARCHY . $priv ); + } + } + + /** + * Handle the docstore directory "created" event. + * + * @param DocstoreDirectory $docstoreDirectory + * + * @return void + */ + public function created( DocstoreDirectory $docstoreDirectory ): void + { + $this->clearCacheOfHierarchiesForUserClasses(); + } + + /** + * Handle the docstore directory "updated" event. + * + * @param DocstoreDirectory $docstoreDirectory + * + * @return void + */ + public function updated( DocstoreDirectory $docstoreDirectory ): void + { + $this->clearCacheOfHierarchiesForUserClasses(); + } + + /** + * Handle the docstore directory "deleted" event. + * + * @param DocstoreDirectory $docstoreDirectory + * + * @return void + */ + public function deleted( DocstoreDirectory $docstoreDirectory ): void + { + $this->clearCacheOfHierarchiesForUserClasses(); + } + + /** + * Handle the docstore directory "restored" event. + * + * @param DocstoreDirectory $docstoreDirectory + * + * @return void + */ + public function restored( DocstoreDirectory $docstoreDirectory ): void + { + $this->clearCacheOfHierarchiesForUserClasses(); + } + + /** + * Handle the docstore directory "force deleted" event. + * + * @param DocstoreDirectory $docstoreDirectory + * + * @return void + */ + public function forceDeleted( DocstoreDirectory $docstoreDirectory ): void + { + $this->clearCacheOfHierarchiesForUserClasses(); + } +} \ No newline at end of file diff --git a/app/Policies/CustomerPolicy.php b/app/Policies/CustomerPolicy.php new file mode 100644 index 000000000..6bb3743cd --- /dev/null +++ b/app/Policies/CustomerPolicy.php @@ -0,0 +1,130 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Policies + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class CustomerPolicy +{ + use HandlesAuthorization; + + + /** + * Superadmins can do anything + * + * @param User $user + * @param $ability + * + * @return null|true + */ + public function before( User $user, $ability ): bool|null + { + if( $user->isSuperUser() ) { + return true; + } + return null; + } + + /** + * Determine whether the user can view the customer. + * + * @param User $user + * @param Customer $customer + * + * @return mixed + */ + public function view( User $user, Customer $customer ): bool + { + return $user->custid === $customer->id; + } + + /** + * Determine whether the user can create customers. + * + * @param User $user + */ + public function create( User $user ): void + { + // + } + + /** + * Determine whether the user can update the customer. + * + * @param User $user + * @param Customer $customer + */ + public function update(User $user, Customer $customer): void + { + // + } + + /** + * Determine whether the user can delete the customer. + * + * @param User $user + * @param Customer $customer + */ + public function delete( User $user, Customer $customer ): void + { + // + } + + /** + * Determine whether the user can restore the customer. + * + * @param User $user + * @param Customer $customer + */ + public function restore( User $user, Customer $customer ): void + { + // + } + + /** + * Determine whether the user can permanently delete the customer. + * + * @param User $user + * @param Customer $customer + */ + public function forceDelete( User $user, Customer $customer ): void + { + // + } +} \ No newline at end of file diff --git a/app/Policies/DocstoreCustomerDirectoryPolicy.php b/app/Policies/DocstoreCustomerDirectoryPolicy.php new file mode 100644 index 000000000..52bc01d8f --- /dev/null +++ b/app/Policies/DocstoreCustomerDirectoryPolicy.php @@ -0,0 +1,155 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Policies + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class DocstoreCustomerDirectoryPolicy +{ + use HandlesAuthorization; + + /** + * Determine whether the user can access the list + * + * @param User $user + * + * @return mixed + */ + public function listCustomers( User $user ) + { + return $user->isSuperUser(); + } + + /** + * Determine whether the user can access the list + * + * @param User $user + * @param Customer $cust + * + * @return mixed + */ + public function listPatchPanelPortFiles( User $user, Customer $cust ): bool + { + return ( $user->isSuperUser() || $user->custid === $cust->id ) + && $cust->patchPanelPorts()->with( 'patchPanelPortFiles' ) + ->has($user->isSuperUser() ? 'patchPanelPortFiles' : 'patchPanelPortFilesPublic' )->get() + ->pluck( 'patchPanelPortFiles' )->isNotEmpty(); + } + + /** + * Determine whether the user can access the list + * + * @param User $user + * @param Customer $cust + * + * @return mixed + */ + public function listPatchPanelPortFilesHistory( User $user, Customer $cust ): bool + { + return ( $user->isSuperUser() || $user->custid === $cust->id ) + && $cust->patchPanelPortHistories() + ->with( 'patchPanelPortHistoryFiles' )->has( 'patchPanelPortHistoryFiles' ) + ->get()->pluck( 'patchPanelPortHistoryFiles' )->isNotEmpty(); + } + + /** + * Determine whether the user can create docstore directories. + * + * @param User $user + * @param Customer $cust + * + * @return mixed + */ + public function list( User $user, Customer $cust ): bool + { + return $user->isSuperUser() || ( $user->privs() >= User::AUTH_CUSTUSER && $user->custid === $cust->id ) ; + } + + /** + * Determine whether the user can create docstore directories. + * + * @param User $user + * @param Customer $cust + * + * @return mixed + */ + public function create( User $user, Customer $cust ): bool + { + return $user->isSuperUser() && $cust->exists; + } + + /** + * Determine whether the user can update the docstore directory. + * + * @param User $user + * @param DocstoreCustomerDirectory $dir + * + * @return mixed + */ + public function update( User $user, DocstoreCustomerDirectory $dir ) + { + return $user->isSuperUser(); + } + + /** + * Determine whether the user can delete the docstore directory. + * + * @param User $user + * @param DocstoreCustomerDirectory $dir + * + * @return mixed + */ + public function delete( User $user, DocstoreCustomerDirectory $dir ) + { + return $user->isSuperUser(); + } + + /** + * Determine whether the user can delete the docstore directory. + * + * @param User $user + * @param Customer $cust + * + * @return mixed + */ + public function deleteForCustomer( User $user, Customer $cust ): bool + { + return $user->isSuperUser() && $cust->docstoreCustomerFiles()->get()->isNotEmpty(); + } +} \ No newline at end of file diff --git a/app/Policies/DocstoreCustomerFilePolicy.php b/app/Policies/DocstoreCustomerFilePolicy.php new file mode 100644 index 000000000..5ebdac0d0 --- /dev/null +++ b/app/Policies/DocstoreCustomerFilePolicy.php @@ -0,0 +1,123 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Policies + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class DocstoreCustomerFilePolicy +{ + use HandlesAuthorization; + + /** + * Determine whether the user can download a docstore customer file. + * + * @param User $user + * @param DocstoreCustomerFile $file + * + * @return mixed + */ + public function download( User $user, DocstoreCustomerFile $file ): bool + { + return $user->isSuperUser() || ( $file->min_privs <= $user->privs() && request()->user()->custid === $file->customer->id ); + } + + /** + * Determine whether the user can view the docstore customer file. + * + * @param User $user + * @param DocstoreCustomerFile $file + * + * @return mixed + */ + public function view( User $user, DocstoreCustomerFile $file ): bool + { + return $user->isSuperUser() || ( $file->min_privs <= $user->privs() && request()->user()->custid === $file->customer->id ); + } + + /** + * Determine whether the user can create docstore customer files. + * + * @param User $user + * + * @return mixed + */ + public function create( User $user ): bool + { + return $user->isSuperUser(); + } + + /** + * Determine whether the user can get info on the docstore customer file. + * + * @param User $user + * @param DocstoreCustomerFile $file + * + * @return mixed + */ + public function info( User $user, DocstoreCustomerFile $file ): bool + { + return $user->isSuperUser(); + } + + /** + * Determine whether the user can update the docstore customer file. + * + * @param User $user + * @param DocstoreCustomerFile $file + * + * @return mixed + */ + public function update( User $user, DocstoreCustomerFile $file ): bool + { + return $user->isSuperUser(); + } + + /** + * Determine whether the user can delete the docstore customer file. + * + * @param User $user + * @param DocstoreCustomerFile $file + * + * @return mixed + */ + public function delete( User $user, DocstoreCustomerFile $file ): bool + { + return $user->isSuperUser() && $file->exists; + } +} \ No newline at end of file diff --git a/app/Policies/DocstoreDirectoryPolicy.php b/app/Policies/DocstoreDirectoryPolicy.php new file mode 100644 index 000000000..5cc445363 --- /dev/null +++ b/app/Policies/DocstoreDirectoryPolicy.php @@ -0,0 +1,83 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Policies + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class DocstoreDirectoryPolicy +{ + use HandlesAuthorization; + + /** + * Determine whether the user can create docstore directories. + * + * @param User $user + * + * @return mixed + */ + public function create( User $user ) + { + return $user->isSuperUser(); + } + + /** + * Determine whether the user can update the docstore directory. + * + * @param User $user + * @param DocstoreDirectory $dir + * + * @return mixed + */ + public function update( User $user, DocstoreDirectory $dir ) + { + return $user->isSuperUser(); + } + + /** + * Determine whether the user can delete the docstore directory. + * + * @param User $user + * @param DocstoreDirectory $dir + * + * @return mixed + */ + public function delete( User $user, DocstoreDirectory $dir ) + { + return $user->isSuperUser(); + } +} \ No newline at end of file diff --git a/app/Policies/DocstoreFilePolicy.php b/app/Policies/DocstoreFilePolicy.php new file mode 100644 index 000000000..d71264b91 --- /dev/null +++ b/app/Policies/DocstoreFilePolicy.php @@ -0,0 +1,123 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Policies + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class DocstoreFilePolicy +{ + use HandlesAuthorization; + + /** + * Determine whether the user can download a docstore file. + * + * @param User|null $user + * @param DocstoreFile $file + * + * @return mixed + */ + public function download( ?User $user, DocstoreFile $file ) + { + return $file->min_privs <= ( $user ? $user->privs() : User::AUTH_PUBLIC ); + } + + /** + * Determine whether the user can view the docstore file. + * + * @param User|null $user + * @param DocstoreFile $file + * + * @return mixed + */ + public function view( ?User $user, DocstoreFile $file ) + { + return $file->min_privs <= ( $user ? $user->privs() : User::AUTH_PUBLIC ); + } + + /** + * Determine whether the user can create docstore files. + * + * @param User $user + * + * @return mixed + */ + public function create( User $user ) + { + return $user->isSuperUser(); + } + + /** + * Determine whether the user can get info on the docstore file. + * + * @param User $user + * @param DocstoreFile $file + * + * @return mixed + */ + public function info( User $user, DocstoreFile $file ) + { + return $user->isSuperUser(); + } + + /** + * Determine whether the user can update the docstore file. + * + * @param User $user + * @param DocstoreFile $file + * + * @return mixed + */ + public function update( User $user, DocstoreFile $file ) + { + return $user->isSuperUser(); + } + + /** + * Determine whether the user can delete the docstore file. + * + * @param User $user + * @param DocstoreFile $file + * + * @return mixed + */ + public function delete( User $user, DocstoreFile $file ) + { + return $user->isSuperUser(); + } +} \ No newline at end of file diff --git a/app/Policies/DocstoreLogPolicy.php b/app/Policies/DocstoreLogPolicy.php new file mode 100644 index 000000000..d01ddd593 --- /dev/null +++ b/app/Policies/DocstoreLogPolicy.php @@ -0,0 +1,62 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Policies + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class DocstoreLogPolicy +{ + use HandlesAuthorization; + + /** + * @return null|true + */ + public function before( $user, $ability ) + { + if( $user->isSuperUser() ) { + return true; + } + } + + /** + * Determine whether the user can view any docstore logs. + * + * @param User $user + */ + public function viewAny( User $user ): void {} +} diff --git a/app/Policies/RouteServerFilterPolicy.php b/app/Policies/RouteServerFilterPolicy.php new file mode 100644 index 000000000..f0fa66b0a --- /dev/null +++ b/app/Policies/RouteServerFilterPolicy.php @@ -0,0 +1,128 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Http\Policies + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class RouteServerFilterPolicy +{ + use HandlesAuthorization; + + /** + * Super admins can do anything + * + * @param User $user + * @param $ability + * + * @return bool + * + * @throws + */ + public function before( User $user, $ability): bool + { + $privs = $user->privs(); + if( $privs !== User::AUTH_SUPERUSER ) { + $minAuth = User::AUTH_CUSTADMIN; + + if( in_array( explode('@', Route::getCurrentRoute()->getActionName() )[1], [ "view", "list" ] ) ){ + $minAuth = User::AUTH_CUSTUSER; + } + + if( $privs < $minAuth ) { + return false; + } + } + return true; + } + + + /** + * Determine whether the user can access the list of customers with filters + * + * @param User $user + * + * @return bool + * + * @throws + */ + public function checkListCustomers( User $user ): bool + { + return $user->isSuperUser(); + } + + + /** + * Determine whether the user can access to that route + * + * @param User $user + * @param Customer $cust + * + * @return bool + * + * @throws + */ + public function checkCustObject( User $user, Customer $cust ): bool + { + if( $cust->id !== $user->custid && !$user->isSuperUser() ){ + return false; + } + return $cust->routeServerClient(); + } + + /** + * Determine whether the user can access to that route + * + * @param User $user + * @param RouteServerFilter $rsf + * + * @return bool + * + * @throws + */ + public function checkRsfObject( User $user, RouteServerFilter $rsf ): bool + { + if( $rsf->customer_id !== $user->custid && !$user->isSuperUser() ){ + return false; + } + return $rsf->customer->routeServerClient(); + } +} \ No newline at end of file diff --git a/app/Policies/UserPolicy.php b/app/Policies/UserPolicy.php new file mode 100644 index 000000000..211579310 --- /dev/null +++ b/app/Policies/UserPolicy.php @@ -0,0 +1,79 @@ + + * @author Barry O'Donovan + * @category IXP + * @package IXP\Http\Policies + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ + +class UserPolicy +{ + use HandlesAuthorization; + + /** + * + * @param User $user + * + * @return mixed + */ + public function any( User $user ) + { + if( !$user->isCustUser() ){ + return true; + } + } + + /** + * Determine whether the user can update the docstore file. + * + * @param User $auth + * @param User $user + * + * @return mixed + */ + public function access( User $auth, User $user ) + { + $privs = $auth->privs(); + + if( $privs === User::AUTH_SUPERUSER ){ + return true; + } + + if( $privs === User::AUTH_CUSTADMIN && CustomerToUser::where( 'customer_id', $auth->custid )->where( 'user_id', $user->id )->exists() ){ + return true; + } + } +} \ No newline at end of file diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php new file mode 100644 index 000000000..2241dc0de --- /dev/null +++ b/app/Providers/AppServiceProvider.php @@ -0,0 +1,90 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Providers + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class AppServiceProvider extends ServiceProvider +{ + /** + * Bootstrap any application services. + * + * @return void + */ + public function boot(): void + { + view()->composer( [ 'telescope::layout' ], function ( $view ) { + $view->with( 'telescopeScriptVariables', [ + 'path' => config( 'telescope.url_path' ), + 'timezone' => config('app.timezone'), + 'recording' => !cache('telescope:pause-recording'), + ]); + }); + + Former::framework( TwitterBootstrap4::class ); + // observer for docstore directory + DocstoreDirectory::observe( DocstoreDirectoryObserver::class ); + // observer for docstore customer directory + DocstoreCustomerDirectory::observe( DocstoreCustomerDirectoryObserver::class ); + + Paginator::useBootstrap(); + } + + /** + * Register any application services. + * + * This service provider is a great spot to register your various container + * bindings with the application. As you can see, we are registering our + * "Registrar" implementation here. You can add your own bindings too! + * + * @return void + */ + #[\Override] + public function register(): void + { + $this->app->bind( + 'Illuminate\Contracts\Auth\Registrar', + 'IXP\Services\Registrar' + ); + } +} diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php new file mode 100644 index 000000000..b7327b910 --- /dev/null +++ b/app/Providers/AuthServiceProvider.php @@ -0,0 +1,96 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Providers + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class AuthServiceProvider extends ServiceProvider +{ + + /** + * The policy mappings for the application. + * + * @var array + */ + protected $policies = [ + // 'App\Model' => 'App\Policies\ModelPolicy', + ]; + + /** + * Register any authentication / authorization services. + * + * @return void + */ + public function boot() + { + $this->registerPolicies(); + + // autoload model policies + Gate::guessPolicyNamesUsing( function ( $modelClass ) { + return 'IXP\\Policies\\' . class_basename( $modelClass ) . 'Policy'; + }); + + Auth::extend('session', function ( $app, $name, $config ) { + $provider = $app[ 'auth' ]->createUserProvider( $config['provider'] ?? null ); + + $guard = new SessionGuard( $name, $provider, $app[ 'session.store' ], request() ); + + if( $config[ 'expire' ] ) { + $guard->setRememberDuration( $config[ 'expire' ] ); + } + + if( method_exists( $guard, 'setCookieJar' ) ) { + $guard->setCookieJar( $app['cookie'] ); + } + + if ( method_exists( $guard, 'setDispatcher' ) ) { + $guard->setDispatcher( $app['events'] ); + } + + if (method_exists( $guard, 'setRequest' ) ) { + $guard->setRequest( $app->refresh( 'request', $guard, 'setRequest' ) ); + } + + return $guard; + }); + + Auth::provider('eloquent', function ( $app, array $config ) { + return new EloquentUserProvider( $app['hash'], $config['model'] ); + }); + } +} diff --git a/app/Providers/BroadcastServiceProvider.php b/app/Providers/BroadcastServiceProvider.php new file mode 100644 index 000000000..ab18ed6b0 --- /dev/null +++ b/app/Providers/BroadcastServiceProvider.php @@ -0,0 +1,50 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Providers + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class BroadcastServiceProvider extends ServiceProvider +{ + /** + * Bootstrap any application services. + * + * @return void + */ + public function boot(): void + { + Broadcast::routes(); + /* require base_path('routes/channels.php'); */ + } +} \ No newline at end of file diff --git a/app/Providers/DiagnosticsServiceProvider.php b/app/Providers/DiagnosticsServiceProvider.php new file mode 100644 index 000000000..a3f2a6699 --- /dev/null +++ b/app/Providers/DiagnosticsServiceProvider.php @@ -0,0 +1,97 @@ + + * @author Laszlo Kiss + * @category IXP + * @package IXP\Providers + * @copyright Copyright (C) 2009 - 2024 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class DiagnosticsServiceProvider extends ServiceProvider +{ + protected $defer = true; + + + /** + * Bootstrap the application services. + * + * @return void + * + * @throws + */ + public function boot(): void + { + Route::group([ + 'middleware' => config( 'google2fa.enabled' ) + ? [ 'web' , 'auth' , '2fa' , 'assert.privilege:' . User::AUTH_SUPERUSER ] + : [ 'web' , 'auth', 'assert.privilege:' . User::AUTH_SUPERUSER ], + 'namespace' => 'IXP\Http\Controllers' ], function(){ + + Route::get( 'diagnostics/customer/{customer}', 'DiagnosticsController@customer')->name('diagnostics@customer' ); + Route::get( 'diagnostics/irrdb/{customer}', 'DiagnosticsController@irrdb' )->name('diagnostics@irrdb' ); + }); + + // we have a few rendering functions we want to include here: + // $this->app->make( Engine::class )->loadExtension( new GrapherRendererExtension(), [] ); + } + + /** + * Register the application services. + * + * @return void|Diagnostics + * + * @throws + */ + #[\Override] + public function register() + { + $this->app->singleton( + Diagnostics::class, function() { + return new Diagnostics; + }); + } + + /** + * Get the services provided by the provider. + * + * @return string[] + * + * @psalm-return list{Diagnostics::class} + */ + #[\Override] + public function provides(): array + { + return [ Diagnostics::class ]; + } +} \ No newline at end of file diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php new file mode 100644 index 000000000..bd585de24 --- /dev/null +++ b/app/Providers/EventServiceProvider.php @@ -0,0 +1,173 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Providers + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class EventServiceProvider extends ServiceProvider +{ + /** + * The subscriber classes to register. + * + * @var array + */ + protected $subscribe = [ + EmailOnChange::class, + ]; + + /** + * The event handler mappings for the application. + * + * @var array + */ + protected $listen = [ + + Login::class => [ + LoginSuccessful::class + ], + + Failed::class => [ + LoginFailed::class + ], + + BillingDetailsChanged::class => [ + \IXP\Listeners\Customer\BillingDetailsChanged::class + ], + + Added::class => [ + Changed::class, + ], + Deleted::class => [ + Changed::class, + ], + + UserCreated::class => [ + SendNewUserWelcomeEmail::class + ], + + UserAddedToCustomer::class => [ + SendUserAddedToCustomerWelcomeEmail::class + ], + + ForgotUsername::class => [ + \IXP\Listeners\Auth\ForgotUsername::class + ], + + ForgotPassword::class => [ + \IXP\Listeners\Auth\ForgotPassword::class + ], + + PasswordReset::class => [ + \IXP\Listeners\Auth\PasswordReset::class + ], + + LoginSucceeded::class => [ + Google2FALoginSucceeded::class + ], + + SocialiteWasCalled::class => [ + 'SocialiteProviders\\PeeringDB\\PeeringDBExtendSocialite@handle', + ], + + MeasurementComplete::class => [ + \IXP\Listeners\RipeAtlas\MeasurementComplete::class + ], + + ]; + + /** + * Register any other events for your application. + * + * @return void + */ + #[\Override] + public function boot(): void + { + // + } + + /** + * Determine if events and listeners should be automatically discovered. + * + * @return false + */ + #[\Override] + public function shouldDiscoverEvents() + { + return false; + } +} \ No newline at end of file diff --git a/app/Providers/FoilServiceProvider.php b/app/Providers/FoilServiceProvider.php new file mode 100644 index 000000000..7b58b4cb2 --- /dev/null +++ b/app/Providers/FoilServiceProvider.php @@ -0,0 +1,88 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Providers + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class FoilServiceProvider extends ServiceProvider +{ + /** + * Register the service provider. + * + * @return void + */ + #[\Override] + public function register(): void + { + $app = $this->app; + + $app->singleton( \Foil\Engine::class, function () { + $engine = \Foil\engine([ + 'folders' => config('view.paths'), + 'ext' => 'foil.php', + 'autoescape' => false, // enabling this is a serious performance hit + // e.g. >30secs to generate INEX's MRTG config + // vs. x without it + 'strict_variables' => true, // enabled as using undef'd vars is a programming error + 'alias' => 't' // $t is now shorthand for $this + ]); + + return $engine; + }); + + $app->resolving('view', function($view) use ( $app ) { + $engine = new Engine( $app->make( \Foil\Engine::class ) ); + + // we have a few rendering functions we want to include here: + $engine->engine()->loadExtension( new IXPFoilExtensions(), [ 'alerts' ] ); + $engine->engine()->loadExtension( new BirdFoilExtensions(), [] ); + + $view->addExtension('foil.php', 'foil', function() use ( $engine) { + return $engine; + }); + + $view->addExtension('foil.js', 'foil', function() use ( $engine) { + return $engine; + }); + }); + } +} \ No newline at end of file diff --git a/app/Providers/GrapherServiceProvider.php b/app/Providers/GrapherServiceProvider.php new file mode 100644 index 000000000..64f450ed5 --- /dev/null +++ b/app/Providers/GrapherServiceProvider.php @@ -0,0 +1,143 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Providers + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class GrapherServiceProvider extends ServiceProvider +{ + protected $defer = false; + + protected $commands = [ + EmailPortsWithCounts::class, + EmailPortUtilisation::class, + EmailTrafficDeltas::class, + GenerateConfiguration::class, + UploadStatsToDb::class, + ]; + + /** + * Bootstrap the application services. + * + * @return void + * + * @throws + */ + public function boot(): void + { + Route::group( ['middleware' => 'grapher', 'namespace' => 'IXP\Http\Controllers\Services', 'as' => 'grapher::', 'prefix' => 'grapher' ], function(){ + Route::get( 'ixp', 'Grapher@ixp' ); + Route::get( 'infrastructure', 'Grapher@infrastructure' ); + Route::get( 'vlan', 'Grapher@vlan' ); + Route::get( 'switch', 'Grapher@switch' ); + Route::get( 'location', 'Grapher@location' ); + Route::get( 'trunk', 'Grapher@trunk' ); + Route::get( 'corebundle', 'Grapher@coreBundle' ); + Route::get( 'physicalinterface', 'Grapher@physicalInterface' ); // individual member port + Route::get( 'virtualinterface', 'Grapher@virtualInterface' ); // member LAG (of physint's) + Route::get( 'customer', 'Grapher@customer' ); // member agg over all physint's + Route::get( 'vlaninterface', 'Grapher@vlanInterface' ); // member vlan interface + Route::get( 'p2p', 'Grapher@p2p' ); // member vlan interface + Route::get( 'latency', 'Grapher@latency' ); + }); + + Route::group(['middleware' => [ 'api/v4', 'assert.privilege:' . User::AUTH_SUPERUSER ], + 'namespace' => 'IXP\Http\Controllers\Services', 'as' => 'grapher::' ], function(){ + + Route::get( 'api/v4/grapher/mrtg-config', 'Grapher\Api@generateConfiguration' ); + Route::get( 'api/v4/grapher/config', 'Grapher\Api@generateConfiguration' ); + Route::post( 'api/v4/grapher/config', 'Grapher\Api@generateConfiguration' ); + }); + + // we have a few rendering functions we want to include here: + $this->app->make( Engine::class )->loadExtension( new GrapherRendererExtension(), [] ); + } + + /** + * Register the application services. + * + * @return void|Grapher + * + * @throws + */ + #[\Override] + public function register() + { + // make sure the config is okay: + foreach( $this->app[ 'config' ][ 'grapher' ][ 'backend' ] as $backend ) { + if( !isset( $this->app[ 'config' ][ 'grapher' ][ 'providers' ][ $backend ] ) ) { + throw new ConfigurationException( "Requested grapher backend ({$backend}) is not available" ); + } + } + + $this->app->singleton( + Grapher::class, function() { + return new Grapher; + }); + + $this->commands( $this->commands ); + } + + /** + * Get the services provided by the provider. + * + * @return string[] + * + * @psalm-return list{Grapher::class} + */ + #[\Override] + public function provides(): array + { + return [ Grapher::class ]; + } +} \ No newline at end of file diff --git a/app/Providers/HelpdeskServiceProvider.php b/app/Providers/HelpdeskServiceProvider.php new file mode 100644 index 000000000..6430751a8 --- /dev/null +++ b/app/Providers/HelpdeskServiceProvider.php @@ -0,0 +1,101 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Providers + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class HelpdeskServiceProvider extends ServiceProvider +{ + + protected $defer = true; + + protected $commands = [ + UpdateOrganisations::class + ]; + + /** + * Bootstrap the application services. + * + * @return void + */ + public function boot(): void {} + + /** + * Register the application services. + * + * @return void + */ + #[\Override] + public function register(): void + { + $this->app->singleton( Helpdesk::class, function( $app ) { + $backend = $app[ 'config' ][ 'helpdesk' ][ 'backend' ]; + + switch( $backend ) { + case 'none': + return false; + break; + case 'zendesk': + return new Zendesk( $app[ 'config' ][ 'helpdesk' ][ 'backends' ][ $backend ] ); + break; + default: + throw new ConfigurationException( 'Invalid, no or unimplemented helpdesk backend requested' ); + } + }); + + $this->commands( $this->commands ); + } + + + /** + * Get the services provided by the provider. + * + * @return string[] + * + * @psalm-return list{Helpdesk::class} + */ + #[\Override] + public function provides(): array + { + return [ Helpdesk::class ]; + } +} \ No newline at end of file diff --git a/app/Providers/HorizonServiceProvider.php b/app/Providers/HorizonServiceProvider.php new file mode 100644 index 000000000..7ef067200 --- /dev/null +++ b/app/Providers/HorizonServiceProvider.php @@ -0,0 +1,77 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Providers + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class HorizonServiceProvider extends HorizonApplicationServiceProvider +{ + /** + * Bootstrap any application services. + * + * @return void + */ + #[\Override] + public function boot(): void + { + parent::boot(); + + // Horizon::routeSmsNotificationsTo('15556667777'); + // Horizon::routeMailNotificationsTo('example@example.com'); + // Horizon::routeSlackNotificationsTo('slack-webhook-url', '#channel'); + + // Horizon::night(); + } + + /** + * Register the Horizon gate. + * + * This gate determines who can access Horizon in non-local environments. + * + * @return void + */ + #[\Override] + protected function gate(): void + { + Gate::define('viewHorizon', function ( $user ) { + /** @var User $us */ + $us = Auth::getUser(); + return Auth::check() && $us->isSuperUser(); + }); + } +} \ No newline at end of file diff --git a/app/Providers/IXFServiceProvider.php b/app/Providers/IXFServiceProvider.php new file mode 100644 index 000000000..6d05582f2 --- /dev/null +++ b/app/Providers/IXFServiceProvider.php @@ -0,0 +1,73 @@ + + * @category IXP + * @package IXP\Providers + * @copyright Copyright (C) 2009 - 2024 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class IXFServiceProvider extends ServiceProvider +{ + /** + * @var bool + */ + protected $defer = false; + + /** + * Bootstrap the application services. + * + * @return void + */ + public function boot(): void + { + Route::get( 'ix-f/ixp', function() { + return response()->json( + app()->make(IXF::class)->ixps() + ); + })->name('api-v4-ixf-ixs'); + } + + /** + * Register the application services. + * + * @return void + */ + #[\Override] + public function register(): void + { + $this->app->singleton( IXF::class, function( $app ) { + return new IXF(); + }); + } +} \ No newline at end of file diff --git a/app/Providers/IxpServiceProvider.php b/app/Providers/IxpServiceProvider.php new file mode 100644 index 000000000..048344742 --- /dev/null +++ b/app/Providers/IxpServiceProvider.php @@ -0,0 +1,75 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Providers + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class IxpServiceProvider extends ServiceProvider +{ + /** + * Bootstrap the application services. + * + * @return void + */ + public function boot():void{} + + /** + * Register the application services. + * + * @return void + */ + #[\Override] + public function register(): void + { + $this->app->resolving('view', function( $view ) { + View::composer('*', function($view) { + /** @var User $us */ + $us = Auth::getUser(); + + if( ( Auth::check() && $us->isSuperUser() ) || config( 'IXP_PHPUNIT_RUNNING', false ) ) { + // get an array of customer id => names + if( !( $customers = Cache::get( 'admin_home_customers' ) ) ) { + $customers = Customer::select( [ 'id', 'name' ] )->current()->orderBy( 'name' )->get()->keyBy( 'id' )->toArray(); + Cache::put( 'admin_home_customers', $customers, 3600 ); + } + $view->with( 'dd_customer_id_name', $customers ); + } + }); + }); + } +} \ No newline at end of file diff --git a/app/Providers/LookingGlassServiceProvider.php b/app/Providers/LookingGlassServiceProvider.php new file mode 100644 index 000000000..49af3dc1d --- /dev/null +++ b/app/Providers/LookingGlassServiceProvider.php @@ -0,0 +1,106 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Providers + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class LookingGlassServiceProvider extends ServiceProvider +{ + /** + * @var bool + */ + protected $defer = false; + + /** + * Bootstrap the application services. + * + * @return void + */ + public function boot(): void + { + if( config('ixp_fe.frontend.disabled.lg' ) ) { + return; + } + + Route::group( [ 'middleware' => 'lookingglass', 'namespace' => 'IXP\Http\Controllers\Services', + 'as' => 'lg::', 'prefix' => 'lg' ], function() { + + Route::get( '', 'LookingGlass@index' )->name('index'); + Route::get( '{handle}', 'LookingGlass@bgpSummary' )->name( 'bgp-sum' ); + Route::get( '{handle}/routes/table/{table}', 'LookingGlass@routesForTable' ); + Route::get( '{handle}/routes/protocol/{protocol}', 'LookingGlass@routesForProtocol' )->name( 'route-protocol' ); + Route::get( '{handle}/routes/export/{protocol}', 'LookingGlass@routesForExport' ); + Route::get( '{handle}/route-search', 'LookingGlass@routeSearch' )->name( 'route-search' ); + Route::get( '{handle}/route/{net}/{mask}/protocol/{protocol}', 'LookingGlass@routeProtocol' ); + Route::get( '{handle}/route/{net}/{mask}/table/{table}', 'LookingGlass@routeTable' ); + Route::get( '{handle}/route/{net}/{mask}/export/{protocol}', 'LookingGlass@routeExport' ); + }); + + Route::group( [ 'middleware' => 'lookingglass', 'namespace' => 'IXP\Http\Controllers\Services', + 'as' => 'lg-api::', 'prefix' => 'api/v4/lg' ], function() { + + Route::get( '{handle}/status', 'LookingGlass@status' )->name('status'); + Route::get( '{handle}/bgp-summary', 'LookingGlass@bgpSummaryApi' )->name('bgp-sum'); + }); + } + + /** + * Register the application services. + * + * @return void + */ + #[\Override] + public function register(): void + { + $this->app->singleton( LookingGlass::class, function( $app ) { + return new LookingGlass; + }); + } + + /** + * Get the services provided by the provider. + * + * @return string[] + * + * @psalm-return list{LookingGlass::class} + */ + #[\Override] + public function provides(): array + { + return [ LookingGlass::class ]; + } +} \ No newline at end of file diff --git a/app/Providers/ParsedownServiceProvider.php b/app/Providers/ParsedownServiceProvider.php new file mode 100644 index 000000000..f8eb7d7fb --- /dev/null +++ b/app/Providers/ParsedownServiceProvider.php @@ -0,0 +1,78 @@ +compiler()->directive('parsedown', function (string $expression = '') { + return ""; + }); + + $this->publishes([ + __DIR__ . '/../Support/parsedown.php' => config_path('parsedown.php'), + ]); + } + + /** + * @return BladeCompiler + * @psalm-suppress all + */ + protected function compiler(): BladeCompiler + { + return app('view') + ->getEngineResolver() + ->resolve('blade') + ->getCompiler(); + } + + /** + * @return void + */ + #[\Override] + public function register(): void + { + $this->app->singleton('parsedown', function () { + $parsedown = Parsedown::instance(); + + $parsedown->setBreaksEnabled( + Config::get('parsedown.breaks_enabled') + ); + + $parsedown->setMarkupEscaped( + Config::get('parsedown.markup_escaped') + ); + + $parsedown->setSafeMode( + Config::get('parsedown.safe_mode') + ); + + $parsedown->setUrlsLinked( + Config::get('parsedown.urls_linked') + ); + + return $parsedown; + }); + + $this->mergeConfigFrom(__DIR__ . '/../Support/parsedown.php', 'parsedown'); + } +} diff --git a/app/Providers/PeeringDbServiceProvider.php b/app/Providers/PeeringDbServiceProvider.php new file mode 100644 index 000000000..ccaf7f112 --- /dev/null +++ b/app/Providers/PeeringDbServiceProvider.php @@ -0,0 +1,81 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Providers + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class PeeringDbServiceProvider extends ServiceProvider +{ + /** + * @var bool + */ + protected $defer = false; + + /** + * Bootstrap the application services. + * + * @return void + */ + public function boot(): void + { + Route::get( 'peeringdb/ix', function() { + return response()->json( + app()->make(PeeringDb::class)->ixps() + ); + })->name('api-v4-peeringdb-ixs'); + + + Route::get( 'peeringdb/fac', function() { + return response()->json( + app()->make(PeeringDb::class)->facilities() + ); + })->name('api-v4-peeringdb-fac'); + } + + /** + * Register the application services. + * + * @return void + */ + #[\Override] + public function register(): void + { + $this->app->singleton( PeeringDb::class, function( $app ) { + return new PeeringDb(); + }); + } +} \ No newline at end of file diff --git a/app/Providers/PurifierServiceProvider.php b/app/Providers/PurifierServiceProvider.php new file mode 100644 index 000000000..f522054dd --- /dev/null +++ b/app/Providers/PurifierServiceProvider.php @@ -0,0 +1,74 @@ +setupConfig(); + } + + /** + * Setup the config. + * + * @return void + */ + protected function setupConfig(): void + { + $source = config_path( 'purifier.php' ); + if ($this->app instanceof LaravelApplication && $this->app->runningInConsole()) { + $this->publishes([$source => config_path('purifier.php')]); + } + $this->mergeConfigFrom($source, 'purifier'); + } + + + /** + * Register the service provider. + * + * @return void + */ + #[\Override] + public function register() + { + $this->app->singleton('purifier', function (Container $app) { + return new Purifier($app['files'], $app['config']); + }); + + $this->app->alias('purifier', Purifier::class); + } + + /** + * Get the services provided by the provider. + * + * @return string[] + * + * @psalm-return list{'purifier'} + */ + #[\Override] + public function provides() + { + return ['purifier']; + } +} diff --git a/app/Providers/RipeAtlasProvider.php b/app/Providers/RipeAtlasProvider.php new file mode 100644 index 000000000..19a220567 --- /dev/null +++ b/app/Providers/RipeAtlasProvider.php @@ -0,0 +1,52 @@ +app->singleton( 'IXP\Services\ApiCall', function( $app ) { + return new ApiCall(); + }); + } +} \ No newline at end of file diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php new file mode 100644 index 000000000..91197fa02 --- /dev/null +++ b/app/Providers/RouteServiceProvider.php @@ -0,0 +1,259 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Providers + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class RouteServiceProvider extends ServiceProvider +{ + /** + * The path to the "home" route for your application. + * + * Typically, users are redirected here after authentication. + * + * @var string + */ + public const HOME = ''; + + /** + * The controller namespace for the application. + * + * When present, controller route declarations will automatically be prefixed with this namespace. + * + * @var string|null + */ + protected $namespace = 'IXP\Http\Controllers'; + + /** + * Define your route model bindings, pattern filters, and other route configuration. + * + * @return void + */ + #[\Override] + public function boot(): void + { + parent::boot(); + } + + /** + * Define the routes for the application. + * + * @return void + */ + public function map(): void + { + $this->mapWebRoutes(); + $this->mapWebEloquent2FrontendRoutes(); + $this->mapWebAuthRoutes(); + $this->mapWebAuthSuperuserRoutes(); + $this->mapApiExternalAuthSuperuserRoutes(); + $this->mapApiV4Routes(); + $this->mapApiV4AuthRoutes(); + $this->mapApiAuthSuperuserRoutes(); + $this->mapPublicApiRoutes(); + + // aliases that need to be deprecated: + require base_path('routes/apiv1-aliases.php' ); + } + + /** + * Define the "web" routes for the application. + * + * These routes all receive session state, CSRF protection, etc. + * + * @return void + */ + protected function mapWebRoutes(): void + { + Route::group([ + 'middleware' => [ 'web', '2fa' ], + 'namespace' => $this->namespace, + ], function () { + require base_path('routes/web.php' ); + }); + } + + /** + * Define the "web" routes using Eloquent2Frontend for the application. + * + * These routes all receive session state, CSRF protection, etc. + * + * @return void + */ + protected function mapWebEloquent2FrontendRoutes(): void + { + Route::group([ + 'middleware' => config( 'google2fa.enabled' ) ? ['e2frontend', '2fa'] : ['e2frontend'], + 'namespace' => $this->namespace, + ], function () { + require base_path('routes/web-eloquent2frontend.php'); + }); + } + + /** + * Define the "web" routes for the application **WHICH REQUIRE ANY AUTHENTICATION**. + * + * These routes all receive session state, CSRF protection, etc and require an authenticated user. + * + * @return void + */ + protected function mapWebAuthRoutes(): void + { + Route::group([ + 'middleware' => config( 'google2fa.enabled' ) ? [ 'web', 'auth', '2fa' ] : [ 'web', 'auth' ], + 'namespace' => $this->namespace, + ], function () { + require base_path('routes/web-auth.php'); + }); + } + + /** + * Define the "web" routes for the application **WHICH REQUIRE AUTHENTICATION**. + * + * These routes all receive session state, CSRF protection, etc and require an authenticated user. + * + * @return void + */ + protected function mapWebAuthSuperuserRoutes(): void + { + Route::group([ + 'middleware' => config( 'google2fa.enabled' ) ? [ 'web' , 'auth' , '2fa' , 'assert.privilege:' . User::AUTH_SUPERUSER ] : [ 'web' , 'auth', 'assert.privilege:' . User::AUTH_SUPERUSER ], + 'namespace' => $this->namespace, + ], function () { + require base_path('routes/web-auth-superuser.php' ); + }); + } + + /** + * Define the "api" routes for the application. + * + * These routes are typically stateless. + * + * @return void + */ + protected function mapApiV4Routes(): void + { + Route::group([ + 'middleware' => [ 'web', 'public/api/v4' ], + 'namespace' => $this->namespace . '\\Api\\V4', + 'prefix' => 'api/v4', + ], function () { + require base_path('routes/apiv4.php'); + }); + } + + /** + * Define the "api" routes for the application. + * + * These routes are typically stateless. + * + * @return void + */ + protected function mapApiV4AuthRoutes(): void + { + Route::group([ + 'middleware' => [ 'web', 'api/v4', 'auth' ], + 'namespace' => $this->namespace . '\\Api\\V4', + 'prefix' => 'api/v4', + ], function () { + require base_path('routes/apiv4-auth.php'); + }); + } + + /** + * Define the "api" routes for the application. + * + * These routes are typically stateless. + * + * @return void + */ + protected function mapApiAuthSuperuserRoutes(): void + { + Route::group([ + 'middleware' => [ + 'web', + 'api/v4', + 'assert.privilege:' . User::AUTH_SUPERUSER + ], + 'namespace' => $this->namespace . '\\Api\\V4', + 'prefix' => 'api/v4', + ], function () { + require base_path('routes/apiv4-auth-superuser.php'); + }); + } + + + /** + * Define the "api" routes for the application. + * + * These routes are typically stateless. + * + * @return void + */ + protected function mapApiExternalAuthSuperuserRoutes(): void + { + Route::group([ + 'middleware' => [ + 'api/v4', + 'assert.privilege:' . User::AUTH_SUPERUSER + ], + 'namespace' => $this->namespace . '\\Api\\V4', + 'prefix' => 'api/v4', + ], function () { + require base_path('routes/apiv4-ext-auth-superuser.php'); + }); + } + + + /** + * Define the "web" routes for the application. + * + * These routes all receive session state, CSRF protection, etc. + * + * @return void + */ + protected function mapPublicApiRoutes() + { + Route::group([ + 'middleware' => 'publicapi', + 'namespace' => $this->namespace . '\\Api\\V4', + 'prefix' => 'api/v4/public', + ], function ($router) { + require base_path('routes/publicapi.php'); + }); + } +} diff --git a/app/Providers/TelescopeServiceProvider.php b/app/Providers/TelescopeServiceProvider.php new file mode 100644 index 000000000..f96ad5968 --- /dev/null +++ b/app/Providers/TelescopeServiceProvider.php @@ -0,0 +1,103 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Providers + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class TelescopeServiceProvider extends TelescopeApplicationServiceProvider +{ + /** + * Register any application services. + * + * @return void + */ + #[\Override] + public function register(): void + { + // Telescope::night(); + + $this->hideSensitiveRequestDetails(); + + Telescope::filter( function ( IncomingEntry $entry ) { + /** @psalm-suppress UndefinedInterfaceMethod - Telescope package provides the isLocal method */ + if ($this->app->isLocal()) { + return true; + } + + return $entry->isReportableException() || + $entry->isFailedJob() || + $entry->isScheduledTask() || + $entry->hasMonitoredTag(); + }); + } + + /** + * Prevent sensitive request details from being logged by Telescope. + * + * @return void + */ + protected function hideSensitiveRequestDetails(): void + { + /** @psalm-suppress UndefinedInterfaceMethod - Telescope package provides the isLocal method */ + if( $this->app->isLocal() ) { + return; + } + + Telescope::hideRequestParameters(['_token']); + + Telescope::hideRequestHeaders([ + 'cookie', + 'x-csrf-token', + 'x-xsrf-token', + ]); + } + + /** + * Register the Telescope gate. + * + * This gate determines who can access Telescope in non-local environments. + * + * @return void + */ + #[\Override] + protected function gate(): void + { + Gate::define('viewTelescope', function ( $user ) { + return config( 'app.env' ) !== 'testing' && $user->isSuperUser(); + }); + } +} diff --git a/app/Rules/IPv4Cidr.php b/app/Rules/IPv4Cidr.php new file mode 100644 index 000000000..217103675 --- /dev/null +++ b/app/Rules/IPv4Cidr.php @@ -0,0 +1,86 @@ + + * @author Yann Robin + * @category Rules + * @package IXP\Rules + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class IPv4Cidr implements Rule +{ + /** + * Determine if the validation rule passes. + * + * @param string $attribute + * @param mixed $value + * + * @return bool + */ + #[\Override] + public function passes( $attribute, $value): bool + { + if( !strpos( $value, '/' ) ) { + return false; + } + + $parts = explode( '/', $value ); + + if( sizeof( $parts ) !== 2 ) { + return false; + } + + // mask: + $mask = (int)$parts[1]; + if( $mask < 0 || $mask > 32 ) { + return false; + } + + if( filter_var( $parts[0], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) === false ) { + return false; + } + + return true; + } + + /** + * Get the validation error message. + * + * @return string + * + * @psalm-return 'Invalid IPv4 address in CIDR format (e.g. 192.0.2.0/24).' + */ + #[\Override] + public function message(): string + { + return 'Invalid IPv4 address in CIDR format (e.g. 192.0.2.0/24).'; + } +} \ No newline at end of file diff --git a/app/Rules/IPv6Cidr.php b/app/Rules/IPv6Cidr.php new file mode 100644 index 000000000..6b68a42b6 --- /dev/null +++ b/app/Rules/IPv6Cidr.php @@ -0,0 +1,95 @@ + + * @author Yann Robin + * @category Rules + * @package IXP\Rules + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class IPv6Cidr implements Rule +{ + /** + * Create a new rule instance. + * + * @return void + */ + public function __construct() + { + // + } + + /** + * Determine if the validation rule passes. + * + * @param string $attribute + * @param mixed $value + * @return bool + */ + #[\Override] + public function passes( $attribute, $value ): bool + { + if( !strpos( $value, '/' ) ) { + return false; + } + + $parts = explode( '/', $value ); + + if( sizeof( $parts ) !== 2 ) { + return false; + } + + // mask: + $mask = (int)$parts[1]; + if( $mask < 0 || $mask > 128 ) { + return false; + } + + if( filter_var( $parts[0], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 ) === false ) { + return false; + } + + return true; + } + + /** + * Get the validation error message. + * + * @return string + * + * @psalm-return 'Invalid IPv6 address in CIDR format (e.g. 2001:db8:10::/48).' + */ + #[\Override] + public function message(): string + { + return 'Invalid IPv6 address in CIDR format (e.g. 2001:db8:10::/48).'; + } +} \ No newline at end of file diff --git a/app/Rules/IdnValidate.php b/app/Rules/IdnValidate.php new file mode 100644 index 000000000..d50cbbe87 --- /dev/null +++ b/app/Rules/IdnValidate.php @@ -0,0 +1,74 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Rules + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class IdnValidate implements Rule +{ + /** + * Create a new rule instance. + * + * @return void + */ + public function __construct(){} + + /** + * Determine if the validation rule passes. + * + * @param string $attribute + * @param mixed $value + * + * @return bool + */ + #[\Override] + public function passes( $attribute, $value ): bool + { + $foo = idn_to_ascii ( $value, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46 ); + return ( filter_var( $foo, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME ) ) ? true : false; + } + + /** + * Get the validation error message. + * + * @return string + * + * @psalm-return 'The :attribute is not valid.' + */ + #[\Override] + public function message(): string + { + return 'The :attribute is not valid.'; + } +} \ No newline at end of file diff --git a/app/Rules/Ipv4SubnetSize.php b/app/Rules/Ipv4SubnetSize.php new file mode 100644 index 000000000..df0cc143f --- /dev/null +++ b/app/Rules/Ipv4SubnetSize.php @@ -0,0 +1,73 @@ + + * @author Yann Robin + * @category Rules + * @package IXP\Rules + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Ipv4SubnetSize implements Rule +{ + /** + * Determine if the validation rule passes. + * + * @param string $attribute + * @param mixed $value + * + * @return bool + */ + #[\Override] + public function passes( $attribute, $value ): bool + { + $parts = explode( '/', $value ); + + // mask: + $mask = (int)$parts[1]; + + if( $mask > config( "ixp.irrdb.min_v4_subnet_size" ) ) { + return false; + } + + return true; + } + + /** + * Get the validation error message. + * + * @return string + */ + #[\Override] + public function message(): string + { + return 'Invalid subnet, must be minimum ' . config( "ixp.irrdb.min_v4_subnet_size" ); + } +} \ No newline at end of file diff --git a/app/Rules/Ipv6SubnetSize.php b/app/Rules/Ipv6SubnetSize.php new file mode 100644 index 000000000..674e420cf --- /dev/null +++ b/app/Rules/Ipv6SubnetSize.php @@ -0,0 +1,72 @@ + + * @author Yann Robin + * @category Rules + * @package IXP\Rules + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Ipv6SubnetSize implements Rule +{ + /** + * Determine if the validation rule passes. + * + * @param string $attribute + * @param mixed $value + * + * @return bool + */ + #[\Override] + public function passes( $attribute, $value ): bool + { + $parts = explode( '/', $value ); + + // mask: + $mask = (int)$parts[1]; + if( $mask > config( "ixp.irrdb.min_v6_subnet_size" ) ) { + return false; + } + + return true; + } + + /** + * Get the validation error message. + * + * @return string + */ + #[\Override] + public function message(): string + { + return 'Invalid subnet, must be minimum ' . config( "ixp.irrdb.min_v6_subnet_size" ); + } +} \ No newline at end of file diff --git a/app/Services/Auth/EloquentUserProvider.php b/app/Services/Auth/EloquentUserProvider.php new file mode 100644 index 000000000..b6c98c949 --- /dev/null +++ b/app/Services/Auth/EloquentUserProvider.php @@ -0,0 +1,218 @@ + + * @author Yann Robin + * @package IXP\Services\Auth + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class EloquentUserProvider implements IlluminateUserProvider +{ + /** + * @var Hasher + */ + protected $hasher; + + /** + * @var string + */ + protected $model; + + /** + * @param Hasher $hasher + * @param $model + */ + public function __construct( Hasher $hasher, $model) + { + $this->hasher = $hasher; + $this->model = $model; + } + /** + * Retrieve a user by their unique identifier and "remember me" token. + * + * @param mixed $identifier + * @param string $token + * + * @return Authenticatable|null + * + * @throws + */ + #[\Override] + public function retrieveByToken( $identifier, $token ): ?Authenticatable + { + $urt = UserRememberToken::where( [ 'user_id' => $identifier ] )->where( [ 'token' => $token ] )->first(); + + if( !$urt ) { + return null; + } + + return $urt->expires > now() ? $urt->user : null; + } + + /** + * Add a new user remember token for a "remember me" session. + * + * @param User|Authenticatable $user + * + * @return UserRememberToken + * + * @throws + */ + public function addRememberToken( $user ): UserRememberToken + { + $browser = new BrowserDetection(); + + return UserRememberToken::create([ + 'user_id' => $user->id, + 'token' => Str::random(60), + 'device' => $browser->getPlatform() . " " . $browser->getPlatformVersion(true) . " / " . $browser->getName() . " " . $browser->getVersion() , + 'ip' => IpAddress::getIp(), + 'expires' => now()->addMinutes( config('auth.guards.web.expire', 60) ), + ]); + } + + /** + * Purge old or expired "remember me" tokens. + * + * @param User $user + * + * @throws + */ + public function purgeExpiredRememberTokens( User $user ): void + { + UserRememberToken::where( 'user_id', $user->id ) + ->where( 'expires', '<=', now()->format( 'Y-m-d H:i:s' ) ) + ->delete(); + } + + /** + * Retrieve a user by their unique identifier. + * + * @param mixed $identifier + * + * @return Authenticatable|null + */ + #[\Override] + public function retrieveById( $identifier ): ?Authenticatable + { + return $this->model::find( $identifier ); + } + + /** + * We do not need this as we have a multi remember token per user + * + * @param Authenticatable $user + * @param string $token + * + * @return void + */ + #[\Override] + public function updateRememberToken( Authenticatable $user, $token ){} + + /** + * Retrieve a user by the given credentials. + * + * @param array $credentials + * + * @return Authenticatable|null + */ + #[\Override] + public function retrieveByCredentials( array $credentials ): ?Authenticatable + { + // First we will add each credential element to the query as a where clause. + // Then we can execute the query and, if we found a user, return it in a + // Eloquent User "model" that will be utilized by the Guard instances. + $query = $this->model::query(); + + foreach( $credentials as $key => $value ) { + if( Str::contains( $key, 'password' ) ) { + continue; + } + + if( is_array($value) || $value instanceof Arrayable ) { + $query->whereIn( $key, $value ); + } else { + $query->where( $key, $value ); + } + } + + return $query->first(); + } + + /** + * Validate a user against the given credentials. + * + * @param Authenticatable $user + * @param array $credentials + * + * @return bool + */ + #[\Override] + public function validateCredentials( Authenticatable $user, array $credentials ): bool + { + return $this->hasher->check( $credentials['password'], $user->getAuthPassword() ); + } + + + /** + * Rehash the user's password if required and supported. + * + * @param \Illuminate\Contracts\Auth\Authenticatable $user + * @param array $credentials + * @param bool $force + * @return void + */ + #[\Override] + public function rehashPasswordIfRequired( Authenticatable $user, array $credentials, bool $force = false ) + { + // TODO: Implement rehashPasswordIfRequired() method. + } +} \ No newline at end of file diff --git a/app/Services/Auth/SessionGuard.php b/app/Services/Auth/SessionGuard.php new file mode 100644 index 000000000..4b1c3a215 --- /dev/null +++ b/app/Services/Auth/SessionGuard.php @@ -0,0 +1,214 @@ + + * @author Yann Robin + * @package IXP\Services\Auth + * @copyright Copyright (C) 2009 - 2019 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class SessionGuard extends BaseGuard +{ + /** + * @var UserRememberToken; + */ + protected $userRememberToken; + + /** + * Get the currently authenticated user. + * + * Overrides Laravel's default version which is a single shared token so we + * can have a per-browser / device token. + * + * We need to override so we can /immediately/ log out users if the current user session + * was deleted (by the user) from another session. + * + * @return User|AuthenticatableContract|void|null + */ + #[\Override] + public function user() + { + if( $this->loggedOut ){ + return; + } + + // If we've already retrieved the user for the current request we can just + // return it back immediately. We do not want to fetch the user data on + // every call to this method because that would be tremendously slow. + if (! is_null( $this->user ) ) { + return $this->user; + } + + $id = $this->session->get( $this->getName() ); + $recaller = $this->recaller(); + + // First we will try to load the user using the identifier in the session if + // one exists. Otherwise we will check for a "remember me" cookie in this + // request, and if one exists, attempt to retrieve the user using that. + if (! is_null( $id ) && $this->user = $this->provider->retrieveById( $id ) ) { + + // User has local session - make sure it hasn't been invalidated if a remember me cookie exists. + // This is the bit we added to allow a user to invalidate other sessions via the UI. + if( $recaller ) { + $urt = UserRememberToken::whereToken( $recaller->token() )->first(); + + if( !$urt || $urt->expired() ) { + $this->logout(); + return null; + } + } + + $this->fireAuthenticatedEvent( $this->user ); + } + + // If the user is null, but we decrypt a "recaller" cookie we can attempt to + // pull the user data on that cookie which serves as a remember cookie on + // the application. Once we have a user we can return it to the caller. + if( is_null( $this->user ) && ! is_null( $recaller ) ) { + $this->user = $this->userFromRecaller( $recaller ); + + if( $this->user ) { + $this->updateSession( $this->user->getAuthIdentifier() ); + + // Get the UserRememberToken and, if 2fa has been completed, don't redo it: + if( $this->user->user2FA && $this->user->user2FA->enabled ) { + $urt = UserRememberToken::whereToken( $recaller->token() )->first(); + + if( $urt && $urt->is_2fa_complete ) { + $authenticator = new GoogleAuthenticator( $this->request ); + $authenticator->login(); + } + } + + $this->fireLoginEvent( $this->user, true); + } + } + return $this->user; + } + + + /** + * Create a "remember me" token for the user. + * + * This function is called from the parent SessionGuard's login() method as follows: + * + * ``` + * // If the user should be permanently "remembered" by the application we will + * // queue a permanent cookie that contains the encrypted copy of the user + * // identifier. We will then decrypt this later to retrieve the users. + * if ($remember) { + * $this->ensureRememberTokenIsSet($user); + * $this->queueRecallerCookie($user); + * } + * ``` + * + * Overrides Laravel's default version which is a single shared token so we + * can have a per-browser / device token. + * + * @param AuthenticatableContract $user + * + * @return void + * @psalm-suppress UndefinedInterfaceMethod + */ + #[\Override] + protected function ensureRememberTokenIsSet( AuthenticatableContract $user ): void + { + if( !$this->userRememberToken ) { + // The addRememberToken() creates a new UserRememberToken for the user + $this->userRememberToken = $this->provider->addRememberToken($user); + $this->provider->purgeExpiredRememberTokens( $user ); + } + } + + /** + * Queue the recaller cookie into the cookie jar + * + * We need to override this function as the parent version calls `$user->getRememberToken()` + * which, in Laravel, is a single token. In our case we want a per-browser / session token. + * + * @param AuthenticatableContract $user + * + * @return void + * + * @throws GeneralException + */ + #[\Override] + protected function queueRecallerCookie( AuthenticatableContract $user ): void + { + // we shouldn't have called this function unless a UserRememberToken has been created + // (or so barryo's understanding as of 20200127). So we'll throw an exception if that happens + // and fix this then. + if( !$this->userRememberToken ) { + throw new GeneralException( 'UserRememberToken not already created in queueRecallerCookie() ??' ); + } + + $this->getCookieJar()->queue($this->createRecaller( + $user->getAuthIdentifier() . '|' . $this->userRememberToken->token . '|' . $user->getAuthPassword() + )); + } + + /** + * Refresh the "remember me" token for the user. + * + * We need to override this function and use it to delete the user's remember token + * as our system does not have a single token (where cycling the single token would + * be sufficient). + * + * @param AuthenticatableContract $user + * + * @return void + */ + #[\Override] + protected function cycleRememberToken( AuthenticatableContract $user ): void + { + if( $this->recaller() && $this->recaller()->token() ) { + foreach( $this->user()->userRememberTokens as $urt ) { + if( $urt->token === $this->recaller()->token() ) { + $urt->delete(); + break; + } + } + } + } +} \ No newline at end of file diff --git a/app/Services/Diagnostics.php b/app/Services/Diagnostics.php new file mode 100644 index 000000000..68b3e06ad --- /dev/null +++ b/app/Services/Diagnostics.php @@ -0,0 +1,138 @@ + + * @author Laszlo Kiss + * @category IXP + * @package IXP\Services + * @copyright Copyright (C) 2009 - 2024 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Diagnostics +{ + + /** + * @param Customer $customer + * @return DiagnosticResultSet + */ + public function getCustomerDiagnostics(Customer $customer): DiagnosticResultSet + { + $d = new CustomerDiagnosticSuite( $customer ); + return $d->run()->results(); + } + + /** + * @param Customer $customer + * @return DiagnosticResultSet + */ + public function getCustomerIrrdbDiagnostics(Customer $customer): DiagnosticResultSet + { + $d = new IrrdbDiagnosticSuite( $customer ); + return $d->run()->results(); + } + + /** + * @param VirtualInterface $vi + * @return DiagnosticResultSet + */ + public function getVirtualInterfaceDiagnostics( VirtualInterface $vi ): DiagnosticResultSet + { + $d = new VirtualInterfaceDiagnosticSuite( $vi ); + return $d->run()->results(); + } + + /** + * @param PhysicalInterface $pi + * @return DiagnosticResultSet + */ + public function getPhysicalInterfaceDiagnostics(PhysicalInterface $pi): DiagnosticResultSet + { + $d = new PhysicalInterfaceDiagnosticSuite( $pi ); + return $d->run()->results(); + } + + /** + * @param PhysicalInterface $pi + * @return DiagnosticResultSet + */ + public function getTransceiverDiagnostics(PhysicalInterface $pi): DiagnosticResultSet + { + $d = new TransceiverDiagnosticSuite( $pi ); + return $d->run()->results(); + } + + /** + * @param VlanInterface $vli + * @param int $protocol + * @return DiagnosticResultSet + * @throws \Illuminate\Contracts\Container\BindingResolutionException + */ + public function getRouterBgpSessionsDiagnostics(VlanInterface $vli, int $protocol): DiagnosticResultSet + { + $d = new RouterBgpSessionsDiagnosticSuite( $vli, $protocol ); + return $d->run()->results(); + } + + /** + * @param VlanInterface $vli + * @return DiagnosticResultSet + * @throws \Illuminate\Contracts\Container\BindingResolutionException + */ + public function getVlanInterfaceL2Diagnostics(VlanInterface $vli): DiagnosticResultSet + { + $d = new VlanInterfaceL2DiagnosticSuite( $vli ); + return $d->run()->results(); + } + + /** + * @param VlanInterface $vli + * @param int $protocol + * @return DiagnosticResultSet + * @throws \Illuminate\Contracts\Container\BindingResolutionException + */ + public function getVlanInterfaceL3Diagnostics(VlanInterface $vli, int $protocol): DiagnosticResultSet + { + $d = new VlanInterfaceL3DiagnosticSuite( $vli, $protocol ); + return $d->run()->results(); + } + +} \ No newline at end of file diff --git a/app/Services/Diagnostics/DiagnosticResult.php b/app/Services/Diagnostics/DiagnosticResult.php new file mode 100644 index 000000000..986a56290 --- /dev/null +++ b/app/Services/Diagnostics/DiagnosticResult.php @@ -0,0 +1,98 @@ + + * @author Laszlo Kiss + * @category IXP + * @package IXP\Services\Grapher + * @copyright Copyright (C) 2009 - 2024 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ + +class DiagnosticResult +{ + public const int TYPE_FATAL = 1000; + public const int TYPE_ERROR = 900; + public const int TYPE_WARN = 800; + public const int TYPE_GOOD = 600; + public const int TYPE_INFO = 500; + public const int TYPE_UNKNOWN = 400; + public const int TYPE_DEBUG = 200; + public const int TYPE_TRACE = 100; + + public static array $RESULT_TYPES_TEXT = [ + self::TYPE_FATAL => 'Fatal', + self::TYPE_ERROR => 'Error', + self::TYPE_WARN => 'Warning', + self::TYPE_INFO => 'Info', + self::TYPE_UNKNOWN => 'Unknown', + self::TYPE_GOOD => 'Good', + self::TYPE_DEBUG => 'Debug', + self::TYPE_TRACE => 'Trace', + ]; + + public static array $RESULT_TYPES_ICON_STYLE = [ + self::TYPE_FATAL => 'tw-bg-red-50 tw-text-red-700 tw-ring-red-600/10', + self::TYPE_ERROR => 'tw-bg-pink-50 tw-text-pink-700 tw-ring-pink-700/10', + self::TYPE_WARN => 'tw-bg-yellow-50 -text-yellow-800 tw-ring-yellow-600/20', + self::TYPE_INFO => 'tw-bg-blue-50 tw-text-blue-700 tw-ring-blue-700/10', + self::TYPE_UNKNOWN => 'tw-bg-gray-700 tw-text-gray-100 tw-ring-gray-800/50', + self::TYPE_GOOD => 'tw-bg-green-50 tw-text-green-700 tw-ring-green-600/20', + self::TYPE_DEBUG => 'tw-bg-gray-50 tw-text-gray-600 tw-ring-gray-500/10', + self::TYPE_TRACE => 'tw-bg-gray-100 tw-text-gray-800 tw-ring-gray-800/10', + ]; + + + public function __construct( + public string $name, + public int $result, + public ?string $narrative = null, + public int $auth = User::AUTH_SUPERUSER, // whether the diagnostic result should be visible to the member + public ?string $narrativeHtml = null, + public ?string $infoBadge = null, + ) { } + + public function iconStyle(): string { + return "tw-inline-flex tw-items-center tw-rounded-md tw-ml-2 tw-px-2 tw-py-1 tw-text-xs tw-font-medium " + . self::$RESULT_TYPES_ICON_STYLE[$this->result] + . " tw-ring-1 tw-ring-inset"; + } + + public function result(): string { + return self::$RESULT_TYPES_TEXT[$this->result]; + } + + + public function badge(): string { + return "iconStyle() . "\">" . $this->result() . ""; + } + + +} \ No newline at end of file diff --git a/app/Services/Diagnostics/DiagnosticResultSet.php b/app/Services/Diagnostics/DiagnosticResultSet.php new file mode 100644 index 000000000..c143446de --- /dev/null +++ b/app/Services/Diagnostics/DiagnosticResultSet.php @@ -0,0 +1,90 @@ + + * @author Laszlo Kiss + * @category IXP + * @package IXP\Services\Grapher + * @copyright Copyright (C) 2009 - 2024 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ + +class DiagnosticResultSet +{ + public DiagnosticSuite $suite; + + /** @var DiagnosticResult[] */ + public array $results = []; + + /** @var DiagnosticResultSet[] */ + protected array $subsets = []; + + + /** + * @param DiagnosticResult|null $result + */ + public function __construct( DiagnosticSuite $suite, ?DiagnosticResult $result = null ) { + $this->suite = $suite; + + if ($result !== null) { + $this->results[] = $result; + } + } + + /** + * Adds a diagnostic result to the result set. + * + * @param DiagnosticResult|DiagnosticResult[] $result The diagnostic result to add. + * + * @return static This diagnostic result set. + */ + public function add( DiagnosticResult|array $result ): static { + + $this->results = array_merge( + $this->results, + is_array($result) ? $result : [$result] + ); + + return $this; + } + + + public function addSubset(DiagnosticResultSet $subset): void { + $this->subsets[] = $subset; + } + + /** + * @return DiagnosticResultSet[] + */ + public function subsets(): array { + return $this->subsets; + } + + + +} \ No newline at end of file diff --git a/app/Services/Diagnostics/DiagnosticSuite.php b/app/Services/Diagnostics/DiagnosticSuite.php new file mode 100644 index 000000000..b7c896d77 --- /dev/null +++ b/app/Services/Diagnostics/DiagnosticSuite.php @@ -0,0 +1,74 @@ + + * @author Laszlo Kiss + * @category IXP + * @package IXP\Services\Grapher + * @copyright Copyright (C) 2009 - 2024 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +abstract class DiagnosticSuite +{ + protected string $name = 'Err: Suite name not set!'; + protected string $description = 'Err: No suite description set!'; + protected string $type = 'Err: No suite type set!'; + protected ?string $link = null; + + protected DiagnosticResultSet $results; + + + public function __construct() { + $this->results = new DiagnosticResultSet( $this ); + } + + + /** + * @return DiagnosticResultSet + */ + public function results(): DiagnosticResultSet { + return $this->results; + } + + public function name(): string { + return $this->name; + } + + public function description(): string { + return $this->description; + } + + public function type(): string { + return $this->type; + } + + public function link(): ?string { + return $this->link; + } + +} \ No newline at end of file diff --git a/app/Services/Diagnostics/Exception.php b/app/Services/Diagnostics/Exception.php new file mode 100644 index 000000000..ef6e84f0d --- /dev/null +++ b/app/Services/Diagnostics/Exception.php @@ -0,0 +1,26 @@ + + * @author Laszlo Kiss + * @category IXP + * @package IXP\Services\Diagnostics + * @copyright Copyright (C) 2009 - 2024 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ + +class CustomerDiagnosticSuite extends DiagnosticSuite +{ + + public function __construct( + private readonly Customer $customer + ) { + $this->name = 'Member Overview'; + $this->description = 'Diagnostics for the overall member\'s set-up.'; + $this->type = 'CUSTOMER'; + $this->link = route( 'customer@overview', ['cust' => $this->customer] ); + + + parent::__construct(); + } + + /** + * Run the diagnostics suite + */ + public function run(): static + { + // ordering here will determine order on view + $this->results->add( $this->customerType( $this->customer ) ); + $this->results->add( $this->customerStatus( $this->customer ) ); + $this->results->add( $this->customerHasLeft( $this->customer ) ); + $this->results->add( $this->customerRouteServerClient( $this->customer ) ); + + return $this; + } + + + /** + * Examine the customer type and provide information on it. + * + * @param Customer $customer + * @return DiagnosticResult + */ + public function customerType( Customer $customer ): DiagnosticResult { + $mainName = 'Member Type: '; + + return match ( $customer->type ) { + + Customer::TYPE_FULL => new DiagnosticResult( + name: $mainName . $customer->type(), + result: DiagnosticResult::TYPE_DEBUG, + narrative: "The member is a standard full member", + ), + + Customer::TYPE_PROBONO => new DiagnosticResult( + name: $mainName . $customer->type(), + result: DiagnosticResult::TYPE_INFO, + narrative: "The member is a pro bono member", + ), + + Customer::TYPE_INTERNAL => new DiagnosticResult( + name: $mainName . $customer->type(), + result: DiagnosticResult::TYPE_WARN, + narrative: "The member is an internal member used for IXP infrastructure. Do not assume normal member interfaces and behaviors.", + ), + + Customer::TYPE_ASSOCIATE => new DiagnosticResult( + name: $mainName .$customer->type(), + result: DiagnosticResult::TYPE_WARN, + narrative: "The member is an associate member and should not have any connections or other services.", + ), + + default => new DiagnosticResult( + name: $mainName . 'UNKNOWN', + result: DiagnosticResult::TYPE_FATAL, + narrative: "The member type {$this->customer->type()} is an unknown type to the diagnostic logic.", + ), + }; + + } + + + /** + * Examine the customer status and provide information on it. + * + * @param Customer $customer + * @return DiagnosticResult + */ + public function customerStatus( Customer $customer ): DiagnosticResult { + $mainName = 'Member Status: '; + + return match ($customer->status ) { + + Customer::STATUS_NOTCONNECTED, Customer::STATUS_SUSPENDED => new DiagnosticResult( + name: $mainName .$customer->status(), + result: DiagnosticResult::TYPE_WARN, + narrative: "The member's status is " .$customer->status(), + ), + + Customer::STATUS_NORMAL => new DiagnosticResult( + name: $mainName .$customer->status(), + result: DiagnosticResult::TYPE_DEBUG, + narrative: "The member's status is " .$customer->status(), + ), + + default => new DiagnosticResult( + name: $mainName . 'UNKNOWN', + result: DiagnosticResult::TYPE_FATAL, + narrative: "The member's status {$this->customer->status()} is an unknown status to the diagnostic logic.", + ), + }; + + } + + + /** + * Examine the customer left the IXP and provide information on it. + * + */ + public function customerHasLeft( Customer $customer ): DiagnosticResult { + + if($customer->hasLeft() ) { + return new DiagnosticResult( + name: "This member left the IXP " . Carbon::parse($this->customer->dateleave)->diffForHumans(), + result: DiagnosticResult::TYPE_ERROR, + narrative: "The member left the IXP on " . Carbon::parse($this->customer->dateleave)->format('Y-m-d'), + ); + } + + return new DiagnosticResult( + name: "This member has not left the IXP", + result: DiagnosticResult::TYPE_TRACE, + narrative: "The member has not left the IXP", + ); + } + + + /** + * Examine the customer Route Server Client status and provide information on it. + * + * @param Customer $customer + * @return DiagnosticResult + * @throws GeneralException + */ + public function customerRouteServerClient( Customer $customer ): DiagnosticResult { + $mainName = 'Route Server Client: '; + + if($customer->routeServerClient() ) { + return new DiagnosticResult( + name: $mainName . 'Yes', + result: DiagnosticResult::TYPE_DEBUG, + narrative: "The member is a route server client", + ); + } + + return new DiagnosticResult( + name: $mainName . 'No', + result: DiagnosticResult::TYPE_DEBUG, + narrative: "The member is not a route server client", + ); + + } + + +} \ No newline at end of file diff --git a/app/Services/Diagnostics/Suites/IrrdbDiagnosticSuite.php b/app/Services/Diagnostics/Suites/IrrdbDiagnosticSuite.php new file mode 100644 index 000000000..c9dbffbce --- /dev/null +++ b/app/Services/Diagnostics/Suites/IrrdbDiagnosticSuite.php @@ -0,0 +1,258 @@ + + * @author Laszlo Kiss + * @category IXP + * @package IXP\Services\Diagnostics + * @copyright Copyright (C) 2009 - 2024 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ + +class IrrdbDiagnosticSuite extends DiagnosticSuite +{ + + public function __construct( + private readonly Customer $customer + ) { + $this->name = 'IRRDB Filtering'; + $this->description = "Diagnostics related to IRRDB filtering."; + $this->type = 'CUSTOMER'; + $this->link = route( 'irrdb@list', ['cust' => $this->customer] ); + + parent::__construct(); + } + + /** + * Run the diagnostics suite + * + * @throws GeneralException + */ + public function run(): static + { + // ordering here will determine order on view + $this->results->add( $this->customerIrrdbFiltered( $this->customer ) ); + + if( $this->customer->routeServerClient() && $this->customer->irrdbFiltered() ) { + $this->results->add( $this->customerIrrdbAsnsPresent( $this->customer,IXP::IPv4 ) ); + $this->results->add( $this->customerIrrdbAsnsPresent( $this->customer,IXP::IPv6 ) ); + $this->results->add( $this->customerIrrdbPrefixesPresent( $this->customer,IXP::IPv4 ) ); + $this->results->add( $this->customerIrrdbPrefixesPresent( $this->customer,IXP::IPv6 ) ); + } + + return $this; + } + + + /** + * Examine the customer IRRDB filtering status and provide information on it. + * + * @param Customer $customer + * @return DiagnosticResult + * @throws GeneralException + */ + public function customerIrrdbFiltered( Customer $customer ): DiagnosticResult { + + if( !$this->customer->routeServerClient() ) { + return new DiagnosticResult( + name: 'IRRDB Filtering: not a route server client so no IRRDB filtering', + result: DiagnosticResult::TYPE_DEBUG, + narrative: "The member is not a route server client so IRRDB filtering not considered", + ); + } + + if($customer->fullyIrrdbFiltered() ) { + + if($this->customer->irrdbMoreSpecificsAllowed()) { + return new DiagnosticResult( + name: 'IRRDB Filtering: yes but more specifics allowed', + result: DiagnosticResult::TYPE_INFO, + narrative: "The member is IRRDB filtered but note that more specific prefixes are allowed on at least one VLAN interface", + ); + } + + return new DiagnosticResult( + name: 'IRRDB Filtering: yes', + result: DiagnosticResult::TYPE_DEBUG, + narrative: "The member is IRRDB filtered", + ); + + } + + return new DiagnosticResult( + name: 'IRRDB Filtering: no, route server sessions not secured with IRRDB', + result: DiagnosticResult::TYPE_ERROR, + narrative: "The member is a route server client but is not IRRDB filtered on at least one VLAN interface", + ); + } + + + /** + * Examine the customer IRRDB filtering status and provide information on it. + * + * @param Customer $customer + * @param int $proto + * @return DiagnosticResult + * @throws GeneralException + */ + public function customerIrrdbAsnsPresent( Customer $customer, int $proto ): DiagnosticResult { + + if( !$this->customer->isIPvXEnabled($proto) ) { + return new DiagnosticResult( + name: "No IRRDB ASNs as " . IXP::protocol($proto) . ' not enabled for this member', + result: DiagnosticResult::TYPE_TRACE, + narrative: IXP::protocol($proto) . ' not enabled for this member', + ); + } + + try { + $irrdblog = IrrdbUpdateLog::where( [ 'cust_id' =>$customer->id ] )->firstOrFail(); + + $m = 'asn_v' . $proto; + + if( $irrdblog->$m === null ) { + // the exception is irrelevant as we just want to catch and send a diagnostic result. + throw new Exception(); + } + + } catch( Exception ) { + return new DiagnosticResult( + name: "IRRDB ASNs have never been updated for " . IXP::protocol($proto), + result: DiagnosticResult::TYPE_ERROR, + narrative: "IRRDB ASNs have never been updated for " . IXP::protocol($proto), + ); + } + + $count = IrrdbAsn::where( 'customer_id',$customer->id ) + ->where( 'protocol', $proto ) + ->count(); + + if( $count === 0 ) { + return new DiagnosticResult( + name: "Zero IRRDB ASNs for " . IXP::protocol($proto) . "(last update was " . $irrdblog->$m->diffForHumans() . ")", + result: DiagnosticResult::TYPE_ERROR, + narrative: "There are zero IRRDB ASNs for " . IXP::protocol($proto) . " (last update was " . $irrdblog->$m->diffForHumans() . ")", + ); + } + + if( $irrdblog->$m < now()->subDay() ) { + return new DiagnosticResult( + name: "IRRDB ASNs (x{$count}) for " . IXP::protocol($proto) . " have not been updated since " . $irrdblog->$m->diffForHumans(), + result: DiagnosticResult::TYPE_WARN, + narrative: "IRRDB ASNs (x{$count}) for " . IXP::protocol($proto) . " have not been updated since " . $irrdblog->$m->diffForHumans(), + ); + } + + + return new DiagnosticResult( + name: "IRRDB ASNs (x{$count}) for " . IXP::protocol($proto) . " last updated " . $irrdblog->$m->diffForHumans(), + result: DiagnosticResult::TYPE_GOOD, + narrative: "IRRDB ASNs (x{$count}) for " . IXP::protocol($proto) . " last updated " . $irrdblog->$m->diffForHumans(), + ); + + } + + + /** + * Examine the customer IRRDB filtering status and provide information on it. + * + * @param Customer $customer + * @param int $proto + * @return DiagnosticResult + * @throws GeneralException + */ + public function customerIrrdbPrefixesPresent( Customer $customer , int $proto ): DiagnosticResult { + + if( !$this->customer->isIPvXEnabled($proto) ) { + return new DiagnosticResult( + name: "No " . IXP::protocol($proto) . " IRRDB prefixes, " . IXP::protocol($proto) . ' not enabled for this member', + result: DiagnosticResult::TYPE_TRACE, + narrative: IXP::protocol($proto) . ' not enabled for this member', + ); + } + + try { + $irrdblog = IrrdbUpdateLog::where( [ 'cust_id' =>$customer->id ] )->firstOrFail(); + + $m = 'prefix_v' . $proto; + + if( $irrdblog->$m === null ) { + // the exception is irrelevant as we just want to catch and send a diagnostic result. + throw new Exception(); + } + } catch( Exception ) { + return new DiagnosticResult( + name: "IRRDB prefixes have never been updated for " . IXP::protocol($proto), + result: DiagnosticResult::TYPE_ERROR, + narrative: "IRRDB prefixes have never been updated for " . IXP::protocol($proto), + ); + } + + $count = IrrdbPrefix::where( 'customer_id',$customer->id ) + ->where( 'protocol', $proto ) + ->count(); + + if( $count === 0 ) { + return new DiagnosticResult( + name: "There are zero IRRDB prefixes for " . IXP::protocol($proto) . " (last update was " . $irrdblog->$m->diffForHumans() . ")", + result: DiagnosticResult::TYPE_ERROR, + narrative: "There are zero IRRDB prefixes for " . IXP::protocol($proto) . " (last update was " . $irrdblog->$m->diffForHumans() . ")", + ); + } + + if( $irrdblog->$m < now()->subDay() ) { + return new DiagnosticResult( + name: "IRRDB prefixes (x{$count}) for " . IXP::protocol($proto) . " have not been updated since " . $irrdblog->$m->diffForHumans(), + result: DiagnosticResult::TYPE_WARN, + narrative: "IRRDB prefixes (x{$count}) for " . IXP::protocol($proto) . " have not been updated since " . $irrdblog->$m->diffForHumans(), + ); + } + + + return new DiagnosticResult( + name: "IRRDB prefixes (x{$count}) for " . IXP::protocol($proto) . " last updated " . $irrdblog->$m->diffForHumans(), + result: DiagnosticResult::TYPE_GOOD, + narrative: "IRRDB prefixes (x{$count}) for " . IXP::protocol($proto) . " last updated " . $irrdblog->$m->diffForHumans(), + ); + + } + + + +} \ No newline at end of file diff --git a/app/Services/Diagnostics/Suites/PhysicalInterfaceDiagnosticSuite.php b/app/Services/Diagnostics/Suites/PhysicalInterfaceDiagnosticSuite.php new file mode 100644 index 000000000..5e7a0c5b6 --- /dev/null +++ b/app/Services/Diagnostics/Suites/PhysicalInterfaceDiagnosticSuite.php @@ -0,0 +1,479 @@ + + * @author Laszlo Kiss + * @category IXP + * @package IXP\Services\Diagnostics + * @copyright Copyright (C) 2009 - 2024 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ + +class PhysicalInterfaceDiagnosticSuite extends DiagnosticSuite +{ + public const string DIAGNOSTIC_SUITE_NAME = 'Physical Interfaces Overview'; + + public const string DIAGNOSTIC_SUITE_DESCRIPTION = "Physical Interfaces overview diagnostics."; + + public const string DIAGNOSTIC_SUITE_TYPE = 'PHYSICAL_INTERFACE'; + + private VirtualInterface $vi; + + private SNMP|bool|null $snmpClient = null; + + private bool $stale = true; + + private static string $badgeStale = 'Stale'; + private static string $badgeLive = 'Live'; + + + + public function __construct( + private PhysicalInterface $pi, + ) { + + if( $pi?->switchPort ) { + $this->name = $pi->switchPort->switcher->name . ' :: ' . $pi->switchPort->name . ' / Physical Interface #' . $pi->id; + } else { + $this->name = 'Physical Interface #' . $pi->id; + } + + $this->description = 'Physical Interfaces general diagnostics.'; + $this->type = 'INTERFACE'; + $this->link = route( 'virtual-interface@edit', ['vi' => $pi->virtualInterface] );; + + parent::__construct(); + } + + /** + * Get / instantiate the snmp client + * + * @return SNMP|bool + */ + private function snmpClient(PhysicalInterface $pi): bool|SNMP|null { + + if( $this->snmpClient === null ) { + if( empty( $pi?->switchPort->switcher->snmppasswd ) ) { + $this->snmpClient = false; + } else { + $this->snmpClient = new SNMP( $pi->switchPort->switcher->hostname, $pi->switchPort->switcher->snmppasswd ); + } + } + + return $this->snmpClient; + } + + + /** + * Run the diagnostics suite + */ + public function run(): static + { + $this->results->add( $this->switchportLastPoll() ); + + // We want to poll the port now to (a) make sure we can and (b) use live data + // for the remaining tests without making multiple snmp get requests. + $this->results->add( $this->switchportCanPoll() ); + + $this->results->add( new DiagnosticResult( + name: "Switch port last change registered " + . ( $this->pi->switchPort->ifLastChange ? Carbon::parse($this->pi->switchPort->ifLastChange)->diffForHumans() : 'never' ), + result: DiagnosticResult::TYPE_DEBUG, + narrative: "Switch port last change counter: " + . ( $this->pi->switchPort->ifLastChange ? Carbon::parse($this->pi->switchPort->ifLastChange)->format('Y-m-d H:i:s') : 'never' ), + infoBadge: $this->stale ? self::$badgeStale : self::$badgeLive + ) ); + + $this->results->add( $this->mtu() ); + + $this->results->add( $this->adminStatus() ); + $this->results->add( $this->operatingStatus() ); + $this->results->add( $this->switchPortActive() ); + $this->results->add( $this->speed() ); + $this->results->add( $this->mauState() ); + + + return $this; + } + + + /** + * Examine the physical interface last poll and provide information on it. + * + * @return DiagnosticResult + */ + private function switchportLastPoll(): DiagnosticResult + { + $mainName = "Switch port information current?"; + + $lastPolled = Carbon::parse( $this->pi->switchPort->lastSnmpPoll ); + + if( now()->diffInHours( $lastPolled ) >= 1 || is_null($this->pi->switchPort->lastSnmpPoll ) ) { + return new DiagnosticResult( + name: $mainName . " No, last polled: " . $lastPolled ? $lastPolled->diffForHumans() : 'never', + result: DiagnosticResult::TYPE_WARN, + narrative: "No, last polled: " . $lastPolled ? $lastPolled->diffForHumans() : 'never', + ); + } + + return new DiagnosticResult( + name: $mainName . " Yes, last polled: " . $lastPolled->diffForHumans(), + result: DiagnosticResult::TYPE_DEBUG, + narrative: "SNMP information has been recently retrieved for this port.", + ); + } + + /** + * We want to poll the port now to (a) make sure we can and (b) use live data + * for the remaining tests without making multiple snmp get requests. + * + * @return DiagnosticResult + */ + private function switchportCanPoll(): DiagnosticResult + { + $mainName = "Can poll switch port via snmp?"; + + $before = $this->pi->switchPort->lastSnmpPoll; + + while( $before === now()->format('Y-m-d H:i:s') ) { + sleep(1); + } + + try { + + $this->pi->switchPort->snmpUpdate( $this->snmpClient($this->pi) ); + + if( $before !== $this->pi->switchPort->lastSnmpPoll->format('Y-m-d H:i:s') ) { + $this->stale = false; + return new DiagnosticResult( + name: $mainName . " Yes, refreshed successfully now", + result: DiagnosticResult::TYPE_DEBUG, + narrative: "SNMP information has been retrieved for this port.", + ); + } + + $this->stale = true; + return new DiagnosticResult( + name: $mainName . " No, could not poll the switch port", + result: DiagnosticResult::TYPE_FATAL, + narrative: "As we could not poll the switch port via SNMP, all other diagnostics tests relying on this information may not be accurate.", + ); + + } catch(\Exception $e) { + + return new DiagnosticResult( + name: $mainName . ' - diagnostic failed to run', + result: DiagnosticResult::TYPE_UNKNOWN, + narrativeHtml: $e->getMessage(), + ); + + } + + } + + /** + * Examine the physical interface mtu and provide information on it. + * + * @return DiagnosticResult + */ + private function mtu(): DiagnosticResult + { + $mainName = " MTU - "; + + if ( $this->pi->switchPort->ifMtu < 1500 ) { + + return new DiagnosticResult( + name: $mainName . "switch port is reporting a MTU of {$this->pi->switchPort->ifMtu} which is <1500", + result: DiagnosticResult::TYPE_FATAL, + narrative: "Switch port is reporting a MTU of <1500", + infoBadge: $this->stale ? self::$badgeStale : self::$badgeLive + ); + + } else if( $this->pi->switchPort->ifMtu === $this->pi->virtualInterface->mtu ) { + + return new DiagnosticResult( + name: $mainName . "both set to {$this->pi->virtualInterface->mtu}", + result: DiagnosticResult::TYPE_DEBUG, + narrative: "Switch port matches configured MTU of {$this->pi->virtualInterface->mtu}", + infoBadge: $this->stale ? self::$badgeStale : self::$badgeLive + ); + + } else if ( !$this->pi->virtualInterface->mtu ) { + + return new DiagnosticResult( + name: $mainName ."configured as null/0 but switch port reports " . $this->pi->switchPort->ifMtu ?: 'null', + result: DiagnosticResult::TYPE_INFO, + narrative: "Configured MTU is null/0 but switch port reports " . $this->pi->switchPort->ifMtu ?: 'null', + infoBadge: $this->stale ? self::$badgeStale : self::$badgeLive + ); + + } + + return new DiagnosticResult( + name: $mainName . ( $this->pi->virtualInterface->mtu ?? 'null' ) . " configured but switch port reports ({$this->pi->switchPort->ifMtu})", + result: DiagnosticResult::TYPE_ERROR, + narrative: "Configured MTU of {$this->pi->virtualInterface->mtu} does not match the switch port MTU of {$this->pi->switchPort->ifMtu}", + infoBadge: $this->stale ? self::$badgeStale : self::$badgeLive + ); + + } + + + + + /** + * Examine the physical interface status and provide information on it. + * + * @return DiagnosticResult + */ + private function adminStatus(): DiagnosticResult + { + $mainName = 'Admin status: ' . ( $this->pi->status ? 'Enabled' : 'Disabled' ) + . " in IXP Manager; Switch port configured as " . Iface::$IF_ADMIN_STATES[$this->pi->switchPort->ifAdminStatus]; + + if( $this->pi->status && $this->pi->switchPort->ifAdminStatus != Iface::IF_ADMIN_STATUS_UP ) { + + return new DiagnosticResult( + name: $mainName, + result: DiagnosticResult::TYPE_ERROR, + narrative: "The switch configuration state of the switch port is not up - perhaps it is shutdown/disabled in switch configuration? " + . "However, the configuration of the physical interface on IXP Manager is enabled.", + infoBadge: $this->stale ? self::$badgeStale : self::$badgeLive + ); + + } else if( !$this->pi->status && $this->pi->switchPort->ifAdminStatus == Iface::IF_ADMIN_STATUS_UP ) { + + return new DiagnosticResult( + name: $mainName, + result: DiagnosticResult::TYPE_ERROR, + narrative: "The switch configuration state of the switch port is up. " + . "However, the configuration of the physical interface on IXP Manager is disabled.", + infoBadge: $this->stale ? self::$badgeStale : self::$badgeLive + ); + + } else if( $this->pi->status && $this->pi->switchPort->ifAdminStatus == Iface::IF_ADMIN_STATUS_UP ) { + + return new DiagnosticResult( + name: $mainName, + result: DiagnosticResult::TYPE_DEBUG, + narrative: "The physical interface admin status is up.", + infoBadge: $this->stale ? self::$badgeStale : self::$badgeLive + ); + + } + + return new DiagnosticResult( + name: $mainName, + result: DiagnosticResult::TYPE_WARN, + narrative: "Unknown administrative (configuration) state on switch.", + infoBadge: $this->stale ? self::$badgeStale : self::$badgeLive + ); + + } + + + /** + * Examine the physical interface status and provide information on it. + * + * @return DiagnosticResult + */ + private function switchPortActive(): DiagnosticResult + { + $mainName = 'Within IXP Manager, the phsyical interface is ' + . ( $this->pi->status ? 'enabled' : 'disabled' ) + . ' and the switchport is ' + . ( $this->pi->switchPort->active ? 'active' : 'inactive' ); + + if( $this->pi->status != $this->pi->switchPort->active ) { + + return new DiagnosticResult( + name: $mainName, + result: DiagnosticResult::TYPE_ERROR, + narrative: "IXP Manager is configured with conflicted state for physical interface and switch port.", + infoBadge: $this->stale ? self::$badgeStale : self::$badgeLive + ); + + } + + return new DiagnosticResult( + name: $mainName, + result: DiagnosticResult::TYPE_DEBUG, + narrative: "IXP Manager is configured with a consistent state for physical interface and switch port.", + infoBadge: $this->stale ? self::$badgeStale : self::$badgeLive + ); + + } + + + + /** + * Examine the physical interface status and provide information on it. + * + * @return DiagnosticResult + */ + private function operatingStatus(): DiagnosticResult + { + $mainName = 'Operating status: ' . ( $this->pi->status ? 'Enabled' : 'Disabled' ) + . " in IXP Manager; Switch port reports as " . Iface::$IF_OPER_STATES[$this->pi->switchPort->ifOperStatus]; + + if( $this->pi->status && $this->pi->switchPort->ifOperStatus != Iface::IF_OPER_STATUS_UP ) { + + return new DiagnosticResult( + name: $mainName, + result: DiagnosticResult::TYPE_ERROR, + narrative: "The switch port is not up - perhaps it is shutdown/disabled in switch configuration or disconnected or no light rx? " + . "The configuration of the physical interface on IXP Manager is enabled.", + infoBadge: $this->stale ? self::$badgeStale : self::$badgeLive + ); + + } else if( !$this->pi->status && $this->pi->switchPort->ifOperStatus == Iface::IF_OPER_STATUS_UP ) { + + return new DiagnosticResult( + name: $mainName, + result: DiagnosticResult::TYPE_ERROR, + narrative: "The switch port is up. " + . "However, the configuration of the physical interface on IXP Manager is disabled.", + infoBadge: $this->stale ? self::$badgeStale : self::$badgeLive + ); + + } else if( $this->pi->status && $this->pi->switchPort->ifOperStatus == Iface::IF_OPER_STATUS_UP ) { + + return new DiagnosticResult( + name: $mainName, + result: DiagnosticResult::TYPE_GOOD, + narrative: "The switch port is up/up.", + infoBadge: $this->stale ? self::$badgeStale : self::$badgeLive + ); + + } + + return new DiagnosticResult( + name: $mainName, + result: DiagnosticResult::TYPE_WARN, + narrative: "Unknown port operating state on switch.", + infoBadge: $this->stale ? self::$badgeStale : self::$badgeLive + ); + + } + + + + /** + * Examine the Switch Port physical speed and provide information on it. + * + * @return DiagnosticResult + */ + private function speed(): DiagnosticResult + { + $mainName = "Switch port speed configured as {$this->pi->speed()}; actual switch port speed: " . ( PhysicalInterface::$SPEED[$this->pi->switchPort->ifSpeed] ?? $this->pi->switchPort->ifSpeed ?? 'null' ); + + if( $this->pi->switchPort->ifSpeed == $this->pi->speed ) { + + return new DiagnosticResult( + name: $mainName, + result: DiagnosticResult::TYPE_DEBUG, + narrative: "The configured and actual switch port speeds match", + infoBadge: $this->stale ? self::$badgeStale : self::$badgeLive + ); + + } else { + + return new DiagnosticResult( + name: $mainName, + result: DiagnosticResult::TYPE_ERROR, + narrative: "The configured and actual switch port speeds DO NOT match", + infoBadge: $this->stale ? self::$badgeStale : self::$badgeLive + ); + + } + + } + + /** + * Examine the Switch Port physical speed and provide information on it. + * + * @return DiagnosticResult[] + * + * @psalm-return list{0: DiagnosticResult, 1?: DiagnosticResult, 2?: DiagnosticResult} + */ + private function mauState(): array + { + $results = []; + + if( !$this->pi->switchPort->switcher->mauSupported ) { + + return [ new DiagnosticResult( + name: "Switch does not support MAU (optic) information via SNMP", + result: DiagnosticResult::TYPE_INFO, + narrative: "Switch does not support MAU (optic) information via SNMP", + infoBadge: $this->stale ? self::$badgeStale : self::$badgeLive + ) ]; + + } + + + $results[] = new DiagnosticResult( + name: "Switch supports MAU (optic) information via SNMP", + result: DiagnosticResult::TYPE_TRACE, + narrative: "Switch supports MAU (optic) information via SNMP", + infoBadge: $this->stale ? self::$badgeStale : self::$badgeLive + ); + + $results[] = new DiagnosticResult( + name: "MAU type: " . $this->pi->switchPort->mauType . ( $this->pi->switchPort->mauJacktype ? '(jack type: ' . $this->pi->switchPort->mauJacktype . ')' : '' ), + result: DiagnosticResult::TYPE_INFO, + narrative: "MAU type: " . $this->pi->switchPort->mauType . ( $this->pi->switchPort->mauJacktype ? '(jack type: ' . $this->pi->switchPort->mauJacktype . ')' : '' ), + infoBadge: $this->stale ? self::$badgeStale : self::$badgeLive + ); + + $results[] = new DiagnosticResult( + name: "MAU state: " . $this->pi->switchPort->mauState, + result: DiagnosticResult::TYPE_DEBUG, + narrative: "MAU state: " . $this->pi->switchPort->mauState, + infoBadge: $this->stale ? self::$badgeStale : self::$badgeLive + ); + + + return $results; + + } + + + +} \ No newline at end of file diff --git a/app/Services/Diagnostics/Suites/RouterBgpSessionsDiagnosticSuite.php b/app/Services/Diagnostics/Suites/RouterBgpSessionsDiagnosticSuite.php new file mode 100644 index 000000000..5ba4f2051 --- /dev/null +++ b/app/Services/Diagnostics/Suites/RouterBgpSessionsDiagnosticSuite.php @@ -0,0 +1,238 @@ + + * @author Laszlo Kiss + * @category IXP + * @package IXP\Services\Diagnostics + * @copyright Copyright (C) 2009 - 2024 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ + +class RouterBgpSessionsDiagnosticSuite extends DiagnosticSuite +{ + /** @var Router[] */ + private $routers = []; + + /** @var LookingGlassContract[] */ + private array $lg = []; + + /** + * @param VlanInterface $vli + * @param int $protocol + */ + public function __construct( + private readonly VlanInterface $vli, + private readonly int $protocol, + ) { + + $this->name = 'BGP Sessions over ' . $vli->vlan->name . ' via ' . $vli->getIPAddress($this->protocol)->address; + $this->description = " "; + $this->type = 'VLAN_INTERFACE'; + + $this->routers = Router::where( 'protocol', $protocol === 4 ? Router::PROTOCOL_IPV4 : Router::PROTOCOL_IPV6 ) + ->where( 'vlan_id', $vli->vlan->id ) + ->get(); + + parent::__construct(); + } + + /** + * Run the diagnostics suite + * + * @throws BindingResolutionException + */ + public function run(): static + { + foreach( $this->routers as $router ) { + + if( $router->hasApi() ) { + + try { + $this->lg[$router->handle] = App::make( LookingGlassService::class )->forRouter( $router ); + + if( $status = json_decode( $this->lg[ $router->handle ]->status() ) ) { + + $this->results->add( new DiagnosticResult( + name: "Router {$router->handle} up, last reconfig " . + Carbon::parse( $status->status->last_reconfig )->diffForHumans(), + result: DiagnosticResult::TYPE_TRACE, + ) ); + + } else { + + $this->results->add( new DiagnosticResult( + name: "Router {$router->handle} not up or looking glass failure", + result: DiagnosticResult::TYPE_FATAL, + ) ); + continue; + } + + } catch( \Exception $e) { + + $this->results->add(new DiagnosticResult( + name: 'Router diagnostic failed to run', + result: DiagnosticResult::TYPE_UNKNOWN, + narrativeHtml: $e->getMessage(), + ) ); + + } + + } else { + + $this->results->add( new DiagnosticResult( + name: "Router {$router->handle} does not have an API, skipping tests", + result: DiagnosticResult::TYPE_DEBUG + ) ); + continue; + } + + $this->results->add( $this->protocolStatus( $this->vli, $this->protocol, $router, $this->lg[ $router->handle ] ) ); + + } + + return $this; + } + + + /** + * Examine the Router Protocol Status and provide information on it. + * + * @return DiagnosticResult + */ + public function protocolStatus( VlanInterface $vli, int $protocol, Router $r, LookingGlassContract $lg ) + { + $mainName = "BGP status for {$r->handle} - "; + + // we have inconsistent protocol naming which needs to be corrected + if( $r->isType( Router::TYPE_ROUTE_SERVER ) ) { + $pb = "pb_" . sprintf( "%04d", $vli->id ) . "_as" . $vli->virtualInterface->customer->autsys; + } else { + $pb = "pb_as" . $vli->virtualInterface->customer->autsys . "_vli{$vli->id}_ipv{$protocol}"; + } + + try { + if( !( $bgpsum = json_decode( $lg->bgpNeighbourSummary( $pb ) ) ) ) { + + return new DiagnosticResult( + name: $mainName . 'could not query looking glass', + result: DiagnosticResult::TYPE_FATAL, + narrative: "API call to looking glass failed.", + ); + + } + } catch( \Exception $e ) { + + return new DiagnosticResult( + name: $mainName . ' - diagnostic failed to run', + result: DiagnosticResult::TYPE_UNKNOWN, + narrativeHtml: $e->getMessage(), + ); + + } + + $bgpsum = $bgpsum->protocol; // narrow focus to what interests us + + if( !isset($bgpsum->import_limit) ) { + $bgpsum->import_limit = 0; + $max_prefixes = false; + } else { + $max_prefixes = true; + if( isset($bgpsum->route_limit_at) ) { + $max_prefixes_percent = (int)( $bgpsum->route_limit_at / $bgpsum->import_limit ) * 100; + } else { + $max_prefixes_percent = '?'; + } + } + + $narrative = <<State: {$bgpsum->state}
+ Changed: {$bgpsum->state_changed}
+ Connection: {$bgpsum->connection}
+ ENDNARR; + + if( isset( $bgpsum->hold_timer_now ) ) { + $narrative .= "Hold timer (now): {$bgpsum->hold_timer} ({$bgpsum->hold_timer_now})
"; + } else if( isset( $bgpsum->hold_timer ) ) { + $narrative .= "Hold timer: {$bgpsum->hold_timer}
"; + } + + if( isset( $bgpsum->keepalive_now ) ) { + $narrative .= "Keepalive (now): {$bgpsum->keepalive} ({$bgpsum->keepalive_now})
"; + } else if( isset( $bgpsum->keepalive ) ) { + $narrative .= "Keepalive: {$bgpsum->keepalive}
"; + } + + $narrative .= "Max prefixes: {$bgpsum->import_limit}
"; + + if( isset( $bgpsum->route_limit_at ) ) { + $narrative .= "# Routes: {$bgpsum->route_limit_at}
"; + } + + if( $bgpsum->state !== 'up' ) { + + return new DiagnosticResult( + name: $mainName . 'session state ' . $bgpsum->state, + result: DiagnosticResult::TYPE_ERROR, + narrativeHtml: $narrative, + ); + + } + + + if( $max_prefixes && $max_prefixes_percent > 80 ) { + + return new DiagnosticResult( + name: $mainName . "session up but max prefixes at {$max_prefixes_percent}% ({$bgpsum->route_limit_at}/{$bgpsum->import_limit})", + result: DiagnosticResult::TYPE_WARN, + narrativeHtml: $narrative, + ); + + } + + return new DiagnosticResult( + name: $mainName . "session up " . ( $max_prefixes && isset($bgpsum->route_limit_at) ? "({$bgpsum->route_limit_at}/{$bgpsum->import_limit} prefixes) " : " " ), + result: DiagnosticResult::TYPE_GOOD, + narrativeHtml: $narrative, + ); + } + + +} \ No newline at end of file diff --git a/app/Services/Diagnostics/Suites/TransceiverDiagnosticSuite.php b/app/Services/Diagnostics/Suites/TransceiverDiagnosticSuite.php new file mode 100644 index 000000000..fe5711860 --- /dev/null +++ b/app/Services/Diagnostics/Suites/TransceiverDiagnosticSuite.php @@ -0,0 +1,415 @@ + + * @category IXP + * @package IXP\Services\Diagnostics + * @copyright Copyright (C) 2009 - 2024 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ + +class TransceiverDiagnosticSuite extends DiagnosticSuite +{ + public const string DIAGNOSTIC_SUITE_NAME = 'Transceiver Diagnostic'; + + public const string DIAGNOSTIC_SUITE_DESCRIPTION = "Transceiver diagnostics."; + + public const string DIAGNOSTIC_SUITE_TYPE = 'PHYSICAL_INTERFACE'; + + private SNMP|bool|null $snmpClient = null; + + + const OID_XCVR_DOM_TEMPERATURE = '.1.3.6.1.2.1.47.1.1.1.1.2.1003%02d201'; // %d => XX port number + const OID_XCVR_DOM_VOLTAGE = '.1.3.6.1.2.1.47.1.1.1.1.2.1003%02d202'; // %d => XX port number + + const OID_XCVR_DOM_LANE_SENSOR = '.1.3.6.1.2.1.47.1.1.1.1.2.1003%02d2%d%d'; // %d => XX port number + // %d => 1-4: Lane number + // %d => 1: DOM TX Bias Sensor + // => 2: DOM TX Power Sensor + // => 3: DOM RX Power Sensor + + const OID_XCVR_DOM_TEMPERATURE_UNITS = '.1.3.6.1.2.1.99.1.1.1.6.1003%02d201'; // %d => XX port number + const OID_XCVR_DOM_VOLTAGE_UNITS = '.1.3.6.1.2.1.99.1.1.1.6.1003%02d202'; // %d => XX port number + + const OID_XCVR_DOM_LANE_SENSOR_UNITS = '.1.3.6.1.2.1.99.1.1.1.6.1003%02d2%D%D'; // %d => XX port number + // %d => 1-4: Lane number + // %d => 1: DOM TX Bias Sensor + // => 2: DOM TX Power Sensor + // => 3: DOM RX Power Sensor + + const OID_XCVR_DOM_TEMPERATURE_THRESHOLD = '.1.3.6.1.4.1.30065.3.12.1.1.1.5.1003%02d201'; // %d => XX port number - "Sensor value 38.1 Celsius is within bounds" + const OID_XCVR_DOM_VOLTAGE_UNITS_THRESHOLD = '.1.3.6.1.4.1.30065.3.12.1.1.1.5.1003%02d202'; // %d => XX port number - "Sensor value 3.29 Volts is within bounds" + + + // 1 - "Sensor value 42.21 mA is within bounds" + // 2 - "Sensor value 1.0030 mW is within bounds" + // 3 - "Sensor value 0.6926 mW is within bounds" + const OID_XCVR_DOM_LANE_SENSOR_UNITS_THRESHOLD = '.1.3.6.1.4.1.30065.3.12.1.1.1.5.1003%02d2%d%d'; // %d => XX port number + // %d => 1-4: Lane number + // %d => 1: DOM TX Bias Sensor + // => 2: DOM TX Power Sensor + // => 3: DOM RX Power Sensor + + const OID_XCVR_SERIAL_NUMBER = '.1.3.6.1.2.1.47.1.1.1.1.11.1003%02d100'; // %d => XX port number + const OID_XCVR_MANUFACTURER = '.1.3.6.1.2.1.47.1.1.1.1.12.1003%02d100'; // %d => XX port number + const OID_XCVR_MODEL = '.1.3.6.1.2.1.47.1.1.1.1.13.1003%02d100'; // %d => XX port number + + + public function __construct( + private PhysicalInterface $pi, + ) { + + if( $pi?->switchPort ) { + $this->name = 'Transceiver diagnostics for: ' . $pi->switchPort->switcher->name . ' :: ' . $pi->switchPort->name . ' / Physical Interface #' . $pi->id; + } else { + $this->name = 'Physical Interface #' . $pi->id; + } + + parent::__construct(); + } + + + + /** + * Get / instantiate the snmp client + * + * @return SNMP|bool + */ + private function snmpClient(PhysicalInterface $pi): bool|SNMP|null { + + if( $this->snmpClient === null ) { + if( empty( $pi?->switchPort->switcher->snmppasswd ) ) { + $this->snmpClient = false; + } else { + $this->snmpClient = new SNMP( $pi->switchPort->switcher->hostname, $pi->switchPort->switcher->snmppasswd ); + $this->snmpClient->disableCache(); + } + } + + return $this->snmpClient; + } + + + + /** + * Run the diagnostics suite + */ + public function run(): static + { + + // check if we can even run these first + if( !$this->snmpClient($this->pi) ) { + + $this->results->add( new DiagnosticResult( + name: "Transceiver diagnostics not available via SNMP for this switch.", + result: DiagnosticResult::TYPE_WARN, + narrative: "Please log into the switch and run transceiver diagnostics manually.", + ) ); + + return $this; + } + + // check if we can even run these first + if( $this->pi->switchPort->switcher->os !== 'EOS' ) { + + $this->results->add( new DiagnosticResult( + name: "Transceiver diagnostics not available for non-Arista EOS switches currently.", + result: DiagnosticResult::TYPE_WARN, + narrative: "Please log into the switch and run transceiver diagnostics manually.", + ) ); + + return $this; + } + + $this->results->add( $this->info( $this->pi ) ); + $this->results->add( $this->temperature( $this->pi ) ); + $this->results->add( $this->voltage( $this->pi ) ); + $this->results->add( $this->lightLevels( $this->pi ) ); + + return $this; + } + + + /** + * Get general information on the transceiver + * + * @return DiagnosticResult + */ + private function temperature( PhysicalInterface $pi ): DiagnosticResult + { + $mainName = "Transceiver temperature - "; + + try { + + $t = $this->snmpClient($this->pi)->get( sprintf( self::OID_XCVR_DOM_TEMPERATURE_THRESHOLD, $this->oidPort( $this->pi->switchPort->name ) ) ); + + } catch( \Exception $e ) { + + return new DiagnosticResult( + name: $mainName . ' - diagnostic failed to run', + result: DiagnosticResult::TYPE_UNKNOWN, + narrativeHtml: $e->getMessage(), + ); + + } + + if( str_ends_with( $t, 'is within bounds' ) ) { + + return new DiagnosticResult( + name: $mainName . "{$t}", + result: DiagnosticResult::TYPE_DEBUG, + narrative: "{$t}", + ); + + } + + return new DiagnosticResult( + name: $mainName . "{$t}", + result: DiagnosticResult::TYPE_ERROR, + narrative: "{$t}", + ); + + } + + + /** + * Get general information on the transceiver + * + * @return DiagnosticResult + */ + private function voltage( PhysicalInterface $pi ): DiagnosticResult + { + $mainName = "Transceiver voltage - "; + + try { + + $v = $this->snmpClient($this->pi)->get( sprintf( self::OID_XCVR_DOM_VOLTAGE_UNITS_THRESHOLD, $this->oidPort( $this->pi->switchPort->name ) ) ); + + } catch( \Exception $e ) { + + return new DiagnosticResult( + name: $mainName . ' - diagnostic failed to run', + result: DiagnosticResult::TYPE_UNKNOWN, + narrativeHtml: $e->getMessage(), + ); + + } + + if( str_ends_with( $v, 'is within bounds' ) ) { + + return new DiagnosticResult( + name: $mainName . "{$v}", + result: DiagnosticResult::TYPE_DEBUG, + narrative: "{$v}", + ); + + } + + return new DiagnosticResult( + name: $mainName . "{$v}", + result: DiagnosticResult::TYPE_ERROR, + narrative: "{$v}", + ); + + } + + + /** + * Get general information on the transceiver + * + * const OID_XCVR_DOM_LANE_SENSOR_UNITS_THRESHOLD = '.1.3.6.1.4.1.30065.3.12.1.1.1.5.1003%d2%D%D'; + * // %d => XX port number + * // %d => 1-4: Lane number + * // %d => 1: DOM TX Bias Sensor + * // => 2: DOM TX Power Sensor + * // => 3: DOM RX Power Sensor + * + * @return DiagnosticResult[] + * + * @psalm-return list{DiagnosticResult,...} + */ + private function lightLevels( PhysicalInterface $pi ): array + { + $mainName = "Light levels - "; + $results = []; + + $sensors = [ + 1 => 'DOM TX Bias Sensor', + 2 => 'DOM TX Power Sensor', + 3 => 'DOM RX Power Sensor', + ]; + + if( $pi->speed <= 10_000 ) { + $lanes = [1]; + } else { + $lanes = [ 1, 2, 3, 4 ]; + } + + $readings = []; + + foreach( array_keys( $sensors ) as $sensor ) { + foreach( $lanes as $lane ) { + $v = null; + + try { + + $v = $this->snmpClient($this->pi)->get( sprintf( self::OID_XCVR_DOM_LANE_SENSOR_UNITS_THRESHOLD, $this->oidPort( $this->pi->switchPort->name ), $lane, $sensor ) ); + + } catch( \Exception $e ) { + if( $lane > 1) { + continue; + } + + $results[] = new DiagnosticResult( + name: $mainName . ' - diagnostic failed to run', + result: DiagnosticResult::TYPE_UNKNOWN, + narrativeHtml: $e->getMessage(), + ); + + $readings[ $sensor ][ $lane ] = 'ERR'; + + continue; + } + + if( str_ends_with( $v, 'is within bounds' ) ) { + + $results[] = new DiagnosticResult( + name: $mainName . "{$sensors[$sensor]} for lane {$lane} - {$v}", + result: DiagnosticResult::TYPE_DEBUG, + narrative: "{$v}", + ); + + } else { + + $results[] = new DiagnosticResult( + name: $mainName . "{$sensors[$sensor]} for lane {$lane} - {$v}", + result: DiagnosticResult::TYPE_ERROR, + narrative: "{$v}", + ); + + } + + // "Sensor value 42.21 mA is within bounds" + // "Sensor value 1.0030 mW is within bounds" + // "Sensor value 0.6926 mW is within bounds" + $matches = []; + preg_match( "/Sensor value ([\d\.]+) ([a-zA-Z]+)\s.*/", $v, $matches ); + + if( isset( $matches[2] ) && $matches[2] == 'mW') { + $matches[1] = 10 * log10($matches[1]); // to dbm + } + $readings[ $sensor ][ $lane ] = sprintf( "%+2.2f", $matches[ 1 ] ); + + } + } + + $swoutput = <<{$swoutput}", + ); + + return $results; + + } + + /** + * Get general information on the transceiver + * + * @return DiagnosticResult + */ + private function info( PhysicalInterface $pi ): DiagnosticResult + { + $mainName = "Transceiver information - "; + + try { + + $serial = trim( $this->snmpClient($this->pi)->get( sprintf( self::OID_XCVR_SERIAL_NUMBER, $this->oidPort($pi->switchPort->name) ) ) ); + $manuf = trim( $this->snmpClient($this->pi)->get( sprintf( self::OID_XCVR_MANUFACTURER, $this->oidPort($pi->switchPort->name) ) ) ); + $model = trim( $this->snmpClient($this->pi)->get( sprintf( self::OID_XCVR_MODEL, $this->oidPort($pi->switchPort->name) ) ) ); + + } catch( \Exception $e ) { + + return new DiagnosticResult( + name: $mainName . ' - diagnostic failed to run', + result: DiagnosticResult::TYPE_UNKNOWN, + narrativeHtml: $e->getMessage(), + ); + + } + + return new DiagnosticResult( + name: $mainName . "{$manuf} {$model}, serial #{$serial}", + result: DiagnosticResult::TYPE_INFO, + narrativeHtml: "Manufacturer: {$manuf}
Model: {$model}
Serial: {$serial}", + ); + } + + + /** + * PoC - assumes Arista right now + * @param string $name + * @return string + */ + private function oidPort( string $name ): string { + $matches = []; + preg_match( '/Ethernet(\d+).*/', $name, $matches ); + return sprintf( '%02d', (int)$matches[1] ); + } + + +} \ No newline at end of file diff --git a/app/Services/Diagnostics/Suites/VirtualInterfaceDiagnosticSuite.php b/app/Services/Diagnostics/Suites/VirtualInterfaceDiagnosticSuite.php new file mode 100644 index 000000000..aab2d7ac4 --- /dev/null +++ b/app/Services/Diagnostics/Suites/VirtualInterfaceDiagnosticSuite.php @@ -0,0 +1,446 @@ + + * @author Laszlo Kiss + * @category IXP + * @package IXP\Services\Diagnostics + * @copyright Copyright (C) 2009 - 2024 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ + +class VirtualInterfaceDiagnosticSuite extends DiagnosticSuite +{ + public function __construct( + private readonly VirtualInterface $vi, + ) { + + $name = ''; + + if( $vi->physicalInterfaces ) { + $name .= 'Connection to '; + $name .= $vi->physicalInterfaces[0]?->switchPort?->switcher?->infrastructureModel->name + . ' via ' . $vi->physicalInterfaces[0]?->switchPort?->switcher?->name; + $name .= ' / '; + } + + $this->name = $name . 'Virtual Interface #' . $vi->id; + $this->description = "Virtual interfaces general diagnostics."; + $this->type = 'INTERFACE'; + $this->link = route( 'virtual-interface@edit', ['vi' => $vi] ); + + parent::__construct(); + } + + /** + * Run the diagnostics suite + */ + public function run(): static + { + $this->results->add( $this->portType( $this->vi ) ); + $this->results->add( $this->sameSwitch( $this->vi ) ); + $this->results->add( $this->lag( $this->vi ) ); + $this->results->add( $this->lagSpeeds( $this->vi ) ); + $this->results->add( $this->lagName( $this->vi ) ); + $this->results->add( $this->trunk( $this->vi ) ); + $this->results->add( $this->mtu( $this->vi ) ); + + return $this; + } + + + /** + * Examine the virtual interface type and provide information on it. + * + * @param VirtualInterface $vi + * @return DiagnosticResult + */ + public function portType( VirtualInterface $vi ): DiagnosticResult { + + $mainName = 'Switch Port Type(s): '; + + if( $vi->physicalInterfaces->isEmpty() ) { + + return new DiagnosticResult( + name: $mainName . "can not determine type as there are no physical interfaces", + result: DiagnosticResult::TYPE_ERROR, + narrative: "Can not determine type as there are no physical interfaces for this virtual interface.", + ); + + } + + $piTypes = []; + foreach( $vi->physicalInterfaces as $pi ) { + $piTypes[] = $pi->switchPort->type; + } + + $piTypes = array_unique($piTypes); + + if( count($piTypes) > 1 ) { + + $piTypesDesc = []; + foreach( $piTypes as $piType ) { + $piTypesDesc[] = SwitchPort::$TYPES[ $piType ]; + } + + return new DiagnosticResult( + name: $mainName . "physical interfaces have mixed types: " . implode(', ', $piTypesDesc), + result: DiagnosticResult::TYPE_ERROR, + narrative: "There are multiple physical interfaces but they have mixed types: " . implode(', ', $piTypesDesc), + ); + } + + if( $vi->typePeering() ) { + return new DiagnosticResult( + name: $mainName . " peering", + result: DiagnosticResult::TYPE_DEBUG, + narrative: "The physical interface(s) type is: peering", + ); + } + + return new DiagnosticResult( + name: $mainName . $vi->resolveType(), + result: DiagnosticResult::TYPE_WARN, + narrative: "The physical interface(s) type is: " . $vi->resolveType(), + ); + + } + + + /** + * Examine the virtual interface physical interfaces position in switch and provide information on it. + * + * @param VirtualInterface $vi + * @return DiagnosticResult + */ + public function sameSwitch( VirtualInterface $vi ): DiagnosticResult { + $mainName = 'Physical Interfaces on the Same Switch'; + + if( count( $vi->physicalInterfaces ) <= 1 ) { + return new DiagnosticResult( + name: $mainName . ' - n/a as <=1 port', + result: DiagnosticResult::TYPE_TRACE, + narrative: "N/A as this virtual interface does not have multiple physical interfaces.", + ); + } + + if( $vi->sameSwitchForEachPI() ) { + return new DiagnosticResult( + name: $mainName . ' - yes', + result: DiagnosticResult::TYPE_DEBUG, + narrative: "The physical interfaces of this virtual interface are connected to the same switch", + ); + } + + return new DiagnosticResult( + name: $mainName . ' - no, this is not support!', + result: DiagnosticResult::TYPE_ERROR, + narrative: "The physical interfaces are on different switches", + ); + } + + + /** + * Examine the virtual interface LAG and provide information on it. + * + * @param VirtualInterface $vi + * @return DiagnosticResult + */ + public function lag( VirtualInterface $vi ): DiagnosticResult { + + $mainName = 'LAG/LACP Configuration'; + + $numPis = count( $vi->physicalInterfaces ); + $lacpMode = $vi->fastlacp ? 'fast' : 'slow'; + + if( $numPis > 1 ) { + + if( $vi->lag_framing ) { + + return new DiagnosticResult( + name: $mainName . " - yes with {$lacpMode} timeout", + result: DiagnosticResult::TYPE_INFO, + narrative: "Configured as a LAG with " . $lacpMode . " timeout", + ); + + } else { // !$vi->lag_framing + + return new DiagnosticResult( + name: $mainName . ' - no BUT there are multiple physical interfaces!', + result: DiagnosticResult::TYPE_ERROR, + narrative: "Multiple physical interfaces but not configured as a LAG", + ); + } + + } else if( $numPis === 1 ) { + + if( $vi->lag_framing ) { + + return new DiagnosticResult( + name: $mainName . " - configured as single member LAG with {$lacpMode} timeout", + result: DiagnosticResult::TYPE_INFO, + narrative: "Configured as a single member LAG with " . $lacpMode . " timeout", + ); + + } else { // !$this->vi->lag_framing + + return new DiagnosticResult( + name: $mainName . ' - no', + result: DiagnosticResult::TYPE_TRACE, + narrative: "Single physical interface and not configured as a LAG", + ); + } + } + + // no physical interfaces + return new DiagnosticResult( + name: $mainName . ' - n/a, no physical interfaces', + result: DiagnosticResult::TYPE_TRACE, + narrative: "No physical interfaces configured for this connection", + ); + } + + + /** + * All ports in a LAG have the same speed? + * + * @param VirtualInterface $vi + * @return DiagnosticResult + */ + public function lagSpeeds( VirtualInterface $vi ): DiagnosticResult { + + $mainName = 'LAG - Port Speeds Match?'; + + $numPis = count( $vi->physicalInterfaces ); + + if( $numPis <= 1 ) { + + // no physical interfaces + return new DiagnosticResult( + name: $mainName . ' - n/a, applies only if multiple physical interfaces', + result: DiagnosticResult::TYPE_TRACE, + narrative: "The connection does not have multiple physical interfaces and so we cannot compare speeds.", + ); + + } + + $speeds = []; + + foreach( $vi->physicalInterfaces as $pi ) { + $speeds[] = $pi->speed(); + } + + $speeds = array_unique($speeds); + + if( count( $speeds ) > 1 ) { + + return new DiagnosticResult( + name: $mainName . " - no, multiple speeds found: " . implode( ', ', $speeds ), + result: DiagnosticResult::TYPE_ERROR, + narrative: "Physical interfaces in a LAG port must all have the same speed but we found different speeds configured.", + ); + + } + + return new DiagnosticResult( + name: $mainName . ' - yes, all speeds configured as ' . array_pop( $speeds ), + result: DiagnosticResult::TYPE_INFO, + narrative: "Physical interfaces in a LAG port must all have the same speed, which is what we found.", + ); + + } + + + /** + * Examine the virtual interface LAG framing and provide information on it. + * + * @param VirtualInterface $vi + * @return DiagnosticResult + */ + public function lagName(VirtualInterface $vi): DiagnosticResult { + + $mainName = 'LAG Interface Naming'; + + if( !$vi->lag_framing && count( $vi->physicalInterfaces ) <= 1 ) { + return new DiagnosticResult( + name: $mainName . " - n/a, no LACP configuration", + result: DiagnosticResult::TYPE_TRACE, + narrative: "N/A - LAG framing not set and <= 1 physical interface", + ); + } + + if( !$vi->name || !$vi->channelgroup ) { + + $narratives = []; + + if(!$vi->name) { + $narratives[] = "no name"; + } else if(!$vi->channelgroup) { + $narratives[] = "no channel group number"; + } + + $narrative = implode(', ', $narratives); + + return new DiagnosticResult( + name: $mainName . " - LAG framing but {$narrative} defined", + result: DiagnosticResult::TYPE_ERROR, + narrative: "LAG framing required but " . $narrative . " defined on the virtual interface", + ); + } + + + $switchIds = SwitchPort::join( 'physicalinterface', 'switchport.id', '=', 'physicalinterface.switchportid' ) + ->join( 'virtualinterface', 'virtualinterface.id', '=', 'physicalinterface.virtualinterfaceid' ) + ->where( 'virtualinterface.id', $vi->id ) + ->pluck('switchport.switchid'); + + $bundleNamesCount = VirtualInterface::select('virtualinterface.id') + ->where( 'virtualinterface.name', $vi->name ) + ->where( 'virtualinterface.channelgroup', $vi->channelgroup ) + ->join( 'physicalinterface', 'virtualinterface.id', '=', 'physicalinterface.virtualinterfaceid' ) + ->join( 'switchport', 'physicalinterface.switchportid', '=', 'switchport.id' ) + ->whereIn( 'switchport.switchid', $switchIds ) + ->groupBy('virtualinterface.id') + ->get()->toArray(); + + if( count($bundleNamesCount) > 1 ) { + return new DiagnosticResult( + name: $mainName . " - LAG bundle name not unique!", + result: DiagnosticResult::TYPE_ERROR, + narrative: "LAG interface bundle name (" . $vi->bundleName() . ") is not unique for the switch", + ); + } else { + return new DiagnosticResult( + name: $mainName . " - unique bundle name ({$vi->bundleName()})", + result: DiagnosticResult::TYPE_DEBUG, + narrative: "LAG interface bundle name (" . $vi->bundleName() . ") is unique for the switch", + ); + } + } + + + /** + * Examine the virtual interface VLAN and Trunks and provide information on it. + * + * @param VirtualInterface $vi + * @return DiagnosticResult + */ + public function trunk( VirtualInterface $vi ): DiagnosticResult { + $mainName = 'Trunk / 802.1q Configuration'; + + $numVlis = count( $vi->vlanInterfaces ); + + if( $numVlis > 1 ) { + + if( $vi->trunk ) { + return new DiagnosticResult( + name: $mainName . " - yes and >1 vlan interfaces", + result: DiagnosticResult::TYPE_DEBUG, + narrative: "More than one vlan interface and so correctly configured as a trunk port", + ); + + } else { // !$vi->trunk + + return new DiagnosticResult( + name: $mainName . " - no but >1 vlan interfaces", + result: DiagnosticResult::TYPE_ERROR, + narrative: "Multiple vlan interfaces exist but virtual interface is not configured as a trunk port", + ); + + } + + } else if( $numVlis === 1 ) { + + if( $vi->trunk ) { + + return new DiagnosticResult( + name: $mainName . " - yes but only one vlan interface", + result: DiagnosticResult::TYPE_INFO, + narrative: "Configured as a trunk port while there is only one vlan interface", + ); + + } else { // !$vi->trunk + + return new DiagnosticResult( + name: $mainName . " - no and single vlan interface", + result: DiagnosticResult::TYPE_DEBUG, + narrative: "Single vlan interface and not configured as a trunk port", + ); + + } + } + + return new DiagnosticResult( + name: $mainName . " - n/a, no vlan interfaces", + result: DiagnosticResult::TYPE_WARN, + narrative: "No VLAN interfaces configured for this virtual interface", + ); + + } + + + + /** + * Examine the virtual interface MTU values and provide information on it. + * + * @param VirtualInterface $vi + * @return DiagnosticResult + */ + public function mtu(VirtualInterface $vi): DiagnosticResult { + $mainName = 'L2 MTU - '; + + if(is_null($vi->mtu) || $vi->mtu === 1500 || $vi->mtu >= 9000) { + return new DiagnosticResult( + name: $mainName . "set to " . ( $vi->mtu ?: 'null' ), + result: DiagnosticResult::TYPE_DEBUG, + narrative: "L2 MTU is set to " . ( $vi->mtu ?: 'null' ), + ); + } else if ($vi->mtu < 1500) { + return new DiagnosticResult( + name: $mainName . "<1500 (" . $vi->mtu . ")", + result: DiagnosticResult::TYPE_ERROR, + narrative: "L2 MTU on virtual interface is <1500 (" . $vi->mtu . ")", + ); + } + + return new DiagnosticResult( + name: $mainName . "non-standard L2 MTU {$vi->mtu}", + result: DiagnosticResult::TYPE_WARN, + narrative: "Non-standard L2 MTU on the virtual interface: " . $vi->mtu, + ); + } + + +} \ No newline at end of file diff --git a/app/Services/Diagnostics/Suites/VlanInterfaceL2DiagnosticSuite.php b/app/Services/Diagnostics/Suites/VlanInterfaceL2DiagnosticSuite.php new file mode 100644 index 000000000..254044bb7 --- /dev/null +++ b/app/Services/Diagnostics/Suites/VlanInterfaceL2DiagnosticSuite.php @@ -0,0 +1,122 @@ + + * @category IXP + * @package IXP\Services\Diagnostics + * @copyright Copyright (C) 2009 - 2024 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ + +class VlanInterfaceL2DiagnosticSuite extends DiagnosticSuite +{ + + /** + * @param VlanInterface $vli + */ + public function __construct( + private readonly VlanInterface $vli, + ) { + + $this->name = 'Vlan Interface / L2 diagnostics on ' . $vli->vlan->name; + $this->description = " "; + $this->type = 'VLAN_INTERFACE'; + + parent::__construct(); + } + + /** + * Run the diagnostics suite + * + * @throws BindingResolutionException + */ + public function run(): static + { + + $this->results->add( $this->arping( $this->vli ) ); + + return $this; + } + + + + /** + * @return DiagnosticResult[] + * + * @psalm-return list + */ + public function arping( VlanInterface $vli ): array + { + $results = []; + + foreach( $vli->layer2addresses as $l2a ) { + + $mainName = $l2a->macFormatted(':') . ' responds to arp pings'; + + try{ + + $result = Process::run( sprintf( config( "ixp.exec.arping.{$vli->vlanid}" ), $l2a->macFormatted(':') ) ); + + if( $result->successful() ) { + + $results[] = new DiagnosticResult( + name: $mainName, + result: DiagnosticResult::TYPE_GOOD, + narrativeHtml: "
{$result->output()}
", + ); + + } + + $results[] = new DiagnosticResult( + name: $mainName . ' - no, see detail for more information', + result: DiagnosticResult::TYPE_ERROR, + narrativeHtml: "
{$result->output()}
", + ); + + } catch(\Exception $e) { + + $results[] = new DiagnosticResult( + name: $mainName . ' - diagnostic failed to run', + result: DiagnosticResult::TYPE_UNKNOWN, + narrativeHtml: $e->getMessage(), + ); + + } + + } + + return $results; + } + +} \ No newline at end of file diff --git a/app/Services/Diagnostics/Suites/VlanInterfaceL3DiagnosticSuite.php b/app/Services/Diagnostics/Suites/VlanInterfaceL3DiagnosticSuite.php new file mode 100644 index 000000000..66c4af1a3 --- /dev/null +++ b/app/Services/Diagnostics/Suites/VlanInterfaceL3DiagnosticSuite.php @@ -0,0 +1,121 @@ + + * @category IXP + * @package IXP\Services\Diagnostics + * @copyright Copyright (C) 2009 - 2024 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ + +class VlanInterfaceL3DiagnosticSuite extends DiagnosticSuite +{ + + /** + * @param VlanInterface $vli + * @param int $protocol + */ + public function __construct( + private readonly VlanInterface $vli, + private readonly int $protocol, + ) { + + $this->name = 'Vlan Interface / L3 diagnostics on ' . $vli->vlan->name . ' via ' . $vli->getIPAddress($this->protocol)->address; + $this->description = " "; + $this->type = 'VLAN_INTERFACE'; + + parent::__construct(); + } + + /** + * Run the diagnostics suite + * + * @throws BindingResolutionException + */ + public function run(): static + { + + $this->results->add( $this->ping( $this->vli, $this->protocol ) ); + + return $this; + } + + + /** + * + * @return DiagnosticResult + */ + public function ping( VlanInterface $vli, int $protocol ): DiagnosticResult + { + $mainName = $vli->getIPAddress($protocol)->address . ' responds to pings'; + + try { + $result = Process::run( sprintf( config( "ixp.exec.ping{$protocol}" ), $vli->getIPAddress($protocol)->address ) ); + + if( $result->successful() ) { + + return new DiagnosticResult( + name: $mainName, + result: DiagnosticResult::TYPE_GOOD, + narrativeHtml: "
{$result->output()}
", + ); + + } else { + + return new DiagnosticResult( + name: $mainName . ' - no, see detail for more information', + result: DiagnosticResult::TYPE_ERROR, + narrativeHtml: "
{$result->output()}
", + ); + + } + + } catch ( \Exception $e ) { + + return new DiagnosticResult( + name: $mainName . ' - diagnostic failed to run', + result: DiagnosticResult::TYPE_UNKNOWN, + narrativeHtml: $e->getMessage(), + ); + + } + } + +} \ No newline at end of file diff --git a/app/Services/FoilEngine.php b/app/Services/FoilEngine.php new file mode 100644 index 000000000..efb8a30cf --- /dev/null +++ b/app/Services/FoilEngine.php @@ -0,0 +1,56 @@ +engine = $engine; + } + + public function engine(): EngineFoil + { + return $this->engine; + } + + /** + * Get the evaluated contents of the view. + * + * @param string $path + * @param array $data + * + * @return string + */ + #[\Override] + public function get( $path, array $data = array() ): string + { + return $this->engine->render( $path, $data ); + } +} \ No newline at end of file diff --git a/app/Services/Grapher.php b/app/Services/Grapher.php new file mode 100644 index 000000000..81f84919c --- /dev/null +++ b/app/Services/Grapher.php @@ -0,0 +1,463 @@ + Mrtg + * + * @author Barry O'Donovan + * @author Yann Robin + * @category IXP + * @package IXP\Services\Grapher + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Grapher +{ + /** + * Is the cache enabled? + * + * @var bool + */ + private $cacheEnabled = false; + + /** + * Is the cache enabled? + * + * @var integer + */ + private $cacheLifetime = 300; + + /** + * Constructor + */ + public function __construct() + { + $this->setupCache(); + } + + /** + * As we allow multiple graphing backends, we need to resolve + * which one we're meant to use here. + * + * The order of resolution is: + * + * 1. As specified in the `$backend` parameter if not null + * 2. First backend in `configs/grapher.php` `backend` element. + * + * @param string|null $backend |null + * + * @return string + * + * @throws + */ + public function resolveBackend( ?string $backend = null ): string + { + $config = config('grapher.backend'); + + if( $backend === null ) { + if( is_array( $config ) && count( $config ) ) { + $backend = config('grapher.backend')[0]; + } else { + throw new ConfigurationException( 'No graphing backend supplied or configured (see configs/grapher.php)' ); + } + } + + if( !is_array( $config ) || !in_array( $backend, $config, true ) ) { + throw new BadBackendException( 'No graphing provider enabled (see configs/grapher.php) for ' . $backend ); + } + + return $backend; + } + + /** + * Return the required grapher for the specified backend + * + * If the backend is not specified, it is resolved via `resolveBackend()`. + * @param string|null $backend A specific backend to return. If not specified, we use command line arguments + * + * @return BackendContract + * + * @throws + * + * @see \IXP\Console\Commands\Grapher\GrapherCommand::resolveBackend() + * + */ + public function backend( $backend = null ): BackendContract + { + $backend = $this->resolveBackend( $backend ); + $backendClass = Config::get( "grapher.providers.{$backend}" ); + return new $backendClass( config('grapher.backends')[ $backend ] ); + } + + /** + * Return the required grapher for the specified graph + * + * @param Graph $graph + * + * @param array|string $backends Limit search to specified backends + * + * @return BackendContract + * + * @throws + */ + public function backendForGraph( Graph $graph, array $backends = [] ): BackendContract + { + if( !count( $backends ) ) { + $backends = config('grapher.backend'); + } + + if( !count( $backends ) ) { + throw new ConfigurationException( 'No graphing backend supplied or configured (see configs/grapher.php)' ); + } + + foreach( $backends as $backend ) { + if( ( $b = $this->backend( $backend ) )->canProcess( $graph ) ) { + return $b; + } + } + + throw new GraphCannotBeProcessedException('No backend available to process this graph'); + } + + /** + * Return the available grapher backends for the specified graph + * + * @param Graph $graph + * + * @return BackendContract[] + * + * @throws + * + * @psalm-return list + */ + public function backendsForGraph( Graph $graph ): array + { + $config = config('grapher.backend'); + if( !is_array( $config ) || !count( $config ) ) { + throw new ConfigurationException( 'No graphing backend supplied or configured (see configs/grapher.php)' ); + } + + $backends = []; + foreach( config('grapher.backend') as $backend ) { + if( ( $b = $this->backend( $backend ) )->canProcess( $graph ) ) { + $backends[] = $b; + } + } + + return $backends; + } + + /** + * Iterate over all configured backends and provide a complete array of what + * graph types are supported + * + * @return array + */ + public function supports(): array + { + $s = []; + + foreach( config('grapher.backend') as $backend ) { + $backendClass = Config::get( "grapher.providers.{$backend}" ); + $s = array_replace_recursive( $s, $backendClass::supports() ); + } + + return $s; + } + + /** + * Get an instance of an IXP graph + * + * @return IXPGraph + */ + public function ixp(): IXPGraph + { + return new IXPGraph( $this ); + } + + /** + * Get an instance of an infrastructure graph + * + * @param Infrastructure $infra + * + * @return InfrastructureGraph + */ + public function infrastructure( Infrastructure $infra ): InfrastructureGraph + { + return new InfrastructureGraph( $this, $infra ); + } + + /** + * Get an instance of an vlan graph + * + * @param Vlan $vlan + * + * @return VlanGraph + */ + public function vlan( Vlan $vlan ): VlanGraph + { + return new VlanGraph( $this, $vlan ); + } + + /** + * Get an instance of a location graph + * + * @param Location $location + * + * @return LocationGraph + */ + public function location( Location $location ): LocationGraph + { + return new LocationGraph( $this, $location ); + } + + /** + * Get an instance of a switch graph + * + * @param Switcher $switch + * + * @return SwitchGraph + */ + public function switch( Switcher $switch ): SwitchGraph + { + return new SwitchGraph( $this, $switch ); + } + + /** + * Get an instance of a trunk graph + * + * @param string $trunkname + * + * @return TrunkGraph + * + * @throws + */ + public function trunk( string $trunkname ): TrunkGraph + { + return new TrunkGraph( $this, $trunkname ); + } + + /** + * Get an instance of a customer aggregate graph + * + * @param Customer $c + * + * @return CustomerGraph + */ + public function customer( Customer $c ): CustomerGraph + { + return new CustomerGraph( $this, $c ); + } + + /** + * Get an instance of a physint graph + * + * @param PhysicalInterface $int + * + * @return PhysIntGraph + */ + public function physint( PhysicalInterface $int ): PhysIntGraph + { + return new PhysIntGraph( $this, $int ); + } + + /** + * Get an instance of a virtint graph + * + * @param VirtualInterface $int + * + * @return PhysIntGraph|VirtIntGraph + */ + public function virtint( VirtualInterface $int ): VirtIntGraph|PhysIntGraph + { + // if there is only one physint, then the user really wants that: + if( $int->physicalInterfaces->count() === 1 ) { + return $this->physint( $int->physicalInterfaces->first() ); + } + return new VirtIntGraph( $this, $int ); + } + + /** + * Get an instance of a vlanint graph + * + * @param VlanInterface $int + * + * @return VlanIntGraph + */ + public function vlanint( VlanInterface $int ): VlanIntGraph + { + return new VlanIntGraph( $this, $int ); + } + + /** + * Get an instance of a CoreBundle aggregate graph + * + * @param CoreBundle $cb + * @param string $side + * + * @return CoreBundleGraph + */ + public function coreBundle( CoreBundle $cb, string $side = 'a' ): CoreBundleGraph + { + return new CoreBundleGraph( $this, $cb, $side ); + } + + /** + * Get an instance of a p2p graph + * + * @param VlanInterface $svli + * @param VlanInterface $dvli + * + * @return P2pGraph + */ + public function p2p( VlanInterface $svli, VlanInterface $dvli ): P2pGraph + { + return new P2pGraph( $this, $svli, $dvli ); + } + + /** + * Get an instance of a latency graph + * + * @param VlanInterface $vli + * + * @return LatencyGraph + * + * @throws + */ + public function latency( VlanInterface $vli ): LatencyGraph + { + return new LatencyGraph( $this, $vli ); + } + + /** + * initialise the cache + * + * @return void + */ + private function setupCache(): void + { + if( config('grapher.cache.enabled', false ) ) { + $this->cacheEnabled = true; + $this->cacheLifetime = config('grapher.cache.lifetime', 5 ); + } else { + $this->cacheEnabled = false; + } + } + + /** + * Is the cache enabled? + * @return bool + */ + public function cacheEnabled(): bool + { + return (bool)$this->cacheEnabled; + } + + /** + * Manually disable the cache + */ + public function disableCache(): void + { + $this->cacheEnabled = false; + } + + /** + * How long do we cache entries for? + * + * @return int (minutes) + */ + public function cacheLifetime(): int + { + return (int)$this->cacheLifetime; + } + + /** + * Get the cache repository + * + * @return Repository + */ + public function cacheRepository(): Repository + { + return Cache::store( config('grapher.cache.store' ) ); + } + + /** + * If the cache is enabled, return a previously cached item or else update / set it + * + * See Laravel's Cache::remember() function + * + * @param string $key + * @param Closure $fn Callback to populate the cache + * + * @return mixed + */ + public function remember( string $key, Closure $fn ) + { + if( $this->cacheEnabled() ) { + return $this->cacheRepository()->remember( $key, $this->cacheLifetime(), $fn ); + } + return $fn(); + } +} \ No newline at end of file diff --git a/app/Services/Grapher/Backend.php b/app/Services/Grapher/Backend.php new file mode 100644 index 000000000..2e58059d0 --- /dev/null +++ b/app/Services/Grapher/Backend.php @@ -0,0 +1,68 @@ + abstract + * + * @author Barry O'Donovan + * @category Grapher + * @package IXP\Services\Grapher + * @copyright Copyright (C) 2009 - 2019 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +abstract class Backend +{ + /** + * Get a complete list of functionality that this backend supports. + * + * @return array + */ + abstract public static function supports(): array; + + /** + * Examines the provided graph object and determines if this backend is able to + * process the request or not. + * + * {inheritDoc} + * + * @param Graph $graph + * @return bool + */ + public function canProcess( Graph $graph ): bool + { + // find what this backend can support + $s = $this->supports(); + + if( isset( $s[ $graph->lcClassType() ] ) + && ( isset($s[ $graph->lcClassType() ]['categories']) && in_array( $graph->category(), $s[ $graph->lcClassType() ]['categories'] ) ) + && ( isset($s[ $graph->lcClassType() ]['periods'] ) && in_array( $graph->period(), $s[ $graph->lcClassType() ]['periods' ] ) ) + && ( isset($s[ $graph->lcClassType() ]['protocols'] ) && in_array( $graph->protocol(), $s[ $graph->lcClassType() ]['protocols' ] ) ) + && ( isset($s[ $graph->lcClassType() ]['types'] ) && in_array( $graph->type(), $s[ $graph->lcClassType() ]['types' ] ) ) + ) { + return true; + } + return false; + } +} \ No newline at end of file diff --git a/app/Services/Grapher/Backend/Dummy.php b/app/Services/Grapher/Backend/Dummy.php new file mode 100644 index 000000000..d54e95d5c --- /dev/null +++ b/app/Services/Grapher/Backend/Dummy.php @@ -0,0 +1,297 @@ + Dummy + * + * @author Barry O'Donovan + * @author Yann Robin + * @category IXP + * @package IXP\Services\Grapher + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Dummy extends GrapherBackend implements GrapherBackendContract +{ + /** + * {@inheritDoc} + * + * @return string + */ + #[\Override] + public function name(): string + { + return 'dummy'; + } + + /** + * The dummy backend required no configuration. + * + * {@inheritDoc} + * + * @return bool + */ + #[\Override] + public function isConfigurationRequired(): bool + { + return false; + } + + /** + * This function indicates whether this graphing engine supports single monolithic text + * + * @see Dummy::isMonolithicConfigurationSupported() for an explanation + * + * @return false + */ + #[\Override] + public function isMonolithicConfigurationSupported(): bool + { + return false; + } + + /** + * This function indicates whether this graphing engine supports multiple files to a directory + * + * @see Dummy::isMonolithicConfigurationSupported() for an explanation + * + * @return false + */ + #[\Override] + public function isMultiFileConfigurationSupported(): bool + { + return false; + } + + /** + * Generate the configuration file(s) for this graphing backend + * + * {inheritDoc} + * + * @param int $type The type of configuration to generate + * @param array $options + * + * @return array + * + * @psalm-return array + */ + #[\Override] + public function generateConfiguration( int $type = self::GENERATED_CONFIG_TYPE_MONOLITHIC, array $options = [] ): array + { + return []; + } + + /** + * Get a complete list of functionality that this backend supports. + * + * {inheritDoc} + * + * @return string[][][] + * + * @psalm-return array{ixp: array{protocols: array{all: 'all', ipv4: 'ipv4', ipv6: 'ipv6'}, categories: array{bits: 'bits', pkts: 'pkts', errs: 'errs', discs: 'discs', bcasts: 'bcasts'}, periods: array{day: 'day', week: 'week', month: 'month', year: 'year'}, types: array}, infrastructure: array{protocols: array{all: 'all', ipv4: 'ipv4', ipv6: 'ipv6'}, categories: array{bits: 'bits', pkts: 'pkts', errs: 'errs', discs: 'discs', bcasts: 'bcasts'}, periods: array{day: 'day', week: 'week', month: 'month', year: 'year'}, types: array}, vlan: array{protocols: array{all: 'all', ipv4: 'ipv4', ipv6: 'ipv6'}, categories: array{bits: 'bits', pkts: 'pkts', errs: 'errs', discs: 'discs', bcasts: 'bcasts'}, periods: array{day: 'day', week: 'week', month: 'month', year: 'year'}, types: array}, trunk: array{protocols: array{all: 'all', ipv4: 'ipv4', ipv6: 'ipv6'}, categories: array{bits: 'bits', pkts: 'pkts', errs: 'errs', discs: 'discs', bcasts: 'bcasts'}, periods: array{day: 'day', week: 'week', month: 'month', year: 'year'}, types: array}, switcher: array{protocols: array{all: 'all', ipv4: 'ipv4', ipv6: 'ipv6'}, categories: array{bits: 'bits', pkts: 'pkts', errs: 'errs', discs: 'discs', bcasts: 'bcasts'}, periods: array{day: 'day', week: 'week', month: 'month', year: 'year'}, types: array}, physicalinterface: array{protocols: array{all: 'all', ipv4: 'ipv4', ipv6: 'ipv6'}, categories: array{bits: 'bits', pkts: 'pkts', errs: 'errs', discs: 'discs', bcasts: 'bcasts'}, periods: array{day: 'day', week: 'week', month: 'month', year: 'year'}, types: array}, virtualinterface: array{protocols: array{all: 'all', ipv4: 'ipv4', ipv6: 'ipv6'}, categories: array{bits: 'bits', pkts: 'pkts', errs: 'errs', discs: 'discs', bcasts: 'bcasts'}, periods: array{day: 'day', week: 'week', month: 'month', year: 'year'}, types: array}, customer: array{protocols: array{all: 'all', ipv4: 'ipv4', ipv6: 'ipv6'}, categories: array{bits: 'bits', pkts: 'pkts', errs: 'errs', discs: 'discs', bcasts: 'bcasts'}, periods: array{day: 'day', week: 'week', month: 'month', year: 'year'}, types: array}, vlaninterface: array{protocols: array{all: 'all', ipv4: 'ipv4', ipv6: 'ipv6'}, categories: array{bits: 'bits', pkts: 'pkts', errs: 'errs', discs: 'discs', bcasts: 'bcasts'}, periods: array{day: 'day', week: 'week', month: 'month', year: 'year'}, types: array}, latency: array{protocols: array{ipv4: 'ipv4', ipv6: 'ipv6'}, categories: array{bits: 'bits', pkts: 'pkts', errs: 'errs', discs: 'discs', bcasts: 'bcasts'}, periods: array{3hours: '3hours', 30hours: '30hours', 10days: '10days', 1year: '1year'}, types: array{png: 'png'}}, p2p: array{protocols: array{all: 'all', ipv4: 'ipv4', ipv6: 'ipv6'}, categories: array{bits: 'bits', pkts: 'pkts', errs: 'errs', discs: 'discs', bcasts: 'bcasts'}, periods: array{day: 'day', week: 'week', month: 'month', year: 'year'}, types: array}} + */ + #[\Override] + public static function supports(): array + { + return [ + 'ixp' => [ + 'protocols' => Graph::PROTOCOLS, + 'categories' => Graph::CATEGORIES, + 'periods' => Graph::PERIODS, + 'types' => Graph::TYPES + ], + 'infrastructure' => [ + 'protocols' => Graph::PROTOCOLS, + 'categories' => Graph::CATEGORIES, + 'periods' => Graph::PERIODS, + 'types' => Graph::TYPES + ], + 'vlan' => [ + 'protocols' => Graph::PROTOCOLS, + 'categories' => Graph::CATEGORIES, + 'periods' => Graph::PERIODS, + 'types' => Graph::TYPES + ], + 'trunk' => [ + 'protocols' => Graph::PROTOCOLS, + 'categories' => Graph::CATEGORIES, + 'periods' => Graph::PERIODS, + 'types' => Graph::TYPES + ], + 'switcher' => [ + 'protocols' => Graph::PROTOCOLS, + 'categories' => Graph::CATEGORIES, + 'periods' => Graph::PERIODS, + 'types' => Graph::TYPES + ], + 'physicalinterface' => [ + 'protocols' => Graph::PROTOCOLS, + 'categories' => Graph::CATEGORIES, + 'periods' => Graph::PERIODS, + 'types' => Graph::TYPES + ], + 'virtualinterface' => [ + 'protocols' => Graph::PROTOCOLS, + 'categories' => Graph::CATEGORIES, + 'periods' => Graph::PERIODS, + 'types' => Graph::TYPES + ], + 'customer' => [ + 'protocols' => Graph::PROTOCOLS, + 'categories' => Graph::CATEGORIES, + 'periods' => Graph::PERIODS, + 'types' => Graph::TYPES + ], + 'vlaninterface' => [ + 'protocols' => Graph::PROTOCOLS, + 'categories' => Graph::CATEGORIES, + 'periods' => Graph::PERIODS, + 'types' => Graph::TYPES + ], + 'latency' => [ + 'protocols' => Graph::PROTOCOLS_REAL, + 'categories' => Graph::CATEGORIES, + 'periods' => LatencyGraph::PERIODS, + 'types' => [ Graph::TYPE_PNG => Graph::TYPE_PNG ], + ], + 'p2p' => [ + 'protocols' => Graph::PROTOCOLS, + 'categories' => Graph::CATEGORIES, + 'periods' => Graph::PERIODS, + 'types' => Graph::TYPES + ], + ]; + } + + /** + * Get the data points for a given graph + * + * {inheritDoc} + * + * @param Graph $graph + * + * @return array + * + * @throws + * + * @psalm-return array, mixed> + */ + #[\Override] + public function data( Graph $graph ): array + { + $dummy = new DummyFile( $this->resolveFilePath( $graph, 'log' ) ); + return $dummy->data( $graph ); + } + + /** + * Get the PNG image for a given graph + * + * {inheritDoc} + * + * @param Graph $graph + * + * @return false|string + */ + #[\Override] + public function png( Graph $graph ): false|string + { + return @file_get_contents( $this->resolveFilePath( $graph, 'png' ) ); + } + + /** + * Get the RRD file for a given graph + * + * {inheritDoc} + * + * @param Graph $graph + * + * @return string + * + * @psalm-return '' + */ + #[\Override] + public function rrd( Graph $graph ): false|string + { + return ''; + } + + /** + * Get the path to the graphing data file (e.g. path to log or rrd file). + * + * {inheritDoc} + * + * @param Graph $graph + * + * @return string Path or empty string + * + * @throws CannotHandleRequestException + */ + #[\Override] + public function dataPath( Graph $graph ): string + { + return $this->resolveFilePath( $graph, 'log' ); + } + + /** + * For a given graph, return the path where the appropriate log file + * will be found. + * + * @param Graph $graph + * @param string $type + * + * @return string + * + * @throws + * + * @psalm-param 'log'|'png' $type + */ + private function resolveFilePath( Graph $graph, string $type ): string + { + $config = config('grapher.backends.dummy'); + + switch( $graph->classType() ) { + default: + $file = sprintf( "%s/dummy%s.%s", $config['logdir'], $type === 'log' ? '' : "-{$graph->period()}", $type ); + if( !file_exists( $file ) ) { + throw new CannotHandleRequestException("Backend asserted it could process but cannot handle graph of type: {$graph->type()}" ); + } + return $file; + break; + } + } +} \ No newline at end of file diff --git a/app/Services/Grapher/Backend/Mrtg.php b/app/Services/Grapher/Backend/Mrtg.php new file mode 100644 index 000000000..6571a0324 --- /dev/null +++ b/app/Services/Grapher/Backend/Mrtg.php @@ -0,0 +1,617 @@ + Mrtg + * + * @author Barry O'Donovan + * @author Yann Robin + * @category Grapher + * @package IXP\Services\Grapher + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Mrtg extends GrapherBackend implements GrapherBackendContract +{ + /** + * {@inheritDoc} + * + * @return string + */ + #[\Override] + public function name(): string + { + return 'mrtg'; + } + + /** + * The dummy backend requires no configuration. + * + * {@inheritDoc} + * + * @return bool + */ + #[\Override] + public function isConfigurationRequired(): bool + { + return true; + } + + /** + * This function indicates whether this graphing engine supports single monolithic text + * + * @see \IXP\Contracts\Grapher::isMonolithicConfigurationSupported() for an explanation + * + * @return true + */ + #[\Override] + public function isMonolithicConfigurationSupported(): bool + { + return true; + } + + /** + * This function indicates whether this graphing engine supports multiple files to a directory + * + * @see \IXP\Contracts\Grapher::isMonolithicConfigurationSupported() for an explanation + * + * @return false + */ + #[\Override] + public function isMultiFileConfigurationSupported(): bool + { + return false; + } + + /** + * Generate the configuration file(s) for this graphing backend + * + * {inheritDoc} + * + * @param int $type The type of configuration to generate + * @param array $options + * + * @return string[] + * + * @psalm-return list{string} + */ + #[\Override] + public function generateConfiguration( int $type = self::GENERATED_CONFIG_TYPE_MONOLITHIC, array $options = [] ): array + { + return [ + View::make( 'services.grapher.mrtg.monolithic', [ + 'data' => $this->getPeeringPorts(), + 'snmppasswd' => config('grapher.backends.mrtg.snmppasswd'), + ] + )->render(), + ]; + } + + /** + * Utility function to slurp all peering ports from the database and arrange them in + * arrays for genertaing Mrtg configuration files. + * + * The array returned is an array of arrays containing: + * + * array `['pis']` of PhysicalInterfaceEntity objects indexed by their ID + * array `['custs']` of Customer objects indexed by their ID + * array `['sws']` of Switcher objects indexed by their ID + * array `['locs']` of Location objects indexed by their ID + * array `['infras']` of Infrastructure objects indexed by their ID + * array `['custports']` containing an array of PhysicalInterfaceEntity IDs indexed by customer ID + * array `['custlags']` containing an array of PhysicalInterfaceEntity IDs contained in an array indexed + * by VirtualInterfaceEntity IDs in turn in an array of customer IDs: + * `['custlags'][$custid][$viid][]` + * array `['swports']` indexed by Switcher ID conataining the PhysicalInterfaceEntity IDs of peering ports + * array `['infraports']` indexed by Infrastructure ID conataining the PhysicalInterfaceEntity IDs of peering ports + * array `['ixpports']` conataining the PhysicalInterfaceEntity IDs of peering ports + * + * @return (((int|int[])[]|Customer|Infrastructure|PhysicalInterface|\IXP\Models\CoreBundle|\IXP\Models\Location|\IXP\Models\Switcher|float|int|null)[]|float|int)[] + * + * @psalm-return array{ixpports: list, ixpports_maxbytes: float|int, infras: array<''|int, Infrastructure|null>, infraports: array<''|int, non-empty-list>, infraports_maxbytes: array<''|int, float|int>, custs: array, custports: array>, custlags: array>>, locs: array<''|int, \IXP\Models\Location|null>, locports: array<''|int, non-empty-list>, locports_maxbytes: array<''|int, float|int>, sws: array<''|int, \IXP\Models\Switcher|null>, swports: array<''|int, list{int,...}>, swports_maxbytes: array<''|int, float|int>, cbs: array, cbports: array>, cbbundles: array, pis?: array} + */ + public function getPeeringPorts(): array + { + $data = []; + $data['ixpports'] = []; + $data['ixpports_maxbytes'] = 0; + $data['infras'] = []; + $data['infraports'] = []; + $data['infraports_maxbytes'] = []; + $data['custs'] = []; + $data['custports'] = []; + $data['custlags'] = []; + $data['locs'] = []; + $data['locports'] = []; + $data['locports_maxbytes'] = []; + $data['sws'] = []; + $data['swports'] = []; + $data['swports_maxbytes'] = []; + $data['cbs'] = []; + $data['cbports'] = []; + $data['cbbundles'] = []; + + + // we need to wrap switch ports in physical interfaces for switch aggregates and, as such, we need to use unused physical interface IDs + $maxPiID = 0; + + foreach( Customer::all() as $c ) { + foreach( $c->virtualInterfaces as $vi ) { + // we do not include core bundle interfaces here + if( $vi->getCoreBundle() !== false ) { + continue; + } + + /** @var PhysicalInterface $pi */ + foreach( $vi->physicalInterfaces as $pi ) { + if( $pi->id > $maxPiID ) { + $maxPiID = $pi->id; + } + + // per inex/IXP-Manager##746 - added ifIndex check to skip manually added dummy ports + if( !$pi->isConnectedOrQuarantine() + || !$pi->switchPort->ifIndex + || !( $pi->switchPort->switcher->active && $pi->switchPort->switcher->poll ) + ) { + continue; + } + + $data[ 'pis' ][ $pi->id ] = $pi; + + if( !isset( $data[ 'custs' ][ $c->id ] ) ) { + $data[ 'custs' ][ $c->id ] = $c; + } + + if( !isset( $data['sws'][ $pi->switchPort->switcher->id ] ) ) { + $s = $pi->switchPort->switcher; + $data['sws'][ $s->id ] = $s; + $data['swports_maxbytes'][ $s->id ] = 0; + } + + if( !isset( $data['locs'][ $pi->switchPort->switcher->cabinet->location->id ] ) ) { + $l = $pi->switchPort->switcher->cabinet->location; + $data['locs'][ $l->id ] = $l; + $data['locports_maxbytes'][ $l->id ] = 0; + } + + if( !isset( $data['infras'][ $pi->switchPort->switcher->infrastructureModel->id ] ) ) { + $i = $pi->switchPort->switcher->infrastructureModel; + $data['infras'][ $i->id ] = $i; + $data['infraports_maxbytes'][ $i->id ] = 0; + } + + + $data[ 'custports' ][ $c->id ][] = $pi->id; + + if( $vi->physicalInterfaces->count() > 1 ) { + $data[ 'custlags' ][ $c->id ][ $vi->id ][] = $pi->id; + } + + $maxbytes = $pi->detectedSpeed() * 1000000 / 8; // Mbps * bps / to bytes + $switcher = $pi->switchPort->switcher; + $location = $pi->switchPort->switcher->cabinet->location; + + // don't count reseller ports or fanout ports in agregates + if( !$pi->switchPort->typeReseller() && !$pi->switchPort->typeFanout() ) { + $data[ 'swports' ][ $pi->switchPort->switcher->id ][] = $pi->id; + $data[ 'locports' ][ $pi->switchPort->switcher->cabinet->location->id ][] = $pi->id; + $data[ 'infraports' ][ $pi->switchPort->switcher->infrastructureModel->id ][] = $pi->id; + $data[ 'ixpports' ][] = $pi->id; + + $data['swports_maxbytes' ][ $switcher->id ] += $maxbytes; + $data['locports_maxbytes' ][ $location->id ] += $maxbytes; + $data['infraports_maxbytes'][ $switcher->infrastructureModel->id ] += $maxbytes; + $data['ixpports_maxbytes'] += $maxbytes; + } + + } + } + } + + // core bundles + foreach( Infrastructure::all() as $infra ) { + foreach( $infra->switchers as $switch ) { + if( !( $switch->active && $switch->poll ) ) { + continue; + } + + if( !isset( $data['sws'][ $switch->id ] ) ) { + $data['sws'][$switch->id ] = $switch; + } + + // Handle Core Bundles + foreach( $switch->getCoreBundles() as $cb ) { + // because we iterate through each switch, we see each $cb twice + if( isset( $data['cbs'][ $cb->id ] ) ) { + continue; + } + + $data['cbs'][ $cb->id ] = $cb; + + foreach( $cb->corelinks as $cl ) { + foreach( [ 'sidea', 'sideb' ] as $side ) { + $pi = ( $side === 'sidea' ) ? + $cl->coreinterfacesidea->physicalinterface + : $cl->coreinterfacesideb->physicalinterface; + + $data[ 'cbports' ][ $cb->id ][ $cl->id ][ $side ] = $pi->id; + + if( !isset( $data[ 'pis' ][ $pi->id ] ) ) { + $data[ 'pis' ][ $pi->id ] = $pi; + } + + if( $pi->id > $maxPiID ) { + $maxPiID = $pi->id; + } + + $data[ 'cbbundles' ][ $cb->id ][ $side ][] = $pi->id; + } + } + } + } + } + + // include core + fanout + reseller switch ports in switch aggregates ("how much work is the switch doing?") + // This is a slight hack as the template requires PhysicalInterfaces so we wrap core SwitchPorts in temporary PhyInts. + foreach( Infrastructure::all() as $infra ) { + foreach( $infra->switchers as $switch ) { + + if( !( $switch->active && $switch->poll ) ) { + continue; + } + + /** @var SwitchPort $sp */ + foreach( $switch->switchPorts as $sp ) { + if( $sp->typeCore() || $sp->typeReseller() || $sp->typeFanout() ) { + + // this needs to be wrapped in a physical interface for the template + // [TODO 2023-06 is this necessary with CoreBundles? Probably for anyone not using them...] + if( $sp->physicalInterface ) { + $pi = $sp->physicalInterface; + } else { + $pi = $this->wrapSwitchPortInPhysicalInterface( $sp, ++$maxPiID ); + } + $data[ 'pis' ][ $pi->id ] = $pi; + $data[ 'swports' ][ $switch->id ][] = $pi->id; + + if( !isset( $data[ 'swports_maxbytes' ][ $switch->id ] ) ) { + $data[ 'swports_maxbytes' ][ $switch->id ] = 0; + } + + $data[ 'swports_maxbytes' ][ $switch->id ] += ( ( $pi->detectedSpeed() > 0 ) ? $pi->detectedSpeed() : 1 ) * 1000000 / 8; + } + } + } + } + + return $data; + } + + /** + * Wrap a switch port in a temporary PhysicalInterface. + * + * @see getPeeringPorts() for usage + * + * @param SwitchPort $sp + * @param int $id The ID to set in the physical interface + * + * @return PhysicalInterface + */ + public function wrapSwitchPortInPhysicalInterface( SwitchPort $sp, int $id ): PhysicalInterface + { + $pi = new PhysicalInterface; + $pi->id = $id; + $pi->switchportid = $sp->id; + $pi->speed = $sp->ifHighSpeed; + return $pi; + } + + /** + * Get a complete list of functionality that this backend supports. + * + * {inheritDoc} + * + * @return string[][][] + * + * @psalm-return array{ixp: array{protocols: array{all: 'all'}, categories: array{bits: 'bits', pkts: 'pkts'}, periods: array{day: 'day', week: 'week', month: 'month', year: 'year'}, types: array}, infrastructure: array{protocols: array{all: 'all'}, categories: array{bits: 'bits', pkts: 'pkts'}, periods: array{day: 'day', week: 'week', month: 'month', year: 'year'}, types: array}, location: array{protocols: array{all: 'all'}, categories: array{bits: 'bits', pkts: 'pkts'}, periods: array{day: 'day', week: 'week', month: 'month', year: 'year'}, types: array}, switcher: array{protocols: array{all: 'all'}, categories: array{bits: 'bits', pkts: 'pkts'}, periods: array{day: 'day', week: 'week', month: 'month', year: 'year'}, types: array}, trunk: array{protocols: array{all: 'all'}, categories: array{bits: 'bits'}, periods: array{day: 'day', week: 'week', month: 'month', year: 'year'}, types: array}, corebundle: array{protocols: array{all: 'all'}, categories: array{bits: 'bits', pkts: 'pkts', errs: 'errs', discs: 'discs', bcasts: 'bcasts'}, periods: array{day: 'day', week: 'week', month: 'month', year: 'year'}, types: array}, physicalinterface: array{protocols: array{all: 'all'}, categories: array{bits: 'bits', pkts: 'pkts', errs: 'errs', discs: 'discs', bcasts: 'bcasts'}, periods: array{day: 'day', week: 'week', month: 'month', year: 'year'}, types: array}, virtualinterface: array{protocols: array{all: 'all'}, categories: array{bits: 'bits', pkts: 'pkts', errs: 'errs', discs: 'discs', bcasts: 'bcasts'}, periods: array{day: 'day', week: 'week', month: 'month', year: 'year'}, types: array}, customer: array{protocols: array{all: 'all'}, categories: array{bits: 'bits', pkts: 'pkts', errs: 'errs', discs: 'discs', bcasts: 'bcasts'}, periods: array{day: 'day', week: 'week', month: 'month', year: 'year'}, types: array}} + */ + #[\Override] + public static function supports(): array + { + $rrd = config('grapher.backends.mrtg.dbtype') === 'rrd'; + + $graphTypes = Graph::TYPES; + unset( $graphTypes[ Graph::TYPE_RRD ] ); + + return [ + 'ixp' => [ + 'protocols' => [ Graph::PROTOCOL_ALL => Graph::PROTOCOL_ALL ], + 'categories' => [ Graph::CATEGORY_BITS => Graph::CATEGORY_BITS, + Graph::CATEGORY_PACKETS => Graph::CATEGORY_PACKETS ], + 'periods' => Graph::PERIODS, + 'types' => $rrd ? Graph::TYPES : $graphTypes, + ], + 'infrastructure' => [ + 'protocols' => [ Graph::PROTOCOL_ALL => Graph::PROTOCOL_ALL ], + 'categories' => [ Graph::CATEGORY_BITS => Graph::CATEGORY_BITS, + Graph::CATEGORY_PACKETS => Graph::CATEGORY_PACKETS ], + 'periods' => Graph::PERIODS, + 'types' => $rrd ? Graph::TYPES : $graphTypes, + ], + 'location' => [ + 'protocols' => [ Graph::PROTOCOL_ALL => Graph::PROTOCOL_ALL ], + 'categories' => [ Graph::CATEGORY_BITS => Graph::CATEGORY_BITS, + Graph::CATEGORY_PACKETS => Graph::CATEGORY_PACKETS ], + 'periods' => Graph::PERIODS, + 'types' => $rrd ? Graph::TYPES : $graphTypes, + ], + 'switcher' => [ + 'protocols' => [ Graph::PROTOCOL_ALL => Graph::PROTOCOL_ALL ], + 'categories' => [ Graph::CATEGORY_BITS => Graph::CATEGORY_BITS, + Graph::CATEGORY_PACKETS => Graph::CATEGORY_PACKETS ], + 'periods' => Graph::PERIODS, + 'types' => $rrd ? Graph::TYPES : $graphTypes, + ], + 'trunk' => [ + 'protocols' => [ Graph::PROTOCOL_ALL => Graph::PROTOCOL_ALL ], + 'categories' => [ Graph::CATEGORY_BITS => Graph::CATEGORY_BITS ], + 'periods' => Graph::PERIODS, + 'types' => $rrd ? Graph::TYPES : $graphTypes, + ], + 'corebundle' => [ + 'protocols' => [ Graph::PROTOCOL_ALL => Graph::PROTOCOL_ALL ], + 'categories' => Graph::CATEGORIES, + 'periods' => Graph::PERIODS, + 'types' => $rrd ? Graph::TYPES : $graphTypes, + ], + 'physicalinterface' => [ + 'protocols' => [ Graph::PROTOCOL_ALL => Graph::PROTOCOL_ALL ], + 'categories' => Graph::CATEGORIES, + 'periods' => Graph::PERIODS, + 'types' => $rrd ? Graph::TYPES : $graphTypes, + ], + 'virtualinterface' => [ + 'protocols' => [ Graph::PROTOCOL_ALL => Graph::PROTOCOL_ALL ], + 'categories' => Graph::CATEGORIES, + 'periods' => Graph::PERIODS, + 'types' => $rrd ? Graph::TYPES : $graphTypes, + ], + 'customer' => [ + 'protocols' => [ Graph::PROTOCOL_ALL => Graph::PROTOCOL_ALL ], + 'categories' => Graph::CATEGORIES, + 'periods' => Graph::PERIODS, + 'types' => $rrd ? Graph::TYPES : $graphTypes, + ], + ]; + } + + /** + * Get the data points for a given graph + * + * {inheritDoc} + * + * @param Graph $graph + * + * @return array + * + * @throws + */ + #[\Override] + public function data( Graph $graph ): array + { + try { + if( config('grapher.backends.mrtg.dbtype') === 'log' ) { + $mrtg = new MrtgFile( $this->resolveFilePath( $graph, 'log' ) ); + return $mrtg->data( $graph ); + } + + $rrd = new RrdUtil( $this->resolveFilePath( $graph, 'rrd' ), $graph ); + return $rrd->data(); + } catch( FileErrorException $e ) { + Log::notice("[Grapher] {$this->name()} data(): could not load file {$this->resolveFilePath( $graph, config('grapher.backends.mrtg.dbtype') )}"); + return []; + } + } + + /** + * Get the PNG image for a given graph + * + * {inheritDoc} + * + * @param Graph $graph + * + * @return false|string + */ + #[\Override] + public function png( Graph $graph ): false|string + { + try { + if( config('grapher.backends.mrtg.dbtype') === 'log' ) { + if( ( $img = @file_get_contents( $this->resolveFilePath( $graph, 'png' ) ) ) === false ) { + // couldn't load the image so return a placeholder + Log::notice( "[Grapher] {$this->name()} png(): could not load file {$this->resolveFilePath( $graph, 'png' )}" ); + return @file_get_contents( public_path() . "/images/image-missing.png" ); + } + return $img; + } + + $rrd = new RrdUtil( $this->resolveFilePath( $graph, 'rrd' ), $graph ); + return @file_get_contents( $rrd->png() ); + } catch( FileErrorException $e ) { + Log::notice("[Grapher] {$this->name()} png(): could not load rrd file " . ( isset( $rrd ) ? $rrd->file() : '???' ) ); + return ''; // FIXME check handling of this + } + } + + /** + * Get the RRD file for a given graph + * + * {inheritDoc} + * + * @param Graph $graph + * @return false|string + */ + #[\Override] + public function rrd( Graph $graph ): false|string + { + try { + if( config('grapher.backends.mrtg.dbtype') === 'log' ) { + return ''; + } + return file_get_contents( $this->resolveFilePath( $graph, 'rrd' ) ); + } catch( FileErrorException $e ) { + Log::notice("[Grapher] {$this->name()} rrd(): could not load file {$this->resolveFilePath( $graph, 'rrd' )}"); + return ''; + } + } + + /** + * Get the path to the graphing data file (e.g. path to log or rrd file). + * + * {inheritDoc} + * + * @param Graph $graph + * + * @return string Path or empty string + * + * @throws + */ + #[\Override] + public function dataPath( Graph $graph ): string + { + try { + if( config( 'grapher.backends.mrtg.dbtype' ) === 'log' ) { + return $this->resolveFilePath( $graph, 'log' ); + } + + return $this->resolveFilePath( $graph, 'rrd' ); + } catch( CannotHandleRequestException $e ) { + return ''; + } + } + + /** + * For larger IXPs, allow sharding of directories over 16 possible base directories + * + * @param int $id The customer entity id + * + * @return string shared path -> e.g. 18 -> 18 % 16 = 2 / 00016 -> 2/00016 + */ + private function shardMemberDir( int $id ): string + { + return sprintf( "%x/%05d", $id % 16, $id ); + } + + /** + * For a given graph, return the path where the appropriate log file + * will be found. + * + * @param Graph $graph + * @param string $type + * + * @return string + * + * @throws + */ + public function resolveFilePath( Graph $graph, string $type ): string + { + $config = config('grapher.backends.mrtg'); + $loggyType = $type === 'rrd' || $type === 'log'; + + switch( $graph->classType() ) { + case 'IXP': + /** @var Graph\IXP $graph */ + return sprintf( "%s/ixp/ixp%03d-%s%s.%s", $config[ 'logdir' ], 1, + $graph->category(), $loggyType ? '' : "-{$graph->period()}", $type ); + case 'Infrastructure': + /** @var Graph\Infrastructure $graph */ + return sprintf( "%s/infras/%03d/ixp%03d-infra%03d-%s%s.%s", $config['logdir'], + $graph->infrastructure()->id, 1, + $graph->infrastructure()->id, $graph->category(), $loggyType ? '' : "-{$graph->period()}", $type ); + case 'Location': + /** @var Graph\Location $graph */ + return sprintf( "%s/locations/%03d/location-aggregate-%05d-%s%s.%s", $config['logdir'], + $graph->location()->id, $graph->location()->id, + $graph->category(), $loggyType ? '' : "-{$graph->period()}", $type ); + case 'Switcher': + /** @var Graph\Switcher $graph */ + return sprintf( "%s/switches/%03d/switch-aggregate-%05d-%s%s.%s", $config['logdir'], + $graph->switch()->id, $graph->switch()->id, + $graph->category(), $loggyType ? '' : "-{$graph->period()}", $type ); + case 'Trunk': + /** @var Graph\Trunk $graph */ + return sprintf( "%s/trunks/%s%s.%s", $config['logdir'], $graph->trunkname(), + $loggyType ? '' : "-{$graph->period()}", $type ); + case 'CoreBundle': + /** @var Graph\CoreBundle $graph */ + return sprintf( "%s/corebundles/%05d/%s-%s%s.%s", $config['logdir'], + $graph->coreBundle()->id, + $graph->identifier(), $graph->category(), + $loggyType ? '' : "-{$graph->period()}", $type ); + case 'PhysicalInterface': + /** @var Graph\PhysicalInterface $graph */ + return sprintf( "%s/members/%s/ints/%s-%s%s.%s", $config['logdir'], + $this->shardMemberDir( $graph->physicalInterface()->virtualInterface->customer->id ), + $graph->identifier(), $graph->category(), + $loggyType ? '' : "-{$graph->period()}", $type ); + case 'VirtualInterface': + /** @var Graph\VirtualInterface $graph */ + return sprintf( "%s/members/%s/lags/%s-%s%s.%s", $config['logdir'], + $this->shardMemberDir( $graph->virtualInterface()->customer->id ), + $graph->identifier(), $graph->category(), + $loggyType ? '' : "-{$graph->period()}", $type ); + case 'Customer': + /** @var Graph\Customer $graph */ + return sprintf( "%s/members/%s/%s-%s%s.%s", $config['logdir'], + $this->shardMemberDir( $graph->customer()->id ), + $graph->identifier(), $graph->category(), + $loggyType ? '' : "-{$graph->period()}", $type ); + default: + throw new CannotHandleRequestException("Backend asserted it could process but cannot handle graph of type: {$graph->type()}" ); + } + } +} \ No newline at end of file diff --git a/app/Services/Grapher/Backend/Sflow.php b/app/Services/Grapher/Backend/Sflow.php new file mode 100644 index 000000000..2a3841414 --- /dev/null +++ b/app/Services/Grapher/Backend/Sflow.php @@ -0,0 +1,331 @@ + Sflow + * + * @author Barry O'Donovan + * @author Yann Robin + * @category Ixp + * @package IXP\Services\Grapher + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Sflow extends GrapherBackend implements GrapherBackendContract +{ + /** + * {@inheritDoc} + * + * @return string + */ + #[\Override] + public function name(): string + { + return 'sflow'; + } + + /** + * The sflow backend requires no configuration. + * + * {@inheritDoc} + * + * @return bool + */ + #[\Override] + public function isConfigurationRequired(): bool + { + return false; + } + + /** + * This function indicates whether this graphing engine supports single monolithic text + * + * @see \IXP\Contracts\Grapher::isMonolithicConfigurationSupported() for an explanation + * + * @return false + */ + #[\Override] + public function isMonolithicConfigurationSupported(): bool + { + return false; + } + + /** + * This function indicates whether this graphing engine supports multiple files to a directory + * + * @see \IXP\Contracts\Grapher::isMonolithicConfigurationSupported() for an explanation + * + * @return false + */ + #[\Override] + public function isMultiFileConfigurationSupported(): bool + { + return false; + } + + /** + * Generate the configuration file(s) for this graphing backend + * + * {inheritDoc} + * + * @param int $type The type of configuration to generate + * @param array $options + * + * @return array + * + * @psalm-return array + */ + #[\Override] + public function generateConfiguration( int $type = self::GENERATED_CONFIG_TYPE_MONOLITHIC, array $options = [] ): array + { + return []; + } + + /** + * Get a complete list of functionality that this backend supports. + * + * {inheritDoc} + * + * @return string[][][] + * + * @psalm-return array{vlan: array{protocols: array{ipv4: 'ipv4', ipv6: 'ipv6'}, categories: array{bits: 'bits', pkts: 'pkts'}, periods: array{day: 'day', week: 'week', month: 'month', year: 'year'}, types: array}, vlaninterface: array{protocols: array{ipv4: 'ipv4', ipv6: 'ipv6'}, categories: array{bits: 'bits', pkts: 'pkts'}, periods: array{day: 'day', week: 'week', month: 'month', year: 'year'}, types: array}, p2p: array{protocols: array{ipv4: 'ipv4', ipv6: 'ipv6'}, categories: array{bits: 'bits', pkts: 'pkts'}, periods: array{day: 'day', week: 'week', month: 'month', year: 'year', custom: 'custom'}, types: array}} + */ + #[\Override] + public static function supports(): array + { + $graphProtocols = Graph::PROTOCOLS; + unset( $graphProtocols[ Graph::PROTOCOL_ALL ] ); + + return [ + 'vlan' => [ + 'protocols' => Graph::PROTOCOLS_REAL, + 'categories' => [ Graph::CATEGORY_BITS => Graph::CATEGORY_BITS, + Graph::CATEGORY_PACKETS => Graph::CATEGORY_PACKETS ], + 'periods' => Graph::PERIODS, + 'types' => Graph::TYPES, + ], + 'vlaninterface' => [ + 'protocols' => $graphProtocols, + 'categories' => [ Graph::CATEGORY_BITS => Graph::CATEGORY_BITS, + Graph::CATEGORY_PACKETS => Graph::CATEGORY_PACKETS ], + 'periods' => Graph::PERIODS, + 'types' => Graph::TYPES, + ], + 'p2p' => [ + 'protocols' => $graphProtocols, + 'categories' => [ Graph::CATEGORY_BITS => Graph::CATEGORY_BITS, + Graph::CATEGORY_PACKETS => Graph::CATEGORY_PACKETS ], + 'periods' => Graph::PERIODS_EXTENDED, + 'types' => Graph::TYPES, + ], + ]; + } + + + + + /** + * Get the data points for a given graph + * + * {inheritDoc} + * + * @param Graph $graph + * + * @return array + * + * @throws + */ + #[\Override] + public function data( Graph $graph ): array + { + try { + $rrd = new RrdUtil( $this->resolveFilePath( $graph, 'rrd' ), $graph ); + return $rrd->data(); + } catch( FileErrorException $e ) { + Log::notice("[Grapher] {$this->name()} data(): could not load file {$this->resolveFilePath( $graph, 'rrd' )}"); + return []; + } + } + + /** + * Get the path to the graphing data file (e.g. path to log or rrd file). + * + * {inheritDoc} + * + * @param Graph $graph + * + * @return string Path or empty string + * + * @throws + */ + #[\Override] + public function dataPath( Graph $graph ): string + { + return $this->resolveFilePath( $graph, 'rrd' ); + } + + /** + * Get the PNG image for a given graph + * + * {inheritDoc} + * + * @param Graph $graph + * + * @return false|string + */ + #[\Override] + public function png( Graph $graph ): false|string + { + try { + $rrd = new RrdUtil( $this->resolveFilePath( $graph, 'rrd' ), $graph ); + return @file_get_contents( $rrd->png() ); + } catch( FileErrorException $e ) { + Log::notice("[Grapher] {$this->name()} png(): could not load rrd file " . ( isset( $rrd ) ? $rrd->file() : '???' ) ); + return ''; // FIXME check handling of this + } + } + + /** + * Get the RRD file for a given graph + * + * {inheritDoc} + * + * @param Graph $graph + * + * @return string + */ + #[\Override] + public function rrd( Graph $graph ): string + { + try { + $rrd = new RrdUtil( $this->resolveFilePath( $graph, 'rrd' ), $graph ); + return $rrd->rrd(); + } catch( FileErrorException $e ) { + Log::notice("[Grapher] {$this->name()} rrd(): could not load rrd file {$rrd->file()}"); + return ''; // FIXME check handling of this + } + } + + /** + * Our sflow/p2p script stores as bytes and names directories / files accordingly. This + * function just s/bits/bytes for accessing these files. + * + * @param string $c + * + * @return string + */ + private function translateCategory( $c ): string + { + if( $c === Graph::CATEGORY_BITS ) { + return 'bytes'; + } + return $c; + } + + /** + * For a given graph, return the filename where the appropriate data + * will be found. + * + * @param Graph $graph + * @param string $type + * + * @return string + * + * @throws + */ + private function resolveFileName( Graph $graph, string $type ): string + { + switch( $graph->classType() ) { + case 'Vlan': + /** @var Graph\Vlan $graph */ + return sprintf( "aggregate.%s.%s.vlan%05d.%s", + $graph->protocol(), $this->translateCategory( $graph->category() ), + $graph->vlan()->number, $type ); + case 'VlanInterface': + /** @var Graph\VlanInterface $graph */ + return sprintf( "individual.%s.%s.src-%05d.%s", + $graph->protocol(), $this->translateCategory( $graph->category() ), + $graph->vlanInterface()->id, $type ); + case 'P2p': + /** @var Graph\P2p $graph */ + return sprintf( "p2p.%s.%s.src-%05d.dst-%05d.%s", + $graph->protocol(), $this->translateCategory( $graph->category() ), + $graph->svli()->id, $graph->dvli()->id, $type ); + default: + throw new CannotHandleRequestException("Backend asserted it could process but cannot handle graph of type: {$graph->type()}" ); + } + } + + /** + * For a given graph, return the path where the appropriate file + * will be found. + * + * @param Graph $graph + * + * @return string + * + * @throws + * + * @psalm-param 'rrd' $type + */ + private function resolveFilePath( Graph $graph, string $type ): string + { + $config = config('grapher.backends.sflow'); + + switch( $graph->classType() ) { + case 'Vlan': + /** @var Graph\Vlan $graph */ + return sprintf( "%s/%s/%s/aggregate/%s", $config['root'], + $graph->protocol(), $this->translateCategory( $graph->category() ), + $this->resolveFileName( $graph, $type ) ); + case 'VlanInterface': + /** @var Graph\VlanInterface $graph */ + return sprintf( "%s/%s/%s/individual/%s", $config['root'], + $graph->protocol(), $this->translateCategory( $graph->category() ), + $this->resolveFileName( $graph, $type ) ); + case 'P2p': + /** @var Graph\P2p $graph */ + return sprintf( "%s/%s/%s/p2p/src-%05d/%s", $config['root'], + $graph->protocol(), $this->translateCategory( $graph->category() ), + $graph->svli()->id, $this->resolveFileName( $graph, $type ) ); + default: + throw new CannotHandleRequestException("Backend asserted it could process but cannot handle graph of type: {$graph->type()}" ); + } + } +} \ No newline at end of file diff --git a/app/Services/Grapher/Backend/Smokeping.php b/app/Services/Grapher/Backend/Smokeping.php new file mode 100644 index 000000000..1faaa8025 --- /dev/null +++ b/app/Services/Grapher/Backend/Smokeping.php @@ -0,0 +1,292 @@ + Smokeping + * + * @author Barry O'Donovan + * @author Yann Robin + * @category IXP + * @package IXP\Services\Grapher + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Smokeping extends GrapherBackend implements GrapherBackendContract +{ + /** + * {@inheritDoc} + * + * @return string + */ + #[\Override] + public function name(): string + { + return 'smokeping'; + } + + /** + * The Smokeping backend requires configuration. + * + * {@inheritDoc} + * + * @return bool + */ + #[\Override] + public function isConfigurationRequired(): bool + { + return true; + } + + /** + * This function indicates whether this graphing engine supports single monolithic text + * + * @see \IXP\Contracts\Grapher::isMonolithicConfigurationSupported() for an explanation + * + * @return true + */ + #[\Override] + public function isMonolithicConfigurationSupported(): bool + { + return true; + } + + /** + * This function indicates whether this graphing engine supports multiple files to a directory + * + * @see \IXP\Contracts\Grapher::isMonolithicConfigurationSupported() for an explanation + * + * @return false + */ + #[\Override] + public function isMultiFileConfigurationSupported(): bool + { + return false; + } + + /** + * Generate the configuration file(s) for this graphing backend + * + * {inheritDoc} + * + * @param int $type The type of configuration to generate + * @param array $options + * + * @return string[] + * + * @psalm-return list{string} + */ + #[\Override] + public function generateConfiguration( int $type = self::GENERATED_CONFIG_TYPE_MONOLITHIC, array $options = [] ): array + { + $v = Vlan::findOrFail( $options[ 'vlanid' ] ?? null ); + + if( !in_array( $options[ 'protocol' ] ?? null , Graph::PROTOCOLS_REAL, false ) ) { + abort( 404, 'No "protocol" parameter provided or unknown protocol' ); + } + + if( !isset( $options['template'] ) ) { + $tmpl = 'services/grapher/smokeping/default'; + } else { + $tmpl = sprintf( 'services/grapher/smokeping/%s', preg_replace( '/[^a-z0-9\-]/', '', strtolower( $options['template'] ) ) ); + } + + if( !FacadeView::exists( $tmpl ) ) { + abort(404, 'Unknown template'); + } + + $probe = $options[ 'probe' ] ?? ('FPing'.($options[ 'protocol' ] === Graph::PROTOCOL_IPV4 ? '' : '6')); + + // try and reorder the VLIs into alphabetical order of customer names + $vlis = VlanInterfaceAggregator::forProto( $v, $options['protocol'] === Graph::PROTOCOL_IPV4 ? 4 : 6 ); + + $orderedVlis = []; + foreach( $vlis as $vli ) { + $orderedVlis[ $vli['cname'] . '::' . $vli['vliid'] ] = $vli; + } + ksort( $orderedVlis, SORT_STRING | SORT_FLAG_CASE ); + + return [ + View::make( $tmpl, [ + 'vlan' => $v, + 'vlis' => $orderedVlis, + 'probe' => $probe, + 'level' => $options[ 'level' ] ?? '+++', + 'protocol' => $options['protocol'] === Graph::PROTOCOL_IPV4 ? 4 : 6, + ] )->render() + ]; + } + + /** + * Get a complete list of functionality that this backend supports. + * + * {inheritDoc} + * + * @return string[][][] + * + * @psalm-return array{latency: array{protocols: array{ipv4: 'ipv4', ipv6: 'ipv6'}, categories: array{bits: 'bits', pkts: 'pkts', errs: 'errs', discs: 'discs', bcasts: 'bcasts'}, periods: array{3hours: '3hours', 30hours: '30hours', 10days: '10days', 1year: '1year'}, types: array{png: 'png'}}} + */ + #[\Override] + public static function supports(): array + { + return [ + 'latency' => [ + 'protocols' => Graph::PROTOCOLS_REAL, + 'categories' => Graph::CATEGORIES, + 'periods' => LatencyGraph::PERIODS, + 'types' => [ Graph::TYPE_PNG => Graph::TYPE_PNG ], + ], + ]; + } + + /** + * Get the data points for a given graph + * + * {inheritDoc} + * + * @param Graph $graph + * + * @return array + * + * @psalm-return array + */ + #[\Override] + public function data( Graph $graph ): array + { + return []; + } + + /** + * Get the PNG image for a given graph + * + * {inheritDoc} + * + * @param Graph $graph + * + * @return false|string + */ + #[\Override] + public function png( Graph $graph ): false|string + { + return @file_get_contents( $this->resolveFilePath( $graph ) ); + } + + /** + * Get the path to the graphing data file (e.g. path to log or rrd file). + * + * {inheritDoc} + * + * @param Graph $graph + * + * @return string Path or empty string + * + * @throws + */ + #[\Override] + public function dataPath( Graph $graph ): string + { + return $this->resolveFilePath( $graph ); + } + + /** + * Get the RRD file for a given graph + * + * {inheritDoc} + * + * @param Graph $graph + * + * @return string + * + * @psalm-return '' + */ + #[\Override] + public function rrd( Graph $graph ): false|string + { + return ''; + } + + /** + * Function to decide what URL Smokeping should be reached via. + * + * Typically, this is just the 'grapher.backends.smokeping.url' config + * option (or more appropriately the GRAPHER_BACKEND_SMOKEPING_URL .env option). + * + * However we allow per VLAN overrides. See the documentation for details above this: + * + * @see https://docs.ixpmanager.org/latest/grapher/smokeping/ + * + * @param LatencyGraph $graph + * + * @return string + */ + private function resolveGraphURL( LatencyGraph $graph ): string + { + // does an override exist? + $urls = config( 'grapher.backends.smokeping.overrides.per_vlan_urls', [] ); + + return $urls[ $graph->vli()->vlan->id ] ?? config('grapher.backends.smokeping.url'); + } + + /** + * For a given graph, return the path where the appropriate file + * will be found. + * + * @param Graph $graph + * + * @return string + * + * @throws + */ + private function resolveFilePath( Graph $graph ): string + { + switch( $graph->classType() ) { + case 'Latency': + /** @var LatencyGraph $graph */ + return sprintf( "%s/?displaymode=a;start=now-%s;end=now;target=infra_%s.vlan_%s.vlanint_%s_%s", $this->resolveGraphURL( $graph ), + $graph->period(), $graph->vli()->virtualInterface->physicalInterfaces()->first()->switchPort->switcher->infrastructure, + $graph->vli()->vlan->id, $graph->vli()->id, $graph->protocol() ); + default: + throw new CannotHandleRequestException("Backend asserted it could process but cannot handle graph of type: {$graph->type()}" ); + } + } +} \ No newline at end of file diff --git a/app/Services/Grapher/Graph.php b/app/Services/Grapher/Graph.php new file mode 100644 index 000000000..41d15ee60 --- /dev/null +++ b/app/Services/Grapher/Graph.php @@ -0,0 +1,1115 @@ + Abstract Graph + * + * @author Barry O'Donovan + * @author Yann Robin + * @category IXP + * @package IXP\Services\Grapher + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +abstract class Graph +{ + /** + * Category to use + * + * @var + */ + private $category = self::CATEGORY_DEFAULT; + + /** + * Protocol to use + * + * @var + */ + private $protocol = self::PROTOCOL_DEFAULT; + + /** + * Grapher Service + * + * @var Grapher + */ + private $grapher; + + /** + * Backend to use + * + * @var GrapherBackend + */ + private $backend = null; + + /** + * Data points (essentially a cache which is wiped as appropriate) + * + * @var array + */ + private $data = null; + + /** + * Statistics object (essentially a cache which is wiped as appropriate) + * @var Statistics + */ + private $statistics = null; + + /** + * Renderer object (essentially a cache which is wiped as appropriate) + * + * @var Renderer + */ + private $renderer = null; + + + /** + * Period of one day for graphs + */ + public const PERIOD_DAY = 'day'; + + /** + * Period of one week for graphs + */ + public const PERIOD_WEEK = 'week'; + + /** + * Period of one month for graphs + */ + public const PERIOD_MONTH = 'month'; + + /** + * Period of one year for graphs + */ + public const PERIOD_YEAR = 'year'; + + /** + * Custom periods allow any start time and an end time that is not "now" + */ + public const PERIOD_CUSTOM = 'custom'; + + /** + * Custom Period range parameters - start + */ + protected ?Carbon $custom_date_start; + + /** + * Custom Period range parameters - end + */ + protected ?Carbon $custom_date_end; + + + /** + * Default period + */ + public const PERIOD_DEFAULT = self::PERIOD_DAY; + + /** + * Period to use + * @var + */ + protected $period = self::PERIOD_DEFAULT; + + /** + * Array of valid periods for drill down graphs + */ + public const PERIODS = [ + self::PERIOD_DAY => self::PERIOD_DAY, + self::PERIOD_WEEK => self::PERIOD_WEEK, + self::PERIOD_MONTH => self::PERIOD_MONTH, + self::PERIOD_YEAR => self::PERIOD_YEAR, + ]; + + /** + * Array of valid periods for drill down graphs + */ + public const PERIODS_EXTENDED = [ + self::PERIOD_DAY => self::PERIOD_DAY, + self::PERIOD_WEEK => self::PERIOD_WEEK, + self::PERIOD_MONTH => self::PERIOD_MONTH, + self::PERIOD_YEAR => self::PERIOD_YEAR, + self::PERIOD_CUSTOM => self::PERIOD_CUSTOM, + ]; + + + /** + * Array of valid periods for drill down graphs + */ + public const PERIOD_DESCS = [ + self::PERIOD_DAY => 'Day', + self::PERIOD_WEEK => 'Week', + self::PERIOD_MONTH => 'Month', + self::PERIOD_YEAR => 'Year', + ]; + + /** + * Array of valid periods for drill down graphs + */ + public const PERIOD_DESCS_EXTENDED = [ + self::PERIOD_DAY => 'Day', + self::PERIOD_WEEK => 'Week', + self::PERIOD_MONTH => 'Month', + self::PERIOD_YEAR => 'Year', + self::PERIOD_CUSTOM => 'Custom', + ]; + + + /** + * 'Bits' category for graphs + */ + public const CATEGORY_BITS = 'bits'; + + /** + * 'Packets' category for graphs + */ + public const CATEGORY_PACKETS = 'pkts'; + + /** + * 'Errors' category for graphs + */ + public const CATEGORY_ERRORS = 'errs'; + + /** + * 'Discards' category for graphs + */ + public const CATEGORY_DISCARDS = 'discs'; + + /** + * 'Broadcasts' category for graphs + */ + public const CATEGORY_BROADCASTS = 'bcasts'; + + /** + * Default category + */ + public const CATEGORY_DEFAULT = self::CATEGORY_BITS; + + /** + * Array of valid categories for graphs + */ + public const CATEGORIES = [ + self::CATEGORY_BITS => self::CATEGORY_BITS, + self::CATEGORY_PACKETS => self::CATEGORY_PACKETS, + self::CATEGORY_ERRORS => self::CATEGORY_ERRORS, + self::CATEGORY_DISCARDS => self::CATEGORY_DISCARDS, + self::CATEGORY_BROADCASTS => self::CATEGORY_BROADCASTS, + ]; + + /** + * Useful array of just bits and packets categories for graphs + */ + public const CATEGORIES_BITS_PKTS = [ + self::CATEGORY_BITS => self::CATEGORY_BITS, + self::CATEGORY_PACKETS => self::CATEGORY_PACKETS, + ]; + + /** + * Array of valid categories for graphs + */ + public const CATEGORY_DESCS = [ + self::CATEGORY_BITS => 'Bits', + self::CATEGORY_PACKETS => 'Packets', + self::CATEGORY_ERRORS => 'Errors', + self::CATEGORY_DISCARDS => 'Discards', + self::CATEGORY_BROADCASTS => 'Broadcasts', + ]; + + /** + * Useful array of just bits and packets categories for graphs + */ + public const CATEGORIES_BITS_PKTS_DESCS = [ + self::CATEGORY_BITS => 'Bits', + self::CATEGORY_PACKETS => 'Packets', + ]; + + /** + * Protocols for graphs + */ + public const PROTOCOL_IPV4 = 'ipv4'; + + /** + * Protocols for graphs + */ + public const PROTOCOL_IPV6 = 'ipv6'; + + /** + * Protocols for graphs + */ + public const PROTOCOL_ALL = 'all'; + + /** + * Default protocol for graphs + */ + public const PROTOCOL_DEFAULT = self::PROTOCOL_ALL; + + /** + * Array of valid protocols + */ + public const PROTOCOLS = [ + self::PROTOCOL_ALL => self::PROTOCOL_ALL, + self::PROTOCOL_IPV4 => self::PROTOCOL_IPV4, + self::PROTOCOL_IPV6 => self::PROTOCOL_IPV6 + ]; + + /** + * Array of valid real protocols + */ + public const PROTOCOLS_REAL = [ + self::PROTOCOL_IPV4 => self::PROTOCOL_IPV4, + self::PROTOCOL_IPV6 => self::PROTOCOL_IPV6 + ]; + + /** + * Array of valid protocols + */ + public const PROTOCOL_DESCS = [ + self::PROTOCOL_ALL => 'All', + self::PROTOCOL_IPV4 => 'IPv4', + self::PROTOCOL_IPV6 => 'IPv6' + ]; + + /** + * Array of valid real protocols + */ + public const PROTOCOL_REAL_DESCS = [ + self::PROTOCOL_IPV4 => 'IPv4', + self::PROTOCOL_IPV6 => 'IPv6' + ]; + + /** + * Grapher file format return type: png + * + * @var string + */ + public const TYPE_PNG = 'png'; + + /** + * Grapher file format return type: log + * + * @var string + */ + public const TYPE_LOG = 'log'; + + /** + * Grapher file format return type: rrd + * + * @var string + */ + public const TYPE_RRD = 'rrd'; + + /** + * Grapher file format return type: json + * + * @var string + */ + public const TYPE_JSON = 'json'; + + /** + * Default type + * + * @var string + */ + public const TYPE_DEFAULT = self::TYPE_PNG; + + /** + * Type to use + * + * @var string + */ + private $type = self::TYPE_DEFAULT; + + /** + * Possible types and descriptions + * + * @var array + */ + public const TYPES = [ + self::TYPE_PNG => self::TYPE_PNG, + self::TYPE_LOG => self::TYPE_LOG, + self::TYPE_RRD => self::TYPE_RRD, + self::TYPE_JSON => self::TYPE_JSON, + ]; + + /** + * Possible types and descriptions + * + * @var array + */ + public const TYPE_DESCS = [ + self::TYPE_PNG => 'PNG', + self::TYPE_LOG => 'LOG', + self::TYPE_RRD => 'RRD', + self::TYPE_JSON => 'JSON', + ]; + + /** + * Possible content types + * + * @var array + */ + public const CONTENT_TYPES = [ + self::TYPE_PNG => 'image/png', + self::TYPE_LOG => 'application/json', + self::TYPE_RRD => 'application/octet-stream', + self::TYPE_JSON => 'application/json', + ]; + + /** + * Constructor + * + * @param Grapher $grapher + */ + public function __construct( Grapher $grapher ) + { + $this->setGrapher( $grapher ); + } + + /** + * Get the grapher service + * + * @return Grapher + */ + protected function grapher(): Grapher + { + return $this->grapher; + } + + /** + * Set the grapher service + * + * @param Grapher $grapher + */ + protected function setGrapher( $grapher ): static + { + $this->grapher = $grapher; + return $this; + } + + /** + * For a given graph object ($this), find a backend that can process it + * + * @return GrapherBackend + * + * @throws + */ + public function backend(): GrapherBackend + { + if( $this->backend === null ) { + $this->backend = $this->grapher()->backendForGraph( $this ); + } + return $this->backend; + } + + /** + * For a given graph object ($this), find its data via the backend + * + * @return array + */ + public function data(): array + { + if( $this->data === null ) { + $this->data = $this->grapher()->remember( $this->cacheKey('data'), function() { + return $this->backend()->data($this); + }); + } + return $this->data; + } + + /** + * For a given graph object ($this), find its data via the backend and + * return as JSON + * + * @return string + * + * @throws + */ + public function log(): string + { + return json_encode($this->data(), JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT); + } + + /** + * A veritable table of contents for API access to this graph + * + * @return ((Carbon|float|mixed|null|string)[]|mixed|string)[] + * + * @throws + * + * @psalm-return array{class: string, urls?: array, base_url: string, statistics: array{totalin: float, totalout: float, curin: float, curout: float, averagein: float, averageout: float, maxin: float, maxout: float, maxinat: Carbon|null, maxoutat: Carbon|null}, params: array{protocol: mixed, period: mixed, category: mixed, type: mixed, period_start?: Carbon|null, period_end?: Carbon|null}, supports: mixed, backends: array, backend: string} + */ + public function toc(): array + { + $arr = [ 'class' => $this->lcClassType() ]; + $supports = $this->backend()->supports(); + + foreach( $supports[ $this->lcClassType() ][ 'types' ] as $t ) { + $arr[ 'urls' ][ $t ] = $this->url( [ 'type' => $t ] ); + } + + $arr[ 'base_url' ] = url('grapher/'.$this->lcClassType()); + $arr[ 'statistics' ] = $this->statistics()->all(); + $arr[ 'params' ] = $this->getParamsAsArray(); + $arr[ 'supports' ] = $supports[ $this->lcClassType() ]; + $arr[ 'backends' ] = []; + + foreach( $this->grapher()->backendsForGraph($this) as $backend ) { + $arr[ 'backends' ][ $backend->name() ] = $backend->name(); + } + + $arr['backend'] = $this->backend()->name(); + + return $arr; + } + + /** + * For a given graph object ($this), find its data via the backend and + * return as JSON + * + * @return string + * + * @throws + */ + public function json(): string + { + return json_encode( $this->toc(), JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT ); + } + + /** + * Generate a URL to get this graphs 'file' of a given type + * + * **NB:** should be overridden in subclasses as appropriate! + * + * @param array $overrides Allow standard parameters to be overridden (e.g. category) + * + * @return string + */ + public function url( array $overrides = [] ): string + { + return url('grapher/' . $this->lcClassType()) . sprintf( "?period=%s&type=%s&category=%s&protocol=%s" , + $overrides[ 'period' ] ?? $this->period(), + $overrides[ 'type' ] ?? $this->type(), + $overrides[ 'category' ] ?? $this->category(), + $overrides[ 'protocol' ] ?? $this->protocol() + ); + } + + /** + * For a given graph object ($this), get it's png + * + * @return string + */ + public function png(): string + { + return $this->grapher()->remember( $this->cacheKey('png' ), function() { + return $this->backend()->png( $this ); + }); + } + + /** + * Get the path to the graphing data file (e.g. path to log or rrd file). + * + * @return string + */ + public function dataPath(): string + { + return $this->backend()->dataPath( $this ); + } + + /** + * For a given graph object ($this), get it's rrd + * + * @return string + */ + public function rrd(): string + { + return $this->grapher()->remember( $this->cacheKey('rrd'), function() { + return $this->backend()->rrd($this); + }); + } + + /** + * For a given graph object ($this), calculate various statistics + * + * @return Statistics + */ + public function statistics(): Statistics + { + if( $this->statistics === null ) { + $this->statistics = new Statistics( $this ); + } + return $this->statistics; + } + + /** + * For a given graph object ($this), render it + * + * @return Renderer + */ + public function renderer(): Renderer + { + if( $this->renderer === null ) { + $this->renderer = new Renderer( $this ); + } + + return $this->renderer; + } + + /** + * We cache certain data (e.g. backend, data, statistics). This needs to be wiped + * if certain graph parameters are changed. + */ + public function wipe(): void + { + $this->backend = null; + $this->data = null; + $this->statistics = null; + $this->renderer = null; + } + + /** + * Return the class name less the IXP\Grapher\Graph\ namespace + * + * @return string + */ + public function classType(): string + { + $class = explode( '\\', get_class( $this ) ); + return array_pop( $class ); + } + + /** + * Return the class name less the IXP\Grapher\Graph\ namespace as lower case + * + * @return string + */ + public function lcClassType(): string + { + return strtolower( $this->classType() ); + } + + /** + * A function to generate a cache key for a given graph object + * + * @param string $type The 'type' to append to the key (e.g. png, log, rrd, etc.) + * + * @return string + */ + public function cacheKey( string $type = '' ): string + { + return 'grapher::' . $this->identifier() + . '-proto' . $this->protocol() + . '-' . $this->category() + . '-' . $this->period() + . '-' . $this->type() + . '.' . $type; + } + + /** + * The name of a graph (e.g. member name, IXP name, etc) + * + * @return string + */ + abstract public function name(): string; + + /** + * The title of a graph (e.g. member name, IXP name, etc) + * + * Example: ORGNAME :: Graph->name() :: PROTOCOL + * + * @return string + */ + public function title(): string + { + return config( 'identity.orgname') . " :: " . $this->name() + . ( $this->protocol() === self::PROTOCOL_ALL ? '' : ' :: ' . self::PROTOCOL_DESCS[ $this->protocol() ] ); + } + + /** + * Watermark for graphs (e.g. member name, IXP name, etc) + * + * Example: ORGNAME :: Graph->name() :: PROTOCOL + * + * @return string + */ + public function watermark(): string + { + return (string)config( 'identity.watermark' ); + } + + /** + * A unique identifier for this 'graph type' + * + * E.g. for an IXP, it might be ixpxxx where xxx is the database id + * + * @return string + */ + abstract public function identifier(): string; + + /** + * A simple key for this graph + * + * e.g. grapher-ixp002-ipv4-bits-day-rrd + * + * @return string + */ + public function key(): string + { + return 'grapher-' . $this->identifier() + . '-' . $this->protocol() + . '-' . $this->category() + . '-' . $this->period() + . '-' . $this->type(); + } + + /** + * This function controls access to the graph. + * + * You can check user privileges / membership / etc. and then call allow() + * to permit access or deny() to deny it. These methods can also be + * overridden. + * + * In it's default incarnation, this will **always** fail. You need to explicitly + * allow graph access based on your own requirements. + * + * @return never + * + * @throws + */ + public function authorise(): bool + { + $this->deny(); + return false; + } + + /** + * Action to take when authorisation fails. + * + * @throws AuthorizationException + * + * @param string $message Deny message + * + * @return never + */ + protected function deny( $message = 'This action is unauthorized.' ) + { + throw new AuthorizationException($message); + } + + /** + * Action to take when authorisation succeeds. + * + * @return true + */ + protected function allow(): bool + { + return true; + } + + /** + * Get the period we're set to use + * @return string + */ + public function period(): string + { + return $this->period; + } + + /** + * Set the period we should use + * + * @param string $value + * @param Carbon $start + * @param Carbon $end + * + * @return static Fluid interface + * + * @throws ParameterException + */ + public function setPeriod( string $value, ?Carbon $start = null, ?Carbon $end = null ): Graph + { + if( !isset( $this::PERIODS_EXTENDED[ $value ] ) ) { + throw new ParameterException('Invalid period ' . $value ); + } + + if( $this->period() !== $value ) { + $this->wipe(); + } + + $this->period = $value; + + if( $value === $this::PERIOD_CUSTOM ) { + $this->setPeriodStart( $start ); + $this->setPeriodEnd( $end ); + } else { + $this->setPeriodStart(); + $this->setPeriodEnd(); + } + + return $this; + } + + /** + * Set the start date for the custom period + * + * @param Carbon|null $value + */ + public function setPeriodStart( ?Carbon $value = null ): static + { + $this->custom_date_start = $value; + return $this; + } + + /** + * Get the start date for the custom period + * + * @return Carbon|null $value + */ + public function periodStart(): ?Carbon + { + return $this->custom_date_start; + } + + /** + * Set the start date for the custom period + * + * @param Carbon|null $value + */ + public function setPeriodEnd( ?Carbon $value = null ): static + { + $this->custom_date_end = $value; + return $this; + } + + /** + * Get the end date for the custom period + * + * @return Carbon|null $value + */ + public function periodEnd(): ?Carbon + { + return $this->custom_date_end; + } + + /** + * Get the period description for a given period identifier + * + * @param string|null $period + * + * @return string + */ + public static function resolvePeriod( ?string $period = null ): string + { + return self::PERIOD_DESCS[ $period ] ?? 'Unknown'; + } + + /** + * Get the protocol we're set to use + * + * @return string + */ + public function protocol(): string + { + return $this->protocol; + } + + /** + * Set the protocol we should use + * + * @param string $value + * + * @return static Fluid interface + * + * @throws ParameterException + */ + public function setProtocol( string $value ): static + { + if( !isset( $this::PROTOCOLS[ $value ] ) ) { + throw new ParameterException('Invalid protocol ' . $value ); + } + + if( $this->protocol() !== $value ) { + $this->wipe(); + } + + $this->protocol = $value; + return $this; + } + + /** + * Get the protocol description for a given protocol identifier + * + * @param string $protocol + * + * @return string + */ + public static function resolveProtocol( string $protocol ): string + { + return self::PROTOCOL_DESCS[ $protocol ] ?? 'Unknown'; + } + + /** + * Get the category we're set to use + * + * @return string + */ + public function category(): string + { + return $this->category; + } + + /** + * Get the category description + * + * @return string + */ + public function resolveMyCategory(): string + { + return self::CATEGORY_DESCS[ $this->category() ] ?? 'Unknown'; + } + + /** + * Get the category description for a given category identifier + * @param string $category + * + * @return string + */ + public static function resolveCategory( string $category ): string + { + return self::CATEGORY_DESCS[ $category ] ?? 'Unknown'; + } + + /** + * Set the category we should use + * + * @param string $category_value + * + * @return static Fluid interface + * + * @throws ParameterException + */ + public function setCategory( string $category_value ): static + { + if( !isset( $this::CATEGORIES[ $category_value ] ) ) { + throw new ParameterException('Invalid category ' . $category_value ); + } + + if( $this->category() !== $category_value ) { + $this->wipe(); + } + + $this->category = $category_value; + return $this; + } + + /** + * Get the type we're set to use + * @return string + */ + public function type(): string + { + return $this->type; + } + + /** + * Set the type we should use + * + * @param string $type_value + * + * @return static Fluid interface + * + * @throws ParameterException + */ + public function setType( string $type_value ): static + { + if( !isset( $this::TYPES[ $type_value ] ) ) { + throw new ParameterException('Invalid type ' . $type_value ); + } + + if( $this->type() !== $type_value ) { + $this->wipe(); + } + + $this->type = $type_value; + return $this; + } + + /** + * Set parameters in bulk from associative array + * + * Base function supports keys: type, protocol, category, period + * + * Will pass through thrown exceptions from setXXX() functions + * + * @param array $params + * + * @return static Fluid interface + */ + public function setParamsFromArray( array $params ): Graph + { + foreach( [ 'type', 'category', 'period', 'protocol'] as $param ){ + if( isset( $params[$param] ) ) { + $fn = 'set' . ucfirst( $param ); + $this->$fn( $params[$param] ); + + if($param === 'period' && $params[ 'period' ] === $this::PERIOD_CUSTOM) { + if(isset($params[ 'period_start' ])) { + $this->setPeriodStart( Carbon::parse( $params[ 'period_start' ] ) ); + } + if(isset($params[ 'period_end' ])) { + $this->setPeriodEnd( Carbon::parse( $params[ 'period_end' ] ) ); + } + } + } + } + return $this; + } + + /** + * Get parameters in bulk as associative array + * + * Base function supports keys: type, protocol, category, period + * + * @return (Carbon|mixed|null)[] $params + * + * @psalm-return array{protocol: mixed, period: mixed, category: mixed, type: mixed, period_start?: Carbon|null, period_end?: Carbon|null} + */ + public function getParamsAsArray(): array + { + // todo: extend for PERIOD_CUSTOM + $parameters = []; + foreach( [ 'type', 'category', 'period', 'protocol'] as $param ){ + $parameters[ $param ] = $this->$param(); + if($param === 'period' && $this->period === $this::PERIOD_CUSTOM) { + $parameters[ 'period_start' ] = $this->custom_date_start; + $parameters[ 'period_end' ] = $this->custom_date_end; + } + } + return $parameters; + } + + /** + * Process user input for the parameter: period + * + * Note that this function just sets the default if the input is invalid. + * If you want to force an exception in such cases, use setPeriod() + * + * @param string|null $value The user input value + * @param string|null $default The preferred default value + * + * @return string|null The verified / sanitised / default value + */ + public static function processParameterPeriod( ?string $value = null, ?string $default = null, $withExtended = false ): string|null + { + if( $withExtended && !isset( self::PERIODS_EXTENDED[ $value ] ) ) { + $value = $default ?? self::PERIOD_DEFAULT; + } else if( !isset( self::PERIODS[ $value ] ) ) { + $value = $default ?? self::PERIOD_DEFAULT; + } + + return $value; + } + + /** + * Process user input for the parameter: protocol + * + * Note that this function just sets the default if the input is invalid. + * If you want to force an exception in such cases, use setProtocol() + * + * @param string|null $value The user input value + * + * @return string|null The verified / sanitised / default value + */ + public static function processParameterProtocol( ?string $value = null ): string|null + { + if( !isset( self::PROTOCOLS[ $value ] ) ) { + $value = self::PROTOCOL_DEFAULT; + } + return $value; + } + + /** + * Process user input for the parameter: protocol (real only, not both) + * + * Note that this function just sets the default (ipv4) if the input is invalid. + * If you want to force an exception in such cases, use setProtocol() + * + * @param string|null $value The user input value + * @return string|null The verified / sanitised / default value + */ + public static function processParameterRealProtocol( ?string $value = null ): string|null + { + if( !isset( self::PROTOCOLS_REAL[ $value ] ) ) { + $value = self::PROTOCOL_IPV4; + } + return $value; + } + + /** + * Process user input for the parameter: category + * + * Note that this function just sets the default if the input is invalid. + * If you want to force an exception in such cases, use setCategory() + * + * @param string|null $value The user input value + * @param bool $bits_pkts_only + * + * @return string|null The verified / sanitised / default value + */ + public static function processParameterCategory( ?string $value = null, bool $bits_pkts_only = false ): string|null + { + if( ( $bits_pkts_only && !isset( self::CATEGORIES_BITS_PKTS[$value] ) ) || ( !$bits_pkts_only && !isset( self::CATEGORIES[ $value ] ) ) ) { + $value = self::CATEGORY_DEFAULT; + } + return $value; + } + + /** + * Process user input for the parameter: type + * + * Note that this function just sets the default if the input is invalid. + * If you want to force an exception in such cases, use setType() + * + * @param string|null $value The user input value + * + * @return string The verified / sanitised / default value + */ + public static function processParameterType( ?string $value = null ): string|null + { + if( !isset( self::TYPES[ $value ] ) ) { + $value = self::TYPE_DEFAULT; + } + return $value; + } +} \ No newline at end of file diff --git a/app/Services/Grapher/Graph/CoreBundle.php b/app/Services/Grapher/Graph/CoreBundle.php new file mode 100644 index 000000000..7a2dd5752 --- /dev/null +++ b/app/Services/Grapher/Graph/CoreBundle.php @@ -0,0 +1,253 @@ + Core Bundle Graph + * + * @author Barry O'Donovan + * @author Yann Robin + * @category IXP + * @package IXP\Services\Grapher + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class CoreBundle extends Graph +{ + /** + * CoreBundle to graph + * + * @var CoreBundleModel + */ + private $cb = null; + + /** + * Which side of the core bundle to show? + * + * @var string + */ + private $side = 'a'; + + /** + * Constructor + * + * @param Grapher $grapher + * @param CoreBundleModel $cb + * @param string $side + * + */ + public function __construct( Grapher $grapher, CoreBundleModel $cb, string $side = 'a' ) + { + parent::__construct( $grapher ); + $this->cb = $cb; + $this->side = strtolower( $side ); + } + + /** + * Get the CoreBundle we're set to use + * + * @return CoreBundleModel + */ + public function coreBundle(): CoreBundleModel + { + return $this->cb; + } + + /** + * Set the CoreBundleEntity we should use + * + * @param CoreBundleModel $cb + * + * @return static Fluid interface + */ + public function setCoreBundle( CoreBundleModel $cb ): static + { + if( $this->coreBundle() && $this->coreBundle()->id !== $cb->id ) { + $this->wipe(); + } + + $this->cb = $cb; + return $this; + } + + /** + * Get the side to show + * + * @return string + */ + public function side(): string + { + if( in_array( $this->side, [ 'a', 'b' ] ) ) { + return $this->side; + } + + return 'a'; + } + + /** + * Get the side to show + * + * @param string $side + */ + public function setSide( string $side ): static + { + $this->side = self::processParameterSide( $side ); + return $this; + } + + /** + * The name of a graph (e.g. member name, IXP name, etc) + * + * @return string + */ + #[\Override] + public function name(): string + { + return $this->coreBundle()->graph_title; + } + + /** + * A unique identifier for this 'graph type' + * + * E.g. for an IXP, it might be ixpxxx where xxx is the database id + * + * @return string + */ + #[\Override] + public function identifier(): string + { + return sprintf( "cb-aggregate-%05d-side%s", $this->coreBundle()->id, $this->side() ); + } + + /** + * This function controls access to the graph. + * + * {@inheritDoc} + * + * For (public) vlan aggregate graphs we pretty much allow complete access. + * + * @return bool + */ + #[\Override] + public function authorise(): bool + { + /** @var User $us */ + $us = Auth::getUser(); + + if( Auth::check() && $us->isSuperUser() ) { + return $this->allow(); + } + + if( !in_array( $this->category(), Graph::CATEGORIES_BITS_PKTS ) ) { + $this->deny(); + return false; + } + + if( !Auth::check() && is_numeric( config( 'grapher.access.trunk' ) ) && config( 'grapher.access.trunk' ) === User::AUTH_PUBLIC ) { + return $this->allow(); + } + + if( Auth::check() && is_numeric( config( 'grapher.access.trunk' ) ) && $us->privs() >= config( 'grapher.access.trunk' ) ) { + return $this->allow(); + } + + $this->deny(); + return false; + } + + /** + * Generate a URL to get this graphs 'file' of a given type + * + * @param array $overrides Allow standard parameters to be overridden (e.g. category) + * + * @return string + */ + #[\Override] + public function url( array $overrides = [] ): string + { + return parent::url( $overrides ) . sprintf("&id=%d", + $overrides[ 'id' ] ?? $this->coreBundle()->id + ); + } + + /** + * Get parameters in bulk as associative array + * + * Extends base function + * + * @return (\Carbon\Carbon|int|mixed|null|string)[] + * + * @psalm-return array{protocol: mixed, period: mixed, category: mixed, type: mixed, period_start?: \Carbon\Carbon|null, period_end?: \Carbon\Carbon|null, id: int, side: string} + */ + #[\Override] + public function getParamsAsArray(): array + { + $p = parent::getParamsAsArray(); + $p['id'] = $this->coreBundle()->id; + $p['side'] = $this->side(); + return $p; + } + + /** + * Process user input for the parameter: cb + * + * Does a abort(404) if invalid + * + * @param int $cb The user input value + * + * @return CoreBundleModel The verified / sanitised / default value + */ + public static function processParameterCoreBundle( int $cb ): CoreBundleModel + { + return CoreBundleModel::findOrFail( $cb ); + } + + /** + * Process user input for the parameter: side + * + * Does a abort(404) if invalid + * + * @param string $s The user input value + * + * @return string The verified / sanitised / default value + */ + public static function processParameterSide( string $s ): string + { + $s = strtolower( $s ); + if( !in_array( $s, [ 'a', 'b'] ) ) { + abort(404); + } + return $s; + } +} \ No newline at end of file diff --git a/app/Services/Grapher/Graph/Customer.php b/app/Services/Grapher/Graph/Customer.php new file mode 100644 index 000000000..44b251a9e --- /dev/null +++ b/app/Services/Grapher/Graph/Customer.php @@ -0,0 +1,242 @@ + Customer Graph (LAGs) + * + * @author Barry O'Donovan + * @author Yann Robin + * @category IXP + * @package IXP\Services\Grapher + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Customer extends Graph +{ + /** + * Customer to graph + * + * @var CustomerModel + */ + private $cust = null; + + /** + * Constructor + * + * @param Grapher $grapher + * + * @param CustomerModel $c + */ + public function __construct( Grapher $grapher, CustomerModel $c ) + { + parent::__construct( $grapher ); + $this->cust = $c; + } + + /** + * Get the customer we're set to use + * + * @return CustomerModel + */ + public function customer(): CustomerModel + { + return $this->cust; + } + + /** + * Set the customer we should use + * + * @param CustomerModel $c + * + * @return static Fluid interface + */ + public function setCustomer( CustomerModel $c ): static + { + if( $this->customer() && $this->customer()->id !== $c->id ) { + $this->wipe(); + } + + $this->cust = $c; + return $this; + } + + /** + * The name of a graph (e.g. member name, IXP name, etc) + * + * @return string + */ + #[\Override] + public function name(): string + { + return $this->customer()->name ?: ''; + } + + /** + * A unique identifier for this 'graph type' + * + * E.g. for an IXP, it might be ixpxxx where xxx is the database id + * + * @return string + */ + #[\Override] + public function identifier(): string + { + return sprintf( "aggregate-%05d", $this->customer()->id ); + } + + /** + * Utility function to determine if the currently logged in user can access 'all customer' graphs + * + * @return bool + */ + public static function authorisedForAllCustomers(): bool + { + /** @var User $us */ + $us = Auth::getUser(); + + if( Auth::check() && $us->isSuperUser() ) { + return true; + } + + if( !Auth::check() && is_numeric( config( 'grapher.access.customer' ) ) && config( 'grapher.access.customer' ) === User::AUTH_PUBLIC ) { + return true; + } + + return Auth::check() && is_numeric( config( 'grapher.access.customer' ) ) && $us->privs() >= config( 'grapher.access.customer' ); + } + + /** + * This function controls access to the graph. + * + * {@inheritDoc} + * + * For (public) vlan aggregate graphs we pretty much allow complete access. + * + * @return bool + */ + #[\Override] + public function authorise(): bool + { + /** @var User $us */ + $us = Auth::getUser(); + + // NB: see above authorisedForAllCustomers() + if( is_numeric( config( 'grapher.access.customer' ) ) && config( 'grapher.access.customer' ) === User::AUTH_PUBLIC ) { + return $this->allow(); + } + + if( !Auth::check() ) { + $this->deny(); + return false; + } + + if( $us->isSuperUser() ) { + return $this->allow(); + } + + if( $us->custid === $this->customer()->id ) { + return $this->allow(); + } + + if( config( 'grapher.access.customer' ) !== 'own_graphs_only' + && is_numeric( config( 'grapher.access.customer' ) ) + && $us->privs() >= config( 'grapher.access.customer' ) + ) { + return $this->allow(); + } + + Log::notice( sprintf( "[Grapher] [Customer]: user %d::%s tried to access a customer aggregate graph " + . "{$this->customer()->id} which is not theirs", Auth::id(), $us->username ) + ); + + $this->deny(); + return false; + } + + + /** + * Generate a URL to get this graphs 'file' of a given type + * + * @param array $overrides Allow standard parameters to be overridden (e.g. category) + * + * @return string + */ + #[\Override] + public function url( array $overrides = [] ): string + { + return parent::url( $overrides ) . sprintf("&id=%d", + $overrides[ 'id' ] ?? $this->customer()->id + ); + } + + /** + * Get parameters in bulk as associative array + * + * Extends base function + * + * @return (\Carbon\Carbon|int|mixed|null)[] + * + * @psalm-return array{protocol: mixed, period: mixed, category: mixed, type: mixed, period_start?: \Carbon\Carbon|null, period_end?: \Carbon\Carbon|null, id: int} + */ + #[\Override] + public function getParamsAsArray(): array + { + $p = parent::getParamsAsArray(); + $p['id'] = $this->customer()->id; + return $p; + } + + /** + * Process user input for the parameter: cust + * + * Does a abort(404) if invalid + * + * @param int $i The user input value + * + * @return CustomerModel The verified / sanitised / default value + */ + public static function processParameterCustomer( int $i ): CustomerModel + { + /** @var User $us */ + $us = Auth::getUser(); + + // if we're not an admin, default to the currently logged in customer + if( !$i && Auth::check() && !$us->isSuperUser() && !$us->customer->typeAssociate() ) { + return CustomerModel::find( $us->custid ); + } + + return CustomerModel::findOrFail( $i ); + } +} \ No newline at end of file diff --git a/app/Services/Grapher/Graph/IXP.php b/app/Services/Grapher/Graph/IXP.php new file mode 100644 index 000000000..30e87dcb0 --- /dev/null +++ b/app/Services/Grapher/Graph/IXP.php @@ -0,0 +1,151 @@ + Abstract Graph + * + * @author Barry O'Donovan + * @author Yann Robin + * @category IXP + * @package IXP\Services\Grapher + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class IXP extends Graph +{ + /** + * Set parameters in bulk from associative array + * + * {@inheritDoc} + * + * @param array $params + * + * @return IXP Fluid interface + */ + #[\Override] + public function setParamsFromArray( array $params ): IXP + { + parent::setParamsFromArray( $params ); + return $this; + } + + /** + * The name of a graph (e.g. member name, IXP name, etc) + * @return string + */ + #[\Override] + public function name(): string + { + return config( 'identity.orgname' ); + } + + /** + * A unique identifier for this 'graph type' + * + * E.g. for an IXP, it might be ixpxxx where xxx is the database id + * + * @return string + */ + #[\Override] + public function identifier(): string + { + return sprintf( "ixp%03d", 1 ); + } + + /** + * This function controls access to the graph. + * + * {@inheritDoc} + * + * For IXP aggregate graphs we pretty much allow complete access. + * + * @return bool + */ + #[\Override] + public function authorise(): bool + { + /** @var User $us */ + $us = Auth::getUser(); + + if( Auth::check() && $us->isSuperUser() ) { + return $this->allow(); + } + + if( in_array( $this->category(), [ self::CATEGORY_ERRORS, self::CATEGORY_DISCARDS ], true ) ) { + $this->deny(); + return false; + } + + if( is_numeric( config( 'grapher.access.ixp' ) ) && (int)config( 'grapher.access.ixp' ) === User::AUTH_PUBLIC ) { + return $this->allow(); + } + + if( Auth::check() && is_numeric( config( 'grapher.access.ixp' ) ) && $us->privs() >= config( 'grapher.access.ixp' ) ) { + return $this->allow(); + } + + $this->deny(); + return false; + } + + /** + * Generate a URL to get this graphs 'file' of a given type + * + * @param array $overrides Allow standard parameters to be overridden (e.g. category) + * + * @return string + */ + #[\Override] + public function url( array $overrides = [] ): string + { + return parent::url( $overrides ) . sprintf("&id=%d", + $overrides[ 'id' ] ?? 1 + ); + } + + /** + * Get parameters in bulk as associative array + * + * Extends base function + * + * @return (\Carbon\Carbon|int|mixed|null)[] + * + * @psalm-return array{protocol: mixed, period: mixed, category: mixed, type: mixed, period_start?: \Carbon\Carbon|null, period_end?: \Carbon\Carbon|null, id: 1} + */ + #[\Override] + public function getParamsAsArray(): array + { + $p = parent::getParamsAsArray(); + $p['id'] = 1; + return $p; + } +} \ No newline at end of file diff --git a/app/Services/Grapher/Graph/Infrastructure.php b/app/Services/Grapher/Graph/Infrastructure.php new file mode 100644 index 000000000..21fce540e --- /dev/null +++ b/app/Services/Grapher/Graph/Infrastructure.php @@ -0,0 +1,201 @@ + Infrastructure Graph + * + * @author Barry O'Donovan + * @author Yann Robin + * @category IXP + * @package IXP\Services\Grapher + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Infrastructure extends Graph +{ + /** + * Infrastructure to graph + * + * @var InfrastructureModel + */ + private $infrastructure = null; + + /** + * Constructor + * + * @param Grapher $grapher + * + * @param InfrastructureModel $i + */ + public function __construct( Grapher $grapher, InfrastructureModel $i ) + { + parent::__construct( $grapher ); + $this->infrastructure = $i; + } + + /** + * Get the infrastructure we're set to use + * + * @return InfrastructureModel + */ + public function infrastructure(): InfrastructureModel + { + return $this->infrastructure; + } + + /** + * Set the infrastructure we should use + * + * @param InfrastructureModel $infra + * + * @return static Fluid interface + */ + public function setInfrastructure( InfrastructureModel $infra ): static + { + if( $this->infrastructure() && $this->infrastructure()->id !== $infra->id ) { + $this->wipe(); + } + + $this->infrastructure = $infra; + return $this; + } + + /** + * The name of a graph (e.g. member name, IXP name, etc) + * + * @return string + */ + #[\Override] + public function name(): string + { + return $this->infrastructure()->name ?: ''; + } + + /** + * A unique identifier for this 'graph type' + * + * E.g. for an IXP, it might be ixpxxx where xxx is the database id + * + * @return string + */ + #[\Override] + public function identifier(): string + { + return sprintf( "infrastructure%03d", $this->infrastructure()->id ); + } + + /** + * This function controls access to the graph. + * + * {@inheritDoc} + * + * For infrastructure aggregate graphs we pretty much allow complete access. + * + * @return bool + */ + #[\Override] + public function authorise(): bool + { + /** @var User $us */ + $us = Auth::getUser(); + + if( Auth::check() && $us->isSuperUser() ) { + return $this->allow(); + } + + if( in_array( $this->category(), [ self::CATEGORY_ERRORS, self::CATEGORY_DISCARDS ], true ) ) { + $this->deny(); + return false; + } + + if( is_numeric( config( 'grapher.access.infrastructure' ) ) && (int)config( 'grapher.access.infrastructure' ) === User::AUTH_PUBLIC ) { + return $this->allow(); + } + + if( Auth::check() && is_numeric( config( 'grapher.access.infrastructure' ) ) && $us->privs() >= config( 'grapher.access.infrastructure' ) ) { + return $this->allow(); + } + + $this->deny(); + return false; + } + + /** + * Generate a URL to get this graphs 'file' of a given type + * + * @param array $overrides Allow standard parameters to be overridden (e.g. category) + * + * @return string + */ + #[\Override] + public function url( array $overrides = [] ): string + { + return parent::url( $overrides ) . sprintf("&id=%d", + $overrides[ 'id' ] ?? $this->infrastructure()->id + ); + } + + /** + * Get parameters in bulk as associative array + * + * Extends base function + * + * @return (\Carbon\Carbon|int|mixed|null)[] + * + * @psalm-return array{protocol: mixed, period: mixed, category: mixed, type: mixed, period_start?: \Carbon\Carbon|null, period_end?: \Carbon\Carbon|null, id: int} + */ + #[\Override] + public function getParamsAsArray(): array + { + $p = parent::getParamsAsArray(); + $p['id'] = $this->infrastructure()->id; + return $p; + } + + /** + * Process user input for the parameter: ixp + * + * Note that this function just sets the default if the input is invalid. + * If you want to force an exception in such cases, use setIXP() + * + * @param int $v The user input value + * + * @return InfrastructureModel The verified / sanitised / default value + */ + public static function processParameterInfrastructure( int $v ): InfrastructureModel + { + return InfrastructureModel::findOrFail( $v ); + } +} \ No newline at end of file diff --git a/app/Services/Grapher/Graph/Latency.php b/app/Services/Grapher/Graph/Latency.php new file mode 100644 index 000000000..95538799a --- /dev/null +++ b/app/Services/Grapher/Graph/Latency.php @@ -0,0 +1,344 @@ + Latency Graphs + * + * @author Barry O'Donovan + * @author Yann Robin + * @category IXP + * @package IXP\Services\Grapher + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Latency extends Graph +{ + /** + * VLAN interface to graph + * + * @var VlanInterfaceModel + */ + private $vli = null; + + /** + * Period of three hours for graphs + */ + public const PERIOD_3HOURS = '3hours'; + + /** + * Period of thirty hours for graphs + */ + public const PERIOD_30HOURS = '30hours'; + + /** + * Period of ten days for graphs + */ + public const PERIOD_10DAYS = '10days'; + + /** + * Period of one year for graphs + */ + public const PERIOD_1YEAR = '1year'; + + /** + * Default period + */ + public const PERIOD_DEFAULT = self::PERIOD_3HOURS; + + /** + * Array of valid periods for drill down graphs + */ + public const PERIODS = [ + self::PERIOD_3HOURS => "3hours", + self::PERIOD_30HOURS => "30hours", + self::PERIOD_10DAYS => "10days", + self::PERIOD_1YEAR => "1year" + ]; + + /** + * Array of valid periods for drill down graphs - descriptions + */ + public const PERIODS_DESC = [ + self::PERIOD_3HOURS => "3 hours", + self::PERIOD_30HOURS => "30 hours", + self::PERIOD_10DAYS => "10 days", + self::PERIOD_1YEAR => "year" + ]; + + /** + * Default protocol for graphs + */ + public const PROTOCOL_DEFAULT = self::PROTOCOL_IPV4; + + /** + * Constructor + * + * @param Grapher $grapher + * @param VlanInterfaceModel $vli + * + * @throws ParameterException + */ + public function __construct( Grapher $grapher, VlanInterfaceModel $vli ) + { + parent::__construct( $grapher ); + $this->vli = $vli; + $this->setPeriod( self::PERIOD_3HOURS ); + } + + /** + * Set the period we should use + * + * @param string $value + * + * @return static Fluid interface + * + * @throws ParameterException + */ + #[\Override] + public function setPeriod( string $value, ?Carbon $start = null, ?Carbon $end = null ): Graph + { + if( !isset( self::PERIODS[ $value ] ) ) { + throw new ParameterException('Invalid period ' . $value ); + } + + if( $value === self::PERIOD_CUSTOM ) { + throw new ParameterException('Invalid period ' . $value . ' for Graph/Latency graphs' ); + } + + if( $this->period() !== $value ) { + $this->wipe(); + } + + $this->period = $value; + return $this; + } + + /** + * Get the period description for a given period identifier + * + * @param string|null $period + * + * @return string + */ + #[\Override] + public static function resolvePeriod( ?string $period = null ): string + { + return self::PERIODS[ $period ] ?? 'Unknown'; + } + + + /** + * Process user input for the parameter: period + * + * Note that this function just sets the default if the input is invalid. + * If you want to force an exception in such cases, use setPeriod() + * + * @param string|null $value The user input value + * @param string|null $default The preferred default value + * + * @return string|null The verified / sanitised / default value + */ + #[\Override] + public static function processParameterPeriod( ?string $value = null, ?string $default = null, $withExtended = false ): string|null + { + if( $withExtended && !isset( self::PERIODS_EXTENDED[ $value ] ) ) { + $value = $default ?? self::PERIOD_DEFAULT; + } else if( !isset( self::PERIODS[ $value ] ) ) { + $value = $default ?? self::PERIOD_DEFAULT; + } + + return $value; + } + + /** + * Get the vlan interface we're meant to graph for latency + * + * @return VlanInterfaceModel + */ + public function vli(): VlanInterfaceModel + { + return $this->vli; + } + + /** + * The name of a graph (e.g. member name, IXP name, etc + * + * @return string + */ + #[\Override] + public function name(): string + { + return sprintf( "Latency Graph :: %s :: %s :: %s", + $this->vli()->vlan->name, + $this->vli()->virtualInterface->customer->abbreviatedName, + $this->protocol() === self::PROTOCOL_IPV4 ? $this->vli()->ipv4address->address : $this->vli()->ipv6address->address + ); + } + + /** + * A unique identifier for this 'graph type' + * + * E.g. for an IXP, it might be ixpxxx where xxx is the database id + * + * @return string + */ + #[\Override] + public function identifier(): string + { + return sprintf( "latency-vli%d-%s", $this->vli()->id, $this->protocol() ); + } + + /** + * Utility function to determine if the currently logged in user can access 'all customer's latency' graphs + * + * @return bool + */ + public static function authorisedForAllCustomers(): bool + { + /** @var User $us */ + $us = Auth::getUser(); + + if( Auth::check() && $us->isSuperUser() ) { + return true; + } + + if( !Auth::check() && is_numeric( config( 'grapher.access.latency' ) ) && config( 'grapher.access.latency' ) === User::AUTH_PUBLIC ) { + return true; + } + + return Auth::check() && is_numeric( config( 'grapher.access.latency' ) ) && $us->privs() >= config( 'grapher.access.latency' ); + } + + /** + * This function controls access to the graph. + * + * {@inheritDoc} + * + * For (public) vlan aggregate graphs we pretty much allow complete access. + * + * @throws AuthorizationException + */ + #[\Override] + public function authorise(): bool + { + /** @var User $us */ + $us = Auth::getUser(); + + // NB: see above authorisedForAllCustomers() + if( is_numeric( config( 'grapher.access.latency' ) ) && config( 'grapher.access.latency' ) === User::AUTH_PUBLIC ) { + return $this->allow(); + } + + if( !Auth::check() ) { + $this->deny(); + return false; + } + + if( $us->isSuperUser() ) { + return $this->allow(); + } + + if( $us->custid === $this->vli()->virtualInterface->customer->id ) { + return $this->allow(); + } + + if( config( 'grapher.access.latency' ) !== 'own_graphs_only' + && is_numeric( config( 'grapher.access.latency' ) ) + && $us->privs >= config( 'grapher.access.latency' ) + ) { + return $this->allow(); + } + + Log::notice( sprintf( "[Grapher] [Latency]: user %d::%s tried to access a latency graph for vli " + . "{$this->vli()->id} which is not theirs", Auth::id(), $us->username ) + ); + + $this->deny(); + return false; + } + + /** + * Generate a URL to get this graphs 'file' of a given type + * + * @param array $overrides Allow standard parameters to be overridden (e.g. category) + * + * @return string + */ + #[\Override] + public function url( array $overrides = [] ): string + { + return parent::url( $overrides ) . sprintf("&id=%d", + $overrides[ 'id' ] ?? $this->vli()->id + ); + } + + /** + * Get parameters in bulk as associative array + * + * Extends base function + * + * @return (Carbon|int|mixed|null)[] + * + * @psalm-return array{protocol: mixed, period: mixed, category: mixed, type: mixed, period_start?: Carbon|null, period_end?: Carbon|null, id: int} + */ + #[\Override] + public function getParamsAsArray(): array + { + $p = parent::getParamsAsArray(); + $p['id'] = $this->vli()->id; + return $p; + } + + /** + * Process user input for the parameter: vlanint + * + * Does a abort(404) if invalid + * + * @param int $vliid The user input value + * + * @return VlanInterfaceModel + */ + public static function processParameterVlanInterface( int $vliid ): VlanInterfaceModel + { + return VlanInterfaceModel::findOrFail( $vliid ); + } +} \ No newline at end of file diff --git a/app/Services/Grapher/Graph/Location.php b/app/Services/Grapher/Graph/Location.php new file mode 100644 index 000000000..9805753af --- /dev/null +++ b/app/Services/Grapher/Graph/Location.php @@ -0,0 +1,193 @@ + Location Graph + * + * @author Barry O'Donovan + * @category IXP + * @package IXP\Services\Grapher + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Location extends Graph +{ + /** + * Switcher to graph + * + * @var LocationModel + */ + private $location = null; + + /** + * Constructor + * + * @param Grapher $grapher + * @param LocationModel $l + */ + public function __construct( Grapher $grapher, LocationModel $l ) + { + parent::__construct( $grapher ); + $this->location = $l; + } + + /** + * Get the switch we're set to use + * + * @return LocationModel + */ + public function location(): LocationModel + { + return $this->location; + } + + /** + * Set the switch we should use + * + * @param LocationModel $switch + * + * @return static Fluid interface + */ + public function setLocation( LocationModel $location ): static + { + if( $this->location() && $this->location()->id !== $location->id ) { + $this->wipe(); + } + + $this->location = $location; + return $this; + } + + /** + * The name of a graph (e.g. member name, IXP name, etc) + * + * @return string + */ + #[\Override] + public function name(): string + { + return $this->location()->name ?: ''; + } + + /** + * A unique identifier for this 'graph type' + * + * E.g. for an IXP, it might be ixpxxx where xxx is the database id + * + * @return string + */ + #[\Override] + public function identifier(): string + { + return sprintf( "location%05d", $this->location()->id ); + } + + /** + * This function controls access to the graph. + * + * {@inheritDoc} + * + * For (public) vlan aggregate graphs we pretty much allow complete access. + * + * @return bool + */ + #[\Override] + public function authorise(): bool + { + /** @var User $us */ + $us = Auth::getUser(); + + if( Auth::check() && $us->isSuperUser() ) { + return $this->allow(); + } + + if( is_numeric( config( 'grapher.access.location' ) ) && (int)config( 'grapher.access.location' ) === User::AUTH_PUBLIC ) { + return $this->allow(); + } + + if( Auth::check() && is_numeric( config( 'grapher.access.location' ) ) && $us->privs() >= (int)config( 'grapher.access.location' ) ) { + return $this->allow(); + } + + $this->deny(); + return false; + } + + /** + * Generate a URL to get this graphs 'file' of a given type + * + * @param array $overrides Allow standard parameters to be overridden (e.g. category) + * + * @return string + */ + #[\Override] + public function url( array $overrides = [] ): string + { + return parent::url( $overrides ) . sprintf("&id=%d", + $overrides[ 'id' ] ?? $this->location()->id + ); + } + + /** + * Get parameters in bulk as associative array + * + * Extends base function + * + * @return (\Carbon\Carbon|int|mixed|null)[] + * + * @psalm-return array{protocol: mixed, period: mixed, category: mixed, type: mixed, period_start?: \Carbon\Carbon|null, period_end?: \Carbon\Carbon|null, id: int} + */ + #[\Override] + public function getParamsAsArray(): array + { + $p = parent::getParamsAsArray(); + $p['id'] = $this->location()->id; + return $p; + } + + /** + * Process user input for the parameter: switch + * + * Does a abort(404) if invalid + * + * @param int $l The user input value + * + * @return LocationModel The verified / sanitised / default value + */ + public static function processParameterLocation( int $l ): LocationModel + { + return LocationModel::findOrFail( $l ); + } +} \ No newline at end of file diff --git a/app/Services/Grapher/Graph/P2p.php b/app/Services/Grapher/Graph/P2p.php new file mode 100644 index 000000000..5578682a2 --- /dev/null +++ b/app/Services/Grapher/Graph/P2p.php @@ -0,0 +1,301 @@ + P2P Graph + * + * @author Barry O'Donovan + * @author Yann Robin + * @category IXP + * @package IXP\Services\Grapher + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class P2p extends Graph +{ + /** + * Source VlanInterface to graph + * + * @var VlanInterfaceModel + */ + private $svli = null; + + /** + * Destination VlanInterface to graph + * + * @var VlanInterfaceModel + */ + private $dvli = null; + + /** + * Constructor + * + * @param Grapher $grapher + * @param VlanInterfaceModel $svli + * @param VlanInterfaceModel $dvli + */ + public function __construct( Grapher $grapher, VlanInterfaceModel $svli, VlanInterfaceModel $dvli ) { + parent::__construct( $grapher ); + $this->svli = $svli; + $this->dvli = $dvli; + } + + /** + * Get the source vlan we're set to use + * + * @return VlanInterfaceModel + */ + public function svli(): VlanInterfaceModel + { + return $this->svli; + } + + /** + * Get the dest vlan we're set to use + * + * @return VlanInterfaceModel + */ + public function dvli(): VlanInterfaceModel + { + return $this->dvli; + } + + /** + * Set the source vli we should use + * + * @param VlanInterfaceModel $vli + * + * @return static Fluid interface + */ + public function setSourceVlanInterface( VlanInterfaceModel $vli ): static + { + if( $this->svli() && $this->svli()->id !== $vli->id ) { + $this->wipe(); + } + + $this->svli = $vli; + return $this; + } + + /** + * Set the dest vli we should use + * + * @param VlanInterfaceModel $vli + * @param bool $wipe Graph settings are wiped by default when the dvli changes + * + * @return static Fluid interface + */ + public function setDestinationVlanInterface( VlanInterfaceModel $vli, bool $wipe = true ): static + { + if( $this->dvli() && $this->dvli()->id !== $vli->id ) { + if( $wipe ) { $this->wipe(); } + } + + $this->dvli = $vli; + return $this; + } + + /** + * The name of a graph (e.g. member name, IXP name, etc) + * @return string + */ + #[\Override] + public function name(): string + { + return sprintf( "P2P :: %s - %s :: %s", + $this->svli()->virtualInterface->customer->abbreviatedName, + $this->dvli()->virtualInterface->customer->abbreviatedName, + $this->dvli()->vlan->name + ); + } + + /** + * A unique identifier for this 'graph type' + * + * E.g. for an IXP, it might be ixpxxx where xxx is the database id + * + * @return string + */ + #[\Override] + public function identifier(): string + { + return sprintf( "p2p-svli%05d-dvli%05d", $this->svli()->id, $this->dvli()->id ); + } + + /** + * Utility function to determine if the currently logged in user can access 'all customer's p2p' graphs + * + * @return bool + */ + public static function authorisedForAllCustomers(): bool + { + /** @var User $us */ + $us = Auth::getUser(); + + if( Auth::check() && $us->isSuperUser() ) { + return true; + } + + if( !Auth::check() && is_numeric( config( 'grapher.access.p2p' ) ) && config( 'grapher.access.p2p' ) === User::AUTH_PUBLIC ) { + return true; + } + + return Auth::check() && is_numeric( config( 'grapher.access.p2p' ) ) && $us->privs() >= config( 'grapher.access.p2p' ); + } + + /** + * This function controls access to the graph. + * + * {@inheritDoc} + * + * For (public) vlan aggregate graphs we pretty much allow complete access. + * + * @return bool + */ + #[\Override] + public function authorise(): bool + { + /** @var User $us */ + $us = Auth::getUser(); + + // NB: see above authorisedForAllCustomers() + if( is_numeric( config( 'grapher.access.p2p' ) ) && config( 'grapher.access.p2p' ) === User::AUTH_PUBLIC ) { + return $this->allow(); + } + + if( !Auth::check() ) { + $this->deny(); + return false; + } + + if( $us->isSuperUser() ) { + return $this->allow(); + } + + if( $us->custid === $this->svli()->virtualInterface->customer->id ) { + return $this->allow(); + } + + if( config( 'grapher.access.p2p' ) !== 'own_graphs_only' + && is_numeric( config( 'grapher.access.p2p' ) ) + && $us->privs() >= (int)config( 'grapher.access.p2p' ) + ) { + return $this->allow(); + } + + Log::notice( sprintf( "[Grapher] [Customer]: user %d::%s tried to access a customer p2p vli graph " + . "{$this->svli()->id} with dvli {$this->dvli()->id} which is not theirs", Auth::id(), $us->username ) + ); + + $this->deny(); + return false; + + } + + /** + * Generate a URL to get this graphs 'file' of a given type + * + * @param array $overrides Allow standard parameters to be overridden (e.g. category) + * + * @return string + */ + #[\Override] + public function url( array $overrides = [] ): string + { + return parent::url( $overrides ) . sprintf("&svli=%d&dvli=%d", + $overrides['svli'] ?? $this->svli()->id, + $overrides['dvli'] ?? $this->dvli()->id + ); + } + + /** + * Get parameters in bulk as associative array + * + * Extends base function + * + * @return (\Carbon\Carbon|int|mixed|null)[] + * + * @psalm-return array{protocol: mixed, period: mixed, category: mixed, type: mixed, period_start?: \Carbon\Carbon|null, period_end?: \Carbon\Carbon|null, svli: int, dvli: int} + */ + #[\Override] + public function getParamsAsArray(): array + { + $p = parent::getParamsAsArray(); + $p['svli'] = $this->svli()->id; + $p['dvli'] = $this->dvli()->id; + return $p; + } + + /** + * Process user input for the parameter: vlanint + * + * Does a abort(404) if invalid + * + * @param int $vli The user input value + * + * @return VlanInterfaceModel The verified / sanitised / default value + */ + public static function processParameterVlanInterface( int $vli ): VlanInterfaceModel + { + return VlanInterfaceModel::findOrFail( $vli ); + } + + /** + * Process user input for the parameter: srv vlanint + * + * Does a abort(404) if invalid + * + * @param int $vli The user input value + * + * @return VlanInterfaceModel The verified / sanitised / default value + */ + public static function processParameterSourceVlanInterface( int $vli ): VlanInterfaceModel + { + return self::processParameterVlanInterface( $vli ); + } + + /** + * Process user input for the parameter: dst vlanint + * + * Does a abort(404) if invalid + * + * @param int $vli The user input value + * + * @return VlanInterfaceModel The verified / sanitised / default value + */ + public static function processParameterDestinationVlanInterface( int $vli ): VlanInterfaceModel { + return self::processParameterVlanInterface( $vli ); + } +} \ No newline at end of file diff --git a/app/Services/Grapher/Graph/PhysicalInterface.php b/app/Services/Grapher/Graph/PhysicalInterface.php new file mode 100644 index 000000000..d256f3aaf --- /dev/null +++ b/app/Services/Grapher/Graph/PhysicalInterface.php @@ -0,0 +1,219 @@ + PhysicalInterface Graph + * + * @author Barry O'Donovan + * @author Yann Robin + * @category IXP + * @package IXP\Services\Grapher + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class PhysicalInterface extends Graph +{ + /** + * PhysicalInterface to graph + * + * @var PhysicalInterfaceModel + */ + private $physint = null; + + /** + * Constructor + * + * @param Grapher $grapher + * @param PhysicalInterfaceModel $pi + */ + public function __construct( Grapher $grapher, PhysicalInterfaceModel $pi ) + { + parent::__construct( $grapher ); + $this->physint = $pi; + } + + /** + * Get the vlan we're set to use + * + * @return PhysicalInterfaceModel + */ + public function physicalInterface(): PhysicalInterfaceModel + { + return $this->physint; + } + + /** + * Get the customer owning this virtual interface + */ + public function customer(): Customer|null + { + return $this->physint->virtualInterface->customer; + } + + /** + * Set the physint we should use + * + * @param PhysicalInterfaceModel $pi + * + * @return static Fluid interface + */ + public function setPhysicalInterface( PhysicalInterfaceModel $pi ): static + { + if( $this->physicalInterface() && $this->physicalInterface()->id !== $pi->id ) { + $this->wipe(); + } + + $this->physint = $pi; + return $this; + } + + /** + * The name of a graph (e.g. member name, IXP name, etc) + * + * @return string + */ + #[\Override] + public function name(): string + { + return $this->physicalInterface()->switchPort->name ?? ''; + } + + /** + * A unique identifier for this 'graph type' + * + * E.g. for an IXP, it might be ixpxxx where xxx is the database id + * + * @return string + */ + #[\Override] + public function identifier(): string + { + return sprintf( "pi%05d", $this->physicalInterface()->id ); + } + + /** + * This function controls access to the graph. + * + * {@inheritDoc} + * + * For (public) vlan aggregate graphs we pretty much allow complete access. + * + * @return bool + */ + #[\Override] + public function authorise(): bool + { + /** @var User $us */ + $us = Auth::getUser(); + + if( is_numeric( config( 'grapher.access.customer' ) ) && config( 'grapher.access.customer' ) === User::AUTH_PUBLIC ) { + return $this->allow(); + } + + if( !Auth::check() ) { + $this->deny(); + return false; + } + + if( $us->isSuperUser() ) { + return $this->allow(); + } + + if( $us->custid === $this->physicalInterface()->virtualInterface->customer->id ) { + return $this->allow(); + } + + if( config( 'grapher.access.customer' ) !== 'own_graphs_only' + && is_numeric( config( 'grapher.access.customer' ) ) + && $us->privs >= config( 'grapher.access.customer' ) + ) { + return $this->allow(); + } + + Log::notice( sprintf( "[Grapher] [PhysicalInterface]: user %d::%s tried to access a physical interface graph " + . "{$this->physicalInterface()->id} which is not theirs", Auth::id(), $us->username ) + ); + + $this->deny(); + return false; + } + + /** + * Generate a URL to get this graphs 'file' of a given type + * + * @param array $overrides Allow standard parameters to be overridden (e.g. category) + * + * @return string + */ + #[\Override] + public function url( array $overrides = [] ): string + { + return parent::url( $overrides ) . sprintf("&id=%d", + $overrides[ 'id' ] ?? $this->physicalInterface()->id + ); + } + + /** + * Get parameters in bulk as associative array + * + * Extends base function + * + * @return (\Carbon\Carbon|int|mixed|null)[] + * + * @psalm-return array{protocol: mixed, period: mixed, category: mixed, type: mixed, period_start?: \Carbon\Carbon|null, period_end?: \Carbon\Carbon|null, id: int} + */ + #[\Override] + public function getParamsAsArray(): array + { + $p = parent::getParamsAsArray(); + $p['id'] = $this->physicalInterface()->id; + return $p; + } + + /** + * Process user input for the parameter: physint + * + * Does a abort(404) if invalid + * + * @param int $pi The user input value + * + * @return PhysicalInterfaceModel The verified / sanitised / default value + */ + public static function processParameterPhysicalInterface( int $pi ): PhysicalInterfaceModel + { + return PhysicalInterfaceModel::findOrFail( $pi ); + } +} \ No newline at end of file diff --git a/app/Services/Grapher/Graph/Switcher.php b/app/Services/Grapher/Graph/Switcher.php new file mode 100644 index 000000000..67d4c480f --- /dev/null +++ b/app/Services/Grapher/Graph/Switcher.php @@ -0,0 +1,194 @@ + Switch Graph + * + * @author Barry O'Donovan + * @author Yann Robin + * @category IXP + * @package IXP\Services\Grapher + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Switcher extends Graph +{ + /** + * Switcher to graph + * + * @var SwitcherModel + */ + private $switch = null; + + /** + * Constructor + * + * @param Grapher $grapher + * @param SwitcherModel $s + */ + public function __construct( Grapher $grapher, SwitcherModel $s ) + { + parent::__construct( $grapher ); + $this->switch = $s; + } + + /** + * Get the switch we're set to use + * + * @return SwitcherModel + */ + public function switch(): SwitcherModel + { + return $this->switch; + } + + /** + * Set the switch we should use + * + * @param SwitcherModel $switch + * + * @return static Fluid interface + */ + public function setSwitch( SwitcherModel $switch ): static + { + if( $this->switch() && $this->switch()->id !== $switch->id ) { + $this->wipe(); + } + + $this->switch = $switch; + return $this; + } + + /** + * The name of a graph (e.g. member name, IXP name, etc) + * + * @return string + */ + #[\Override] + public function name(): string + { + return $this->switch()->name ?: ''; + } + + /** + * A unique identifier for this 'graph type' + * + * E.g. for an IXP, it might be ixpxxx where xxx is the database id + * + * @return string + */ + #[\Override] + public function identifier(): string + { + return sprintf( "switch%05d", $this->switch()->id ); + } + + /** + * This function controls access to the graph. + * + * {@inheritDoc} + * + * For (public) vlan aggregate graphs we pretty much allow complete access. + * + * @return bool + */ + #[\Override] + public function authorise(): bool + { + /** @var User $us */ + $us = Auth::getUser(); + + if( Auth::check() && $us->isSuperUser() ) { + return $this->allow(); + } + + if( is_numeric( config( 'grapher.access.switch' ) ) && (int)config( 'grapher.access.switch' ) === User::AUTH_PUBLIC ) { + return $this->allow(); + } + + if( Auth::check() && is_numeric( config( 'grapher.access.switch' ) ) && $us->privs() >= (int)config( 'grapher.access.switch' ) ) { + return $this->allow(); + } + + $this->deny(); + return false; + } + + /** + * Generate a URL to get this graphs 'file' of a given type + * + * @param array $overrides Allow standard parameters to be overridden (e.g. category) + * + * @return string + */ + #[\Override] + public function url( array $overrides = [] ): string + { + return parent::url( $overrides ) . sprintf("&id=%d", + $overrides[ 'id' ] ?? $this->switch()->id + ); + } + + /** + * Get parameters in bulk as associative array + * + * Extends base function + * + * @return (\Carbon\Carbon|int|mixed|null)[] + * + * @psalm-return array{protocol: mixed, period: mixed, category: mixed, type: mixed, period_start?: \Carbon\Carbon|null, period_end?: \Carbon\Carbon|null, id: int} + */ + #[\Override] + public function getParamsAsArray(): array + { + $p = parent::getParamsAsArray(); + $p['id'] = $this->switch()->id; + return $p; + } + + /** + * Process user input for the parameter: switch + * + * Does a abort(404) if invalid + * + * @param int $s The user input value + * + * @return SwitcherModel The verified / sanitised / default value + */ + public static function processParameterSwitch( int $s ): SwitcherModel + { + return SwitcherModel::findOrFail( $s ); + } +} \ No newline at end of file diff --git a/app/Services/Grapher/Graph/Trunk.php b/app/Services/Grapher/Graph/Trunk.php new file mode 100644 index 000000000..5b53970bb --- /dev/null +++ b/app/Services/Grapher/Graph/Trunk.php @@ -0,0 +1,197 @@ + Switch Graph + * + * @author Barry O'Donovan + * @author Yann Robin + * @category IXP + * @package IXP\Services\Grapher + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Trunk extends Graph +{ + /** + * Trunk to graph + * + * @var string + */ + private $trunkname = null; + + /** + * Constructor + * + * @param Grapher $grapher + * @param string $n + * + * @throws + */ + public function __construct( Grapher $grapher, string $n ) + { + parent::__construct( $grapher ); + $this->setTrunkname($n); + } + + /** + * Get the trunk name we're meant to graph + * + * @return string + */ + public function trunkname(): string + { + return $this->trunkname; + } + + /** + * Set the trunk we should use + * + * @param string $n + * + * @throws + */ + public function setTrunkname( string $n ): static + { + if( !is_array( config('grapher.backends.mrtg.trunks.'.$n) ) ) { + throw new ParameterException("Invalid trunk name in constructor"); + } + + $this->trunkname = $n; + return $this; + } + + /** + * The name of a graph (e.g. member name, IXP name, etc) + * @return string + */ + #[\Override] + public function name(): string + { + return config('grapher.backends.mrtg.trunks.' . $this->trunkname().'.title' ); + } + + /** + * A unique identifier for this 'graph type' + * + * E.g. for an IXP, it might be ixpxxx where xxx is the database id + * + * @return string + */ + #[\Override] + public function identifier(): string + { + return $this->trunkname(); + } + + /** + * This function controls access to the graph. + * + * {@inheritDoc} + * + * For (public) vlan aggregate graphs we pretty much allow complete access. + * + * @return bool + */ + #[\Override] + public function authorise(): bool + { + /** @var User $us */ + $us = Auth::getUser(); + + if( Auth::check() && $us->isSuperUser() ) { + return $this->allow(); + } + + if( is_numeric( config( 'grapher.access.trunk' ) ) && (int)config( 'grapher.access.trunk' ) === User::AUTH_PUBLIC ) { + return $this->allow(); + } + + if( Auth::check() && is_numeric( config( 'grapher.access.trunk' ) ) && $us->privs() >= config( 'grapher.access.trunk' ) ) { + return $this->allow(); + } + + $this->deny(); + return false; + } + + /** + * Generate a URL to get this graphs 'file' of a given type + * + * @param array $overrides Allow standard parameters to be overridden (e.g. category) + * + * @return string + */ + #[\Override] + public function url( array $overrides = [] ): string + { + return parent::url( $overrides ) . sprintf("&id=%s", + $overrides[ 'id' ] ?? $this->trunkname() + ); + } + + /** + * Get parameters in bulk as associative array + * + * Extends base function + * + * @return (\Carbon\Carbon|mixed|null|string)[] + * + * @psalm-return array{protocol: mixed, period: mixed, category: mixed, type: mixed, period_start?: \Carbon\Carbon|null, period_end?: \Carbon\Carbon|null, id: string} + */ + #[\Override] + public function getParamsAsArray(): array + { + $p = parent::getParamsAsArray(); + $p['id'] = $this->trunkname(); + return $p; + } + + /** + * Process user input for the parameter: switch + * + * Does a abort(404) if invalid + * + * @param string $n The user input value + * + * @return string The verified / sanitised / default value + */ + public static function processParameterTrunkname( string $n ): string + { + if( !is_array( config('grapher.backends.mrtg.trunks.'.$n) ) ) { + abort(404); + } + return $n; + } +} \ No newline at end of file diff --git a/app/Services/Grapher/Graph/VirtualInterface.php b/app/Services/Grapher/Graph/VirtualInterface.php new file mode 100644 index 000000000..9443c3566 --- /dev/null +++ b/app/Services/Grapher/Graph/VirtualInterface.php @@ -0,0 +1,218 @@ + VirtualInterface Graph (LAGs) + * + * @author Barry O'Donovan + * @author Yann Robin + * @category IXP + * @package IXP\Services\Grapher + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class VirtualInterface extends Graph +{ + /** + * VirtualInterface to graph + * + * @var VirtualInterfaceModel + */ + private $virtint = null; + + /** + * Constructor + * + * @param Grapher $grapher + * @param VirtualInterfaceModel $vi + */ + public function __construct( Grapher $grapher, VirtualInterfaceModel $vi ) + { + parent::__construct( $grapher ); + $this->virtint = $vi; + } + + /** + * Get the vlan we're set to use + * + * @return VirtualInterfaceModel + */ + public function virtualInterface(): VirtualInterfaceModel + { + return $this->virtint; + } + + /** + * Get the customer owning this virtual interface + */ + public function customer(): Customer|null + { + return $this->virtint->customer; + } + + /** + * Set the interface we should use + * + * @param VirtualInterfaceModel $vi + * + * @return static Fluid interface + */ + public function setVirtualInterface( VirtualInterfaceModel $vi ): static + { + if( $this->virtualInterface() && $this->virtualInterface()->id !== $vi->id ) { + $this->wipe(); + } + + $this->virtint = $vi; + return $this; + } + + /** + * The name of a graph (e.g. member name, IXP name, etc) + * + * @return string + */ + #[\Override] + public function name(): string + { + $pi = $this->virtualInterface()->physicalInterfaces[ 0 ]; + return "LAG over ports on " . $pi->switchPort->switcher->name; + } + + /** + * A unique identifier for this 'graph type' + * + * E.g. for an IXP, it might be ixpxxx where xxx is the database id + * @return string + */ + #[\Override] + public function identifier(): string + { + return sprintf( "vi%05d", $this->virtualInterface()->id ); + } + + /** + * This function controls access to the graph. + * + * {@inheritDoc} + * + * For (public) vlan aggregate graphs we pretty much allow complete access. + * + * @return bool + */ + #[\Override] + public function authorise(): bool + { + /** @var User $us */ + $us = Auth::getUser(); + + if( is_numeric( config( 'grapher.access.customer' ) ) && config( 'grapher.access.customer' ) === User::AUTH_PUBLIC ) { + return $this->allow(); + } + + if( !Auth::check() ) { + $this->deny(); + return false; + } + + if( $us->isSuperUser() ) { + return $this->allow(); + } + + if( $us->custid === $this->virtualInterface()->customer->id ) { + return $this->allow(); + } + + if( config( 'grapher.access.customer' ) !== 'own_graphs_only' + && is_numeric( config( 'grapher.access.customer' ) ) + && $us->privs() >= config( 'grapher.access.customer' ) + ) { + return $this->allow(); + } + + Log::notice( sprintf( "[Grapher] [VirtualInterface]: user %d::%s tried to access a virtual interface graph " + . "{$this->virtualInterface()->id} which is not theirs", Auth::id(), $us->username ) + ); + + $this->deny(); + return false; + } + + /** + * Generate a URL to get this graphs 'file' of a given type + * + * @param array $overrides Allow standard parameters to be overridden (e.g. category) + * @return string + */ + #[\Override] + public function url( array $overrides = [] ): string + { + return parent::url( $overrides ) . sprintf("&id=%d", + $overrides[ 'id' ] ?? $this->virtualInterface()->id + ); + } + + /** + * Get parameters in bulk as associative array + * + * Extends base function + * + * @return (\Carbon\Carbon|int|mixed|null)[] + * + * @psalm-return array{protocol: mixed, period: mixed, category: mixed, type: mixed, period_start?: \Carbon\Carbon|null, period_end?: \Carbon\Carbon|null, id: int} + */ + #[\Override] + public function getParamsAsArray(): array + { + $p = parent::getParamsAsArray(); + $p['id'] = $this->virtualInterface()->id; + return $p; + } + + /** + * Process user input for the parameter: virtint + * + * Does a abort(404) if invalid + * + * @param int $vi The user input value + * + * @return VirtualInterfaceModel The verified / sanitised / default value + */ + public static function processParameterVirtualInterface( int $vi ): VirtualInterfaceModel + { + return VirtualInterfaceModel::findOrFail( $vi ); + } +} \ No newline at end of file diff --git a/app/Services/Grapher/Graph/Vlan.php b/app/Services/Grapher/Graph/Vlan.php new file mode 100644 index 000000000..8c6c37ee0 --- /dev/null +++ b/app/Services/Grapher/Graph/Vlan.php @@ -0,0 +1,200 @@ + Vlan Graph + * + * @author Barry O'Donovan + * @author Yann Robin + * @category IXP + * @package IXP\Services\Grapher + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Vlan extends Graph +{ + /** + * Vlan to graph + * + * @var VlanModel + */ + private $vlan = null; + + /** + * Constructor + * + * @param Grapher $grapher + * @param VlanModel $v + * + */ + public function __construct( Grapher $grapher, VlanModel $v ) + { + parent::__construct( $grapher ); + $this->vlan = $v; + } + + /** + * Get the vlan we're set to use + * + * @return VlanModel + */ + public function vlan(): VlanModel + { + return $this->vlan; + } + + /** + * Set the vlan we should use + * + * @param VlanModel $vlan + * + * @return static Fluid interface + */ + public function setVlan( VlanModel $vlan ): static + { + if( $this->vlan() && $this->vlan()->id !== $vlan->id ) { + $this->wipe(); + } + + $this->vlan = $vlan; + return $this; + } + + /** + * The name of a graph (e.g. member name, IXP name, etc) + * + * @return string + */ + #[\Override] + public function name(): string + { + return $this->vlan()->name ?: ''; + } + + /** + * A unique identifier for this 'graph type' + * + * E.g. for an IXP, it might be ixpxxx where xxx is the database id + * + * @return string + */ + #[\Override] + public function identifier(): string + { + return sprintf( "vlan%05d", $this->vlan()->number ); + } + + /** + * This function controls access to the graph. + * + * {@inheritDoc} + * + * For (public) vlan aggregate graphs we pretty much allow complete access. + * + * @return bool + */ + #[\Override] + public function authorise(): bool + { + /** @var User $us */ + $us = Auth::getUser(); + + if( Auth::check() && $us->isSuperUser() ) { + return $this->allow(); + } + + if( $this->vlan()->private ) { + // FIXME + $this->deny(); + return false; + } + + if( is_numeric( config( 'grapher.access.vlan' ) ) && (int)config( 'grapher.access.vlan' ) === User::AUTH_PUBLIC ) { + return $this->allow(); + } + + if( Auth::check() && is_numeric( config( 'grapher.access.vlan' ) ) && $us->privs() >= config( 'grapher.access.vlan' ) ) { + return $this->allow(); + } + + $this->deny(); + return false; + } + + /** + * Generate a URL to get this graphs 'file' of a given type + * + * @param array $overrides Allow standard parameters to be overridden (e.g. category) + * @return string + */ + #[\Override] + public function url( array $overrides = [] ): string + { + return parent::url( $overrides ) . sprintf("&id=%d", + $overrides[ 'id' ] ?? $this->vlan()->id + ); + } + + /** + * Get parameters in bulk as associative array + * + * Extends base function + * + * @return (\Carbon\Carbon|int|mixed|null)[] + * + * @psalm-return array{protocol: mixed, period: mixed, category: mixed, type: mixed, period_start?: \Carbon\Carbon|null, period_end?: \Carbon\Carbon|null, id: int} + */ + #[\Override] + public function getParamsAsArray(): array + { + $p = parent::getParamsAsArray(); + $p['id'] = $this->vlan()->id; + return $p; + } + + /** + * Process user input for the parameter: vlan + * + * Does a abort(404) if invalid + * + * @param int $v The user input value + * + * @return VlanModel The verified / sanitised / default value + */ + public static function processParameterVlan( int $v ): VlanModel + { + return VlanModel::findOrFail( $v ); + } +} \ No newline at end of file diff --git a/app/Services/Grapher/Graph/VlanInterface.php b/app/Services/Grapher/Graph/VlanInterface.php new file mode 100644 index 000000000..d239e7e87 --- /dev/null +++ b/app/Services/Grapher/Graph/VlanInterface.php @@ -0,0 +1,229 @@ + VlanInterface Graph (l3) + * + * @author Barry O'Donovan + * @author Yann Robin + * @category IXP + * @package IXP\Services\Grapher + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class VlanInterface extends Graph +{ + /** + * VlanInterface to graph + * + * @var VlanInterfaceModel + */ + private $vlanint = null; + + /** + * Constructor + * @param Grapher $grapher + * @param VlanInterfaceModel $vli + */ + public function __construct( Grapher $grapher, VlanInterfaceModel $vli ) + { + parent::__construct( $grapher ); + $this->vlanint = $vli; + } + + /** + * Get the vlan we're set to use + * + * @return VlanInterfaceModel + */ + public function vlanInterface(): VlanInterfaceModel + { + return $this->vlanint; + } + + /** + * Set the interface we should use + * + * @param VlanInterfaceModel $vli + * + * @return static Fluid interface + */ + public function setVlanInterface( VlanInterfaceModel $vli ): static + { + if( $this->vlanInterface() && $this->vlanInterface()->id !== $vli->id ) { + $this->wipe(); + } + + $this->vlanint = $vli; + return $this; + } + + /** + * Get the customer owning this virtual interface + */ + public function customer(): Customer|null + { + return $this->vlanint->virtualInterface->customer; + } + + /** + * The name of a graph (e.g. member name, IXP name, etc) + * @return string + */ + #[\Override] + public function name(): string + { + $n = "VLAN Interface Traffic"; + + if( $this->vlanInterface()->ipv4enabled || $this->vlanInterface()->ipv6enabled ) { + $n .= ' :: '; + + if( $this->vlanInterface()->ipv4enabled ) { + $n .= $this->vlanInterface()->ipv4address->address . ' '; + } + + if( $this->vlanInterface()->ipv6enabled ) { + $n .= $this->vlanInterface()->ipv6address->address; + } + } + return $n; + } + + /** + * A unique identifier for this 'graph type' + * + * E.g. for an IXP, it might be ixpxxx where xxx is the database id + * @return string + */ + #[\Override] + public function identifier(): string + { + return sprintf( "vli%05d", $this->vlanInterface()->id ); + } + + /** + * This function controls access to the graph. + * + * {@inheritDoc} + * + * For (public) vlan aggregate graphs we pretty much allow complete access. + * + * @return bool + */ + #[\Override] + public function authorise(): bool + { + /** @var User $us */ + $us = Auth::getUser(); + + if( is_numeric( config( 'grapher.access.customer' ) ) && (int)config( 'grapher.access.customer' ) === User::AUTH_PUBLIC ) { + return $this->allow(); + } + + if( !Auth::check() ) { + $this->deny(); + return false; + } + + if( $us->isSuperUser() ) { + return $this->allow(); + } + + if( $us->custid === $this->vlanInterface()->virtualInterface->customer->id ) { + return $this->allow(); + } + + if( config( 'grapher.access.customer' ) !== 'own_graphs_only' + && is_numeric( config( 'grapher.access.customer' ) ) + && $us->privs() >= config( 'grapher.access.customer' ) + ) { + return $this->allow(); + } + + Log::notice( sprintf( "[Grapher] [VlanInterface]: user %d::%s tried to access a vlan interface graph " + . "{$this->vlanInterface()->id} which is not theirs", Auth::id(), $us->username ) + ); + + $this->deny(); + return false; + } + + /** + * Generate a URL to get this graphs 'file' of a given type + * + * @param array $overrides Allow standard parameters to be overridden (e.g. category) + * + * @return string + */ + #[\Override] + public function url( array $overrides = [] ): string + { + return parent::url( $overrides ) . sprintf("&id=%d", + $overrides[ 'id' ] ?? $this->vlanInterface()->id + ); + } + + /** + * Get parameters in bulk as associative array + * + * Extends base function + * + * @return (\Carbon\Carbon|int|mixed|null)[] + * + * @psalm-return array{protocol: mixed, period: mixed, category: mixed, type: mixed, period_start?: \Carbon\Carbon|null, period_end?: \Carbon\Carbon|null, id: int} + */ + #[\Override] + public function getParamsAsArray(): array + { + $p = parent::getParamsAsArray(); + $p['id'] = $this->vlanInterface()->id; + return $p; + } + + /** + * Process user input for the parameter: vlanint + * + * Does a abort(404) if invalid + * + * @param int $vli The user input value + * + * @return VlanInterfaceModel The verified / sanitised / default value + */ + public static function processParameterVlanInterface( int $vli ): VlanInterfaceModel + { + return VlanInterfaceModel::findOrFail( $vli ); + } +} \ No newline at end of file diff --git a/app/Services/Grapher/Renderer.php b/app/Services/Grapher/Renderer.php new file mode 100644 index 000000000..49886ab78 --- /dev/null +++ b/app/Services/Grapher/Renderer.php @@ -0,0 +1,112 @@ + Renderer of the given graph + * + * @author Barry O'Donovan + * @category Grapher + * @package IXP\Services\Grapher + * @copyright Copyright (C) 2009 - 2019 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Renderer +{ + /** + * Style for future expansion + * @var string + */ + public const BOX_STYLE_LEGACY = 'legacy'; + + /** + * All styles + * @array + */ + public const BOX_STYLES = [ + self::BOX_STYLE_LEGACY, + ]; + + + /** + * Graph under consideration + * + * @var Graph + */ + private $graph; + + + /** + * Constructor + */ + public function __construct( Graph $g ) + { + $this->graph = $g; + } + + /** + * Access for the graph object under consideration + * + * @return Graph + */ + private function graph(): Graph + { + return $this->graph; + } + + /** + * Render the graph box + * + * @param string $style The style (See BOX_STYLES above) + * + * @return string + * + * @throws RendererException + */ + public function box( string $style ): string + { + if( !in_array($style,self::BOX_STYLES) || !View::exists('services.grapher.renderer.box.'.$style) ) { + throw new RendererException("No box style exists for: {$style}"); + } + + return View::make('services.grapher.renderer.box.'.$style)->with( [ 'graph' => $this->graph() ])->render(); + } + + /** + * Alias for box renderer with legacy style + * + * @return string + * + * @throws RendererException + */ + public function boxLegacy(): string + { + return $this->box( self::BOX_STYLE_LEGACY ); + } +} \ No newline at end of file diff --git a/app/Services/Grapher/Renderer/Extensions/Grapher.php b/app/Services/Grapher/Renderer/Extensions/Grapher.php new file mode 100644 index 000000000..c83749df0 --- /dev/null +++ b/app/Services/Grapher/Renderer/Extensions/Grapher.php @@ -0,0 +1,204 @@ + Renderer view extensions + * + * See: http://www.foilphp.it/docs/EXTENDING/CUSTOM-EXTENSIONS.html + * + * @author Barry O'Donovan + * @category Grapher + * @package IXP\Services\Grapher + * @copyright Copyright (C) 2009 - 2019 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Grapher implements ExtensionInterface +{ + + private $args; + + /** + * @return void + */ + #[\Override] + /** + * @return void + */ + public function setup(array $args = []) + { + $this->args = $args; + } + + /** + * @return array + * + * @psalm-return array + */ + #[\Override] + /** + * @return array + * + * @psalm-return array + */ + public function provideFilters() + { + return []; + } + + /** + * @return (static|string)[][] + * + * @psalm-return array{grapher: list{static, 'getObject'}} + */ + #[\Override] + /** + * @return (static|string)[][] + * + * @psalm-return array{grapher: list{static, 'getObject'}} + */ + public function provideFunctions() + { + return [ + 'grapher' => [$this, 'getObject'] + ]; + } + + public function getObject(): static + { + return $this; + } + + /** + * Escape SNMP communities so they are suitable for use in Mrtg config files. + * + * As per: https://oss.oetiker.ch/mrtg/doc/mrtg-reference.en.html + * + * > If your community contains a "@" or a " " these characters must be escaped with a "\". + * + * @param string $c The SNMP community to be escaped + * + * @return string The escaped community + */ + public function escapeCommunityForMrtg( string $c ): string + { + return str_replace( '@', '\@', str_replace(' ', '\\ ', $c ) ); + } + + /** + * Scale function + * + * This function will scale a number to (for example for traffic + * measured in bits/second) to Kbps, Mbps, Gbps or Tbps. + * + * Valid string formats ($strFormats) and what they return are: + * bytes => Bytes, KBytes, MBytes, GBytes, TBytes + * pkts / errs / discs => pps, Kpps, Mpps, Gpps, Tpps + * bits / * => bits, Kbits, Mbits, Gbits, Tbits + * + * Valid return types ($format) are: + * 0 => fully formatted and scaled value. E.g. 12,354.235 Tbits + * 1 => scaled value without string. E.g. 12,354.235 + * 2 => just the string. E.g. Tbits + * + * @param float $value The value to scale + * @param string $format The format to sue (as above: bytes / pkts / errs / etc ) + * @param int $decimals Number of decimals after the decimal point. Defaults to 3. + * @param int $returnType Type of string to return. Valid values are listed above. Defaults to 0. + * + * @return string Scaled / formatted number / type. + */ + public function scale( float $value, string $format, int $decimals = 3, int $returnType = 0 ): string + { + if( $format === "bytes" ) { + $formats = [ + "Bytes", "KBytes", "MBytes", "GBytes", "TBytes" + ]; + } else if( in_array( $format, [ 'pkts', 'errs', 'discs', 'bcasts' ] ) ) { + $formats = [ + "pps", "Kpps", "Mpps", "Gpps", "Tpps" + ]; + } else { + $formats = [ + "bits", "Kbits", "Mbits", "Gbits", "Tbits" + ]; + } + + for( $i = 0; $i < sizeof( $formats ); $i++ ) + { + $format = $i>4 ? $formats[4] : $formats[$i]; + if( ( $value / 1000.0 < 1.0 ) || ( sizeof( $formats ) === $i + 1 ) ) { + if( $returnType == 0 ) + return number_format( $value, $decimals ) . " " . $format; + elseif( $returnType == 1 ) + return number_format( $value, $decimals ); + else + return $format; + } else { + $value /= 1000.0; + } + } + + // should not get here: + return ''; + } + + /** + * Utility function to determine if the currently logged in user can access 'all customer' graphs + * + * @return bool + */ + public function canAccessAllCustomerGraphs(): bool + { + return CustomerGraph::authorisedForAllCustomers(); + } + + /** + * Utility function to determine if the currently logged in user can access 'all customers' p2p' graphs + * + * @return bool + */ + public function canAccessAllCustomerP2pGraphs(): bool + { + return P2pGraph::authorisedForAllCustomers(); + } + + /** + * Utility function to determine if the currently logged in user can access 'all customers' latency' graphs + * + * @return bool + */ + public function canAccessAllCustomerLatencyGraphs(): bool + { + return LatencyGraph::authorisedForAllCustomers(); + } +} \ No newline at end of file diff --git a/app/Services/Grapher/Statistics.php b/app/Services/Grapher/Statistics.php new file mode 100644 index 000000000..65e34caaf --- /dev/null +++ b/app/Services/Grapher/Statistics.php @@ -0,0 +1,464 @@ + Statistics of the given graph + * + * @author Barry O'Donovan + * @category Grapher + * @package IXP\Services\Grapher + * @copyright Copyright (C) 2009 - 2019 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Statistics +{ + /** + * Total packets/bits in + * + * @var float + */ + private $totalIn; + + /** + * Total packets/bits out + * + * @var float + */ + private $totalOut; + + /** + * Current packets/bits in + * + * @var float + */ + private $curIn; + + /** + * Current packets/bits out + * + * @var float + */ + private $curOut; + + /** + * Average packets/bits in + * + * @var float + */ + private $averageIn; + + /** + * Average packets/bits out + * + * @var float + */ + private $averageOut; + + /** + * Max packets/bits in + * + * @var float + */ + private $maxIn; + + /** + * When max packets/bits in occurred + * + * @var ?Carbon + */ + private $maxInAt; + + /** + * Max packets/bits out + * + * @var float + */ + private $maxOut; + + /** + * When max packets/bits out occurred + * + * @var ?Carbon + */ + private $maxOutAt; + + /** + * Graph under consideration + * + * @var Graph + */ + private $graph; + + + /** + * Constructor + * + * @param Graph $g + */ + public function __construct( Graph $g ) + { + $this->graph = $g; + $this->process(); + } + + /** + * Access for the graph object under consideration + * + * @return Graph + */ + public function graph(): Graph + { + return $this->graph; + } + + /** + * Access for the graph object under consideration + * + * Private access as this should be accessed publicly + * via the Graph object (where it is also cached). + * + * @return array + */ + private function data(): array + { + return $this->graph()->data(); + } + + function process(): void + { + $maxIn = 0; + $maxOut = 0; + $maxInAt = 0; + $maxOutAt = 0; + $totalIn = 0; + $totalOut = 0; + $intLastTime = 0; + + $data = $this->data(); + + if( is_array( $data ) && isset( $data[0][0] ) ) { + $curenddate = false; + $starttime = $this->data()[0][0]; + + foreach( $data as $i => $v ) { + $curenddate = $v[ 0 ]; + + [ $intTime, $avgratein, $avgrateout, $peakratein, $peakrateout ] = $v; + + if( $peakratein > $maxIn ) { + $maxIn = $peakratein; + $maxInAt = $intTime; + } + if( $peakrateout > $maxOut ) { + $maxOut = $peakrateout; + $maxOutAt = $intTime; + } + + if( $intLastTime == 0 ) { + $intLastTime = $intTime; + } + else { + $intRange = ($intTime > $intLastTime) ? $intTime - $intLastTime : $intLastTime - $intTime; + $totalIn += ( $intRange * $avgratein ); + $totalOut += ( $intRange * $avgrateout ); + $intLastTime = $intTime; + } + } + + $endtime = $curenddate; + $totalTime = $endtime - $starttime; + } + + $curIn = isset( $avgratein ) ? $avgratein : 0.0; + $curOut = isset( $avgrateout ) ? $avgrateout : 0.0; + $totalTime = isset( $totalTime ) && $totalTime ? $totalTime : 1; + + $this->setTotalIn( floor( $totalIn ) ) + ->setTotalOut( floor( $totalOut ) ) + ->setCurrentIn( (float)$curIn ) + ->setCurrentOut( (float)$curOut ) + ->setMaxIn( (float)$maxIn ) + ->setMaxOut( (float)$maxOut ) + ->setMaxInAt( (int)$maxInAt ) + ->setMaxOutAt( (int)$maxOutAt ) + ->setAverageIn( (float)( $totalIn / $totalTime ) ) + ->setAverageOut( (float)( $totalOut / $totalTime ) ); + } + + /** + * Set statistics value + * + * @param float $value + * + * @return static (for fluid interface) + */ + public function setTotalIn( float $value ): static + { + $this->totalIn = $value; + return $this; + } + + /** + * Set statistics value + * + * @param float $value + * + * @return static (for fluid interface) + */ + public function setTotalOut( float $value ): static + { + $this->totalOut = $value; + return $this; + } + + /** + * Set statistics value + * + * @param float $v + * + * @return static (for fluid interface) + */ + public function setCurrentIn( float $v ): static + { + $this->curIn = $v; + return $this; + } + + /** + * Set statistics value + * + * @param float $v + * + * @return static (for fluid interface) + */ + public function setCurrentOut( float $v ): static + { + $this->curOut = $v; + return $this; + } + + /** + * Set statistics value + * + * @param float $v + * + * @return static (for fluid interface) + */ + public function setAverageIn( float $v ): static + { + $this->averageIn = $v; + return $this; + } + + /** + * Set statistics value + * + * @param float $v + * + * @return static (for fluid interface) + */ + public function setAverageOut( float $v ): static + { + $this->averageOut = $v; + return $this; + } + + /** + * Set statistics value + * + * @param float $v + * + * @return static (for fluid interface) + */ + public function setMaxIn( float $v ): static + { + $this->maxIn = $v; + return $this; + } + + /** + * Set statistics value + * + * @param int $v + * + * @return static (for fluid interface) + * + * @throws \Exception + */ + public function setMaxInAt( int $v ): static + { + $this->maxInAt = $v ? new Carbon( $v ) : null; + return $this; + } + + /** + * Set statistics value + * + * @param float $v + * + * @return static (for fluid interface) + */ + public function setMaxOut( float $v ): static + { + $this->maxOut = $v; + return $this; + } + + /** + * Set statistics value + * + * @param int $v + * + * @return static (for fluid interface) + * + * @throws \Exception + */ + public function setMaxOutAt( int $v ): static + { + $this->maxOutAt = $v ? new Carbon( $v ) : null; + return $this; + } + + /** + * Get statistics value + * + * @return float + */ + public function totalIn(): float + { + return $this->totalIn; + } + + /** + * Get statistics value + * + * @return float + */ + public function totalOut(): float + { + return $this->totalOut; + } + + /** + * Get statistics value + * + * @return float + */ + public function curIn(): float + { + return $this->curIn; + } + + /** + * Get statistics value + * + * @return float + */ + public function curOut(): float + { + return $this->curOut; + } + + /** + * Get statistics value + * + * @return float + */ + public function averageIn(): float + { + return $this->averageIn; + } + + /** + * Get statistics value + * + * @return float + */ + public function averageOut(): float + { + return $this->averageOut; + } + + /** + * Get statistics value + * + * @return float + */ + public function maxIn(): float + { + return $this->maxIn; + } + + /** + * Get statistics value + * + * @return float + */ + public function maxOut(): float + { + return $this->maxOut; + } + + /** + * Get statistics value + */ + public function maxInAt(): ?Carbon + { + return $this->maxInAt; + } + + /** + * Get statistics value + */ + public function maxOutAt(): ?Carbon + { + return $this->maxOutAt; + } + + /** + * Get all defined stats as an associative array + * + * @return (Carbon|float|null)[] + * + * @psalm-return array{totalin: float, totalout: float, curin: float, curout: float, averagein: float, averageout: float, maxin: float, maxout: float, maxinat: Carbon|null, maxoutat: Carbon|null} + */ + public function all(): array + { + return [ + 'totalin' => $this->totalIn(), + 'totalout' => $this->totalOut(), + 'curin' => $this->curIn(), + 'curout' => $this->curOut(), + 'averagein' => $this->averageIn(), + 'averageout' => $this->averageOut(), + 'maxin' => $this->maxIn(), + 'maxout' => $this->maxOut(), + 'maxinat' => $this->maxInAt(), + 'maxoutat' => $this->maxOutAt(), + ]; + } +} \ No newline at end of file diff --git a/app/Services/Helpdesk/ApiException.php b/app/Services/Helpdesk/ApiException.php new file mode 100644 index 000000000..2e97799ea --- /dev/null +++ b/app/Services/Helpdesk/ApiException.php @@ -0,0 +1,91 @@ + + * string(13) "RecordInvalid" + * ["description"]=> + * string(24) "Record validation errors" + * ["details"]=> + * object(stdClass)#6752 (1) { + * ["email"]=> + * array(1) { + * [0]=> + * object(stdClass)#6912 (2) { + * ["description"]=> + * string(62) "Email: ccc@example.com is already being used by another user" + * ["error"]=> + * string(14) "DuplicateValue" + * } + * } + * } + * } + * + */ + protected $errorDetails; + + /** + * Set the error details + * + * @param object $ed + */ + public function setErrorDetails( $ed ): void { + $this->errorDetails = $ed; + } + + /** + * Get the error details + * @return object + */ + public function getErrorDetails() { + return $this->errorDetails; + } + + + /** + * Zendesk uses email addresses as a unique key. + * + * This test lets us check for that error in a helpdesk agnostic way. + * + * @return boolean If the error / exception relates to a duplicate email address + */ + public function userIsDuplicateEmail() { + if( $this->errorDetails && $this->errorDetails->error == 'RecordInvalid' && isset( $this->errorDetails->details->email ) ) { + foreach( $this->errorDetails->details->email as $ed ) { + if( $ed->error == 'DuplicateValue' ) + return true; + } + } + return false; + } +} diff --git a/app/Services/Helpdesk/ConfigurationException.php b/app/Services/Helpdesk/ConfigurationException.php new file mode 100644 index 000000000..06747bfef --- /dev/null +++ b/app/Services/Helpdesk/ConfigurationException.php @@ -0,0 +1,26 @@ + Zendesk + * + * @author Barry O'Donovan + * @author Yann Robin + * @category Helpdesk + * @package IXP\Services + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Zendesk implements HelpdeskContract +{ + /** + * The Zendesk Client + * + * @var ZendeskAPI + */ + private ZendeskAPI $client; + + /** + * Debug object + * + * No specific rules around this yet. Usually means something bad happened... + */ + private $debug; + + /** + * Zendesk constructor. + * + * @param $config + * + * @throws + */ + public function __construct( $config ) + { + if( !isset( $config['subdomain'] ) || !isset( $config['token'] ) || !isset( $config['email'] ) ){ + throw new ConfigurationException( "Zendesk requires that 'subdomain', 'token', 'email' be configured" ); + } + + $this->client = new ZendeskAPI( $config['subdomain'], $config['email'] ); + $this->client->setAuth('basic', [ 'username' => $config['email'], 'token' => $config['token'] ] ); + } + + /** + * Centralised function to perform the Zendesk API call, throw exceptions, etc + * + * @param function $fn Anonymous function containing API call + * + * @throws + * + * @psalm-param \Closure():(\stdClass|null)|\Closure():void $fn + */ + protected function callApi( \Closure $fn ) + { + try { + usleep( 60/200 ); + return call_user_func( $fn ); + } catch( \Exception $e ) { + $this->debug = $this->client->getDebug(); + + $apie = new ApiException( "Zendesk API error - further details available from \$helpdeskInstance->getDebug() / \$this->getErrorDetails()" ); + + if( $e instanceof \Zendesk\API\Exceptions\ApiResponseException ) + $apie->setErrorDetails( (object)$e->getErrorDetails() ); + + throw $apie; + } + } + + /** + * Return the Zendesk debug information + */ + #[\Override] + public function getDebug() + { + return $this->debug; + } + + /** + * Find all tickets on the helpdesk + * + * @throws \IXP\Services\Helpdesk\ApiException + * + * @return void + */ + #[\Override] + public function ticketsFindAll() + { + $this->callApi( function() { $this->client->tickets()->findAll(); } ); + } + + // ******************************************************************************************** + // ******************************************************************************************** + // ******************************************************************************************** + // + // COMPANIES / ORGANISATIONS + // + // ******************************************************************************************** + // ******************************************************************************************** + // ******************************************************************************************** + + + /** + * Convert a IXP Customer entity into an associated array as rerquired by Zendesk's API + * + * @param Customer $cust The IXP Manager customer entity + * @param bool|int $id If updating, set to Zendesk organisation ID + * + * @return ((bool|int|mixed|null|string)[]|int|null|string|true)[] Data in associate array format as required by Zendesk PHP API + * + * @psalm-return array{external_id: int, name: null|string, domain_names?: string, id?: int|true, organization_fields: array{asn: int|null, as_set: null|string, peering_email: null|string, noc_email: null|string, shortname: null|string, type: mixed, addresses: string, status: mixed, has_left: bool}} + */ + private function customerEntityToZendeskObject( Customer $cust, bool|int $id = false ): array + { + $data = []; + $data['external_id'] = $cust->id; + $data['name'] = $cust->name; + + if( $id ) { + // updating so set the Zendesk ID: + $data['id'] = $id; + } else { + // creating - set the initial domains: + if( preg_match( '/^(http[s]*\:\/\/)?www\.([a-zA-Z0-9\.\-]+).*$/', $cust->corpwww, $matches ) ) + $data['domain_names'] = $matches[2]; + } + + $data['organization_fields'] = [ + 'asn' => $cust->autsys, + 'as_set' => $cust->peeringmacro, + 'peering_email' => $cust->peeringemail, + 'noc_email' => $cust->nocemail, + 'shortname' => $cust->shortname, + 'type' => Customer::$CUST_TYPES_TEXT[ $cust->type ], + 'addresses' => '', + 'status' => Customer::$CUST_STATUS_TEXT[ $cust->status ], + 'has_left' => $cust->hasLeft() + ]; + + foreach( $cust->virtualInterfaces as $vi ) { + foreach( $vi->vlanInterfaces as $vli ) { + if( $vli->ipv4enabled && $vli->ipv4address ){ + $data['organization_fields']['addresses'] .= $vli->ipv4address->address . "\n"; + } + + if( $vli->ipv6enabled && $vli->ipv6address ){ + $data['organization_fields']['addresses'] .= $vli->ipv6address->address . "\n"; + } + } + } + + $data['organization_fields']['addresses'] = trim( $data['organization_fields']['addresses'] ); + + return $data; + } + + /** + * Convert a Zendesk organisation object to a IXP Customer entity + * + * @param object $org The Zendesk organisation object + * + * @return Customer $cust The IXP Manager customer entity + */ + private function zendeskObjectToCustomerEntity( $org ): Customer + { + $cust = new Customer(); + $cust->name = $org->name; + $cust->autsys = $org->organization_fields->asn ?? null; + $cust->peeringmacro = $org->organization_fields->as_set ?? null; + $cust->peeringemail = $org->organization_fields->peering_email ?? null; + $cust->nocemail = $org->organization_fields->noc_email ?? null; + $cust->shortname = $org->organization_fields->shortname ?? null; + + if( !( $type = array_search( strtolower( $org->organization_fields->type ?? null ), array_map('strtolower', Customer::$CUST_TYPES_TEXT ) ) ) ) { + throw new GeneralException( 'Unknown customer type' ); + } + $cust->type = $type ; + + if( !( $status = array_search( strtolower( $org->organization_fields->status ?? null ), array_map('strtolower', Customer::$CUST_STATUS_TEXT ) ) ) ) { + throw new GeneralException( 'Unknown customer status' ); + } + $cust->status = $status ; + + // fake has left: + if( isset( $org->organization_fields->has_left ) && $org->organization_fields->has_left ){ + $cust->dateleave = now(); + } + + // store Zendesk's own ID for this organisation + $cust->helpdesk_id = $org->id; + + return $cust; + } + + /** + * Examine customer and Zendesk object and see if Zendesk needs to be updated + * + * @param Customer $cdb The IXP customer entity as known here in the database + * @param Customer $chd The IXP customer entity as known in the helpdesk + * + * @return bool True if these objects are not in sync + */ + #[\Override] + public function organisationNeedsUpdating( Customer $cdb, Customer $chd ): bool + { + try { + return $cdb->name !== $chd->name + || $cdb->autsys !== $chd->autsys + || $cdb->peeringmacro !== $chd->peeringmacro + || $cdb->peeringemail !== $chd->peeringemail + || $cdb->nocemail !== $chd->nocemail + || $cdb->shortname !== $chd->shortname + || $cdb->type !== $chd->type + || $cdb->status !== $chd->status + || $cdb->hasLeft() !== $chd->hasLeft(); + + // FIXME IP addresses + } catch( \IXP\Exceptions\GeneralException $e ) { + // some issue with type / status - means the customer needs updating + return true; + } + } + + /** + * Create organisation + * + * Create an organisation on the helpdesk. Tickets are usually aligned to + * users and they in turn to organisations. + * + * @param Customer $cust An IXP Manager customer to create as organisation + * + * @return Customer|false A decoupled customer entity (including `helpdesk_id`) + * + * @throws \IXP\Services\Helpdesk\ApiException + */ + #[\Override] + public function organisationCreate( Customer $cust ) + { + $response = $this->callApi( function() use ( $cust ) { + return $this->client->organizations()->create( $this->customerEntityToZendeskObject( $cust ) ); + }); + + if( isset( $response->organization ) ) + return $this->zendeskObjectToCustomerEntity( $response->organization ); + + return false; + } + + /** + * Update an organisation **where the helpdesk ID is known!** + * + * Updates an organisation already found via `organisationFind()` as some implementations + * (such as Zendesk's PHP client as of Apr 2015) require knowledge of the helpdesk's ID for + * an organisatoin. + * + * @param int $helpdeskId The ID of the helpdesk's organisation object + * @param Customer $cust An IXP Manager customer as returned by `organisationFind()` + * + * @return Customer|false + * + * @throws + */ + #[\Override] + public function organisationUpdate( int $helpdeskId, Customer $cust ) + { + $response = $this->callApi( function() use ( $cust, $helpdeskId ) { + return $this->client->organizations()->update( null, $this->customerEntityToZendeskObject( $cust, $helpdeskId ) ); + }); + + if( isset( $response->organization ) ) + return $this->zendeskObjectToCustomerEntity( $response->organization ); + + return false; + } + + /** + * Find an organisation by our own customer ID + * + * NB:** the returned entity shouldn't have an ID parameter set - you should already know it! + * + * The reason for this is that the returned customer object is incomplete and is only intended + * to be used to compare local with Zendesk and/or identify if a customer exists. + * + * The returned customer object MUST have a member `helpdesk_id` containing the helpdesk provider's + * ID for this organisation. + * + * @param int $id Our own customer ID to find the organisation from + * + * @return Customer|false + * + * @throws + */ + #[\Override] + public function organisationFind( int $id ) + { + $response = $this->callApi( function() use ( $id ) { + return $this->client->organizations()->search( $id ); + }); + + if( !isset( $response->organizations[0] ) ) + return false; + + return $this->zendeskObjectToCustomerEntity( $response->organizations[0] ); + } + + // ******************************************************************************************** + // ******************************************************************************************** + // ******************************************************************************************** + // + // CONTACTS / USERS + // + // ******************************************************************************************** + // ******************************************************************************************** + // ******************************************************************************************** + + /** + * Convert a IXP Contact entity into an associated array as rerquired by Zendesk's API + * + * @param Contact $contact The IXP Manager customer entity + * @param int $org_id The Zendesk ID of the organisation + * @param false|int $id If updating, set to Zendesk contact ID + * + * @return (int|null|string|true)[] + * + * @psalm-return array{external_id?: int, name: string, email: null|string, phone: null|string, organization_id: int|null, role: 'admin'|'end-user', ticket_restriction?: 'organization'|'requested', verified?: true, locale_id?: 1176, time_zone?: 'Europe/Dublin', id?: int} + */ + private function contactEntityToZendeskObject( Contact $contact, int $org_id = null, int|false $id = false ): array + { + $data = []; + + if( $contact->id ){ + $data['external_id'] = $contact->id; + } + + $data['name'] = $contact->name; + $data['email'] = $contact->email; + $data['phone'] = $contact->mobile; + $data['organization_id'] = $org_id; + + /** FIXME contact table doesn't have user_id anymore */ + if( $contact->getUser() && $contact->getUser()->getPrivs() == User::AUTH_SUPERUSER ){ + $data['role'] = 'admin'; + } else { + $data['role'] = 'end-user'; + + if( $org_id ){ + $data['ticket_restriction'] = 'organization'; + } else { + $data['ticket_restriction'] = 'requested'; + } + } + + if( $id ) { + // updating so set the Zendesk ID: + $data['id'] = $id; + } else { + $data['verified'] = true; + $data['locale_id'] = 1176; // British English + $data['time_zone'] = 'Europe/Dublin'; + } + + return $data; + } + + /** + * Convert a Zendesk user object to a IXP contact entity + * + * @param object $user The Zendesk user object + * + * @return Contact $contact The IXP Manager contact entity + */ + private function zendeskObjectToContactEntity( $user ): Contact + { + $contact = Contact::create([ + 'name' => $user->name, + 'email' => $user->email, + 'mobile' => $user->phone, + ]); + + // store Zendesk's own ID for this organisation + $contact->helpdesk_id = $user->id; + + return $contact; + } + + /** + * Examine contact and Zendesk object and see if Zendesk needs to be updated + * + * @param Contact $cdb The IXP contact entity as known here in the database + * @param Contact $chd The IXP contact entity as known in the helpdesk + * + * @return bool True if these objects are not in sync + */ + #[\Override] + public function contactNeedsUpdating( Contact $cdb, Contact $chd ): bool + { + try { + return $cdb->name !== $chd->name + || $cdb->mobile !== $chd->mobile + || $cdb->email !== $chd->email; + } catch( \IXP\Exceptions\GeneralException $e ) { + // some issue with type / status - means the customer needs updating + return true; + } + } + + /** + * Create user + * + * Create user on the helpdesk. + * + * @param Contact $contact An IXP Manager contact to create + * @param $org_id + * + * @return Contact|false Decoupled contact object with `helpdesk_id` + * + * @throws ApiException + */ + #[\Override] + public function userCreate( Contact $contact, $org_id ) + { + $response = $this->callApi( function() use ( $contact, $org_id ) { + return $this->client->users()->create( $this->contactEntityToZendeskObject( $contact, $org_id ) ); + }); + + if( isset( $response->user ) ) + return $this->zendeskObjectToContactEntity( $response->user ); + + return false; + } + + /** + * Update an user **where the helpdesk ID is known!** + * + * Updates an user already found via `userFind()` as some implementations + * (such as Zendesk's PHP client as of Apr 2015) require knowledge of the helpdesk's ID for + * an user. + * + * @param int $helpdeskId The ID of the helpdesk's user object + * @param Contact $contact An IXP Manager contact as returned by `userFind()` + * + * @throws \IXP\Services\Helpdesk\ApiException + */ + #[\Override] + public function userUpdate( int $helpdeskId, Contact $contact ): Contact|false + { + $response = $this->callApi( function() use ( $contact, $helpdeskId ) { + return $this->client->users()->update( $helpdeskId, $this->contactEntityToZendeskObject( $contact, null, $helpdeskId ) ); + }); + + if( isset( $response->user ) ){ + return $this->zendeskObjectToContactEntity( $response->user ); + } + + return false; + } + + /** + * Find an user by our own contact ID + * + * NB:** the returned entity shouldn't have an ID parameter set - you should already know it! + * + * The reason for this is that the returned contact object is incomplete and is only intended + * to be used to compare local with Zendesk and/or identify if a contact exists. + * + * The returned contact object MUST have a member `helpdesk_id` containing the helpdesk provider's + * ID for this organisation. + * + * @param int $id Our own contact ID to find the contact from + * + * @return Contact|false + * + * @throws + */ + #[\Override] + public function userFind( int $id ) + { + $response = $this->callApi( function() use ( $id ) { + return $this->client->users()->search( [ 'external_id' => $id ] ); + }); + + if( !isset( $response->users[0] ) ) + return false; + + return $this->zendeskObjectToContactEntity( $response->users[0] ); + } +} \ No newline at end of file diff --git a/app/Services/IXF.php b/app/Services/IXF.php new file mode 100644 index 000000000..5ff6ba1d1 --- /dev/null +++ b/app/Services/IXF.php @@ -0,0 +1,191 @@ + + * @category IX-F + * @copyright Copyright (C) 2009 - 2024 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class IXF +{ + + /** @const Cache key for IXs */ + public const CACHE_KEY_IXS = 'api-v4-ixf-ixs'; + + + /** + * @var string|null An error message if the API call to PeeringDB failed + */ + public ?string $error = null; + + /** + * @var int The HTTP response code from the peeringDB api call + */ + public int $status = 0; + + /** + * @var Exception If the api call threw an exception, it is caught and stored here. + */ + public ?Exception $exception = null; + + + + + /** + * Get all PeeringDB IXPs + * + * @param ?array $fields Optional fields to restrict it to. Default is [ ixf_id, name, city, country ] + * @return array + * @throws Exception On Error. + */ + public function ixps( ?array $fields = null ): array + { + if( !$fields ) { + $fields = [ + 'ixf_id' => 'id', + 'name' => 'name', + 'city' => 'city', + 'country' => 'country', + ]; + } + + $cache_key = self::CACHE_KEY_IXS . '-' . implode( ':', $fields ); + + if( Cache::has( $cache_key ) ) { + return Cache::get( $cache_key ); + } + + $ixps = []; + + $ixs = $this->execute( config( 'ixp_api.IXPDB.ixp_api' ) ); + + if( $ixs === null ) { + if( $this->exception ) { + throw $this->exception; + } else { + throw new Exception( 'IXF ixps error' ); + } + } + + foreach( $ixs->json() as $ix ) { + + $row = []; + foreach( $fields as $want => $have ) { + $row[ $want ] = $ix[$have]; + } + $ixps[ $ix['id'] ] = $row; + + } + + Cache::put( $cache_key, $ixps, config( 'ixp_api.peeringDB.api_cache_ttl' ) ); + + return $ixps; + } + + + + /** + * Make the API call to IX-F + * + * Returns the response, sets the $status member and sets an appropriate member $error message. + * + * If null returned, then query failed and exception message in $error member and exception in $exception. + * + * @param string $query The PeeringDB URL for making the request. + * @return HttpResponse|null + */ + private function execute( string $query ): ?HttpResponse + { + $this->reset(); + + // Typically testing Http::fake() belongs in unit test classes but we require it here as + // we are using Laravel Dusk browser tests which make a new http request. + if( app_env_is('testing') ) { + $this->fake($query); + } + + try { + $response = Http::accept( 'application/json' ) + ->get( $query ); + + $this->status = $response->status(); + + switch( $response->status() ) { + + case 200: + return $response; + + default: + $this->error = $response->json()[ 'message' ] ?? 'Error'; + return $response; + } + } catch( Exception $e ) { + $this->error = $e->getMessage(); + $this->exception = $e; + + return null; + } + } + + + /** + * Fake the API calls to PeeringDB for testing + * + * Typically testing Http::fake() belongs in unit test classes but we require it here as + * we are using Laravel Dusk browser tests which make a new http request. + * + * @return void + */ + private function fake(): void + { + Http::fake([ + 'https://api.ixpdb.net/v1/provider/list' => Http::response( file_get_contents( base_path('data/ci/known-good/ix-f/provider.json') ), 200 ), + ]); + } + + + /** + * Reset class members. + * @return void + */ + private function reset(): void + { + $this->error = null; + $this->exception = null; + $this->status = 0; + } + +} \ No newline at end of file diff --git a/app/Services/LookingGlass.php b/app/Services/LookingGlass.php new file mode 100644 index 000000000..335f71259 --- /dev/null +++ b/app/Services/LookingGlass.php @@ -0,0 +1,68 @@ + + * @author Yann Robin + * @category LookingGlass + * @package IXP\Services\LookingGlass + * @copyright Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class LookingGlass +{ + /** + * Get a looking glass implementation for a given router + * + * @param Router $router + * + * @throws + */ + public function forRouter( Router $router ): BirdseyeLookingGlass + { + switch( $router->apiType() ) { + case Router::API_TYPE_BIRDSEYE: + $be = new BirdseyeLookingGlass( $router ); + //Birdseye supports caching but if the user is logged in we want to disable that: + if( Auth::check() ) { + $be->setCacheEnabled(false); + } + return $be; + break; + default: + throw new ConfigurationException( 'Invalid, no or unimplemented looking glass backend requested: ' . $router->apiType() ); + } + } +} \ No newline at end of file diff --git a/app/Services/LookingGlass/BirdsEye.php b/app/Services/LookingGlass/BirdsEye.php new file mode 100644 index 000000000..2d3fe1be8 --- /dev/null +++ b/app/Services/LookingGlass/BirdsEye.php @@ -0,0 +1,268 @@ + Bird's Eye + * + * @author Barry O'Donovan + * @author Yann Robin + * @category IXP + * @package IXP\Services\LookingGlass + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class BirdsEye implements LookingGlassContract +{ + /** + * Instance of a router object representing the looking glass target + * + * @var Router + */ + private $router; + + /** + * Is caching enabled? + * + * @var bool + */ + private $cacheEnabled = true; + + /** + * Constructor + * @param Router $r + */ + public function __construct( Router $r ) + { + $this->setRouter( $r ); + } + + /** + * Enable / disable caching + * + * @param bool $b + */ + public function setCacheEnabled( bool $b ): static + { + $this->cacheEnabled = $b; + return $this; + } + + /** + * Is caching enabled? + * + * @return bool + */ + public function cacheEnabled(): bool + { + return $this->cacheEnabled; + } + + /** + * Set the router object + * + * @param Router $r + * + * @return static For fluent interfaces + */ + #[\Override] + public function setRouter( Router $r ): LookingGlassContract + { + $this->router = $r; + return $this; + } + + /** + * Get the router object + * + * @return Router + */ + #[\Override] + public function router(): Router + { + return $this->router; + } + + /** + * Make the API call + * + * @param string $cmd + * + * @return string + */ + private function apiCall( string $cmd ): string + { + $ret = @file_get_contents( $this->router()->api . '/' . $cmd . ( $this->cacheEnabled ? '' : '?use_cache=0' ) ); + + return $ret ?: ""; + } + + /** + * Get BGP Summary information as JSON + * + * @return string + */ + #[\Override] + public function bgpSummary(): string + { + return $this->apiCall( 'protocols/bgp' ); + } + + /** + * Get BGP neighbour information as JSON + * + * @param string $protocol Protocol name + * @return string + */ + #[\Override] + public function bgpNeighbourSummary( string $protocol ): string + { + return $this->apiCall( 'protocol/' . urlencode( $protocol ) ); + } + + /** + * Get the router's status as JSON + * + * @return string + */ + #[\Override] + public function status(): string + { + return $this->apiCall( 'status' ); + } + + /** + * Get internal symbols. + * + * Particularly we're interested in route tables / vrfs and protocols. + * + * @return string + */ + #[\Override] + public function symbols(): string + { + return $this->apiCall( 'symbols' ); + } + + /** + * Get routes for a named routing table (aka. vrf) + * @param string $table Table name + * @return string + */ + #[\Override] + public function routesForTable( string $table ): string + { + return $this->apiCall( 'routes/table/' . urlencode( $table ) ); + } + + /** + * Get routes learnt from named protocol (e.g. BGP session) + * + * @param string $protocol Protocol name + * + * @return string + */ + #[\Override] + public function routesForProtocol( string $protocol ): string + { + return $this->apiCall( 'routes/protocol/' . urlencode( $protocol ) ); + } + + /** + * Get routes exported to named protocol (e.g. BGP session) + * + * @param string $protocol Protocol name + * + * @return string + */ + #[\Override] + public function routesForExport(string $protocol): string + { + return $this->apiCall( 'routes/export/' . urlencode( $protocol ) ); + } + + /** + * Get details for a specific route as received by a protocol + * + * @param string $protocol Protocol name + * @param string $network The route to lookup + * @param int $mask The mask of the route to look up + * + * @return string + */ + #[\Override] + public function protocolRoute( string $protocol,string $network,int $mask ): string + { + return $this->apiCall( 'route/' . urlencode($network . '/' . $mask ) . '/protocol/' . urlencode( $protocol ) ); + } + + /** + * Get details for a specific route in a named table (vrf) + * + * @param string $table Table name + * @param string $network The route to lookup + * @param int $mask The mask of the route to look up + * + * @return string + */ + #[\Override] + public function protocolTable( string $table,string $network,int $mask ): string + { + return $this->apiCall( 'route/' . urlencode($network . '/' . $mask ) . '/table/' . urlencode( $table ) ); + } + + /** + * Get details for a specific route in a named protocol export + * + * @param string $protocol Protocol name + * @param string $network The route to lookup + * @param int $mask The mask of the route to look up + * + * @return string + */ + #[\Override] + public function exportRoute( string $protocol, string $network, int $mask ): string + { + return $this->apiCall( 'route/' . urlencode($network . '/' . $mask ) . '/export/' . urlencode( $protocol ) ); + } + + /** + * Get wildcard large communities in protocol table of form ( x, y, * ) + * + * @param string $protocol Protocol name + * @param int $x + * @param int $y + * + * @return string + */ + #[\Override] + public function routesProtocolLargeCommunityWildXYRoutes( string $protocol, int $x, int $y ): string + { + return $this->apiCall( 'routes/lc-zwild/protocol/' . urlencode( $protocol ) . '/' . $x . '/' . $y ); + } +} \ No newline at end of file diff --git a/app/Services/PeeringDb.php b/app/Services/PeeringDb.php new file mode 100644 index 000000000..2b1f6d29c --- /dev/null +++ b/app/Services/PeeringDb.php @@ -0,0 +1,350 @@ + + * @author Yann Robin + * @category PeeringDB + * @package IXP\Services\PeeringDb + * @copyright Copyright (C) 2009 - 2024 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class PeeringDb +{ + + /** @const Cache key for IXs */ + public const CACHE_KEY_IXS = 'api-v4-peeringdb-ixs'; + /** @const Cache key for facilities */ + public const CACHE_KEY_FACILITIES = 'api-v4-peeringdb-facilities'; + + + /** + * @var string|null An error message if the API call to PeeringDB failed + */ + public ?string $error = null; + + /** + * @var int The HTTP response code from the peeringDB api call + */ + public int $status = 0; + + /** + * @var ?Exception If the api call threw an exception, it is caught and stored here. + */ + public ?Exception $exception; + + + + + /** + * Get network information by ASN + * + * Returns one of two arrays: + * + * [ 'net' => (json decoded network information directly from PeeringDB) ] + * [ 'error' => (some error message) ] + * + * @param int $asn + * @return array|false Successful or not + */ + public function getNetworkByAsn( int $asn ): array|false + { + $response = $this->execute( + $this->generateBasePeeringDbUrl( "/net.json?asn={$asn}&depth=2" ) + ); + + if( $response->ok() ) { + return $response->json()['data'][0]; + } + + if( $response->notFound() ) { + $this->error = "No network with AS{$asn} found in PeeringDB"; + return false; + } + + $this->error = $response->json()[ 'message' ] ?? 'Error'; + return false; + } + + + + /** + * Get all PeeringDB IXPs + * + * @param ?array $fields Optional fields to restrict it to. Default is [ pdb_id, name, city, country ] + * @return array + * @throws Exception On Error. + */ + public function ixps( ?array $fields = null ): array + { + if( !$fields ) { + $fields = [ + 'pdb_id' => 'id', + 'name' => 'name', + 'city' => 'city', + 'country' => 'country', + ]; + } + + $cache_key = self::CACHE_KEY_IXS . '-' . implode( ':', $fields ); + + if( Cache::has( $cache_key ) ) { + return Cache::get( $cache_key ); + } + + $ixps = []; + + $ixs = $this->execute( + $this->generateBasePeeringDbUrl( '/ix.json' ) + ); + + if( $ixs === null ) { + if( $this->exception ) { + throw $this->exception; + } else { + throw new Exception( 'Failed to retrieve peering db data' ); + } + } + + foreach( $ixs->json()['data'] as $ix ) { + + $row = []; + foreach( $fields as $want => $have ) { + $row[ $want ] = $ix[$have]; + } + $ixps[ $ix['id'] ] = $row; + + } + + Cache::put( $cache_key, $ixps, config( 'ixp_api.peeringDB.api_cache_ttl' ) ); + + return $ixps; + } + + + + + /** + * Get all PeeringDB facilities + * + * @param ?array $fields Optional fields to restrict it to. Default is [ id, name ] + * @return array + * @throws Exception On Error. + */ + public function facilities( ?array $fields = null ): array + { + if( !$fields ) { + $fields = [ + 'id' => 'id', + 'name' => 'name', + 'city' => 'city', + 'country' => 'country', + ]; + } + + $cache_key = self::CACHE_KEY_FACILITIES . '-' . implode( ':', $fields ); + + if( Cache::has( $cache_key ) ) { + return Cache::get( $cache_key ); + } + + $facilities = []; + + $facs = $this->execute( + $this->generateBasePeeringDbUrl( '/fac.json' ) + ); + + if( $facs === null ) { + if( $this->exception ) { + throw $this->exception; + } else { + throw new Exception( 'Failed to retrieve peering db data' ); + } + } + + foreach( $facs->json()['data'] as $fac ) { + + $row = []; + foreach( $fields as $want => $have ) { + $row[ $want ] = $fac[$have]; + } + $facilities[ $fac['id'] ] = $row; + + } + + Cache::put( $cache_key, $facilities, config( 'ixp_api.peeringDB.api_cache_ttl' ) ); + + return $facilities; + } + + + /** + * Takes a response from $this->getNetworkByAsn and formats it into an ASCII table for display in a

+     *
+     * @param array $net
+     * @return string
+     */
+    public function netAsAscii( array $net ): string {
+        return <<reset();
+
+        // Typically testing Http::fake() belongs in unit test classes but we require it here as
+        // we are using Laravel Dusk browser tests which make a new http request.
+        if( app_env_is('testing') ) {
+            $this->fake($query);
+        }
+
+        // api key?
+        if( config( 'ixp_api.peeringDB.api-key' ) ) {
+            $headers = [
+                'Authorization' => 'Api-Key ' . config( 'ixp_api.peeringDB.api-key' ),
+            ];
+        } else {
+            $headers = [];
+            Log::warning( 'PeeringDB has no API key set in .env - see https://docs.peeringdb.com/howto/api_keys/ and set IXP_API_PEERING_DB_API_KEY in .env' );
+        }
+
+        try {
+            $response = Http::withHeaders( $headers )
+                ->accept( 'application/json' )
+                ->get( $query );
+
+            $this->status = $response->status();
+
+            switch( $response->status() ) {
+
+                case 200:
+                    return $response;
+
+                case 429:
+                    $this->error = "Too many requests - PeeringDB throttling applied.";
+                    return $response;
+
+                default:
+                    $this->error = $response->json()[ 'message' ] ?? 'Error';
+                    return $response;
+            }
+        } catch( Exception $e ) {
+            $this->error = $e->getMessage();
+            $this->exception = $e;
+
+            return null;
+        }
+    }
+
+
+    /**
+     * Fake the API calls to PeeringDB for testing
+     *
+     * Typically testing Http::fake() belongs in unit test classes but we require it here as
+     * we are using Laravel Dusk browser tests which make a new http request.
+     *
+     * @return void
+     */
+    private function fake(): void
+    {
+        Http::fake([
+            '*peeringdb.com/api/ix.json*'  => Http::response( file_get_contents( base_path('data/ci/known-good/peeringdb/ix.json') ), 200 ),
+            '*peeringdb.com/api/fac.json*' => Http::response( file_get_contents( base_path('data/ci/known-good/peeringdb/fac.json') ), 200 ),
+        ]);
+    }
+
+
+    /**
+     * Generate the PeeringDB URL for making the request.
+     *
+     * @param string $query API endpoint
+     * @return string Full URL including username/password authentication if appropriate
+     */
+    private function generateBasePeeringDbUrl( string $query = "" ): string
+    {
+        if( config( 'ixp_api.peeringDB.api-key' ) ) {
+            $credentials = '';
+        } else if( ( $un = config( 'ixp_api.peeringDB.username' ) ) === null || ( $pw = config( 'ixp_api.peeringDB.password' ) ) === null ) {
+            Log::warning( 'Neither PeeringDb API Key nor deprecated username / password set in .env. Only public data will be retrieved. Please set an API Key' );
+            $credentials = '';
+        } else {
+            $credentials = urlencode( $un ) . ":" . urlencode( $pw ) . "@";
+        }
+
+        // e.g. https://username:password@www.peeringdb.com/api/ix.json
+        return sprintf( config( 'ixp_api.peeringDB.url' ), $credentials ) . $query;
+    }
+
+
+    /**
+     * Reset class members.
+     * @return void
+     */
+    private function reset(): void
+    {
+        $this->error = null;
+        $this->exception = null;
+        $this->status = 0;
+    }
+
+}
\ No newline at end of file
diff --git a/app/Services/Purifier.php b/app/Services/Purifier.php
new file mode 100644
index 000000000..c2cb24e5c
--- /dev/null
+++ b/app/Services/Purifier.php
@@ -0,0 +1,277 @@
+files = $files;
+        $this->config = $config;
+
+        $this->setUp();
+    }
+
+    /**
+     * Setup
+     *
+     * @throws Exception
+     *
+     * @return void
+     */
+    private function setUp()
+    {
+        if (!$this->config->has('purifier')) {
+            throw new Exception('Configuration parameters not loaded!');
+        }
+
+        $this->checkCacheDirectory();
+
+        // Create a new configuration object
+        $config = HTMLPurifier_Config::createDefault();
+
+        // Allow configuration to be modified
+        if (!$this->config->get('purifier.finalize')) {
+            $config->autoFinalize = false;
+        }
+
+        $config->loadArray($this->getConfig());
+
+        // Load custom definition if set
+        if ($definitionConfig = $this->config->get('purifier.settings.custom_definition')) {
+            $this->addCustomDefinition($definitionConfig, $config);
+        }
+
+        // Load custom elements if set
+        if ($elements = $this->config->get('purifier.settings.custom_elements')) {
+            if ($def = $config->maybeGetRawHTMLDefinition()) {
+                $this->addCustomElements($elements, $def);
+            }
+        }
+
+        // Load custom attributes if set
+        if ($attributes = $this->config->get('purifier.settings.custom_attributes')) {
+            if ($def = $config->maybeGetRawHTMLDefinition()) {
+                $this->addCustomAttributes($attributes, $def);
+            }
+        }
+
+        // Create HTMLPurifier object
+        $this->purifier = new HTMLPurifier($this->configure($config));
+    }
+
+    /**
+     * Add a custom definition
+     *
+     * @see http://htmlpurifier.org/docs/enduser-customize.html
+     * @param array $definitionConfig
+     * @param HTMLPurifier_Config $configObject Defaults to using default config
+     *
+     * @return HTMLPurifier_Config $configObject
+     */
+    private function addCustomDefinition(array $definitionConfig, $configObject = null)
+    {
+        if (!$configObject) {
+            $configObject = HTMLPurifier_Config::createDefault();
+            $configObject->loadArray($this->getConfig());
+        }
+
+        // Setup the custom definition
+        $configObject->set('HTML.DefinitionID', $definitionConfig['id']);
+        $configObject->set('HTML.DefinitionRev', $definitionConfig['rev']);
+
+        // Enable debug mode
+        if (!isset($definitionConfig['debug']) || $definitionConfig['debug']) {
+            $configObject->set('Cache.DefinitionImpl', null);
+        }
+
+        // Start configuring the definition
+        if ($def = $configObject->maybeGetRawHTMLDefinition()) {
+            // Create the definition attributes
+            if (!empty($definitionConfig['attributes'])) {
+                $this->addCustomAttributes($definitionConfig['attributes'], $def);
+            }
+
+            // Create the definition elements
+            if (!empty($definitionConfig['elements'])) {
+                $this->addCustomElements($definitionConfig['elements'], $def);
+            }
+        }
+
+        return $configObject;
+    }
+
+    /**
+     * Add provided attributes to the provided definition
+     *
+     * @param array $attributes
+     * @param HTMLPurifier_HTMLDefinition $definition
+     *
+     * @return HTMLPurifier_HTMLDefinition $definition
+     */
+    private function addCustomAttributes(array $attributes, $definition)
+    {
+        foreach ($attributes as $attribute) {
+            // Get configuration of attribute
+            $required = !empty($attribute[3]) ? true : false;
+            $onElement = $attribute[0];
+            $attrName = $required ? $attribute[1] . '*' : $attribute[1];
+            $validValues = $attribute[2];
+
+            $definition->addAttribute($onElement, $attrName, $validValues);
+        }
+
+        return $definition;
+    }
+
+    /**
+     * Add provided elements to the provided definition
+     *
+     * @param array $elements
+     * @param HTMLPurifier_HTMLDefinition $definition
+     *
+     * @return void $definition
+     */
+    private function addCustomElements(array $elements, $definition): void
+    {
+        foreach ($elements as $element) {
+            // Get configuration of element
+            $name = $element[0];
+            $contentSet = $element[1];
+            $allowedChildren = $element[2];
+            $attributeCollection = $element[3];
+            $attributes = isset($element[4]) ? $element[4] : null;
+
+            if (!empty($attributes)) {
+                $definition->addElement($name, $contentSet, $allowedChildren, $attributeCollection, $attributes);
+            } else {
+                $definition->addElement($name, $contentSet, $allowedChildren, $attributeCollection);
+            }
+        }
+    }
+
+    /**
+     * Check/Create cache directory
+     */
+    private function checkCacheDirectory(): void
+    {
+        $cachePath = $this->config->get('purifier.cachePath');
+
+        if ($cachePath) {
+            if (!$this->files->isDirectory($cachePath)) {
+                $this->files->makeDirectory($cachePath, $this->config->get('purifier.cacheFileMode', 0755));
+            }
+        }
+    }
+
+    /**
+     * @param HTMLPurifier_Config $config
+     *
+     * @return HTMLPurifier_Config
+     */
+    protected function configure(HTMLPurifier_Config $config)
+    {
+        return HTMLPurifier_Config::inherit($config);
+    }
+
+    /**
+     * @param null $config
+     *
+     * @return mixed|null
+     */
+    protected function getConfig($config = null)
+    {
+        $default_config = [];
+        $default_config['Core.Encoding'] = $this->config->get('purifier.encoding');
+        $default_config['Cache.SerializerPath'] = $this->config->get('purifier.cachePath');
+        $default_config['Cache.SerializerPermissions'] = $this->config->get('purifier.cacheFileMode', 0755);
+
+        if (!$config) {
+            $config = $this->config->get('purifier.settings.default');
+        } elseif (is_string($config)) {
+            $config = $this->config->get('purifier.settings.'.$config);
+        }
+
+        if (!is_array($config)) {
+            $config = [];
+        }
+
+        $config = $default_config + $config;
+
+        return $config;
+    }
+
+    /**
+     * @param      $dirty
+     * @param null $config
+     *
+     * @return mixed
+     */
+    public function clean($dirty, $config = null)
+    {
+        if (is_array($dirty)) {
+            return array_map(function ($item) use ($config) {
+                return $this->clean($item, $config);
+            }, $dirty);
+        }
+
+        return $this->purifier->purify($dirty, $this->getConfig($config));
+    }
+
+    /**
+     * Get HTMLPurifier instance.
+     *
+     * @return \HTMLPurifier
+     */
+    public function getInstance()
+    {
+        return $this->purifier;
+    }
+}
diff --git a/app/Services/RipeAtlas/ApiCall.php b/app/Services/RipeAtlas/ApiCall.php
new file mode 100644
index 000000000..b217205a1
--- /dev/null
+++ b/app/Services/RipeAtlas/ApiCall.php
@@ -0,0 +1,234 @@
+
+ * @author     Yann Robin       
+ * @category   IXP
+ * @package    IXP\Services\RipeAtlas
+ * @copyright  Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee
+ * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
+ */
+class ApiCall
+{
+    /**
+     * Call the Ripe Atlas Api and return the Probes list for the protocol and Asn
+     *
+     * @param $customer
+     * @param $protocol
+     *
+     * @return (bool|mixed|string)[]
+     *
+     * @throws
+     *
+     * @psalm-return array{error: bool, response: mixed|string}
+     */
+    public function queryAtlasForProbes( $customer, $protocol ): array
+    {
+        $client = new GuzzleHttp();
+        $asn    = $customer->autsys;
+        $name   = $customer->name;
+
+        try {
+            $req = $client->request( 'GET', "https://atlas.ripe.net/api/v2/probes/?asn_v{$protocol}={$asn}&is_public=true&status=1" );
+
+            if( $req->getStatusCode() === 200 ) {
+                return [ 'error' => false, 'response' => json_decode( $req->getBody()->getContents(), false, 512,
+                    JSON_THROW_ON_ERROR)
+                ];
+            }
+
+            if( $req->getStatusCode() === 404 ) {
+                return [ 'error' => true, 'response' => "Probe Atlas API request not found for {$name}/ASN{$asn}" ];
+            }
+        } catch (RequestException $e) {
+            if( $e->hasResponse() ) {
+                return [ 'error' => true, 'response' => json_decode((string) $e->getResponse()->getBody(), true, 512,
+                    JSON_THROW_ON_ERROR)
+                ];
+            }
+            return [ 'error' => true, 'response' => $e->getMessage() ];
+        }
+        return [ 'error' => true, 'response' => "Probe Atlas API request failed for {$name}/ASN{$asn}" ];
+    }
+
+    /**
+     * Call the Ripe Atlas Api and return the Probes list for the protocol and Asn
+     *
+     * @param $fromASN
+     * @param $target
+     * @param $protocol
+     *
+     * @return int|null
+     *
+     * @throws
+     */
+    public function requestAtlasTraceroute( $fromASN, $target, $protocol ): ?int
+    {
+        $query = [
+            'definitions' => [ [
+                'target'      => $target,
+                'description' => 'IXP Asymmentric routing detector',
+                'type'        => 'traceroute',
+                'af'          => $protocol,
+                'protocol'    => 'ICMP',
+                'is_oneoff'   => true,
+                'is_public'   => true
+            ] ],
+            'probes' => [ [
+                'requested' => 1,
+                'type'      => 'asn',
+                'value'     => $fromASN
+            ] ]
+        ];
+
+        // use key 'http' even if you send the request to https://...
+        $options = [
+            'http' => [
+                'header'  => "Content-Type: application/json",
+                'method'  => 'POST',
+                'content' => json_encode( $query, JSON_THROW_ON_ERROR )
+            ]
+        ];
+
+        $context = stream_context_create( $options );
+
+        try {
+            $result = file_get_contents('https://atlas.ripe.net/api/v2/measurements/?key=' . config( "ixp_api.atlas_measurement_key" ) , false, $context);
+            $response = json_decode( $result, false, 512, JSON_THROW_ON_ERROR);
+            return $response->measurements[0];
+        } catch( \Exception $e ) {
+            /*if( $this->isVerbose() ) {
+                $this->error( "  - FAILED: " . json_encode( $query ) );
+            }*/
+        }
+
+        return null;
+    }
+
+    /**
+     * Call the Ripe Atlas measurement Api and return the measurement info
+     *
+     * @param int $atlasId
+     *
+     * @return (bool|mixed|string)[]
+     *
+     * @throws
+     *
+     * @psalm-return array{error: bool, response: mixed|string}
+     */
+    public function updateAtlasMeasurement( int $atlasId ): array
+    {
+        $apiUrl = "https://atlas.ripe.net/api/v2/measurements/" . $atlasId;
+
+        //        if( $this->isVerbose() ) {
+        //            $this->info( 'Checking result for measurement ' . $m->$getAtlasIdFn() . ' [' . $apiUrl . ']'  );
+        //        }
+
+        $client = new GuzzleHttp();
+
+        try {
+            $req = $client->request( 'GET', $apiUrl );
+
+            if( $req->getStatusCode() === 200 ) {
+                return [ 'error' => false, 'response' => json_decode($req->getBody()->getContents(), false, 512,
+                    JSON_THROW_ON_ERROR)
+                ];
+            }
+
+        } catch (RequestException $e) {
+            if( $e->hasResponse() ) {
+                return [ 'error' => true, 'response' => json_decode((string) $e->getResponse()->getBody(), false, 512,
+                    JSON_THROW_ON_ERROR)
+                ];
+            }
+            return [ 'error' => true, 'response' => $e->getMessage() ];
+        }
+        return [ 'error' => true, 'response' => "Update Atlas measurement API request failed for {$atlasId}" ];
+    }
+
+    /**
+     * Get the Atlas measurements from the API
+     *
+     * @return (bool|mixed|string)[]
+     *
+     * @throws
+     *
+     * @psalm-return array{error: bool, response: mixed|string}
+     */
+    public function myAtlasMeasurements(): array
+    {
+        $client = new GuzzleHttp();
+        try {
+            // Get measurements with Status "Ongoing" (2)
+            $req = $client->request( 'GET', 'https://atlas.ripe.net/api/v2/measurements/my/?key=' . config('ixp_api.atlas_measurement_key') . '&status=2&page_size=500' );
+            if( $req->getStatusCode() === 200 ) {
+                return [ 'error' => false, 'response' => json_decode( $req->getBody()->getContents(), false, 512,
+                    JSON_THROW_ON_ERROR)
+                ];
+            }
+
+            if( $req->getStatusCode() === 404 ) {
+                return [ 'error' => true, 'response' => "Ripe Atlas API request not found" ];
+            }
+        } catch (RequestException $e) {
+            if( $e->hasResponse() ) {
+                return [ 'error' => true, 'response' => json_decode((string) $e->getResponse()->getBody(), false, 512,
+                    JSON_THROW_ON_ERROR)
+                ];
+            }
+            return [ 'error' => true, 'response' => $e->getMessage() ];
+        }
+        return [ 'error' => true, 'response' => "Ripe Atlas API request failed" ];
+    }
+
+
+    /**
+     * Stop a measurement
+     *
+     * @param  int  $atlasId
+     *
+     * @return void
+     *
+     */
+    public function atlasStopMeasurement( int $atlasId ): void
+    {
+        $ch = curl_init();
+        curl_setopt( $ch, CURLOPT_URL, "https://atlas.ripe.net/api/v2/measurements/" . $atlasId . "/?key=" . config('ixp_api.atlas_measurement_key' ) );
+        curl_setopt( $ch, CURLOPT_CUSTOMREQUEST, "DELETE" );
+        curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
+        $result = curl_exec( $ch );
+        curl_close( $ch );
+        Log::alert( $result );
+    }
+}
\ No newline at end of file
diff --git a/app/Services/RipeAtlas/Interpretor.php b/app/Services/RipeAtlas/Interpretor.php
new file mode 100644
index 000000000..6542f1e4c
--- /dev/null
+++ b/app/Services/RipeAtlas/Interpretor.php
@@ -0,0 +1,163 @@
+
+ * @author     Yann Robin       
+ * @category   IXP
+ * @package    IXP\Services\RipeAtlas
+ * @copyright  Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee
+ * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
+ */
+class Interpretor
+{
+    /**
+     * Basic Interpretor
+     * FIXME?: Has to be adapted for ripe atlas
+     *
+     * @param  AtlasMeasurement  $atlasMeasurement
+     *
+     * @return AtlasResult
+     *
+     * @throws
+     */
+    public function interpret( AtlasMeasurement $atlasMeasurement ): AtlasResult
+    {
+        $atlasRun = $atlasMeasurement->atlasRun;
+
+        // what is the source network's peering addresses?
+        $srcAddrs = Customer::addressesForVlan( $atlasRun->vlan_id, $atlasMeasurement->cust_source, $atlasRun->protocol );
+        $dstAddrs = Customer::addressesForVlan( $atlasRun->vlan_id, $atlasMeasurement->cust_dest,  $atlasRun->protocol );
+
+        // traceroute from different routers can use the ingress or egress path so we merge these:
+        $allLanAddrs = array_merge( $dstAddrs->toArray(), $srcAddrs->toArray() );
+
+        $atlas_data = json_decode( $atlasMeasurement->atlas_data, false, 512, JSON_THROW_ON_ERROR);
+
+        $path = $this->parsePath( $atlas_data );
+
+        $viaLan = $this->queryPassesThrough( $path, $allLanAddrs );
+
+        //dd( $viaLan, $path);
+
+        $ar = new AtlasResult;
+
+        if( $viaLan ) {
+            $ar->routing = 'IXP_LAN_SYM';
+        } else {
+            //$viaIx = $this->queryPassesThrough( $path, $allIxpAddrs );
+            //
+            //            if( ( $viaIxpOut && $viaIxpIn ) || ( $viaIxpOut && $viaLanIn ) || ( $viaLanOut && $viaIxpIn ) ) {
+            //                $ar->setAttribute( 'routing', 'IXP_SYM' );
+            //            } else if( !$viaIxpOut && $viaIxpIn ) {
+            //                $ar->setAttribute( 'routing', 'IXP_ASYM_OUT' );
+            //            } else if( $viaIxpOut && !$viaIxpIn ) {
+            //                $ar->setAttribute( 'routing', 'IXP_ASYM_IN' );
+            //            } else {
+            //                $ar->setAttribute( 'routing', 'NON_IXP' );
+            //            }
+        }
+
+        $ar->path = serialize( $path );
+
+        $ar->save();
+
+        return $ar;
+    }
+
+    /**
+     * Take a RIPE Atles traceroute result and extract the path
+     *
+     * NB: FIXME?: Assumes no ECMP... takes only one IP per hop.
+     *
+     * @param array $tracert Raw RIPE Atles JSON result as PHP
+     *
+     * @return (mixed|string)[][][]
+     *
+     * @psalm-return array{hops: list}>, ixpx: array}
+     */
+    private function parsePath( array $tracert ): array
+    {
+        $path = [
+            'hops' => [],
+            'ixpx' => [],  // point of intersection with IXP
+        ];
+
+        foreach( $tracert[0]->result as $hop ) {
+            // three iterations means each hop has three results:
+            $results = [];
+            foreach( $hop->result as $result ) {
+                if( !isset( $result->from ) ) {
+                    continue;
+                }
+
+                if( in_array($result->from, $results, true ) ) {
+                    continue;
+                }
+
+                $results[] = $result->from;
+            }
+
+            if( count($results) ) {
+                $path['hops'][] = $results;
+            } else {
+                $path['hops'][] = [ '*' ];
+            }
+        }
+
+        return $path;
+    }
+
+    /**
+     * For a given path of IP addresses, see if another list of addresses appears in the path
+     *
+     * @param array $path Path of IP addresses
+     * @param array $addrs List of addresses to find in $path
+     *
+     * @psalm-return int<0, max>
+     */
+    private function queryPassesThrough( array &$path, array $addrs ): int
+    {
+
+        foreach( $path[ 'hops' ] as $ipset ) {
+            foreach( $ipset as $ip ) {
+                if( in_array( $ip, $addrs ) ) {
+                    $path['ixpx'][] = $ip;
+                }
+            }
+        }
+
+        return count( $path[ 'ixpx' ] );
+    }
+}
\ No newline at end of file
diff --git a/app/Support/Facades/Grapher.php b/app/Support/Facades/Grapher.php
new file mode 100755
index 000000000..367f4aaf1
--- /dev/null
+++ b/app/Support/Facades/Grapher.php
@@ -0,0 +1,55 @@
+
+ * @author     Yann Robin       
+ * @category   IXP
+ * @package    IXP\Support\Facedes
+ * @see        IXP\Contracts\Grapher
+ * @copyright  Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee
+ * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
+ */
+class Grapher extends Facade
+{
+    /**
+     * Get the registered name of the component.
+     *
+     * @return string
+     *
+     * @psalm-return GrapherService::class
+     */
+    #[\Override]
+    protected static function getFacadeAccessor(): string
+    {
+        return GrapherService::class;
+    }
+}
diff --git a/app/Support/Facades/Purifier.php b/app/Support/Facades/Purifier.php
new file mode 100644
index 000000000..991b67845
--- /dev/null
+++ b/app/Support/Facades/Purifier.php
@@ -0,0 +1,31 @@
+
+ * @author     Yann Robin 
+ * @copyright  Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee
+ * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
+ */
+
+if( !function_exists( 'resolve_dns_a' ) )
+{
+    /**
+     * Do a DNS A record lookup on a hostname
+     *
+     * @param string $hostname
+     *
+     * @return string|null The IP address or null
+     */
+    function resolve_dns_a( string $hostname ): ?string
+    {
+        $a = dns_get_record( $hostname, DNS_A );
+
+        if( empty( $a ) ){
+            return null;
+        }
+        return $a[0]['ip'];
+    }
+}
+
+if( !function_exists( 'resolve_dns_aaaa' ) )
+{
+    /**
+     * Do a DNS AAAA record lookup on a hostname
+     *
+     * @param string $hostname
+     *
+     * @return string|null The IP address or null
+     */
+    function resolve_dns_aaaa( string $hostname ): ?string
+    {
+        $a = dns_get_record( $hostname, DNS_AAAA );
+
+        if( empty( $a ) ){
+            return null;
+        }
+        return $a[0]['ipv6'];
+    }
+}
+
+if( !function_exists( 'ixp_min_auth' ) )
+{
+    /**
+     * Check is a logged/public user meets the minimum authentication level provided
+     *
+     * @param int $minAuth
+     *
+     * @return bool
+     */
+    function ixp_min_auth( int $minAuth ): bool
+    {
+        if( Auth::check() ) {
+            /** @var IXP\Models\User $us */
+            $us = Auth::getUser();
+
+            return $us->privs() >= $minAuth;
+        }
+        return $minAuth === 0;
+    }
+}
+
+if( !function_exists( 'ixp_get_client_ip' ) )
+{
+    /**
+     * Try to get the clients real IP address even when behind a proxy.
+     *
+     * Source: https://stackoverflow.com/questions/33268683/how-to-get-client-ip-address-in-laravel-5/41769505#41769505
+     *
+     * @return string|null
+     */
+    function ixp_get_client_ip(): null|string
+    {
+        // look for public:
+        foreach( [ 'HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR' ] as $key ) {
+            if( array_key_exists( $key, $_SERVER ) === true ) {
+                $value = (string)$_SERVER[ $key ];
+                foreach( explode(',', $value ) as $ip ) {
+                    $ip = trim( $ip );
+                    if( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ) !== false ) {
+                        return $ip;
+                    }
+                }
+            }
+        }
+
+        // accept private:
+        foreach( [ 'HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR' ] as $key ) {
+            if( array_key_exists( $key, $_SERVER ) === true ) {
+                $value = (string)$_SERVER[ $key ];
+                foreach( explode(',', $value ) as $ip ) {
+                    $ip = trim( $ip );
+                    if( filter_var( $ip, FILTER_VALIDATE_IP ) !== false ) {
+                        return $ip;
+                    }
+                }
+            }
+        }
+
+        if( request() && request()->getClientIp() ) {
+            return request()->getClientIp();
+        }
+        return '';
+    }
+}
+
+// Many development environments do not have the php_rrd extension and this
+// simple allows the dev system to return a blank graph.
+if( !function_exists( 'rrd_graph' ) )
+{
+    /**
+     * @psalm-return array
+     */
+    function rrd_graph( $a, $b ): array { return []; }
+}
+
+
+/**
+ *  Originally copied as is from https://github.com/parsedown/laravel
+ *  on 21 Apr 2024 as that repo has switched to read-only and was
+ *  marked as no longer supported. MIT licensed.
+ *
+ * @param ?string $value
+ * @param ?bool $inline
+ * @return Parsedown|string
+ */
+function parsedown(?string $value = null, ?bool $inline = null): Parsedown|string
+{
+    /**
+     * @var Parsedown $parser
+     */
+    $parser = app('parsedown');
+
+    if (!func_num_args()) {
+        return $parser;
+    }
+
+    if (is_null($inline)) {
+        $inline = config('parsedown.inline');
+    }
+
+    if ($inline) {
+        return $parser->line($value);
+    }
+
+    return $parser->text($value);
+}
+
+if( !function_exists( 'app_env_is' ) )
+{
+    function app_env_is( string $env ): bool
+    {
+        return config('app.env') === $env;
+    }
+}
diff --git a/app/Support/parsedown.php b/app/Support/parsedown.php
new file mode 100644
index 000000000..66f080988
--- /dev/null
+++ b/app/Support/parsedown.php
@@ -0,0 +1,45 @@
+` tags.
+     *
+     * @see https://github.com/erusev/parsedown/wiki/Usage
+     */
+    'breaks_enabled' => false,
+
+    /**
+     *  Tells the `parsedown()` helper and the `@parsedown` **Blade** directive if the user input should be inline parsed by default.
+     *
+     * @see https://github.com/erusev/parsedown/wiki/Usage
+     */
+    'inline' => false,
+
+    /**
+     * Tells **Parsedown** if it should escape **HTML** in trusted input. This method isn't safe from XSS!
+     *
+     * @see https://github.com/erusev/parsedown#escaping-html
+     */
+    'markup_escaped' => false,
+
+    /**
+     * Tells **Parsedown** if it needs to process untrusted user-input.
+     *
+     * @see https://github.com/erusev/parsedown#security
+     */
+    'safe_mode' => true,
+
+    /**
+     * Tells **Parsedown** if it should automatically convert urls into anchor tags.
+     *
+     * @see https://github.com/erusev/parsedown/wiki/Usage
+     */
+    'urls_linked' => true,
+];
diff --git a/app/Tasks/Irrdb/UpdateAsnDb.php b/app/Tasks/Irrdb/UpdateAsnDb.php
new file mode 100644
index 000000000..2a667cd21
--- /dev/null
+++ b/app/Tasks/Irrdb/UpdateAsnDb.php
@@ -0,0 +1,105 @@
+
+ * @category   Tasks
+ * @package    IXP\Tasks\Irrdb
+ * @copyright  Copyright (C) 2009 - 2019 Internet Neutral Exchange Association Company Limited By Guarantee
+ * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
+ */
+class UpdateAsnDb extends UpdateDb
+{
+    /**
+     * Update the prefix database
+     *
+     * @return array
+     *
+      * @throws
+     */
+    public function update(): array
+    {
+        foreach( $this->protocols() as $protocol ) {
+            if( $this->customer()->irrdbConfig && $this->customer()->routeServerClient( $protocol ) && $this->customer()->irrdbFiltered() ) {
+                $this->bgpq3()->setWhois( $this->customer()->irrdbConfig->host );
+                $this->bgpq3()->setSources( $this->customer()->irrdbConfig->source );
+
+                $this->startTimer();
+                $asns = $this->bgpq3()->getAsnList( $this->customer()->asMacro( $protocol, 'as' ), $protocol );
+                $this->result[ 'netTime' ] += $this->timeElapsed();
+
+                $this->result[ 'v' . $protocol ][ 'count' ] = count( $asns );
+
+                if( $this->updateDb( $asns, $protocol, 'asn' ) ) {
+                    $this->result[ 'v' . $protocol ][ 'dbUpdated' ] = true;
+                }
+            } else {
+                // This customer is not appropriate for IRRDB filtering.
+                // Delete any pre-existing entries just in case this has changed recently:
+                $this->startTimer();
+
+                Cache::store()->forget( 'irrdb:asns:ipv' . $protocol . ':' . $this->customer()->asMacro( $protocol ) );
+
+                IrrdbAsn::whereCustomerId( $this->customer()->id )
+                    ->whereProtocol( $protocol )->delete();
+
+                $this->result[ 'dbTime' ] += $this->timeElapsed();
+                $this->result[ 'v' . $protocol ][ 'dbUpdated' ] = true;
+                $this->result[ 'msg' ] = "{$this->customer()->name} not a RS client or IRRDB filtered for IPv{$protocol}. IPv{$protocol} ASNs, if any, wiped from database.";
+            }
+        }
+
+        return $this->result;
+    }
+
+
+    /**
+     * Validate a given array of CIDR formatted prefixes for the given protocol and
+     * remove (and alert on) any elements failing validation.
+     *
+     * @param array $entries ASNs from IRRDB
+     * @param int $protocol Either 4/6
+     *
+     * @return array Valid ASNs
+     */
+    #[\Override]
+    protected function validate( array $entries, int $protocol ) : array
+    {
+        foreach( $entries as $key => $value ) {
+            if( !is_numeric( $value ) || $value <= 0 || $value > 4294967294 ) {
+                unset( $entries[ $key ] );
+                Log::alert( 'IRRDB CLI action - removing invalid ASN ' . $value . ' from IRRDB result set!' );
+            }
+        }
+        return $entries;
+    }
+}
diff --git a/app/Tasks/Irrdb/UpdateDb.php b/app/Tasks/Irrdb/UpdateDb.php
new file mode 100644
index 000000000..afd3003c9
--- /dev/null
+++ b/app/Tasks/Irrdb/UpdateDb.php
@@ -0,0 +1,342 @@
+
+ * @author     Yann Robin      
+ * @category   Tasks
+ * @package    IXP\Tasks\Irrdb
+ * @copyright  Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee
+ * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
+ */
+abstract class UpdateDb
+{
+    /**
+     * BGPQ3 Utility Interface details object.
+     *
+     * @var Bgpq3
+     */
+    private $bgpq3 = null;
+
+    /**
+     * Customer to update prefixes of
+     *
+     * @var Customer
+     */
+    private $customer = null;
+
+    /**
+     * Protocols to update
+     *
+     * @var array
+     */
+    private $protocols = [ 4,6 ];
+
+    /**
+     * Variable for timing
+     */
+    private $time     = 0.0;
+
+    /**
+     * Standard result array
+     *
+     * @var array
+     */
+    protected $result = [
+        'wiped'     => false,
+        'v4'        => [
+            'count'     => 0,
+            'stale'     => [],
+            'new'       => [],
+            'dbUpdated' => false,
+        ],
+        'v6'        => [
+            'count'     => 0,
+            'stale'     => [],
+            'new'       => [],
+            'dbUpdated' => false,
+        ],
+        'netTime'   => 0.0,
+        'dbTime'    => 0.0,
+        'procTime'  => 0.0,
+        'msg'       => null,
+    ];
+
+    /**
+     * UpdatePrefixDb constructor.
+     *
+     * @param Customer      $c
+     * @param array|null    $protocols
+     *
+     * @throws ConfigurationException
+     */
+    public function __construct( Customer $c, ?array $protocols = null ) {
+        $this->setCustomer( $c );
+
+        if( $protocols !== null ) {
+            $this->protocols = $protocols;
+        }
+
+        $this->setBgpq3( new Bgpq3( config( 'ixp.irrdb.bgpq3.path' ) ) );
+    }
+
+    /**
+     * Set the customer member
+     *
+     * @param Customer $customer
+     */
+    public function setCustomer( Customer $customer ): static
+    {
+        $this->customer = $customer;
+        return $this;
+    }
+
+    /**
+     * Get the customer
+     *
+     * @return Customer
+     */
+    public function customer(): Customer
+    {
+        return $this->customer;
+    }
+
+    /**
+     * Get the protocols to update
+     *
+     * @return array
+     */
+    public function protocols(): array
+    {
+        return $this->protocols;
+    }
+
+    /**
+     * Set the Bgpq3 utility
+     *
+     * @param Bgpq3 $bgpq3
+     *
+     * @throws
+     */
+    public function setBgpq3( Bgpq3 $bgpq3 ): static
+    {
+        $this->bgpq3 = $bgpq3;
+        return $this;
+    }
+
+    /**
+     * Get the Bgpq3 utility
+     *
+     * @return Bgpq3
+     */
+    public function bgpq3(): Bgpq3
+    {
+        return $this->bgpq3;
+    }
+
+    /**
+     * Start a timer
+     * @return $this
+     */
+    protected function startTimer(): self
+    {
+        $this->time = microtime(true);
+        return $this;
+    }
+
+    /**
+     * Return time since timer started
+     * @return float
+     */
+    protected function timeElapsed(): float
+    {
+        return microtime(true) - $this->time;
+    }
+
+    /**
+     * Update the database IrrdbAsn table with the member's ASNs for a given protocol.
+     *
+     * This is transaction safe and works as follows ensuring the member's ASNs are available
+     * to any script requiring them at any time.
+     *
+     * @param array     $fromIrrdb
+     * @param int       $protocol   The protocol to use (4 or 6)
+     * @param string    $type
+     *
+     * @return bool
+     *
+     * @throws
+     *
+     * @psalm-param 'asn'|'prefix' $type
+     */
+    protected function updateDb( array $fromIrrdb, int $protocol, string $type = 'prefix' ): bool
+    {
+        switch( $type ) {
+            case 'asn':
+                $model      = IrrdbAsn::class; /** @var IrrdbAsn $model  */
+                break;
+            case 'prefix':
+                $model      = IrrdbPrefix::class; /** @var IrrdbPrefix $model  */
+                break;
+            default:
+                throw new GeneralException( 'Unknown type for updating: ' . $type );
+        }
+
+        $this->startTimer();
+        $fromDb = IrrdbAggregator::forCustomerAndProtocol( $this->customer()->id, $protocol, $type );
+        $this->result['dbTime'] += $this->timeElapsed();
+
+        // The calling function and the Bgpq3 class does a lot of validation and error
+        // checking. But the last thing we need to do is start filtering all prefixes/ASNs if
+        // something falls through to here. So, as a basic check, make sure we do not accept
+        // an empty array of prefixes/ASNs for a customer that has a lot.
+
+        if( count( $fromIrrdb ) === 0 ) {
+            // make sure the customer doesn't have a non-empty prefix/ASN set that we're about to delete
+            if( count( $fromDb ) !== 0 ) {
+                $msg = "IRRDB {$type}: {$this->customer()->name} has a non-zero {$type} count for IPv{$protocol} in the database but "
+                    . "BGPQ3 returned none. Please examine manually. No databases changes made for this customer.";
+                Log::alert( $msg );
+                $result['msg'] = $msg;
+            }
+
+            // in either case, we have nothing to do with an empty ASN list:
+            return false;
+        }
+
+        $this->startTimer();
+
+        $fromIrrdbSet = new \Ds\Set( $fromIrrdb );
+
+        foreach( $fromDb as $i => $p ) {
+            if( $fromIrrdbSet->contains( $p[ $type ] ) ) {
+                // ASN/prefix exists in both db and IRRDB - no action required
+                unset( $fromDb[ $i ] );
+                $fromIrrdbSet->remove( $p[$type] );
+            }
+        }
+
+        $fromIrrdb = $fromIrrdbSet->toArray();
+
+        // at this stage, the arrays are now:
+        // $fromDb      => asns/prefixes in the database that need to be deleted
+        // $fromIrrdb   => new asns/prefixes that need to be added
+
+        $this->result[ 'v'.$protocol ][ 'stale' ] = $fromDb;
+        $this->result[ 'v'.$protocol ][ 'new' ]   = $fromIrrdb;
+
+        // validate any remaining IRRDB prefixes/ASNs before we put them near the database
+        $fromIrrdb = $this->validate( $fromIrrdb, $protocol );
+
+        $this->result['procTime'] += $this->timeElapsed();
+
+        $this->startTimer();
+
+        DB::beginTransaction();
+
+        try {
+            $now = now()->format( 'Y-m-d H:i:s' );
+
+            foreach( $fromIrrdb as $p ) {
+                Log::debug( "INSERT [{$type}]: {$this->customer()->shortname} IPv{$protocol} {$p}" );
+                $model::create(
+                    [
+                        'customer_id'   => $this->customer()->id,
+                        $type           => $p,
+                        'protocol'      => $protocol,
+                        'last_seen'     => $now,
+                        'first_seen'    => $now,
+                    ]
+                );
+            }
+
+            foreach( $fromDb as $i => $p ) {
+                Log::debug( "DELETE [{$type}]: {$this->customer()->shortname} IPv{$protocol} ID:{$p['id']} {$p[$type]}" );
+                $model::where( 'id', $p['id'] )->delete();
+            }
+
+            $model::where( 'customer_id', $this->customer()->id )
+                ->where( 'protocol', $protocol )
+                ->update( [ 'last_seen' => $now ] );
+
+            DB::commit();
+            $this->result['dbTime'] += $this->timeElapsed();
+
+            // Store the prefixes to cache to speed up route server configuration generation.
+            if( $type === 'asn' ) {
+                IrrdbAggregator::asnsForRouterConfiguration( $this->customer(), $protocol, true );
+            } else {
+                IrrdbAggregator::prefixesForRouterConfiguration( $this->customer(), $protocol, true );
+            }
+
+        } catch( Exception $e ) {
+            DB::rollBack();
+            $this->result['dbTime'] += $this->timeElapsed();
+            throw $e;
+        }
+
+        $this->logUpdate( $protocol, $type );
+
+        return true;
+    }
+
+    /**
+     * Update the database to record that a IRRDB update completed successfully.
+     *
+     * @param int       $protocol   The protocol to use (4 or 6)
+     * @param string    $type
+     */
+    protected function logUpdate( int $protocol, string $type ): void
+    {
+        IrrdbUpdateLog::updateOrCreate(
+            [ 'cust_id' => $this->customer()->id ],
+            [ "{$type}_v{$protocol}" => now() ]
+        );
+    }
+
+    /**
+     * Validate ASNs/prefixes. Implement in subclasses.
+     *
+     * @param array $entries
+     * @param int $protocol
+     *
+     * @return array
+     */
+    abstract protected function validate( array $entries, int $protocol ): array;
+}
\ No newline at end of file
diff --git a/app/Tasks/Irrdb/UpdatePrefixDb.php b/app/Tasks/Irrdb/UpdatePrefixDb.php
new file mode 100644
index 000000000..39fa948f6
--- /dev/null
+++ b/app/Tasks/Irrdb/UpdatePrefixDb.php
@@ -0,0 +1,118 @@
+
+ * @category   Tasks
+ * @package    IXP\Tasks\Irrdb
+ * @copyright  Copyright (C) 2009 - 2019 Internet Neutral Exchange Association Company Limited By Guarantee
+ * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
+ */
+class UpdatePrefixDb extends UpdateDb
+{
+    /**
+     * Update the prefix database
+     *
+     * @return array
+     *
+     * @throws
+     */
+    public function update(): array
+    {
+        foreach( $this->protocols() as $protocol ) {
+            if( $this->customer()->irrdbConfig && $this->customer()->routeServerClient( $protocol ) && $this->customer()->irrdbFiltered() ) {
+                $this->bgpq3()->setWhois( $this->customer()->irrdbConfig->host );
+                $this->bgpq3()->setSources( $this->customer()->irrdbConfig->source );
+
+                $this->startTimer();
+                $prefixes = $this->bgpq3()->getPrefixList( $this->customer()->asMacro( $protocol, 'as' ), $protocol );
+                $this->result[ 'netTime' ] += $this->timeElapsed();
+
+                $this->result[ 'v' . $protocol ][ 'count' ] = count( $prefixes );
+
+                if( $this->updateDb( $prefixes, $protocol, 'prefix' ) ) {
+                    $this->result[ 'v' . $protocol ][ 'dbUpdated' ] = true;
+                }
+            } else {
+                // This customer is not appropriate for IRRDB filtering.
+                // Delete any pre-existing entries just in case this has changed recently:
+                $this->startTimer();
+
+                Cache::store()->forget( 'irrdb:prefix:ipv' . $protocol . ':' . $this->customer()->asMacro( $protocol ) );
+
+                IrrdbPrefix::whereCustomerId( $this->customer()->id )
+                    ->whereProtocol( $protocol )->delete();
+
+                $this->result[ 'dbTime' ] += $this->timeElapsed();
+                $this->result[ 'v' . $protocol ][ 'dbUpdated' ] = true;
+                $this->result[ 'msg' ] .= "{$this->customer()->name} not a RS client or IRRDB filtered for IPv{$protocol}. IPv{$protocol} prefixes, if any, wiped from database. ";
+            }
+        }
+
+        return $this->result;
+    }
+
+
+    /**
+     * Validate a given array of CIDR formatted prefixes for the given protocol and
+     * remove (and alert on) any elements failing validation.
+     *
+     * @param array $entries Prefixes in CIDR notation
+     * @param int $protocol Either 4/6
+     *
+     * @return array Valid prefixes
+     */
+    #[\Override]
+    protected function validate( array $entries, int $protocol ): array
+    {
+        if( $protocol === 4 ) {
+            $validator = new ValidateIPv4Cidr;
+        } else {
+            $validator = new ValidateIPv6Cidr;
+        }
+
+        foreach( $entries as $key => $prefix ) {
+            if( !$validator->passes( '', $prefix ) ) {
+                unset( $entries[$key] );
+                Log::alert( 'IRRDB CLI action - removing invalid prefix ' . $prefix . ' from IRRDB result set!' );
+            }
+        }
+
+        return $entries;
+    }
+}
diff --git a/app/Tasks/Rir/Generator.php b/app/Tasks/Rir/Generator.php
new file mode 100644
index 000000000..ff815c0a0
--- /dev/null
+++ b/app/Tasks/Rir/Generator.php
@@ -0,0 +1,275 @@
+
+ * @author     Yann Robin       
+ * @category   Tasks
+ * @package    IXP\Tasks\Router
+ * @copyright  Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee
+ * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
+ */
+class Generator
+{
+    /**
+     * Template chosen
+     *
+     * @var string
+     */
+    private $tmpl = "";
+
+    /**
+     * Generator constructor.
+     *
+     * @param string $tmpl
+     */
+    public function __construct( string $tmpl )
+    {
+        $this->setTemplate( $tmpl );
+    }
+
+    /**
+     * Set the template name
+     *
+     * @param string $tmpl
+     */
+    public function setTemplate( string $tmpl ): static
+    {
+        $this->tmpl = $tmpl;
+        return $this;
+    }
+
+    /**
+     * Get the template name
+     *
+     * @return string
+     */
+    public function template(): string
+    {
+        return $this->tmpl;
+    }
+
+    /**
+     * Generate the RIR object
+     *
+     * @return string
+     *
+     * @throws
+     */
+    public function generate(): string
+    {
+        if( !( $this->template() ) ) {
+            throw new GeneralException( 'You must specify a template name so you can generate a RIR object' );
+        }
+
+        // sanitise template name
+        $tmpl = preg_replace( '/[^0-9a-z_\-]/', '', strtolower( $this->template() ) );
+
+        if( !ViewFacade::exists ( 'api/v4/rir/' . $tmpl ) ) {
+            throw new GeneralException("Unknown RIR object template provided - api/v4/rir/{$tmpl}" );
+        }
+
+        // populate the template variables
+        $customers =  Customer::getConnected( false, false, 'autsys' )->keyBy( 'id' );
+
+        return view( 'api/v4/rir/' . $tmpl, [
+                'customers'     => $customers,
+                'asns'          => $this->generateASNs( $customers ),
+                "rsclients"     => $this->generateRouteServerClientDetails( $customers ),
+                "protocols"     => [ 4, 6 ]
+            ] )->render();
+    }
+
+    /**
+     * Gather and create the IXP customer ASN details.
+     *
+     * Returns an associate array indexed by ordered ASNs of active external trafficking customers:
+     *
+     * [
+     * [65500] => [
+     * ['name']    => Customer Name
+     * ['asmacro'] => AS-CUSTOMER
+     * ],
+     * ...
+     * ]
+     *
+     * @param Collection $customers Array of all active external trafficking customers
+     *
+     * @return array[]
+     *
+     * @throws
+     *
+     * @psalm-return array
+     */
+    private function generateASNs( Collection $customers ): array
+    {
+        $asns = [];
+        foreach( $customers as $c ) {
+            /** @var $c Customer */
+            $asns[ $c->autsys ] = [
+                'asmacro' => $c->asMacro( 4, 'AS' ),
+                'name'    => $c->name
+            ];
+        }
+
+        ksort( $asns, SORT_NUMERIC );
+
+        return $asns;
+    }
+
+    /**
+     * Gather up route server client information for building RIR objects
+     *
+     * Returns an array of the form:
+     *
+     * [
+     * [ vlans ] => [
+     * [ $vlanid ] => [
+     * [servers] => [   // route server IP addresses by protocol
+     * [4] => [
+     * [0] => 193.242.111.8
+     * ...
+     * ],
+     * [6] => [
+     * ...
+     * ]
+     * ]
+     * ],
+     * [ $another_vlanid ] => [
+     * ...
+     * ],
+     * ...
+     * ],
+     * [clients] => [
+     * [$customer_asn] => [
+     * [id] => customer id,
+     * [ vlans ] => [
+     * [ vlanid ] => [
+     * [$vlan_interface_id] => [    // customer's IP addresses by protocol
+     * [4] => 193.242.111.xx
+     * [6] => 2001:7f8:18::xx
+     * ],
+     * ...   // if the user has more than one VLAN interface on this VLAN
+     * ],
+     * ...
+     * ],
+     * ],
+     * ],
+     * ]
+     *
+     * @param Collection $customers
+     *
+     * @return ((((array|mixed)[]|mixed)[]|mixed)[]|mixed)[]
+     *
+     * @throws
+     *
+     * @psalm-return array{vlans?: array, 4: list}}>, clients?: array|mixed}|mixed>|mixed}
+     */
+    private function generateRouteServerClientDetails( Collection $customers ): array
+    {
+        // get the public peering VLANs
+        $vlans = Vlan::publicOnly()->orderBy( 'number' )->get();
+
+        $rsclients = [];
+
+        foreach( $vlans as $vlan ) {
+            foreach( [ 4, 6 ] as $proto ) {
+                // get the available route servers
+                $servers = Router::routeServer()->where( 'vlan_id', $vlan->id )
+                    ->where( 'protocol', 4 )->get()->pluck( 'peering_ip' )->toArray();
+
+                if( !count( $servers ) ){
+                    continue;
+                }
+
+                $rsclients[ 'vlans' ][ $vlan->id ][ 'servers' ][ $proto ] = [];
+
+                foreach( $servers as $server ){
+                    $rsclients[ 'vlans' ][ $vlan->id ][ 'servers' ][ $proto ][] = $server;
+                }
+
+                foreach( $vlan->vlanInterfaces as $vli ) {
+                    if( !$vli->rsclient ){
+                        continue;
+                    }
+
+                    $oneConnectedInterface = false;
+
+                    foreach( $vli->virtualInterface->physicalInterfaces as $pi ) {
+                        if( $pi->statusConnected() ) {
+                            $oneConnectedInterface = true;
+                            break;
+                        }
+                    }
+
+                    if( !$oneConnectedInterface ){
+                        continue;
+                    }
+
+                    $cust = $vli->virtualInterface->customer;
+
+                    if( !$cust->statusNormal() ){
+                        continue;
+                    }
+
+                    // Customer still active?
+                    if( !isset( $customers[ $cust->id ] ) ){
+                        continue;
+                    }
+
+                    if( !isset( $rsclients[ 'clients' ][ $cust->autsys ] ) ) {
+                        $rsclients[ 'clients' ][ $cust->autsys ][ 'id' ]       = $cust->id;
+                        $rsclients[ 'clients' ][ $cust->autsys ][ 'vlans' ]    = [];
+                    }
+
+                    $fnEnabled = "ipv{$proto}enabled";
+
+                    if( $vli->$fnEnabled ) {
+                        $fnIpaddress = "ipv{$proto}address";
+                        $rsclients[ 'clients' ][ $cust->autsys ][ 'vlans' ][ $vlan->id ][ $vli->id ][ $proto ] = $vli->$fnIpaddress->address;
+                    }
+                }
+            }
+        }
+
+        if( isset( $rsclients[ 'clients' ] ) ){
+            ksort( $rsclients[ 'clients' ], SORT_NUMERIC );
+        }
+
+        return $rsclients;
+    }
+}
\ No newline at end of file
diff --git a/app/Tasks/Router/ConfigurationGenerator.php b/app/Tasks/Router/ConfigurationGenerator.php
new file mode 100644
index 000000000..d992f5b67
--- /dev/null
+++ b/app/Tasks/Router/ConfigurationGenerator.php
@@ -0,0 +1,109 @@
+
+ * @author     Yann Robin       
+ * @category   Tasks
+ * @package    IXP\Tasks\Router
+ * @copyright  Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee
+ * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
+ */
+class ConfigurationGenerator
+{
+    /**
+     * Router details object.
+     *
+     * @var Router $r
+     */
+    private $router = null;
+
+    /**
+     * ConfigurationGenerator constructor
+     * .
+     * @param Router $r
+     *
+     * @throws
+     */
+    public function __construct( Router $r )
+    {
+        $this->setRouter( $r );
+    }
+
+    /**
+     * Set the router options array
+     *
+     * @param Router $router Router details
+     *
+     * @throws
+     */
+    public function setRouter( Router $router ): static
+    {
+        $this->router = $router;
+        return $this;
+    }
+
+    /**
+     * Get the router options array
+     *
+     * @return Router
+     */
+    public function router(): Router
+    {
+        return $this->router;
+    }
+
+    /**
+     * Generate and return the configuration
+     *
+     * @throws
+     */
+    public function render(): ViewContract
+    {
+        $ints = VlanInterfaceAggregator::sanitiseVlanInterfaces(
+            $this->router()->vlan, $this->router()->protocol, $this->router()->type, $this->router()->quarantine
+        );
+
+        $v = view( $this->router()->template )->with(
+            [ 'handle'  => $this->router()->handle,
+              'ints'    => $ints,
+              'router'  => $this->router(),
+              'vlan'    => $this->router()->vlan
+            ]
+        );
+
+        return $v;
+    }
+}
\ No newline at end of file
diff --git a/app/Tasks/Yaml/SwitchConfigurationGenerator.php b/app/Tasks/Yaml/SwitchConfigurationGenerator.php
new file mode 100644
index 000000000..15f158163
--- /dev/null
+++ b/app/Tasks/Yaml/SwitchConfigurationGenerator.php
@@ -0,0 +1,337 @@
+
+ * @author     Yann Robin       
+ * @category   Tasks
+ * @package    IXP\Tasks\Router
+ * @copyright  Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee
+ * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
+ */
+class SwitchConfigurationGenerator
+{
+    /**
+     *
+     * @var Switcher
+     */
+    private $switch = null;
+
+    public function __construct( Switcher $switch )
+    {
+        $this->setSwitch( $switch );
+    }
+
+    /**
+     * Set the switch
+     *
+     * @param Switcher $switch
+     */
+    public function setSwitch( Switcher $switch ): static
+    {
+        $this->switch = $switch;
+        return $this;
+    }
+
+    /**
+     * Get the switch options array
+     *
+     * @return Switcher
+     */
+    public function getSwitch(): Switcher
+    {
+        return $this->switch;
+    }
+
+    /**
+     * Generate and return the configuration
+     *
+     * @return (((((mixed|null|string)[]|int|mixed|null)[]|mixed|null|string)[]|bool|int|mixed|null|string)[]|mixed)[][]
+     *
+     * @throws
+     *
+     * @psalm-return array{layer2interfaces: list{0?: array{type: 'core'|'edge', description: null|string, dot1q: bool|null, stp?: bool, cost?: int|null, preference?: int|null, virtualinterfaceid: int|null, corebundleid?: int, lagframing: bool|null, lagindex?: int, vlans: list{0?: array{number: int|mixed|null, macaddresses: list{0?: mixed|null|string,...}, vlaninterfaceid?: int, ipaddresses?: array{ipv4?: null|string, ipv6?: null|string}}|mixed,...}, name: null|string, lagmaster?: bool, fastlacp?: bool, lagmembers?: list, shutdown: bool, autoneg?: bool, speed?: int|null, status?: mixed|string, rate_limit?: int|null}|mixed,...}}
+     */
+    public function generate(): array
+    {
+        $ports = [];
+        $visProcessed = [];
+        $cbsProcessed = [];
+
+        foreach( $this->getSwitch()->switchPorts as $sp ) {
+            // is the port in use?
+            if( !( $pi = $sp->physicalInterface ) ) {
+                continue;
+            }
+
+            // don't emit ports which aren't ready for production
+            if( $pi->statusAwaitingXConnect() ) {
+                continue;
+            }
+
+            if( ( $sp->typeUnset() || $sp->typePeering() ) && !in_array( $pi->virtualinterfaceid, $visProcessed )  ) {
+                $ports = array_merge( $ports, $this->processVirtualInterface( $pi->virtualInterface ));
+                $visProcessed[] = $pi->virtualinterfaceid;
+            } else if( $sp->typeCore() && !in_array( $pi->virtualinterfaceid, $cbsProcessed ) && $pi->coreBundle() && $pi->coreBundle()->typeL2Lag() ) {
+                $ports = array_merge( $ports, $this->processCoreBundleInterface( $pi ) );
+                $cbsProcessed[] = $pi->virtualinterfaceid;
+            }
+        }
+        return array( 'layer2interfaces' => $ports );
+    }
+
+    /**
+     * @param VirtualInterface $vi
+     *
+     * @return ((((null|string)[]|int|null)[]|null|string)[]|bool|int|mixed|null|string)[][]
+     *
+     * @psalm-return list{0?: array{type: 'edge', description: null|string, dot1q: bool|null, virtualinterfaceid: int, lagframing: bool, lagindex?: int, vlans: non-empty-list, number: int|null, vlaninterfaceid: int}>, name: null|string, lagmaster?: bool, fastlacp?: bool, lagmembers?: list, shutdown: bool, status: mixed|string, autoneg?: bool, speed?: int|null, rate_limit?: int|null}, 1?: array{type: 'edge', description: null|string, dot1q: bool|null, virtualinterfaceid: int, lagframing: bool, lagindex?: int, vlans: non-empty-list, number: int|null, vlaninterfaceid: int}>, name: null|string, lagmaster: bool, fastlacp: bool, shutdown: bool, status: mixed|string, autoneg: bool, speed: int|null, rate_limit: int|null, lagmembers?: list},...}
+     */
+    private function processVirtualInterface( VirtualInterface $vi ): array
+    {
+        $p                       = [];
+        $p['type']               = 'edge';
+        $p['description']        = $vi->customer->abbreviatedName;
+        $p['dot1q']              = $vi->trunk;
+        $p['virtualinterfaceid'] = $vi->id;
+        $p['lagframing']         = $vi->lag_framing;
+        if( $vi->channelgroup ) {
+            $p['lagindex'] = $vi->channelgroup;
+        }
+
+        $p['vlans'] = [];
+
+        foreach( $vi->vlanInterfaces as $vli ) {
+            $v = [];
+            $v[ 'number' ] = $vli->vlan->number;
+            $v[ 'vlaninterfaceid' ] = $vli->id;
+            
+            $v[ 'macaddresses' ] = [];
+            foreach( $vli->layer2addresses as $mac ) {
+                $v[ 'macaddresses' ][] = $mac->macFormatted( ':' );
+            }
+
+            $v['ipaddresses'] = [];
+
+            if( $vli->ipv4enabled && $vli->ipv4address ) {
+                $v[ 'ipaddresses' ][ 'ipv4' ] = $vli->ipv4address->address;
+            }
+
+            if( $vli->ipv6enabled && $vli->ipv6address ) {
+                $v[ 'ipaddresses' ]['ipv6'] = $vli->ipv6address->address;
+            }
+            $p[ 'vlans' ][] = $v;
+        }
+
+        // return nothing if there are no vlans defined on the port
+        if( empty( $p[ 'vlans' ] ) ) {
+            return [];
+        }
+
+        // we now have the base port config. If this is not a LAG, just return it:
+        if( !$vi->lag_framing ) {
+            /** @var PhysicalInterface $pi */
+            $pi = $vi->physicalInterfaces()->first();
+            $p['shutdown']           = !$pi->isConnectedOrQuarantine();
+            $p['status']             = $pi->apiStatus();
+            $p['name']               = $pi->switchPort->ifName;
+            $p['speed']              = $pi->speed;
+            $p['rate_limit']         = $pi->rate_limit;
+            $p['autoneg']            = $pi->autoneg;
+            return [ $p ];
+        }
+
+        $ports = [];
+
+        // bundle definition:
+        $p['name']      = $vi->bundleName();
+        $p['lagmaster'] = true;
+        $p['fastlacp']  = $vi->fastlacp;
+        $p['lagmembers']= [];
+        $p['shutdown']  = true;
+        $p['status']    = PhysicalInterface::$APISTATES[ PhysicalInterface::STATUS_NOTCONNECTED ];
+
+        $lagquarantinestatus = true;
+        // build up list of physical ports associated with this lag master
+        foreach( $vi->physicalInterfaces as $pi ) {
+            if( !$pi->switchPort ) {
+                continue;
+            }
+            $p[ 'lagmembers' ][] = $pi->switchPort->ifName;
+
+            // if any bundle members are up, the LAG is up
+            if( $pi->isConnectedOrQuarantine() ) {
+                $p['shutdown'] = false;
+            }
+            // if any bundle members are connected then status is connected
+            // if all bundle members are quarantine then status is quarantine
+            // otherwise status remains as default: notconnected
+            if( $pi->statusConnected() ) {
+                $p['status'] = $pi->apiStatus();
+            }
+            if( $pi->status !== PhysicalInterface::STATUS_QUARANTINE ) {
+                $lagquarantinestatus = false;
+            }
+        }
+
+        if ($lagquarantinestatus) {
+            $p['status'] = PhysicalInterface::$APISTATES[ PhysicalInterface::STATUS_QUARANTINE ];
+        }
+
+        $ports[]        = $p;
+
+        unset( $p['lagmembers'] );
+
+        // interface definitions:
+        foreach( $vi->physicalInterfaces as $pi ) {
+            if( !$pi->switchPort ) {
+                continue;
+            }
+            $p['shutdown']   = !$pi->isConnectedOrQuarantine();
+            $p['status']     = $pi->apiStatus();
+            $p['name']       = $pi->switchPort->ifName;
+            $p['lagmaster']  = false;
+            $p['autoneg']    = $pi->autoneg;
+            $p['speed']      = $pi->speed;
+            $p['rate_limit'] = $pi->rate_limit;
+            $ports[] = $p;
+        }
+
+        return $ports;
+    }
+
+    /**
+     * @param PhysicalInterface $pi
+     *
+     * @return (((array|mixed)[]|null|string)[]|bool|int|null|string)[][]
+     *
+     * @psalm-return list{0: array{type: 'core', description: string, dot1q: bool|null, stp: bool, cost: int|null, preference: int|null, virtualinterfaceid: int|null, corebundleid: int, lagframing: bool|null, lagindex?: int, vlans: list{0?: array{number: mixed, macaddresses: array},...}, name: null|string, lagmaster?: bool, fastlacp?: bool, lagmembers?: list, shutdown: bool, autoneg?: bool, speed?: int|null}, 1?: array{type: 'core', description: string, dot1q: bool|null, stp: bool, cost: int|null, preference: int|null, virtualinterfaceid: int|null, corebundleid: int, lagframing: bool|null, lagindex?: int, vlans: list{0?: array{number: mixed, macaddresses: array},...}, name: null|string, lagmaster: bool, fastlacp: bool, shutdown: bool, autoneg: bool, speed: int|null, lagmembers?: list},...}
+     */
+    private function processCoreBundleInterface( PhysicalInterface $pi ): array
+    {
+        $vi = $pi->virtualInterface;
+        $ci = $pi->coreInterface;
+        $cl = $ci->coreLink();
+        $cb = $cl->coreBundle;
+
+        // side a or side b?
+        if( $cl->core_interface_sidea_id === $ci->id ) {
+            $sideFn = 'coreInterfaceSideA';
+        } else {
+            $sideFn = 'coreInterfaceSideB';
+        }
+
+        $p                       = [];
+        $p['type']               = 'core';
+        $p['description']        = $cb->description;
+        $p['dot1q']              = $vi->trunk;
+        $p['stp']                = $cb->stp;
+        $p['cost']               = $cb->cost;
+        $p['preference']         = $cb->preference;
+        $p['virtualinterfaceid'] = $vi->id;
+        $p['corebundleid']       = $cb->id;
+        $p['lagframing']         = $vi->lag_framing;
+        if( $vi->channelgroup ) {
+            $p['lagindex'] = $vi->channelgroup;
+        }
+
+        $p['vlans'] = [];
+
+        foreach( $pi->switchPort->switcher->infrastructureModel->vlans as $vlan ) {
+            $v = [];
+            $v[ 'number' ]          = $vlan->number;
+            $v[ 'macaddresses' ]    = [];
+            $p[ 'vlans' ][]         = $v;
+        }
+
+        // we now have the base port config. If this is not a LAG, just return it:
+        if( !$vi->lag_framing ) {
+            $p['shutdown']           = !$pi->isConnectedOrQuarantine();
+            $p['name']               = $pi->switchPort->ifName;
+            $p['speed']              = $pi->speed;
+            $p['autoneg']            = $pi->autoneg;
+            return [ $p ];
+        }
+
+        $ports = [];
+
+        // bundle definition:
+        $p['name']      = $vi->bundleName();
+        $p['lagmaster'] = true;
+        $p['fastlacp']  = $vi->fastlacp;
+        $p['lagmembers']= [];
+        $p['shutdown']  = true;
+
+        // build up list of physical ports associated with this lag master
+        foreach( $cb->corelinks as $_cl ) {
+            /** @var PhysicalInterface $_pi */
+            $_pi = $_cl->$sideFn->physicalInterface;
+            if( !$_pi->switchPort ) {
+                continue;
+            }
+            $p['lagmembers'][]= $_pi->switchPort->ifName;
+
+            // if any bundle members are up, the LAG is up
+            if( $_pi->isConnectedOrQuarantine() ) {
+                $p['shutdown'] = false;
+            }
+        }
+
+        // but if the bundle is down, the whole lot is down
+        if( !$cb->enabled ) {
+            $p['shutdown'] = true;
+        }
+
+        $ports[]        = $p;
+
+        unset( $p[ 'lagmembers' ] );
+
+        // interface definitions:
+        foreach( $cb->corelinks as $_cl ) {
+            /** @var PhysicalInterface $_pi */
+            $_pi = $_cl->$sideFn->physicalInterface;
+
+            if( !$_pi->switchPort ) {
+                continue;
+            }
+            $p['shutdown']   = !$pi->isConnectedOrQuarantine();
+            $p['name']       = $_pi->switchPort->ifName;
+            $p['lagmaster']  = false;
+            $p['autoneg']    = $_pi->autoneg;
+            $p['speed']      = $_pi->speed;
+            $ports[] = $p;
+        }
+
+        return $ports;
+    }
+}
\ No newline at end of file
diff --git a/app/Traits/Observable.php b/app/Traits/Observable.php
new file mode 100644
index 000000000..37f22f7bf
--- /dev/null
+++ b/app/Traits/Observable.php
@@ -0,0 +1,155 @@
+wasRecentlyCreated ) {
+                    static::logChange( $model, Log::ACTION_CREATED );
+                } elseif ( $model->getChanges() ) {
+                    // Check if with have field with log exception
+                    if( !method_exists( $model, 'observerSkipUpdateLogging' ) || !$model->observerSkipUpdateLogging( $model->getChanges() ) ) {
+                        static::logChange( $model, Log::ACTION_UPDATED );
+                    }
+                }
+            }
+        );
+
+        static::deleted(
+            function( Model $model ) {
+                static::logChange( $model, Log::ACTION_DELETED );
+            }
+        );
+    }
+
+    /**
+     * saves the changes into the log table
+     *
+     * @param Model     $model
+     * @param string    $action
+     *
+     * @return void
+     */
+    public static function logChange( Model $model, string $action ): void
+    {
+        Log::create(
+            [
+                'user_id'   => Auth::check() ? Auth::id() : null,
+                'model'     => self::getClass(),
+                'model_id'  => $model->id,
+                'action'    => $action,
+                'message'   => static::logSubject( $model ),
+                'models'    => [
+                    'new'       => $action !== Log::ACTION_DELETED ? $model->getAttributes()  : null,
+                    'old'       => $action !== Log::ACTION_CREATED ? $model->getOriginal()    : null,
+                    'changed'   => $action === Log::ACTION_UPDATED ? $model->getChanges()     : null,
+                ]
+            ]
+        );
+    }
+
+    /**
+     * @return string
+     */
+    public static function getClass(): string
+    {
+        return class_basename(static::class );
+    }
+
+    /**
+     * represents the model's attributes as a string
+     *
+     * @param  Model  $model
+     *
+     * @return string
+     *
+     * @throws \JsonException
+     *
+     * @see logImplodeAssoc
+     */
+    public static function logSubject( Model $model ): string
+    {
+        return static::logImplodeAssoc( $model->attributesToArray() );
+    }
+
+    /**
+     * represents the array as a string
+     *
+     * @param  array  $attrs
+     *
+     * @return string
+     *
+     * @throws \JsonException
+     */
+    public static function logImplodeAssoc(array $attrs): string
+    {
+        $l = '';
+
+        foreach ($attrs as $k => $v) {
+            if( is_array( $v ) ){
+                $v = json_encode($v, JSON_THROW_ON_ERROR);
+            }
+            $l .= "{ $k => $v } ";
+        }
+
+        return trim( $l );
+    }
+
+    /**
+     * @param User|null $user limit the log entries to the actions of this user
+     *
+     * @psalm-suppress UndefinedThisPropertyFetch
+     *
+     * @psalm-return \Illuminate\Database\Eloquent\Builder<\IXP\Models\Log>
+     */
+    public function getLogEntries( ?User $user = null ): \Illuminate\Database\Eloquent\Builder
+    {
+        return Log::entries( self::getClass(), $this->id, $user );
+    }
+}
\ No newline at end of file
diff --git a/app/Utils/Bgpq3.php b/app/Utils/Bgpq3.php
new file mode 100644
index 000000000..4792404d6
--- /dev/null
+++ b/app/Utils/Bgpq3.php
@@ -0,0 +1,209 @@
+
+ */
+class Bgpq3
+{
+
+    /**
+     * Constructor
+     *
+     * @param string $path The full executable path of the BGPQ3 utility
+     * @param string $whois Whois server - defaults to BGPQ's own default
+     * @param string $sources Whois server sources - defaults to BGPQ's own default
+     * @throws ConfigurationException
+     */
+    public function __construct( private string $path, private ?string $whois = null, private ?string $sources = null )
+    {
+        if( !$path || !is_file( $path ) || !is_executable( $path ) ) {
+            throw new ConfigurationException('You must set the configuration option IXP_IRRDB_BGPQ3_PATH and it must be the absolute path to the executable bgpq3 utility.');
+        }
+    }
+
+    /**
+     * Get the IRRDB prefix list (based on route[6]: objects) for a given AS
+     * number / macro and protocol.
+     *
+     * Returns an array of prefixes (or empty array).
+     *
+     * @param string $asmacro As number (of the form as1234) or AS macro
+     * @param int $proto The IP protocol - 4 or 6.
+     *
+     * @return array The array of prefixes (or empty array).
+     *
+     * @throws Exception On a JSON decoding error
+     *
+     * @psalm-return list{0?: mixed,...}
+     */
+    public function getPrefixList( string $asmacro, int $proto = 4 ): array
+    {
+        $minSubnetSize = config( 'ixp.irrdb.min_v' . $proto . '_subnet_size' );
+        $json = $this->execute( '-l pl -j -m ' . $minSubnetSize . ' ' . escapeshellarg( $asmacro ), $proto );
+        $array = json_decode( $json, true );
+
+        if( $array === null ){
+            throw new Exception( "Could not decode JSON response from BGPQ" );
+        }
+
+        if( !isset( $array[ 'pl' ] ) ){
+            throw new Exception( "Named prefix list [pl] expected in decoded JSON but not found!" );
+        }
+
+        $prefixes = [];
+        // we're going to ignore the 'exact' for now.
+        foreach( $array[ 'pl' ] as $ar ){
+            $prefixes[] = $ar['prefix'];
+        }
+
+        return $prefixes;
+    }
+
+    /**
+     * Get the IRRDB ASN list (based on route[6]: objects) for a given AS
+     * number / macro and protocol.
+     *
+     * Returns an array of ASNs that may appear in any as path for the
+     * route paths (or empty array).
+     *
+     * @param string    $asmacro As number (of the form as1234) or AS macro
+     * @param int       $proto The IP protocol - 4 or 6.
+     *
+     * @return array The array of prefixes (or empty array).
+     *
+     * @psalm-return list
+     */
+    public function getAsnList( string $asmacro, int $proto = 4 ): array
+    {
+        // -6 makes no sense with as-path (-f/-G) generation
+        $json = $this->execute( '-3j -l pl -f 999 ' . escapeshellarg( $asmacro ) );
+        $array = json_decode( $json, true );
+
+        if( $array === null ){
+            throw new Exception( "Could not decode JSON response from BGPQ when fetching ASN list" );
+        }
+
+        if( !isset( $array[ 'pl' ] ) ){
+            throw new Exception( "Named prefix list [pl] expected in decoded JSON but not found when fetching ASN list!" );
+        }
+
+        $asns = [];
+
+        foreach( $array[ 'pl' ] as $asn ){
+            $asns[] = $asn;
+        }
+
+        return $asns;
+    }
+
+    /**
+     * Ececute the BGPQ command line utility using the defined (or default)
+     * whois host and sources.
+     *
+     * @param string    $cmd The query part of the BGPQ command. I.e. other switches besides -6, -h, -S.
+     * @param int       $proto The protocol. If 6, adds the -6 switch
+     *
+     * @return string The output from the shell command.
+     *
+     * @throws Exception If return code from BGPQ3 is != 0
+     */
+    private function execute( string $cmd, int $proto = 4 ): string
+    {
+        if( $this->whois ){
+            $cmd = '-h ' . escapeshellarg( $this->whois ) . ' ' . $cmd;
+        }
+
+        if( $this->sources ){
+            $cmd = '-S ' . escapeshellarg( $this->sources ) . ' ' . $cmd;
+        }
+
+        if( $proto === 6 ){
+            $cmd = '-6 ' . $cmd;
+        }
+
+        $cmd = $this->path . ' ' . $cmd;
+
+        $output = [];
+        $return_var = 0;
+
+        Log::debug('[BGPQ3] executing: ' . $cmd );
+        exec( $cmd, $output, $return_var );
+
+        if( $return_var != 0 ){
+            throw new Exception( 'Error executing BGPQ3 with: ' . $cmd );
+        }
+
+        return implode( "\n", $output );
+    }
+
+    /**
+     * The whois server to query
+     *
+     * @param string $whois The whois server to query
+     *
+     * @return static For fluent interfaces
+     */
+    public function setWhois( string $whois ): static
+    {
+        $this->whois = $whois;
+        return $this;
+    }
+
+    /**
+     * The whois server sources
+     *
+     * @param string $sources The whois server sources
+     *
+     * @return static For fluent interfaces
+     */
+    public function setSources( string $sources ): static
+    {
+        $this->sources = $sources;
+        return $this;
+    }
+
+    /**
+     * The executable path to the BGPQ executable
+     *
+     * @param string $path The executable path to the BGPQ executable
+     *
+     * @return static For fluent interfaces
+     */
+    public function setPath( string $path ): static
+    {
+        $this->path = $path;
+
+        return $this;
+    }
+}
\ No newline at end of file
diff --git a/app/Utils/DotEnv/DotEnvContainer.php b/app/Utils/DotEnv/DotEnvContainer.php
new file mode 100644
index 000000000..eb1026abd
--- /dev/null
+++ b/app/Utils/DotEnv/DotEnvContainer.php
@@ -0,0 +1,190 @@
+
+ * @package IXP\Utils\DotEnv
+ * @copyright  Copyright (C) 2009 - 2025 Internet Neutral Exchange Association Company Limited By Guarantee
+ * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
+ */
+class DotEnvContainer
+{
+    /**
+     * Constructs a new instance.
+     *
+     * @param array $settings
+     */
+    public function __construct( protected array $settings ) { }
+
+    public function settings(): ?array
+    {
+        return $this->settings;
+    }
+
+    public function setSettings( ?array $settings ): static
+    {
+        $this->settings = $settings;
+        return $this;
+    }
+
+    /**
+     * @param string $key
+     * @return mixed (null if not set)
+     */
+    public function getValue( string $key ): mixed
+    {
+        foreach( $this->settings as $v ) {
+            if( $v[ 'key' ] === $key ) {
+                return $v[ 'value' ];
+            }
+        }
+
+        return null;
+    }
+    
+    /**
+     * @param string $key
+     * @return ?string
+     */
+    public function getComment( string $key ): ?string
+    {
+        foreach( $this->settings as $v ) {
+            if( $v[ 'key' ] === $key ) {
+                return $v[ 'comment' ];
+            }
+        }
+        
+        return null;
+    }
+    
+    public function isset( string $key ): bool
+    {
+        return array_any( $this->settings, fn( $v ) => $v[ 'key' ] === $key );
+    }
+
+    public function indexOf( string $key ): ?int
+    {
+        return array_find_key( $this->settings, fn( $v ) => $v[ 'key' ] === $key );
+    }
+
+    public function unset( string $key ): array
+    {
+        foreach( $this->settings as $k => $v ) {
+            if( $v[ 'key' ] === $key ) {
+                return array_splice( $this->settings, $k, 1 );
+            }
+        }
+
+        return [];
+    }
+
+    /**
+     * For just a comment line, leave key and value null
+     * For a blank line, leave them all as null
+     *
+     * @param string|null $key
+     * @param string|null $value
+     * @param string|null $comment
+     * @return static
+     * @throws DotEnvInvalidSettingException
+     */
+    public function set( ?string $key = null, ?string $value = null, ?string $comment = null ): static
+    {
+        // test
+        if( $key === null && $value !== null ) {
+            throw new DotEnvInvalidSettingException( 'Cannot set a value without a key' );
+        }
+
+        if( $key !== null && !preg_match( '/^[\w_]+$/', $key ) ) {
+            throw new DotEnvInvalidSettingException( 'Invalid key exception: ' . $key );
+        }
+
+        // does the key already exist?
+        if( $key !== null && $this->isset( $key ) ) {
+            throw new DotEnvInvalidSettingException( 'Duplicate key exception: ' . $key );
+        }
+
+        $this->settings[] = [
+            'key'     => $key,
+            'value'   => $value === null ? null : DotEnvParser::parseValue( $value ),
+            'comment' => $comment,
+        ];
+
+        return $this;
+    }
+
+    /**
+     * For just a comment line, leave key and value null
+     * For a blank line, leave them all as null
+     *
+     * @param string $key
+     * @param ?string|null $value
+     * @param ?string|null $comment
+     * @return static
+     * @throws DotEnvInvalidSettingException
+     */
+    public function replace( string $key, ?string $value = null, ?string $comment = null ): static
+    {
+        // does the key already exist?
+        if( ( $idx = $this->indexOf( $key ) ) === null ) {
+            throw new DotEnvInvalidSettingException( 'Cannot replace a key that does not exist: ' . $key );
+        }
+
+        $this->settings[ $idx ] = [
+            'key'     => $key,
+            'value'   => DotEnvParser::parseValue( $value ),
+            'comment' => $comment,
+        ];
+
+        return $this;
+    }
+    
+    
+    /**
+     * Update just the value of an existing key
+     *
+     * @param string $key
+     * @param ?string|null $value
+     * @return static
+     * @throws DotEnvInvalidSettingException
+     */
+    public function updateValue( string $key, ?string $value = null ): static
+    {
+        // does the key already exist?
+        if( ( $idx = $this->indexOf( $key ) ) === null ) {
+            throw new DotEnvInvalidSettingException( 'Cannot update a key that does not exist: ' . $key );
+        }
+        
+        $this->settings[ $idx ][ 'value' ] = DotEnvParser::parseValue( $value );
+        
+        return $this;
+    }
+    
+}
\ No newline at end of file
diff --git a/app/Utils/DotEnv/DotEnvParser.php b/app/Utils/DotEnv/DotEnvParser.php
new file mode 100644
index 000000000..92611f08a
--- /dev/null
+++ b/app/Utils/DotEnv/DotEnvParser.php
@@ -0,0 +1,260 @@
+
+ * @package IXP\Utils\DotEnv
+ * @copyright  Copyright (C) 2009 - 2025 Internet Neutral Exchange Association Company Limited By Guarantee
+ * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
+ */
+class DotEnvParser
+{
+    /**
+     * Parsed settings
+     *
+     * @var        array
+     */
+    protected array $settings = [];
+
+    /**
+     * Constructs a new instance.
+     *
+     * @param string|null $content The dotEnv content
+     */
+    public function __construct( protected ?string $content = null ) {}
+
+    public function content(): ?string
+    {
+        return $this->content;
+    }
+
+    public function setContent( ?string $content ): static
+    {
+        $this->content = $content;
+        return $this;
+    }
+
+    public function settings(): array {
+        return $this->settings;
+    }
+
+    /**
+     * Parses the environment file line by line and store the variables
+     * @throws DotEnvParserException
+     */
+    public function parse(): static
+    {
+        // reset parser if already used
+        if( $this->settings !== [] ) {
+            $this->settings = [];
+        }
+
+        $lines = preg_split( '/\r\n|\r|\n/', $this->content() );
+
+        // remove trailing blank lines
+        while( end( $lines ) === "" ) {
+            array_pop( $lines );
+        }
+
+        // remove leading blank lines
+        while( $lines[0] === "" ) {
+            array_shift( $lines );
+        }
+
+
+        foreach( $lines as $line ) {
+
+            $line = trim($line);
+
+            if( mb_strlen( $line ) && mb_strpos( $line, '#' ) !== 0 ) {
+
+                if( preg_match( '/^(\s*=)|([\w_]+\s+=)|([\w_]+=\s+[\w_"\']+).*/', $line ) ) {
+                    throw new DotEnvParserException( "Cannot parse .env line: " . $line );
+
+                } else if( preg_match( '/^[\w_]+=["\']?.*\${[\w_]+}.*[#]?.*$/', $line ) ) {
+                    throw new DotEnvParserException( "Cannot parse .env line as nested variables are not supported: " . $line );
+
+                } else if( mb_strlen( $line ) && mb_strpos( $line, '#' ) !== 0 && mb_strpos( $line, '=' ) > 1 ) {
+
+                    // all any of the following:
+
+                    // KEY=
+                    // KEY=VALUE
+                    // KEY=VALUE # COMMENT
+                    // KEY=VALUE ### COMMENT
+                    // KEY= # COMMENT
+
+                    // equals can appear in strings after the initial one
+                    $key          = mb_substr( $line, 0, mb_strpos( $line, '=' ) );
+                    $valueElement = mb_substr( $line, mb_strpos( $line, '=' ) + 1 );
+
+                    if( $this->isValidName( $key ) === false ) {
+                        throw new DotEnvParserException( "Invalid key name: " . $key );
+                    }
+
+                    // is there a comment at the end of the line?
+                    $values = explode( '#', $valueElement );
+
+                    $value = $this->parseValue( array_shift( $values ) );
+
+                    $comment = '';
+                    if( count( $values ) === 0 ) {
+                        $comment = null;
+                    } else if( count( $values ) === 1 ) {
+                        $comment = trim( $values[ 0 ] );
+                    } else {
+                        // multiple hashes in the comment element
+                        while( ( $a = array_shift( $values ) ) !== null ) {
+                            if( $a === '' ) {
+                                $comment .= '#';
+                            } else {
+                                $comment .= $a;
+                            }
+                        }
+                        $comment = trim( $comment );
+                    }
+
+                    $this->settings[] = [
+                        "key"     => trim( $key ),
+                        "value"   => $value,
+                        "comment" => $comment,
+                    ];
+                } else {
+                    throw new DotEnvParserException( "Cannot parse .env line: " . $line );
+                }
+
+            } else if( mb_strpos( $line, '#' ) === 0 ) {
+
+                if( mb_strlen( $line ) === 1 ) {
+                    // blank comment line
+                    $this->settings[] = [
+                        "key"     => null,
+                        "value"   => null,
+                        "comment" => "",
+                    ];
+                } else {
+                    $this->settings[] = [
+                        "key"     => null,
+                        "value"   => null,
+                        "comment" => trim( mb_substr( $line, 1 ) ),
+                    ];
+                }
+            } else if( mb_strlen( $line ) === 0 ) {
+                // blank line
+                $this->settings[] = [
+                    "key"     => null,
+                    "value"   => null,
+                    "comment" => null,
+                ];
+            } else {
+                throw new DotEnvParserException( "Cannot parse .env line: " . $line );
+            }
+        }
+
+        // check for duplicate keys
+        $keys = [];
+        foreach( $this->settings as $setting ) {
+            if( $setting['key'] !== null && isset( $keys[ $setting['key'] ] ) ) {
+                throw new DotEnvParserException( "Cannot parse .env - at least two variables have the same name: " . $setting['key'] );
+            }
+            $keys[ $setting['key'] ] = true;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Strips quotes from the values when reading
+     *
+     * @param string $value The value
+     * @return     string
+     */
+    public static function stripQuotes( string $value ): string
+    {
+        // only if we have a single word
+        if( preg_match( '/^\s*[\w\/\.\\_\-,:=\+]+\s*$/', $value ) ) {
+            return preg_replace('/^([\'"])(.*?)\1$/u', '$2', $value) ?? '';
+        }
+
+        return $value;
+    }
+
+    /**
+     * Formats the value for human friendly output
+     *
+     * @param string $value The value
+     * @return     bool|int|string
+     */
+    public static function parseValue( string $value ): bool|int|string
+    {
+        $value = self::stripQuotes( $value );
+
+        if( in_array( strtolower( $value ), [ 'true', 'on', 'yes', '1' ] ) ) {
+            return true;
+        }
+
+        if( in_array( strtolower( $value ), [ 'false', 'off', 'no', '0' ] ) ) {
+            return false;
+        }
+
+        if( preg_match( '/^\-?[1-9][0-9]*$/', $value ) ) {
+            return (int)$value;
+        }
+
+        // if it's a single word, or empty, trim and return
+        if( preg_match( '/^\s*[\w\/\.\\_\-,:=\+]+\s*$/', $value ) || $value === '' ) {
+            return trim( $value );
+        }
+
+        // otherwise return the value, quoted to maintain whitespace
+        $value = preg_replace('/^([\'"])(.*?)\1$/u', '$2', trim( $value ) ) ?? '';
+        return '"' . $value . '"';
+    }
+
+    /**
+     * Determines whether the specified key is valid name for .env files.
+     *
+     * @param string $key The key
+     *
+     * @return     bool
+     */
+    protected function isValidName( string $key ): bool
+    {
+        return (bool)preg_match( '/^[\w]+$/', $key );
+    }
+
+
+}
\ No newline at end of file
diff --git a/app/Utils/DotEnv/DotEnvWriter.php b/app/Utils/DotEnv/DotEnvWriter.php
new file mode 100644
index 000000000..bcae5cf8f
--- /dev/null
+++ b/app/Utils/DotEnv/DotEnvWriter.php
@@ -0,0 +1,99 @@
+
+ * @package IXP\Utils\DotEnv
+ * @copyright  Copyright (C) 2009 - 2025 Internet Neutral Exchange Association Company Limited By Guarantee
+ * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
+ */
+class DotEnvWriter
+{
+    /**
+     * Constructs a new instance.
+     *
+     */
+    public function __construct( protected ?array $settings = null, protected ?string $filename = null ) {
+    }
+
+    public function settings(): ?array
+    {
+        return $this->settings;
+    }
+
+    public function setSettings( ?array $settings ): static
+    {
+        $this->settings = $settings;
+        return $this;
+    }
+
+    public function generateContent(): ?string
+    {
+        if( !is_array($this->settings) || count($this->settings) === 0 ) {
+            return null;
+        }
+
+        $content = '';
+
+        foreach( $this->settings as $l ) {
+
+            $lineStarted = false;
+
+            if( $l['key'] !== null ) {
+                $content .= $l['key'] . '=';
+                $lineStarted = true;
+            }
+
+            if( $l['value'] !== null ) {
+
+                if( is_bool( $l['value'] ) ) {
+                    $content .= $l['value'] ? 'true' : 'false';
+                } elseif( is_int( $l['value'] ) ) {
+                    $content .= $l['value'];
+                } else {
+                    $content .= $l[ 'value' ];
+                }
+
+                $lineStarted = true;
+            }
+
+            if( $l['comment'] !== null ) {
+                $content .= ( $lineStarted ? ' ' : '' ) . '#'
+                    . ( !strlen( trim( $l['comment'] ) ) || trim( $l['comment'] )[0] === '#' ? '' : ' ' )
+                    . $l['comment'];
+            }
+
+            $content .= "\n";
+
+        }
+
+        return $content;
+    }
+
+
+}
\ No newline at end of file
diff --git a/app/Utils/Export/JsonSchema.php b/app/Utils/Export/JsonSchema.php
new file mode 100644
index 000000000..2c08cc042
--- /dev/null
+++ b/app/Utils/Export/JsonSchema.php
@@ -0,0 +1,646 @@
+get( \IXP\Utils\Export\JsonSchema::EUROIX_JSON_LATEST );
+ *
+ * @see        https://github.com/euro-ix/json-schemas
+ * @author     Nick Hilliard 
+ * @author     Barry O'Donovan 
+ * @author     Yann Robin 
+ * @copyright  Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee
+ * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
+ */
+class JsonSchema
+{
+    // Supported versions:
+    // ended 201705: const EUROIX_JSON_VERSION_0_3 = "0.3";
+    // ended 201705: const EUROIX_JSON_VERSION_0_4 = "0.4";
+    // ended 201705: const EUROIX_JSON_VERSION_0_5 = "0.5";
+    public const EUROIX_JSON_VERSION_0_6 = "0.6";
+    public const EUROIX_JSON_VERSION_0_7 = "0.7";
+    public const EUROIX_JSON_VERSION_1_0 = "1.0";
+    // adding a new version? update sanitiseVersion() below also!
+
+    public const EUROIX_JSON_LATEST = self::EUROIX_JSON_VERSION_1_0;
+
+    public const EUROIX_JSON_VERSIONS = [
+        self::EUROIX_JSON_VERSION_0_6,
+        self::EUROIX_JSON_VERSION_0_7,
+        self::EUROIX_JSON_VERSION_1_0,
+    ];
+    
+    /**
+     * Get the JSON schema (for a given version or for the latest version)
+     *
+     * @param string|null   $version    The version to get (or, if null / not present then the latest)
+     * @param bool          $asArray    Do not convert to JSON but rather return the PHP array
+     * @param bool          $detailed   Create the very detailed version (usually for logged in users)
+     * @param bool          $tags       Include customer tags
+     *
+     * @throws
+     */
+    public function get( ?string $version = null, bool $asArray = false, bool $detailed = true, bool $tags = false ): array|string
+    {
+        if( $version === null ) {
+            $version = self::EUROIX_JSON_LATEST;
+        } else {
+            $version = $this->sanitiseVersion( $version );
+        }
+
+        /** @psalm-suppress UndefinedConstant */
+        $output = [
+            'version' => $version,
+            'generator' => 'IXP Manager v' . APPLICATION_VERSION,
+        ];
+
+        // normalise times to UTC for exports
+        date_default_timezone_set('UTC');
+        $output['timestamp'] = now()->toIso8601ZuluString();
+
+        $output['ixp_list']    = $this->getIXPInfo( $version );
+        $output['member_list'] = $this->getMemberInfo( $version, $detailed, $tags );
+
+        // apply filters as some IXs don't want to export all details
+        $output = $this->filter($output);
+
+        if( $asArray ) {
+            return $output;
+        }
+
+        return json_encode( $output, JSON_PRETTY_PRINT )."\n";
+    }
+
+    /**
+     * Ensure a given version exists or default to latest
+     *
+     * @param string $version Version string to sanitise
+     *
+     * @return string Sanitised version
+     */
+    public function sanitiseVersion( string $version ): string
+    {
+        if( in_array( $version, self::EUROIX_JSON_VERSIONS ) ) {
+            return $version;
+        }
+
+        return self::EUROIX_JSON_LATEST;
+    }
+
+    /**
+     * Collate the IXP specific information for the JSON schema export
+     *
+     * @param string $version The version to collate the detail for
+     *
+     * @return (|(array|mixed|value-of)[]|int|null|string)[][]
+     *
+     * @throws
+     *
+     * @psalm-return list{0?: array{shortname: null|string, name: , country: |string, url: , peeringdb_id?: int, ixf_id?: int, ixp_id: int, support_email: , support_phone: , support_contact_hours: , emergency_email: , emergency_phone: , emergency_contact_hours: , billing_contact_hours: , billing_email: , billing_phone: , peering_policy_list: non-empty-list>, vlan: array, array>, switch: array},...}
+     */
+    private function getIXPInfo( string $version ): array
+    {
+        $ixpinfo = [];
+
+        foreach( Infrastructure::with( ['switchers.cabinet.location'] )->get() as $infra ) {
+            $i = [];
+            $i['shortname'] = $infra->name;
+            $i['name'] = config('identity.legalname');
+            $i['country'] = $infra->country ?? config('identity.location.country');
+            $i['url'] = config('identity.corporate_url');
+
+            if( $infra->peeringdb_ix_id ) {
+                $i[ 'peeringdb_id' ] = (int)$infra->peeringdb_ix_id;
+            }
+
+            if( $infra->ixf_ix_id ) {
+                $i[ 'ixf_id' ] = (int)$infra->ixf_ix_id;
+            } else if( $version >= self::EUROIX_JSON_VERSION_0_7 ) {
+
+                // The IX-F ID is officially required for >= v0.7 of the schema.
+                // This shouldn't prevent the IX_F exporter from working though if someone wishes to pull the
+                // information regardless of that being set.
+                //
+                // Two options for this:
+
+                // first pass an ixfid for **every** infrastructure that does not have one
+                // e.g. http://ixp-inex.ldev/api/v4/member-export/ixf/1.0?ixfid_1=30&ixfid_2=31&ixfid_3=30
+                if( request('ixfid_' . $infra->id, false ) ) {
+                    $i[ 'ixf_id' ] = (int)request( 'ixfid_' . $infra->id );
+                }
+
+                // second, just ignore it and set it to zero:
+                // http://ixp-inex.ldev/api/v4/member-export/ixf/1.0?ignore_missing_ixfid=1
+                else if( request('ignore_missing_ixfid', false ) ) {
+                    $i[ 'ixf_id' ] = 0;
+                }
+
+                // by default, we will throw an exception:
+                else {
+                    throw new ExportException( "The IX-F ID is a required parameter for IX-F Export Schema >=v0.7. Set this in IXP Manager in the 'Infrastructures' management page." );
+                }
+            }
+
+            $i['ixp_id'] = $infra->id;    // referenced in member's connections section
+
+            $i['support_email'] = config('identity.support_email');
+            $i['support_phone'] = config('identity.support_phone');
+            $i['support_contact_hours'] = config('identity.support_hours');
+
+            // $infra['stats_api'] = FIXME;
+            $i['emergency_email'] = config('identity.support_email');
+            $i['emergency_phone'] = config('identity.support_phone');
+            $i['emergency_contact_hours'] = config('identity.support_hours');
+            $i['billing_contact_hours'] = config('identity.billing_hours');
+            $i['billing_email'] = config('identity.billing_email');
+            $i['billing_phone'] = config('identity.billing_phone');
+
+            $i['peering_policy_list'] = array_values( Customer::$PEERING_POLICIES);
+
+            $vlansToExport = NetworkInfo::join( 'vlan', 'vlan.id', 'networkinfo.vlanid' )
+                ->where( 'vlan.infrastructureid', $infra->id )
+                ->where( 'vlan.export_to_ixf', true )
+                ->get()->toArray();
+
+            $vlanentry = [];
+            foreach( $vlansToExport as $ni )
+            {
+                $id = $ni['id'];
+                $vlanentry[$id]['id']                                   = $ni['id'];
+                $vlanentry[$id]['name']                                 = $ni['name'];
+                $vlanentry[$id][ 'ipv'.$ni['protocol'] ]['prefix']      = $ni[ 'network' ];
+                $vlanentry[$id][ 'ipv'.$ni['protocol'] ]['mask_length'] = $ni[ 'masklen' ];
+            }
+
+            $data = [];
+            foreach( array_keys( $vlanentry ) as $id ) {
+                $data[] = $vlanentry[ $id ];
+            }
+
+            $i['vlan'] = $data;
+
+            if( $version >= self::EUROIX_JSON_VERSION_0_7 ) {
+                if( !config( 'ixp_fe.frontend.disabled.lg' ) ) {
+                    foreach( $i[ 'vlan' ] as $idx => $vlan ) {
+                        if( isset( $i[ 'vlan' ][ $idx ][ 'ipv4' ] ) ) {
+                            $i[ 'vlan' ][ $idx ][ 'ipv4' ][ 'looking_glass_urls' ][] = url( '/lg' );
+                        }
+                        if( isset( $i[ 'vlan' ][ $idx ][ 'ipv6' ] ) ) {
+                            $i[ 'vlan' ][ $idx ][ 'ipv6' ][ 'looking_glass_urls' ][] = url( '/lg' );
+                        }
+                    }
+                }
+            }
+
+            $i['switch'] = $this->getSwitchInfo( $version, $infra );
+
+            $ixpinfo[] = $i;
+        }
+
+        return $ixpinfo;
+    }
+
+    /**
+     * Collate the IXP's switch information for the JSON schema export
+     *
+     * @param string            $version
+     * @param Infrastructure    $infra
+     *
+     * @return (|int|null|string)[][]
+     *
+     * @psalm-return list
+     */
+    private function getSwitchInfo( string $version, Infrastructure $infra ): array
+    {
+        $data = [];
+
+        foreach( $infra->switchers as $switch ) {
+            if( !$switch->active ) {
+                continue;
+            }
+
+            $switchentry = [];
+            $switchentry['id']      = $switch->id;
+            $switchentry['name']    = $switch->name;
+            $switchentry['colo']    = $switch->cabinet->location->name;
+            $switchentry['city']    = $switch->cabinet->location->city ?? config( 'identity.location.city'    );
+            $switchentry['country'] = $switch->cabinet->location->country ?? config( 'identity.location.country' );
+
+            if( $switch->cabinet->location->pdb_facility_id ) {
+                $switchentry['pdb_facility_id'] = (int)$switch->cabinet->location->pdb_facility_id;
+            }
+
+            if( $version >= self::EUROIX_JSON_VERSION_0_7 ) {
+                $switchentry['manufacturer'] = $switch->vendor->name;
+                $switchentry['model']        = $switch->model;
+            }
+
+            if( $version >= self::EUROIX_JSON_VERSION_1_0 ) {
+                $switchentry['software'] = trim( ( $switch->os ?? '' ) . ' ' . ( $switch->osVersion ?? '' ) );
+            }
+
+            $data[] = $switchentry;
+        }
+
+        return $data;
+    }
+
+    /**
+     * Collate the IXP's member information for the JSON schema export
+     *
+     * @param string $version The version to collate the detail for
+     * @param bool $detailed
+     * @param bool $tags
+     *
+     * @return ((((((stdClass|string)[]|bool|int|mixed|string)[]|int|null)[][]|mixed|string)[]|bool|int|null|string)[]|int|null|string)[][]
+     *
+     * @psalm-return array, array{asnum: int|null, member_since: string, url: null|string, name: null|string, peering_policy: null|string, member_type: string, contact_email?: list{null|string}, contact_phone?: list{null|string}, peering_policy_url?: null|string, contact_hours?: string, ixp_manager?: array{tags: array, in_manrs: bool, is_reseller: bool, is_resold: bool, resold_via_asn?: int|null}, connection_list: list}, ixp_id: mixed, state: 'active', vlan_list: list{non-empty-array}, max_prefix?: int, routeserver: bool, service_type?: list{'ixroutecollector'|'ixrouteserver', ...<'ixroutecollector'|'ixrouteserver'>}, services?: list{stdClass, ...}}|int|null>, ...}, max_prefix?: int, routeserver: bool, service_type?: list{'ixroutecollector'|'ixrouteserver', ...<'ixroutecollector'|'ixrouteserver'>}, services?: list{stdClass, ...}}|int|null>>}}>}>
+     */
+    private function getMemberInfo( string $version, bool $detailed, bool $tags ): array
+    {
+        $memberinfo = [];
+
+        if( $version === self::EUROIX_JSON_VERSION_0_7 ) {
+            $routeServerIPs = [];
+            Router::where( 'type', Router::TYPE_ROUTE_SERVER )
+                ->get()->map( function($item) use(&$routeServerIPs) {
+                    $routeServerIPs[$item->vlan_id][] = $item->peering_ip;
+                });
+
+            $routeCollectorIPs = [];
+            Router::select( [ 'vlan_id', 'peering_ip' ] )
+                ->where( 'type', Router::TYPE_ROUTE_COLLECTOR )
+                ->get()->map( function($item) use(&$routeCollectorIPs) {
+                    $routeCollectorIPs[$item->vlan_id][] = $item->peering_ip;
+                });
+        }
+
+        if( $version === self::EUROIX_JSON_VERSION_1_0 ) {
+            $routeServersByIps = [];
+            Router::where( 'type', Router::TYPE_ROUTE_SERVER )
+                ->get()->map( function($item) use(&$routeServersByIps) {
+                    $routeServersByIps[ $item->vlan_id ][ $item->peering_ip ] = $item;
+                });
+
+            $routeCollectorsByIps = [];
+            Router::where( 'type', Router::TYPE_ROUTE_COLLECTOR )
+                ->get()->map( function($item) use(&$routeCollectorsByIps) {
+                    $routeCollectorsByIps[ $item->vlan_id ][ $item->peering_ip ] = $item;
+                });
+        }
+
+        $customers =  Customer::getConnected( false, false, 'autsys' )
+            ->keyBy( 'id' );
+
+        $cnt = 0;
+        $exclude_asns = [];
+        $exclude_tags = [];
+
+        if( $xas = config( 'ixp_api.json_export_schema.excludes.asnum' ) ) {
+            $exclude_asns = explode( '|', $xas );
+        }
+
+        if( $xt = config( 'ixp_api.json_export_schema.excludes.tags' ) ) {
+            $exclude_tags = explode( '|', $xt );
+        }
+
+        foreach( $customers as $c ) {
+            /** @var Customer  $c */
+            $connlist = [];
+
+            if( config( 'ixp_api.json_export_schema.excludes.rfc6996' ) ) {
+                if( ( $c->autsys >= 64512 && $c->autsys <= 65534 ) ||
+                      ( $c->autsys >= 4200000000 && $c->autsys <= 4294967294 ) ) {
+                    continue;
+                }
+            }
+
+            if( config( 'ixp_api.json_export_schema.excludes.rfc5398' ) ) {
+                if( ( $c->autsys >= 64496 && $c->autsys <= 64511 ) ||
+                      ( $c->autsys >= 65536  && $c->autsys <= 65551 ) ) {
+                    continue;
+                }
+            }
+
+            if( count( $exclude_asns ) && in_array( $c->autsys, $exclude_asns ) ) {
+                continue;
+            }
+
+            if( count( $exclude_tags ) ) {
+                foreach( $c->tags as $tag ) {
+                    if( in_array( $tag->tag, $exclude_tags ) ) {
+                        continue 2;
+                    }
+                }
+            }
+
+            foreach( $c->virtualInterfaces as $vi ) {
+                $iflist = [];
+                $atLeastOnePiIsPeering   = false;
+                $atLeastOnePiIsConnected = false;
+
+                foreach( $vi->physicalInterfaces as $pi ) {
+                    // FIXME: hack for LONAP as they do peering on reseller ports :-(
+                    if( !$pi->switchPort->typePeering() && !$pi->switchPort->typeReseller() ) {
+                        continue;
+                    }
+                    
+                    $atLeastOnePiIsPeering = true;
+                    
+                    if( $pi->statusConnected() ) {
+                        $ifl = [
+                            'switch_id'	=> $pi->switchPort->switchid,
+                            'if_speed'	=> $pi->configuredSpeed(),
+                        ];
+
+                        if( $pi->isRateLimited() ) {
+                            $ifl['if_phys_speed'] = $pi->speed;
+                        }
+
+                        $iflist[] = $ifl;
+
+                        $atLeastOnePiIsConnected = true;
+                    }
+                }
+                
+                if( !$atLeastOnePiIsPeering || !$atLeastOnePiIsConnected ) {
+                    continue;
+                }
+
+                $vlanentries = [];
+                foreach( $vi->vlanInterfaces as $vli ) {
+                    if( $vli->vlan->private || !$vli->vlan->export_to_ixf ) {
+                        continue;
+                    }
+
+                    $vlanentry = [];
+                    $vlanentry[ 'vlan_id' ] = $vli->vlanid;
+
+
+                    foreach( [ 4,6 ] as $protocol ) {
+                        $enabledfn = "ipv{$protocol}enabled";
+                        $ipvaddressfn = "ipv{$protocol}address";
+                        $ipv = "ipv{$protocol}";
+
+                        if( $vli->$enabledfn ) {
+                            $vlanentry[ $ipv ][ 'address' ] = $vli->$ipvaddressfn->address;
+                            if( ( $asmacro = $c->asMacro( $protocol, "AS", true ) ) !== null ) {
+                                $vlanentry[ $ipv ][ 'as_macro' ] = $asmacro;
+                            }
+                            $vlanentry[ $ipv ][ 'routeserver' ] = (bool)$vli->rsclient;
+
+                            $macAddresses = [];
+                            foreach( $vli->layer2addresses as $l2a ) {
+                                $macAddresses[] = wordwrap( $l2a->mac, 2, ':',true);
+                            };
+
+                            $vlanentry[ $ipv ][ 'mac_addresses' ] = $macAddresses;
+
+                            if( !is_null ( $c->maxprefixes ) ) {
+                                $vlanentry[ $ipv ][ 'max_prefix' ] = $c->maxprefixes;
+                            }
+
+
+                            if( $version >= self::EUROIX_JSON_VERSION_0_7 ) {
+                                $services = [];
+                                if( isset( $routeServerIPs[ $vli->vlan->id ] ) && in_array( $vli->$ipvaddressfn->address, $routeServerIPs[ $vli->vlanid ], true ) ) {
+                                    $services[] = 'ixrouteserver';
+                                }
+                                if( isset( $routeCollectorIPs[ $vli->vlanid ] ) && in_array( $vli->$ipvaddressfn->address, $routeCollectorIPs[ $vli->vlanid ], true ) ) {
+                                    $services[] = 'ixroutecollector';
+                                }
+
+                                if( count( $services ) ) {
+                                    $vlanentry[ $ipv ][ 'service_type' ] = $services;
+                                }
+                            }
+
+                            if( $version >= self::EUROIX_JSON_VERSION_1_0 ) {
+                                $services = [];
+
+                                if( isset( $routeServersByIps[ $vli->vlanid ][ $vli->$ipvaddressfn->address ] ) ) {
+                                    /** @var Router $r */
+                                    $r = $routeServersByIps[ $vli->vlanid ][ $vli->$ipvaddressfn->address ];
+                                    $service = new stdClass;
+                                    $service->type           = 'ixrouteserver';
+                                    $service->daemon         = $r->software();
+                                    if( $r->software_version        ) { $service->daemon_version = $r->software_version; }
+                                    if( $r->operating_system       ) { $service->os             = $r->operating_system; }
+                                    if( $r->operating_system_version ) { $service->os_version     = $r->operating_system_version; }
+                                    $services[] = $service;
+                                }
+
+                                if( isset( $routeCollectorsByIps[ $vli->vlanid ][ $vli->$ipvaddressfn->address ] ) ) {
+                                    $r = $routeCollectorsByIps[ $vli->vlanid ][ $vli->$ipvaddressfn->address ];
+                                    /** @var Router $r */
+                                    $service = new stdClass;
+                                    $service->type           = 'ixroutecollector';
+                                    $service->daemon         = $r->software();
+                                    if( $r->software_version        ) { $service->daemon_version = $r->software_version; }
+                                    if( $r->operating_system        ) { $service->os             = $r->operating_system; }
+                                    if( $r->operating_system_version ) { $service->os_version     = $r->operating_system_version; }
+                                    $services[] = $service;
+                                }
+
+                                if( count( $services ) ) {
+                                    $vlanentry[ $ipv ][ 'services' ] = $services;
+                                }
+                            }
+                        }
+                    }
+
+                    $vlanentries[] = $vlanentry;
+                }
+                
+                if( !count( $vlanentries ) ) {
+                    continue;
+                }
+
+                $conn = [];
+
+                $conn['ixp_id']      = $vli->vlan->infrastructureid;
+                $conn['state']       = 'active';
+                $conn['if_list']     = $iflist;
+                $conn['vlan_list']   = $vlanentries;
+
+                $connlist[] = $conn;
+            }
+
+            $memberinfo[ $cnt ] = [
+                'asnum'          => $c->autsys,
+                'member_since'   => \Carbon\Carbon::parse($c->datejoin)->format( 'Y-m-d' ).'T00:00:00Z',
+                'url'            => $c->corpwww,
+                'name'           => $c->name,
+                'peering_policy' => $c->peeringpolicy,
+                'member_type'    => $this->xlateMemberType( $c->type ),
+            ];
+
+            if( $detailed ) {
+                $memberinfo[ $cnt ] = array_merge( $memberinfo[ $cnt ], [
+                    'contact_email' => [ $c->peeringemail ],
+                    'contact_phone' => [ $c->nocphone ],
+                ]);
+
+                if( filter_var($c->nocwww, FILTER_VALIDATE_URL) !== false ) {
+                    $memberinfo[ $cnt ][ 'peering_policy_url' ] = $c->nocwww;
+                }
+
+                if( $c->nochours && strlen( $c->nochours ) ) {
+                    $memberinfo[ $cnt ][ 'contact_hours' ] = $c->nochours;
+                }
+            }
+
+            if( $tags ) {
+                $memberinfo[ $cnt ][ 'ixp_manager' ][ 'tags' ] = [];
+                foreach( $c->tags as $tag ) {
+                    if( !$tag->internal_only || ( Auth::check() && Auth::user()->isSuperUser() ) ) {
+                        $memberinfo[ $cnt ][ 'ixp_manager' ][ 'tags' ][ $tag->tag ] = $tag->display_as;
+                    }
+                }
+                $memberinfo[ $cnt ][ 'ixp_manager' ][ 'in_manrs' ]    = (bool)$c->in_manrs;
+                $memberinfo[ $cnt ][ 'ixp_manager' ][ 'is_reseller' ] = (bool)$c->isReseller;
+                $memberinfo[ $cnt ][ 'ixp_manager' ][ 'is_resold' ]   = $c->reseller ? true : false;
+                if( $c->reseller ) {
+                    $memberinfo[ $cnt ][ 'ixp_manager' ][ 'resold_via_asn' ]   = $c->resellerObject->autsys;
+                }
+            }
+
+            $memberinfo[ $cnt ][ 'connection_list' ] = $connlist;
+
+            $cnt++;
+        }
+
+        return $memberinfo;
+    }
+
+    /**
+     * Translate IXP Manager member types to JSON Export schema types
+     *
+     * @param int $ixpmType
+     *
+     * @return string
+     *
+     * @psalm-return 'ixp'|'other'|'peering'
+     */
+    private function xlateMemberType( int $ixpmType ) : string
+    {
+        switch( $ixpmType ) {
+            case Customer::TYPE_FULL:
+                return 'peering';
+            case Customer::TYPE_INTERNAL:
+                return 'ixp';
+            case Customer::TYPE_PROBONO:
+                return 'peering';
+            default:
+                return 'other';
+        }
+    }
+
+    /**
+     * Filter details if requested by the config
+     * @param array $output
+     * @return array
+     */
+    private function filter( array $output ): array
+    {
+        // switch filters
+        if( $s = config( 'ixp_api.json_export_schema.excludes.switch' ) ) {
+            foreach( explode( '|', $s ) as $exc ) {
+                foreach( $output[ 'ixp_list' ] as $ixid => $ix ) {
+                    foreach( $ix[ 'switch' ] as $sid => $sw ) {
+                        if( isset( $output[ 'ixp_list' ][ $ixid ][ 'switch' ][ $sid ][ $exc ] ) ) {
+                            unset( $output[ 'ixp_list' ][ $ixid ][ 'switch' ][ $sid ][ $exc ] );
+                        }
+                    }
+                }
+            }
+        }
+
+        // ixp filters
+        if( $i = config( 'ixp_api.json_export_schema.excludes.ixp' ) ) {
+            foreach( explode( '|', $i ) as $exc ) {
+                foreach( $output[ 'ixp_list' ] as $ixid => $ix ) {
+                    if( isset( $output[ 'ixp_list' ][ $ixid ][ $exc ] ) ) {
+                        unset( $output[ 'ixp_list' ][ $ixid ][ $exc ] );
+                    }
+                }
+            }
+        }
+
+        // member filters
+        if( $m = config( 'ixp_api.json_export_schema.excludes.member' ) ) {
+            foreach( explode( '|', $m ) as $exc ) {
+                foreach( $output[ 'member_list' ] as $membid => $memb ) {
+                    if( isset( $output[ 'member_list' ][ $membid ][ $exc ] ) ) {
+                        unset( $output[ 'member_list' ][ $membid ][ $exc ] );
+                    }
+                }
+            }
+        }
+
+        // intinfo filters
+        if( $i = config( 'ixp_api.json_export_schema.excludes.intinfo' ) ) {
+            foreach( explode( '|', $i ) as $exc ) {
+                foreach( $output[ 'member_list' ] as $mid => $member ) {
+                    foreach( $member[ 'connection_list' ] as $clid => $connection ) {
+                        foreach( $member[ 'connection_list' ][$clid]['vlan_list'] as $vid => $vlanint ) {
+
+                            if( isset( $output[ 'member_list' ][ $mid ][ 'connection_list' ][ $clid ][ 'vlan_list' ][ $vid ][ 'ipv4' ][ $exc ] ) ) {
+                                unset( $output[ 'member_list' ][ $mid ][ 'connection_list' ][ $clid ][ 'vlan_list' ][ $vid ][ 'ipv4' ][ $exc ] );
+                            }
+
+                            if( isset( $output[ 'member_list' ][ $mid ][ 'connection_list' ][ $clid ][ 'vlan_list' ][ $vid ][ 'ipv6' ][ $exc ] ) ) {
+                                unset( $output[ 'member_list' ][ $mid ][ 'connection_list' ][ $clid ][ 'vlan_list' ][ $vid ][ 'ipv6' ][ $exc ] );
+                            }
+
+                        }
+                    }
+                }
+            }
+        }
+
+        return $output;
+    }
+
+}
diff --git a/app/Utils/Foil/Extensions/Bird.php b/app/Utils/Foil/Extensions/Bird.php
new file mode 100644
index 000000000..088711d55
--- /dev/null
+++ b/app/Utils/Foil/Extensions/Bird.php
@@ -0,0 +1,205 @@
+ Renderer view extensions
+ *
+ * See: http://www.foilphp.it/docs/EXTENDING/CUSTOM-EXTENSIONS.html
+ *
+ * @author     Barry O'Donovan 
+ * @category   Grapher
+ * @package    IXP\Services\Grapher
+ * @copyright  Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee
+ * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
+ */
+class Bird implements ExtensionInterface
+{
+
+    /**
+     * @var
+     */
+    private $args;
+
+    /**
+     * @return void
+     */
+    #[\Override]
+    /**
+     * @return void
+     */
+    public function setup( array $args = [] )
+    {
+        $this->args = $args;
+    }
+
+    /**
+     * @return array
+     *
+     * @psalm-return array
+     */
+    #[\Override]
+    /**
+     * @return array
+     *
+     * @psalm-return array
+     */
+    public function provideFilters()
+    {
+        return [];
+    }
+
+    /**
+     * @return (static|string)[][]
+     *
+     * @psalm-return array{bird: list{static, 'getObject'}}
+     */
+    #[\Override]
+    /**
+     * @return (static|string)[][]
+     *
+     * @psalm-return array{bird: list{static, 'getObject'}}
+     */
+    public function provideFunctions()
+    {
+        return [
+            'bird' => [$this, 'getObject']
+        ];
+    }
+
+
+    public function getObject(): static
+    {
+        return $this;
+    }
+
+    /**
+     * Convert array of prefixes into less specifics
+     * @param array $prefixes
+     * @param int $proto
+     * @param int $max
+     *
+     * @return array
+     */
+    public function prefixExactToLessSpecific( array $prefixes, int $proto = 6, int $max = 48 ): array
+    {
+        foreach( $prefixes as $i => $p ) {
+            [ $net, $mask ] = explode( '/', $p );
+
+            if( $mask > $max ) {
+                // subnet is too small already so we do not allow it
+                unset( $prefixes[$i] );
+                continue;
+            }
+
+            if( $mask != $max ) {
+                $prefixes[ $i ] = $net . '/' . $mask . '{' . $mask . ',' . $max . '}';
+            }
+        }
+
+        return $prefixes;
+    }
+
+    # https://www.iana.org/assignments/bgp-well-known-communities/bgp-well-known-communities.xhtml
+    public static $BGPCS = [
+        '65535:0' => ['GRACEFUL_SHUTDOWN', 'warning'],
+        '65535:1' => ['ACCEPT_OWN', 'info'],
+        '65535:9' => ['STANDBY_PE', 'info'],
+        '65535:666' => ['BLACKHOLE', 'warning'],
+        '65535:65281' => ['NO_EXPORT', 'info'],
+        '65535:65282' => ['NO_ADVERTISE', 'info'],
+        '65535:65283' => ['NO_EXPORT_SUBCONFED', 'info'],
+        '65535:65284' => ['NOPEER', 'info'],
+    ];
+
+    // FIXME: need to find a place for this that allows end users to customise it
+    public static $BGPLCS = [
+        ':1101:1'  => [ 'PREFIX LENGTH TOO LONG', 'danger' ],
+        ':1101:2'  => [ 'PREFIX LENGTH TOO SHORT', 'danger' ],
+        ':1101:3'  => [ 'BOGON', 'danger' ],
+        ':1101:4'  => [ 'BOGON ASN', 'danger' ],
+        ':1101:5'  => [ 'AS PATH TOO LONG', 'danger' ],
+        ':1101:6'  => [ 'AS PATH TOO SHORT', 'danger' ],
+        ':1101:7'  => [ 'FIRST AS NOT PEER AS', 'danger' ],
+        ':1101:8'  => [ 'NEXT HOP NOT PEER IP', 'danger' ],
+        ':1101:9'  => [ 'IRRDB PREFIX FILTERED', 'danger' ],
+        ':1101:10' => [ 'IRRDB ORIGIN AS FILTERED', 'danger' ],
+        ':1101:11' => [ 'PREFIX NOT IN ORIGIN AS', 'danger' ],
+        ':1101:12' => [ 'RPKI UNKNOWN', 'danger' ],
+        ':1101:13' => [ 'RPKI INVALID', 'danger' ],
+        ':1101:14' => [ 'TRANSIT FREE ASN', 'danger' ],
+        ':1101:15' => [ 'TOO MANY COMMUNITIES', 'danger' ],
+
+        ':1000:1'  => [ 'RPKI VALID', 'success' ],
+        ':1000:2'  => [ 'RPKI UNKNOWN', 'info' ],
+        ':1000:3'  => [ 'RPKI NOT CHECKED', 'warning' ],
+
+        ':1001:0'  => [ 'IRRDB INVALID', 'info' ],
+        ':1001:1'  => [ 'IRRDB VALID', 'success' ],
+        ':1001:2'  => [ 'IRRDB NOT CHECKED', 'warning' ],
+        ':1001:3'  => [ 'IRRDB MORE SPECIFIC', 'info' ],
+
+        ':1001:1000'  => [ 'IRRDB FILTERED LOOSE', 'info' ],
+        ':1001:1001'  => [ 'IRRDB FILTERED STRICT', 'info' ],
+        ':1001:1002'  => [ 'IRRDB PREFIX EMPTY', 'warning' ],
+
+        ':1001:1100'  => [ 'FROM IX ROUTESERVER', 'info' ],
+
+        ':1001:1200'  => [ 'SAME AS NEXT HOP', 'info' ],
+    ];
+
+    // FIXME: need to find a place for this that allows end users to customise it
+    public static $BGPLCS_REGEX = [
+        ':101:\d+'  => [ 'PREPEND TO PEERAS - ONCE', 'info' ],
+        ':102:\d+'  => [ 'PREPEND TO PEERAS - TWICE', 'info' ],
+        ':103:\d+'  => [ 'PREPEND TO PEERAS - THREE TIMES', 'info' ],
+    ];
+
+    /**
+     * Get information on a BGP well-known community
+     */
+    public function translateBgpCommunity( string $c ): ?array
+    {
+        return self::$BGPCS[$c] ?? null;
+    }
+
+    /**
+     * Get information on a BGP large community used for filtering / info by IXP Manager
+     */
+    public function translateBgpFilteringLargeCommunity( string $lc ): ?array
+    {
+        if( isset( self::$BGPLCS[$lc] ) ) {
+            return self::$BGPLCS[$lc];
+        }
+
+        foreach( self::$BGPLCS_REGEX as $re => $v ) {
+            if( preg_match( '/^' . $re . '$/', $lc ) ) {
+                return $v;
+            }
+        }
+
+        return null;
+    }
+}
\ No newline at end of file
diff --git a/app/Utils/Foil/Extensions/IXP.php b/app/Utils/Foil/Extensions/IXP.php
new file mode 100644
index 000000000..58702d9e4
--- /dev/null
+++ b/app/Utils/Foil/Extensions/IXP.php
@@ -0,0 +1,465 @@
+ Renderer view extensions
+ *
+ * See: http://www.foilphp.it/docs/EXTENDING/CUSTOM-EXTENSIONS.html
+ *
+ * @author     Barry O'Donovan 
+ * @category   Grapher
+ * @package    IXP\Services\Grapher
+ * @copyright  Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee
+ * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
+ */
+class IXP implements ExtensionInterface
+{
+
+    /**
+     * @var
+     */
+    private $args;
+
+    /**
+     * @return void
+     */
+    #[\Override]
+    /**
+     * @return void
+     */
+    public function setup( array $args = [] )
+    {
+        $this->args = $args;
+    }
+
+    /**
+     * @return array
+     *
+     * @psalm-return array
+     */
+    #[\Override]
+    /**
+     * @return array
+     *
+     * @psalm-return array
+     */
+    public function provideFilters(): array
+    {
+       return [];
+    }
+
+    /**
+     * @return (static|string)[][]
+     *
+     * @psalm-return array{alerts: list{AlertContainer::class, 'html'}, as112UiActive: list{static, 'as112UiActive'}, asNumber: list{static, 'asNumber'}, getCountriesSelection : list{static, 'getCountriesSelection'}, getSelectOptions: list{static, 'getSelectOptions'}, google2faAuthenticator: list{static, 'google2faAuthenticator'}, logoManagementEnabled: list{static, 'logoManagementEnabled'}, maxFileUploadSize: list{static, 'maxFileUploadSize'}, nagiosHostname: list{static, 'nagiosHostname'}, nakedUrl: list{static, 'nakedUrl'}, resellerMode: list{static, 'resellerMode'}, scaleBits: list{static, 'scaleBits'}, scaleBytes: list{static, 'scaleBytes'}, scaleSpeed: list{static, 'scaleSpeed'}, scaleFilesize: list{static, 'scaleFilesize'}, softwrap: list{static, 'softwrap'}, whoisPrefix: list{static, 'whoisPrefix'}}
+     */
+    #[\Override]
+    public function provideFunctions(): array
+    {
+        return [
+            'alerts'                 => [ AlertContainer::class, 'html' ],
+            'as112UiActive'          => [ $this, 'as112UiActive' ],
+            'asNumber'               => [ $this, 'asNumber' ],
+            'getCountriesSelection'  => [ $this, 'getCountriesSelection' ],
+            'getSelectOptions'       => [ $this, 'getSelectOptions' ],
+            'google2faAuthenticator' => [ $this, 'google2faAuthenticator' ],
+            'logoManagementEnabled'  => [ $this, 'logoManagementEnabled' ],
+            'maxFileUploadSize'      => [ $this, 'maxFileUploadSize' ],
+            'nagiosHostname'         => [ $this, 'nagiosHostname' ],
+            'nakedUrl'               => [ $this, 'nakedUrl' ],
+            'resellerMode'           => [ $this, 'resellerMode' ],
+            'scaleBits'              => [ $this, 'scaleBits' ],
+            'scaleBytes'             => [ $this, 'scaleBytes' ],
+            'scaleSpeed'             => [ $this, 'scaleSpeed' ],
+            'scaleFilesize'          => [ $this, 'scaleFilesize' ],
+            'softwrap'               => [ $this, 'softwrap' ],
+            'whoisPrefix'            => [ $this, 'whoisPrefix' ],
+        ];
+    }
+
+    /**
+     * Max file upload size
+     *
+     * Inspired by: http://stackoverflow.com/questions/13076480/php-get-actual-maximum-upload-size
+     *
+     * @return string
+     */
+    public function maxFileUploadSize(): string
+    {
+        static $max_size = null;
+
+        $parseSize = /**
+         * @return numeric-string
+         */
+        function( $size ): string {
+            $unit = preg_replace( '/[^bkmgtpezy]/i', '', $size ); // Remove the non-unit characters from the size.
+            $size = preg_replace( '/[^0-9\.]/',      '', $size ); // Remove the non-numeric characters from the size.
+
+            if( $unit ) {
+                // Find the position of the unit in the ordered string which is the power of magnitude to multiply a kilobyte by.
+                return (string)round( $size * ( 1024 ** stripos( 'bkmgtpezy', $unit[0] ) ) );
+            }
+
+            return (string)round( $size * 1 );
+        };
+
+        if( $max_size === null ) {
+            $max_size = $parseSize( ini_get('post_max_size') );
+
+            // If upload_max_size is less, then reduce. Except if upload_max_size is
+            // zero, which indicates no limit.
+            $upload_max = $parseSize( ini_get('upload_max_filesize') );
+            if ($upload_max > 0 && $upload_max < $max_size) {
+                $max_size = $upload_max;
+            }
+        }
+
+        return $this->scale( $max_size, 'bytes' );
+    }
+
+    /**
+     * Scale function
+     *
+     * This function will scale a number to (for example for traffic
+     * measured in bits/second) to Kbps, Mbps, Gbps or Tbps; or data.
+     * measured in bytes to KB, MB, GB or TB.
+     *
+     * Valid string formats ($strFormats) and what they return are:
+     *    bytes               => Bytes, KBytes, MBytes, GBytes, TBytes
+     *    pkts / errs / discs => pps, Kpps, Mpps, Gpps, Tpps
+     *    bits / *            => bits, Kbits, Mbits, Gbits, Tbits
+     *
+     * Valid return types ($format) are:
+     *    0 => fully formatted and scaled value. E.g.  12,354.235 Tbits
+     *    1 => scaled value without string. E.g. 12,354.235
+     *    2 => just the string. E.g. Tbits
+     *
+     * @param float  $v          The value to scale
+     * @param string $format     The format to sue (as above: bytes / pkts / errs / etc )
+     * @param int    $decs       Number of decimals after the decimal point. Defaults to 3.
+     * @param int    $returnType Type of string to return. Valid values are listed above. Defaults to 0.
+     *
+     * @return string            Scaled / formatted number / type.
+     */
+    private function scale( float $v, string $format, int $decs = 3, int $returnType = 0 ): string
+    {
+        if( $format === 'bytes' ) {
+            $formats = [
+                'Bytes', 'KBytes', 'MBytes', 'GBytes', 'TBytes'
+            ];
+        } else if( in_array( $format, [ 'pkts', 'errs', 'discs', 'bcasts' ] ) ) {
+            $formats = [
+                'pps', 'Kpps', 'Mpps', 'Gpps', 'Tpps'
+            ];
+        } else if( $format === 'speed' ) {
+            $formats = [
+                'Mbps', 'Gbps', 'Tbps'
+            ];
+        } else {
+            $formats = [
+                'bits', 'Kbits', 'Mbits', 'Gbits', 'Tbits'
+            ];
+        }
+
+        $num_formats = count( $formats );
+        for( $i = 0; $i < $num_formats; $i++ ) {
+            $format = $i > 4 ? $formats[ 4 ] : $formats[ $i ];
+            if( ( $v / 1000.0 < 1.0 ) || ( $num_formats === $i + 1 ) ) {
+                if( $returnType === 0 ) {
+                    return number_format( $v, $decs ) . ' ' . $format;
+                }
+
+                if( $returnType === 1 ) {
+                    return number_format( $v, $decs );
+                }
+
+                return $format;
+            }
+
+            $v /= 1000.0;
+        }
+
+        return (string)$v;
+    }
+
+    /**
+     * See scale above
+     * @param float $v
+     * @param int $decs
+     *
+     * @return string
+     */
+    public function scaleBits( float $v, int $decs = 3 ): string
+    {
+        return $this->scale( $v, 'bits', $decs );
+    }
+
+    /**
+     * See scale above
+     * @param float $v
+     * @param int $decs
+     *
+     * @return string
+     */
+    public function scaleSpeed( float $v, int $decs = 0 ): string
+    {
+        return $this->scale( $v, 'speed', $decs );
+    }
+
+
+    /**
+     * See scale above
+     * @param float $v
+     * @param int $decs
+     *
+     * @return string
+     */
+    public function scaleBytes( float $v, int $decs = 3 ): string
+    {
+        return $this->scale( $v, 'bytes', $decs );
+    }
+
+    /**
+     * Scale a size in bytes in human style filesize
+     *
+     * @param int  $bytes          The value to scale
+     *
+     * @return string Scaled / formatted number / type.
+     */
+    public function scaleFilesize( int $bytes ): string
+    {
+        if( $bytes >= 1073741824 ) {
+            return number_format( $bytes / 1073741824, 2 ) . ' GB';
+        }
+
+        if( $bytes >= 1048576 ) {
+            return number_format( $bytes / 1048576, 2 ) . ' MB';
+        }
+
+        if( $bytes >= 1024 ) {
+            return number_format( $bytes / 1024, 2 ) . ' KB';
+        }
+
+        return $bytes . ' bytes';
+    }
+
+
+    /**
+    * Soft wrap
+    *
+    * Print an array of data separated by $elementSeparator within the same line and only
+    * print $perline elements per line terminated each line with $lineEnding (and an implicit \n).
+    *
+    * Set $indent to indent //subsequent// lines (i.e. not the first)
+    *
+    * @param array  $data
+    * @param int    $perline
+    * @param string $elementSeparator
+    * @param string $lineEnding
+    * @param int    $indent
+    * @param int    $pad
+     *
+    * @return string            Scaled / formatted number / type.
+    */
+    public function softwrap( array $data, int $perline, string $elementSeparator, string $lineEnding, int $indent = 0, int $pad = 0 ): string
+    {
+        if( !( $cnt = count( $data ) ) ) {
+            return '';
+        }
+
+        $itrn = 0;
+        $str  = '';
+
+        foreach( $data as $d ) {
+            if( $itrn === $cnt ) {
+                break;
+            }
+
+            if( $itrn === 0 && $cnt > 1 && $perline === 1 ) {
+                $str .= $d . $lineEnding . "\n" . str_repeat(' ', $indent);
+            } else if( ($itrn+1) !== $cnt && ($itrn+1) % $perline !== 0 ) {
+                $str .= str_pad( $d . $elementSeparator, $pad );
+            } else if( $itrn > 0 && ($itrn+1) !== $cnt && ($itrn+1) % $perline == 0 ) {
+                $str .= $d . $lineEnding . "\n" . str_repeat( ' ', $indent );
+            } else {
+                $str .= $d;
+            }
+
+            $itrn++;
+        }
+
+        return $str;
+    }
+
+
+    /**
+     * Get a consistent hostname for a given member VLAN interface
+     *
+     * @param string $abbreviatedName Customer's abbreviated name
+     * @param int    $asn             Customer's ASN
+     * @param int    $protocol        Protocol
+     * @param int    $vlanid          VLAN ID
+     * @param int    $vliid           VLAN Interface ID
+     *
+     * @return string
+     */
+    public function nagiosHostname( string $abbreviatedName, int $asn, int $protocol, int $vlanid, int $vliid ): string
+    {
+        return preg_replace( '/[^a-zA-Z0-9]/', '-', strtolower( $abbreviatedName ) ) . '-as' . $asn . '-ipv' . $protocol . '-vlanid' . $vlanid . '-vliid' . $vliid;
+    }
+
+    /**
+     * Checks if reseller mode is enabled.
+     *
+     * To enable reseller mode set the env variable IXP_RESELLER_ENABLED
+     *
+     * @see https://docs.ixpmanager.org/latest/features/reseller/
+     *
+     * @return bool
+     */
+    public function resellerMode(): bool
+    {
+        return (bool)config( 'ixp.reseller.enabled', false );
+    }
+
+    /**
+     * Checks if logo management is enabled
+     *
+     * To enable logos in the UI set IXP_FE_FRONTEND_DISABLED_LOGO=false in .env
+     *
+     * @return bool
+     */
+    public function logoManagementEnabled(): bool
+    {
+        return !(bool)config( 'ixp_fe.frontend.disabled.logo' );
+    }
+
+    /**
+     * Checks if as112 is activated in the UI.
+     *
+     * To disable as112 in the UI set the env variable IXP_AS112_UI_ACTIVE
+     *
+     * @see https://docs.ixpmanager.org/latest/features/as112/
+     *
+     * @return bool
+     */
+    public function as112UiActive(): bool
+    {
+        return (bool)config( 'ixp.as112.ui_active', false );
+    }
+
+    /**
+     * Replaces an AS  Number with some JS magic to invoke a bootbox.
+     *
+     * @param  int    $asn      The AS number
+     * @param  bool   $addAs    Do we need to add AS?
+     *
+     * @return string
+     */
+    public function asNumber( $asn, $addAs = true ): string
+    {
+        if( Auth::check() && $asn ) {
+            return '' . ( $addAs ? 'AS' : '' ) . $asn . '';
+        }
+
+        return ( $addAs ? 'AS' : '' ) . $asn;
+    }
+
+    /**
+     * Replaces an IP prefix with some JS magic to invoke a bootbox.
+     *
+     * @param $prefix
+     * @param bool $subnet
+     *
+     * @return string
+     */
+    public function whoisPrefix( $prefix, $subnet = true ): string
+    {
+        if( Auth::check() && $prefix ) {
+            return '' . $prefix . '';
+        }
+
+        return $prefix;
+    }
+
+    /**
+     * Takes a URL with https://xxx/ and returns xxx
+     *
+     * @param  string $url      The URL
+     * @return string
+     */
+    public function nakedUrl( string $url ): string
+    {
+        $url = preg_replace( '/^http[s]?:\/\//', '', $url );
+        return preg_replace( '/\/$/', '', $url ) ?? '';
+    }
+
+    /**
+     * Get Google Authenticator
+     *
+     * @return GoogleAuthenticator
+     */
+    public function google2faAuthenticator(): GoogleAuthenticator
+    {
+        return new GoogleAuthenticator( request() );
+    }
+
+
+    /**
+     * Get an array of countries for a select dropdown.
+     *
+     * @return array in form [ISO 2-letter Code] => "Name"
+     */
+    public function getCountriesSelection(): array {
+        $countries = Countries::getList();
+        $list = [];
+        foreach($countries as $country) {
+            $list[$country['iso_3166_2']] = $country['name'];
+        }
+        return $list;
+    }
+
+    /**
+     * Create an option list for a select input element
+     *
+     * @return array
+     */
+    public function getSelectOptions( string $model, string $key, string $value  ): array {
+
+        /** @var \Illuminate\Database\Eloquent\Model $model */
+        return $model::select( $key, $value )->pluck( $value, $key )->toArray();
+    }
+
+}
diff --git a/app/Utils/Former/Framework/TwitterBootstrap4.php b/app/Utils/Former/Framework/TwitterBootstrap4.php
new file mode 100644
index 000000000..d867c3f6b
--- /dev/null
+++ b/app/Utils/Former/Framework/TwitterBootstrap4.php
@@ -0,0 +1,196 @@
+
+ * @author     Yann Robin 
+ * @copyright  Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee
+ * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
+ */
+class TwitterBootstrap4 extends FormerTwitterBootstrap4
+{
+    /**
+     * Create a new TwitterBootstrap instance
+     *
+     * @param Application $app
+     */
+    public function __construct( Application $app )
+    {
+        parent::__construct($app);
+    }
+
+    /**
+     * Wrap a field with potential additional tags
+     *
+     *
+     * @param $field
+     *
+     * @return Element A wrapped field
+     * @psalm-suppress UndefinedDocblockClass - Former Container type confusing psalm
+     */
+    #[\Override]
+    public function wrapField( $field ): Element
+    {
+        $width = $this->app[ 'former.form' ]->getAttributes()[ 'custom-input-width-class' ] ?? $this->fieldWidth;
+
+        if( $this->app[ 'former.form' ]->isOfType( 'horizontal' ) ) {
+            return Element::create('div', $field )->addClass( $width );
+        }
+        return $field;
+    }
+
+    /**
+     * Add label classes
+     *
+     * @psalm-suppress UndefinedDocblockClass - Former Container type confusing psalm
+     */
+    #[\Override]
+    public function getLabelClasses()
+    {
+        $width = $this->app[ 'former.form' ]->getAttributes()[ 'custom-label-width-class' ] ?? $this->labelWidth;
+
+        if( $this->app[ 'former.form' ]->isOfType( 'horizontal' ) ) {
+            return [ 'control-label', $width ];
+        }
+        if ( $this->app[ 'former.form' ]->isOfType( 'inline' ) ) {
+            return [ 'sr-only' ];
+        }
+
+        return [ 'control-label' ];
+    }
+
+    /**
+     * Render an help text
+     *
+     * @param string $text
+     * @param array  $attributes
+     *
+     * @return Element
+     */
+    #[\Override]
+    public function createBlockHelp( $text, $attributes = array() )
+    {
+        return Element::create('small', $text, $attributes )
+            ->addClass('form-text text-muted former-help-text' );
+    }
+
+    /**
+     * Wrap an item to be prepended or appended to the current field
+     *
+     * @return Element A wrapped item
+     */
+    #[\Override]
+    public function placeAround( $item, $place = null ): Element
+    {
+        // Render object
+        if( is_object( $item ) && method_exists( $item, '__toString' ) ) {
+            $item = $item->__toString();
+        }
+
+        // Get class to use
+        $class = ( strpos( $item, 'addClass('input-group-' . $class );
+    }
+
+    /**
+     * Add group classes
+     *
+     * @return string A list of group classes
+     * @psalm-suppress UndefinedDocblockClass - Former Container type confusing psalm
+     */
+    #[\Override]
+    public function getGroupClasses(): string
+    {
+        $classes = 'form-group ';
+        if( $this->app[ 'former.form' ]->isOfType( 'horizontal' ) ){
+            $classes .= 'row ';
+        }
+
+        if( isset( $this->app[ 'former.form' ]->getAttributes()[ 'inputs-position' ] ) ){
+            $classes .= $this->app['former.form']->getAttributes()[ 'inputs-position' ];
+        }
+
+        return $classes;
+    }
+
+    /**
+     * Add actions block class
+     *
+     * @return null|string
+     *
+     * @psalm-suppress UndefinedDocblockClass - Former Container type confusing psalm
+     */
+    #[\Override]
+    public function getActionClasses(): ?string
+    {
+        if( $this->app[ 'former.form' ]->isOfType( 'horizontal' ) || $this->app[ 'former.form' ]->isOfType( 'inline' ) ) {
+            $classes = 'form-group';
+
+            if( isset( $this->app[ 'former.form' ]->getAttributes()[ 'inputs-positions' ] ) ){
+                $classes .= $this->app[ 'former.form' ]->getAttributes()[ 'inputs-positions' ];
+            }
+
+            if( isset( $this->app[ 'former.form' ]->getAttributes()[ 'action-buttons-custom-class' ] ) &&  $this->app[ 'former.form' ]->getAttributes()[ 'action-buttons-custom-class' ] === "grey-box" ){
+                $classes .= " col-sm-12";
+            } else{
+                $classes .= " row";
+            }
+            return $classes;
+        }
+        return null;
+    }
+
+    /**
+     * Wrap actions block with potential additional tags
+     *
+     * @param  $actions
+     *
+     * @return string A wrapped actions block
+     * @psalm-suppress UndefinedDocblockClass - Former Container type confusing psalm
+     */
+    #[\Override]
+    public function wrapActions( $actions ): string
+    {
+        // For horizontal forms, we wrap the actions in a div
+        if( $this->app[ 'former.form' ]->isOfType( 'horizontal' ) ) {
+            $class = $this->app[ 'former.form' ]->getAttributes()[ 'action-buttons-custom-class' ] ?? "";
+
+            if( $class === 'grey-box' ){
+                return Element::create('div', $actions )->addClass( "bg-light p-4 mt-4 shadow-sm text-center col-lg-12" );
+            }
+            return  Element::create('div', $actions )->addClass( $this->fieldOffset.' '.$this->fieldWidth.' '.$class );
+        }
+
+        return $actions;
+    }
+}
\ No newline at end of file
diff --git a/app/Utils/Grapher/Dummy.php b/app/Utils/Grapher/Dummy.php
new file mode 100644
index 000000000..177f4eb59
--- /dev/null
+++ b/app/Utils/Grapher/Dummy.php
@@ -0,0 +1,109 @@
+
+ * @author     Barry O'Donovan 
+ * @author     Yann Robin 
+ * @category   IXP
+ * @package    IXP\Utils\Grapher
+ * @copyright  Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee
+ * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
+ */
+class Dummy extends Mrtg
+{
+    /**
+     * Class constructor.
+     *
+     * @param string $file The MRTG log file to load for analysis
+     */
+    public function __construct( string $file )
+    {
+        parent::__construct( $file );
+    }
+
+    /**
+     * From the data loaded from an MRTG log file, process it and  and return it in the same format
+     * as loadMrtgFile().
+     *
+     * @param Graph $graph
+     *
+     * @return array
+     *
+     * @throws GeneralException
+     *
+     * @see IXP\Utils\Grapher\Mrtg::loadMrtgFile()
+
+     * Processing means:
+     * - only returning the values for the requested period
+     * - MRTG provides traffic as bytes, change to bits
+     *
+     * @psalm-return array, mixed>
+     */
+    #[\Override]
+    public function data( Graph $graph ): array
+    {
+        $values = [];
+
+        if( !( $periodsecs = $this->getPeriodTime( $graph->period() ) ) ) {
+            throw new GeneralException('Invalid period');
+        }
+
+        $starttime  = $this->array[0][0] - $periodsecs;
+        $endtime    = $this->array[0][0];
+
+        // Run through the array and pull out the values we want
+        for( $i = sizeof( $this->array )-1; $i >= 0; $i-- ) {
+            // process within start / end time
+            if( ($this->array[ $i ][ 0 ] >= $starttime) && ($this->array[ $i ][ 0 ] <= $endtime) ) {
+                $values[] = $this->array[ $i ]; //(int)floor( $this->array[ $i ] * ( 1.0 + ( (float)mt_rand( 1, 20 ) / 100.0 ) ) );
+            }
+        }
+
+        // convert bytes to bits
+        if( $graph->category() === Graph::CATEGORY_BITS ) {
+            foreach( $values as $i => $v ) {
+                $values[$i][1] *= 8;
+                $values[$i][2] *= 8;
+                $values[$i][3] *= 8;
+                $values[$i][4] *= 8;
+            }
+        } else if( in_array( $graph->category(), [ Graph::CATEGORY_ERRORS, Graph::CATEGORY_DISCARDS ], false ) ) {
+            foreach( $values as $i => $v ) {
+                $values[$i][1] *= (int)floor( ( mt_rand( 0, 10 ) / 1000.0 ) );
+                $values[$i][2] *= (int)floor( ( mt_rand( 0, 10 ) / 1000.0 ) );
+                $values[$i][3]  = (int)floor( $values[$i][1] * 0.1 );
+                $values[$i][4]  = (int)floor( $values[$i][2] * 0.1 );
+            }
+        }
+        return $values;
+    }
+}
\ No newline at end of file
diff --git a/app/Utils/Grapher/Mrtg.php b/app/Utils/Grapher/Mrtg.php
new file mode 100644
index 000000000..25014557c
--- /dev/null
+++ b/app/Utils/Grapher/Mrtg.php
@@ -0,0 +1,246 @@
+
+ * @author     Barry O'Donovan 
+ * @author     Yann Robin 
+ * @category   IXP
+ * @package    IXP\Utils\Grapher
+ * @copyright  Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee
+ * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
+ */
+class Mrtg
+{
+    /**
+     * the absolute path for the log file under investigation
+     * @var string
+     */
+    protected $file = null;
+
+    /**
+     * Array of MRTG log data from the file
+     * @var array
+     */
+    protected $array = null;
+
+    /**
+     * Period times.
+     *
+     * these values are taken from mrtg/src/rateup.c
+     */
+    public const PERIOD_TIME = [
+        Graph::PERIOD_DAY     => 119988.0,     // ( 33.33 * 3600 ),
+        Graph::PERIOD_WEEK    => 719712.0,     // ( 8.33  * 24 * 3600 ),
+        Graph::PERIOD_MONTH   => 2879712.0,    // ( 33.33 * 24 * 3600 ),
+        Graph::PERIOD_YEAR    => 31622400.0,   // ( 366 * 24 * 3600 )
+        Graph::PERIOD_CUSTOM  => 86400.0,      // ( placeholder - it will calculated by custom dates )
+    ];
+
+    /**
+     * Mrtg / SNMP options for the different traffic types
+     *
+     * @var array
+     */
+    public const TRAFFIC_TYPES = [
+        Graph::CATEGORY_BITS   => [
+            'in'      => 'oidInOctets',
+            'out'     => 'oidOutOctets',
+            'options' => 'growright,bits',
+            'name'    => 'Bits'
+        ],
+        Graph::CATEGORY_PACKETS   => [
+            'in'      => 'oidInUnicastPackets',
+            'out'     => 'oidOutUnicastPackets',
+            'options' => 'growright',
+            'name'    => 'Packets'
+        ],
+        Graph::CATEGORY_ERRORS   => [
+            'in'      => 'oidInErrors',
+            'out'     => 'oidOutErrors',
+            'options' => 'growright',
+            'name'    => 'Errors'
+        ],
+        Graph::CATEGORY_DISCARDS   => [
+            'in'      => 'oidInDiscards',
+            'out'     => 'oidOutDiscards',
+            'options' => 'growright',
+            'name'    => 'Discards'
+        ],
+        Graph::CATEGORY_BROADCASTS  => [
+            'in'      => 'oidInBroadcasts',
+            'out'     => 'oidOutBroadcasts',
+            'options' => 'growright',
+            'name'    => 'Broadcasts'
+        ],
+    ];
+
+    /**
+     * Class constructor.
+     *
+     * @param string $file The MRTG log file to load for analysis
+     */
+    public function __construct( string $file )
+    {
+        $this->file  = $file;
+        $this->array = $this->loadMrtgFile();
+    }
+
+    /**
+     * Accessor for PERIOD_TIME
+     *
+     * @param string $period
+     * @return float
+     */
+    public function getPeriodTime( string $period ): float
+    {
+        return self::PERIOD_TIME[ $period ] ?? 0.0;
+    }
+
+    /**
+     * Load data from an MRTG log file and return it as an indexed array
+     * of associative arrays where the five elements of these arrays are just like the
+     * MRTG log file:
+     *
+     * [
+     * [
+     * 0 =>  unixtime stamp
+     * 1 =>  average incoming rate
+     * 2 =>  average outgoing rate
+     * 3 =>  maximum incoming rate
+     * 4 =>  maximum outgoing rate
+     * ],
+     * ....
+     * ]
+     *
+     * The above will be ordered with the newest first as per the log file.
+     *
+     * @return int[][]
+     *
+     * @throws
+     *
+     * @psalm-return list
+     */
+    protected function loadMrtgFile(): array
+    {
+        $values  = [];
+
+        if( !( $fd = @fopen( $this->file, "r" ) ) ) {
+            throw new FileErrorException("Could not open: {$this->file}");
+        }
+
+        while( $record = fgets( $fd, 4096 ) ) {
+            $data = explode( " ", trim( $record ) );
+
+            if( count( $data ) >= 3 ) {
+                $values[] = [ (int)$data[0], (int)$data[1], (int)$data[2], isset($data[3]) ? (int)$data[3] : 0, isset($data[4]) ? (int)$data[4] : 0 ];
+            }
+        }
+
+        // drop the first record as it's traffic counters from the most recent run of mrtg.
+        array_shift( $values );
+        fclose( $fd );
+
+        return $values;
+    }
+
+    /**
+     * From the data loaded from an MRTG log file, process it and  and return it in the same format
+     * as loadMrtgFile().
+     *
+     * @param Graph $graph
+     *
+     * @return array
+     *
+     * @throws GeneralException
+     *
+     * @see IXP\Utils\Grapher\Mrtg::loadMrtgFile()
+
+     * Processing means:
+     * - only returning the values for the requested period
+     * - MRTG provides traffic as bytes, change to bits
+     *
+     * @psalm-return array, mixed>
+     */
+    public function data( Graph $graph ): array
+    {
+        $values = [];
+
+        if( !( $periodsecs = $this->getPeriodTime( $graph->period() ) ) ) {
+            throw new GeneralException('Invalid period');
+        }
+
+        if($graph->period() === Graph::PERIOD_CUSTOM) {
+            $starttime = $graph->periodStart()->timestamp;
+            $endtime   = $graph->periodEnd()->timestamp;
+        } else {
+            $starttime  = time() - $periodsecs;
+            $endtime    = time();
+        }
+
+        // Run through the array and pull out the values we want
+        for( $i = sizeof( $this->array )-1; $i >= 0; $i-- ) {
+            // process within start / end time
+            if( ($this->array[ $i ][ 0 ] >= $starttime) && ($this->array[ $i ][ 0 ] <= $endtime) ) {
+                $values[] = $this->array[ $i ];
+            }
+        }
+
+        // convert bytes to bits
+        if( $graph->category() === Graph::CATEGORY_BITS ) {
+            foreach( $values as $i => $v ) {
+                $values[$i][1] *= 8;
+                $values[$i][2] *= 8;
+                $values[$i][3] *= 8;
+                $values[$i][4] *= 8;
+            }
+        }
+        return $values;
+    }
+
+    /**
+     * Accessor method for $array - the data from the MRTG file.
+     *
+     * @return array The data from the MRTG file
+     */
+    public function getArray(): array
+    {
+        return $this->array;
+    }
+}
\ No newline at end of file
diff --git a/app/Utils/Grapher/Rrd.php b/app/Utils/Grapher/Rrd.php
new file mode 100644
index 000000000..a63d8830e
--- /dev/null
+++ b/app/Utils/Grapher/Rrd.php
@@ -0,0 +1,486 @@
+
+ * @package Grapher
+ */
+class Rrd
+{
+    /**
+     * the absolute path for the RRD file under investigation
+     * @var string
+     */
+    protected $realfile = null;
+
+    /**
+     * the absolute path for the local copy of the RRD file under investigation
+     *
+     * **WARNING:** this is deleted after processing if set!
+     *
+     * @var string
+     */
+    protected $localfile = null;
+
+    /**
+     * the absolute path to the file we're actually using (real / local)
+     *
+     * @var string
+     */
+    protected $file = null;
+
+
+    /**
+     * Start time of RRD data (as returned by rrd_fetch)
+     * @var int
+     */
+    protected $start = null;
+
+    /**
+     * End time of RRD data (as returned by rrd_fetch)
+     * @var int
+     */
+    protected $end = null;
+
+    /**
+     * Step interval RRD data (as returned by rrd_fetch)
+     * @var int
+     */
+    protected $step = null;
+
+    /**
+     * Data from RRD file formatted as a MRTG Log array as returned by
+     * IXP\Utils\Grapher\Mrtg
+     *
+     * @var array
+     */
+    protected $array = null;
+
+    /**
+     * Graph object under consideration
+     * @var Graph
+     */
+    protected $graph = null;
+
+    /**
+     * Period times.
+     *
+     * these values are taken from mrtg/src/rateup.c
+     */
+    public const PERIOD_TIME = Mrtg::PERIOD_TIME;
+
+
+    /**
+     * Prefix for local cached files
+     */
+    public const LOCAL_CACHE_RRD_PREFIX = "ixp-utils-rrd-";
+
+    /**
+     * Class constructor.
+     *
+     * @param  string  $file  The RRD log file to load for analysis
+     * @param  Graph  $graph  The graph object
+     *
+     * @throws
+     */
+    public function __construct( string $file, Graph $graph )
+    {
+        $this->realfile  = $file;
+        $this->graph     = $graph;
+        $this->loadRrdFile();
+    }
+
+    /**
+     * Class destructor
+     */
+    public function __destruct()
+    {
+        $this->deleteLocalCopies();
+    }
+
+    /**
+     * Get the full path to the RRD file being used
+     *
+     * Could be the original "real file" or a local copy
+     *
+     * @return string
+     */
+    public function file(): string
+    {
+        return $this->file;
+    }
+
+    /**
+     * Accessor for PERIOD_TIME
+     *
+     * @param string $period
+     * @return float
+     */
+    public function getPeriodTime( string $period ): float
+    {
+        return self::PERIOD_TIME[ $period ] ?? 0.0;
+    }
+
+    /**
+     * Access for the graph object under consideration
+     *
+     * @return Graph
+     */
+    private function graph(): Graph
+    {
+        return $this->graph;
+    }
+
+    /**
+     * Get the local directory name.
+     *
+     * @see getLocalCopy() for detals
+     * @see getLocalFilename() for detals
+     *
+     * @throws
+     *
+     * @psalm-return '/Users/barryo/dev/ixpm-inex/storage/grapher'
+     */
+    private function getLocalDirectory(): string
+    {
+        // use Laravel's storage if possible
+        if( !function_exists( 'storage_path' ) ) {
+            throw new FileErrorException("Could not identify storage directory - fn storage_path() not defined");
+        }
+
+        $dir = storage_path() . '/grapher';
+        if ( !file_exists( $dir ) ) {
+            if( !@mkdir( $dir, 0770, true ) ) {
+                throw new FileErrorException("Could not create local RRD storage directory");
+            }
+        }
+        return $dir;
+    }
+
+    /**
+     * Get the local file name.
+     *
+     * @see getLocalCopy() for detals
+     *
+     * @param string $ext The extension
+     *
+     * @throws
+     *
+     * @psalm-param 'png'|'rrd' $ext
+     */
+    private function getLocalFilename( string $ext = 'rrd' ): string
+    {
+        return "{$this->getLocalDirectory()}/" . self::LOCAL_CACHE_RRD_PREFIX . "{$this->graph()->key()}.{$ext}";
+    }
+
+    /**
+     * If necessary, copy a remote rrd to a local file (temporarily)
+     *
+     * Most PHP file functions support URI's such as http://.
+     *
+     * Naturally, rrd_* is an exception :-(
+     *
+     * This function creates a local copy.
+     *
+     * @see removeLocalCopies()
+     *
+     * @return string The full path to the local copy
+     *
+     * @throws
+     */
+    private function getLocalCopy(): string
+    {
+        // if it's already local, just return
+        if( strpos($this->realfile, 'http') !== 0) {
+            $this->file = $this->realfile;
+            return $this->file;
+        }
+
+        $this->file = $this->localfile = $this->getLocalFilename();
+
+        // does the local file exist and is it less than 5mins old?
+        if( !file_exists($this->localfile) || !( time() - filemtime($this->localfile) < 300 ) ) {
+            if( !( ( $r = @file_get_contents( $this->realfile ) ) && @file_put_contents( $this->localfile, $r ) ) ) {
+                throw new FileErrorException("Could not create local RRD copy");
+            }
+        }
+
+        return $this->localfile;
+    }
+
+    /**
+     * If necessary, delete local copies of a remote rrds
+     *
+     * @see getLocalCopy() for an explanation
+     */
+    private function deleteLocalCopies(): void
+    {
+        $files = glob( $this->getLocalDirectory() . "/" . self::LOCAL_CACHE_RRD_PREFIX . "*" );
+        $now   = time();
+
+        foreach( $files as $file ) {
+            if( is_file($file) && ( $now - filemtime( $file ) >= 300 ) ) {
+                @unlink($file);
+            }
+        }
+    }
+
+    /**
+     * Prepare RRD file
+     *
+     * @throws
+     */
+    protected function loadRrdFile(): void
+    {
+        // we need to allow for remote files but php's rrd_* functions don't support this
+        $this->getLocalCopy();
+    }
+
+    /**
+     * Get the RRD file
+     *
+     * @throws
+     */
+    public function rrd(): string
+    {
+        if( !( $rrd = @file_get_contents($this->file) ) ) {
+            throw new FileErrorException("Could not read RRD file [{$this->file}]");
+        }
+        return $rrd;
+    }
+
+    /**
+     * The MRTG files have different data source names than the sflow files.
+     *
+     * This returns the appropriate keys to use when indexing arrays.
+     *
+     * @return string[]
+     *
+     * @psalm-return list{'ds0'|'traffic_in', 'ds1'|'traffic_out'}
+     */
+    private function getIndexKeys(): array
+    {
+        if( $this->graph()->backend()->name() === 'mrtg' ) {
+            return [ 'ds0', 'ds1' ];  // in out
+        }
+
+        return [ 'traffic_in', 'traffic_out' ];
+    }
+
+    /**
+     * From the RRD file, process the data and return it in the same format
+     * as an MRTG log file.
+     *
+     * @see \IXP\Utils\Grapher\Mrtg::loadMrtgFile()
+
+     * Processing means:
+     * - only returning the values for the requested period
+     * - MRTG/RRD provides traffic as bytes, change to bits
+     *
+     * @return int[][]
+     *
+     * @throws
+     *
+     * @psalm-return array, list{int, int, int, int, int}>
+     */
+    public function data(): array
+    {
+        if($this->graph()->period() === Graph::PERIOD_CUSTOM) {
+
+            if( !$this->graph()->periodStart() || !$this->graph()->periodEnd() ) {
+                throw new ParameterException('Period Start and Period End must be set for custom period graphs');
+            }
+
+            return $this->dataWindow(
+                $this->graph()->periodStart()->timestamp,
+                $this->graph()->periodEnd()->timestamp
+            );
+
+        } else {
+
+            return $this->dataWindow(time() - self::PERIOD_TIME[ $this->graph()->period() ], time());
+
+        }
+    }
+
+    /**
+     * @param integer $start : timestamp
+     * @param integer $end : timestamp
+     *
+     * @return int[][]
+     *
+     * @throws FileErrorException
+     *
+     * @psalm-return array, list{int, int, int, int, int}>
+     */
+    public function dataWindow( int $start, int $end): array
+    {
+        $rrd = rrd_fetch( $this->file, [
+            'AVERAGE',
+            '--start', $start,
+            '--end', $end,
+        ]);
+
+        if( $rrd === false || !is_array( $rrd ) ) {
+            throw new FileErrorException("Could not open RRD file");
+        }
+
+        $this->start = $rrd['start'];
+        $this->end   = $rrd['end'];
+        $this->step  = $rrd['step'];
+
+        [ $indexIn, $indexOut ] = $this->getIndexKeys();
+
+        // we want newest first, so iterate in reverse
+        // but.... do, we?
+        // $tin = array_reverse( $rrd['data'][ $indexIn ], true );
+        $tin = $rrd['data'][ $indexIn ];
+
+        $values  = [];
+
+        $isBits = ( $this->graph()->category() === Graph::CATEGORY_BITS );
+
+        $i = 0;
+         foreach( $tin as $ts => $v ) {
+            if( is_numeric( $v ) && is_numeric( $rrd['data'][$indexOut][$ts] ) ) {
+                // first couple are often blank
+                if( $ts > time() - $this->step ) {
+                    continue;
+                }
+
+                $values[$i] = [ (int)$ts, (int)$v, (int)$rrd['data'][$indexOut][$ts], (int)$v, (int)$rrd['data'][$indexOut][$ts] ];
+
+                if( $isBits ) {
+                    $values[$i][1] *= 8;
+                    $values[$i][2] *= 8;
+                    $values[$i][3] *= 8;
+                    $values[$i][4] *= 8;
+                }
+                $i++;
+            }
+        }
+        return $values;
+    }
+
+    /**
+     * From the RRD file, process and return a png
+     *
+     * @throws
+     */
+    public function png(): string
+    {
+        $separated_maxima = self::PERIOD_TIME[ $this->graph()->period() ] > 60*60*24*2;
+
+        [ $indexIn, $indexOut ] = $this->getIndexKeys();
+
+        $options = [
+            '--width=600',
+            '--height=150',
+            '--slope-mode',
+            '--start', time() - self::PERIOD_TIME[ $this->graph()->period() ],
+            '--lower-limit=0',
+            '--title=' . $this->graph()->title(),
+            '--vertical-label=' . $this->graph()->category() . ' / second',
+            '--watermark=' . $this->graph()->watermark(),
+            '--font=TITLE:10:Times',
+            '--font=WATERMARK:8:Times',
+            '--font=LEGEND:8:Courier',
+            '--font=AXIS:6:Courier',
+            '--font=UNIT:6:Courier',
+
+            'DEF:a='.$this->file().':'.$indexIn.':AVERAGE',
+            'DEF:b='.$this->file().':'.$indexIn.':MAX',
+            'DEF:c='.$this->file().':'.$indexOut.':AVERAGE',
+            'DEF:d='.$this->file().':'.$indexOut.':MAX',
+
+            'CDEF:cdefa=a,' . ( $this->graph()->category() === Graph::CATEGORY_BITS ? '8' : '1' ) . ',*',
+            'CDEF:cdefb=b,' . ( $this->graph()->category() === Graph::CATEGORY_BITS ? '8' : '1' ) . ',*',
+            'CDEF:cdefc=c,' . ( $this->graph()->category() === Graph::CATEGORY_BITS ? '8' : '1' ) . ',*',
+            'CDEF:cdefd=d,' . ( $this->graph()->category() === Graph::CATEGORY_BITS ? '8' : '1' ) . ',*',
+
+            'VDEF:last_in=cdefa,LAST',
+            'VDEF:last_out=cdefc,LAST',
+            'VDEF:max_in=cdefb,MAXIMUM',
+            'VDEF:max_out=cdefd,MAXIMUM',
+            'VDEF:avg_in=cdefb,AVERAGE',
+            'VDEF:avg_out=cdefd,AVERAGE',
+        ];
+
+        $options[] = 'COMMENT:Out';
+
+        if( $separated_maxima ) {
+            $options[] = 'AREA:cdefd#006600:Peak';
+            $options[] = 'GPRINT:max_out:%6.2lf%s\t';
+            $options[] = 'AREA:cdefc#00CF00:Avg';
+        } else {
+            $options[] = 'AREA:cdefc#00CF00:Max';
+            $options[] = 'GPRINT:max_out:%6.2lf%s\t';
+            $options[] = 'COMMENT:Avg';
+        }
+        $options[] = 'GPRINT:avg_out:%6.2lf%s';
+        $options[] = 'GPRINT:last_out:\tCur\\: %6.2lf%s\l';
+
+        $options[] = 'COMMENT:In ';
+
+        if( $separated_maxima ) {
+            $options[] = 'LINE1:cdefb#ff00ff:Peak';
+            $options[] = 'GPRINT:max_in:%6.2lf%s\t';
+            $options[] = 'LINE2:cdefa#002A97FF:Avg';
+        } else {
+            $options[] = 'LINE2:cdefa#002A97FF:Max';
+            $options[] = 'GPRINT:max_in:%6.2lf%s\t';
+            $options[] = 'COMMENT:Avg';
+        }
+        $options[] = 'GPRINT:avg_in:%6.2lf%s';
+        $options[] = 'GPRINT:last_in:\tCur\\: %6.2lf%s\l';
+
+        $options[] = 'COMMENT:\s';
+
+        $png = rrd_graph( $this->getLocalFilename('png'), $options );
+
+        if( $png === false ) {
+            throw new FileErrorException("Could not open/create RRD/PNG file");
+        }
+
+        return $this->getLocalFilename('png');
+    }
+
+    /**
+     * Accessor method for $array - the data from the MRTG file.
+     *
+     * @return array The data from the MRTG file
+     */
+    public function getArray(): array
+    {
+        return $this->array;
+    }
+}
\ No newline at end of file
diff --git a/app/Utils/Horizon.php b/app/Utils/Horizon.php
new file mode 100644
index 000000000..d1469dee9
--- /dev/null
+++ b/app/Utils/Horizon.php
@@ -0,0 +1,55 @@
+all() ) {
+            return self::STATUS_INACTIVE;
+        }
+        return collect( $masters )->contains( function( $master ) {
+            return $master->status === self::STATUS_PAUSED;
+        } ) ? self::STATUS_PAUSED : self::STATUS_RUNNING;
+    }
+}
\ No newline at end of file
diff --git a/app/Utils/Http/Controllers/Frontend/EloquentController.php b/app/Utils/Http/Controllers/Frontend/EloquentController.php
new file mode 100644
index 000000000..7d38e6a04
--- /dev/null
+++ b/app/Utils/Http/Controllers/Frontend/EloquentController.php
@@ -0,0 +1,686 @@
+
+ * @author     Yann Robin 
+ * @category   Utils\Http\Controllers\Frontend
+ * @copyright  Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee
+ * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
+ */
+abstract class EloquentController extends Controller
+{
+    /**
+     * Parameters used by the frontend controller
+     *
+     * @var object Parameters used by the frontend controller
+     */
+    protected $feParams = null;
+
+    /**
+     * Array for data that is passed to the templates
+     *
+     * @var array
+     */
+    protected $data = [];
+
+    /**
+     * The object being created / edited / viewed
+     */
+    protected $object = null;
+
+    /**
+     * The http request
+     */
+    protected $request = null;
+
+    /**
+     * Allow controllers to override the default successful store message
+     *
+     * @var string
+     */
+    protected $store_alert_success_message = null;
+
+    /**
+     * The URL prefix to use.
+     *
+     * Automatically determined based on the controller name if not set.
+     *
+     * @var string|null
+     */
+    protected static $route_prefix = null;
+
+    /**
+     * The minimum privileges required to access this controller.
+     *
+     * If you set this to less than the superuser, you need to manage privileges and access
+     * within your own implementation yourself.
+     *
+     * @var int
+     */
+    public static $minimum_privilege = User::AUTH_SUPERUSER;
+
+    /**
+     * Is this a read only controller?
+     *
+     * @var boolean
+     */
+    public static $read_only = false;
+
+    /**
+     * Should we allow a read only controller to delete
+     *
+     * @var boolean
+     */
+    public static $allow_delete_for_read_only = false;
+
+    /**
+     * Do we disable to edit?
+     *
+     * @var boolean
+     */
+    public static $disable_edit = false;
+
+    /**
+     * Where are the base views (list/edit/view).
+     *
+     * @var string
+     */
+    protected static $baseViews = 'frontend';
+
+    /**
+     * Sometimes we need to pass a custom request object for validation / authorisation.
+     *
+     * Set the name of the function here and the route for store will be pointed to it instead of doStore()
+     *
+     * @var string
+     */
+    protected static $storeFn = 'store';
+
+    /**
+     * Column / table data types when displaying data.
+     * @var array
+     */
+    static public $FE_COL_TYPES = [
+        'HAS_ONE'           => 'hasOne',
+        'CUSTOM_HAS_ONE'    => 'customHasOne',
+        'DATETIME'          => 'datetime',
+        'DATE'              => 'date',
+        'TIME'              => 'time',
+        'UNIX_TIMESTAMP'    => 'unix_timestamp',
+        'SCRIPT'            => 'script',
+        'SPRINTF'           => 'sprintf',
+        'REPLACE'           => 'replace',
+        'XLATE'             => 'xlate',
+        'YES_NO'            => 'yes_no',
+        'INVERSE_YES_NO'    => 'yes_no_inverse',
+        'YES_NO_NULL'       => 'yes_no_null',
+        'PARSDOWN'          => 'parsdown',
+        'CONST'             => 'const',
+        'LABEL'             => 'label',
+        'ARRAY'             => 'array',
+        'INTEGER'           => 'integer',
+        'LIMIT'             => 'limit',
+        'TEXT'              => 'text',
+        'COUNTRY'           => 'country',
+        'WHO_IS_PREFIX'     => 'who_is_prefix',
+        'JSON'              => 'json',
+    ];
+
+    /**
+     * The class's initialisation method.
+     */
+    public function __construct()
+    {
+        $this->middleware(function ($request, $next) {
+            $this->feInit();
+            $this->data[ 'col_types' ] = self::$FE_COL_TYPES;
+
+            return $next($request);
+        });
+    }
+
+    /**
+     * This must be overridden.
+     */
+    abstract protected function feInit();
+
+    /**
+     * The default routes for a Doctrine2Frontend class
+     *
+     * @return void
+     */
+    public static function routes(): void
+    {
+        // add leading slash to class name for absolute resolution:
+        $class = '\\' . static::class;
+        $route_prefix = self::route_prefix();
+
+        Route::group( [ 'prefix' => $route_prefix ], static function() use ( $class, $route_prefix ) {
+            Route::get( 'list',      $class . '@list' )->name( $route_prefix . '@list' );
+            Route::get( 'view/{id}', $class . '@view' )->name( $route_prefix . '@view' );
+
+            if( !static::$read_only ) {
+                Route::get(  'create',          $class . '@create'  )->name( $route_prefix . '@create'  );
+                Route::delete( 'delete/{id}',   $class . '@delete'  )->name( $route_prefix . '@delete'  );
+                if( !static::$disable_edit ) {
+                    Route::get( 'edit/{id}',    $class . '@edit'    )->name( $route_prefix . '@edit'    );
+                    Route::put( 'update/{id}',  $class . '@update'  )->name( $route_prefix . '@update'  );
+                }
+                Route::post( 'store', $class . '@' . static::$storeFn )->name( $route_prefix . '@store' );
+            }
+        });
+
+        $class::additionalRoutes( $route_prefix );
+    }
+
+    /**
+     * Work out the route prefix
+     *
+     * @return null|string
+     */
+    public static function route_prefix(): ?string
+    {
+        $class = static::class;
+
+        if( $class::$route_prefix ) {
+            return $class::$route_prefix;
+        }
+        return Str::kebab( substr( class_basename( $class ), 0, -10 ) );
+    }
+
+    /**
+     * Function which can be over-ridden to add additional routes
+     *
+     * If you don't want to use the defaults as well as some additional, override
+     * `routes()` instead.
+     *
+     * @param string $route_prefix
+     *
+     * @return void
+     */
+    protected static function additionalRoutes( string $route_prefix ): void {}
+
+    /**
+     * Provide array of table rows for the list action (and view action)
+     *
+     * @param int|null $id The `id` of the row to load for `view` action. `null` if `list` action.
+     *
+     * @return array
+     */
+    abstract protected function listGetData( ?int $id = null ): array;
+
+    /**
+     * Function which can be over-ridden to perform any pre-list tasks
+     *
+     * E.g. adding elements to $this->data for the pre/post-amble templates.
+     *
+     * @return void
+     */
+    protected function preList():void {}
+
+    /**
+     * Function which can be over-ridden to perform a "can list" authorisation step.
+     *
+     * Must return null (for okay) or a RedirectResponse (`redirect()`).
+     *
+     * @return null ?RedirectResponse
+     */
+    protected function canList(): ?RedirectResponse
+    {
+        return null;
+    }
+
+    /**
+     *  Include the necessary templates to the list function
+     *
+     * @return void
+     */
+    protected function listIncludeTemplates(): void
+    {
+        $this->data[ 'view' ][ 'listEmptyMessage']      = $this->resolveTemplate( 'list-empty-message',     false );
+        $this->data[ 'view' ][ 'listScriptExtra']       = $this->resolveTemplate( 'js/list-extra',          false );
+        $this->data[ 'view' ][ 'listHeadOverride']      = $this->resolveTemplate( 'list-head-override',     false );
+        $this->data[ 'view' ][ 'listRowOverride']       = $this->resolveTemplate( 'list-row-override',      false );
+        $this->data[ 'view' ][ 'listPreamble']          = $this->resolveTemplate( 'list-preamble',          false );
+        $this->data[ 'view' ][ 'listPostamble']         = $this->resolveTemplate( 'list-postamble',         false );
+        $this->data[ 'view' ][ 'listRowMenu']           = $this->resolveTemplate( 'list-row-menu',          false );
+        $this->data[ 'view' ][ 'pageHeaderPreamble']    = $this->resolveTemplate( 'page-header-preamble',   false );
+        $this->data[ 'view' ][ 'listScript' ]           = $this->resolveTemplate( 'js/list' );
+        $this->data[ 'view' ][ 'common' ]               = $this->resolveTemplate( 'js/common',              false );
+    }
+
+    /**
+     * List the contents of a database table.
+     *
+     * @param Request $param
+     *
+     * @return View|RedirectResponse
+     */
+    public function list( Request $param )
+    {
+        if( ( $r = $this->canList() ) !== null ) {
+            return $r;
+        }
+
+        $this->data[ 'rows' ] = $this->listGetData();
+        $this->listIncludeTemplates();
+        $this->preList();
+
+        return $this->display( 'list' );
+    }
+
+    /**
+     * Provide single object for view.
+     *
+     * @param int $id The `id` of the row to load for `view` action.
+     *
+     * @return array
+     */
+    protected function viewGetData( int $id ): array
+    {
+        $data = $this->listGetData( $id );
+
+        if( is_array( $data ) && reset( $data ) ) {
+            // get first value of the array
+            return $data[0];
+        }
+
+        abort( 404, "No Data" );
+    }
+
+    /**
+     * Function which can be over-ridden to perform any pre-view tasks
+     *
+     * E.g. adding elements to $this->view for the pre/post-amble templates.
+     *
+     * @return void
+     */
+    protected function preView(): void {}
+
+    /**
+     * View an object
+     *
+     * @param Request   $r      The HTTP Request object
+     * @param int       $id     The `id` of the row to load for `view` action.
+     *
+     * @return View
+     */
+    public function view( Request $r, int $id ): View
+    {
+        $this->data[ 'item' ]                         = $this->viewGetData( $id ) ;
+        $this->data[ 'view' ][ 'viewPreamble']        = $this->resolveTemplate( 'view-preamble',      false );
+        $this->data[ 'view' ][ 'viewPostamble']       = $this->resolveTemplate( 'view-postamble',     false );
+        $this->data[ 'view' ][ 'viewRowOverride']     = $this->resolveTemplate( 'view-row-override',  false );
+        $this->data[ 'view' ][ 'viewScript' ]         = $this->resolveTemplate( 'js/view',            false );
+        $this->data[ 'view' ][ 'common' ]             = $this->resolveTemplate( 'js/common',         false );
+
+        $this->preView();
+
+        return $this->display( 'view' );
+    }
+
+    /**
+     * Prepares data for the create form
+     *
+     * @return never
+     *
+     * @throws GeneralException
+     */
+    protected function createPrepareForm(): array
+    {
+        throw new GeneralException( 'For non-read-only Eloquent2Frontend controllers, you must override this method.' );
+    }
+
+    /**
+     * Prepares data for the edit form
+     *
+     * @param int $id
+     *
+     * @return never
+     *
+     * @throws GeneralException
+     */
+    protected function editPrepareForm( int $id ): array
+    {
+        throw new GeneralException( 'For non-read-only Eloquent2Frontend controllers, you must override this method.' );
+    }
+
+    /**
+     * Common set up tasks for add and edit actions.
+     *
+     * @return void
+     */
+    protected function addEditSetup() : void
+    {
+        $this->data[ 'view' ][ 'editForm']               = $this->resolveTemplate( 'edit-form' );
+        $this->data[ 'view' ][ 'editPreamble']           = $this->resolveTemplate( 'edit-preamble',         false );
+        $this->data[ 'view' ][ 'editPostamble']          = $this->resolveTemplate( 'edit-postamble',        false );
+        $this->data[ 'view' ][ 'editHeaderPreamble']     = $this->resolveTemplate( 'edit-header-preamble',  false );
+        $this->data[ 'view' ][ 'editScript' ]            = $this->resolveTemplate( 'js/edit',               false );
+    }
+
+    /**
+     * Create an object
+     *
+     * @return View
+     *
+     * @throws GeneralException
+     */
+    public function create(): View
+    {
+        $this->data[ 'params' ] = $this->createPrepareForm();
+        $this->data[ 'params' ]['isAdd'] = true;
+        $this->addEditSetup();
+        return $this->display( 'edit' );
+    }
+
+    /**
+     * Edit an object
+     *
+     * @param int $id ID of the object to edit
+     *
+     * @return view
+     *
+     * @throws
+     */
+    public function edit( int $id ): View
+    {
+        $this->data[ 'params' ] = $this->editPrepareForm( $id );
+        $this->data[ 'params' ]['isAdd'] = false;
+        $this->addEditSetup();
+        return $this->display('edit' );
+    }
+
+    /**
+     * Function to do the actual validation of the create/edit form..
+     *
+     * @param Request $r
+     *
+     * @throws GeneralException
+     *
+     * @return never
+     */
+    public function checkForm( Request $r ): void
+    {
+        throw new GeneralException( 'For non-read-only Eloquent2Frontend controllers, you must override this method.' );
+    }
+
+    /**
+     * Function to do the actual validation and storing of the submitted object.
+     *
+     * @param Request $r
+     *
+     * @return never
+     *
+     * @throws GeneralException
+     */
+    public function doStore( Request $r )
+    {
+        throw new GeneralException( 'For non-read-only Eloquent2Frontend controllers, you must override this method.' );
+    }
+
+    /**
+     * Function to do the actual update of the submitted object.
+     *
+     * @param Request   $r
+     * @param int       $id
+     *
+     * @return never
+     *
+     * @throws GeneralException
+     */
+    public function doUpdate( Request $r, int $id )
+    {
+        throw new GeneralException( 'For non-read-only Doctrine2Frontend controllers, you must override this method.' );
+    }
+
+    /**
+     * Action for storing a new object
+     *
+     * @param Request $r
+     *
+     * @throws
+     */
+    public function store( Request $r ): RedirectResponse|false
+    {
+        $storeResult = $this->doStore( $r );
+
+        if( $storeResult !== true ) {
+            return $storeResult;
+        }
+
+        $this->postFlush( 'create' );
+
+        Log::notice( ( Auth::check() ? Auth::getUser()->username : 'A public user' ) . ' created'
+            . ' ' . $this->feParams->nameSingular . ' with ID ' . $this->object->id );
+
+        AlertContainer::push( $this->store_alert_success_message ?? $this->feParams->titleSingular . " created.", Alert::SUCCESS );
+
+        return redirect()->to( $this->postStoreRedirect() ?? route( self::route_prefix() . '@' . 'list' ) );
+    }
+
+    /**
+     * Action for updating an object
+     *
+     * @param Request   $r
+     * @param int       $id
+     *
+     * @throws GeneralException
+     */
+    public function update( Request $r, int $id ): RedirectResponse|false
+    {
+        $updateResult = $this->doUpdate( $r, $id );
+
+        if( $updateResult !== true ) {
+            return $updateResult;
+        }
+
+        $this->postFlush( 'update' );
+
+        Log::notice( ( Auth::check() ? Auth::getUser()->username : 'A public user' ) . ' updated'
+            . ' ' . $this->feParams->nameSingular . ' with ID ' . $this->object->id );
+
+        AlertContainer::push( $this->update_alert_success_message ?? $this->feParams->titleSingular . " updated.", Alert::SUCCESS );
+
+        return redirect()->to( $this->postStoreRedirect() ?? route( self::route_prefix() . '@' . 'list' ) );
+    }
+
+    /**
+     * Allow D2F implementations to override where the post-store redirect goes.
+     *
+     * To implement this, have it return a valid route name
+     *
+     * @return null
+     */
+    protected function postStoreRedirect(): ?string
+    {
+        return null;
+    }
+
+    /**
+     * Optional method to be overridden if a D2F controllers needs to perform post-database flush actions
+     *
+     * @param string $action Either 'add', 'edit', 'delete'
+     *
+     * @return true
+     */
+    protected function postFlush( string $action ): bool
+    {
+        return true;
+    }
+
+    /**
+     * Function which can be over-ridden to perform any pre-deletion tasks
+     *
+     * You can stop the deletion by returning false but you should also add a
+     * message to explain why (to the AlertContainer).
+     *
+     * The object to be deleted is available via `$this->>object`
+     *
+     * @return true Return false to stop / cancel the deletion
+     */
+    protected function preDelete(): bool
+    {
+        return true;
+    }
+
+    /**
+     * Delete an object
+     *
+     * @param Request $r
+     *
+     * @return RedirectResponse
+     *
+     * @throws
+     */
+    public function delete( Request $r ): RedirectResponse
+    {
+        $this->object = $this->feParams->model::findOrFail( $r->id );
+
+        $this->request = $r;
+
+        if( $this->preDelete() ) {
+            $this->object->delete();
+            $this->postFlush( 'delete' );
+            AlertContainer::push( $this->feParams->titleSingular . " deleted.", Alert::SUCCESS );
+        }
+        if( $url = $this->postDeleteRedirect() ) {
+            return redirect()->to( $url );
+        }
+
+        return redirect()->route( self::route_prefix() . '@' . 'list' );
+    }
+
+    /**
+     * Allow D2F implementations to override where the post-delete redirect goes.
+     *
+     * To implement this, have it return a valid route url (e.g. `return route( "route-name" );`
+     *
+     * @return null
+     */
+    protected function postDeleteRedirect(): ?string
+    {
+        return null;
+    }
+
+    /**
+     * Displays the standard Frontend template or the controllers overridden version.
+     *
+     * @see _resolveTemplate()
+     *
+     * @param string $tpl The template to display
+     *
+     * @return View
+     */
+    protected function display( string $tpl ): View
+    {
+        $this->feParams->route_prefix = self::route_prefix();
+
+        return view( $this->resolveTemplate( $tpl ) )->with( [
+            'data'          => $this->data,
+            'feParams'      => $this->feParams
+        ]);
+    }
+
+    /**
+     * Resolves the standard Frontend template or the controllers overridden version.
+     *
+     * All frontend actions have their own template: `frontend/{$action}.foil.php` which is
+     * displayed by default. You can however override these by creating a template named:
+     * `{$controller}/{$action}.foil.php`. This function looks for an overriding template
+     * and displays that if it exists, otherwise it displays the default.
+     *
+     * This will also work for subdirectories: e.g. `$tpl = forms/add.html` is also valid.
+     *
+     * @param string    $tpl            The template to display
+     * @param bool      $quitOnMissing  If a template is not found, this normally throws a 404. If this is set to false, the function returns false instead.
+     *
+     * @return false|string The template to use of false if none found
+     */
+    protected function resolveTemplate( string $tpl, bool $quitOnMissing = true ): string|false
+    {
+        if( view()->exists( $this->feParams->viewFolderName . "/{$tpl}" ) ) {
+            return $this->feParams->viewFolderName . "/{$tpl}";
+        }
+
+        if( view()->exists( static::$baseViews . "/{$tpl}"  ) ) {
+            return static::$baseViews . "/{$tpl}";
+        }
+
+        if( view()->exists( $tpl  ) ) {
+            return $tpl;
+        }
+
+        if( $quitOnMissing ) {
+            abort( 404, "No template exists in frontend or controller's view directory for " . $tpl );
+        }
+
+        return false;
+    }
+
+    /**
+     * A helper function which can be called when the needs to be logged in
+     * or have a greater privilege.
+     *
+     * @param string $url  URL to redirect to (default: the default route)
+     * @param int    $code Redirection code (default: 302)
+     *
+     * @return never
+     */
+    protected function unauthorized( string $url = '', $code = 302 )
+    {
+        abort( 302, '', [ 'Location' => url($url) ] );
+        // belt and braces:
+        die( "File: " . __FILE__ . "\nLine: " . __LINE__ . "\nBug: you should not see this..." );
+    }
+}
\ No newline at end of file
diff --git a/app/Utils/IpAddress.php b/app/Utils/IpAddress.php
new file mode 100644
index 000000000..bc0b96302
--- /dev/null
+++ b/app/Utils/IpAddress.php
@@ -0,0 +1,92 @@
+
+ */
+class IpAddress
+{
+    /**
+     * Convert an IP address to an ARPA record
+     *
+     * E.g.:
+     *
+     * * 192.0.2.45  => 45.2.0.192.in-addr.arpa.
+     * * 2001:db8::1 => 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.
+     *
+     * @param string $ip The IP address
+     * @param int $protocol Either 4 (IPv4) or 6 (IPv6)
+     *
+     * @return string
+     *
+     * @throws
+     */
+    public static function toArpa( string $ip, int $protocol ): string
+    {
+        switch( $protocol ) {
+            case 4:
+                $parts = explode( '.', $ip );
+                $arpa = sprintf( '%d.%d.%d.%d.in-addr.arpa.', $parts[ 3 ], $parts[ 2 ], $parts[ 1 ], $parts[ 0 ] );
+                break;
+            case 6:
+                $addr = inet_pton( $ip );
+                $unpack = unpack('H*hex', $addr );
+                $hex = $unpack[ 'hex' ];
+                $arpa = implode('.', array_reverse( str_split( $hex ) ) ) . '.ip6.arpa.';
+                break;
+            default:
+                throw new GeneralException( 'Invalid protocol!' );
+        }
+
+        return $arpa;
+    }
+
+    /**
+     * Try to get the clients real IP address even when behind a proxy.
+     *
+     * Source: https://stackoverflow.com/questions/33268683/how-to-get-client-ip-address-in-laravel-5/41769505#41769505
+     *
+     * @return string|null
+     */
+    public static function getIp(): string|null
+    {
+        foreach( [ 'HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR' ] as $key ) {
+            if( array_key_exists( $key, $_SERVER ) === true ) {
+                $value = (string)$_SERVER[ $key ];
+                foreach( explode(',', $value ) as $ip ) {
+                    $ip = trim( $ip );
+                    if( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ) !== false ) {
+                        return $ip;
+                    }
+                }
+            }
+        }
+        return request()->getClientIp();
+    }
+}
\ No newline at end of file
diff --git a/app/Utils/MailingList.php b/app/Utils/MailingList.php
new file mode 100644
index 000000000..3bb3b56ff
--- /dev/null
+++ b/app/Utils/MailingList.php
@@ -0,0 +1,161 @@
+
+ * @author Yann Robin 
+ * @category   IXP
+ * @package    IXP\Http\Utils
+ * @copyright  Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee
+ * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
+ */
+class MailingList
+{
+    /**
+     * @var array-key Mailing list config key (from config/mailinglist.php)
+     */
+    private $key = null;
+
+    /**
+     * @var array Mailing list config (from config/mailinglist.php)
+     */
+    private $ml = null;
+
+    /**
+     * Constructor
+     *
+     * @param string    $listname The list name as defined as the array key in config/mailinglist.php
+     *
+     * @throws Exception
+     */
+    public function __construct( string $listname ) {
+        if( !config( 'mailinglists.lists.' . $listname, false ) ) {
+            throw new Exception('Mailing list name not defined in config/mailinglist.php');
+        }
+
+        $this->key = $listname;
+        $this->ml  = config( 'mailinglists.lists.' . $listname );
+    }
+
+
+    /**
+     * Get all users who are (un)subscribed from the mailing list.
+     *
+     * All emails are validated, normalised to lowercase, duplicates removed and sorted alphabetically.
+     *
+     * @param bool $subscribed
+     *
+     * @return array
+     *
+     * @psalm-return array
+     */
+    public function getSubscriberEmails( bool $subscribed = true ): array
+    {
+        $filtered_users = collect();
+
+        $users = User::when( $subscribed, function ( $query ) {
+            return $query->whereJsonContains( 'prefs->mailinglist', [ $this->key => "1" ] );
+        }, function ($query) {
+            return $query->whereJsonContains( 'prefs->mailinglist', [ $this->key => "0" ] );
+        })->orderBy( 'email' )->get();
+
+        foreach( $users as $user ) {
+            $email = strtolower( $user->email );
+            if( !$filtered_users->contains( $email ) && filter_var( $email, FILTER_VALIDATE_EMAIL ) !== false ) {
+                /** @psalm-suppress InvalidArgument - it is not invalid */
+                $filtered_users->add( $email );
+            }
+        }
+
+        return $filtered_users->toArray();
+    }
+
+    /**
+     * Initialise a new mailing list.
+     *
+     * See documentation for full details: https://docs.ixpmanager.org/latest/features/mailing-lists/
+     *
+     * Returns an array with three arrays keyed as:
+     *
+     * - skipped: users that already had a preference for this list
+     * - subscribed: users with a preference added to indicate they are subscribed
+     * - unsubscribed: users with a preference added to indicate they are unsubscribed
+     * - unknown: no matching user
+     *
+     * @param Collection $addresses Addresses to initialise IXP Manager user preferences with.
+     *                       NB: ensure all addresses passed are normalised to lower case!
+     *
+     * @return array[]
+     *
+     * @psalm-return array{skipped: list, subscribed: list, unsubscribed: list, unknown: array}
+     */
+    public function init( Collection $addresses ): array
+    {
+        // three types of results:
+        $skipped      = [];
+        $subscribed   = [];
+        $unsubscribed = [];
+
+        foreach( User::all() as $u ) {
+            $e = strtolower( $u->email );
+            $prefs = $u->prefs;
+
+            if( isset( $prefs[ 'mailinglist' ][ $this->key ] ) && (int)$prefs[ 'mailinglist' ][ $this->key ] === 1 ) {
+                if( $addresses->contains( $e ) ) {
+                    $skipped[] = $e;
+                    $addresses->forget( $addresses->search( $e ) );
+                }
+                continue;
+            }
+
+            if( $addresses->contains( $e ) ) {
+                $value = 1;
+                $subscribed[] = $e;
+                $addresses->forget( $addresses->search( $e ) );
+            } else if( filter_var( $e, FILTER_VALIDATE_EMAIL ) !== false ) {
+                $value = 0;
+                $unsubscribed[] = $e;
+            }
+
+            $prefs[ 'mailinglist' ][ $this->key ] = $value;
+            $u->prefs = $prefs;
+            $u->save();
+        }
+
+        return [
+            'skipped'      => $skipped,
+            'subscribed'   => $subscribed,
+            'unsubscribed' => $unsubscribed,
+            'unknown'      => $addresses->toArray(),
+        ];
+    }
+}
\ No newline at end of file
diff --git a/app/Utils/OUI.php b/app/Utils/OUI.php
new file mode 100644
index 000000000..62e1e24ad
--- /dev/null
+++ b/app/Utils/OUI.php
@@ -0,0 +1,112 @@
+
+ */
+class OUI
+{
+    /**
+     * Where to get the OUI list from
+     * @var string Where to get the OUI list from
+     */
+    public $file = 'http://standards.ieee.org/develop/regauth/oui/oui.txt';
+
+    /**
+     * Raw OUI data
+     * @var string
+     */
+    private $raw = null;
+
+    /**
+     * Processed OUIs array as [ 'oui' => 'organisation', ... ]
+     * @var array
+     */
+    private $ouis = null;
+
+    /**
+     * Constructor
+     *
+     * @param string|null $file Where to get the OUI list from
+     */
+    public function __construct( string $file = null )
+    {
+        if( $file ) {
+            $this->file = $file;
+        }
+    }
+
+    /**
+     * Load the raw OUI data from the specified location
+     *
+     * @return static An instance of this class for fluent interfaces
+     *
+     * @throws GeneralException
+     */
+    public function loadList(): static
+    {
+        $this->raw = @file_get_contents( $this->file );
+
+        if( $this->raw === false ) {
+            throw new GeneralException( 'IXP\\Utils\\OUI - could not load OUI list from ' . $this->file );
+        }
+
+        return $this;
+    }
+
+    /**
+     * @param false $data
+     *
+     * @return string[]
+     *
+     * @throws GeneralException
+     *
+     * @psalm-return array
+     */
+    public function processRawData( $data = false ): array
+    {
+        if( !$data && $this->raw === null ) {
+            throw new GeneralException( 'IXP\\Utils\\OUI - cannot process when no data has been loaded or provided' );
+        }
+
+        if( !$data ) {
+            $data = $this->raw;
+        }
+
+        $this->ouis = [];
+        foreach( explode( "\n", $data ) as $line ) {
+            if( preg_match( "/^\s*([0-9A-F]{6})\s+\(base 16\)\s+(.*)$/", $line, $matches ) )
+                $this->ouis[ strtolower( trim( $matches[1] ) ) ] = trim( $matches[2] );
+        }
+
+        return $this->ouis;
+    }
+}
\ No newline at end of file
diff --git a/app/Utils/View/Alert/Alert.php b/app/Utils/View/Alert/Alert.php
new file mode 100644
index 000000000..8a5705259
--- /dev/null
+++ b/app/Utils/View/Alert/Alert.php
@@ -0,0 +1,94 @@
+
+ * @copyright  Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee.
+ */
+class Alert
+{
+
+    public const SUCCESS = 'success';
+    public const INFO    = 'info';
+    public const WARNING = 'warning';
+    public const DANGER  = 'danger';
+
+    public const CLASSES = [
+        self::SUCCESS,
+        self::INFO,
+        self::WARNING,
+        self::DANGER,
+    ];
+
+    /**
+     * The message
+     * @var string
+     */
+    protected $message;
+
+    /**
+     * The class
+     * @var string
+     */
+    private $class = '';
+
+    /**
+     * The constructor
+     * @param string $message
+     * @param string $class
+     */
+    public function __construct( string $message, string $class = self::INFO )
+    {
+        $this->message = $message;
+
+        if( !in_array( $class, self::CLASSES ) ) {
+            $this->class = self::INFO;
+        } else {
+            $this->class = $class;
+        }
+    }
+
+    /**
+     * Get the message
+     * @return string
+     */
+    public function message(): string
+    {
+        return $this->message;
+    }
+
+    /**
+     * Get the class
+     * @return string the class
+     */
+    public function class(): string
+    {
+        return $this->class;
+    }
+}
\ No newline at end of file
diff --git a/app/Utils/View/Alert/Container.php b/app/Utils/View/Alert/Container.php
new file mode 100644
index 000000000..cca0272d7
--- /dev/null
+++ b/app/Utils/View/Alert/Container.php
@@ -0,0 +1,123 @@
+
+ * @copyright  Copyright (C) 2009 - 2020 Internet Neutral Exchange Association Company Limited By Guarantee.
+ */
+class Container
+{
+    /**
+     * Push a message onto the message stack
+     * @param string    $message
+     * @param string    $class
+     * @param bool      $clear
+     *
+     * @return void
+     */
+    public static function push( string $message, string $class = Alert::INFO, $clear = false ): void
+    {
+        if( $clear || !( $alerts = session('ixp.utils.view.alerts') ) ) {
+            $alerts = [];
+        }
+
+        $alerts[] = new Alert( $message, $class );
+        session( [ 'ixp.utils.view.alerts' => $alerts ] );
+    }
+
+    /**
+     * Pop an alert off the message stack
+     *
+     * FIXME: when PHP 7.1 is a req, fix the return type
+     *
+     * @return Alert|null for none ( === null)
+     */
+    public static function pop()
+    {
+        $alerts = session('ixp.utils.view.alerts');
+
+        if( !$alerts || !count($alerts) ) {
+            return null;
+        }
+
+        $alert = array_pop($alerts);
+        session(['ixp.utils.view.alerts' => $alerts]);
+        return $alert;
+    }
+
+    /**
+     * Turn alerts into (safe) HTML
+     *
+     * @return string
+     */
+    public static function html(): string
+    {
+        $alerts = '';
+        $color = '';
+
+        // need to explicitly list all CSS classes or they will be purged.
+        // i.e. do NOT dynamically generate CSS class names.
+        while( $alert = self::pop() ) {
+            switch ($alert->class()) {
+                case 'danger':
+                    $icon = "fa-exclamation-triangle";
+                    $color = "tw-bg-red-100";
+                    $border = "tw-border-red-500";
+                    $text = "tw-text-red-700";
+                    break;
+                case 'info':
+                    $icon = "fa-info-circle";
+                    $color = "tw-bg-blue-100";
+                    $border = "tw-border-blue-500";
+                    $text = "tw-text-blue-700";
+                    break;
+                case 'success':
+                    $icon = "fa-check-circle";
+                    $color = "tw-bg-green-100";
+                    $border = "tw-border-green-500";
+                    $text = "tw-text-green-700";
+                    break;
+                case 'warning':
+                    $icon = "fa-exclamation-circle";
+                    $color = "tw-bg-orange-100";
+                    $border = "tw-border-orange-500";
+                    $text = "tw-text-orange-700";
+                    break;
+            }
+
+            $alerts .= '' . "\n\n";
+        }
+
+        return $alerts;
+    }
+}
\ No newline at end of file
diff --git a/app/Utils/Whois.php b/app/Utils/Whois.php
new file mode 100644
index 000000000..c4bfd9346
--- /dev/null
+++ b/app/Utils/Whois.php
@@ -0,0 +1,110 @@
+host = $host;
+        $this->port = $port;
+    }
+
+
+    /**
+     *
+     */
+
+    /**
+     * Do a whois lookup
+     *
+     * @param string $lookup   What to ask the whois server
+     * @param bool $htmlencode If true, return output of htmlspecialchars()
+     *
+     * @return string
+     */
+    public function whois( string $lookup, bool $htmlencode = true ): string
+    {
+        // Whois specification:
+        //
+        // Connect to the service host
+        //    TCP: service port 43 decimal
+        // Send a single "command line", ending with .
+        // Receive information in response to the command line.  The
+        // server closes its connections as soon as the output is finished.
+
+        // Open a socket to PeeringDB's Whois service
+        if( !( $sock = fsockopen( $this->host, $this->port ) ) ) {
+            return "Error: could not connect to {$this->host}:{$this->port}\n\nCheck internet connectivity and {$this->host} status.";
+        }
+
+        // look up the ASN
+        fwrite( $sock, $lookup . "\n" );
+
+        // load the result (streaming text)
+        $data = '';
+        while( !feof( $sock ) ) {
+            $data .= fgets( $sock, 4096 );
+        }
+        fclose( $sock );
+
+        // assume this is for display on a website
+        if( $htmlencode ) {
+            return clean( $data );
+        }
+
+        return $data;
+    }
+
+    /**
+     * @return string
+     */
+    public function host(): string
+    {
+        return $this->host;
+    }
+}
\ No newline at end of file
diff --git a/application/Bootstrap.php b/application/Bootstrap.php
deleted file mode 100644
index 9faeb6324..000000000
--- a/application/Bootstrap.php
+++ /dev/null
@@ -1,57 +0,0 @@
-registerNamespace( 'INEX' );
-    }
-
-
-    /**
-     * Register the OSS library autoloader
-     *
-     * This function ensures that classes from library/OSS are automatically
-     * loaded from the subdirectories where subdirectories are indicated by
-     * underscores in the same manner as Zend.
-     *
-     */
-    protected function _initOSSAutoLoader()
-    {
-        $autoloader = Zend_Loader_Autoloader::getInstance();
-        $autoloader->registerNamespace('OSS');
-    }
-}
-
diff --git a/application/Entities/BGPSessionData.php b/application/Entities/BGPSessionData.php
deleted file mode 100644
index 6770c35da..000000000
--- a/application/Entities/BGPSessionData.php
+++ /dev/null
@@ -1,251 +0,0 @@
-srcipaddressid = $srcipaddressid;
-    
-        return $this;
-    }
-
-    /**
-     * Get srcipaddressid
-     *
-     * @return integer
-     */
-    public function getSrcipaddressid()
-    {
-        return $this->srcipaddressid;
-    }
-
-    /**
-     * Set desipaddressid
-     *
-     * @param integer $desipaddressid
-     * @return BGPSessionData
-     */
-    public function setDesipaddressid($desipaddressid)
-    {
-        $this->desipaddressid = $desipaddressid;
-    
-        return $this;
-    }
-
-    /**
-     * Get desipaddressid
-     *
-     * @return integer
-     */
-    public function getDesipaddressid()
-    {
-        return $this->desipaddressid;
-    }
-
-    /**
-     * Set protocol
-     *
-     * @param integer $protocol
-     * @return BGPSessionData
-     */
-    public function setProtocol($protocol)
-    {
-        $this->protocol = $protocol;
-    
-        return $this;
-    }
-
-    /**
-     * Get protocol
-     *
-     * @return integer
-     */
-    public function getProtocol()
-    {
-        return $this->protocol;
-    }
-
-    /**
-     * Set vlan
-     *
-     * @param integer $vlan
-     * @return BGPSessionData
-     */
-    public function setVlan($vlan)
-    {
-        $this->vlan = $vlan;
-    
-        return $this;
-    }
-
-    /**
-     * Get vlan
-     *
-     * @return integer
-     */
-    public function getVlan()
-    {
-        return $this->vlan;
-    }
-
-    /**
-     * Set packetcount
-     *
-     * @param integer $packetcount
-     * @return BGPSessionData
-     */
-    public function setPacketcount($packetcount)
-    {
-        $this->packetcount = $packetcount;
-    
-        return $this;
-    }
-
-    /**
-     * Get packetcount
-     *
-     * @return integer
-     */
-    public function getPacketcount()
-    {
-        return $this->packetcount;
-    }
-
-    /**
-     * Set timestamp
-     *
-     * @param \DateTime $timestamp
-     * @return BGPSessionData
-     */
-    public function setTimestamp($timestamp)
-    {
-        $this->timestamp = $timestamp;
-    
-        return $this;
-    }
-
-    /**
-     * Get timestamp
-     *
-     * @return \DateTime
-     */
-    public function getTimestamp()
-    {
-        return $this->timestamp;
-    }
-
-    /**
-     * Set source
-     *
-     * @param string $source
-     * @return BGPSessionData
-     */
-    public function setSource($source)
-    {
-        $this->source = $source;
-    
-        return $this;
-    }
-
-    /**
-     * Get source
-     *
-     * @return string
-     */
-    public function getSource()
-    {
-        return $this->source;
-    }
-
-    /**
-     * Get id
-     *
-     * @return integer
-     */
-    public function getId()
-    {
-        return $this->id;
-    }
-    /**
-     * @var integer $dstipaddressid
-     */
-    private $dstipaddressid;
-
-
-    /**
-     * Set dstipaddressid
-     *
-     * @param integer $dstipaddressid
-     * @return BGPSessionData
-     */
-    public function setDstipaddressid($dstipaddressid)
-    {
-        $this->dstipaddressid = $dstipaddressid;
-    
-        return $this;
-    }
-
-    /**
-     * Get dstipaddressid
-     *
-     * @return integer
-     */
-    public function getDstipaddressid()
-    {
-        return $this->dstipaddressid;
-    }
-}
\ No newline at end of file
diff --git a/application/Entities/Cabinet.php b/application/Entities/Cabinet.php
deleted file mode 100644
index 16e277f40..000000000
--- a/application/Entities/Cabinet.php
+++ /dev/null
@@ -1,307 +0,0 @@
-Switches = new \Doctrine\Common\Collections\ArrayCollection();
-        $this->CustomerEquipment = new \Doctrine\Common\Collections\ArrayCollection();
-    }
-    
-    /**
-     * Set name
-     *
-     * @param string $name
-     * @return Cabinet
-     */
-    public function setName($name)
-    {
-        $this->name = $name;
-    
-        return $this;
-    }
-
-    /**
-     * Get name
-     *
-     * @return string 
-     */
-    public function getName()
-    {
-        return $this->name;
-    }
-
-    /**
-     * Set colocation
-     *
-     * @param string $colocation
-     * @return Cabinet
-     */
-    public function setColocation($colocation)
-    {
-        $this->colocation = $colocation;
-    
-        return $this;
-    }
-
-    /**
-     * Get colocation
-     *
-     * @return string 
-     */
-    public function getColocation()
-    {
-        return $this->colocation;
-    }
-
-    /**
-     * Set height
-     *
-     * @param integer $height
-     * @return Cabinet
-     */
-    public function setHeight($height)
-    {
-        $this->height = $height;
-    
-        return $this;
-    }
-
-    /**
-     * Get height
-     *
-     * @return integer 
-     */
-    public function getHeight()
-    {
-        return $this->height;
-    }
-
-    /**
-     * Set type
-     *
-     * @param string $type
-     * @return Cabinet
-     */
-    public function setType($type)
-    {
-        $this->type = $type;
-    
-        return $this;
-    }
-
-    /**
-     * Get type
-     *
-     * @return string 
-     */
-    public function getType()
-    {
-        return $this->type;
-    }
-
-    /**
-     * Set notes
-     *
-     * @param string $notes
-     * @return Cabinet
-     */
-    public function setNotes($notes)
-    {
-        $this->notes = $notes;
-    
-        return $this;
-    }
-
-    /**
-     * Get notes
-     *
-     * @return string 
-     */
-    public function getNotes()
-    {
-        return $this->notes;
-    }
-
-    /**
-     * Get id
-     *
-     * @return integer 
-     */
-    public function getId()
-    {
-        return $this->id;
-    }
-
-    /**
-     * Add Switches
-     *
-     * @param Entities\Switcher $switches
-     * @return Cabinet
-     */
-    public function addSwitche(\Entities\Switcher $switches)
-    {
-        $this->Switches[] = $switches;
-    
-        return $this;
-    }
-
-    /**
-     * Remove Switches
-     *
-     * @param Entities\Switcher $switches
-     */
-    public function removeSwitche(\Entities\Switcher $switches)
-    {
-        $this->Switches->removeElement($switches);
-    }
-
-    /**
-     * Get Switches
-     *
-     * @return Doctrine\Common\Collections\Collection 
-     */
-    public function getSwitches()
-    {
-        return $this->Switches;
-    }
-
-    /**
-     * Add CustomerEquipment
-     *
-     * @param Entities\CustomerEquipment $customerEquipment
-     * @return Cabinet
-     */
-    public function addCustomerEquipment(\Entities\CustomerEquipment $customerEquipment)
-    {
-        $this->CustomerEquipment[] = $customerEquipment;
-    
-        return $this;
-    }
-
-    /**
-     * Remove CustomerEquipment
-     *
-     * @param Entities\CustomerEquipment $customerEquipment
-     */
-    public function removeCustomerEquipment(\Entities\CustomerEquipment $customerEquipment)
-    {
-        $this->CustomerEquipment->removeElement($customerEquipment);
-    }
-
-    /**
-     * Get CustomerEquipment
-     *
-     * @return Doctrine\Common\Collections\Collection 
-     */
-    public function getCustomerEquipment()
-    {
-        return $this->CustomerEquipment;
-    }
-
-    /**
-     * Set Location
-     *
-     * @param Entities\Location $location
-     * @return Cabinet
-     */
-    public function setLocation(\Entities\Location $location = null)
-    {
-        $this->Location = $location;
-    
-        return $this;
-    }
-
-    /**
-     * Get Location
-     *
-     * @return Entities\Location 
-     */
-    public function getLocation()
-    {
-        return $this->Location;
-    }
-    /**
-     * @var string $cololocation
-     */
-    private $cololocation;
-
-
-    /**
-     * Set cololocation
-     *
-     * @param string $cololocation
-     * @return Cabinet
-     */
-    public function setCololocation($cololocation)
-    {
-        $this->cololocation = $cololocation;
-    
-        return $this;
-    }
-
-    /**
-     * Get cololocation
-     *
-     * @return string 
-     */
-    public function getCololocation()
-    {
-        return $this->cololocation;
-    }
-}
\ No newline at end of file
diff --git a/application/Entities/ChangeLog.php b/application/Entities/ChangeLog.php
deleted file mode 100644
index 99f14089e..000000000
--- a/application/Entities/ChangeLog.php
+++ /dev/null
@@ -1,223 +0,0 @@
-title = $title;
-    
-        return $this;
-    }
-
-    /**
-     * Get title
-     *
-     * @return string 
-     */
-    public function getTitle()
-    {
-        return $this->title;
-    }
-
-    /**
-     * Set details
-     *
-     * @param string $details
-     * @return ChangeLog
-     */
-    public function setDetails($details)
-    {
-        $this->details = $details;
-    
-        return $this;
-    }
-
-    /**
-     * Get details
-     *
-     * @return string 
-     */
-    public function getDetails()
-    {
-        return $this->details;
-    }
-
-    /**
-     * Set visibility
-     *
-     * @param integer $visibility
-     * @return ChangeLog
-     */
-    public function setVisibility($visibility)
-    {
-        $this->visibility = $visibility;
-    
-        return $this;
-    }
-
-    /**
-     * Get visibility
-     *
-     * @return integer 
-     */
-    public function getVisibility()
-    {
-        return $this->visibility;
-    }
-
-    /**
-     * Set livedate
-     *
-     * @param \DateTime $livedate
-     * @return ChangeLog
-     */
-    public function setLivedate($livedate)
-    {
-        $this->livedate = $livedate;
-    
-        return $this;
-    }
-
-    /**
-     * Get livedate
-     *
-     * @return \DateTime 
-     */
-    public function getLivedate()
-    {
-        return $this->livedate;
-    }
-
-    /**
-     * Set version
-     *
-     * @param integer $version
-     * @return ChangeLog
-     */
-    public function setVersion($version)
-    {
-        $this->version = $version;
-    
-        return $this;
-    }
-
-    /**
-     * Get version
-     *
-     * @return integer 
-     */
-    public function getVersion()
-    {
-        return $this->version;
-    }
-
-    /**
-     * Set created_at
-     *
-     * @param \DateTime $createdAt
-     * @return ChangeLog
-     */
-    public function setCreatedAt($createdAt)
-    {
-        $this->created_at = $createdAt;
-    
-        return $this;
-    }
-
-    /**
-     * Get created_at
-     *
-     * @return \DateTime 
-     */
-    public function getCreatedAt()
-    {
-        return $this->created_at;
-    }
-
-    /**
-     * Get id
-     *
-     * @return integer 
-     */
-    public function getId()
-    {
-        return $this->id;
-    }
-
-    /**
-     * Set User
-     *
-     * @param Entities\User $user
-     * @return ChangeLog
-     */
-    public function setUser(\Entities\User $user = null)
-    {
-        $this->User = $user;
-    
-        return $this;
-    }
-
-    /**
-     * Get User
-     *
-     * @return Entities\User 
-     */
-    public function getUser()
-    {
-        return $this->User;
-    }
-}
\ No newline at end of file
diff --git a/application/Entities/ConsoleServerConnection.php b/application/Entities/ConsoleServerConnection.php
deleted file mode 100644
index ee0615c20..000000000
--- a/application/Entities/ConsoleServerConnection.php
+++ /dev/null
@@ -1,307 +0,0 @@
-description = $description;
-    
-        return $this;
-    }
-
-    /**
-     * Get description
-     *
-     * @return string 
-     */
-    public function getDescription()
-    {
-        return $this->description;
-    }
-
-    /**
-     * Set port
-     *
-     * @param string $port
-     * @return ConsoleServerConnection
-     */
-    public function setPort($port)
-    {
-        $this->port = $port;
-    
-        return $this;
-    }
-
-    /**
-     * Get port
-     *
-     * @return string 
-     */
-    public function getPort()
-    {
-        return $this->port;
-    }
-
-    /**
-     * Set speed
-     *
-     * @param integer $speed
-     * @return ConsoleServerConnection
-     */
-    public function setSpeed($speed)
-    {
-        $this->speed = $speed;
-    
-        return $this;
-    }
-
-    /**
-     * Get speed
-     *
-     * @return integer 
-     */
-    public function getSpeed()
-    {
-        return $this->speed;
-    }
-
-    /**
-     * Set parity
-     *
-     * @param integer $parity
-     * @return ConsoleServerConnection
-     */
-    public function setParity($parity)
-    {
-        $this->parity = $parity;
-    
-        return $this;
-    }
-
-    /**
-     * Get parity
-     *
-     * @return integer 
-     */
-    public function getParity()
-    {
-        return $this->parity;
-    }
-
-    /**
-     * Set stopbits
-     *
-     * @param integer $stopbits
-     * @return ConsoleServerConnection
-     */
-    public function setStopbits($stopbits)
-    {
-        $this->stopbits = $stopbits;
-    
-        return $this;
-    }
-
-    /**
-     * Get stopbits
-     *
-     * @return integer 
-     */
-    public function getStopbits()
-    {
-        return $this->stopbits;
-    }
-
-    /**
-     * Set flowcontrol
-     *
-     * @param integer $flowcontrol
-     * @return ConsoleServerConnection
-     */
-    public function setFlowcontrol($flowcontrol)
-    {
-        $this->flowcontrol = $flowcontrol;
-    
-        return $this;
-    }
-
-    /**
-     * Get flowcontrol
-     *
-     * @return integer 
-     */
-    public function getFlowcontrol()
-    {
-        return $this->flowcontrol;
-    }
-
-    /**
-     * Set autobaud
-     *
-     * @param boolean $autobaud
-     * @return ConsoleServerConnection
-     */
-    public function setAutobaud($autobaud)
-    {
-        $this->autobaud = $autobaud;
-    
-        return $this;
-    }
-
-    /**
-     * Get autobaud
-     *
-     * @return boolean 
-     */
-    public function getAutobaud()
-    {
-        return $this->autobaud;
-    }
-
-    /**
-     * Set notes
-     *
-     * @param string $notes
-     * @return ConsoleServerConnection
-     */
-    public function setNotes($notes)
-    {
-        $this->notes = $notes;
-    
-        return $this;
-    }
-
-    /**
-     * Get notes
-     *
-     * @return string 
-     */
-    public function getNotes()
-    {
-        return $this->notes;
-    }
-
-    /**
-     * Get id
-     *
-     * @return integer 
-     */
-    public function getId()
-    {
-        return $this->id;
-    }
-
-    /**
-     * Set Customer
-     *
-     * @param Entities\Customer $customer
-     * @return ConsoleServerConnection
-     */
-    public function setCustomer(\Entities\Customer $customer = null)
-    {
-        $this->Customer = $customer;
-    
-        return $this;
-    }
-
-    /**
-     * Get Customer
-     *
-     * @return Entities\Customer 
-     */
-    public function getCustomer()
-    {
-        return $this->Customer;
-    }
-
-    /**
-     * Set Switcher
-     *
-     * @param Entities\Switcher $switcher
-     * @return ConsoleServerConnection
-     */
-    public function setSwitcher(\Entities\Switcher $switcher = null)
-    {
-        $this->Switcher = $switcher;
-    
-        return $this;
-    }
-
-    /**
-     * Get Switcher
-     *
-     * @return Entities\Switcher 
-     */
-    public function getSwitcher()
-    {
-        return $this->Switcher;
-    }
-}
\ No newline at end of file
diff --git a/application/Entities/Contact.php b/application/Entities/Contact.php
deleted file mode 100644
index 9f19d0a3d..000000000
--- a/application/Entities/Contact.php
+++ /dev/null
@@ -1,335 +0,0 @@
-name = $name;
-    
-        return $this;
-    }
-
-    /**
-     * Get name
-     *
-     * @return string 
-     */
-    public function getName()
-    {
-        return $this->name;
-    }
-
-    /**
-     * Set email
-     *
-     * @param string $email
-     * @return Contact
-     */
-    public function setEmail($email)
-    {
-        $this->email = $email;
-    
-        return $this;
-    }
-
-    /**
-     * Get email
-     *
-     * @return string 
-     */
-    public function getEmail()
-    {
-        return $this->email;
-    }
-
-    /**
-     * Set phone
-     *
-     * @param string $phone
-     * @return Contact
-     */
-    public function setPhone($phone)
-    {
-        $this->phone = $phone;
-    
-        return $this;
-    }
-
-    /**
-     * Get phone
-     *
-     * @return string 
-     */
-    public function getPhone()
-    {
-        return $this->phone;
-    }
-
-    /**
-     * Set mobile
-     *
-     * @param string $mobile
-     * @return Contact
-     */
-    public function setMobile($mobile)
-    {
-        $this->mobile = $mobile;
-    
-        return $this;
-    }
-
-    /**
-     * Get mobile
-     *
-     * @return string 
-     */
-    public function getMobile()
-    {
-        return $this->mobile;
-    }
-
-    /**
-     * Set facilityaccess
-     *
-     * @param integer $facilityaccess
-     * @return Contact
-     */
-    public function setFacilityaccess($facilityaccess)
-    {
-        $this->facilityaccess = $facilityaccess;
-    
-        return $this;
-    }
-
-    /**
-     * Get facilityaccess
-     *
-     * @return integer 
-     */
-    public function getFacilityaccess()
-    {
-        return $this->facilityaccess;
-    }
-
-    /**
-     * Set mayauthorize
-     *
-     * @param boolean $mayauthorize
-     * @return Contact
-     */
-    public function setMayauthorize($mayauthorize)
-    {
-        $this->mayauthorize = $mayauthorize;
-    
-        return $this;
-    }
-
-    /**
-     * Get mayauthorize
-     *
-     * @return boolean 
-     */
-    public function getMayauthorize()
-    {
-        return $this->mayauthorize;
-    }
-
-    /**
-     * Set lastupdated
-     *
-     * @param \DateTime $lastupdated
-     * @return Contact
-     */
-    public function setLastupdated($lastupdated)
-    {
-        $this->lastupdated = $lastupdated;
-    
-        return $this;
-    }
-
-    /**
-     * Get lastupdated
-     *
-     * @return \DateTime 
-     */
-    public function getLastupdated()
-    {
-        return $this->lastupdated;
-    }
-
-    /**
-     * Set lastupdatedby
-     *
-     * @param integer $lastupdatedby
-     * @return Contact
-     */
-    public function setLastupdatedby($lastupdatedby)
-    {
-        $this->lastupdatedby = $lastupdatedby;
-    
-        return $this;
-    }
-
-    /**
-     * Get lastupdatedby
-     *
-     * @return integer 
-     */
-    public function getLastupdatedby()
-    {
-        return $this->lastupdatedby;
-    }
-
-    /**
-     * Set creator
-     *
-     * @param string $creator
-     * @return Contact
-     */
-    public function setCreator($creator)
-    {
-        $this->creator = $creator;
-    
-        return $this;
-    }
-
-    /**
-     * Get creator
-     *
-     * @return string 
-     */
-    public function getCreator()
-    {
-        return $this->creator;
-    }
-
-    /**
-     * Set created
-     *
-     * @param \DateTime $created
-     * @return Contact
-     */
-    public function setCreated($created)
-    {
-        $this->created = $created;
-    
-        return $this;
-    }
-
-    /**
-     * Get created
-     *
-     * @return \DateTime 
-     */
-    public function getCreated()
-    {
-        return $this->created;
-    }
-
-    /**
-     * Get id
-     *
-     * @return integer 
-     */
-    public function getId()
-    {
-        return $this->id;
-    }
-
-    /**
-     * Set Customer
-     *
-     * @param Entities\Customer $customer
-     * @return Contact
-     */
-    public function setCustomer(\Entities\Customer $customer = null)
-    {
-        $this->Customer = $customer;
-    
-        return $this;
-    }
-
-    /**
-     * Get Customer
-     *
-     * @return Entities\Customer 
-     */
-    public function getCustomer()
-    {
-        return $this->Customer;
-    }
-}
\ No newline at end of file
diff --git a/application/Entities/Customer.php b/application/Entities/Customer.php
deleted file mode 100644
index 778dd4436..000000000
--- a/application/Entities/Customer.php
+++ /dev/null
@@ -1,1569 +0,0 @@
- 'Full',
-        self::TYPE_ASSOCIATE => 'Associate',
-        self::TYPE_INTERNAL  => 'Internal',
-        self::TYPE_PROBONO   => 'Pro-bono'
-    );
-    
-    
-    const STATUS_NORMAL       = 1;
-    const STATUS_NOTCONNECTED = 2;
-    const STATUS_SUSPENDED    = 3;
-    
-    public static $CUST_STATUS_TEXT = array(
-        self::STATUS_NORMAL           => 'Normal',
-        self::STATUS_NOTCONNECTED     => 'Not Connected',
-        self::STATUS_SUSPENDED        => 'Suspended',
-    );
-    
-    const PEERING_POLICY_OPEN       = 'open';
-    const PEERING_POLICY_SELECTIVE  = 'selective';
-    const PEERING_POLICY_MANDATORY  = 'mandatory';
-    const PEERING_POLICY_CLOSED     = 'closed';
-    
-    public static $PEERING_POLICIES = array(
-        self::PEERING_POLICY_OPEN       => 'open',
-        self::PEERING_POLICY_SELECTIVE  => 'selective',
-        self::PEERING_POLICY_MANDATORY  => 'mandatory',
-        self::PEERING_POLICY_CLOSED     => 'closed'
-    );
-
-    const NOC_HOURS_24x7 = '24x7';    
-    const NOC_HOURS_8x5  = '8x5';    
-    const NOC_HOURS_8x7  = '8x7';    
-    const NOC_HOURS_12x5 = '12x5';    
-    const NOC_HOURS_12x7 = '12x7';    
-    
-    public static $NOC_HOURS = array(
-        self::NOC_HOURS_24x7 => '24x7',
-        self::NOC_HOURS_8x5  => '8x5',
-        self::NOC_HOURS_8x7  => '8x7',
-        self::NOC_HOURS_12x5 => '12x5',
-        self::NOC_HOURS_12x7 => '12x7'
-    );
-    
-    
-    /**
-     * @var string $name
-     */
-    private $name;
-
-    /**
-     * @var integer $type
-     */
-    private $type;
-
-    /**
-     * @var string $shortname
-     */
-    private $shortname;
-
-    /**
-     * @var integer $autsys
-     */
-    private $autsys;
-
-    /**
-     * @var integer $maxprefixes
-     */
-    private $maxprefixes;
-
-    /**
-     * @var string $peeringemail
-     */
-    private $peeringemail;
-
-    /**
-     * @var string $nocphone
-     */
-    private $nocphone;
-
-    /**
-     * @var string $noc24hrphone
-     */
-    private $noc24hrphone;
-
-    /**
-     * @var string $nocfax
-     */
-    private $nocfax;
-
-    /**
-     * @var string $nocemail
-     */
-    private $nocemail;
-
-    /**
-     * @var string $nochours
-     */
-    private $nochours;
-
-    /**
-     * @var string $nocwww
-     */
-    private $nocwww;
-
-    /**
-     * @var integer $irrdb
-     */
-    private $irrdb;
-
-    /**
-     * @var string $peeringmacro
-     */
-    private $peeringmacro;
-
-    /**
-     * @var string $peeringpolicy
-     */
-    private $peeringpolicy;
-
-    /**
-     * @var string $billingContact
-     */
-    private $billingContact;
-
-    /**
-     * @var string $billingAddress1
-     */
-    private $billingAddress1;
-
-    /**
-     * @var string $billingAddress2
-     */
-    private $billingAddress2;
-
-    /**
-     * @var string $billingCity
-     */
-    private $billingCity;
-
-    /**
-     * @var string $billingCountry
-     */
-    private $billingCountry;
-
-    /**
-     * @var string $corpwww
-     */
-    private $corpwww;
-
-    /**
-     * @var \DateTime $datejoin
-     */
-    private $datejoin;
-
-    /**
-     * @var \DateTime $dateleave
-     */
-    private $dateleave;
-
-    /**
-     * @var integer $status
-     */
-    private $status;
-
-    /**
-     * @var boolean $activepeeringmatrix
-     */
-    private $activepeeringmatrix;
-
-    /**
-     * @var string $notes
-     */
-    private $notes;
-
-    /**
-     * @var \DateTime $lastupdated
-     */
-    private $lastupdated;
-
-    /**
-     * @var integer $lastupdatedby
-     */
-    private $lastupdatedby;
-
-    /**
-     * @var string $creator
-     */
-    private $creator;
-
-    /**
-     * @var \DateTime $created
-     */
-    private $created;
-
-    /**
-     * @var integer $id
-     */
-    private $id;
-
-    /**
-     * @var \Doctrine\Common\Collections\ArrayCollection
-     */
-    private $VirtualInterfaces;
-
-    /**
-     * @var \Doctrine\Common\Collections\ArrayCollection
-     */
-    private $Contacts;
-
-    /**
-     * @var \Doctrine\Common\Collections\ArrayCollection
-     */
-    private $ConsoleServerConnections;
-
-    /**
-     * @var \Doctrine\Common\Collections\ArrayCollection
-     */
-    private $CustomerEquipment;
-
-    /**
-     * @var \Doctrine\Common\Collections\ArrayCollection
-     */
-    private $Peers;
-
-    /**
-     * @var \Doctrine\Common\Collections\ArrayCollection
-     */
-    private $PeersWith;
-
-    /**
-     * @var \Doctrine\Common\Collections\ArrayCollection
-     */
-    private $XCusts;
-
-    /**
-     * @var \Doctrine\Common\Collections\ArrayCollection
-     */
-    private $YCusts;
-
-    /**
-     * @var \Doctrine\Common\Collections\ArrayCollection
-     */
-    private $RSDroppedPrefixes;
-
-    /**
-     * @var \Doctrine\Common\Collections\ArrayCollection
-     */
-    private $Users;
-
-    /**
-     * @var \Doctrine\Common\Collections\ArrayCollection
-     */
-    private $Traffic95ths;
-
-    /**
-     * @var \Doctrine\Common\Collections\ArrayCollection
-     */
-    private $Traffic95thMonthlys;
-
-    /**
-     * @var \Doctrine\Common\Collections\ArrayCollection
-     */
-    private $TrafficDailies;
-
-    /**
-     * Constructor
-     */
-    public function __construct()
-    {
-        $this->VirtualInterfaces = new \Doctrine\Common\Collections\ArrayCollection();
-        $this->Contacts = new \Doctrine\Common\Collections\ArrayCollection();
-        $this->ConsoleServerConnections = new \Doctrine\Common\Collections\ArrayCollection();
-        $this->CustomerEquipment = new \Doctrine\Common\Collections\ArrayCollection();
-        $this->Peers = new \Doctrine\Common\Collections\ArrayCollection();
-        $this->PeersWith = new \Doctrine\Common\Collections\ArrayCollection();
-        $this->XCusts = new \Doctrine\Common\Collections\ArrayCollection();
-        $this->YCusts = new \Doctrine\Common\Collections\ArrayCollection();
-        $this->RSDroppedPrefixes = new \Doctrine\Common\Collections\ArrayCollection();
-        $this->Users = new \Doctrine\Common\Collections\ArrayCollection();
-        $this->Traffic95ths = new \Doctrine\Common\Collections\ArrayCollection();
-        $this->Traffic95thMonthlys = new \Doctrine\Common\Collections\ArrayCollection();
-        $this->TrafficDailies = new \Doctrine\Common\Collections\ArrayCollection();
-    }
-    
-    /**
-     * Set name
-     *
-     * @param string $name
-     * @return Customer
-     */
-    public function setName($name)
-    {
-        $this->name = $name;
-    
-        return $this;
-    }
-
-    /**
-     * Get name
-     *
-     * @return string
-     */
-    public function getName()
-    {
-        return $this->name;
-    }
-
-    /**
-     * Set type
-     *
-     * @param integer $type
-     * @return Customer
-     */
-    public function setType($type)
-    {
-        $this->type = $type;
-    
-        return $this;
-    }
-
-    /**
-     * Get type
-     *
-     * @return integer
-     */
-    public function getType()
-    {
-        return $this->type;
-    }
-
-    /**
-     * Set shortname
-     *
-     * @param string $shortname
-     * @return Customer
-     */
-    public function setShortname($shortname)
-    {
-        $this->shortname = $shortname;
-    
-        return $this;
-    }
-
-    /**
-     * Get shortname
-     *
-     * @return string
-     */
-    public function getShortname()
-    {
-        return $this->shortname;
-    }
-
-    /**
-     * Set autsys
-     *
-     * @param integer $autsys
-     * @return Customer
-     */
-    public function setAutsys($autsys)
-    {
-        $this->autsys = $autsys;
-    
-        return $this;
-    }
-
-    /**
-     * Get autsys
-     *
-     * @return integer
-     */
-    public function getAutsys()
-    {
-        return $this->autsys;
-    }
-
-    /**
-     * Set maxprefixes
-     *
-     * @param integer $maxprefixes
-     * @return Customer
-     */
-    public function setMaxprefixes($maxprefixes)
-    {
-        $this->maxprefixes = $maxprefixes;
-    
-        return $this;
-    }
-
-    /**
-     * Get maxprefixes
-     *
-     * @return integer
-     */
-    public function getMaxprefixes()
-    {
-        return $this->maxprefixes;
-    }
-
-    /**
-     * Set peeringemail
-     *
-     * @param string $peeringemail
-     * @return Customer
-     */
-    public function setPeeringemail($peeringemail)
-    {
-        $this->peeringemail = $peeringemail;
-    
-        return $this;
-    }
-
-    /**
-     * Get peeringemail
-     *
-     * @return string
-     */
-    public function getPeeringemail()
-    {
-        return $this->peeringemail;
-    }
-
-    /**
-     * Set nocphone
-     *
-     * @param string $nocphone
-     * @return Customer
-     */
-    public function setNocphone($nocphone)
-    {
-        $this->nocphone = $nocphone;
-    
-        return $this;
-    }
-
-    /**
-     * Get nocphone
-     *
-     * @return string
-     */
-    public function getNocphone()
-    {
-        return $this->nocphone;
-    }
-
-    /**
-     * Set noc24hrphone
-     *
-     * @param string $noc24hrphone
-     * @return Customer
-     */
-    public function setNoc24hrphone($noc24hrphone)
-    {
-        $this->noc24hrphone = $noc24hrphone;
-    
-        return $this;
-    }
-
-    /**
-     * Get noc24hrphone
-     *
-     * @return string
-     */
-    public function getNoc24hrphone()
-    {
-        return $this->noc24hrphone;
-    }
-
-    /**
-     * Set nocfax
-     *
-     * @param string $nocfax
-     * @return Customer
-     */
-    public function setNocfax($nocfax)
-    {
-        $this->nocfax = $nocfax;
-    
-        return $this;
-    }
-
-    /**
-     * Get nocfax
-     *
-     * @return string
-     */
-    public function getNocfax()
-    {
-        return $this->nocfax;
-    }
-
-    /**
-     * Set nocemail
-     *
-     * @param string $nocemail
-     * @return Customer
-     */
-    public function setNocemail($nocemail)
-    {
-        $this->nocemail = $nocemail;
-    
-        return $this;
-    }
-
-    /**
-     * Get nocemail
-     *
-     * @return string
-     */
-    public function getNocemail()
-    {
-        return $this->nocemail;
-    }
-
-    /**
-     * Set nochours
-     *
-     * @param string $nochours
-     * @return Customer
-     */
-    public function setNochours($nochours)
-    {
-        $this->nochours = $nochours;
-    
-        return $this;
-    }
-
-    /**
-     * Get nochours
-     *
-     * @return string
-     */
-    public function getNochours()
-    {
-        return $this->nochours;
-    }
-
-    /**
-     * Set nocwww
-     *
-     * @param string $nocwww
-     * @return Customer
-     */
-    public function setNocwww($nocwww)
-    {
-        $this->nocwww = $nocwww;
-    
-        return $this;
-    }
-
-    /**
-     * Get nocwww
-     *
-     * @return string
-     */
-    public function getNocwww()
-    {
-        return $this->nocwww;
-    }
-
-    /**
-     * Set irrdb
-     *
-     * @param integer $irrdb
-     * @return Customer
-     */
-    public function setIrrdb($irrdb)
-    {
-        $this->irrdb = $irrdb;
-    
-        return $this;
-    }
-
-    /**
-     * Get irrdb
-     *
-     * @return integer
-     */
-    public function getIrrdb()
-    {
-        return $this->irrdb;
-    }
-
-    /**
-     * Set peeringmacro
-     *
-     * @param string $peeringmacro
-     * @return Customer
-     */
-    public function setPeeringmacro($peeringmacro)
-    {
-        $this->peeringmacro = $peeringmacro;
-    
-        return $this;
-    }
-
-    /**
-     * Get peeringmacro
-     *
-     * @return string
-     */
-    public function getPeeringmacro()
-    {
-        return $this->peeringmacro;
-    }
-
-    /**
-     * Set peeringpolicy
-     *
-     * @param string $peeringpolicy
-     * @return Customer
-     */
-    public function setPeeringpolicy($peeringpolicy)
-    {
-        $this->peeringpolicy = $peeringpolicy;
-    
-        return $this;
-    }
-
-    /**
-     * Get peeringpolicy
-     *
-     * @return string
-     */
-    public function getPeeringpolicy()
-    {
-        return $this->peeringpolicy;
-    }
-
-    /**
-     * Set billingContact
-     *
-     * @param string $billingContact
-     * @return Customer
-     */
-    public function setBillingContact($billingContact)
-    {
-        $this->billingContact = $billingContact;
-    
-        return $this;
-    }
-
-    /**
-     * Get billingContact
-     *
-     * @return string
-     */
-    public function getBillingContact()
-    {
-        return $this->billingContact;
-    }
-
-    /**
-     * Set billingAddress1
-     *
-     * @param string $billingAddress1
-     * @return Customer
-     */
-    public function setBillingAddress1($billingAddress1)
-    {
-        $this->billingAddress1 = $billingAddress1;
-    
-        return $this;
-    }
-
-    /**
-     * Get billingAddress1
-     *
-     * @return string
-     */
-    public function getBillingAddress1()
-    {
-        return $this->billingAddress1;
-    }
-
-    /**
-     * Set billingAddress2
-     *
-     * @param string $billingAddress2
-     * @return Customer
-     */
-    public function setBillingAddress2($billingAddress2)
-    {
-        $this->billingAddress2 = $billingAddress2;
-    
-        return $this;
-    }
-
-    /**
-     * Get billingAddress2
-     *
-     * @return string
-     */
-    public function getBillingAddress2()
-    {
-        return $this->billingAddress2;
-    }
-
-    /**
-     * Set billingCity
-     *
-     * @param string $billingCity
-     * @return Customer
-     */
-    public function setBillingCity($billingCity)
-    {
-        $this->billingCity = $billingCity;
-    
-        return $this;
-    }
-
-    /**
-     * Get billingCity
-     *
-     * @return string
-     */
-    public function getBillingCity()
-    {
-        return $this->billingCity;
-    }
-
-    /**
-     * Set billingCountry
-     *
-     * @param string $billingCountry
-     * @return Customer
-     */
-    public function setBillingCountry($billingCountry)
-    {
-        $this->billingCountry = $billingCountry;
-    
-        return $this;
-    }
-
-    /**
-     * Get billingCountry
-     *
-     * @return string
-     */
-    public function getBillingCountry()
-    {
-        return $this->billingCountry;
-    }
-
-    /**
-     * Set corpwww
-     *
-     * @param string $corpwww
-     * @return Customer
-     */
-    public function setCorpwww($corpwww)
-    {
-        $this->corpwww = $corpwww;
-    
-        return $this;
-    }
-
-    /**
-     * Get corpwww
-     *
-     * @return string
-     */
-    public function getCorpwww()
-    {
-        return $this->corpwww;
-    }
-
-    /**
-     * Set datejoin
-     *
-     * @param \DateTime $datejoin
-     * @return Customer
-     */
-    public function setDatejoin($datejoin)
-    {
-        $this->datejoin = $datejoin;
-    
-        return $this;
-    }
-
-    /**
-     * Get datejoin
-     *
-     * @return \DateTime
-     */
-    public function getDatejoin()
-    {
-        return $this->datejoin;
-    }
-
-    /**
-     * Set dateleave
-     *
-     * @param \DateTime $dateleave
-     * @return Customer
-     */
-    public function setDateleave($dateleave)
-    {
-        $this->dateleave = $dateleave;
-    
-        return $this;
-    }
-
-    /**
-     * Get dateleave
-     *
-     * @return \DateTime
-     */
-    public function getDateleave()
-    {
-        // on 64bit system, MySQL's '0000-00-00' is in range and evaluates as a non-zero
-        // date - see: https://bugs.php.net/bug.php?id=60257
-        
-        if( PHP_INT_SIZE == 4 )
-            return $this->dateleave;
-            
-        if( $this->dateleave instanceof \DateTime && $this->dateleave->format( 'Y-m-d' ) == '-0001-11-30' )  // 0000-00-00 00:00:00 on 64bit systems
-            return null;
-                                            
-        return $this->dateleave;
-    }
-
-    /**
-     * Set status
-     *
-     * @param integer $status
-     * @return Customer
-     */
-    public function setStatus($status)
-    {
-        $this->status = $status;
-    
-        return $this;
-    }
-
-    /**
-     * Get status
-     *
-     * @return integer
-     */
-    public function getStatus()
-    {
-        return $this->status;
-    }
-
-    /**
-     * Set activepeeringmatrix
-     *
-     * @param boolean $activepeeringmatrix
-     * @return Customer
-     */
-    public function setActivepeeringmatrix($activepeeringmatrix)
-    {
-        $this->activepeeringmatrix = $activepeeringmatrix;
-    
-        return $this;
-    }
-
-    /**
-     * Get activepeeringmatrix
-     *
-     * @return boolean
-     */
-    public function getActivepeeringmatrix()
-    {
-        return $this->activepeeringmatrix;
-    }
-
-    /**
-     * Set notes
-     *
-     * @param string $notes
-     * @return Customer
-     */
-    public function setNotes($notes)
-    {
-        $this->notes = $notes;
-    
-        return $this;
-    }
-
-    /**
-     * Get notes
-     *
-     * @return string
-     */
-    public function getNotes()
-    {
-        return $this->notes;
-    }
-
-    /**
-     * Set lastupdated
-     *
-     * @param \DateTime $lastupdated
-     * @return Customer
-     */
-    public function setLastupdated($lastupdated)
-    {
-        $this->lastupdated = $lastupdated;
-    
-        return $this;
-    }
-
-    /**
-     * Get lastupdated
-     *
-     * @return \DateTime
-     */
-    public function getLastupdated()
-    {
-        return $this->lastupdated;
-    }
-
-    /**
-     * Set lastupdatedby
-     *
-     * @param integer $lastupdatedby
-     * @return Customer
-     */
-    public function setLastupdatedby($lastupdatedby)
-    {
-        $this->lastupdatedby = $lastupdatedby;
-    
-        return $this;
-    }
-
-    /**
-     * Get lastupdatedby
-     *
-     * @return integer
-     */
-    public function getLastupdatedby()
-    {
-        return $this->lastupdatedby;
-    }
-
-    /**
-     * Set creator
-     *
-     * @param string $creator
-     * @return Customer
-     */
-    public function setCreator($creator)
-    {
-        $this->creator = $creator;
-    
-        return $this;
-    }
-
-    /**
-     * Get creator
-     *
-     * @return string
-     */
-    public function getCreator()
-    {
-        return $this->creator;
-    }
-
-    /**
-     * Set created
-     *
-     * @param \DateTime $created
-     * @return Customer
-     */
-    public function setCreated($created)
-    {
-        $this->created = $created;
-    
-        return $this;
-    }
-
-    /**
-     * Get created
-     *
-     * @return \DateTime
-     */
-    public function getCreated()
-    {
-        return $this->created;
-    }
-
-    /**
-     * Get id
-     *
-     * @return integer
-     */
-    public function getId()
-    {
-        return $this->id;
-    }
-
-    /**
-     * Add VirtualInterfaces
-     *
-     * @param Entities\VirtualInterface $virtualInterfaces
-     * @return Customer
-     */
-    public function addVirtualInterface(\Entities\VirtualInterface $virtualInterfaces)
-    {
-        $this->VirtualInterfaces[] = $virtualInterfaces;
-    
-        return $this;
-    }
-
-    /**
-     * Remove VirtualInterfaces
-     *
-     * @param Entities\VirtualInterface $virtualInterfaces
-     */
-    public function removeVirtualInterface(\Entities\VirtualInterface $virtualInterfaces)
-    {
-        $this->VirtualInterfaces->removeElement($virtualInterfaces);
-    }
-
-    /**
-     * Get VirtualInterfaces
-     *
-     * @return Doctrine\Common\Collections\Collection
-     */
-    public function getVirtualInterfaces()
-    {
-        return $this->VirtualInterfaces;
-    }
-
-    /**
-     * Add Contacts
-     *
-     * @param Entities\Contact $contacts
-     * @return Customer
-     */
-    public function addContact(\Entities\Contact $contacts)
-    {
-        $this->Contacts[] = $contacts;
-    
-        return $this;
-    }
-
-    /**
-     * Remove Contacts
-     *
-     * @param Entities\Contact $contacts
-     */
-    public function removeContact(\Entities\Contact $contacts)
-    {
-        $this->Contacts->removeElement($contacts);
-    }
-
-    /**
-     * Get Contacts
-     *
-     * @return Doctrine\Common\Collections\Collection
-     */
-    public function getContacts()
-    {
-        return $this->Contacts;
-    }
-
-    /**
-     * Add ConsoleServerConnections
-     *
-     * @param Entities\ConsoleServerConnection $consoleServerConnections
-     * @return Customer
-     */
-    public function addConsoleServerConnection(\Entities\ConsoleServerConnection $consoleServerConnections)
-    {
-        $this->ConsoleServerConnections[] = $consoleServerConnections;
-    
-        return $this;
-    }
-
-    /**
-     * Remove ConsoleServerConnections
-     *
-     * @param Entities\ConsoleServerConnection $consoleServerConnections
-     */
-    public function removeConsoleServerConnection(\Entities\ConsoleServerConnection $consoleServerConnections)
-    {
-        $this->ConsoleServerConnections->removeElement($consoleServerConnections);
-    }
-
-    /**
-     * Get ConsoleServerConnections
-     *
-     * @return Doctrine\Common\Collections\Collection
-     */
-    public function getConsoleServerConnections()
-    {
-        return $this->ConsoleServerConnections;
-    }
-
-    /**
-     * Add CustomerEquipment
-     *
-     * @param Entities\CustomerEquipment $customerEquipment
-     * @return Customer
-     */
-    public function addCustomerEquipment(\Entities\CustomerEquipment $customerEquipment)
-    {
-        $this->CustomerEquipment[] = $customerEquipment;
-    
-        return $this;
-    }
-
-    /**
-     * Remove CustomerEquipment
-     *
-     * @param Entities\CustomerEquipment $customerEquipment
-     */
-    public function removeCustomerEquipment(\Entities\CustomerEquipment $customerEquipment)
-    {
-        $this->CustomerEquipment->removeElement($customerEquipment);
-    }
-
-    /**
-     * Get CustomerEquipment
-     *
-     * @return Doctrine\Common\Collections\Collection
-     */
-    public function getCustomerEquipment()
-    {
-        return $this->CustomerEquipment;
-    }
-
-    /**
-     * Add Peers
-     *
-     * @param Entities\PeeringManager $peers
-     * @return Customer
-     */
-    public function addPeer(\Entities\PeeringManager $peers)
-    {
-        $this->Peers[] = $peers;
-    
-        return $this;
-    }
-
-    /**
-     * Remove Peers
-     *
-     * @param Entities\PeeringManager $peers
-     */
-    public function removePeer(\Entities\PeeringManager $peers)
-    {
-        $this->Peers->removeElement($peers);
-    }
-
-    /**
-     * Get Peers
-     *
-     * @return Doctrine\Common\Collections\Collection
-     */
-    public function getPeers()
-    {
-        return $this->Peers;
-    }
-
-    /**
-     * Add PeersWith
-     *
-     * @param Entities\PeeringManager $peersWith
-     * @return Customer
-     */
-    public function addPeersWith(\Entities\PeeringManager $peersWith)
-    {
-        $this->PeersWith[] = $peersWith;
-    
-        return $this;
-    }
-
-    /**
-     * Remove PeersWith
-     *
-     * @param Entities\PeeringManager $peersWith
-     */
-    public function removePeersWith(\Entities\PeeringManager $peersWith)
-    {
-        $this->PeersWith->removeElement($peersWith);
-    }
-
-    /**
-     * Get PeersWith
-     *
-     * @return Doctrine\Common\Collections\Collection
-     */
-    public function getPeersWith()
-    {
-        return $this->PeersWith;
-    }
-
-    /**
-     * Add XCusts
-     *
-     * @param Entities\PeeringMatrix $xCusts
-     * @return Customer
-     */
-    public function addXCust(\Entities\PeeringMatrix $xCusts)
-    {
-        $this->XCusts[] = $xCusts;
-    
-        return $this;
-    }
-
-    /**
-     * Remove XCusts
-     *
-     * @param Entities\PeeringMatrix $xCusts
-     */
-    public function removeXCust(\Entities\PeeringMatrix $xCusts)
-    {
-        $this->XCusts->removeElement($xCusts);
-    }
-
-    /**
-     * Get XCusts
-     *
-     * @return Doctrine\Common\Collections\Collection
-     */
-    public function getXCusts()
-    {
-        return $this->XCusts;
-    }
-
-    /**
-     * Add YCusts
-     *
-     * @param Entities\PeeringMatrix $yCusts
-     * @return Customer
-     */
-    public function addYCust(\Entities\PeeringMatrix $yCusts)
-    {
-        $this->YCusts[] = $yCusts;
-    
-        return $this;
-    }
-
-    /**
-     * Remove YCusts
-     *
-     * @param Entities\PeeringMatrix $yCusts
-     */
-    public function removeYCust(\Entities\PeeringMatrix $yCusts)
-    {
-        $this->YCusts->removeElement($yCusts);
-    }
-
-    /**
-     * Get YCusts
-     *
-     * @return Doctrine\Common\Collections\Collection
-     */
-    public function getYCusts()
-    {
-        return $this->YCusts;
-    }
-
-    /**
-     * Add RSDroppedPrefixes
-     *
-     * @param Entities\RSDroppedPrefix $rSDroppedPrefixes
-     * @return Customer
-     */
-    public function addRSDroppedPrefixe(\Entities\RSDroppedPrefix $rSDroppedPrefixes)
-    {
-        $this->RSDroppedPrefixes[] = $rSDroppedPrefixes;
-    
-        return $this;
-    }
-
-    /**
-     * Remove RSDroppedPrefixes
-     *
-     * @param Entities\RSDroppedPrefix $rSDroppedPrefixes
-     */
-    public function removeRSDroppedPrefixe(\Entities\RSDroppedPrefix $rSDroppedPrefixes)
-    {
-        $this->RSDroppedPrefixes->removeElement($rSDroppedPrefixes);
-    }
-
-    /**
-     * Get RSDroppedPrefixes
-     *
-     * @return Doctrine\Common\Collections\Collection
-     */
-    public function getRSDroppedPrefixes()
-    {
-        return $this->RSDroppedPrefixes;
-    }
-
-    /**
-     * Add Users
-     *
-     * @param Entities\User $users
-     * @return Customer
-     */
-    public function addUser(\Entities\User $users)
-    {
-        $this->Users[] = $users;
-    
-        return $this;
-    }
-
-    /**
-     * Remove Users
-     *
-     * @param Entities\User $users
-     */
-    public function removeUser(\Entities\User $users)
-    {
-        $this->Users->removeElement($users);
-    }
-
-    /**
-     * Get Users
-     *
-     * @return Doctrine\Common\Collections\Collection
-     */
-    public function getUsers()
-    {
-        return $this->Users;
-    }
-
-    /**
-     * Add Traffic95ths
-     *
-     * @param Entities\Traffic95th $traffic95ths
-     * @return Customer
-     */
-    public function addTraffic95th(\Entities\Traffic95th $traffic95ths)
-    {
-        $this->Traffic95ths[] = $traffic95ths;
-    
-        return $this;
-    }
-
-    /**
-     * Remove Traffic95ths
-     *
-     * @param Entities\Traffic95th $traffic95ths
-     */
-    public function removeTraffic95th(\Entities\Traffic95th $traffic95ths)
-    {
-        $this->Traffic95ths->removeElement($traffic95ths);
-    }
-
-    /**
-     * Get Traffic95ths
-     *
-     * @return Doctrine\Common\Collections\Collection
-     */
-    public function getTraffic95ths()
-    {
-        return $this->Traffic95ths;
-    }
-
-    /**
-     * Add Traffic95thMonthlys
-     *
-     * @param Entities\Traffic95thMonthly $traffic95thMonthlys
-     * @return Customer
-     */
-    public function addTraffic95thMonthly(\Entities\Traffic95thMonthly $traffic95thMonthlys)
-    {
-        $this->Traffic95thMonthlys[] = $traffic95thMonthlys;
-    
-        return $this;
-    }
-
-    /**
-     * Remove Traffic95thMonthlys
-     *
-     * @param Entities\Traffic95thMonthly $traffic95thMonthlys
-     */
-    public function removeTraffic95thMonthly(\Entities\Traffic95thMonthly $traffic95thMonthlys)
-    {
-        $this->Traffic95thMonthlys->removeElement($traffic95thMonthlys);
-    }
-
-    /**
-     * Get Traffic95thMonthlys
-     *
-     * @return Doctrine\Common\Collections\Collection
-     */
-    public function getTraffic95thMonthlys()
-    {
-        return $this->Traffic95thMonthlys;
-    }
-
-    /**
-     * Add TrafficDailies
-     *
-     * @param Entities\TrafficDaily $trafficDailies
-     * @return Customer
-     */
-    public function addTrafficDailie(\Entities\TrafficDaily $trafficDailies)
-    {
-        $this->TrafficDailies[] = $trafficDailies;
-    
-        return $this;
-    }
-
-    /**
-     * Remove TrafficDailies
-     *
-     * @param Entities\TrafficDaily $trafficDailies
-     */
-    public function removeTrafficDailie(\Entities\TrafficDaily $trafficDailies)
-    {
-        $this->TrafficDailies->removeElement($trafficDailies);
-    }
-
-    /**
-     * Get TrafficDailies
-     *
-     * @return Doctrine\Common\Collections\Collection
-     */
-    public function getTrafficDailies()
-    {
-        return $this->TrafficDailies;
-    }
-    /**
-     * @var string $noc24hphone
-     */
-    private $noc24hphone;
-
-
-    /**
-     * Set noc24hphone
-     *
-     * @param string $noc24hphone
-     * @return Customer
-     */
-    public function setNoc24hphone($noc24hphone)
-    {
-        $this->noc24hphone = $noc24hphone;
-    
-        return $this;
-    }
-
-    /**
-     * Get noc24hphone
-     *
-     * @return string
-     */
-    public function getNoc24hphone()
-    {
-        return $this->noc24hphone;
-    }
-    /**
-     * @var \Doctrine\Common\Collections\ArrayCollection
-     */
-    private $SecEvents;
-
-
-    /**
-     * Add SecEvents
-     *
-     * @param Entities\SecEvent $secEvents
-     * @return Customer
-     */
-    public function addSecEvent(\Entities\SecEvent $secEvents)
-    {
-        $this->SecEvents[] = $secEvents;
-    
-        return $this;
-    }
-
-    /**
-     * Remove SecEvents
-     *
-     * @param Entities\SecEvent $secEvents
-     */
-    public function removeSecEvent(\Entities\SecEvent $secEvents)
-    {
-        $this->SecEvents->removeElement($secEvents);
-    }
-
-    /**
-     * Get SecEvents
-     *
-     * @return Doctrine\Common\Collections\Collection
-     */
-    public function getSecEvents()
-    {
-        return $this->SecEvents;
-    }
-    
-    
-    public function hasLeft()
-    {
-        // sigh. Using a date field to determine if an account is closed or not is a
-        // very bad idea and should be changed => FIXME
-        
-        return $this->getDateleave() != null;
-    }
-    
-    
-    /**
-     * Find all users of privilege CUSTADMIN for this customer
-     *
-     * @return \Entities\User[] Array of CUSTADMIN users
-     */
-    public function getAdminUsers()
-    {
-        $ausers = [];
-        
-        foreach( $this->getUsers() as $u )
-            if( $u->getPrivs() == \Entities\User::AUTH_CUSTADMIN )
-                $ausers[] = $u;
-        
-        return $ausers;
-    }
-    
-    /**
-     * Check if this customer is of the named type
-     * @return boolean
-     */
-    public function isTypeFull()
-    {
-        return $this->getType() == self::TYPE_FULL;
-    }
-
-    /**
-     * Check if this customer is of the named type
-     * @return boolean
-     */
-    public function isTypeAssociate()
-    {
-        return $this->getType() == self::TYPE_ASSOCIATE;
-    }
-
-    /**
-     * Check if this customer is of the named type
-     * @return boolean
-     */
-    public function isTypeInternal()
-    {
-        return $this->getType() == self::TYPE_INTERNAL;
-    }
-
-    /**
-     * Check if this customer is of the named type
-     * @return boolean
-     */
-    public function isTypeProBono()
-    {
-        return $this->getType() == self::TYPE_PROBONO;
-    }
-
-
-}
\ No newline at end of file
diff --git a/application/Entities/CustomerEquipment.php b/application/Entities/CustomerEquipment.php
deleted file mode 100644
index 4c42158bd..000000000
--- a/application/Entities/CustomerEquipment.php
+++ /dev/null
@@ -1,167 +0,0 @@
-name = $name;
-    
-        return $this;
-    }
-
-    /**
-     * Get name
-     *
-     * @return string 
-     */
-    public function getName()
-    {
-        return $this->name;
-    }
-
-    /**
-     * Set description
-     *
-     * @param string $description
-     * @return CustomerEquipment
-     */
-    public function setDescription($description)
-    {
-        $this->description = $description;
-    
-        return $this;
-    }
-
-    /**
-     * Get description
-     *
-     * @return string 
-     */
-    public function getDescription()
-    {
-        return $this->description;
-    }
-
-    /**
-     * Get id
-     *
-     * @return integer 
-     */
-    public function getId()
-    {
-        return $this->id;
-    }
-
-    /**
-     * Set Customer
-     *
-     * @param Entities\Customer $customer
-     * @return CustomerEquipment
-     */
-    public function setCustomer(\Entities\Customer $customer = null)
-    {
-        $this->Customer = $customer;
-    
-        return $this;
-    }
-
-    /**
-     * Get Customer
-     *
-     * @return Entities\Customer 
-     */
-    public function getCustomer()
-    {
-        return $this->Customer;
-    }
-
-    /**
-     * Set Cabinet
-     *
-     * @param Entities\Cabinet $cabinet
-     * @return CustomerEquipment
-     */
-    public function setCabinet(\Entities\Cabinet $cabinet = null)
-    {
-        $this->Cabinet = $cabinet;
-    
-        return $this;
-    }
-
-    /**
-     * Get Cabinet
-     *
-     * @return Entities\Cabinet 
-     */
-    public function getCabinet()
-    {
-        return $this->Cabinet;
-    }
-    /**
-     * @var string $descr
-     */
-    private $descr;
-
-
-    /**
-     * Set descr
-     *
-     * @param string $descr
-     * @return CustomerEquipment
-     */
-    public function setDescr($descr)
-    {
-        $this->descr = $descr;
-    
-        return $this;
-    }
-
-    /**
-     * Get descr
-     *
-     * @return string 
-     */
-    public function getDescr()
-    {
-        return $this->descr;
-    }
-}
\ No newline at end of file
diff --git a/application/Entities/IPv4Address.php b/application/Entities/IPv4Address.php
deleted file mode 100644
index efdf80003..000000000
--- a/application/Entities/IPv4Address.php
+++ /dev/null
@@ -1,111 +0,0 @@
-address = $address;
-    
-        return $this;
-    }
-
-    /**
-     * Get address
-     *
-     * @return string 
-     */
-    public function getAddress()
-    {
-        return $this->address;
-    }
-
-    /**
-     * Get id
-     *
-     * @return integer 
-     */
-    public function getId()
-    {
-        return $this->id;
-    }
-
-    /**
-     * Set VlanInterface
-     *
-     * @param Entities\VlanInterface $vlanInterface
-     * @return IPv4Address
-     */
-    public function setVlanInterface(\Entities\VlanInterface $vlanInterface = null)
-    {
-        $this->VlanInterface = $vlanInterface;
-    
-        return $this;
-    }
-
-    /**
-     * Get VlanInterface
-     *
-     * @return Entities\VlanInterface 
-     */
-    public function getVlanInterface()
-    {
-        return $this->VlanInterface;
-    }
-
-    /**
-     * Set Vlan
-     *
-     * @param Entities\Vlan $vlan
-     * @return IPv4Address
-     */
-    public function setVlan(\Entities\Vlan $vlan = null)
-    {
-        $this->Vlan = $vlan;
-    
-        return $this;
-    }
-
-    /**
-     * Get Vlan
-     *
-     * @return Entities\Vlan 
-     */
-    public function getVlan()
-    {
-        return $this->Vlan;
-    }
-}
\ No newline at end of file
diff --git a/application/Entities/IPv6Address.php b/application/Entities/IPv6Address.php
deleted file mode 100644
index c93e18e8d..000000000
--- a/application/Entities/IPv6Address.php
+++ /dev/null
@@ -1,111 +0,0 @@
-address = $address;
-    
-        return $this;
-    }
-
-    /**
-     * Get address
-     *
-     * @return string 
-     */
-    public function getAddress()
-    {
-        return $this->address;
-    }
-
-    /**
-     * Get id
-     *
-     * @return integer 
-     */
-    public function getId()
-    {
-        return $this->id;
-    }
-
-    /**
-     * Set VlanInterface
-     *
-     * @param Entities\VlanInterface $vlanInterface
-     * @return IPv6Address
-     */
-    public function setVlanInterface(\Entities\VlanInterface $vlanInterface = null)
-    {
-        $this->VlanInterface = $vlanInterface;
-    
-        return $this;
-    }
-
-    /**
-     * Get VlanInterface
-     *
-     * @return Entities\VlanInterface 
-     */
-    public function getVlanInterface()
-    {
-        return $this->VlanInterface;
-    }
-
-    /**
-     * Set Vlan
-     *
-     * @param Entities\Vlan $vlan
-     * @return IPv6Address
-     */
-    public function setVlan(\Entities\Vlan $vlan = null)
-    {
-        $this->Vlan = $vlan;
-    
-        return $this;
-    }
-
-    /**
-     * Get Vlan
-     *
-     * @return Entities\Vlan 
-     */
-    public function getVlan()
-    {
-        return $this->Vlan;
-    }
-}
\ No newline at end of file
diff --git a/application/Entities/IRRDBConfig.php b/application/Entities/IRRDBConfig.php
deleted file mode 100644
index bef4e44fe..000000000
--- a/application/Entities/IRRDBConfig.php
+++ /dev/null
@@ -1,139 +0,0 @@
-host = $host;
-    
-        return $this;
-    }
-
-    /**
-     * Get host
-     *
-     * @return string 
-     */
-    public function getHost()
-    {
-        return $this->host;
-    }
-
-    /**
-     * Set protocol
-     *
-     * @param string $protocol
-     * @return IRRDBConfig
-     */
-    public function setProtocol($protocol)
-    {
-        $this->protocol = $protocol;
-    
-        return $this;
-    }
-
-    /**
-     * Get protocol
-     *
-     * @return string 
-     */
-    public function getProtocol()
-    {
-        return $this->protocol;
-    }
-
-    /**
-     * Set source
-     *
-     * @param string $source
-     * @return IRRDBConfig
-     */
-    public function setSource($source)
-    {
-        $this->source = $source;
-    
-        return $this;
-    }
-
-    /**
-     * Get source
-     *
-     * @return string 
-     */
-    public function getSource()
-    {
-        return $this->source;
-    }
-
-    /**
-     * Set notes
-     *
-     * @param string $notes
-     * @return IRRDBConfig
-     */
-    public function setNotes($notes)
-    {
-        $this->notes = $notes;
-    
-        return $this;
-    }
-
-    /**
-     * Get notes
-     *
-     * @return string 
-     */
-    public function getNotes()
-    {
-        return $this->notes;
-    }
-
-    /**
-     * Get id
-     *
-     * @return integer 
-     */
-    public function getId()
-    {
-        return $this->id;
-    }
-}
\ No newline at end of file
diff --git a/application/Entities/Location.php b/application/Entities/Location.php
deleted file mode 100644
index cbfe5a994..000000000
--- a/application/Entities/Location.php
+++ /dev/null
@@ -1,380 +0,0 @@
-Cabinets = new \Doctrine\Common\Collections\ArrayCollection();
-    }
-    
-    /**
-     * Set name
-     *
-     * @param string $name
-     * @return Location
-     */
-    public function setName($name)
-    {
-        $this->name = $name;
-    
-        return $this;
-    }
-
-    /**
-     * Get name
-     *
-     * @return string 
-     */
-    public function getName()
-    {
-        return $this->name;
-    }
-
-    /**
-     * Set shortname
-     *
-     * @param string $shortname
-     * @return Location
-     */
-    public function setShortname($shortname)
-    {
-        $this->shortname = $shortname;
-    
-        return $this;
-    }
-
-    /**
-     * Get shortname
-     *
-     * @return string 
-     */
-    public function getShortname()
-    {
-        return $this->shortname;
-    }
-
-    /**
-     * Set tag
-     *
-     * @param string $tag
-     * @return Location
-     */
-    public function setTag($tag)
-    {
-        $this->tag = $tag;
-    
-        return $this;
-    }
-
-    /**
-     * Get tag
-     *
-     * @return string 
-     */
-    public function getTag()
-    {
-        return $this->tag;
-    }
-
-    /**
-     * Set address
-     *
-     * @param string $address
-     * @return Location
-     */
-    public function setAddress($address)
-    {
-        $this->address = $address;
-    
-        return $this;
-    }
-
-    /**
-     * Get address
-     *
-     * @return string 
-     */
-    public function getAddress()
-    {
-        return $this->address;
-    }
-
-    /**
-     * Set nocphone
-     *
-     * @param string $nocphone
-     * @return Location
-     */
-    public function setNocphone($nocphone)
-    {
-        $this->nocphone = $nocphone;
-    
-        return $this;
-    }
-
-    /**
-     * Get nocphone
-     *
-     * @return string 
-     */
-    public function getNocphone()
-    {
-        return $this->nocphone;
-    }
-
-    /**
-     * Set nocfax
-     *
-     * @param string $nocfax
-     * @return Location
-     */
-    public function setNocfax($nocfax)
-    {
-        $this->nocfax = $nocfax;
-    
-        return $this;
-    }
-
-    /**
-     * Get nocfax
-     *
-     * @return string 
-     */
-    public function getNocfax()
-    {
-        return $this->nocfax;
-    }
-
-    /**
-     * Set nocemail
-     *
-     * @param string $nocemail
-     * @return Location
-     */
-    public function setNocemail($nocemail)
-    {
-        $this->nocemail = $nocemail;
-    
-        return $this;
-    }
-
-    /**
-     * Get nocemail
-     *
-     * @return string 
-     */
-    public function getNocemail()
-    {
-        return $this->nocemail;
-    }
-
-    /**
-     * Set officephone
-     *
-     * @param string $officephone
-     * @return Location
-     */
-    public function setOfficephone($officephone)
-    {
-        $this->officephone = $officephone;
-    
-        return $this;
-    }
-
-    /**
-     * Get officephone
-     *
-     * @return string 
-     */
-    public function getOfficephone()
-    {
-        return $this->officephone;
-    }
-
-    /**
-     * Set officefax
-     *
-     * @param string $officefax
-     * @return Location
-     */
-    public function setOfficefax($officefax)
-    {
-        $this->officefax = $officefax;
-    
-        return $this;
-    }
-
-    /**
-     * Get officefax
-     *
-     * @return string 
-     */
-    public function getOfficefax()
-    {
-        return $this->officefax;
-    }
-
-    /**
-     * Set officeemail
-     *
-     * @param string $officeemail
-     * @return Location
-     */
-    public function setOfficeemail($officeemail)
-    {
-        $this->officeemail = $officeemail;
-    
-        return $this;
-    }
-
-    /**
-     * Get officeemail
-     *
-     * @return string 
-     */
-    public function getOfficeemail()
-    {
-        return $this->officeemail;
-    }
-
-    /**
-     * Get id
-     *
-     * @return integer 
-     */
-    public function getId()
-    {
-        return $this->id;
-    }
-
-    /**
-     * Add Cabinets
-     *
-     * @param Entities\Cabinet $cabinets
-     * @return Location
-     */
-    public function addCabinet(\Entities\Cabinet $cabinets)
-    {
-        $this->Cabinets[] = $cabinets;
-    
-        return $this;
-    }
-
-    /**
-     * Remove Cabinets
-     *
-     * @param Entities\Cabinet $cabinets
-     */
-    public function removeCabinet(\Entities\Cabinet $cabinets)
-    {
-        $this->Cabinets->removeElement($cabinets);
-    }
-
-    /**
-     * Get Cabinets
-     *
-     * @return Doctrine\Common\Collections\Collection 
-     */
-    public function getCabinets()
-    {
-        return $this->Cabinets;
-    }
-    /**
-     * @var string $notes
-     */
-    private $notes;
-
-
-    /**
-     * Set notes
-     *
-     * @param string $notes
-     * @return Location
-     */
-    public function setNotes($notes)
-    {
-        $this->notes = $notes;
-    
-        return $this;
-    }
-
-    /**
-     * Get notes
-     *
-     * @return string 
-     */
-    public function getNotes()
-    {
-        return $this->notes;
-    }
-}
\ No newline at end of file
diff --git a/application/Entities/MACAddress.php b/application/Entities/MACAddress.php
deleted file mode 100644
index 95a06b438..000000000
--- a/application/Entities/MACAddress.php
+++ /dev/null
@@ -1,139 +0,0 @@
-firstseen = $firstseen;
-    
-        return $this;
-    }
-
-    /**
-     * Get firstseen
-     *
-     * @return \DateTime 
-     */
-    public function getFirstseen()
-    {
-        return $this->firstseen;
-    }
-
-    /**
-     * Set lastseen
-     *
-     * @param \DateTime $lastseen
-     * @return MACAddress
-     */
-    public function setLastseen($lastseen)
-    {
-        $this->lastseen = $lastseen;
-    
-        return $this;
-    }
-
-    /**
-     * Get lastseen
-     *
-     * @return \DateTime 
-     */
-    public function getLastseen()
-    {
-        return $this->lastseen;
-    }
-
-    /**
-     * Set mac
-     *
-     * @param string $mac
-     * @return MACAddress
-     */
-    public function setMac($mac)
-    {
-        $this->mac = $mac;
-    
-        return $this;
-    }
-
-    /**
-     * Get mac
-     *
-     * @return string 
-     */
-    public function getMac()
-    {
-        return $this->mac;
-    }
-
-    /**
-     * Get id
-     *
-     * @return integer 
-     */
-    public function getId()
-    {
-        return $this->id;
-    }
-
-    /**
-     * Set VirtualInterface
-     *
-     * @param Entities\VirtualInterface $virtualInterface
-     * @return MACAddress
-     */
-    public function setVirtualInterface(\Entities\VirtualInterface $virtualInterface = null)
-    {
-        $this->VirtualInterface = $virtualInterface;
-    
-        return $this;
-    }
-
-    /**
-     * Get VirtualInterface
-     *
-     * @return Entities\VirtualInterface 
-     */
-    public function getVirtualInterface()
-    {
-        return $this->VirtualInterface;
-    }
-}
\ No newline at end of file
diff --git a/application/Entities/Meeting.php b/application/Entities/Meeting.php
deleted file mode 100644
index 8ffcea44e..000000000
--- a/application/Entities/Meeting.php
+++ /dev/null
@@ -1,380 +0,0 @@
-MeetingItems = new \Doctrine\Common\Collections\ArrayCollection();
-    }
-    
-    /**
-     * Set title
-     *
-     * @param string $title
-     * @return Meeting
-     */
-    public function setTitle($title)
-    {
-        $this->title = $title;
-    
-        return $this;
-    }
-
-    /**
-     * Get title
-     *
-     * @return string 
-     */
-    public function getTitle()
-    {
-        return $this->title;
-    }
-
-    /**
-     * Set before_text
-     *
-     * @param string $beforeText
-     * @return Meeting
-     */
-    public function setBeforeText($beforeText)
-    {
-        $this->before_text = $beforeText;
-    
-        return $this;
-    }
-
-    /**
-     * Get before_text
-     *
-     * @return string 
-     */
-    public function getBeforeText()
-    {
-        return $this->before_text;
-    }
-
-    /**
-     * Set after_text
-     *
-     * @param string $afterText
-     * @return Meeting
-     */
-    public function setAfterText($afterText)
-    {
-        $this->after_text = $afterText;
-    
-        return $this;
-    }
-
-    /**
-     * Get after_text
-     *
-     * @return string 
-     */
-    public function getAfterText()
-    {
-        return $this->after_text;
-    }
-
-    /**
-     * Set date
-     *
-     * @param \DateTime $date
-     * @return Meeting
-     */
-    public function setDate($date)
-    {
-        $this->date = $date;
-    
-        return $this;
-    }
-
-    /**
-     * Get date
-     *
-     * @return \DateTime 
-     */
-    public function getDate()
-    {
-        return $this->date;
-    }
-
-    /**
-     * Set time
-     *
-     * @param \DateTime $time
-     * @return Meeting
-     */
-    public function setTime($time)
-    {
-        $this->time = $time;
-    
-        return $this;
-    }
-
-    /**
-     * Get time
-     *
-     * @return \DateTime 
-     */
-    public function getTime()
-    {
-        return $this->time;
-    }
-
-    /**
-     * Set venue
-     *
-     * @param string $venue
-     * @return Meeting
-     */
-    public function setVenue($venue)
-    {
-        $this->venue = $venue;
-    
-        return $this;
-    }
-
-    /**
-     * Get venue
-     *
-     * @return string 
-     */
-    public function getVenue()
-    {
-        return $this->venue;
-    }
-
-    /**
-     * Set venue_url
-     *
-     * @param string $venueUrl
-     * @return Meeting
-     */
-    public function setVenueUrl($venueUrl)
-    {
-        $this->venue_url = $venueUrl;
-    
-        return $this;
-    }
-
-    /**
-     * Get venue_url
-     *
-     * @return string 
-     */
-    public function getVenueUrl()
-    {
-        return $this->venue_url;
-    }
-
-    /**
-     * Set created_at
-     *
-     * @param \DateTime $createdAt
-     * @return Meeting
-     */
-    public function setCreatedAt($createdAt)
-    {
-        $this->created_at = $createdAt;
-    
-        return $this;
-    }
-
-    /**
-     * Get created_at
-     *
-     * @return \DateTime 
-     */
-    public function getCreatedAt()
-    {
-        return $this->created_at;
-    }
-
-    /**
-     * Set updated_by
-     *
-     * @param integer $updatedBy
-     * @return Meeting
-     */
-    public function setUpdatedBy($updatedBy)
-    {
-        $this->updated_by = $updatedBy;
-    
-        return $this;
-    }
-
-    /**
-     * Get updated_by
-     *
-     * @return integer 
-     */
-    public function getUpdatedBy()
-    {
-        return $this->updated_by;
-    }
-
-    /**
-     * Set updated_at
-     *
-     * @param \DateTime $updatedAt
-     * @return Meeting
-     */
-    public function setUpdatedAt($updatedAt)
-    {
-        $this->updated_at = $updatedAt;
-    
-        return $this;
-    }
-
-    /**
-     * Get updated_at
-     *
-     * @return \DateTime 
-     */
-    public function getUpdatedAt()
-    {
-        return $this->updated_at;
-    }
-
-    /**
-     * Get id
-     *
-     * @return integer 
-     */
-    public function getId()
-    {
-        return $this->id;
-    }
-
-    /**
-     * Add MeetingItems
-     *
-     * @param Entities\MeetingItem $meetingItems
-     * @return Meeting
-     */
-    public function addMeetingItem(\Entities\MeetingItem $meetingItems)
-    {
-        $this->MeetingItems[] = $meetingItems;
-    
-        return $this;
-    }
-
-    /**
-     * Remove MeetingItems
-     *
-     * @param Entities\MeetingItem $meetingItems
-     */
-    public function removeMeetingItem(\Entities\MeetingItem $meetingItems)
-    {
-        $this->MeetingItems->removeElement($meetingItems);
-    }
-
-    /**
-     * Get MeetingItems
-     *
-     * @return Doctrine\Common\Collections\Collection 
-     */
-    public function getMeetingItems()
-    {
-        return $this->MeetingItems;
-    }
-
-    /**
-     * Set CreatedBy
-     *
-     * @param Entities\User $createdBy
-     * @return Meeting
-     */
-    public function setCreatedBy(\Entities\User $createdBy = null)
-    {
-        $this->CreatedBy = $createdBy;
-    
-        return $this;
-    }
-
-    /**
-     * Get CreatedBy
-     *
-     * @return Entities\User 
-     */
-    public function getCreatedBy()
-    {
-        return $this->CreatedBy;
-    }
-}
diff --git a/application/Entities/MeetingItem.php b/application/Entities/MeetingItem.php
deleted file mode 100644
index 35d56285d..000000000
--- a/application/Entities/MeetingItem.php
+++ /dev/null
@@ -1,480 +0,0 @@
-title = $title;
-    
-        return $this;
-    }
-
-    /**
-     * Get title
-     *
-     * @return string 
-     */
-    public function getTitle()
-    {
-        return $this->title;
-    }
-
-    /**
-     * Set name
-     *
-     * @param string $name
-     * @return MeetingItem
-     */
-    public function setName($name)
-    {
-        $this->name = $name;
-    
-        return $this;
-    }
-
-    /**
-     * Get name
-     *
-     * @return string 
-     */
-    public function getName()
-    {
-        return $this->name;
-    }
-
-    /**
-     * Set role
-     *
-     * @param string $role
-     * @return MeetingItem
-     */
-    public function setRole($role)
-    {
-        $this->role = $role;
-    
-        return $this;
-    }
-
-    /**
-     * Get role
-     *
-     * @return string 
-     */
-    public function getRole()
-    {
-        return $this->role;
-    }
-
-    /**
-     * Set email
-     *
-     * @param string $email
-     * @return MeetingItem
-     */
-    public function setEmail($email)
-    {
-        $this->email = $email;
-    
-        return $this;
-    }
-
-    /**
-     * Get email
-     *
-     * @return string 
-     */
-    public function getEmail()
-    {
-        return $this->email;
-    }
-
-    /**
-     * Set company
-     *
-     * @param string $company
-     * @return MeetingItem
-     */
-    public function setCompany($company)
-    {
-        $this->company = $company;
-    
-        return $this;
-    }
-
-    /**
-     * Get company
-     *
-     * @return string 
-     */
-    public function getCompany()
-    {
-        return $this->company;
-    }
-
-    /**
-     * Set company_url
-     *
-     * @param string $companyUrl
-     * @return MeetingItem
-     */
-    public function setCompanyUrl($companyUrl)
-    {
-        $this->company_url = $companyUrl;
-    
-        return $this;
-    }
-
-    /**
-     * Get company_url
-     *
-     * @return string 
-     */
-    public function getCompanyUrl()
-    {
-        return $this->company_url;
-    }
-
-    /**
-     * Set summary
-     *
-     * @param string $summary
-     * @return MeetingItem
-     */
-    public function setSummary($summary)
-    {
-        $this->summary = $summary;
-    
-        return $this;
-    }
-
-    /**
-     * Get summary
-     *
-     * @return string 
-     */
-    public function getSummary()
-    {
-        return $this->summary;
-    }
-
-    /**
-     * Set presentation
-     *
-     * @param string $presentation
-     * @return MeetingItem
-     */
-    public function setPresentation($presentation)
-    {
-        $this->presentation = $presentation;
-    
-        return $this;
-    }
-
-    /**
-     * Get presentation
-     *
-     * @return string 
-     */
-    public function getPresentation()
-    {
-        return $this->presentation;
-    }
-
-    /**
-     * Set filename
-     *
-     * @param string $filename
-     * @return MeetingItem
-     */
-    public function setFilename($filename)
-    {
-        $this->filename = $filename;
-    
-        return $this;
-    }
-
-    /**
-     * Get filename
-     *
-     * @return string 
-     */
-    public function getFilename()
-    {
-        return $this->filename;
-    }
-
-    /**
-     * Set video_url
-     *
-     * @param string $videoUrl
-     * @return MeetingItem
-     */
-    public function setVideoUrl($videoUrl)
-    {
-        $this->video_url = $videoUrl;
-    
-        return $this;
-    }
-
-    /**
-     * Get video_url
-     *
-     * @return string 
-     */
-    public function getVideoUrl()
-    {
-        return $this->video_url;
-    }
-
-    /**
-     * Set other_content
-     *
-     * @param boolean $otherContent
-     * @return MeetingItem
-     */
-    public function setOtherContent($otherContent)
-    {
-        $this->other_content = $otherContent;
-    
-        return $this;
-    }
-
-    /**
-     * Get other_content
-     *
-     * @return boolean 
-     */
-    public function getOtherContent()
-    {
-        return $this->other_content;
-    }
-
-    /**
-     * Set created_by
-     *
-     * @param integer $createdBy
-     * @return MeetingItem
-     */
-    public function setCreatedBy($createdBy)
-    {
-        $this->created_by = $createdBy;
-    
-        return $this;
-    }
-
-    /**
-     * Get created_by
-     *
-     * @return integer 
-     */
-    public function getCreatedBy()
-    {
-        return $this->created_by;
-    }
-
-    /**
-     * Set created_at
-     *
-     * @param \DateTime $createdAt
-     * @return MeetingItem
-     */
-    public function setCreatedAt($createdAt)
-    {
-        $this->created_at = $createdAt;
-    
-        return $this;
-    }
-
-    /**
-     * Get created_at
-     *
-     * @return \DateTime 
-     */
-    public function getCreatedAt()
-    {
-        return $this->created_at;
-    }
-
-    /**
-     * Set updated_by
-     *
-     * @param integer $updatedBy
-     * @return MeetingItem
-     */
-    public function setUpdatedBy($updatedBy)
-    {
-        $this->updated_by = $updatedBy;
-    
-        return $this;
-    }
-
-    /**
-     * Get updated_by
-     *
-     * @return integer 
-     */
-    public function getUpdatedBy()
-    {
-        return $this->updated_by;
-    }
-
-    /**
-     * Get id
-     *
-     * @return integer 
-     */
-    public function getId()
-    {
-        return $this->id;
-    }
-
-    /**
-     * Set Meeting
-     *
-     * @param Entities\Meeting $meeting
-     * @return MeetingItem
-     */
-    public function setMeeting(\Entities\Meeting $meeting = null)
-    {
-        $this->Meeting = $meeting;
-    
-        return $this;
-    }
-
-    /**
-     * Get Meeting
-     *
-     * @return Entities\Meeting 
-     */
-    public function getMeeting()
-    {
-        return $this->Meeting;
-    }
-    /**
-     * @var \DateTime $updated_at
-     */
-    private $updated_at;
-
-
-    /**
-     * Set updated_at
-     *
-     * @param \DateTime $updatedAt
-     * @return MeetingItem
-     */
-    public function setUpdatedAt($updatedAt)
-    {
-        $this->updated_at = $updatedAt;
-    
-        return $this;
-    }
-
-    /**
-     * Get updated_at
-     *
-     * @return \DateTime 
-     */
-    public function getUpdatedAt()
-    {
-        return $this->updated_at;
-    }
-}
\ No newline at end of file
diff --git a/application/Entities/NetworkInfo.php b/application/Entities/NetworkInfo.php
deleted file mode 100644
index affc57430..000000000
--- a/application/Entities/NetworkInfo.php
+++ /dev/null
@@ -1,223 +0,0 @@
-protocol = $protocol;
-    
-        return $this;
-    }
-
-    /**
-     * Get protocol
-     *
-     * @return integer 
-     */
-    public function getProtocol()
-    {
-        return $this->protocol;
-    }
-
-    /**
-     * Set network
-     *
-     * @param string $network
-     * @return NetworkInfo
-     */
-    public function setNetwork($network)
-    {
-        $this->network = $network;
-    
-        return $this;
-    }
-
-    /**
-     * Get network
-     *
-     * @return string 
-     */
-    public function getNetwork()
-    {
-        return $this->network;
-    }
-
-    /**
-     * Set masklen
-     *
-     * @param integer $masklen
-     * @return NetworkInfo
-     */
-    public function setMasklen($masklen)
-    {
-        $this->masklen = $masklen;
-    
-        return $this;
-    }
-
-    /**
-     * Get masklen
-     *
-     * @return integer 
-     */
-    public function getMasklen()
-    {
-        return $this->masklen;
-    }
-
-    /**
-     * Set rs1address
-     *
-     * @param string $rs1address
-     * @return NetworkInfo
-     */
-    public function setRs1address($rs1address)
-    {
-        $this->rs1address = $rs1address;
-    
-        return $this;
-    }
-
-    /**
-     * Get rs1address
-     *
-     * @return string 
-     */
-    public function getRs1address()
-    {
-        return $this->rs1address;
-    }
-
-    /**
-     * Set rs2address
-     *
-     * @param string $rs2address
-     * @return NetworkInfo
-     */
-    public function setRs2address($rs2address)
-    {
-        $this->rs2address = $rs2address;
-    
-        return $this;
-    }
-
-    /**
-     * Get rs2address
-     *
-     * @return string 
-     */
-    public function getRs2address()
-    {
-        return $this->rs2address;
-    }
-
-    /**
-     * Set dnsfile
-     *
-     * @param string $dnsfile
-     * @return NetworkInfo
-     */
-    public function setDnsfile($dnsfile)
-    {
-        $this->dnsfile = $dnsfile;
-    
-        return $this;
-    }
-
-    /**
-     * Get dnsfile
-     *
-     * @return string 
-     */
-    public function getDnsfile()
-    {
-        return $this->dnsfile;
-    }
-
-    /**
-     * Get id
-     *
-     * @return integer 
-     */
-    public function getId()
-    {
-        return $this->id;
-    }
-
-    /**
-     * Set Vlan
-     *
-     * @param Entities\Vlan $vlan
-     * @return NetworkInfo
-     */
-    public function setVlan(\Entities\Vlan $vlan = null)
-    {
-        $this->Vlan = $vlan;
-    
-        return $this;
-    }
-
-    /**
-     * Get Vlan
-     *
-     * @return Entities\Vlan 
-     */
-    public function getVlan()
-    {
-        return $this->Vlan;
-    }
-}
\ No newline at end of file
diff --git a/application/Entities/PeeringManager.php b/application/Entities/PeeringManager.php
deleted file mode 100644
index d5e58c102..000000000
--- a/application/Entities/PeeringManager.php
+++ /dev/null
@@ -1,279 +0,0 @@
-email_last_sent = $emailLastSent;
-    
-        return $this;
-    }
-
-    /**
-     * Get email_last_sent
-     *
-     * @return \DateTime 
-     */
-    public function getEmailLastSent()
-    {
-        return $this->email_last_sent;
-    }
-
-    /**
-     * Set emails_sent
-     *
-     * @param integer $emailsSent
-     * @return PeeringManager
-     */
-    public function setEmailsSent($emailsSent)
-    {
-        $this->emails_sent = $emailsSent;
-    
-        return $this;
-    }
-
-    /**
-     * Get emails_sent
-     *
-     * @return integer 
-     */
-    public function getEmailsSent()
-    {
-        return $this->emails_sent;
-    }
-
-    /**
-     * Set peered
-     *
-     * @param boolean $peered
-     * @return PeeringManager
-     */
-    public function setPeered($peered)
-    {
-        $this->peered = $peered;
-    
-        return $this;
-    }
-
-    /**
-     * Get peered
-     *
-     * @return boolean 
-     */
-    public function getPeered()
-    {
-        return $this->peered;
-    }
-
-    /**
-     * Set rejected
-     *
-     * @param boolean $rejected
-     * @return PeeringManager
-     */
-    public function setRejected($rejected)
-    {
-        $this->rejected = $rejected;
-    
-        return $this;
-    }
-
-    /**
-     * Get rejected
-     *
-     * @return boolean 
-     */
-    public function getRejected()
-    {
-        return $this->rejected;
-    }
-
-    /**
-     * Set notes
-     *
-     * @param string $notes
-     * @return PeeringManager
-     */
-    public function setNotes($notes)
-    {
-        $this->notes = $notes;
-    
-        return $this;
-    }
-
-    /**
-     * Get notes
-     *
-     * @return string 
-     */
-    public function getNotes()
-    {
-        return $this->notes;
-    }
-
-    /**
-     * Get id
-     *
-     * @return integer 
-     */
-    public function getId()
-    {
-        return $this->id;
-    }
-
-    /**
-     * Set Customer
-     *
-     * @param Entities\Customer $customer
-     * @return PeeringManager
-     */
-    public function setCustomer(\Entities\Customer $customer = null)
-    {
-        $this->Customer = $customer;
-    
-        return $this;
-    }
-
-    /**
-     * Get Customer
-     *
-     * @return Entities\Customer 
-     */
-    public function getCustomer()
-    {
-        return $this->Customer;
-    }
-
-    /**
-     * Set Peer
-     *
-     * @param Entities\Customer $peer
-     * @return PeeringManager
-     */
-    public function setPeer(\Entities\Customer $peer = null)
-    {
-        $this->Peer = $peer;
-    
-        return $this;
-    }
-
-    /**
-     * Get Peer
-     *
-     * @return Entities\Customer 
-     */
-    public function getPeer()
-    {
-        return $this->Peer;
-    }
-    /**
-     * @var \DateTime $created
-     */
-    private $created;
-
-    /**
-     * @var \DateTime $updated
-     */
-    private $updated;
-
-
-    /**
-     * Set created
-     *
-     * @param \DateTime $created
-     * @return PeeringManager
-     */
-    public function setCreated($created)
-    {
-        $this->created = $created;
-    
-        return $this;
-    }
-
-    /**
-     * Get created
-     *
-     * @return \DateTime 
-     */
-    public function getCreated()
-    {
-        return $this->created;
-    }
-
-    /**
-     * Set updated
-     *
-     * @param \DateTime $updated
-     * @return PeeringManager
-     */
-    public function setUpdated($updated)
-    {
-        $this->updated = $updated;
-    
-        return $this;
-    }
-
-    /**
-     * Get updated
-     *
-     * @return \DateTime 
-     */
-    public function getUpdated()
-    {
-        return $this->updated;
-    }
-}
\ No newline at end of file
diff --git a/application/Entities/PeeringMatrix.php b/application/Entities/PeeringMatrix.php
deleted file mode 100644
index 3345a5524..000000000
--- a/application/Entities/PeeringMatrix.php
+++ /dev/null
@@ -1,223 +0,0 @@
-x_as = $xAs;
-    
-        return $this;
-    }
-
-    /**
-     * Get x_as
-     *
-     * @return integer 
-     */
-    public function getXAs()
-    {
-        return $this->x_as;
-    }
-
-    /**
-     * Set y_as
-     *
-     * @param integer $yAs
-     * @return PeeringMatrix
-     */
-    public function setYAs($yAs)
-    {
-        $this->y_as = $yAs;
-    
-        return $this;
-    }
-
-    /**
-     * Get y_as
-     *
-     * @return integer 
-     */
-    public function getYAs()
-    {
-        return $this->y_as;
-    }
-
-    /**
-     * Set peering_status
-     *
-     * @param string $peeringStatus
-     * @return PeeringMatrix
-     */
-    public function setPeeringStatus($peeringStatus)
-    {
-        $this->peering_status = $peeringStatus;
-    
-        return $this;
-    }
-
-    /**
-     * Get peering_status
-     *
-     * @return string 
-     */
-    public function getPeeringStatus()
-    {
-        return $this->peering_status;
-    }
-
-    /**
-     * Set updated
-     *
-     * @param \DateTime $updated
-     * @return PeeringMatrix
-     */
-    public function setUpdated($updated)
-    {
-        $this->updated = $updated;
-    
-        return $this;
-    }
-
-    /**
-     * Get updated
-     *
-     * @return \DateTime 
-     */
-    public function getUpdated()
-    {
-        return $this->updated;
-    }
-
-    /**
-     * Get id
-     *
-     * @return integer 
-     */
-    public function getId()
-    {
-        return $this->id;
-    }
-
-    /**
-     * Set XCustomer
-     *
-     * @param Entities\Customer $xCustomer
-     * @return PeeringMatrix
-     */
-    public function setXCustomer(\Entities\Customer $xCustomer = null)
-    {
-        $this->XCustomer = $xCustomer;
-    
-        return $this;
-    }
-
-    /**
-     * Get XCustomer
-     *
-     * @return Entities\Customer 
-     */
-    public function getXCustomer()
-    {
-        return $this->XCustomer;
-    }
-
-    /**
-     * Set YCustomer
-     *
-     * @param Entities\Customer $yCustomer
-     * @return PeeringMatrix
-     */
-    public function setYCustomer(\Entities\Customer $yCustomer = null)
-    {
-        $this->YCustomer = $yCustomer;
-    
-        return $this;
-    }
-
-    /**
-     * Get YCustomer
-     *
-     * @return Entities\Customer 
-     */
-    public function getYCustomer()
-    {
-        return $this->YCustomer;
-    }
-    /**
-     * @var integer $vlan
-     */
-    private $vlan;
-
-
-    /**
-     * Set vlan
-     *
-     * @param integer $vlan
-     * @return PeeringMatrix
-     */
-    public function setVlan($vlan)
-    {
-        $this->vlan = $vlan;
-    
-        return $this;
-    }
-
-    /**
-     * Get vlan
-     *
-     * @return integer 
-     */
-    public function getVlan()
-    {
-        return $this->vlan;
-    }
-}
\ No newline at end of file
diff --git a/application/Entities/PhysicalInterface.php b/application/Entities/PhysicalInterface.php
deleted file mode 100644
index b261436b4..000000000
--- a/application/Entities/PhysicalInterface.php
+++ /dev/null
@@ -1,250 +0,0 @@
- 'Connected',
-        self::STATUS_DISABLED     => 'Disabled',
-        self::STATUS_NOTCONNECTED => 'Not Connected',
-        self::STATUS_XCONNECT     => 'Awaiting X-Connect',
-        self::STATUS_QUARANTINE   => 'Quarantine'
-    );
-    
-    public static $SPEED = array(
-        10    => '10 Mbps',
-        100   => '100 Mbps',
-        1000  => '1 Gbps',
-        10000 => '10 Gbps'
-    );
-    
-    public static $DUPLEX = array(
-        'full'   => 'full',
-        'half'   => 'half'
-    );
-    
-    
-    /**
-     * @var integer $status
-     */
-    private $status;
-
-    /**
-     * @var integer $speed
-     */
-    private $speed;
-
-    /**
-     * @var string $duplex
-     */
-    private $duplex;
-
-    /**
-     * @var integer $monitorindex
-     */
-    private $monitorindex;
-
-    /**
-     * @var string $notes
-     */
-    private $notes;
-
-    /**
-     * @var integer $id
-     */
-    private $id;
-
-    /**
-     * @var Entities\SwitchPort
-     */
-    private $SwitchPort;
-
-    /**
-     * @var Entities\VirtualInterface
-     */
-    private $VirtualInterface;
-
-
-    /**
-     * Set status
-     *
-     * @param integer $status
-     * @return PhysicalInterface
-     */
-    public function setStatus($status)
-    {
-        $this->status = $status;
-    
-        return $this;
-    }
-
-    /**
-     * Get status
-     *
-     * @return integer
-     */
-    public function getStatus()
-    {
-        return $this->status;
-    }
-
-    /**
-     * Set speed
-     *
-     * @param integer $speed
-     * @return PhysicalInterface
-     */
-    public function setSpeed($speed)
-    {
-        $this->speed = $speed;
-    
-        return $this;
-    }
-
-    /**
-     * Get speed
-     *
-     * @return integer
-     */
-    public function getSpeed()
-    {
-        return $this->speed;
-    }
-
-    /**
-     * Set duplex
-     *
-     * @param string $duplex
-     * @return PhysicalInterface
-     */
-    public function setDuplex($duplex)
-    {
-        $this->duplex = $duplex;
-    
-        return $this;
-    }
-
-    /**
-     * Get duplex
-     *
-     * @return string
-     */
-    public function getDuplex()
-    {
-        return $this->duplex;
-    }
-
-    /**
-     * Set monitorindex
-     *
-     * @param integer $monitorindex
-     * @return PhysicalInterface
-     */
-    public function setMonitorindex($monitorindex)
-    {
-        $this->monitorindex = $monitorindex;
-    
-        return $this;
-    }
-
-    /**
-     * Get monitorindex
-     *
-     * @return integer
-     */
-    public function getMonitorindex()
-    {
-        return $this->monitorindex;
-    }
-
-    /**
-     * Set notes
-     *
-     * @param string $notes
-     * @return PhysicalInterface
-     */
-    public function setNotes($notes)
-    {
-        $this->notes = $notes;
-    
-        return $this;
-    }
-
-    /**
-     * Get notes
-     *
-     * @return string
-     */
-    public function getNotes()
-    {
-        return $this->notes;
-    }
-
-    /**
-     * Get id
-     *
-     * @return integer
-     */
-    public function getId()
-    {
-        return $this->id;
-    }
-
-    /**
-     * Set SwitchPort
-     *
-     * @param Entities\SwitchPort $switchPort
-     * @return PhysicalInterface
-     */
-    public function setSwitchPort(\Entities\SwitchPort $switchPort = null)
-    {
-        $this->SwitchPort = $switchPort;
-    
-        return $this;
-    }
-
-    /**
-     * Get SwitchPort
-     *
-     * @return Entities\SwitchPort
-     */
-    public function getSwitchPort()
-    {
-        return $this->SwitchPort;
-    }
-
-    /**
-     * Set VirtualInterface
-     *
-     * @param Entities\VirtualInterface $virtualInterface
-     * @return PhysicalInterface
-     */
-    public function setVirtualInterface(\Entities\VirtualInterface $virtualInterface = null)
-    {
-        $this->VirtualInterface = $virtualInterface;
-    
-        return $this;
-    }
-
-    /**
-     * Get VirtualInterface
-     *
-     * @return Entities\VirtualInterface
-     */
-    public function getVirtualInterface()
-    {
-        return $this->VirtualInterface;
-    }
-}
\ No newline at end of file
diff --git a/application/Entities/RSDroppedPrefix.php b/application/Entities/RSDroppedPrefix.php
deleted file mode 100644
index a0a78510f..000000000
--- a/application/Entities/RSDroppedPrefix.php
+++ /dev/null
@@ -1,195 +0,0 @@
-timestamp = $timestamp;
-    
-        return $this;
-    }
-
-    /**
-     * Get timestamp
-     *
-     * @return \DateTime 
-     */
-    public function getTimestamp()
-    {
-        return $this->timestamp;
-    }
-
-    /**
-     * Set prefix
-     *
-     * @param string $prefix
-     * @return RSDroppedPrefix
-     */
-    public function setPrefix($prefix)
-    {
-        $this->prefix = $prefix;
-    
-        return $this;
-    }
-
-    /**
-     * Get prefix
-     *
-     * @return string 
-     */
-    public function getPrefix()
-    {
-        return $this->prefix;
-    }
-
-    /**
-     * Set protocol
-     *
-     * @param integer $protocol
-     * @return RSDroppedPrefix
-     */
-    public function setProtocol($protocol)
-    {
-        $this->protocol = $protocol;
-    
-        return $this;
-    }
-
-    /**
-     * Get protocol
-     *
-     * @return integer 
-     */
-    public function getProtocol()
-    {
-        return $this->protocol;
-    }
-
-    /**
-     * Set irrdb
-     *
-     * @param integer $irrdb
-     * @return RSDroppedPrefix
-     */
-    public function setIrrdb($irrdb)
-    {
-        $this->irrdb = $irrdb;
-    
-        return $this;
-    }
-
-    /**
-     * Get irrdb
-     *
-     * @return integer 
-     */
-    public function getIrrdb()
-    {
-        return $this->irrdb;
-    }
-
-    /**
-     * Set rs_origin
-     *
-     * @param integer $rsOrigin
-     * @return RSDroppedPrefix
-     */
-    public function setRsOrigin($rsOrigin)
-    {
-        $this->rs_origin = $rsOrigin;
-    
-        return $this;
-    }
-
-    /**
-     * Get rs_origin
-     *
-     * @return integer 
-     */
-    public function getRsOrigin()
-    {
-        return $this->rs_origin;
-    }
-
-    /**
-     * Get id
-     *
-     * @return integer 
-     */
-    public function getId()
-    {
-        return $this->id;
-    }
-
-    /**
-     * Set Customer
-     *
-     * @param Entities\Customer $customer
-     * @return RSDroppedPrefix
-     */
-    public function setCustomer(\Entities\Customer $customer = null)
-    {
-        $this->Customer = $customer;
-    
-        return $this;
-    }
-
-    /**
-     * Get Customer
-     *
-     * @return Entities\Customer 
-     */
-    public function getCustomer()
-    {
-        return $this->Customer;
-    }
-}
\ No newline at end of file
diff --git a/application/Entities/SecEvent.php b/application/Entities/SecEvent.php
deleted file mode 100644
index 9f418eaee..000000000
--- a/application/Entities/SecEvent.php
+++ /dev/null
@@ -1,235 +0,0 @@
- 1,
-        self::TYPE_PORT_UPDOWN        => 1,
-        self::TYPE_SECURITY_VIOLATION => 1
-    );
-
-    /**
-     * @var string $type
-     */
-    private $type;
-
-    /**
-     * @var string $message
-     */
-    private $message;
-
-    /**
-     * @var string $recorded_date
-     */
-    private $recorded_date;
-
-    /**
-     * @var \DateTime $timestamp
-     */
-    private $timestamp;
-
-    /**
-     * @var integer $id
-     */
-    private $id;
-
-    /**
-     * @var Entities\Customer
-     */
-    private $Customer;
-
-    /**
-     * @var Entities\Switcher
-     */
-    private $Switch;
-
-    /**
-     * @var Entities\SwitchPort
-     */
-    private $SwitchPort;
-
-
-    /**
-     * Set type
-     *
-     * @param string $type
-     * @return SecEvent
-     */
-    public function setType($type)
-    {
-        $this->type = $type;
-    
-        return $this;
-    }
-
-    /**
-     * Get type
-     *
-     * @return string 
-     */
-    public function getType()
-    {
-        return $this->type;
-    }
-
-    /**
-     * Set message
-     *
-     * @param string $message
-     * @return SecEvent
-     */
-    public function setMessage($message)
-    {
-        $this->message = $message;
-    
-        return $this;
-    }
-
-    /**
-     * Get message
-     *
-     * @return string 
-     */
-    public function getMessage()
-    {
-        return $this->message;
-    }
-
-    /**
-     * Set recorded_date
-     *
-     * @param string $recordedDate
-     * @return SecEvent
-     */
-    public function setRecordedDate($recordedDate)
-    {
-        $this->recorded_date = $recordedDate;
-    
-        return $this;
-    }
-
-    /**
-     * Get recorded_date
-     *
-     * @return string 
-     */
-    public function getRecordedDate()
-    {
-        return $this->recorded_date;
-    }
-
-    /**
-     * Set timestamp
-     *
-     * @param \DateTime $timestamp
-     * @return SecEvent
-     */
-    public function setTimestamp($timestamp)
-    {
-        $this->timestamp = $timestamp;
-    
-        return $this;
-    }
-
-    /**
-     * Get timestamp
-     *
-     * @return \DateTime 
-     */
-    public function getTimestamp()
-    {
-        return $this->timestamp;
-    }
-
-    /**
-     * Get id
-     *
-     * @return integer 
-     */
-    public function getId()
-    {
-        return $this->id;
-    }
-
-    /**
-     * Set Customer
-     *
-     * @param Entities\Customer $customer
-     * @return SecEvent
-     */
-    public function setCustomer(\Entities\Customer $customer = null)
-    {
-        $this->Customer = $customer;
-    
-        return $this;
-    }
-
-    /**
-     * Get Customer
-     *
-     * @return Entities\Customer 
-     */
-    public function getCustomer()
-    {
-        return $this->Customer;
-    }
-
-    /**
-     * Set Switch
-     *
-     * @param Entities\Switcher $switch
-     * @return SecEvent
-     */
-    public function setSwitch(\Entities\Switcher $switch = null)
-    {
-        $this->Switch = $switch;
-    
-        return $this;
-    }
-
-    /**
-     * Get Switch
-     *
-     * @return Entities\Switcher 
-     */
-    public function getSwitch()
-    {
-        return $this->Switch;
-    }
-
-    /**
-     * Set SwitchPort
-     *
-     * @param Entities\SwitchPort $switchPort
-     * @return SecEvent
-     */
-    public function setSwitchPort(\Entities\SwitchPort $switchPort = null)
-    {
-        $this->SwitchPort = $switchPort;
-    
-        return $this;
-    }
-
-    /**
-     * Get SwitchPort
-     *
-     * @return Entities\SwitchPort 
-     */
-    public function getSwitchPort()
-    {
-        return $this->SwitchPort;
-    }
-}
\ No newline at end of file
diff --git a/application/Entities/SwitchPort.php b/application/Entities/SwitchPort.php
deleted file mode 100644
index 3e83d01b5..000000000
--- a/application/Entities/SwitchPort.php
+++ /dev/null
@@ -1,201 +0,0 @@
- 'Unset / Unknown',
-        self::TYPE_PEERING    => 'Peering',
-        self::TYPE_MONITOR    => 'Monitor',
-        self::TYPE_CORE       => 'Core',
-        self::TYPE_OTHER      => 'Other',
-        self::TYPE_MANAGEMENT => 'Management'
-    );
-    
-    /**
-     * @var integer $type
-     */
-    private $type;
-
-    /**
-     * @var string $name
-     */
-    private $name;
-
-    /**
-     * @var integer $id
-     */
-    private $id;
-
-    /**
-     * @var Entities\PhysicalInterface
-     */
-    private $PhysicalInterface;
-
-    /**
-     * @var Entities\Switcher
-     */
-    private $Switcher;
-
-
-    /**
-     * Set type
-     *
-     * @param integer $type
-     * @return SwitchPort
-     */
-    public function setType($type)
-    {
-        $this->type = $type;
-    
-        return $this;
-    }
-
-    /**
-     * Get type
-     *
-     * @return integer
-     */
-    public function getType()
-    {
-        return $this->type;
-    }
-
-    /**
-     * Set name
-     *
-     * @param string $name
-     * @return SwitchPort
-     */
-    public function setName($name)
-    {
-        $this->name = $name;
-    
-        return $this;
-    }
-
-    /**
-     * Get name
-     *
-     * @return string
-     */
-    public function getName()
-    {
-        return $this->name;
-    }
-
-    /**
-     * Get id
-     *
-     * @return integer
-     */
-    public function getId()
-    {
-        return $this->id;
-    }
-
-    /**
-     * Set PhysicalInterface
-     *
-     * @param Entities\PhysicalInterface $physicalInterface
-     * @return SwitchPort
-     */
-    public function setPhysicalInterface(\Entities\PhysicalInterface $physicalInterface = null)
-    {
-        $this->PhysicalInterface = $physicalInterface;
-    
-        return $this;
-    }
-
-    /**
-     * Get PhysicalInterface
-     *
-     * @return Entities\PhysicalInterface
-     */
-    public function getPhysicalInterface()
-    {
-        return $this->PhysicalInterface;
-    }
-
-    /**
-     * Set Switcher
-     *
-     * @param Entities\Switcher $switcher
-     * @return SwitchPort
-     */
-    public function setSwitcher(\Entities\Switcher $switcher = null)
-    {
-        $this->Switcher = $switcher;
-    
-        return $this;
-    }
-
-    /**
-     * Get Switcher
-     *
-     * @return Entities\Switcher
-     */
-    public function getSwitcher()
-    {
-        return $this->Switcher;
-    }
-    /**
-     * @var \Doctrine\Common\Collections\ArrayCollection
-     */
-    private $SecEvents;
-
-    /**
-     * Constructor
-     */
-    public function __construct()
-    {
-        $this->SecEvents = new \Doctrine\Common\Collections\ArrayCollection();
-    }
-    
-    /**
-     * Add SecEvents
-     *
-     * @param Entities\SecEvent $secEvents
-     * @return SwitchPort
-     */
-    public function addSecEvent(\Entities\SecEvent $secEvents)
-    {
-        $this->SecEvents[] = $secEvents;
-    
-        return $this;
-    }
-
-    /**
-     * Remove SecEvents
-     *
-     * @param Entities\SecEvent $secEvents
-     */
-    public function removeSecEvent(\Entities\SecEvent $secEvents)
-    {
-        $this->SecEvents->removeElement($secEvents);
-    }
-
-    /**
-     * Get SecEvents
-     *
-     * @return Doctrine\Common\Collections\Collection
-     */
-    public function getSecEvents()
-    {
-        return $this->SecEvents;
-    }
-}
\ No newline at end of file
diff --git a/application/Entities/Switcher.php b/application/Entities/Switcher.php
deleted file mode 100644
index 659894a7e..000000000
--- a/application/Entities/Switcher.php
+++ /dev/null
@@ -1,495 +0,0 @@
- 'Switch',
-        self::TYPE_CONSOLESERVER => 'Console Server'
-    ];
-    
-    
-    /**
-     * @var string $name
-     */
-    private $name;
-
-    /**
-     * @var string $ipv4addr
-     */
-    private $ipv4addr;
-
-    /**
-     * @var string $ipv6addr
-     */
-    private $ipv6addr;
-
-    /**
-     * @var string $snmppasswd
-     */
-    private $snmppasswd;
-
-    /**
-     * @var integer $infrastructure
-     */
-    private $infrastructure;
-
-    /**
-     * @var integer $switchtype
-     */
-    private $switchtype;
-
-    /**
-     * @var string $model
-     */
-    private $model;
-
-    /**
-     * @var boolean $actrive
-     */
-    private $actrive;
-
-    /**
-     * @var string $notes
-     */
-    private $notes;
-
-    /**
-     * @var integer $id
-     */
-    private $id;
-
-    /**
-     * @var \Doctrine\Common\Collections\ArrayCollection
-     */
-    private $Ports;
-
-    /**
-     * @var \Doctrine\Common\Collections\ArrayCollection
-     */
-    private $ConsoleServerConnections;
-
-    /**
-     * @var Entities\Cabinet
-     */
-    private $Cabinet;
-
-    /**
-     * @var Entities\Vendor
-     */
-    private $Vendor;
-
-    /**
-     * Constructor
-     */
-    public function __construct()
-    {
-        $this->Ports = new \Doctrine\Common\Collections\ArrayCollection();
-        $this->ConsoleServerConnections = new \Doctrine\Common\Collections\ArrayCollection();
-    }
-    
-    /**
-     * Set name
-     *
-     * @param string $name
-     * @return Switcher
-     */
-    public function setName($name)
-    {
-        $this->name = $name;
-    
-        return $this;
-    }
-
-    /**
-     * Get name
-     *
-     * @return string
-     */
-    public function getName()
-    {
-        return $this->name;
-    }
-
-    /**
-     * Set ipv4addr
-     *
-     * @param string $ipv4addr
-     * @return Switcher
-     */
-    public function setIpv4addr($ipv4addr)
-    {
-        $this->ipv4addr = $ipv4addr;
-    
-        return $this;
-    }
-
-    /**
-     * Get ipv4addr
-     *
-     * @return string
-     */
-    public function getIpv4addr()
-    {
-        return $this->ipv4addr;
-    }
-
-    /**
-     * Set ipv6addr
-     *
-     * @param string $ipv6addr
-     * @return Switcher
-     */
-    public function setIpv6addr($ipv6addr)
-    {
-        $this->ipv6addr = $ipv6addr;
-    
-        return $this;
-    }
-
-    /**
-     * Get ipv6addr
-     *
-     * @return string
-     */
-    public function getIpv6addr()
-    {
-        return $this->ipv6addr;
-    }
-
-    /**
-     * Set snmppasswd
-     *
-     * @param string $snmppasswd
-     * @return Switcher
-     */
-    public function setSnmppasswd($snmppasswd)
-    {
-        $this->snmppasswd = $snmppasswd;
-    
-        return $this;
-    }
-
-    /**
-     * Get snmppasswd
-     *
-     * @return string
-     */
-    public function getSnmppasswd()
-    {
-        return $this->snmppasswd;
-    }
-
-    /**
-     * Set infrastructure
-     *
-     * @param integer $infrastructure
-     * @return Switcher
-     */
-    public function setInfrastructure($infrastructure)
-    {
-        $this->infrastructure = $infrastructure;
-    
-        return $this;
-    }
-
-    /**
-     * Get infrastructure
-     *
-     * @return integer
-     */
-    public function getInfrastructure()
-    {
-        return $this->infrastructure;
-    }
-
-    /**
-     * Set switchtype
-     *
-     * @param integer $switchtype
-     * @return Switcher
-     */
-    public function setSwitchtype($switchtype)
-    {
-        $this->switchtype = $switchtype;
-    
-        return $this;
-    }
-
-    /**
-     * Get switchtype
-     *
-     * @return integer
-     */
-    public function getSwitchtype()
-    {
-        return $this->switchtype;
-    }
-
-    /**
-     * Set model
-     *
-     * @param string $model
-     * @return Switcher
-     */
-    public function setModel($model)
-    {
-        $this->model = $model;
-    
-        return $this;
-    }
-
-    /**
-     * Get model
-     *
-     * @return string
-     */
-    public function getModel()
-    {
-        return $this->model;
-    }
-
-    /**
-     * Set actrive
-     *
-     * @param boolean $actrive
-     * @return Switcher
-     */
-    public function setActrive($actrive)
-    {
-        $this->actrive = $actrive;
-    
-        return $this;
-    }
-
-    /**
-     * Get actrive
-     *
-     * @return boolean
-     */
-    public function getActrive()
-    {
-        return $this->actrive;
-    }
-
-    /**
-     * Set notes
-     *
-     * @param string $notes
-     * @return Switcher
-     */
-    public function setNotes($notes)
-    {
-        $this->notes = $notes;
-    
-        return $this;
-    }
-
-    /**
-     * Get notes
-     *
-     * @return string
-     */
-    public function getNotes()
-    {
-        return $this->notes;
-    }
-
-    /**
-     * Get id
-     *
-     * @return integer
-     */
-    public function getId()
-    {
-        return $this->id;
-    }
-
-    /**
-     * Add Ports
-     *
-     * @param Entities\SwitchPort $ports
-     * @return Switcher
-     */
-    public function addPort(\Entities\SwitchPort $ports)
-    {
-        $this->Ports[] = $ports;
-    
-        return $this;
-    }
-
-    /**
-     * Remove Ports
-     *
-     * @param Entities\SwitchPort $ports
-     */
-    public function removePort(\Entities\SwitchPort $ports)
-    {
-        $this->Ports->removeElement($ports);
-    }
-
-    /**
-     * Get Ports
-     *
-     * @return Doctrine\Common\Collections\Collection
-     */
-    public function getPorts()
-    {
-        return $this->Ports;
-    }
-
-    /**
-     * Add ConsoleServerConnections
-     *
-     * @param Entities\ConsoleServerConnection $consoleServerConnections
-     * @return Switcher
-     */
-    public function addConsoleServerConnection(\Entities\ConsoleServerConnection $consoleServerConnections)
-    {
-        $this->ConsoleServerConnections[] = $consoleServerConnections;
-    
-        return $this;
-    }
-
-    /**
-     * Remove ConsoleServerConnections
-     *
-     * @param Entities\ConsoleServerConnection $consoleServerConnections
-     */
-    public function removeConsoleServerConnection(\Entities\ConsoleServerConnection $consoleServerConnections)
-    {
-        $this->ConsoleServerConnections->removeElement($consoleServerConnections);
-    }
-
-    /**
-     * Get ConsoleServerConnections
-     *
-     * @return Doctrine\Common\Collections\Collection
-     */
-    public function getConsoleServerConnections()
-    {
-        return $this->ConsoleServerConnections;
-    }
-
-    /**
-     * Set Cabinet
-     *
-     * @param Entities\Cabinet $cabinet
-     * @return Switcher
-     */
-    public function setCabinet(\Entities\Cabinet $cabinet = null)
-    {
-        $this->Cabinet = $cabinet;
-    
-        return $this;
-    }
-
-    /**
-     * Get Cabinet
-     *
-     * @return Entities\Cabinet
-     */
-    public function getCabinet()
-    {
-        return $this->Cabinet;
-    }
-
-    /**
-     * Set Vendor
-     *
-     * @param Entities\Vendor $vendor
-     * @return Switcher
-     */
-    public function setVendor(\Entities\Vendor $vendor = null)
-    {
-        $this->Vendor = $vendor;
-    
-        return $this;
-    }
-
-    /**
-     * Get Vendor
-     *
-     * @return Entities\Vendor
-     */
-    public function getVendor()
-    {
-        return $this->Vendor;
-    }
-    /**
-     * @var \Doctrine\Common\Collections\ArrayCollection
-     */
-    private $SecEvents;
-
-
-    /**
-     * Add SecEvents
-     *
-     * @param Entities\SecEvent $secEvents
-     * @return Switcher
-     */
-    public function addSecEvent(\Entities\SecEvent $secEvents)
-    {
-        $this->SecEvents[] = $secEvents;
-    
-        return $this;
-    }
-
-    /**
-     * Remove SecEvents
-     *
-     * @param Entities\SecEvent $secEvents
-     */
-    public function removeSecEvent(\Entities\SecEvent $secEvents)
-    {
-        $this->SecEvents->removeElement($secEvents);
-    }
-
-    /**
-     * Get SecEvents
-     *
-     * @return Doctrine\Common\Collections\Collection
-     */
-    public function getSecEvents()
-    {
-        return $this->SecEvents;
-    }
-    /**
-     * @var boolean $active
-     */
-    private $active;
-
-
-    /**
-     * Set active
-     *
-     * @param boolean $active
-     * @return Switcher
-     */
-    public function setActive($active)
-    {
-        $this->active = $active;
-    
-        return $this;
-    }
-
-    /**
-     * Get active
-     *
-     * @return boolean
-     */
-    public function getActive()
-    {
-        return $this->active;
-    }
-}
\ No newline at end of file
diff --git a/application/Entities/Traffic95th.php b/application/Entities/Traffic95th.php
deleted file mode 100644
index edcce681d..000000000
--- a/application/Entities/Traffic95th.php
+++ /dev/null
@@ -1,139 +0,0 @@
-datetime = $datetime;
-    
-        return $this;
-    }
-
-    /**
-     * Get datetime
-     *
-     * @return \DateTime 
-     */
-    public function getDatetime()
-    {
-        return $this->datetime;
-    }
-
-    /**
-     * Set average
-     *
-     * @param integer $average
-     * @return Traffic95th
-     */
-    public function setAverage($average)
-    {
-        $this->average = $average;
-    
-        return $this;
-    }
-
-    /**
-     * Get average
-     *
-     * @return integer 
-     */
-    public function getAverage()
-    {
-        return $this->average;
-    }
-
-    /**
-     * Set max
-     *
-     * @param integer $max
-     * @return Traffic95th
-     */
-    public function setMax($max)
-    {
-        $this->max = $max;
-    
-        return $this;
-    }
-
-    /**
-     * Get max
-     *
-     * @return integer 
-     */
-    public function getMax()
-    {
-        return $this->max;
-    }
-
-    /**
-     * Get id
-     *
-     * @return integer 
-     */
-    public function getId()
-    {
-        return $this->id;
-    }
-
-    /**
-     * Set Customer
-     *
-     * @param Entities\Customer $customer
-     * @return Traffic95th
-     */
-    public function setCustomer(\Entities\Customer $customer = null)
-    {
-        $this->Customer = $customer;
-    
-        return $this;
-    }
-
-    /**
-     * Get Customer
-     *
-     * @return Entities\Customer 
-     */
-    public function getCustomer()
-    {
-        return $this->Customer;
-    }
-}
\ No newline at end of file
diff --git a/application/Entities/Traffic95thMonthly.php b/application/Entities/Traffic95thMonthly.php
deleted file mode 100644
index 744b1ca73..000000000
--- a/application/Entities/Traffic95thMonthly.php
+++ /dev/null
@@ -1,111 +0,0 @@
-month = $month;
-    
-        return $this;
-    }
-
-    /**
-     * Get month
-     *
-     * @return \DateTime 
-     */
-    public function getMonth()
-    {
-        return $this->month;
-    }
-
-    /**
-     * Set max_95th
-     *
-     * @param integer $max95th
-     * @return Traffic95thMonthly
-     */
-    public function setMax95th($max95th)
-    {
-        $this->max_95th = $max95th;
-    
-        return $this;
-    }
-
-    /**
-     * Get max_95th
-     *
-     * @return integer 
-     */
-    public function getMax95th()
-    {
-        return $this->max_95th;
-    }
-
-    /**
-     * Get id
-     *
-     * @return integer 
-     */
-    public function getId()
-    {
-        return $this->id;
-    }
-
-    /**
-     * Set Customer
-     *
-     * @param Entities\Customer $customer
-     * @return Traffic95thMonthly
-     */
-    public function setCustomer(\Entities\Customer $customer = null)
-    {
-        $this->Customer = $customer;
-    
-        return $this;
-    }
-
-    /**
-     * Get Customer
-     *
-     * @return Entities\Customer 
-     */
-    public function getCustomer()
-    {
-        return $this->Customer;
-    }
-}
\ No newline at end of file
diff --git a/application/Entities/TrafficDaily.php b/application/Entities/TrafficDaily.php
deleted file mode 100644
index 1d2162bb6..000000000
--- a/application/Entities/TrafficDaily.php
+++ /dev/null
@@ -1,783 +0,0 @@
-day = $day;
-    
-        return $this;
-    }
-
-    /**
-     * Get day
-     *
-     * @return \DateTime 
-     */
-    public function getDay()
-    {
-        return $this->day;
-    }
-
-    /**
-     * Set category
-     *
-     * @param string $category
-     * @return TrafficDaily
-     */
-    public function setCategory($category)
-    {
-        $this->category = $category;
-    
-        return $this;
-    }
-
-    /**
-     * Get category
-     *
-     * @return string 
-     */
-    public function getCategory()
-    {
-        return $this->category;
-    }
-
-    /**
-     * Set day_avg_in
-     *
-     * @param integer $dayAvgIn
-     * @return TrafficDaily
-     */
-    public function setDayAvgIn($dayAvgIn)
-    {
-        $this->day_avg_in = $dayAvgIn;
-    
-        return $this;
-    }
-
-    /**
-     * Get day_avg_in
-     *
-     * @return integer 
-     */
-    public function getDayAvgIn()
-    {
-        return $this->day_avg_in;
-    }
-
-    /**
-     * Set day_avg_out
-     *
-     * @param integer $dayAvgOut
-     * @return TrafficDaily
-     */
-    public function setDayAvgOut($dayAvgOut)
-    {
-        $this->day_avg_out = $dayAvgOut;
-    
-        return $this;
-    }
-
-    /**
-     * Get day_avg_out
-     *
-     * @return integer 
-     */
-    public function getDayAvgOut()
-    {
-        return $this->day_avg_out;
-    }
-
-    /**
-     * Set day_max_in
-     *
-     * @param integer $dayMaxIn
-     * @return TrafficDaily
-     */
-    public function setDayMaxIn($dayMaxIn)
-    {
-        $this->day_max_in = $dayMaxIn;
-    
-        return $this;
-    }
-
-    /**
-     * Get day_max_in
-     *
-     * @return integer 
-     */
-    public function getDayMaxIn()
-    {
-        return $this->day_max_in;
-    }
-
-    /**
-     * Set day_max_out
-     *
-     * @param integer $dayMaxOut
-     * @return TrafficDaily
-     */
-    public function setDayMaxOut($dayMaxOut)
-    {
-        $this->day_max_out = $dayMaxOut;
-    
-        return $this;
-    }
-
-    /**
-     * Get day_max_out
-     *
-     * @return integer 
-     */
-    public function getDayMaxOut()
-    {
-        return $this->day_max_out;
-    }
-
-    /**
-     * Set day_tot_in
-     *
-     * @param integer $dayTotIn
-     * @return TrafficDaily
-     */
-    public function setDayTotIn($dayTotIn)
-    {
-        $this->day_tot_in = $dayTotIn;
-    
-        return $this;
-    }
-
-    /**
-     * Get day_tot_in
-     *
-     * @return integer 
-     */
-    public function getDayTotIn()
-    {
-        return $this->day_tot_in;
-    }
-
-    /**
-     * Set day_tot_out
-     *
-     * @param integer $dayTotOut
-     * @return TrafficDaily
-     */
-    public function setDayTotOut($dayTotOut)
-    {
-        $this->day_tot_out = $dayTotOut;
-    
-        return $this;
-    }
-
-    /**
-     * Get day_tot_out
-     *
-     * @return integer 
-     */
-    public function getDayTotOut()
-    {
-        return $this->day_tot_out;
-    }
-
-    /**
-     * Set week_avg_in
-     *
-     * @param integer $weekAvgIn
-     * @return TrafficDaily
-     */
-    public function setWeekAvgIn($weekAvgIn)
-    {
-        $this->week_avg_in = $weekAvgIn;
-    
-        return $this;
-    }
-
-    /**
-     * Get week_avg_in
-     *
-     * @return integer 
-     */
-    public function getWeekAvgIn()
-    {
-        return $this->week_avg_in;
-    }
-
-    /**
-     * Set week_avg_out
-     *
-     * @param integer $weekAvgOut
-     * @return TrafficDaily
-     */
-    public function setWeekAvgOut($weekAvgOut)
-    {
-        $this->week_avg_out = $weekAvgOut;
-    
-        return $this;
-    }
-
-    /**
-     * Get week_avg_out
-     *
-     * @return integer 
-     */
-    public function getWeekAvgOut()
-    {
-        return $this->week_avg_out;
-    }
-
-    /**
-     * Set week_max_in
-     *
-     * @param integer $weekMaxIn
-     * @return TrafficDaily
-     */
-    public function setWeekMaxIn($weekMaxIn)
-    {
-        $this->week_max_in = $weekMaxIn;
-    
-        return $this;
-    }
-
-    /**
-     * Get week_max_in
-     *
-     * @return integer 
-     */
-    public function getWeekMaxIn()
-    {
-        return $this->week_max_in;
-    }
-
-    /**
-     * Set week_max_out
-     *
-     * @param integer $weekMaxOut
-     * @return TrafficDaily
-     */
-    public function setWeekMaxOut($weekMaxOut)
-    {
-        $this->week_max_out = $weekMaxOut;
-    
-        return $this;
-    }
-
-    /**
-     * Get week_max_out
-     *
-     * @return integer 
-     */
-    public function getWeekMaxOut()
-    {
-        return $this->week_max_out;
-    }
-
-    /**
-     * Set week_tot_in
-     *
-     * @param integer $weekTotIn
-     * @return TrafficDaily
-     */
-    public function setWeekTotIn($weekTotIn)
-    {
-        $this->week_tot_in = $weekTotIn;
-    
-        return $this;
-    }
-
-    /**
-     * Get week_tot_in
-     *
-     * @return integer 
-     */
-    public function getWeekTotIn()
-    {
-        return $this->week_tot_in;
-    }
-
-    /**
-     * Set week_tot_out
-     *
-     * @param integer $weekTotOut
-     * @return TrafficDaily
-     */
-    public function setWeekTotOut($weekTotOut)
-    {
-        $this->week_tot_out = $weekTotOut;
-    
-        return $this;
-    }
-
-    /**
-     * Get week_tot_out
-     *
-     * @return integer 
-     */
-    public function getWeekTotOut()
-    {
-        return $this->week_tot_out;
-    }
-
-    /**
-     * Set month_avg_in
-     *
-     * @param integer $monthAvgIn
-     * @return TrafficDaily
-     */
-    public function setMonthAvgIn($monthAvgIn)
-    {
-        $this->month_avg_in = $monthAvgIn;
-    
-        return $this;
-    }
-
-    /**
-     * Get month_avg_in
-     *
-     * @return integer 
-     */
-    public function getMonthAvgIn()
-    {
-        return $this->month_avg_in;
-    }
-
-    /**
-     * Set month_avg_out
-     *
-     * @param integer $monthAvgOut
-     * @return TrafficDaily
-     */
-    public function setMonthAvgOut($monthAvgOut)
-    {
-        $this->month_avg_out = $monthAvgOut;
-    
-        return $this;
-    }
-
-    /**
-     * Get month_avg_out
-     *
-     * @return integer 
-     */
-    public function getMonthAvgOut()
-    {
-        return $this->month_avg_out;
-    }
-
-    /**
-     * Set month_max_in
-     *
-     * @param integer $monthMaxIn
-     * @return TrafficDaily
-     */
-    public function setMonthMaxIn($monthMaxIn)
-    {
-        $this->month_max_in = $monthMaxIn;
-    
-        return $this;
-    }
-
-    /**
-     * Get month_max_in
-     *
-     * @return integer 
-     */
-    public function getMonthMaxIn()
-    {
-        return $this->month_max_in;
-    }
-
-    /**
-     * Set month_max_out
-     *
-     * @param integer $monthMaxOut
-     * @return TrafficDaily
-     */
-    public function setMonthMaxOut($monthMaxOut)
-    {
-        $this->month_max_out = $monthMaxOut;
-    
-        return $this;
-    }
-
-    /**
-     * Get month_max_out
-     *
-     * @return integer 
-     */
-    public function getMonthMaxOut()
-    {
-        return $this->month_max_out;
-    }
-
-    /**
-     * Set month_tot_in
-     *
-     * @param integer $monthTotIn
-     * @return TrafficDaily
-     */
-    public function setMonthTotIn($monthTotIn)
-    {
-        $this->month_tot_in = $monthTotIn;
-    
-        return $this;
-    }
-
-    /**
-     * Get month_tot_in
-     *
-     * @return integer 
-     */
-    public function getMonthTotIn()
-    {
-        return $this->month_tot_in;
-    }
-
-    /**
-     * Set month_tot_out
-     *
-     * @param integer $monthTotOut
-     * @return TrafficDaily
-     */
-    public function setMonthTotOut($monthTotOut)
-    {
-        $this->month_tot_out = $monthTotOut;
-    
-        return $this;
-    }
-
-    /**
-     * Get month_tot_out
-     *
-     * @return integer 
-     */
-    public function getMonthTotOut()
-    {
-        return $this->month_tot_out;
-    }
-
-    /**
-     * Set year_avg_in
-     *
-     * @param integer $yearAvgIn
-     * @return TrafficDaily
-     */
-    public function setYearAvgIn($yearAvgIn)
-    {
-        $this->year_avg_in = $yearAvgIn;
-    
-        return $this;
-    }
-
-    /**
-     * Get year_avg_in
-     *
-     * @return integer 
-     */
-    public function getYearAvgIn()
-    {
-        return $this->year_avg_in;
-    }
-
-    /**
-     * Set year_avg_out
-     *
-     * @param integer $yearAvgOut
-     * @return TrafficDaily
-     */
-    public function setYearAvgOut($yearAvgOut)
-    {
-        $this->year_avg_out = $yearAvgOut;
-    
-        return $this;
-    }
-
-    /**
-     * Get year_avg_out
-     *
-     * @return integer 
-     */
-    public function getYearAvgOut()
-    {
-        return $this->year_avg_out;
-    }
-
-    /**
-     * Set year_max_in
-     *
-     * @param integer $yearMaxIn
-     * @return TrafficDaily
-     */
-    public function setYearMaxIn($yearMaxIn)
-    {
-        $this->year_max_in = $yearMaxIn;
-    
-        return $this;
-    }
-
-    /**
-     * Get year_max_in
-     *
-     * @return integer 
-     */
-    public function getYearMaxIn()
-    {
-        return $this->year_max_in;
-    }
-
-    /**
-     * Set year_max_out
-     *
-     * @param integer $yearMaxOut
-     * @return TrafficDaily
-     */
-    public function setYearMaxOut($yearMaxOut)
-    {
-        $this->year_max_out = $yearMaxOut;
-    
-        return $this;
-    }
-
-    /**
-     * Get year_max_out
-     *
-     * @return integer 
-     */
-    public function getYearMaxOut()
-    {
-        return $this->year_max_out;
-    }
-
-    /**
-     * Set year_tot_in
-     *
-     * @param integer $yearTotIn
-     * @return TrafficDaily
-     */
-    public function setYearTotIn($yearTotIn)
-    {
-        $this->year_tot_in = $yearTotIn;
-    
-        return $this;
-    }
-
-    /**
-     * Get year_tot_in
-     *
-     * @return integer 
-     */
-    public function getYearTotIn()
-    {
-        return $this->year_tot_in;
-    }
-
-    /**
-     * Set year_tot_out
-     *
-     * @param integer $yearTotOut
-     * @return TrafficDaily
-     */
-    public function setYearTotOut($yearTotOut)
-    {
-        $this->year_tot_out = $yearTotOut;
-    
-        return $this;
-    }
-
-    /**
-     * Get year_tot_out
-     *
-     * @return integer 
-     */
-    public function getYearTotOut()
-    {
-        return $this->year_tot_out;
-    }
-
-    /**
-     * Get id
-     *
-     * @return integer 
-     */
-    public function getId()
-    {
-        return $this->id;
-    }
-
-    /**
-     * Set Customer
-     *
-     * @param Entities\Customer $customer
-     * @return TrafficDaily
-     */
-    public function setCustomer(\Entities\Customer $customer = null)
-    {
-        $this->Customer = $customer;
-    
-        return $this;
-    }
-
-    /**
-     * Get Customer
-     *
-     * @return Entities\Customer 
-     */
-    public function getCustomer()
-    {
-        return $this->Customer;
-    }
-}
\ No newline at end of file
diff --git a/application/Entities/User.php b/application/Entities/User.php
deleted file mode 100644
index 8a0171bda..000000000
--- a/application/Entities/User.php
+++ /dev/null
@@ -1,624 +0,0 @@
- 'CUSTUSER',
-        User::AUTH_CUSTADMIN => 'CUSTADMIN',
-        User::AUTH_SUPERUSER => 'SUPERUSER'
-    );
-    
-    public static $PRIVILEGES_TEXT = array(
-        User::AUTH_CUSTUSER  => 'Customer User',
-        User::AUTH_CUSTADMIN => 'Customer Superuser',
-        User::AUTH_SUPERUSER => 'Superuser'
-    );
-    
-    
-    /**
-     * @var string $username
-     */
-    private $username;
-
-    /**
-     * @var string $password
-     */
-    private $password;
-
-    /**
-     * @var string $email
-     */
-    private $email;
-
-    /**
-     * @var string $authorisedMobile
-     */
-    private $authorisedMobile;
-
-    /**
-     * @var integer $uid
-     */
-    private $uid;
-
-    /**
-     * @var integer $privs
-     */
-    private $privs;
-
-    /**
-     * @var boolean $disabled
-     */
-    private $disabled;
-
-    /**
-     * @var \DateTime $lastupdated
-     */
-    private $lastupdated;
-
-    /**
-     * @var integer $lastupdatedby
-     */
-    private $lastupdatedby;
-
-    /**
-     * @var string $creator
-     */
-    private $creator;
-
-    /**
-     * @var \DateTime $created
-     */
-    private $created;
-
-    /**
-     * @var integer $id
-     */
-    private $id;
-
-    /**
-     * @var \Doctrine\Common\Collections\ArrayCollection
-     */
-    private $Parent;
-
-    /**
-     * @var \Doctrine\Common\Collections\ArrayCollection
-     */
-    private $Preferences;
-
-    /**
-     * @var \Doctrine\Common\Collections\ArrayCollection
-     */
-    private $ChangeLogs;
-
-    /**
-     * @var Entities\Customer
-     */
-    private $Customer;
-
-    /**
-     * @var Entities\User
-     */
-    private $Children;
-
-    /**
-     * Constructor
-     */
-    public function __construct()
-    {
-        $this->Parent = new \Doctrine\Common\Collections\ArrayCollection();
-        $this->Preferences = new \Doctrine\Common\Collections\ArrayCollection();
-        $this->ChangeLogs = new \Doctrine\Common\Collections\ArrayCollection();
-    }
-    
-    /**
-     * Set username
-     *
-     * @param string $username
-     * @return User
-     */
-    public function setUsername($username)
-    {
-        $this->username = $username;
-    
-        return $this;
-    }
-
-    /**
-     * Get username
-     *
-     * @return string
-     */
-    public function getUsername()
-    {
-        return $this->username;
-    }
-
-    /**
-     * Set password
-     *
-     * @param string $password
-     * @return User
-     */
-    public function setPassword($password)
-    {
-        $this->password = $password;
-    
-        return $this;
-    }
-
-    /**
-     * Get password
-     *
-     * @return string
-     */
-    public function getPassword()
-    {
-        return $this->password;
-    }
-
-    /**
-     * Set email
-     *
-     * @param string $email
-     * @return User
-     */
-    public function setEmail($email)
-    {
-        $this->email = $email;
-    
-        return $this;
-    }
-
-    /**
-     * Get email
-     *
-     * @return string
-     */
-    public function getEmail()
-    {
-        return $this->email;
-    }
-
-    /**
-     * Set authorisedMobile
-     *
-     * @param string $authorisedMobile
-     * @return User
-     */
-    public function setAuthorisedMobile($authorisedMobile)
-    {
-        $this->authorisedMobile = $authorisedMobile;
-    
-        return $this;
-    }
-
-    /**
-     * Get authorisedMobile
-     *
-     * @return string
-     */
-    public function getAuthorisedMobile()
-    {
-        return $this->authorisedMobile;
-    }
-
-    /**
-     * Set uid
-     *
-     * @param integer $uid
-     * @return User
-     */
-    public function setUid($uid)
-    {
-        $this->uid = $uid;
-    
-        return $this;
-    }
-
-    /**
-     * Get uid
-     *
-     * @return integer
-     */
-    public function getUid()
-    {
-        return $this->uid;
-    }
-
-    /**
-     * Set privs
-     *
-     * @param integer $privs
-     * @return User
-     */
-    public function setPrivs($privs)
-    {
-        $this->privs = $privs;
-    
-        return $this;
-    }
-
-    /**
-     * Get privs
-     *
-     * @return integer
-     */
-    public function getPrivs()
-    {
-        return $this->privs;
-    }
-
-    /**
-     * Set disabled
-     *
-     * @param boolean $disabled
-     * @return User
-     */
-    public function setDisabled($disabled)
-    {
-        $this->disabled = $disabled;
-    
-        return $this;
-    }
-
-    /**
-     * Get disabled
-     *
-     * @return boolean
-     */
-    public function getDisabled()
-    {
-        return $this->disabled;
-    }
-
-    /**
-     * Set lastupdated
-     *
-     * @param \DateTime $lastupdated
-     * @return User
-     */
-    public function setLastupdated($lastupdated)
-    {
-        $this->lastupdated = $lastupdated;
-    
-        return $this;
-    }
-
-    /**
-     * Get lastupdated
-     *
-     * @return \DateTime
-     */
-    public function getLastupdated()
-    {
-        return $this->lastupdated;
-    }
-
-    /**
-     * Set lastupdatedby
-     *
-     * @param integer $lastupdatedby
-     * @return User
-     */
-    public function setLastupdatedby($lastupdatedby)
-    {
-        $this->lastupdatedby = $lastupdatedby;
-    
-        return $this;
-    }
-
-    /**
-     * Get lastupdatedby
-     *
-     * @return integer
-     */
-    public function getLastupdatedby()
-    {
-        return $this->lastupdatedby;
-    }
-
-    /**
-     * Set creator
-     *
-     * @param string $creator
-     * @return User
-     */
-    public function setCreator($creator)
-    {
-        $this->creator = $creator;
-    
-        return $this;
-    }
-
-    /**
-     * Get creator
-     *
-     * @return string
-     */
-    public function getCreator()
-    {
-        return $this->creator;
-    }
-
-    /**
-     * Set created
-     *
-     * @param \DateTime $created
-     * @return User
-     */
-    public function setCreated($created)
-    {
-        $this->created = $created;
-    
-        return $this;
-    }
-
-    /**
-     * Get created
-     *
-     * @return \DateTime
-     */
-    public function getCreated()
-    {
-        return $this->created;
-    }
-
-    /**
-     * Get id
-     *
-     * @return integer
-     */
-    public function getId()
-    {
-        return $this->id;
-    }
-
-    /**
-     * Add Parent
-     *
-     * @param Entities\User $parent
-     * @return User
-     */
-    public function addParent(\Entities\User $parent)
-    {
-        $this->Parent[] = $parent;
-    
-        return $this;
-    }
-
-    /**
-     * Remove Parent
-     *
-     * @param Entities\User $parent
-     */
-    public function removeParent(\Entities\User $parent)
-    {
-        $this->Parent->removeElement($parent);
-    }
-
-    /**
-     * Get Parent
-     *
-     * @return Doctrine\Common\Collections\Collection
-     */
-    public function getParent()
-    {
-        return $this->Parent;
-    }
-
-    /**
-     * Add Preferences
-     *
-     * @param Entities\UserPreference $preferences
-     * @return User
-     */
-    public function addPreference(\Entities\UserPreference $preferences)
-    {
-        $this->Preferences[] = $preferences;
-    
-        return $this;
-    }
-
-    /**
-     * Remove Preferences
-     *
-     * @param Entities\UserPreference $preferences
-     */
-    public function removePreference(\Entities\UserPreference $preferences)
-    {
-        $this->Preferences->removeElement($preferences);
-    }
-
-    /**
-     * Get Preferences
-     *
-     * @return Doctrine\Common\Collections\Collection
-     */
-    public function getPreferences()
-    {
-        return $this->Preferences;
-    }
-
-    /**
-     * Add ChangeLogs
-     *
-     * @param Entities\ChangeLog $changeLogs
-     * @return User
-     */
-    public function addChangeLog(\Entities\ChangeLog $changeLogs)
-    {
-        $this->ChangeLogs[] = $changeLogs;
-    
-        return $this;
-    }
-
-    /**
-     * Remove ChangeLogs
-     *
-     * @param Entities\ChangeLog $changeLogs
-     */
-    public function removeChangeLog(\Entities\ChangeLog $changeLogs)
-    {
-        $this->ChangeLogs->removeElement($changeLogs);
-    }
-
-    /**
-     * Get ChangeLogs
-     *
-     * @return Doctrine\Common\Collections\Collection
-     */
-    public function getChangeLogs()
-    {
-        return $this->ChangeLogs;
-    }
-
-    /**
-     * Set Customer
-     *
-     * @param Entities\Customer $customer
-     * @return User
-     */
-    public function setCustomer(\Entities\Customer $customer = null)
-    {
-        $this->Customer = $customer;
-    
-        return $this;
-    }
-
-    /**
-     * Get Customer
-     *
-     * @return Entities\Customer
-     */
-    public function getCustomer()
-    {
-        return $this->Customer;
-    }
-
-    /**
-     * Set Children
-     *
-     * @param Entities\User $children
-     * @return User
-     */
-    public function setChildren(\Entities\User $children = null)
-    {
-        $this->Children = $children;
-    
-        return $this;
-    }
-
-    /**
-     * Get Children
-     *
-     * @return Entities\User
-     */
-    public function getChildren()
-    {
-        return $this->Children;
-    }
-
-
-
-
-    /**
-     * Get Formatted Name - utility function required by OSS library
-     *
-     * @return string
-     */
-    public function getFormattedName()
-    {
-        return $this->getUsername();
-    }
-
-
-
-
-    /**
-     * Add Children
-     *
-     * @param Entities\User $children
-     * @return User
-     */
-    public function addChildren(\Entities\User $children)
-    {
-        $this->Children[] = $children;
-    
-        return $this;
-    }
-
-    /**
-     * Remove Children
-     *
-     * @param Entities\User $children
-     */
-    public function removeChildren(\Entities\User $children)
-    {
-        $this->Children->removeElement($children);
-    }
-
-    /**
-     * Set Parent
-     *
-     * @param Entities\User $parent
-     * @return User
-     */
-    public function setParent(\Entities\User $parent = null)
-    {
-        $this->Parent = $parent;
-    
-        return $this;
-    }
-    /**
-     * @var \Doctrine\Common\Collections\ArrayCollection
-     */
-    private $Meetings;
-
-
-    /**
-     * Add Meetings
-     *
-     * @param Entities\Meeting $meetings
-     * @return User
-     */
-    public function addMeeting(\Entities\Meeting $meetings)
-    {
-        $this->Meetings[] = $meetings;
-    
-        return $this;
-    }
-
-    /**
-     * Remove Meetings
-     *
-     * @param Entities\Meeting $meetings
-     */
-    public function removeMeeting(\Entities\Meeting $meetings)
-    {
-        $this->Meetings->removeElement($meetings);
-    }
-
-    /**
-     * Get Meetings
-     *
-     * @return Doctrine\Common\Collections\Collection 
-     */
-    public function getMeetings()
-    {
-        return $this->Meetings;
-    }
-}
\ No newline at end of file
diff --git a/application/Entities/UserPreference.php b/application/Entities/UserPreference.php
deleted file mode 100644
index 79caaf205..000000000
--- a/application/Entities/UserPreference.php
+++ /dev/null
@@ -1,195 +0,0 @@
-attribute = $attribute;
-    
-        return $this;
-    }
-
-    /**
-     * Get attribute
-     *
-     * @return string 
-     */
-    public function getAttribute()
-    {
-        return $this->attribute;
-    }
-
-    /**
-     * Set op
-     *
-     * @param string $op
-     * @return UserPreference
-     */
-    public function setOp($op)
-    {
-        $this->op = $op;
-    
-        return $this;
-    }
-
-    /**
-     * Get op
-     *
-     * @return string 
-     */
-    public function getOp()
-    {
-        return $this->op;
-    }
-
-    /**
-     * Set value
-     *
-     * @param string $value
-     * @return UserPreference
-     */
-    public function setValue($value)
-    {
-        $this->value = $value;
-    
-        return $this;
-    }
-
-    /**
-     * Get value
-     *
-     * @return string 
-     */
-    public function getValue()
-    {
-        return $this->value;
-    }
-
-    /**
-     * Get id
-     *
-     * @return integer 
-     */
-    public function getId()
-    {
-        return $this->id;
-    }
-
-    /**
-     * Set User
-     *
-     * @param Entities\User $user
-     * @return UserPreference
-     */
-    public function setUser(\Entities\User $user = null)
-    {
-        $this->User = $user;
-    
-        return $this;
-    }
-
-    /**
-     * Get User
-     *
-     * @return Entities\User 
-     */
-    public function getUser()
-    {
-        return $this->User;
-    }
-    /**
-     * @var integer $ix
-     */
-    private $ix;
-
-
-    /**
-     * Set ix
-     *
-     * @param integer $ix
-     * @return UserPreference
-     */
-    public function setIx($ix)
-    {
-        $this->ix = $ix;
-    
-        return $this;
-    }
-
-    /**
-     * Get ix
-     *
-     * @return integer 
-     */
-    public function getIx()
-    {
-        return $this->ix;
-    }
-    /**
-     * @var integer $expire
-     */
-    private $expire;
-
-
-    /**
-     * Set expire
-     *
-     * @param integer $expire
-     * @return UserPreference
-     */
-    public function setExpire($expire)
-    {
-        $this->expire = $expire;
-    
-        return $this;
-    }
-
-    /**
-     * Get expire
-     *
-     * @return integer 
-     */
-    public function getExpire()
-    {
-        return $this->expire;
-    }
-}
\ No newline at end of file
diff --git a/application/Entities/Vendor.php b/application/Entities/Vendor.php
deleted file mode 100644
index 1deee22d4..000000000
--- a/application/Entities/Vendor.php
+++ /dev/null
@@ -1,100 +0,0 @@
-Switches = new \Doctrine\Common\Collections\ArrayCollection();
-    }
-    
-    /**
-     * Set name
-     *
-     * @param string $name
-     * @return Vendor
-     */
-    public function setName($name)
-    {
-        $this->name = $name;
-    
-        return $this;
-    }
-
-    /**
-     * Get name
-     *
-     * @return string 
-     */
-    public function getName()
-    {
-        return $this->name;
-    }
-
-    /**
-     * Get id
-     *
-     * @return integer 
-     */
-    public function getId()
-    {
-        return $this->id;
-    }
-
-    /**
-     * Add Switches
-     *
-     * @param Entities\Switcher $switches
-     * @return Vendor
-     */
-    public function addSwitche(\Entities\Switcher $switches)
-    {
-        $this->Switches[] = $switches;
-    
-        return $this;
-    }
-
-    /**
-     * Remove Switches
-     *
-     * @param Entities\Switcher $switches
-     */
-    public function removeSwitche(\Entities\Switcher $switches)
-    {
-        $this->Switches->removeElement($switches);
-    }
-
-    /**
-     * Get Switches
-     *
-     * @return Doctrine\Common\Collections\Collection 
-     */
-    public function getSwitches()
-    {
-        return $this->Switches;
-    }
-}
\ No newline at end of file
diff --git a/application/Entities/VirtualInterface.php b/application/Entities/VirtualInterface.php
deleted file mode 100644
index e5ce85abd..000000000
--- a/application/Entities/VirtualInterface.php
+++ /dev/null
@@ -1,318 +0,0 @@
-PhysicalInterfaces = new \Doctrine\Common\Collections\ArrayCollection();
-        $this->VlanInterfaces = new \Doctrine\Common\Collections\ArrayCollection();
-        $this->MACAddresses = new \Doctrine\Common\Collections\ArrayCollection();
-    }
-    
-    /**
-     * Set name
-     *
-     * @param string $name
-     * @return VirtualInterface
-     */
-    public function setName($name)
-    {
-        $this->name = $name;
-    
-        return $this;
-    }
-
-    /**
-     * Get name
-     *
-     * @return string 
-     */
-    public function getName()
-    {
-        return $this->name;
-    }
-
-    /**
-     * Set description
-     *
-     * @param string $description
-     * @return VirtualInterface
-     */
-    public function setDescription($description)
-    {
-        $this->description = $description;
-    
-        return $this;
-    }
-
-    /**
-     * Get description
-     *
-     * @return string 
-     */
-    public function getDescription()
-    {
-        return $this->description;
-    }
-
-    /**
-     * Set mtu
-     *
-     * @param integer $mtu
-     * @return VirtualInterface
-     */
-    public function setMtu($mtu)
-    {
-        $this->mtu = $mtu;
-    
-        return $this;
-    }
-
-    /**
-     * Get mtu
-     *
-     * @return integer 
-     */
-    public function getMtu()
-    {
-        return $this->mtu;
-    }
-
-    /**
-     * Set trunk
-     *
-     * @param boolean $trunk
-     * @return VirtualInterface
-     */
-    public function setTrunk($trunk)
-    {
-        $this->trunk = $trunk;
-    
-        return $this;
-    }
-
-    /**
-     * Get trunk
-     *
-     * @return boolean 
-     */
-    public function getTrunk()
-    {
-        return $this->trunk;
-    }
-
-    /**
-     * Set channelgroup
-     *
-     * @param integer $channelgroup
-     * @return VirtualInterface
-     */
-    public function setChannelgroup($channelgroup)
-    {
-        $this->channelgroup = $channelgroup;
-    
-        return $this;
-    }
-
-    /**
-     * Get channelgroup
-     *
-     * @return integer 
-     */
-    public function getChannelgroup()
-    {
-        return $this->channelgroup;
-    }
-
-    /**
-     * Get id
-     *
-     * @return integer 
-     */
-    public function getId()
-    {
-        return $this->id;
-    }
-
-    /**
-     * Add PhysicalInterfaces
-     *
-     * @param Entities\PhysicalInterface $physicalInterfaces
-     * @return VirtualInterface
-     */
-    public function addPhysicalInterface(\Entities\PhysicalInterface $physicalInterfaces)
-    {
-        $this->PhysicalInterfaces[] = $physicalInterfaces;
-    
-        return $this;
-    }
-
-    /**
-     * Remove PhysicalInterfaces
-     *
-     * @param Entities\PhysicalInterface $physicalInterfaces
-     */
-    public function removePhysicalInterface(\Entities\PhysicalInterface $physicalInterfaces)
-    {
-        $this->PhysicalInterfaces->removeElement($physicalInterfaces);
-    }
-
-    /**
-     * Get PhysicalInterfaces
-     *
-     * @return Doctrine\Common\Collections\Collection 
-     */
-    public function getPhysicalInterfaces()
-    {
-        return $this->PhysicalInterfaces;
-    }
-
-    /**
-     * Add VlanInterfaces
-     *
-     * @param Entities\VlanInterface $vlanInterfaces
-     * @return VirtualInterface
-     */
-    public function addVlanInterface(\Entities\VlanInterface $vlanInterfaces)
-    {
-        $this->VlanInterfaces[] = $vlanInterfaces;
-    
-        return $this;
-    }
-
-    /**
-     * Remove VlanInterfaces
-     *
-     * @param Entities\VlanInterface $vlanInterfaces
-     */
-    public function removeVlanInterface(\Entities\VlanInterface $vlanInterfaces)
-    {
-        $this->VlanInterfaces->removeElement($vlanInterfaces);
-    }
-
-    /**
-     * Get VlanInterfaces
-     *
-     * @return Doctrine\Common\Collections\Collection 
-     */
-    public function getVlanInterfaces()
-    {
-        return $this->VlanInterfaces;
-    }
-
-    /**
-     * Add MACAddresses
-     *
-     * @param Entities\MACAddress $mACAddresses
-     * @return VirtualInterface
-     */
-    public function addMACAddresse(\Entities\MACAddress $mACAddresses)
-    {
-        $this->MACAddresses[] = $mACAddresses;
-    
-        return $this;
-    }
-
-    /**
-     * Remove MACAddresses
-     *
-     * @param Entities\MACAddress $mACAddresses
-     */
-    public function removeMACAddresse(\Entities\MACAddress $mACAddresses)
-    {
-        $this->MACAddresses->removeElement($mACAddresses);
-    }
-
-    /**
-     * Get MACAddresses
-     *
-     * @return Doctrine\Common\Collections\Collection 
-     */
-    public function getMACAddresses()
-    {
-        return $this->MACAddresses;
-    }
-
-    /**
-     * Set Customer
-     *
-     * @param Entities\Customer $customer
-     * @return VirtualInterface
-     */
-    public function setCustomer(\Entities\Customer $customer = null)
-    {
-        $this->Customer = $customer;
-    
-        return $this;
-    }
-
-    /**
-     * Get Customer
-     *
-     * @return Entities\Customer 
-     */
-    public function getCustomer()
-    {
-        return $this->Customer;
-    }
-}
\ No newline at end of file
diff --git a/application/Entities/Vlan.php b/application/Entities/Vlan.php
deleted file mode 100644
index 3c3ec689b..000000000
--- a/application/Entities/Vlan.php
+++ /dev/null
@@ -1,301 +0,0 @@
-VlanInterfaces = new \Doctrine\Common\Collections\ArrayCollection();
-        $this->IPv4Addresses = new \Doctrine\Common\Collections\ArrayCollection();
-        $this->IPv6Addresses = new \Doctrine\Common\Collections\ArrayCollection();
-        $this->NetworkInfo = new \Doctrine\Common\Collections\ArrayCollection();
-    }
-    
-    /**
-     * Set name
-     *
-     * @param string $name
-     * @return Vlan
-     */
-    public function setName($name)
-    {
-        $this->name = $name;
-    
-        return $this;
-    }
-
-    /**
-     * Get name
-     *
-     * @return string 
-     */
-    public function getName()
-    {
-        return $this->name;
-    }
-
-    /**
-     * Set number
-     *
-     * @param integer $number
-     * @return Vlan
-     */
-    public function setNumber($number)
-    {
-        $this->number = $number;
-    
-        return $this;
-    }
-
-    /**
-     * Get number
-     *
-     * @return integer 
-     */
-    public function getNumber()
-    {
-        return $this->number;
-    }
-
-    /**
-     * Set rcvrfname
-     *
-     * @param string $rcvrfname
-     * @return Vlan
-     */
-    public function setRcvrfname($rcvrfname)
-    {
-        $this->rcvrfname = $rcvrfname;
-    
-        return $this;
-    }
-
-    /**
-     * Get rcvrfname
-     *
-     * @return string 
-     */
-    public function getRcvrfname()
-    {
-        return $this->rcvrfname;
-    }
-
-    /**
-     * Set notes
-     *
-     * @param string $notes
-     * @return Vlan
-     */
-    public function setNotes($notes)
-    {
-        $this->notes = $notes;
-    
-        return $this;
-    }
-
-    /**
-     * Get notes
-     *
-     * @return string 
-     */
-    public function getNotes()
-    {
-        return $this->notes;
-    }
-
-    /**
-     * Get id
-     *
-     * @return integer 
-     */
-    public function getId()
-    {
-        return $this->id;
-    }
-
-    /**
-     * Add VlanInterfaces
-     *
-     * @param Entities\VlanInterface $vlanInterfaces
-     * @return Vlan
-     */
-    public function addVlanInterface(\Entities\VlanInterface $vlanInterfaces)
-    {
-        $this->VlanInterfaces[] = $vlanInterfaces;
-    
-        return $this;
-    }
-
-    /**
-     * Remove VlanInterfaces
-     *
-     * @param Entities\VlanInterface $vlanInterfaces
-     */
-    public function removeVlanInterface(\Entities\VlanInterface $vlanInterfaces)
-    {
-        $this->VlanInterfaces->removeElement($vlanInterfaces);
-    }
-
-    /**
-     * Get VlanInterfaces
-     *
-     * @return Doctrine\Common\Collections\Collection 
-     */
-    public function getVlanInterfaces()
-    {
-        return $this->VlanInterfaces;
-    }
-
-    /**
-     * Add IPv4Addresses
-     *
-     * @param Entities\IPv4Address $iPv4Addresses
-     * @return Vlan
-     */
-    public function addIPv4Addresse(\Entities\IPv4Address $iPv4Addresses)
-    {
-        $this->IPv4Addresses[] = $iPv4Addresses;
-    
-        return $this;
-    }
-
-    /**
-     * Remove IPv4Addresses
-     *
-     * @param Entities\IPv4Address $iPv4Addresses
-     */
-    public function removeIPv4Addresse(\Entities\IPv4Address $iPv4Addresses)
-    {
-        $this->IPv4Addresses->removeElement($iPv4Addresses);
-    }
-
-    /**
-     * Get IPv4Addresses
-     *
-     * @return Doctrine\Common\Collections\Collection 
-     */
-    public function getIPv4Addresses()
-    {
-        return $this->IPv4Addresses;
-    }
-
-    /**
-     * Add IPv6Addresses
-     *
-     * @param Entities\IPv6Address $iPv6Addresses
-     * @return Vlan
-     */
-    public function addIPv6Addresse(\Entities\IPv6Address $iPv6Addresses)
-    {
-        $this->IPv6Addresses[] = $iPv6Addresses;
-    
-        return $this;
-    }
-
-    /**
-     * Remove IPv6Addresses
-     *
-     * @param Entities\IPv6Address $iPv6Addresses
-     */
-    public function removeIPv6Addresse(\Entities\IPv6Address $iPv6Addresses)
-    {
-        $this->IPv6Addresses->removeElement($iPv6Addresses);
-    }
-
-    /**
-     * Get IPv6Addresses
-     *
-     * @return Doctrine\Common\Collections\Collection 
-     */
-    public function getIPv6Addresses()
-    {
-        return $this->IPv6Addresses;
-    }
-
-    /**
-     * Add NetworkInfo
-     *
-     * @param Entities\NetworkInfo $networkInfo
-     * @return Vlan
-     */
-    public function addNetworkInfo(\Entities\NetworkInfo $networkInfo)
-    {
-        $this->NetworkInfo[] = $networkInfo;
-    
-        return $this;
-    }
-
-    /**
-     * Remove NetworkInfo
-     *
-     * @param Entities\NetworkInfo $networkInfo
-     */
-    public function removeNetworkInfo(\Entities\NetworkInfo $networkInfo)
-    {
-        $this->NetworkInfo->removeElement($networkInfo);
-    }
-
-    /**
-     * Get NetworkInfo
-     *
-     * @return Doctrine\Common\Collections\Collection 
-     */
-    public function getNetworkInfo()
-    {
-        return $this->NetworkInfo;
-    }
-}
\ No newline at end of file
diff --git a/application/Entities/VlanInterface.php b/application/Entities/VlanInterface.php
deleted file mode 100644
index eedc9a7ff..000000000
--- a/application/Entities/VlanInterface.php
+++ /dev/null
@@ -1,643 +0,0 @@
-ipv4enabled = $ipv4enabled;
-    
-        return $this;
-    }
-
-    /**
-     * Get ipv4enabled
-     *
-     * @return boolean 
-     */
-    public function getIpv4enabled()
-    {
-        return $this->ipv4enabled;
-    }
-
-    /**
-     * Set ipv4hostname
-     *
-     * @param string $ipv4hostname
-     * @return VlanInterface
-     */
-    public function setIpv4hostname($ipv4hostname)
-    {
-        $this->ipv4hostname = $ipv4hostname;
-    
-        return $this;
-    }
-
-    /**
-     * Get ipv4hostname
-     *
-     * @return string 
-     */
-    public function getIpv4hostname()
-    {
-        return $this->ipv4hostname;
-    }
-
-    /**
-     * Set ipv6enabled
-     *
-     * @param boolean $ipv6enabled
-     * @return VlanInterface
-     */
-    public function setIpv6enabled($ipv6enabled)
-    {
-        $this->ipv6enabled = $ipv6enabled;
-    
-        return $this;
-    }
-
-    /**
-     * Get ipv6enabled
-     *
-     * @return boolean 
-     */
-    public function getIpv6enabled()
-    {
-        return $this->ipv6enabled;
-    }
-
-    /**
-     * Set ipv6hostname
-     *
-     * @param string $ipv6hostname
-     * @return VlanInterface
-     */
-    public function setIpv6hostname($ipv6hostname)
-    {
-        $this->ipv6hostname = $ipv6hostname;
-    
-        return $this;
-    }
-
-    /**
-     * Get ipv6hostname
-     *
-     * @return string 
-     */
-    public function getIpv6hostname()
-    {
-        return $this->ipv6hostname;
-    }
-
-    /**
-     * Set mcastenabled
-     *
-     * @param boolean $mcastenabled
-     * @return VlanInterface
-     */
-    public function setMcastenabled($mcastenabled)
-    {
-        $this->mcastenabled = $mcastenabled;
-    
-        return $this;
-    }
-
-    /**
-     * Get mcastenabled
-     *
-     * @return boolean 
-     */
-    public function getMcastenabled()
-    {
-        return $this->mcastenabled;
-    }
-
-    /**
-     * Set irrdbfilter
-     *
-     * @param boolean $irrdbfilter
-     * @return VlanInterface
-     */
-    public function setIrrdbfilter($irrdbfilter)
-    {
-        $this->irrdbfilter = $irrdbfilter;
-    
-        return $this;
-    }
-
-    /**
-     * Get irrdbfilter
-     *
-     * @return boolean 
-     */
-    public function getIrrdbfilter()
-    {
-        return $this->irrdbfilter;
-    }
-
-    /**
-     * Set bgpmd5secret
-     *
-     * @param string $bgpmd5secret
-     * @return VlanInterface
-     */
-    public function setBgpmd5secret($bgpmd5secret)
-    {
-        $this->bgpmd5secret = $bgpmd5secret;
-    
-        return $this;
-    }
-
-    /**
-     * Get bgpmd5secret
-     *
-     * @return string 
-     */
-    public function getBgpmd5secret()
-    {
-        return $this->bgpmd5secret;
-    }
-
-    /**
-     * Set ipv4bgpmd5secret
-     *
-     * @param string $ipv4bgpmd5secret
-     * @return VlanInterface
-     */
-    public function setIpv4bgpmd5secret($ipv4bgpmd5secret)
-    {
-        $this->ipv4bgpmd5secret = $ipv4bgpmd5secret;
-    
-        return $this;
-    }
-
-    /**
-     * Get ipv4bgpmd5secret
-     *
-     * @return string 
-     */
-    public function getIpv4bgpmd5secret()
-    {
-        return $this->ipv4bgpmd5secret;
-    }
-
-    /**
-     * Set ipv6bgpmd5secret
-     *
-     * @param string $ipv6bgpmd5secret
-     * @return VlanInterface
-     */
-    public function setIpv6bgpmd5secret($ipv6bgpmd5secret)
-    {
-        $this->ipv6bgpmd5secret = $ipv6bgpmd5secret;
-    
-        return $this;
-    }
-
-    /**
-     * Get ipv6bgpmd5secret
-     *
-     * @return string 
-     */
-    public function getIpv6bgpmd5secret()
-    {
-        return $this->ipv6bgpmd5secret;
-    }
-
-    /**
-     * Set maxbgpprefix
-     *
-     * @param integer $maxbgpprefix
-     * @return VlanInterface
-     */
-    public function setMaxbgpprefix($maxbgpprefix)
-    {
-        $this->maxbgpprefix = $maxbgpprefix;
-    
-        return $this;
-    }
-
-    /**
-     * Get maxbgpprefix
-     *
-     * @return integer 
-     */
-    public function getMaxbgpprefix()
-    {
-        return $this->maxbgpprefix;
-    }
-
-    /**
-     * Set rsclient
-     *
-     * @param boolean $rsclient
-     * @return VlanInterface
-     */
-    public function setRsclient($rsclient)
-    {
-        $this->rsclient = $rsclient;
-    
-        return $this;
-    }
-
-    /**
-     * Get rsclient
-     *
-     * @return boolean 
-     */
-    public function getRsclient()
-    {
-        return $this->rsclient;
-    }
-
-    /**
-     * Set ipv4canping
-     *
-     * @param boolean $ipv4canping
-     * @return VlanInterface
-     */
-    public function setIpv4canping($ipv4canping)
-    {
-        $this->ipv4canping = $ipv4canping;
-    
-        return $this;
-    }
-
-    /**
-     * Get ipv4canping
-     *
-     * @return boolean 
-     */
-    public function getIpv4canping()
-    {
-        return $this->ipv4canping;
-    }
-
-    /**
-     * Set ipv6canping
-     *
-     * @param boolean $ipv6canping
-     * @return VlanInterface
-     */
-    public function setIpv6canping($ipv6canping)
-    {
-        $this->ipv6canping = $ipv6canping;
-    
-        return $this;
-    }
-
-    /**
-     * Get ipv6canping
-     *
-     * @return boolean 
-     */
-    public function getIpv6canping()
-    {
-        return $this->ipv6canping;
-    }
-
-    /**
-     * Set ipv4monitorrcbgp
-     *
-     * @param boolean $ipv4monitorrcbgp
-     * @return VlanInterface
-     */
-    public function setIpv4monitorrcbgp($ipv4monitorrcbgp)
-    {
-        $this->ipv4monitorrcbgp = $ipv4monitorrcbgp;
-    
-        return $this;
-    }
-
-    /**
-     * Get ipv4monitorrcbgp
-     *
-     * @return boolean 
-     */
-    public function getIpv4monitorrcbgp()
-    {
-        return $this->ipv4monitorrcbgp;
-    }
-
-    /**
-     * Set ipv6monitorrcbgp
-     *
-     * @param boolean $ipv6monitorrcbgp
-     * @return VlanInterface
-     */
-    public function setIpv6monitorrcbgp($ipv6monitorrcbgp)
-    {
-        $this->ipv6monitorrcbgp = $ipv6monitorrcbgp;
-    
-        return $this;
-    }
-
-    /**
-     * Get ipv6monitorrcbgp
-     *
-     * @return boolean 
-     */
-    public function getIpv6monitorrcbgp()
-    {
-        return $this->ipv6monitorrcbgp;
-    }
-
-    /**
-     * Set as112client
-     *
-     * @param boolean $as112client
-     * @return VlanInterface
-     */
-    public function setAs112client($as112client)
-    {
-        $this->as112client = $as112client;
-    
-        return $this;
-    }
-
-    /**
-     * Get as112client
-     *
-     * @return boolean 
-     */
-    public function getAs112client()
-    {
-        return $this->as112client;
-    }
-
-    /**
-     * Set busyhost
-     *
-     * @param boolean $busyhost
-     * @return VlanInterface
-     */
-    public function setBusyhost($busyhost)
-    {
-        $this->busyhost = $busyhost;
-    
-        return $this;
-    }
-
-    /**
-     * Get busyhost
-     *
-     * @return boolean 
-     */
-    public function getBusyhost()
-    {
-        return $this->busyhost;
-    }
-
-    /**
-     * Set notes
-     *
-     * @param string $notes
-     * @return VlanInterface
-     */
-    public function setNotes($notes)
-    {
-        $this->notes = $notes;
-    
-        return $this;
-    }
-
-    /**
-     * Get notes
-     *
-     * @return string 
-     */
-    public function getNotes()
-    {
-        return $this->notes;
-    }
-
-    /**
-     * Get id
-     *
-     * @return integer 
-     */
-    public function getId()
-    {
-        return $this->id;
-    }
-
-    /**
-     * Set IPv4Address
-     *
-     * @param Entities\IPv4Address $iPv4Address
-     * @return VlanInterface
-     */
-    public function setIPv4Address(\Entities\IPv4Address $iPv4Address = null)
-    {
-        $this->IPv4Address = $iPv4Address;
-    
-        return $this;
-    }
-
-    /**
-     * Get IPv4Address
-     *
-     * @return Entities\IPv4Address 
-     */
-    public function getIPv4Address()
-    {
-        return $this->IPv4Address;
-    }
-
-    /**
-     * Set IPv6Address
-     *
-     * @param Entities\IPv6Address $iPv6Address
-     * @return VlanInterface
-     */
-    public function setIPv6Address(\Entities\IPv6Address $iPv6Address = null)
-    {
-        $this->IPv6Address = $iPv6Address;
-    
-        return $this;
-    }
-
-    /**
-     * Get IPv6Address
-     *
-     * @return Entities\IPv6Address 
-     */
-    public function getIPv6Address()
-    {
-        return $this->IPv6Address;
-    }
-
-    /**
-     * Set VirtualInterface
-     *
-     * @param Entities\VirtualInterface $virtualInterface
-     * @return VlanInterface
-     */
-    public function setVirtualInterface(\Entities\VirtualInterface $virtualInterface = null)
-    {
-        $this->VirtualInterface = $virtualInterface;
-    
-        return $this;
-    }
-
-    /**
-     * Get VirtualInterface
-     *
-     * @return Entities\VirtualInterface 
-     */
-    public function getVirtualInterface()
-    {
-        return $this->VirtualInterface;
-    }
-
-    /**
-     * Set Vlan
-     *
-     * @param Entities\Vlan $vlan
-     * @return VlanInterface
-     */
-    public function setVlan(\Entities\Vlan $vlan = null)
-    {
-        $this->Vlan = $vlan;
-    
-        return $this;
-    }
-
-    /**
-     * Get Vlan
-     *
-     * @return Entities\Vlan 
-     */
-    public function getVlan()
-    {
-        return $this->Vlan;
-    }
-}
\ No newline at end of file
diff --git a/application/Proxies/__CG__EntitiesBGPSessionData.php b/application/Proxies/__CG__EntitiesBGPSessionData.php
deleted file mode 100644
index d3fbeab1d..000000000
--- a/application/Proxies/__CG__EntitiesBGPSessionData.php
+++ /dev/null
@@ -1,172 +0,0 @@
-_entityPersister = $entityPersister;
-        $this->_identifier = $identifier;
-    }
-    /** @private */
-    public function __load()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-
-            if (method_exists($this, "__wakeup")) {
-                // call this after __isInitialized__to avoid infinite recursion
-                // but before loading to emulate what ClassMetadata::newInstance()
-                // provides.
-                $this->__wakeup();
-            }
-
-            if ($this->_entityPersister->load($this->_identifier, $this) === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-    }
-
-    /** @private */
-    public function __isInitialized()
-    {
-        return $this->__isInitialized__;
-    }
-
-    
-    public function setSrcipaddressid($srcipaddressid)
-    {
-        $this->__load();
-        return parent::setSrcipaddressid($srcipaddressid);
-    }
-
-    public function getSrcipaddressid()
-    {
-        $this->__load();
-        return parent::getSrcipaddressid();
-    }
-
-    public function setDesipaddressid($desipaddressid)
-    {
-        $this->__load();
-        return parent::setDesipaddressid($desipaddressid);
-    }
-
-    public function getDesipaddressid()
-    {
-        $this->__load();
-        return parent::getDesipaddressid();
-    }
-
-    public function setProtocol($protocol)
-    {
-        $this->__load();
-        return parent::setProtocol($protocol);
-    }
-
-    public function getProtocol()
-    {
-        $this->__load();
-        return parent::getProtocol();
-    }
-
-    public function setVlan($vlan)
-    {
-        $this->__load();
-        return parent::setVlan($vlan);
-    }
-
-    public function getVlan()
-    {
-        $this->__load();
-        return parent::getVlan();
-    }
-
-    public function setPacketcount($packetcount)
-    {
-        $this->__load();
-        return parent::setPacketcount($packetcount);
-    }
-
-    public function getPacketcount()
-    {
-        $this->__load();
-        return parent::getPacketcount();
-    }
-
-    public function setTimestamp($timestamp)
-    {
-        $this->__load();
-        return parent::setTimestamp($timestamp);
-    }
-
-    public function getTimestamp()
-    {
-        $this->__load();
-        return parent::getTimestamp();
-    }
-
-    public function setSource($source)
-    {
-        $this->__load();
-        return parent::setSource($source);
-    }
-
-    public function getSource()
-    {
-        $this->__load();
-        return parent::getSource();
-    }
-
-    public function getId()
-    {
-        if ($this->__isInitialized__ === false) {
-            return (int) $this->_identifier["id"];
-        }
-        $this->__load();
-        return parent::getId();
-    }
-
-    public function setDstipaddressid($dstipaddressid)
-    {
-        $this->__load();
-        return parent::setDstipaddressid($dstipaddressid);
-    }
-
-    public function getDstipaddressid()
-    {
-        $this->__load();
-        return parent::getDstipaddressid();
-    }
-
-
-    public function __sleep()
-    {
-        return array('__isInitialized__', 'srcipaddressid', 'dstipaddressid', 'protocol', 'vlan', 'packetcount', 'timestamp', 'source', 'id');
-    }
-
-    public function __clone()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-            $class = $this->_entityPersister->getClassMetadata();
-            $original = $this->_entityPersister->load($this->_identifier);
-            if ($original === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            foreach ($class->reflFields as $field => $reflProperty) {
-                $reflProperty->setValue($this, $reflProperty->getValue($original));
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-        
-    }
-}
\ No newline at end of file
diff --git a/application/Proxies/__CG__EntitiesCabinet.php b/application/Proxies/__CG__EntitiesCabinet.php
deleted file mode 100644
index 60c68de14..000000000
--- a/application/Proxies/__CG__EntitiesCabinet.php
+++ /dev/null
@@ -1,196 +0,0 @@
-_entityPersister = $entityPersister;
-        $this->_identifier = $identifier;
-    }
-    /** @private */
-    public function __load()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-
-            if (method_exists($this, "__wakeup")) {
-                // call this after __isInitialized__to avoid infinite recursion
-                // but before loading to emulate what ClassMetadata::newInstance()
-                // provides.
-                $this->__wakeup();
-            }
-
-            if ($this->_entityPersister->load($this->_identifier, $this) === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-    }
-
-    /** @private */
-    public function __isInitialized()
-    {
-        return $this->__isInitialized__;
-    }
-
-    
-    public function setName($name)
-    {
-        $this->__load();
-        return parent::setName($name);
-    }
-
-    public function getName()
-    {
-        $this->__load();
-        return parent::getName();
-    }
-
-    public function setColocation($colocation)
-    {
-        $this->__load();
-        return parent::setColocation($colocation);
-    }
-
-    public function getColocation()
-    {
-        $this->__load();
-        return parent::getColocation();
-    }
-
-    public function setHeight($height)
-    {
-        $this->__load();
-        return parent::setHeight($height);
-    }
-
-    public function getHeight()
-    {
-        $this->__load();
-        return parent::getHeight();
-    }
-
-    public function setType($type)
-    {
-        $this->__load();
-        return parent::setType($type);
-    }
-
-    public function getType()
-    {
-        $this->__load();
-        return parent::getType();
-    }
-
-    public function setNotes($notes)
-    {
-        $this->__load();
-        return parent::setNotes($notes);
-    }
-
-    public function getNotes()
-    {
-        $this->__load();
-        return parent::getNotes();
-    }
-
-    public function getId()
-    {
-        if ($this->__isInitialized__ === false) {
-            return (int) $this->_identifier["id"];
-        }
-        $this->__load();
-        return parent::getId();
-    }
-
-    public function addSwitche(\Entities\Switcher $switches)
-    {
-        $this->__load();
-        return parent::addSwitche($switches);
-    }
-
-    public function removeSwitche(\Entities\Switcher $switches)
-    {
-        $this->__load();
-        return parent::removeSwitche($switches);
-    }
-
-    public function getSwitches()
-    {
-        $this->__load();
-        return parent::getSwitches();
-    }
-
-    public function addCustomerEquipment(\Entities\CustomerEquipment $customerEquipment)
-    {
-        $this->__load();
-        return parent::addCustomerEquipment($customerEquipment);
-    }
-
-    public function removeCustomerEquipment(\Entities\CustomerEquipment $customerEquipment)
-    {
-        $this->__load();
-        return parent::removeCustomerEquipment($customerEquipment);
-    }
-
-    public function getCustomerEquipment()
-    {
-        $this->__load();
-        return parent::getCustomerEquipment();
-    }
-
-    public function setLocation(\Entities\Location $location = NULL)
-    {
-        $this->__load();
-        return parent::setLocation($location);
-    }
-
-    public function getLocation()
-    {
-        $this->__load();
-        return parent::getLocation();
-    }
-
-    public function setCololocation($cololocation)
-    {
-        $this->__load();
-        return parent::setCololocation($cololocation);
-    }
-
-    public function getCololocation()
-    {
-        $this->__load();
-        return parent::getCololocation();
-    }
-
-
-    public function __sleep()
-    {
-        return array('__isInitialized__', 'name', 'cololocation', 'height', 'type', 'notes', 'id', 'Switches', 'CustomerEquipment', 'Location');
-    }
-
-    public function __clone()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-            $class = $this->_entityPersister->getClassMetadata();
-            $original = $this->_entityPersister->load($this->_identifier);
-            if ($original === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            foreach ($class->reflFields as $field => $reflProperty) {
-                $reflProperty->setValue($this, $reflProperty->getValue($original));
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-        
-    }
-}
\ No newline at end of file
diff --git a/application/Proxies/__CG__EntitiesChangeLog.php b/application/Proxies/__CG__EntitiesChangeLog.php
deleted file mode 100644
index a883139da..000000000
--- a/application/Proxies/__CG__EntitiesChangeLog.php
+++ /dev/null
@@ -1,160 +0,0 @@
-_entityPersister = $entityPersister;
-        $this->_identifier = $identifier;
-    }
-    /** @private */
-    public function __load()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-
-            if (method_exists($this, "__wakeup")) {
-                // call this after __isInitialized__to avoid infinite recursion
-                // but before loading to emulate what ClassMetadata::newInstance()
-                // provides.
-                $this->__wakeup();
-            }
-
-            if ($this->_entityPersister->load($this->_identifier, $this) === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-    }
-
-    /** @private */
-    public function __isInitialized()
-    {
-        return $this->__isInitialized__;
-    }
-
-    
-    public function setTitle($title)
-    {
-        $this->__load();
-        return parent::setTitle($title);
-    }
-
-    public function getTitle()
-    {
-        $this->__load();
-        return parent::getTitle();
-    }
-
-    public function setDetails($details)
-    {
-        $this->__load();
-        return parent::setDetails($details);
-    }
-
-    public function getDetails()
-    {
-        $this->__load();
-        return parent::getDetails();
-    }
-
-    public function setVisibility($visibility)
-    {
-        $this->__load();
-        return parent::setVisibility($visibility);
-    }
-
-    public function getVisibility()
-    {
-        $this->__load();
-        return parent::getVisibility();
-    }
-
-    public function setLivedate($livedate)
-    {
-        $this->__load();
-        return parent::setLivedate($livedate);
-    }
-
-    public function getLivedate()
-    {
-        $this->__load();
-        return parent::getLivedate();
-    }
-
-    public function setVersion($version)
-    {
-        $this->__load();
-        return parent::setVersion($version);
-    }
-
-    public function getVersion()
-    {
-        $this->__load();
-        return parent::getVersion();
-    }
-
-    public function setCreatedAt($createdAt)
-    {
-        $this->__load();
-        return parent::setCreatedAt($createdAt);
-    }
-
-    public function getCreatedAt()
-    {
-        $this->__load();
-        return parent::getCreatedAt();
-    }
-
-    public function getId()
-    {
-        if ($this->__isInitialized__ === false) {
-            return $this->_identifier["id"];
-        }
-        $this->__load();
-        return parent::getId();
-    }
-
-    public function setUser(\Entities\User $user = NULL)
-    {
-        $this->__load();
-        return parent::setUser($user);
-    }
-
-    public function getUser()
-    {
-        $this->__load();
-        return parent::getUser();
-    }
-
-
-    public function __sleep()
-    {
-        return array('__isInitialized__', 'title', 'details', 'visibility', 'livedate', 'version', 'created_at', 'id', 'User');
-    }
-
-    public function __clone()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-            $class = $this->_entityPersister->getClassMetadata();
-            $original = $this->_entityPersister->load($this->_identifier);
-            if ($original === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            foreach ($class->reflFields as $field => $reflProperty) {
-                $reflProperty->setValue($this, $reflProperty->getValue($original));
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-        
-    }
-}
\ No newline at end of file
diff --git a/application/Proxies/__CG__EntitiesConsoleServerConnection.php b/application/Proxies/__CG__EntitiesConsoleServerConnection.php
deleted file mode 100644
index 7c18c683a..000000000
--- a/application/Proxies/__CG__EntitiesConsoleServerConnection.php
+++ /dev/null
@@ -1,196 +0,0 @@
-_entityPersister = $entityPersister;
-        $this->_identifier = $identifier;
-    }
-    /** @private */
-    public function __load()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-
-            if (method_exists($this, "__wakeup")) {
-                // call this after __isInitialized__to avoid infinite recursion
-                // but before loading to emulate what ClassMetadata::newInstance()
-                // provides.
-                $this->__wakeup();
-            }
-
-            if ($this->_entityPersister->load($this->_identifier, $this) === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-    }
-
-    /** @private */
-    public function __isInitialized()
-    {
-        return $this->__isInitialized__;
-    }
-
-    
-    public function setDescription($description)
-    {
-        $this->__load();
-        return parent::setDescription($description);
-    }
-
-    public function getDescription()
-    {
-        $this->__load();
-        return parent::getDescription();
-    }
-
-    public function setPort($port)
-    {
-        $this->__load();
-        return parent::setPort($port);
-    }
-
-    public function getPort()
-    {
-        $this->__load();
-        return parent::getPort();
-    }
-
-    public function setSpeed($speed)
-    {
-        $this->__load();
-        return parent::setSpeed($speed);
-    }
-
-    public function getSpeed()
-    {
-        $this->__load();
-        return parent::getSpeed();
-    }
-
-    public function setParity($parity)
-    {
-        $this->__load();
-        return parent::setParity($parity);
-    }
-
-    public function getParity()
-    {
-        $this->__load();
-        return parent::getParity();
-    }
-
-    public function setStopbits($stopbits)
-    {
-        $this->__load();
-        return parent::setStopbits($stopbits);
-    }
-
-    public function getStopbits()
-    {
-        $this->__load();
-        return parent::getStopbits();
-    }
-
-    public function setFlowcontrol($flowcontrol)
-    {
-        $this->__load();
-        return parent::setFlowcontrol($flowcontrol);
-    }
-
-    public function getFlowcontrol()
-    {
-        $this->__load();
-        return parent::getFlowcontrol();
-    }
-
-    public function setAutobaud($autobaud)
-    {
-        $this->__load();
-        return parent::setAutobaud($autobaud);
-    }
-
-    public function getAutobaud()
-    {
-        $this->__load();
-        return parent::getAutobaud();
-    }
-
-    public function setNotes($notes)
-    {
-        $this->__load();
-        return parent::setNotes($notes);
-    }
-
-    public function getNotes()
-    {
-        $this->__load();
-        return parent::getNotes();
-    }
-
-    public function getId()
-    {
-        if ($this->__isInitialized__ === false) {
-            return (int) $this->_identifier["id"];
-        }
-        $this->__load();
-        return parent::getId();
-    }
-
-    public function setCustomer(\Entities\Customer $customer = NULL)
-    {
-        $this->__load();
-        return parent::setCustomer($customer);
-    }
-
-    public function getCustomer()
-    {
-        $this->__load();
-        return parent::getCustomer();
-    }
-
-    public function setSwitcher(\Entities\Switcher $switcher = NULL)
-    {
-        $this->__load();
-        return parent::setSwitcher($switcher);
-    }
-
-    public function getSwitcher()
-    {
-        $this->__load();
-        return parent::getSwitcher();
-    }
-
-
-    public function __sleep()
-    {
-        return array('__isInitialized__', 'description', 'port', 'speed', 'parity', 'stopbits', 'flowcontrol', 'autobaud', 'notes', 'id', 'Customer', 'Switcher');
-    }
-
-    public function __clone()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-            $class = $this->_entityPersister->getClassMetadata();
-            $original = $this->_entityPersister->load($this->_identifier);
-            if ($original === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            foreach ($class->reflFields as $field => $reflProperty) {
-                $reflProperty->setValue($this, $reflProperty->getValue($original));
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-        
-    }
-}
\ No newline at end of file
diff --git a/application/Proxies/__CG__EntitiesContact.php b/application/Proxies/__CG__EntitiesContact.php
deleted file mode 100644
index a5b47caba..000000000
--- a/application/Proxies/__CG__EntitiesContact.php
+++ /dev/null
@@ -1,208 +0,0 @@
-_entityPersister = $entityPersister;
-        $this->_identifier = $identifier;
-    }
-    /** @private */
-    public function __load()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-
-            if (method_exists($this, "__wakeup")) {
-                // call this after __isInitialized__to avoid infinite recursion
-                // but before loading to emulate what ClassMetadata::newInstance()
-                // provides.
-                $this->__wakeup();
-            }
-
-            if ($this->_entityPersister->load($this->_identifier, $this) === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-    }
-
-    /** @private */
-    public function __isInitialized()
-    {
-        return $this->__isInitialized__;
-    }
-
-    
-    public function setName($name)
-    {
-        $this->__load();
-        return parent::setName($name);
-    }
-
-    public function getName()
-    {
-        $this->__load();
-        return parent::getName();
-    }
-
-    public function setEmail($email)
-    {
-        $this->__load();
-        return parent::setEmail($email);
-    }
-
-    public function getEmail()
-    {
-        $this->__load();
-        return parent::getEmail();
-    }
-
-    public function setPhone($phone)
-    {
-        $this->__load();
-        return parent::setPhone($phone);
-    }
-
-    public function getPhone()
-    {
-        $this->__load();
-        return parent::getPhone();
-    }
-
-    public function setMobile($mobile)
-    {
-        $this->__load();
-        return parent::setMobile($mobile);
-    }
-
-    public function getMobile()
-    {
-        $this->__load();
-        return parent::getMobile();
-    }
-
-    public function setFacilityaccess($facilityaccess)
-    {
-        $this->__load();
-        return parent::setFacilityaccess($facilityaccess);
-    }
-
-    public function getFacilityaccess()
-    {
-        $this->__load();
-        return parent::getFacilityaccess();
-    }
-
-    public function setMayauthorize($mayauthorize)
-    {
-        $this->__load();
-        return parent::setMayauthorize($mayauthorize);
-    }
-
-    public function getMayauthorize()
-    {
-        $this->__load();
-        return parent::getMayauthorize();
-    }
-
-    public function setLastupdated($lastupdated)
-    {
-        $this->__load();
-        return parent::setLastupdated($lastupdated);
-    }
-
-    public function getLastupdated()
-    {
-        $this->__load();
-        return parent::getLastupdated();
-    }
-
-    public function setLastupdatedby($lastupdatedby)
-    {
-        $this->__load();
-        return parent::setLastupdatedby($lastupdatedby);
-    }
-
-    public function getLastupdatedby()
-    {
-        $this->__load();
-        return parent::getLastupdatedby();
-    }
-
-    public function setCreator($creator)
-    {
-        $this->__load();
-        return parent::setCreator($creator);
-    }
-
-    public function getCreator()
-    {
-        $this->__load();
-        return parent::getCreator();
-    }
-
-    public function setCreated($created)
-    {
-        $this->__load();
-        return parent::setCreated($created);
-    }
-
-    public function getCreated()
-    {
-        $this->__load();
-        return parent::getCreated();
-    }
-
-    public function getId()
-    {
-        if ($this->__isInitialized__ === false) {
-            return (int) $this->_identifier["id"];
-        }
-        $this->__load();
-        return parent::getId();
-    }
-
-    public function setCustomer(\Entities\Customer $customer = NULL)
-    {
-        $this->__load();
-        return parent::setCustomer($customer);
-    }
-
-    public function getCustomer()
-    {
-        $this->__load();
-        return parent::getCustomer();
-    }
-
-
-    public function __sleep()
-    {
-        return array('__isInitialized__', 'name', 'email', 'phone', 'mobile', 'facilityaccess', 'mayauthorize', 'lastupdated', 'lastupdatedby', 'creator', 'created', 'id', 'Customer');
-    }
-
-    public function __clone()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-            $class = $this->_entityPersister->getClassMetadata();
-            $original = $this->_entityPersister->load($this->_identifier);
-            if ($original === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            foreach ($class->reflFields as $field => $reflProperty) {
-                $reflProperty->setValue($this, $reflProperty->getValue($original));
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-        
-    }
-}
\ No newline at end of file
diff --git a/application/Proxies/__CG__EntitiesCustomer.php b/application/Proxies/__CG__EntitiesCustomer.php
deleted file mode 100644
index c3c0a5062..000000000
--- a/application/Proxies/__CG__EntitiesCustomer.php
+++ /dev/null
@@ -1,736 +0,0 @@
-_entityPersister = $entityPersister;
-        $this->_identifier = $identifier;
-    }
-    /** @private */
-    public function __load()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-
-            if (method_exists($this, "__wakeup")) {
-                // call this after __isInitialized__to avoid infinite recursion
-                // but before loading to emulate what ClassMetadata::newInstance()
-                // provides.
-                $this->__wakeup();
-            }
-
-            if ($this->_entityPersister->load($this->_identifier, $this) === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-    }
-
-    /** @private */
-    public function __isInitialized()
-    {
-        return $this->__isInitialized__;
-    }
-
-    
-    public function setName($name)
-    {
-        $this->__load();
-        return parent::setName($name);
-    }
-
-    public function getName()
-    {
-        $this->__load();
-        return parent::getName();
-    }
-
-    public function setType($type)
-    {
-        $this->__load();
-        return parent::setType($type);
-    }
-
-    public function getType()
-    {
-        $this->__load();
-        return parent::getType();
-    }
-
-    public function setShortname($shortname)
-    {
-        $this->__load();
-        return parent::setShortname($shortname);
-    }
-
-    public function getShortname()
-    {
-        $this->__load();
-        return parent::getShortname();
-    }
-
-    public function setAutsys($autsys)
-    {
-        $this->__load();
-        return parent::setAutsys($autsys);
-    }
-
-    public function getAutsys()
-    {
-        $this->__load();
-        return parent::getAutsys();
-    }
-
-    public function setMaxprefixes($maxprefixes)
-    {
-        $this->__load();
-        return parent::setMaxprefixes($maxprefixes);
-    }
-
-    public function getMaxprefixes()
-    {
-        $this->__load();
-        return parent::getMaxprefixes();
-    }
-
-    public function setPeeringemail($peeringemail)
-    {
-        $this->__load();
-        return parent::setPeeringemail($peeringemail);
-    }
-
-    public function getPeeringemail()
-    {
-        $this->__load();
-        return parent::getPeeringemail();
-    }
-
-    public function setNocphone($nocphone)
-    {
-        $this->__load();
-        return parent::setNocphone($nocphone);
-    }
-
-    public function getNocphone()
-    {
-        $this->__load();
-        return parent::getNocphone();
-    }
-
-    public function setNoc24hrphone($noc24hrphone)
-    {
-        $this->__load();
-        return parent::setNoc24hrphone($noc24hrphone);
-    }
-
-    public function getNoc24hrphone()
-    {
-        $this->__load();
-        return parent::getNoc24hrphone();
-    }
-
-    public function setNocfax($nocfax)
-    {
-        $this->__load();
-        return parent::setNocfax($nocfax);
-    }
-
-    public function getNocfax()
-    {
-        $this->__load();
-        return parent::getNocfax();
-    }
-
-    public function setNocemail($nocemail)
-    {
-        $this->__load();
-        return parent::setNocemail($nocemail);
-    }
-
-    public function getNocemail()
-    {
-        $this->__load();
-        return parent::getNocemail();
-    }
-
-    public function setNochours($nochours)
-    {
-        $this->__load();
-        return parent::setNochours($nochours);
-    }
-
-    public function getNochours()
-    {
-        $this->__load();
-        return parent::getNochours();
-    }
-
-    public function setNocwww($nocwww)
-    {
-        $this->__load();
-        return parent::setNocwww($nocwww);
-    }
-
-    public function getNocwww()
-    {
-        $this->__load();
-        return parent::getNocwww();
-    }
-
-    public function setIrrdb($irrdb)
-    {
-        $this->__load();
-        return parent::setIrrdb($irrdb);
-    }
-
-    public function getIrrdb()
-    {
-        $this->__load();
-        return parent::getIrrdb();
-    }
-
-    public function setPeeringmacro($peeringmacro)
-    {
-        $this->__load();
-        return parent::setPeeringmacro($peeringmacro);
-    }
-
-    public function getPeeringmacro()
-    {
-        $this->__load();
-        return parent::getPeeringmacro();
-    }
-
-    public function setPeeringpolicy($peeringpolicy)
-    {
-        $this->__load();
-        return parent::setPeeringpolicy($peeringpolicy);
-    }
-
-    public function getPeeringpolicy()
-    {
-        $this->__load();
-        return parent::getPeeringpolicy();
-    }
-
-    public function setBillingContact($billingContact)
-    {
-        $this->__load();
-        return parent::setBillingContact($billingContact);
-    }
-
-    public function getBillingContact()
-    {
-        $this->__load();
-        return parent::getBillingContact();
-    }
-
-    public function setBillingAddress1($billingAddress1)
-    {
-        $this->__load();
-        return parent::setBillingAddress1($billingAddress1);
-    }
-
-    public function getBillingAddress1()
-    {
-        $this->__load();
-        return parent::getBillingAddress1();
-    }
-
-    public function setBillingAddress2($billingAddress2)
-    {
-        $this->__load();
-        return parent::setBillingAddress2($billingAddress2);
-    }
-
-    public function getBillingAddress2()
-    {
-        $this->__load();
-        return parent::getBillingAddress2();
-    }
-
-    public function setBillingCity($billingCity)
-    {
-        $this->__load();
-        return parent::setBillingCity($billingCity);
-    }
-
-    public function getBillingCity()
-    {
-        $this->__load();
-        return parent::getBillingCity();
-    }
-
-    public function setBillingCountry($billingCountry)
-    {
-        $this->__load();
-        return parent::setBillingCountry($billingCountry);
-    }
-
-    public function getBillingCountry()
-    {
-        $this->__load();
-        return parent::getBillingCountry();
-    }
-
-    public function setCorpwww($corpwww)
-    {
-        $this->__load();
-        return parent::setCorpwww($corpwww);
-    }
-
-    public function getCorpwww()
-    {
-        $this->__load();
-        return parent::getCorpwww();
-    }
-
-    public function setDatejoin($datejoin)
-    {
-        $this->__load();
-        return parent::setDatejoin($datejoin);
-    }
-
-    public function getDatejoin()
-    {
-        $this->__load();
-        return parent::getDatejoin();
-    }
-
-    public function setDateleave($dateleave)
-    {
-        $this->__load();
-        return parent::setDateleave($dateleave);
-    }
-
-    public function getDateleave()
-    {
-        $this->__load();
-        return parent::getDateleave();
-    }
-
-    public function setStatus($status)
-    {
-        $this->__load();
-        return parent::setStatus($status);
-    }
-
-    public function getStatus()
-    {
-        $this->__load();
-        return parent::getStatus();
-    }
-
-    public function setActivepeeringmatrix($activepeeringmatrix)
-    {
-        $this->__load();
-        return parent::setActivepeeringmatrix($activepeeringmatrix);
-    }
-
-    public function getActivepeeringmatrix()
-    {
-        $this->__load();
-        return parent::getActivepeeringmatrix();
-    }
-
-    public function setNotes($notes)
-    {
-        $this->__load();
-        return parent::setNotes($notes);
-    }
-
-    public function getNotes()
-    {
-        $this->__load();
-        return parent::getNotes();
-    }
-
-    public function setLastupdated($lastupdated)
-    {
-        $this->__load();
-        return parent::setLastupdated($lastupdated);
-    }
-
-    public function getLastupdated()
-    {
-        $this->__load();
-        return parent::getLastupdated();
-    }
-
-    public function setLastupdatedby($lastupdatedby)
-    {
-        $this->__load();
-        return parent::setLastupdatedby($lastupdatedby);
-    }
-
-    public function getLastupdatedby()
-    {
-        $this->__load();
-        return parent::getLastupdatedby();
-    }
-
-    public function setCreator($creator)
-    {
-        $this->__load();
-        return parent::setCreator($creator);
-    }
-
-    public function getCreator()
-    {
-        $this->__load();
-        return parent::getCreator();
-    }
-
-    public function setCreated($created)
-    {
-        $this->__load();
-        return parent::setCreated($created);
-    }
-
-    public function getCreated()
-    {
-        $this->__load();
-        return parent::getCreated();
-    }
-
-    public function getId()
-    {
-        if ($this->__isInitialized__ === false) {
-            return (int) $this->_identifier["id"];
-        }
-        $this->__load();
-        return parent::getId();
-    }
-
-    public function addVirtualInterface(\Entities\VirtualInterface $virtualInterfaces)
-    {
-        $this->__load();
-        return parent::addVirtualInterface($virtualInterfaces);
-    }
-
-    public function removeVirtualInterface(\Entities\VirtualInterface $virtualInterfaces)
-    {
-        $this->__load();
-        return parent::removeVirtualInterface($virtualInterfaces);
-    }
-
-    public function getVirtualInterfaces()
-    {
-        $this->__load();
-        return parent::getVirtualInterfaces();
-    }
-
-    public function addContact(\Entities\Contact $contacts)
-    {
-        $this->__load();
-        return parent::addContact($contacts);
-    }
-
-    public function removeContact(\Entities\Contact $contacts)
-    {
-        $this->__load();
-        return parent::removeContact($contacts);
-    }
-
-    public function getContacts()
-    {
-        $this->__load();
-        return parent::getContacts();
-    }
-
-    public function addConsoleServerConnection(\Entities\ConsoleServerConnection $consoleServerConnections)
-    {
-        $this->__load();
-        return parent::addConsoleServerConnection($consoleServerConnections);
-    }
-
-    public function removeConsoleServerConnection(\Entities\ConsoleServerConnection $consoleServerConnections)
-    {
-        $this->__load();
-        return parent::removeConsoleServerConnection($consoleServerConnections);
-    }
-
-    public function getConsoleServerConnections()
-    {
-        $this->__load();
-        return parent::getConsoleServerConnections();
-    }
-
-    public function addCustomerEquipment(\Entities\CustomerEquipment $customerEquipment)
-    {
-        $this->__load();
-        return parent::addCustomerEquipment($customerEquipment);
-    }
-
-    public function removeCustomerEquipment(\Entities\CustomerEquipment $customerEquipment)
-    {
-        $this->__load();
-        return parent::removeCustomerEquipment($customerEquipment);
-    }
-
-    public function getCustomerEquipment()
-    {
-        $this->__load();
-        return parent::getCustomerEquipment();
-    }
-
-    public function addPeer(\Entities\PeeringManager $peers)
-    {
-        $this->__load();
-        return parent::addPeer($peers);
-    }
-
-    public function removePeer(\Entities\PeeringManager $peers)
-    {
-        $this->__load();
-        return parent::removePeer($peers);
-    }
-
-    public function getPeers()
-    {
-        $this->__load();
-        return parent::getPeers();
-    }
-
-    public function addPeersWith(\Entities\PeeringManager $peersWith)
-    {
-        $this->__load();
-        return parent::addPeersWith($peersWith);
-    }
-
-    public function removePeersWith(\Entities\PeeringManager $peersWith)
-    {
-        $this->__load();
-        return parent::removePeersWith($peersWith);
-    }
-
-    public function getPeersWith()
-    {
-        $this->__load();
-        return parent::getPeersWith();
-    }
-
-    public function addXCust(\Entities\PeeringMatrix $xCusts)
-    {
-        $this->__load();
-        return parent::addXCust($xCusts);
-    }
-
-    public function removeXCust(\Entities\PeeringMatrix $xCusts)
-    {
-        $this->__load();
-        return parent::removeXCust($xCusts);
-    }
-
-    public function getXCusts()
-    {
-        $this->__load();
-        return parent::getXCusts();
-    }
-
-    public function addYCust(\Entities\PeeringMatrix $yCusts)
-    {
-        $this->__load();
-        return parent::addYCust($yCusts);
-    }
-
-    public function removeYCust(\Entities\PeeringMatrix $yCusts)
-    {
-        $this->__load();
-        return parent::removeYCust($yCusts);
-    }
-
-    public function getYCusts()
-    {
-        $this->__load();
-        return parent::getYCusts();
-    }
-
-    public function addRSDroppedPrefixe(\Entities\RSDroppedPrefix $rSDroppedPrefixes)
-    {
-        $this->__load();
-        return parent::addRSDroppedPrefixe($rSDroppedPrefixes);
-    }
-
-    public function removeRSDroppedPrefixe(\Entities\RSDroppedPrefix $rSDroppedPrefixes)
-    {
-        $this->__load();
-        return parent::removeRSDroppedPrefixe($rSDroppedPrefixes);
-    }
-
-    public function getRSDroppedPrefixes()
-    {
-        $this->__load();
-        return parent::getRSDroppedPrefixes();
-    }
-
-    public function addUser(\Entities\User $users)
-    {
-        $this->__load();
-        return parent::addUser($users);
-    }
-
-    public function removeUser(\Entities\User $users)
-    {
-        $this->__load();
-        return parent::removeUser($users);
-    }
-
-    public function getUsers()
-    {
-        $this->__load();
-        return parent::getUsers();
-    }
-
-    public function addTraffic95th(\Entities\Traffic95th $traffic95ths)
-    {
-        $this->__load();
-        return parent::addTraffic95th($traffic95ths);
-    }
-
-    public function removeTraffic95th(\Entities\Traffic95th $traffic95ths)
-    {
-        $this->__load();
-        return parent::removeTraffic95th($traffic95ths);
-    }
-
-    public function getTraffic95ths()
-    {
-        $this->__load();
-        return parent::getTraffic95ths();
-    }
-
-    public function addTraffic95thMonthly(\Entities\Traffic95thMonthly $traffic95thMonthlys)
-    {
-        $this->__load();
-        return parent::addTraffic95thMonthly($traffic95thMonthlys);
-    }
-
-    public function removeTraffic95thMonthly(\Entities\Traffic95thMonthly $traffic95thMonthlys)
-    {
-        $this->__load();
-        return parent::removeTraffic95thMonthly($traffic95thMonthlys);
-    }
-
-    public function getTraffic95thMonthlys()
-    {
-        $this->__load();
-        return parent::getTraffic95thMonthlys();
-    }
-
-    public function addTrafficDailie(\Entities\TrafficDaily $trafficDailies)
-    {
-        $this->__load();
-        return parent::addTrafficDailie($trafficDailies);
-    }
-
-    public function removeTrafficDailie(\Entities\TrafficDaily $trafficDailies)
-    {
-        $this->__load();
-        return parent::removeTrafficDailie($trafficDailies);
-    }
-
-    public function getTrafficDailies()
-    {
-        $this->__load();
-        return parent::getTrafficDailies();
-    }
-
-    public function setNoc24hphone($noc24hphone)
-    {
-        $this->__load();
-        return parent::setNoc24hphone($noc24hphone);
-    }
-
-    public function getNoc24hphone()
-    {
-        $this->__load();
-        return parent::getNoc24hphone();
-    }
-
-    public function addSecEvent(\Entities\SecEvent $secEvents)
-    {
-        $this->__load();
-        return parent::addSecEvent($secEvents);
-    }
-
-    public function removeSecEvent(\Entities\SecEvent $secEvents)
-    {
-        $this->__load();
-        return parent::removeSecEvent($secEvents);
-    }
-
-    public function getSecEvents()
-    {
-        $this->__load();
-        return parent::getSecEvents();
-    }
-
-    public function hasLeft()
-    {
-        $this->__load();
-        return parent::hasLeft();
-    }
-
-    public function getAdminUsers()
-    {
-        $this->__load();
-        return parent::getAdminUsers();
-    }
-
-    public function isTypeFull()
-    {
-        $this->__load();
-        return parent::isTypeFull();
-    }
-
-    public function isTypeAssociate()
-    {
-        $this->__load();
-        return parent::isTypeAssociate();
-    }
-
-    public function isTypeInternal()
-    {
-        $this->__load();
-        return parent::isTypeInternal();
-    }
-
-    public function isTypeProBono()
-    {
-        $this->__load();
-        return parent::isTypeProBono();
-    }
-
-
-    public function __sleep()
-    {
-        return array('__isInitialized__', 'name', 'type', 'shortname', 'autsys', 'maxprefixes', 'peeringemail', 'nocphone', 'noc24hphone', 'nocfax', 'nocemail', 'nochours', 'nocwww', 'irrdb', 'peeringmacro', 'peeringpolicy', 'billingContact', 'billingAddress1', 'billingAddress2', 'billingCity', 'billingCountry', 'corpwww', 'datejoin', 'dateleave', 'status', 'activepeeringmatrix', 'notes', 'lastupdated', 'lastupdatedby', 'creator', 'created', 'id', 'VirtualInterfaces', 'Contacts', 'ConsoleServerConnections', 'CustomerEquipment', 'Peers', 'PeersWith', 'XCusts', 'YCusts', 'RSDroppedPrefixes', 'Users', 'Traffic95ths', 'Traffic95thMonthlys', 'TrafficDailies', 'SecEvents');
-    }
-
-    public function __clone()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-            $class = $this->_entityPersister->getClassMetadata();
-            $original = $this->_entityPersister->load($this->_identifier);
-            if ($original === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            foreach ($class->reflFields as $field => $reflProperty) {
-                $reflProperty->setValue($this, $reflProperty->getValue($original));
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-        
-    }
-}
\ No newline at end of file
diff --git a/application/Proxies/__CG__EntitiesCustomerEquipment.php b/application/Proxies/__CG__EntitiesCustomerEquipment.php
deleted file mode 100644
index b02aa3853..000000000
--- a/application/Proxies/__CG__EntitiesCustomerEquipment.php
+++ /dev/null
@@ -1,136 +0,0 @@
-_entityPersister = $entityPersister;
-        $this->_identifier = $identifier;
-    }
-    /** @private */
-    public function __load()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-
-            if (method_exists($this, "__wakeup")) {
-                // call this after __isInitialized__to avoid infinite recursion
-                // but before loading to emulate what ClassMetadata::newInstance()
-                // provides.
-                $this->__wakeup();
-            }
-
-            if ($this->_entityPersister->load($this->_identifier, $this) === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-    }
-
-    /** @private */
-    public function __isInitialized()
-    {
-        return $this->__isInitialized__;
-    }
-
-    
-    public function setName($name)
-    {
-        $this->__load();
-        return parent::setName($name);
-    }
-
-    public function getName()
-    {
-        $this->__load();
-        return parent::getName();
-    }
-
-    public function setDescription($description)
-    {
-        $this->__load();
-        return parent::setDescription($description);
-    }
-
-    public function getDescription()
-    {
-        $this->__load();
-        return parent::getDescription();
-    }
-
-    public function getId()
-    {
-        if ($this->__isInitialized__ === false) {
-            return (int) $this->_identifier["id"];
-        }
-        $this->__load();
-        return parent::getId();
-    }
-
-    public function setCustomer(\Entities\Customer $customer = NULL)
-    {
-        $this->__load();
-        return parent::setCustomer($customer);
-    }
-
-    public function getCustomer()
-    {
-        $this->__load();
-        return parent::getCustomer();
-    }
-
-    public function setCabinet(\Entities\Cabinet $cabinet = NULL)
-    {
-        $this->__load();
-        return parent::setCabinet($cabinet);
-    }
-
-    public function getCabinet()
-    {
-        $this->__load();
-        return parent::getCabinet();
-    }
-
-    public function setDescr($descr)
-    {
-        $this->__load();
-        return parent::setDescr($descr);
-    }
-
-    public function getDescr()
-    {
-        $this->__load();
-        return parent::getDescr();
-    }
-
-
-    public function __sleep()
-    {
-        return array('__isInitialized__', 'name', 'descr', 'id', 'Customer', 'Cabinet');
-    }
-
-    public function __clone()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-            $class = $this->_entityPersister->getClassMetadata();
-            $original = $this->_entityPersister->load($this->_identifier);
-            if ($original === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            foreach ($class->reflFields as $field => $reflProperty) {
-                $reflProperty->setValue($this, $reflProperty->getValue($original));
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-        
-    }
-}
\ No newline at end of file
diff --git a/application/Proxies/__CG__EntitiesIPv4Address.php b/application/Proxies/__CG__EntitiesIPv4Address.php
deleted file mode 100644
index 89fe0fc98..000000000
--- a/application/Proxies/__CG__EntitiesIPv4Address.php
+++ /dev/null
@@ -1,112 +0,0 @@
-_entityPersister = $entityPersister;
-        $this->_identifier = $identifier;
-    }
-    /** @private */
-    public function __load()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-
-            if (method_exists($this, "__wakeup")) {
-                // call this after __isInitialized__to avoid infinite recursion
-                // but before loading to emulate what ClassMetadata::newInstance()
-                // provides.
-                $this->__wakeup();
-            }
-
-            if ($this->_entityPersister->load($this->_identifier, $this) === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-    }
-
-    /** @private */
-    public function __isInitialized()
-    {
-        return $this->__isInitialized__;
-    }
-
-    
-    public function setAddress($address)
-    {
-        $this->__load();
-        return parent::setAddress($address);
-    }
-
-    public function getAddress()
-    {
-        $this->__load();
-        return parent::getAddress();
-    }
-
-    public function getId()
-    {
-        if ($this->__isInitialized__ === false) {
-            return (int) $this->_identifier["id"];
-        }
-        $this->__load();
-        return parent::getId();
-    }
-
-    public function setVlanInterface(\Entities\VlanInterface $vlanInterface = NULL)
-    {
-        $this->__load();
-        return parent::setVlanInterface($vlanInterface);
-    }
-
-    public function getVlanInterface()
-    {
-        $this->__load();
-        return parent::getVlanInterface();
-    }
-
-    public function setVlan(\Entities\Vlan $vlan = NULL)
-    {
-        $this->__load();
-        return parent::setVlan($vlan);
-    }
-
-    public function getVlan()
-    {
-        $this->__load();
-        return parent::getVlan();
-    }
-
-
-    public function __sleep()
-    {
-        return array('__isInitialized__', 'address', 'id', 'VlanInterface', 'Vlan');
-    }
-
-    public function __clone()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-            $class = $this->_entityPersister->getClassMetadata();
-            $original = $this->_entityPersister->load($this->_identifier);
-            if ($original === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            foreach ($class->reflFields as $field => $reflProperty) {
-                $reflProperty->setValue($this, $reflProperty->getValue($original));
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-        
-    }
-}
\ No newline at end of file
diff --git a/application/Proxies/__CG__EntitiesIPv6Address.php b/application/Proxies/__CG__EntitiesIPv6Address.php
deleted file mode 100644
index 683e79387..000000000
--- a/application/Proxies/__CG__EntitiesIPv6Address.php
+++ /dev/null
@@ -1,112 +0,0 @@
-_entityPersister = $entityPersister;
-        $this->_identifier = $identifier;
-    }
-    /** @private */
-    public function __load()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-
-            if (method_exists($this, "__wakeup")) {
-                // call this after __isInitialized__to avoid infinite recursion
-                // but before loading to emulate what ClassMetadata::newInstance()
-                // provides.
-                $this->__wakeup();
-            }
-
-            if ($this->_entityPersister->load($this->_identifier, $this) === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-    }
-
-    /** @private */
-    public function __isInitialized()
-    {
-        return $this->__isInitialized__;
-    }
-
-    
-    public function setAddress($address)
-    {
-        $this->__load();
-        return parent::setAddress($address);
-    }
-
-    public function getAddress()
-    {
-        $this->__load();
-        return parent::getAddress();
-    }
-
-    public function getId()
-    {
-        if ($this->__isInitialized__ === false) {
-            return (int) $this->_identifier["id"];
-        }
-        $this->__load();
-        return parent::getId();
-    }
-
-    public function setVlanInterface(\Entities\VlanInterface $vlanInterface = NULL)
-    {
-        $this->__load();
-        return parent::setVlanInterface($vlanInterface);
-    }
-
-    public function getVlanInterface()
-    {
-        $this->__load();
-        return parent::getVlanInterface();
-    }
-
-    public function setVlan(\Entities\Vlan $vlan = NULL)
-    {
-        $this->__load();
-        return parent::setVlan($vlan);
-    }
-
-    public function getVlan()
-    {
-        $this->__load();
-        return parent::getVlan();
-    }
-
-
-    public function __sleep()
-    {
-        return array('__isInitialized__', 'address', 'id', 'VlanInterface', 'Vlan');
-    }
-
-    public function __clone()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-            $class = $this->_entityPersister->getClassMetadata();
-            $original = $this->_entityPersister->load($this->_identifier);
-            if ($original === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            foreach ($class->reflFields as $field => $reflProperty) {
-                $reflProperty->setValue($this, $reflProperty->getValue($original));
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-        
-    }
-}
\ No newline at end of file
diff --git a/application/Proxies/__CG__EntitiesIRRDBConfig.php b/application/Proxies/__CG__EntitiesIRRDBConfig.php
deleted file mode 100644
index 3d39b92a0..000000000
--- a/application/Proxies/__CG__EntitiesIRRDBConfig.php
+++ /dev/null
@@ -1,124 +0,0 @@
-_entityPersister = $entityPersister;
-        $this->_identifier = $identifier;
-    }
-    /** @private */
-    public function __load()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-
-            if (method_exists($this, "__wakeup")) {
-                // call this after __isInitialized__to avoid infinite recursion
-                // but before loading to emulate what ClassMetadata::newInstance()
-                // provides.
-                $this->__wakeup();
-            }
-
-            if ($this->_entityPersister->load($this->_identifier, $this) === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-    }
-
-    /** @private */
-    public function __isInitialized()
-    {
-        return $this->__isInitialized__;
-    }
-
-    
-    public function setHost($host)
-    {
-        $this->__load();
-        return parent::setHost($host);
-    }
-
-    public function getHost()
-    {
-        $this->__load();
-        return parent::getHost();
-    }
-
-    public function setProtocol($protocol)
-    {
-        $this->__load();
-        return parent::setProtocol($protocol);
-    }
-
-    public function getProtocol()
-    {
-        $this->__load();
-        return parent::getProtocol();
-    }
-
-    public function setSource($source)
-    {
-        $this->__load();
-        return parent::setSource($source);
-    }
-
-    public function getSource()
-    {
-        $this->__load();
-        return parent::getSource();
-    }
-
-    public function setNotes($notes)
-    {
-        $this->__load();
-        return parent::setNotes($notes);
-    }
-
-    public function getNotes()
-    {
-        $this->__load();
-        return parent::getNotes();
-    }
-
-    public function getId()
-    {
-        if ($this->__isInitialized__ === false) {
-            return (int) $this->_identifier["id"];
-        }
-        $this->__load();
-        return parent::getId();
-    }
-
-
-    public function __sleep()
-    {
-        return array('__isInitialized__', 'host', 'protocol', 'source', 'notes', 'id');
-    }
-
-    public function __clone()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-            $class = $this->_entityPersister->getClassMetadata();
-            $original = $this->_entityPersister->load($this->_identifier);
-            if ($original === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            foreach ($class->reflFields as $field => $reflProperty) {
-                $reflProperty->setValue($this, $reflProperty->getValue($original));
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-        
-    }
-}
\ No newline at end of file
diff --git a/application/Proxies/__CG__EntitiesLocation.php b/application/Proxies/__CG__EntitiesLocation.php
deleted file mode 100644
index 3ec598f65..000000000
--- a/application/Proxies/__CG__EntitiesLocation.php
+++ /dev/null
@@ -1,226 +0,0 @@
-_entityPersister = $entityPersister;
-        $this->_identifier = $identifier;
-    }
-    /** @private */
-    public function __load()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-
-            if (method_exists($this, "__wakeup")) {
-                // call this after __isInitialized__to avoid infinite recursion
-                // but before loading to emulate what ClassMetadata::newInstance()
-                // provides.
-                $this->__wakeup();
-            }
-
-            if ($this->_entityPersister->load($this->_identifier, $this) === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-    }
-
-    /** @private */
-    public function __isInitialized()
-    {
-        return $this->__isInitialized__;
-    }
-
-    
-    public function setName($name)
-    {
-        $this->__load();
-        return parent::setName($name);
-    }
-
-    public function getName()
-    {
-        $this->__load();
-        return parent::getName();
-    }
-
-    public function setShortname($shortname)
-    {
-        $this->__load();
-        return parent::setShortname($shortname);
-    }
-
-    public function getShortname()
-    {
-        $this->__load();
-        return parent::getShortname();
-    }
-
-    public function setTag($tag)
-    {
-        $this->__load();
-        return parent::setTag($tag);
-    }
-
-    public function getTag()
-    {
-        $this->__load();
-        return parent::getTag();
-    }
-
-    public function setAddress($address)
-    {
-        $this->__load();
-        return parent::setAddress($address);
-    }
-
-    public function getAddress()
-    {
-        $this->__load();
-        return parent::getAddress();
-    }
-
-    public function setNocphone($nocphone)
-    {
-        $this->__load();
-        return parent::setNocphone($nocphone);
-    }
-
-    public function getNocphone()
-    {
-        $this->__load();
-        return parent::getNocphone();
-    }
-
-    public function setNocfax($nocfax)
-    {
-        $this->__load();
-        return parent::setNocfax($nocfax);
-    }
-
-    public function getNocfax()
-    {
-        $this->__load();
-        return parent::getNocfax();
-    }
-
-    public function setNocemail($nocemail)
-    {
-        $this->__load();
-        return parent::setNocemail($nocemail);
-    }
-
-    public function getNocemail()
-    {
-        $this->__load();
-        return parent::getNocemail();
-    }
-
-    public function setOfficephone($officephone)
-    {
-        $this->__load();
-        return parent::setOfficephone($officephone);
-    }
-
-    public function getOfficephone()
-    {
-        $this->__load();
-        return parent::getOfficephone();
-    }
-
-    public function setOfficefax($officefax)
-    {
-        $this->__load();
-        return parent::setOfficefax($officefax);
-    }
-
-    public function getOfficefax()
-    {
-        $this->__load();
-        return parent::getOfficefax();
-    }
-
-    public function setOfficeemail($officeemail)
-    {
-        $this->__load();
-        return parent::setOfficeemail($officeemail);
-    }
-
-    public function getOfficeemail()
-    {
-        $this->__load();
-        return parent::getOfficeemail();
-    }
-
-    public function getId()
-    {
-        if ($this->__isInitialized__ === false) {
-            return (int) $this->_identifier["id"];
-        }
-        $this->__load();
-        return parent::getId();
-    }
-
-    public function addCabinet(\Entities\Cabinet $cabinets)
-    {
-        $this->__load();
-        return parent::addCabinet($cabinets);
-    }
-
-    public function removeCabinet(\Entities\Cabinet $cabinets)
-    {
-        $this->__load();
-        return parent::removeCabinet($cabinets);
-    }
-
-    public function getCabinets()
-    {
-        $this->__load();
-        return parent::getCabinets();
-    }
-
-    public function setNotes($notes)
-    {
-        $this->__load();
-        return parent::setNotes($notes);
-    }
-
-    public function getNotes()
-    {
-        $this->__load();
-        return parent::getNotes();
-    }
-
-
-    public function __sleep()
-    {
-        return array('__isInitialized__', 'name', 'shortname', 'tag', 'address', 'nocphone', 'nocfax', 'nocemail', 'officephone', 'officefax', 'officeemail', 'notes', 'id', 'Cabinets');
-    }
-
-    public function __clone()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-            $class = $this->_entityPersister->getClassMetadata();
-            $original = $this->_entityPersister->load($this->_identifier);
-            if ($original === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            foreach ($class->reflFields as $field => $reflProperty) {
-                $reflProperty->setValue($this, $reflProperty->getValue($original));
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-        
-    }
-}
\ No newline at end of file
diff --git a/application/Proxies/__CG__EntitiesMACAddress.php b/application/Proxies/__CG__EntitiesMACAddress.php
deleted file mode 100644
index 86b88879a..000000000
--- a/application/Proxies/__CG__EntitiesMACAddress.php
+++ /dev/null
@@ -1,124 +0,0 @@
-_entityPersister = $entityPersister;
-        $this->_identifier = $identifier;
-    }
-    /** @private */
-    public function __load()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-
-            if (method_exists($this, "__wakeup")) {
-                // call this after __isInitialized__to avoid infinite recursion
-                // but before loading to emulate what ClassMetadata::newInstance()
-                // provides.
-                $this->__wakeup();
-            }
-
-            if ($this->_entityPersister->load($this->_identifier, $this) === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-    }
-
-    /** @private */
-    public function __isInitialized()
-    {
-        return $this->__isInitialized__;
-    }
-
-    
-    public function setFirstseen($firstseen)
-    {
-        $this->__load();
-        return parent::setFirstseen($firstseen);
-    }
-
-    public function getFirstseen()
-    {
-        $this->__load();
-        return parent::getFirstseen();
-    }
-
-    public function setLastseen($lastseen)
-    {
-        $this->__load();
-        return parent::setLastseen($lastseen);
-    }
-
-    public function getLastseen()
-    {
-        $this->__load();
-        return parent::getLastseen();
-    }
-
-    public function setMac($mac)
-    {
-        $this->__load();
-        return parent::setMac($mac);
-    }
-
-    public function getMac()
-    {
-        $this->__load();
-        return parent::getMac();
-    }
-
-    public function getId()
-    {
-        if ($this->__isInitialized__ === false) {
-            return (int) $this->_identifier["id"];
-        }
-        $this->__load();
-        return parent::getId();
-    }
-
-    public function setVirtualInterface(\Entities\VirtualInterface $virtualInterface = NULL)
-    {
-        $this->__load();
-        return parent::setVirtualInterface($virtualInterface);
-    }
-
-    public function getVirtualInterface()
-    {
-        $this->__load();
-        return parent::getVirtualInterface();
-    }
-
-
-    public function __sleep()
-    {
-        return array('__isInitialized__', 'firstseen', 'lastseen', 'mac', 'id', 'VirtualInterface');
-    }
-
-    public function __clone()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-            $class = $this->_entityPersister->getClassMetadata();
-            $original = $this->_entityPersister->load($this->_identifier);
-            if ($original === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            foreach ($class->reflFields as $field => $reflProperty) {
-                $reflProperty->setValue($this, $reflProperty->getValue($original));
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-        
-    }
-}
\ No newline at end of file
diff --git a/application/Proxies/__CG__EntitiesMeeting.php b/application/Proxies/__CG__EntitiesMeeting.php
deleted file mode 100644
index 7ee8fe54c..000000000
--- a/application/Proxies/__CG__EntitiesMeeting.php
+++ /dev/null
@@ -1,226 +0,0 @@
-_entityPersister = $entityPersister;
-        $this->_identifier = $identifier;
-    }
-    /** @private */
-    public function __load()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-
-            if (method_exists($this, "__wakeup")) {
-                // call this after __isInitialized__to avoid infinite recursion
-                // but before loading to emulate what ClassMetadata::newInstance()
-                // provides.
-                $this->__wakeup();
-            }
-
-            if ($this->_entityPersister->load($this->_identifier, $this) === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-    }
-
-    /** @private */
-    public function __isInitialized()
-    {
-        return $this->__isInitialized__;
-    }
-
-    
-    public function setTitle($title)
-    {
-        $this->__load();
-        return parent::setTitle($title);
-    }
-
-    public function getTitle()
-    {
-        $this->__load();
-        return parent::getTitle();
-    }
-
-    public function setBeforeText($beforeText)
-    {
-        $this->__load();
-        return parent::setBeforeText($beforeText);
-    }
-
-    public function getBeforeText()
-    {
-        $this->__load();
-        return parent::getBeforeText();
-    }
-
-    public function setAfterText($afterText)
-    {
-        $this->__load();
-        return parent::setAfterText($afterText);
-    }
-
-    public function getAfterText()
-    {
-        $this->__load();
-        return parent::getAfterText();
-    }
-
-    public function setDate($date)
-    {
-        $this->__load();
-        return parent::setDate($date);
-    }
-
-    public function getDate()
-    {
-        $this->__load();
-        return parent::getDate();
-    }
-
-    public function setTime($time)
-    {
-        $this->__load();
-        return parent::setTime($time);
-    }
-
-    public function getTime()
-    {
-        $this->__load();
-        return parent::getTime();
-    }
-
-    public function setVenue($venue)
-    {
-        $this->__load();
-        return parent::setVenue($venue);
-    }
-
-    public function getVenue()
-    {
-        $this->__load();
-        return parent::getVenue();
-    }
-
-    public function setVenueUrl($venueUrl)
-    {
-        $this->__load();
-        return parent::setVenueUrl($venueUrl);
-    }
-
-    public function getVenueUrl()
-    {
-        $this->__load();
-        return parent::getVenueUrl();
-    }
-
-    public function setCreatedAt($createdAt)
-    {
-        $this->__load();
-        return parent::setCreatedAt($createdAt);
-    }
-
-    public function getCreatedAt()
-    {
-        $this->__load();
-        return parent::getCreatedAt();
-    }
-
-    public function setUpdatedBy($updatedBy)
-    {
-        $this->__load();
-        return parent::setUpdatedBy($updatedBy);
-    }
-
-    public function getUpdatedBy()
-    {
-        $this->__load();
-        return parent::getUpdatedBy();
-    }
-
-    public function setUpdatedAt($updatedAt)
-    {
-        $this->__load();
-        return parent::setUpdatedAt($updatedAt);
-    }
-
-    public function getUpdatedAt()
-    {
-        $this->__load();
-        return parent::getUpdatedAt();
-    }
-
-    public function getId()
-    {
-        if ($this->__isInitialized__ === false) {
-            return (int) $this->_identifier["id"];
-        }
-        $this->__load();
-        return parent::getId();
-    }
-
-    public function addMeetingItem(\Entities\MeetingItem $meetingItems)
-    {
-        $this->__load();
-        return parent::addMeetingItem($meetingItems);
-    }
-
-    public function removeMeetingItem(\Entities\MeetingItem $meetingItems)
-    {
-        $this->__load();
-        return parent::removeMeetingItem($meetingItems);
-    }
-
-    public function getMeetingItems()
-    {
-        $this->__load();
-        return parent::getMeetingItems();
-    }
-
-    public function setCreatedBy(\Entities\User $createdBy = NULL)
-    {
-        $this->__load();
-        return parent::setCreatedBy($createdBy);
-    }
-
-    public function getCreatedBy()
-    {
-        $this->__load();
-        return parent::getCreatedBy();
-    }
-
-
-    public function __sleep()
-    {
-        return array('__isInitialized__', 'title', 'before_text', 'after_text', 'date', 'time', 'venue', 'venue_url', 'created_at', 'updated_by', 'updated_at', 'id', 'MeetingItems', 'CreatedBy');
-    }
-
-    public function __clone()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-            $class = $this->_entityPersister->getClassMetadata();
-            $original = $this->_entityPersister->load($this->_identifier);
-            if ($original === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            foreach ($class->reflFields as $field => $reflProperty) {
-                $reflProperty->setValue($this, $reflProperty->getValue($original));
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-        
-    }
-}
\ No newline at end of file
diff --git a/application/Proxies/__CG__EntitiesMeetingItem.php b/application/Proxies/__CG__EntitiesMeetingItem.php
deleted file mode 100644
index 18b138e11..000000000
--- a/application/Proxies/__CG__EntitiesMeetingItem.php
+++ /dev/null
@@ -1,280 +0,0 @@
-_entityPersister = $entityPersister;
-        $this->_identifier = $identifier;
-    }
-    /** @private */
-    public function __load()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-
-            if (method_exists($this, "__wakeup")) {
-                // call this after __isInitialized__to avoid infinite recursion
-                // but before loading to emulate what ClassMetadata::newInstance()
-                // provides.
-                $this->__wakeup();
-            }
-
-            if ($this->_entityPersister->load($this->_identifier, $this) === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-    }
-
-    /** @private */
-    public function __isInitialized()
-    {
-        return $this->__isInitialized__;
-    }
-
-    
-    public function setTitle($title)
-    {
-        $this->__load();
-        return parent::setTitle($title);
-    }
-
-    public function getTitle()
-    {
-        $this->__load();
-        return parent::getTitle();
-    }
-
-    public function setName($name)
-    {
-        $this->__load();
-        return parent::setName($name);
-    }
-
-    public function getName()
-    {
-        $this->__load();
-        return parent::getName();
-    }
-
-    public function setRole($role)
-    {
-        $this->__load();
-        return parent::setRole($role);
-    }
-
-    public function getRole()
-    {
-        $this->__load();
-        return parent::getRole();
-    }
-
-    public function setEmail($email)
-    {
-        $this->__load();
-        return parent::setEmail($email);
-    }
-
-    public function getEmail()
-    {
-        $this->__load();
-        return parent::getEmail();
-    }
-
-    public function setCompany($company)
-    {
-        $this->__load();
-        return parent::setCompany($company);
-    }
-
-    public function getCompany()
-    {
-        $this->__load();
-        return parent::getCompany();
-    }
-
-    public function setCompanyUrl($companyUrl)
-    {
-        $this->__load();
-        return parent::setCompanyUrl($companyUrl);
-    }
-
-    public function getCompanyUrl()
-    {
-        $this->__load();
-        return parent::getCompanyUrl();
-    }
-
-    public function setSummary($summary)
-    {
-        $this->__load();
-        return parent::setSummary($summary);
-    }
-
-    public function getSummary()
-    {
-        $this->__load();
-        return parent::getSummary();
-    }
-
-    public function setPresentation($presentation)
-    {
-        $this->__load();
-        return parent::setPresentation($presentation);
-    }
-
-    public function getPresentation()
-    {
-        $this->__load();
-        return parent::getPresentation();
-    }
-
-    public function setFilename($filename)
-    {
-        $this->__load();
-        return parent::setFilename($filename);
-    }
-
-    public function getFilename()
-    {
-        $this->__load();
-        return parent::getFilename();
-    }
-
-    public function setVideoUrl($videoUrl)
-    {
-        $this->__load();
-        return parent::setVideoUrl($videoUrl);
-    }
-
-    public function getVideoUrl()
-    {
-        $this->__load();
-        return parent::getVideoUrl();
-    }
-
-    public function setOtherContent($otherContent)
-    {
-        $this->__load();
-        return parent::setOtherContent($otherContent);
-    }
-
-    public function getOtherContent()
-    {
-        $this->__load();
-        return parent::getOtherContent();
-    }
-
-    public function setCreatedBy($createdBy)
-    {
-        $this->__load();
-        return parent::setCreatedBy($createdBy);
-    }
-
-    public function getCreatedBy()
-    {
-        $this->__load();
-        return parent::getCreatedBy();
-    }
-
-    public function setCreatedAt($createdAt)
-    {
-        $this->__load();
-        return parent::setCreatedAt($createdAt);
-    }
-
-    public function getCreatedAt()
-    {
-        $this->__load();
-        return parent::getCreatedAt();
-    }
-
-    public function setUpdatedBy($updatedBy)
-    {
-        $this->__load();
-        return parent::setUpdatedBy($updatedBy);
-    }
-
-    public function getUpdatedBy()
-    {
-        $this->__load();
-        return parent::getUpdatedBy();
-    }
-
-    public function setUpdatedAr($updatedAr)
-    {
-        $this->__load();
-        return parent::setUpdatedAr($updatedAr);
-    }
-
-    public function getUpdatedAr()
-    {
-        $this->__load();
-        return parent::getUpdatedAr();
-    }
-
-    public function getId()
-    {
-        if ($this->__isInitialized__ === false) {
-            return (int) $this->_identifier["id"];
-        }
-        $this->__load();
-        return parent::getId();
-    }
-
-    public function setMeeting(\Entities\Meeting $meeting = NULL)
-    {
-        $this->__load();
-        return parent::setMeeting($meeting);
-    }
-
-    public function getMeeting()
-    {
-        $this->__load();
-        return parent::getMeeting();
-    }
-
-    public function setUpdatedAt($updatedAt)
-    {
-        $this->__load();
-        return parent::setUpdatedAt($updatedAt);
-    }
-
-    public function getUpdatedAt()
-    {
-        $this->__load();
-        return parent::getUpdatedAt();
-    }
-
-
-    public function __sleep()
-    {
-        return array('__isInitialized__', 'title', 'name', 'role', 'email', 'company', 'company_url', 'summary', 'presentation', 'filename', 'video_url', 'other_content', 'created_by', 'created_at', 'updated_by', 'updated_at', 'id', 'Meeting');
-    }
-
-    public function __clone()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-            $class = $this->_entityPersister->getClassMetadata();
-            $original = $this->_entityPersister->load($this->_identifier);
-            if ($original === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            foreach ($class->reflFields as $field => $reflProperty) {
-                $reflProperty->setValue($this, $reflProperty->getValue($original));
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-        
-    }
-}
\ No newline at end of file
diff --git a/application/Proxies/__CG__EntitiesNetworkInfo.php b/application/Proxies/__CG__EntitiesNetworkInfo.php
deleted file mode 100644
index d8058b76b..000000000
--- a/application/Proxies/__CG__EntitiesNetworkInfo.php
+++ /dev/null
@@ -1,160 +0,0 @@
-_entityPersister = $entityPersister;
-        $this->_identifier = $identifier;
-    }
-    /** @private */
-    public function __load()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-
-            if (method_exists($this, "__wakeup")) {
-                // call this after __isInitialized__to avoid infinite recursion
-                // but before loading to emulate what ClassMetadata::newInstance()
-                // provides.
-                $this->__wakeup();
-            }
-
-            if ($this->_entityPersister->load($this->_identifier, $this) === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-    }
-
-    /** @private */
-    public function __isInitialized()
-    {
-        return $this->__isInitialized__;
-    }
-
-    
-    public function setProtocol($protocol)
-    {
-        $this->__load();
-        return parent::setProtocol($protocol);
-    }
-
-    public function getProtocol()
-    {
-        $this->__load();
-        return parent::getProtocol();
-    }
-
-    public function setNetwork($network)
-    {
-        $this->__load();
-        return parent::setNetwork($network);
-    }
-
-    public function getNetwork()
-    {
-        $this->__load();
-        return parent::getNetwork();
-    }
-
-    public function setMasklen($masklen)
-    {
-        $this->__load();
-        return parent::setMasklen($masklen);
-    }
-
-    public function getMasklen()
-    {
-        $this->__load();
-        return parent::getMasklen();
-    }
-
-    public function setRs1address($rs1address)
-    {
-        $this->__load();
-        return parent::setRs1address($rs1address);
-    }
-
-    public function getRs1address()
-    {
-        $this->__load();
-        return parent::getRs1address();
-    }
-
-    public function setRs2address($rs2address)
-    {
-        $this->__load();
-        return parent::setRs2address($rs2address);
-    }
-
-    public function getRs2address()
-    {
-        $this->__load();
-        return parent::getRs2address();
-    }
-
-    public function setDnsfile($dnsfile)
-    {
-        $this->__load();
-        return parent::setDnsfile($dnsfile);
-    }
-
-    public function getDnsfile()
-    {
-        $this->__load();
-        return parent::getDnsfile();
-    }
-
-    public function getId()
-    {
-        if ($this->__isInitialized__ === false) {
-            return (int) $this->_identifier["id"];
-        }
-        $this->__load();
-        return parent::getId();
-    }
-
-    public function setVlan(\Entities\Vlan $vlan = NULL)
-    {
-        $this->__load();
-        return parent::setVlan($vlan);
-    }
-
-    public function getVlan()
-    {
-        $this->__load();
-        return parent::getVlan();
-    }
-
-
-    public function __sleep()
-    {
-        return array('__isInitialized__', 'protocol', 'network', 'masklen', 'rs1address', 'rs2address', 'dnsfile', 'id', 'Vlan');
-    }
-
-    public function __clone()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-            $class = $this->_entityPersister->getClassMetadata();
-            $original = $this->_entityPersister->load($this->_identifier);
-            if ($original === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            foreach ($class->reflFields as $field => $reflProperty) {
-                $reflProperty->setValue($this, $reflProperty->getValue($original));
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-        
-    }
-}
\ No newline at end of file
diff --git a/application/Proxies/__CG__EntitiesPeeringManager.php b/application/Proxies/__CG__EntitiesPeeringManager.php
deleted file mode 100644
index c48cc5066..000000000
--- a/application/Proxies/__CG__EntitiesPeeringManager.php
+++ /dev/null
@@ -1,184 +0,0 @@
-_entityPersister = $entityPersister;
-        $this->_identifier = $identifier;
-    }
-    /** @private */
-    public function __load()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-
-            if (method_exists($this, "__wakeup")) {
-                // call this after __isInitialized__to avoid infinite recursion
-                // but before loading to emulate what ClassMetadata::newInstance()
-                // provides.
-                $this->__wakeup();
-            }
-
-            if ($this->_entityPersister->load($this->_identifier, $this) === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-    }
-
-    /** @private */
-    public function __isInitialized()
-    {
-        return $this->__isInitialized__;
-    }
-
-    
-    public function setEmailLastSent($emailLastSent)
-    {
-        $this->__load();
-        return parent::setEmailLastSent($emailLastSent);
-    }
-
-    public function getEmailLastSent()
-    {
-        $this->__load();
-        return parent::getEmailLastSent();
-    }
-
-    public function setEmailsSent($emailsSent)
-    {
-        $this->__load();
-        return parent::setEmailsSent($emailsSent);
-    }
-
-    public function getEmailsSent()
-    {
-        $this->__load();
-        return parent::getEmailsSent();
-    }
-
-    public function setPeered($peered)
-    {
-        $this->__load();
-        return parent::setPeered($peered);
-    }
-
-    public function getPeered()
-    {
-        $this->__load();
-        return parent::getPeered();
-    }
-
-    public function setRejected($rejected)
-    {
-        $this->__load();
-        return parent::setRejected($rejected);
-    }
-
-    public function getRejected()
-    {
-        $this->__load();
-        return parent::getRejected();
-    }
-
-    public function setNotes($notes)
-    {
-        $this->__load();
-        return parent::setNotes($notes);
-    }
-
-    public function getNotes()
-    {
-        $this->__load();
-        return parent::getNotes();
-    }
-
-    public function getId()
-    {
-        if ($this->__isInitialized__ === false) {
-            return (int) $this->_identifier["id"];
-        }
-        $this->__load();
-        return parent::getId();
-    }
-
-    public function setCustomer(\Entities\Customer $customer = NULL)
-    {
-        $this->__load();
-        return parent::setCustomer($customer);
-    }
-
-    public function getCustomer()
-    {
-        $this->__load();
-        return parent::getCustomer();
-    }
-
-    public function setPeer(\Entities\Customer $peer = NULL)
-    {
-        $this->__load();
-        return parent::setPeer($peer);
-    }
-
-    public function getPeer()
-    {
-        $this->__load();
-        return parent::getPeer();
-    }
-
-    public function setCreated($created)
-    {
-        $this->__load();
-        return parent::setCreated($created);
-    }
-
-    public function getCreated()
-    {
-        $this->__load();
-        return parent::getCreated();
-    }
-
-    public function setUpdated($updated)
-    {
-        $this->__load();
-        return parent::setUpdated($updated);
-    }
-
-    public function getUpdated()
-    {
-        $this->__load();
-        return parent::getUpdated();
-    }
-
-
-    public function __sleep()
-    {
-        return array('__isInitialized__', 'email_last_sent', 'emails_sent', 'peered', 'rejected', 'notes', 'created', 'updated', 'id', 'Customer', 'Peer');
-    }
-
-    public function __clone()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-            $class = $this->_entityPersister->getClassMetadata();
-            $original = $this->_entityPersister->load($this->_identifier);
-            if ($original === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            foreach ($class->reflFields as $field => $reflProperty) {
-                $reflProperty->setValue($this, $reflProperty->getValue($original));
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-        
-    }
-}
\ No newline at end of file
diff --git a/application/Proxies/__CG__EntitiesPeeringMatrix.php b/application/Proxies/__CG__EntitiesPeeringMatrix.php
deleted file mode 100644
index b840346c8..000000000
--- a/application/Proxies/__CG__EntitiesPeeringMatrix.php
+++ /dev/null
@@ -1,160 +0,0 @@
-_entityPersister = $entityPersister;
-        $this->_identifier = $identifier;
-    }
-    /** @private */
-    public function __load()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-
-            if (method_exists($this, "__wakeup")) {
-                // call this after __isInitialized__to avoid infinite recursion
-                // but before loading to emulate what ClassMetadata::newInstance()
-                // provides.
-                $this->__wakeup();
-            }
-
-            if ($this->_entityPersister->load($this->_identifier, $this) === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-    }
-
-    /** @private */
-    public function __isInitialized()
-    {
-        return $this->__isInitialized__;
-    }
-
-    
-    public function setXAs($xAs)
-    {
-        $this->__load();
-        return parent::setXAs($xAs);
-    }
-
-    public function getXAs()
-    {
-        $this->__load();
-        return parent::getXAs();
-    }
-
-    public function setYAs($yAs)
-    {
-        $this->__load();
-        return parent::setYAs($yAs);
-    }
-
-    public function getYAs()
-    {
-        $this->__load();
-        return parent::getYAs();
-    }
-
-    public function setPeeringStatus($peeringStatus)
-    {
-        $this->__load();
-        return parent::setPeeringStatus($peeringStatus);
-    }
-
-    public function getPeeringStatus()
-    {
-        $this->__load();
-        return parent::getPeeringStatus();
-    }
-
-    public function setUpdated($updated)
-    {
-        $this->__load();
-        return parent::setUpdated($updated);
-    }
-
-    public function getUpdated()
-    {
-        $this->__load();
-        return parent::getUpdated();
-    }
-
-    public function getId()
-    {
-        if ($this->__isInitialized__ === false) {
-            return (int) $this->_identifier["id"];
-        }
-        $this->__load();
-        return parent::getId();
-    }
-
-    public function setXCustomer(\Entities\Customer $xCustomer = NULL)
-    {
-        $this->__load();
-        return parent::setXCustomer($xCustomer);
-    }
-
-    public function getXCustomer()
-    {
-        $this->__load();
-        return parent::getXCustomer();
-    }
-
-    public function setYCustomer(\Entities\Customer $yCustomer = NULL)
-    {
-        $this->__load();
-        return parent::setYCustomer($yCustomer);
-    }
-
-    public function getYCustomer()
-    {
-        $this->__load();
-        return parent::getYCustomer();
-    }
-
-    public function setVlan($vlan)
-    {
-        $this->__load();
-        return parent::setVlan($vlan);
-    }
-
-    public function getVlan()
-    {
-        $this->__load();
-        return parent::getVlan();
-    }
-
-
-    public function __sleep()
-    {
-        return array('__isInitialized__', 'vlan', 'x_as', 'y_as', 'peering_status', 'updated', 'id', 'XCustomer', 'YCustomer');
-    }
-
-    public function __clone()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-            $class = $this->_entityPersister->getClassMetadata();
-            $original = $this->_entityPersister->load($this->_identifier);
-            if ($original === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            foreach ($class->reflFields as $field => $reflProperty) {
-                $reflProperty->setValue($this, $reflProperty->getValue($original));
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-        
-    }
-}
\ No newline at end of file
diff --git a/application/Proxies/__CG__EntitiesPhysicalInterface.php b/application/Proxies/__CG__EntitiesPhysicalInterface.php
deleted file mode 100644
index c01d8015a..000000000
--- a/application/Proxies/__CG__EntitiesPhysicalInterface.php
+++ /dev/null
@@ -1,160 +0,0 @@
-_entityPersister = $entityPersister;
-        $this->_identifier = $identifier;
-    }
-    /** @private */
-    public function __load()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-
-            if (method_exists($this, "__wakeup")) {
-                // call this after __isInitialized__to avoid infinite recursion
-                // but before loading to emulate what ClassMetadata::newInstance()
-                // provides.
-                $this->__wakeup();
-            }
-
-            if ($this->_entityPersister->load($this->_identifier, $this) === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-    }
-
-    /** @private */
-    public function __isInitialized()
-    {
-        return $this->__isInitialized__;
-    }
-
-    
-    public function setStatus($status)
-    {
-        $this->__load();
-        return parent::setStatus($status);
-    }
-
-    public function getStatus()
-    {
-        $this->__load();
-        return parent::getStatus();
-    }
-
-    public function setSpeed($speed)
-    {
-        $this->__load();
-        return parent::setSpeed($speed);
-    }
-
-    public function getSpeed()
-    {
-        $this->__load();
-        return parent::getSpeed();
-    }
-
-    public function setDuplex($duplex)
-    {
-        $this->__load();
-        return parent::setDuplex($duplex);
-    }
-
-    public function getDuplex()
-    {
-        $this->__load();
-        return parent::getDuplex();
-    }
-
-    public function setMonitorindex($monitorindex)
-    {
-        $this->__load();
-        return parent::setMonitorindex($monitorindex);
-    }
-
-    public function getMonitorindex()
-    {
-        $this->__load();
-        return parent::getMonitorindex();
-    }
-
-    public function setNotes($notes)
-    {
-        $this->__load();
-        return parent::setNotes($notes);
-    }
-
-    public function getNotes()
-    {
-        $this->__load();
-        return parent::getNotes();
-    }
-
-    public function getId()
-    {
-        if ($this->__isInitialized__ === false) {
-            return (int) $this->_identifier["id"];
-        }
-        $this->__load();
-        return parent::getId();
-    }
-
-    public function setSwitchPort(\Entities\SwitchPort $switchPort = NULL)
-    {
-        $this->__load();
-        return parent::setSwitchPort($switchPort);
-    }
-
-    public function getSwitchPort()
-    {
-        $this->__load();
-        return parent::getSwitchPort();
-    }
-
-    public function setVirtualInterface(\Entities\VirtualInterface $virtualInterface = NULL)
-    {
-        $this->__load();
-        return parent::setVirtualInterface($virtualInterface);
-    }
-
-    public function getVirtualInterface()
-    {
-        $this->__load();
-        return parent::getVirtualInterface();
-    }
-
-
-    public function __sleep()
-    {
-        return array('__isInitialized__', 'status', 'speed', 'duplex', 'monitorindex', 'notes', 'id', 'SwitchPort', 'VirtualInterface');
-    }
-
-    public function __clone()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-            $class = $this->_entityPersister->getClassMetadata();
-            $original = $this->_entityPersister->load($this->_identifier);
-            if ($original === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            foreach ($class->reflFields as $field => $reflProperty) {
-                $reflProperty->setValue($this, $reflProperty->getValue($original));
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-        
-    }
-}
\ No newline at end of file
diff --git a/application/Proxies/__CG__EntitiesRSDroppedPrefix.php b/application/Proxies/__CG__EntitiesRSDroppedPrefix.php
deleted file mode 100644
index 1988c0ae7..000000000
--- a/application/Proxies/__CG__EntitiesRSDroppedPrefix.php
+++ /dev/null
@@ -1,148 +0,0 @@
-_entityPersister = $entityPersister;
-        $this->_identifier = $identifier;
-    }
-    /** @private */
-    public function __load()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-
-            if (method_exists($this, "__wakeup")) {
-                // call this after __isInitialized__to avoid infinite recursion
-                // but before loading to emulate what ClassMetadata::newInstance()
-                // provides.
-                $this->__wakeup();
-            }
-
-            if ($this->_entityPersister->load($this->_identifier, $this) === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-    }
-
-    /** @private */
-    public function __isInitialized()
-    {
-        return $this->__isInitialized__;
-    }
-
-    
-    public function setTimestamp($timestamp)
-    {
-        $this->__load();
-        return parent::setTimestamp($timestamp);
-    }
-
-    public function getTimestamp()
-    {
-        $this->__load();
-        return parent::getTimestamp();
-    }
-
-    public function setPrefix($prefix)
-    {
-        $this->__load();
-        return parent::setPrefix($prefix);
-    }
-
-    public function getPrefix()
-    {
-        $this->__load();
-        return parent::getPrefix();
-    }
-
-    public function setProtocol($protocol)
-    {
-        $this->__load();
-        return parent::setProtocol($protocol);
-    }
-
-    public function getProtocol()
-    {
-        $this->__load();
-        return parent::getProtocol();
-    }
-
-    public function setIrrdb($irrdb)
-    {
-        $this->__load();
-        return parent::setIrrdb($irrdb);
-    }
-
-    public function getIrrdb()
-    {
-        $this->__load();
-        return parent::getIrrdb();
-    }
-
-    public function setRsOrigin($rsOrigin)
-    {
-        $this->__load();
-        return parent::setRsOrigin($rsOrigin);
-    }
-
-    public function getRsOrigin()
-    {
-        $this->__load();
-        return parent::getRsOrigin();
-    }
-
-    public function getId()
-    {
-        if ($this->__isInitialized__ === false) {
-            return (int) $this->_identifier["id"];
-        }
-        $this->__load();
-        return parent::getId();
-    }
-
-    public function setCustomer(\Entities\Customer $customer = NULL)
-    {
-        $this->__load();
-        return parent::setCustomer($customer);
-    }
-
-    public function getCustomer()
-    {
-        $this->__load();
-        return parent::getCustomer();
-    }
-
-
-    public function __sleep()
-    {
-        return array('__isInitialized__', 'timestamp', 'prefix', 'protocol', 'irrdb', 'rs_origin', 'id', 'Customer');
-    }
-
-    public function __clone()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-            $class = $this->_entityPersister->getClassMetadata();
-            $original = $this->_entityPersister->load($this->_identifier);
-            if ($original === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            foreach ($class->reflFields as $field => $reflProperty) {
-                $reflProperty->setValue($this, $reflProperty->getValue($original));
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-        
-    }
-}
\ No newline at end of file
diff --git a/application/Proxies/__CG__EntitiesSecEvent.php b/application/Proxies/__CG__EntitiesSecEvent.php
deleted file mode 100644
index 0cff7f0a8..000000000
--- a/application/Proxies/__CG__EntitiesSecEvent.php
+++ /dev/null
@@ -1,160 +0,0 @@
-_entityPersister = $entityPersister;
-        $this->_identifier = $identifier;
-    }
-    /** @private */
-    public function __load()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-
-            if (method_exists($this, "__wakeup")) {
-                // call this after __isInitialized__to avoid infinite recursion
-                // but before loading to emulate what ClassMetadata::newInstance()
-                // provides.
-                $this->__wakeup();
-            }
-
-            if ($this->_entityPersister->load($this->_identifier, $this) === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-    }
-
-    /** @private */
-    public function __isInitialized()
-    {
-        return $this->__isInitialized__;
-    }
-
-    
-    public function setType($type)
-    {
-        $this->__load();
-        return parent::setType($type);
-    }
-
-    public function getType()
-    {
-        $this->__load();
-        return parent::getType();
-    }
-
-    public function setMessage($message)
-    {
-        $this->__load();
-        return parent::setMessage($message);
-    }
-
-    public function getMessage()
-    {
-        $this->__load();
-        return parent::getMessage();
-    }
-
-    public function setRecordedDate($recordedDate)
-    {
-        $this->__load();
-        return parent::setRecordedDate($recordedDate);
-    }
-
-    public function getRecordedDate()
-    {
-        $this->__load();
-        return parent::getRecordedDate();
-    }
-
-    public function setTimestamp($timestamp)
-    {
-        $this->__load();
-        return parent::setTimestamp($timestamp);
-    }
-
-    public function getTimestamp()
-    {
-        $this->__load();
-        return parent::getTimestamp();
-    }
-
-    public function getId()
-    {
-        if ($this->__isInitialized__ === false) {
-            return $this->_identifier["id"];
-        }
-        $this->__load();
-        return parent::getId();
-    }
-
-    public function setCustomer(\Entities\Customer $customer = NULL)
-    {
-        $this->__load();
-        return parent::setCustomer($customer);
-    }
-
-    public function getCustomer()
-    {
-        $this->__load();
-        return parent::getCustomer();
-    }
-
-    public function setSwitch(\Entities\Switcher $switch = NULL)
-    {
-        $this->__load();
-        return parent::setSwitch($switch);
-    }
-
-    public function getSwitch()
-    {
-        $this->__load();
-        return parent::getSwitch();
-    }
-
-    public function setSwitchPort(\Entities\SwitchPort $switchPort = NULL)
-    {
-        $this->__load();
-        return parent::setSwitchPort($switchPort);
-    }
-
-    public function getSwitchPort()
-    {
-        $this->__load();
-        return parent::getSwitchPort();
-    }
-
-
-    public function __sleep()
-    {
-        return array('__isInitialized__', 'type', 'message', 'recorded_date', 'timestamp', 'id', 'Customer', 'Switch', 'SwitchPort');
-    }
-
-    public function __clone()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-            $class = $this->_entityPersister->getClassMetadata();
-            $original = $this->_entityPersister->load($this->_identifier);
-            if ($original === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            foreach ($class->reflFields as $field => $reflProperty) {
-                $reflProperty->setValue($this, $reflProperty->getValue($original));
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-        
-    }
-}
\ No newline at end of file
diff --git a/application/Proxies/__CG__EntitiesSwitchPort.php b/application/Proxies/__CG__EntitiesSwitchPort.php
deleted file mode 100644
index e482c3fce..000000000
--- a/application/Proxies/__CG__EntitiesSwitchPort.php
+++ /dev/null
@@ -1,142 +0,0 @@
-_entityPersister = $entityPersister;
-        $this->_identifier = $identifier;
-    }
-    /** @private */
-    public function __load()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-
-            if (method_exists($this, "__wakeup")) {
-                // call this after __isInitialized__to avoid infinite recursion
-                // but before loading to emulate what ClassMetadata::newInstance()
-                // provides.
-                $this->__wakeup();
-            }
-
-            if ($this->_entityPersister->load($this->_identifier, $this) === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-    }
-
-    /** @private */
-    public function __isInitialized()
-    {
-        return $this->__isInitialized__;
-    }
-
-    
-    public function setType($type)
-    {
-        $this->__load();
-        return parent::setType($type);
-    }
-
-    public function getType()
-    {
-        $this->__load();
-        return parent::getType();
-    }
-
-    public function setName($name)
-    {
-        $this->__load();
-        return parent::setName($name);
-    }
-
-    public function getName()
-    {
-        $this->__load();
-        return parent::getName();
-    }
-
-    public function getId()
-    {
-        if ($this->__isInitialized__ === false) {
-            return (int) $this->_identifier["id"];
-        }
-        $this->__load();
-        return parent::getId();
-    }
-
-    public function setPhysicalInterface(\Entities\PhysicalInterface $physicalInterface = NULL)
-    {
-        $this->__load();
-        return parent::setPhysicalInterface($physicalInterface);
-    }
-
-    public function getPhysicalInterface()
-    {
-        $this->__load();
-        return parent::getPhysicalInterface();
-    }
-
-    public function setSwitcher(\Entities\Switcher $switcher = NULL)
-    {
-        $this->__load();
-        return parent::setSwitcher($switcher);
-    }
-
-    public function getSwitcher()
-    {
-        $this->__load();
-        return parent::getSwitcher();
-    }
-
-    public function addSecEvent(\Entities\SecEvent $secEvents)
-    {
-        $this->__load();
-        return parent::addSecEvent($secEvents);
-    }
-
-    public function removeSecEvent(\Entities\SecEvent $secEvents)
-    {
-        $this->__load();
-        return parent::removeSecEvent($secEvents);
-    }
-
-    public function getSecEvents()
-    {
-        $this->__load();
-        return parent::getSecEvents();
-    }
-
-
-    public function __sleep()
-    {
-        return array('__isInitialized__', 'type', 'name', 'id', 'PhysicalInterface', 'SecEvents', 'Switcher');
-    }
-
-    public function __clone()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-            $class = $this->_entityPersister->getClassMetadata();
-            $original = $this->_entityPersister->load($this->_identifier);
-            if ($original === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            foreach ($class->reflFields as $field => $reflProperty) {
-                $reflProperty->setValue($this, $reflProperty->getValue($original));
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-        
-    }
-}
\ No newline at end of file
diff --git a/application/Proxies/__CG__EntitiesSwitcher.php b/application/Proxies/__CG__EntitiesSwitcher.php
deleted file mode 100644
index 59d2dc047..000000000
--- a/application/Proxies/__CG__EntitiesSwitcher.php
+++ /dev/null
@@ -1,274 +0,0 @@
-_entityPersister = $entityPersister;
-        $this->_identifier = $identifier;
-    }
-    /** @private */
-    public function __load()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-
-            if (method_exists($this, "__wakeup")) {
-                // call this after __isInitialized__to avoid infinite recursion
-                // but before loading to emulate what ClassMetadata::newInstance()
-                // provides.
-                $this->__wakeup();
-            }
-
-            if ($this->_entityPersister->load($this->_identifier, $this) === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-    }
-
-    /** @private */
-    public function __isInitialized()
-    {
-        return $this->__isInitialized__;
-    }
-
-    
-    public function setName($name)
-    {
-        $this->__load();
-        return parent::setName($name);
-    }
-
-    public function getName()
-    {
-        $this->__load();
-        return parent::getName();
-    }
-
-    public function setIpv4addr($ipv4addr)
-    {
-        $this->__load();
-        return parent::setIpv4addr($ipv4addr);
-    }
-
-    public function getIpv4addr()
-    {
-        $this->__load();
-        return parent::getIpv4addr();
-    }
-
-    public function setIpv6addr($ipv6addr)
-    {
-        $this->__load();
-        return parent::setIpv6addr($ipv6addr);
-    }
-
-    public function getIpv6addr()
-    {
-        $this->__load();
-        return parent::getIpv6addr();
-    }
-
-    public function setSnmppasswd($snmppasswd)
-    {
-        $this->__load();
-        return parent::setSnmppasswd($snmppasswd);
-    }
-
-    public function getSnmppasswd()
-    {
-        $this->__load();
-        return parent::getSnmppasswd();
-    }
-
-    public function setInfrastructure($infrastructure)
-    {
-        $this->__load();
-        return parent::setInfrastructure($infrastructure);
-    }
-
-    public function getInfrastructure()
-    {
-        $this->__load();
-        return parent::getInfrastructure();
-    }
-
-    public function setSwitchtype($switchtype)
-    {
-        $this->__load();
-        return parent::setSwitchtype($switchtype);
-    }
-
-    public function getSwitchtype()
-    {
-        $this->__load();
-        return parent::getSwitchtype();
-    }
-
-    public function setModel($model)
-    {
-        $this->__load();
-        return parent::setModel($model);
-    }
-
-    public function getModel()
-    {
-        $this->__load();
-        return parent::getModel();
-    }
-
-    public function setActrive($actrive)
-    {
-        $this->__load();
-        return parent::setActrive($actrive);
-    }
-
-    public function getActrive()
-    {
-        $this->__load();
-        return parent::getActrive();
-    }
-
-    public function setNotes($notes)
-    {
-        $this->__load();
-        return parent::setNotes($notes);
-    }
-
-    public function getNotes()
-    {
-        $this->__load();
-        return parent::getNotes();
-    }
-
-    public function getId()
-    {
-        if ($this->__isInitialized__ === false) {
-            return (int) $this->_identifier["id"];
-        }
-        $this->__load();
-        return parent::getId();
-    }
-
-    public function addPort(\Entities\SwitchPort $ports)
-    {
-        $this->__load();
-        return parent::addPort($ports);
-    }
-
-    public function removePort(\Entities\SwitchPort $ports)
-    {
-        $this->__load();
-        return parent::removePort($ports);
-    }
-
-    public function getPorts()
-    {
-        $this->__load();
-        return parent::getPorts();
-    }
-
-    public function addConsoleServerConnection(\Entities\ConsoleServerConnection $consoleServerConnections)
-    {
-        $this->__load();
-        return parent::addConsoleServerConnection($consoleServerConnections);
-    }
-
-    public function removeConsoleServerConnection(\Entities\ConsoleServerConnection $consoleServerConnections)
-    {
-        $this->__load();
-        return parent::removeConsoleServerConnection($consoleServerConnections);
-    }
-
-    public function getConsoleServerConnections()
-    {
-        $this->__load();
-        return parent::getConsoleServerConnections();
-    }
-
-    public function setCabinet(\Entities\Cabinet $cabinet = NULL)
-    {
-        $this->__load();
-        return parent::setCabinet($cabinet);
-    }
-
-    public function getCabinet()
-    {
-        $this->__load();
-        return parent::getCabinet();
-    }
-
-    public function setVendor(\Entities\Vendor $vendor = NULL)
-    {
-        $this->__load();
-        return parent::setVendor($vendor);
-    }
-
-    public function getVendor()
-    {
-        $this->__load();
-        return parent::getVendor();
-    }
-
-    public function addSecEvent(\Entities\SecEvent $secEvents)
-    {
-        $this->__load();
-        return parent::addSecEvent($secEvents);
-    }
-
-    public function removeSecEvent(\Entities\SecEvent $secEvents)
-    {
-        $this->__load();
-        return parent::removeSecEvent($secEvents);
-    }
-
-    public function getSecEvents()
-    {
-        $this->__load();
-        return parent::getSecEvents();
-    }
-
-    public function setActive($active)
-    {
-        $this->__load();
-        return parent::setActive($active);
-    }
-
-    public function getActive()
-    {
-        $this->__load();
-        return parent::getActive();
-    }
-
-
-    public function __sleep()
-    {
-        return array('__isInitialized__', 'name', 'ipv4addr', 'ipv6addr', 'snmppasswd', 'infrastructure', 'switchtype', 'model', 'active', 'notes', 'id', 'Ports', 'ConsoleServerConnections', 'SecEvents', 'Cabinet', 'Vendor');
-    }
-
-    public function __clone()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-            $class = $this->_entityPersister->getClassMetadata();
-            $original = $this->_entityPersister->load($this->_identifier);
-            if ($original === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            foreach ($class->reflFields as $field => $reflProperty) {
-                $reflProperty->setValue($this, $reflProperty->getValue($original));
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-        
-    }
-}
\ No newline at end of file
diff --git a/application/Proxies/__CG__EntitiesTraffic95th.php b/application/Proxies/__CG__EntitiesTraffic95th.php
deleted file mode 100644
index 675aa9f5b..000000000
--- a/application/Proxies/__CG__EntitiesTraffic95th.php
+++ /dev/null
@@ -1,124 +0,0 @@
-_entityPersister = $entityPersister;
-        $this->_identifier = $identifier;
-    }
-    /** @private */
-    public function __load()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-
-            if (method_exists($this, "__wakeup")) {
-                // call this after __isInitialized__to avoid infinite recursion
-                // but before loading to emulate what ClassMetadata::newInstance()
-                // provides.
-                $this->__wakeup();
-            }
-
-            if ($this->_entityPersister->load($this->_identifier, $this) === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-    }
-
-    /** @private */
-    public function __isInitialized()
-    {
-        return $this->__isInitialized__;
-    }
-
-    
-    public function setDatetime($datetime)
-    {
-        $this->__load();
-        return parent::setDatetime($datetime);
-    }
-
-    public function getDatetime()
-    {
-        $this->__load();
-        return parent::getDatetime();
-    }
-
-    public function setAverage($average)
-    {
-        $this->__load();
-        return parent::setAverage($average);
-    }
-
-    public function getAverage()
-    {
-        $this->__load();
-        return parent::getAverage();
-    }
-
-    public function setMax($max)
-    {
-        $this->__load();
-        return parent::setMax($max);
-    }
-
-    public function getMax()
-    {
-        $this->__load();
-        return parent::getMax();
-    }
-
-    public function getId()
-    {
-        if ($this->__isInitialized__ === false) {
-            return $this->_identifier["id"];
-        }
-        $this->__load();
-        return parent::getId();
-    }
-
-    public function setCustomer(\Entities\Customer $customer = NULL)
-    {
-        $this->__load();
-        return parent::setCustomer($customer);
-    }
-
-    public function getCustomer()
-    {
-        $this->__load();
-        return parent::getCustomer();
-    }
-
-
-    public function __sleep()
-    {
-        return array('__isInitialized__', 'datetime', 'average', 'max', 'id', 'Customer');
-    }
-
-    public function __clone()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-            $class = $this->_entityPersister->getClassMetadata();
-            $original = $this->_entityPersister->load($this->_identifier);
-            if ($original === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            foreach ($class->reflFields as $field => $reflProperty) {
-                $reflProperty->setValue($this, $reflProperty->getValue($original));
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-        
-    }
-}
\ No newline at end of file
diff --git a/application/Proxies/__CG__EntitiesTraffic95thMonthly.php b/application/Proxies/__CG__EntitiesTraffic95thMonthly.php
deleted file mode 100644
index 996510161..000000000
--- a/application/Proxies/__CG__EntitiesTraffic95thMonthly.php
+++ /dev/null
@@ -1,112 +0,0 @@
-_entityPersister = $entityPersister;
-        $this->_identifier = $identifier;
-    }
-    /** @private */
-    public function __load()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-
-            if (method_exists($this, "__wakeup")) {
-                // call this after __isInitialized__to avoid infinite recursion
-                // but before loading to emulate what ClassMetadata::newInstance()
-                // provides.
-                $this->__wakeup();
-            }
-
-            if ($this->_entityPersister->load($this->_identifier, $this) === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-    }
-
-    /** @private */
-    public function __isInitialized()
-    {
-        return $this->__isInitialized__;
-    }
-
-    
-    public function setMonth($month)
-    {
-        $this->__load();
-        return parent::setMonth($month);
-    }
-
-    public function getMonth()
-    {
-        $this->__load();
-        return parent::getMonth();
-    }
-
-    public function setMax95th($max95th)
-    {
-        $this->__load();
-        return parent::setMax95th($max95th);
-    }
-
-    public function getMax95th()
-    {
-        $this->__load();
-        return parent::getMax95th();
-    }
-
-    public function getId()
-    {
-        if ($this->__isInitialized__ === false) {
-            return $this->_identifier["id"];
-        }
-        $this->__load();
-        return parent::getId();
-    }
-
-    public function setCustomer(\Entities\Customer $customer = NULL)
-    {
-        $this->__load();
-        return parent::setCustomer($customer);
-    }
-
-    public function getCustomer()
-    {
-        $this->__load();
-        return parent::getCustomer();
-    }
-
-
-    public function __sleep()
-    {
-        return array('__isInitialized__', 'month', 'max_95th', 'id', 'Customer');
-    }
-
-    public function __clone()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-            $class = $this->_entityPersister->getClassMetadata();
-            $original = $this->_entityPersister->load($this->_identifier);
-            if ($original === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            foreach ($class->reflFields as $field => $reflProperty) {
-                $reflProperty->setValue($this, $reflProperty->getValue($original));
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-        
-    }
-}
\ No newline at end of file
diff --git a/application/Proxies/__CG__EntitiesTrafficDaily.php b/application/Proxies/__CG__EntitiesTrafficDaily.php
deleted file mode 100644
index 5606b1064..000000000
--- a/application/Proxies/__CG__EntitiesTrafficDaily.php
+++ /dev/null
@@ -1,400 +0,0 @@
-_entityPersister = $entityPersister;
-        $this->_identifier = $identifier;
-    }
-    /** @private */
-    public function __load()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-
-            if (method_exists($this, "__wakeup")) {
-                // call this after __isInitialized__to avoid infinite recursion
-                // but before loading to emulate what ClassMetadata::newInstance()
-                // provides.
-                $this->__wakeup();
-            }
-
-            if ($this->_entityPersister->load($this->_identifier, $this) === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-    }
-
-    /** @private */
-    public function __isInitialized()
-    {
-        return $this->__isInitialized__;
-    }
-
-    
-    public function setDay($day)
-    {
-        $this->__load();
-        return parent::setDay($day);
-    }
-
-    public function getDay()
-    {
-        $this->__load();
-        return parent::getDay();
-    }
-
-    public function setCategory($category)
-    {
-        $this->__load();
-        return parent::setCategory($category);
-    }
-
-    public function getCategory()
-    {
-        $this->__load();
-        return parent::getCategory();
-    }
-
-    public function setDayAvgIn($dayAvgIn)
-    {
-        $this->__load();
-        return parent::setDayAvgIn($dayAvgIn);
-    }
-
-    public function getDayAvgIn()
-    {
-        $this->__load();
-        return parent::getDayAvgIn();
-    }
-
-    public function setDayAvgOut($dayAvgOut)
-    {
-        $this->__load();
-        return parent::setDayAvgOut($dayAvgOut);
-    }
-
-    public function getDayAvgOut()
-    {
-        $this->__load();
-        return parent::getDayAvgOut();
-    }
-
-    public function setDayMaxIn($dayMaxIn)
-    {
-        $this->__load();
-        return parent::setDayMaxIn($dayMaxIn);
-    }
-
-    public function getDayMaxIn()
-    {
-        $this->__load();
-        return parent::getDayMaxIn();
-    }
-
-    public function setDayMaxOut($dayMaxOut)
-    {
-        $this->__load();
-        return parent::setDayMaxOut($dayMaxOut);
-    }
-
-    public function getDayMaxOut()
-    {
-        $this->__load();
-        return parent::getDayMaxOut();
-    }
-
-    public function setDayTotIn($dayTotIn)
-    {
-        $this->__load();
-        return parent::setDayTotIn($dayTotIn);
-    }
-
-    public function getDayTotIn()
-    {
-        $this->__load();
-        return parent::getDayTotIn();
-    }
-
-    public function setDayTotOut($dayTotOut)
-    {
-        $this->__load();
-        return parent::setDayTotOut($dayTotOut);
-    }
-
-    public function getDayTotOut()
-    {
-        $this->__load();
-        return parent::getDayTotOut();
-    }
-
-    public function setWeekAvgIn($weekAvgIn)
-    {
-        $this->__load();
-        return parent::setWeekAvgIn($weekAvgIn);
-    }
-
-    public function getWeekAvgIn()
-    {
-        $this->__load();
-        return parent::getWeekAvgIn();
-    }
-
-    public function setWeekAvgOut($weekAvgOut)
-    {
-        $this->__load();
-        return parent::setWeekAvgOut($weekAvgOut);
-    }
-
-    public function getWeekAvgOut()
-    {
-        $this->__load();
-        return parent::getWeekAvgOut();
-    }
-
-    public function setWeekMaxIn($weekMaxIn)
-    {
-        $this->__load();
-        return parent::setWeekMaxIn($weekMaxIn);
-    }
-
-    public function getWeekMaxIn()
-    {
-        $this->__load();
-        return parent::getWeekMaxIn();
-    }
-
-    public function setWeekMaxOut($weekMaxOut)
-    {
-        $this->__load();
-        return parent::setWeekMaxOut($weekMaxOut);
-    }
-
-    public function getWeekMaxOut()
-    {
-        $this->__load();
-        return parent::getWeekMaxOut();
-    }
-
-    public function setWeekTotIn($weekTotIn)
-    {
-        $this->__load();
-        return parent::setWeekTotIn($weekTotIn);
-    }
-
-    public function getWeekTotIn()
-    {
-        $this->__load();
-        return parent::getWeekTotIn();
-    }
-
-    public function setWeekTotOut($weekTotOut)
-    {
-        $this->__load();
-        return parent::setWeekTotOut($weekTotOut);
-    }
-
-    public function getWeekTotOut()
-    {
-        $this->__load();
-        return parent::getWeekTotOut();
-    }
-
-    public function setMonthAvgIn($monthAvgIn)
-    {
-        $this->__load();
-        return parent::setMonthAvgIn($monthAvgIn);
-    }
-
-    public function getMonthAvgIn()
-    {
-        $this->__load();
-        return parent::getMonthAvgIn();
-    }
-
-    public function setMonthAvgOut($monthAvgOut)
-    {
-        $this->__load();
-        return parent::setMonthAvgOut($monthAvgOut);
-    }
-
-    public function getMonthAvgOut()
-    {
-        $this->__load();
-        return parent::getMonthAvgOut();
-    }
-
-    public function setMonthMaxIn($monthMaxIn)
-    {
-        $this->__load();
-        return parent::setMonthMaxIn($monthMaxIn);
-    }
-
-    public function getMonthMaxIn()
-    {
-        $this->__load();
-        return parent::getMonthMaxIn();
-    }
-
-    public function setMonthMaxOut($monthMaxOut)
-    {
-        $this->__load();
-        return parent::setMonthMaxOut($monthMaxOut);
-    }
-
-    public function getMonthMaxOut()
-    {
-        $this->__load();
-        return parent::getMonthMaxOut();
-    }
-
-    public function setMonthTotIn($monthTotIn)
-    {
-        $this->__load();
-        return parent::setMonthTotIn($monthTotIn);
-    }
-
-    public function getMonthTotIn()
-    {
-        $this->__load();
-        return parent::getMonthTotIn();
-    }
-
-    public function setMonthTotOut($monthTotOut)
-    {
-        $this->__load();
-        return parent::setMonthTotOut($monthTotOut);
-    }
-
-    public function getMonthTotOut()
-    {
-        $this->__load();
-        return parent::getMonthTotOut();
-    }
-
-    public function setYearAvgIn($yearAvgIn)
-    {
-        $this->__load();
-        return parent::setYearAvgIn($yearAvgIn);
-    }
-
-    public function getYearAvgIn()
-    {
-        $this->__load();
-        return parent::getYearAvgIn();
-    }
-
-    public function setYearAvgOut($yearAvgOut)
-    {
-        $this->__load();
-        return parent::setYearAvgOut($yearAvgOut);
-    }
-
-    public function getYearAvgOut()
-    {
-        $this->__load();
-        return parent::getYearAvgOut();
-    }
-
-    public function setYearMaxIn($yearMaxIn)
-    {
-        $this->__load();
-        return parent::setYearMaxIn($yearMaxIn);
-    }
-
-    public function getYearMaxIn()
-    {
-        $this->__load();
-        return parent::getYearMaxIn();
-    }
-
-    public function setYearMaxOut($yearMaxOut)
-    {
-        $this->__load();
-        return parent::setYearMaxOut($yearMaxOut);
-    }
-
-    public function getYearMaxOut()
-    {
-        $this->__load();
-        return parent::getYearMaxOut();
-    }
-
-    public function setYearTotIn($yearTotIn)
-    {
-        $this->__load();
-        return parent::setYearTotIn($yearTotIn);
-    }
-
-    public function getYearTotIn()
-    {
-        $this->__load();
-        return parent::getYearTotIn();
-    }
-
-    public function setYearTotOut($yearTotOut)
-    {
-        $this->__load();
-        return parent::setYearTotOut($yearTotOut);
-    }
-
-    public function getYearTotOut()
-    {
-        $this->__load();
-        return parent::getYearTotOut();
-    }
-
-    public function getId()
-    {
-        if ($this->__isInitialized__ === false) {
-            return $this->_identifier["id"];
-        }
-        $this->__load();
-        return parent::getId();
-    }
-
-    public function setCustomer(\Entities\Customer $customer = NULL)
-    {
-        $this->__load();
-        return parent::setCustomer($customer);
-    }
-
-    public function getCustomer()
-    {
-        $this->__load();
-        return parent::getCustomer();
-    }
-
-
-    public function __sleep()
-    {
-        return array('__isInitialized__', 'day', 'category', 'day_avg_in', 'day_avg_out', 'day_max_in', 'day_max_out', 'day_tot_in', 'day_tot_out', 'week_avg_in', 'week_avg_out', 'week_max_in', 'week_max_out', 'week_tot_in', 'week_tot_out', 'month_avg_in', 'month_avg_out', 'month_max_in', 'month_max_out', 'month_tot_in', 'month_tot_out', 'year_avg_in', 'year_avg_out', 'year_max_in', 'year_max_out', 'year_tot_in', 'year_tot_out', 'id', 'Customer');
-    }
-
-    public function __clone()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-            $class = $this->_entityPersister->getClassMetadata();
-            $original = $this->_entityPersister->load($this->_identifier);
-            if ($original === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            foreach ($class->reflFields as $field => $reflProperty) {
-                $reflProperty->setValue($this, $reflProperty->getValue($original));
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-        
-    }
-}
\ No newline at end of file
diff --git a/application/Proxies/__CG__EntitiesUser.php b/application/Proxies/__CG__EntitiesUser.php
deleted file mode 100644
index 5938ae27b..000000000
--- a/application/Proxies/__CG__EntitiesUser.php
+++ /dev/null
@@ -1,400 +0,0 @@
-_entityPersister = $entityPersister;
-        $this->_identifier = $identifier;
-    }
-    /** @private */
-    public function __load()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-
-            if (method_exists($this, "__wakeup")) {
-                // call this after __isInitialized__to avoid infinite recursion
-                // but before loading to emulate what ClassMetadata::newInstance()
-                // provides.
-                $this->__wakeup();
-            }
-
-            if ($this->_entityPersister->load($this->_identifier, $this) === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-    }
-
-    /** @private */
-    public function __isInitialized()
-    {
-        return $this->__isInitialized__;
-    }
-
-    
-    public function setUsername($username)
-    {
-        $this->__load();
-        return parent::setUsername($username);
-    }
-
-    public function getUsername()
-    {
-        $this->__load();
-        return parent::getUsername();
-    }
-
-    public function setPassword($password)
-    {
-        $this->__load();
-        return parent::setPassword($password);
-    }
-
-    public function getPassword()
-    {
-        $this->__load();
-        return parent::getPassword();
-    }
-
-    public function setEmail($email)
-    {
-        $this->__load();
-        return parent::setEmail($email);
-    }
-
-    public function getEmail()
-    {
-        $this->__load();
-        return parent::getEmail();
-    }
-
-    public function setAuthorisedMobile($authorisedMobile)
-    {
-        $this->__load();
-        return parent::setAuthorisedMobile($authorisedMobile);
-    }
-
-    public function getAuthorisedMobile()
-    {
-        $this->__load();
-        return parent::getAuthorisedMobile();
-    }
-
-    public function setUid($uid)
-    {
-        $this->__load();
-        return parent::setUid($uid);
-    }
-
-    public function getUid()
-    {
-        $this->__load();
-        return parent::getUid();
-    }
-
-    public function setPrivs($privs)
-    {
-        $this->__load();
-        return parent::setPrivs($privs);
-    }
-
-    public function getPrivs()
-    {
-        $this->__load();
-        return parent::getPrivs();
-    }
-
-    public function setDisabled($disabled)
-    {
-        $this->__load();
-        return parent::setDisabled($disabled);
-    }
-
-    public function getDisabled()
-    {
-        $this->__load();
-        return parent::getDisabled();
-    }
-
-    public function setLastupdated($lastupdated)
-    {
-        $this->__load();
-        return parent::setLastupdated($lastupdated);
-    }
-
-    public function getLastupdated()
-    {
-        $this->__load();
-        return parent::getLastupdated();
-    }
-
-    public function setLastupdatedby($lastupdatedby)
-    {
-        $this->__load();
-        return parent::setLastupdatedby($lastupdatedby);
-    }
-
-    public function getLastupdatedby()
-    {
-        $this->__load();
-        return parent::getLastupdatedby();
-    }
-
-    public function setCreator($creator)
-    {
-        $this->__load();
-        return parent::setCreator($creator);
-    }
-
-    public function getCreator()
-    {
-        $this->__load();
-        return parent::getCreator();
-    }
-
-    public function setCreated($created)
-    {
-        $this->__load();
-        return parent::setCreated($created);
-    }
-
-    public function getCreated()
-    {
-        $this->__load();
-        return parent::getCreated();
-    }
-
-    public function getId()
-    {
-        if ($this->__isInitialized__ === false) {
-            return (int) $this->_identifier["id"];
-        }
-        $this->__load();
-        return parent::getId();
-    }
-
-    public function addParent(\Entities\User $parent)
-    {
-        $this->__load();
-        return parent::addParent($parent);
-    }
-
-    public function removeParent(\Entities\User $parent)
-    {
-        $this->__load();
-        return parent::removeParent($parent);
-    }
-
-    public function getParent()
-    {
-        $this->__load();
-        return parent::getParent();
-    }
-
-    public function addPreference(\Entities\UserPreference $preferences)
-    {
-        $this->__load();
-        return parent::addPreference($preferences);
-    }
-
-    public function removePreference(\Entities\UserPreference $preferences)
-    {
-        $this->__load();
-        return parent::removePreference($preferences);
-    }
-
-    public function getPreferences()
-    {
-        $this->__load();
-        return parent::getPreferences();
-    }
-
-    public function addChangeLog(\Entities\ChangeLog $changeLogs)
-    {
-        $this->__load();
-        return parent::addChangeLog($changeLogs);
-    }
-
-    public function removeChangeLog(\Entities\ChangeLog $changeLogs)
-    {
-        $this->__load();
-        return parent::removeChangeLog($changeLogs);
-    }
-
-    public function getChangeLogs()
-    {
-        $this->__load();
-        return parent::getChangeLogs();
-    }
-
-    public function setCustomer(\Entities\Customer $customer = NULL)
-    {
-        $this->__load();
-        return parent::setCustomer($customer);
-    }
-
-    public function getCustomer()
-    {
-        $this->__load();
-        return parent::getCustomer();
-    }
-
-    public function setChildren(\Entities\User $children = NULL)
-    {
-        $this->__load();
-        return parent::setChildren($children);
-    }
-
-    public function getChildren()
-    {
-        $this->__load();
-        return parent::getChildren();
-    }
-
-    public function getFormattedName()
-    {
-        $this->__load();
-        return parent::getFormattedName();
-    }
-
-    public function addChildren(\Entities\User $children)
-    {
-        $this->__load();
-        return parent::addChildren($children);
-    }
-
-    public function removeChildren(\Entities\User $children)
-    {
-        $this->__load();
-        return parent::removeChildren($children);
-    }
-
-    public function setParent(\Entities\User $parent = NULL)
-    {
-        $this->__load();
-        return parent::setParent($parent);
-    }
-
-    public function addMeeting(\Entities\Meeting $meetings)
-    {
-        $this->__load();
-        return parent::addMeeting($meetings);
-    }
-
-    public function removeMeeting(\Entities\Meeting $meetings)
-    {
-        $this->__load();
-        return parent::removeMeeting($meetings);
-    }
-
-    public function getMeetings()
-    {
-        $this->__load();
-        return parent::getMeetings();
-    }
-
-    public function loadPreference($attribute, $index = 0, $includeExpired = false)
-    {
-        $this->__load();
-        return parent::loadPreference($attribute, $index, $includeExpired);
-    }
-
-    public function hasPreference($attribute, $index = 0, $includeExpired = false)
-    {
-        $this->__load();
-        return parent::hasPreference($attribute, $index, $includeExpired);
-    }
-
-    public function getPreference($attribute, $index = 0, $includeExpired = false)
-    {
-        $this->__load();
-        return parent::getPreference($attribute, $index, $includeExpired);
-    }
-
-    public function setPreference($attribute, $value, $operator = '=', $expires = 0, $index = 0)
-    {
-        $this->__load();
-        return parent::setPreference($attribute, $value, $operator, $expires, $index);
-    }
-
-    public function addIndexedPreference($attribute, $value, $operator = '=', $expires = 0, $max = 0)
-    {
-        $this->__load();
-        return parent::addIndexedPreference($attribute, $value, $operator, $expires, $max);
-    }
-
-    public function cleanExpiredPreferences($asOf = NULL, $attribute = NULL)
-    {
-        $this->__load();
-        return parent::cleanExpiredPreferences($asOf, $attribute);
-    }
-
-    public function deletePreference($attribute, $index = NULL)
-    {
-        $this->__load();
-        return parent::deletePreference($attribute, $index);
-    }
-
-    public function expungePreferences()
-    {
-        $this->__load();
-        return parent::expungePreferences();
-    }
-
-    public function getIndexedPreference($attribute, $withIndex = false, $ignoreExpired = true)
-    {
-        $this->__load();
-        return parent::getIndexedPreference($attribute, $withIndex, $ignoreExpired);
-    }
-
-    public function getAssocPreference($attribute, $index = NULL, $ignoreExpired = true)
-    {
-        $this->__load();
-        return parent::getAssocPreference($attribute, $index, $ignoreExpired);
-    }
-
-    public function deleteAssocPreference($attribute, $index = false)
-    {
-        $this->__load();
-        return parent::deleteAssocPreference($attribute, $index);
-    }
-
-    public function _getPreferences()
-    {
-        $this->__load();
-        return parent::_getPreferences();
-    }
-
-
-    public function __sleep()
-    {
-        return array('__isInitialized__', 'username', 'password', 'email', 'authorisedMobile', 'uid', 'privs', 'disabled', 'lastupdated', 'lastupdatedby', 'creator', 'created', 'id', 'Meetings', 'Children', 'Preferences', 'ChangeLogs', 'Customer', 'Parent');
-    }
-
-    public function __clone()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-            $class = $this->_entityPersister->getClassMetadata();
-            $original = $this->_entityPersister->load($this->_identifier);
-            if ($original === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            foreach ($class->reflFields as $field => $reflProperty) {
-                $reflProperty->setValue($this, $reflProperty->getValue($original));
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-        
-    }
-}
\ No newline at end of file
diff --git a/application/Proxies/__CG__EntitiesUserPreference.php b/application/Proxies/__CG__EntitiesUserPreference.php
deleted file mode 100644
index 707ca05a1..000000000
--- a/application/Proxies/__CG__EntitiesUserPreference.php
+++ /dev/null
@@ -1,148 +0,0 @@
-_entityPersister = $entityPersister;
-        $this->_identifier = $identifier;
-    }
-    /** @private */
-    public function __load()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-
-            if (method_exists($this, "__wakeup")) {
-                // call this after __isInitialized__to avoid infinite recursion
-                // but before loading to emulate what ClassMetadata::newInstance()
-                // provides.
-                $this->__wakeup();
-            }
-
-            if ($this->_entityPersister->load($this->_identifier, $this) === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-    }
-
-    /** @private */
-    public function __isInitialized()
-    {
-        return $this->__isInitialized__;
-    }
-
-    
-    public function setAttribute($attribute)
-    {
-        $this->__load();
-        return parent::setAttribute($attribute);
-    }
-
-    public function getAttribute()
-    {
-        $this->__load();
-        return parent::getAttribute();
-    }
-
-    public function setOp($op)
-    {
-        $this->__load();
-        return parent::setOp($op);
-    }
-
-    public function getOp()
-    {
-        $this->__load();
-        return parent::getOp();
-    }
-
-    public function setValue($value)
-    {
-        $this->__load();
-        return parent::setValue($value);
-    }
-
-    public function getValue()
-    {
-        $this->__load();
-        return parent::getValue();
-    }
-
-    public function getId()
-    {
-        if ($this->__isInitialized__ === false) {
-            return (int) $this->_identifier["id"];
-        }
-        $this->__load();
-        return parent::getId();
-    }
-
-    public function setUser(\Entities\User $user = NULL)
-    {
-        $this->__load();
-        return parent::setUser($user);
-    }
-
-    public function getUser()
-    {
-        $this->__load();
-        return parent::getUser();
-    }
-
-    public function setIx($ix)
-    {
-        $this->__load();
-        return parent::setIx($ix);
-    }
-
-    public function getIx()
-    {
-        $this->__load();
-        return parent::getIx();
-    }
-
-    public function setExpire($expire)
-    {
-        $this->__load();
-        return parent::setExpire($expire);
-    }
-
-    public function getExpire()
-    {
-        $this->__load();
-        return parent::getExpire();
-    }
-
-
-    public function __sleep()
-    {
-        return array('__isInitialized__', 'attribute', 'ix', 'op', 'value', 'expire', 'id', 'User');
-    }
-
-    public function __clone()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-            $class = $this->_entityPersister->getClassMetadata();
-            $original = $this->_entityPersister->load($this->_identifier);
-            if ($original === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            foreach ($class->reflFields as $field => $reflProperty) {
-                $reflProperty->setValue($this, $reflProperty->getValue($original));
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-        
-    }
-}
\ No newline at end of file
diff --git a/application/Proxies/__CG__EntitiesVendor.php b/application/Proxies/__CG__EntitiesVendor.php
deleted file mode 100644
index 26ad1135a..000000000
--- a/application/Proxies/__CG__EntitiesVendor.php
+++ /dev/null
@@ -1,106 +0,0 @@
-_entityPersister = $entityPersister;
-        $this->_identifier = $identifier;
-    }
-    /** @private */
-    public function __load()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-
-            if (method_exists($this, "__wakeup")) {
-                // call this after __isInitialized__to avoid infinite recursion
-                // but before loading to emulate what ClassMetadata::newInstance()
-                // provides.
-                $this->__wakeup();
-            }
-
-            if ($this->_entityPersister->load($this->_identifier, $this) === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-    }
-
-    /** @private */
-    public function __isInitialized()
-    {
-        return $this->__isInitialized__;
-    }
-
-    
-    public function setName($name)
-    {
-        $this->__load();
-        return parent::setName($name);
-    }
-
-    public function getName()
-    {
-        $this->__load();
-        return parent::getName();
-    }
-
-    public function getId()
-    {
-        if ($this->__isInitialized__ === false) {
-            return (int) $this->_identifier["id"];
-        }
-        $this->__load();
-        return parent::getId();
-    }
-
-    public function addSwitche(\Entities\Switcher $switches)
-    {
-        $this->__load();
-        return parent::addSwitche($switches);
-    }
-
-    public function removeSwitche(\Entities\Switcher $switches)
-    {
-        $this->__load();
-        return parent::removeSwitche($switches);
-    }
-
-    public function getSwitches()
-    {
-        $this->__load();
-        return parent::getSwitches();
-    }
-
-
-    public function __sleep()
-    {
-        return array('__isInitialized__', 'name', 'id', 'Switches');
-    }
-
-    public function __clone()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-            $class = $this->_entityPersister->getClassMetadata();
-            $original = $this->_entityPersister->load($this->_identifier);
-            if ($original === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            foreach ($class->reflFields as $field => $reflProperty) {
-                $reflProperty->setValue($this, $reflProperty->getValue($original));
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-        
-    }
-}
\ No newline at end of file
diff --git a/application/Proxies/__CG__EntitiesVirtualInterface.php b/application/Proxies/__CG__EntitiesVirtualInterface.php
deleted file mode 100644
index 618d62f52..000000000
--- a/application/Proxies/__CG__EntitiesVirtualInterface.php
+++ /dev/null
@@ -1,202 +0,0 @@
-_entityPersister = $entityPersister;
-        $this->_identifier = $identifier;
-    }
-    /** @private */
-    public function __load()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-
-            if (method_exists($this, "__wakeup")) {
-                // call this after __isInitialized__to avoid infinite recursion
-                // but before loading to emulate what ClassMetadata::newInstance()
-                // provides.
-                $this->__wakeup();
-            }
-
-            if ($this->_entityPersister->load($this->_identifier, $this) === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-    }
-
-    /** @private */
-    public function __isInitialized()
-    {
-        return $this->__isInitialized__;
-    }
-
-    
-    public function setName($name)
-    {
-        $this->__load();
-        return parent::setName($name);
-    }
-
-    public function getName()
-    {
-        $this->__load();
-        return parent::getName();
-    }
-
-    public function setDescription($description)
-    {
-        $this->__load();
-        return parent::setDescription($description);
-    }
-
-    public function getDescription()
-    {
-        $this->__load();
-        return parent::getDescription();
-    }
-
-    public function setMtu($mtu)
-    {
-        $this->__load();
-        return parent::setMtu($mtu);
-    }
-
-    public function getMtu()
-    {
-        $this->__load();
-        return parent::getMtu();
-    }
-
-    public function setTrunk($trunk)
-    {
-        $this->__load();
-        return parent::setTrunk($trunk);
-    }
-
-    public function getTrunk()
-    {
-        $this->__load();
-        return parent::getTrunk();
-    }
-
-    public function setChannelgroup($channelgroup)
-    {
-        $this->__load();
-        return parent::setChannelgroup($channelgroup);
-    }
-
-    public function getChannelgroup()
-    {
-        $this->__load();
-        return parent::getChannelgroup();
-    }
-
-    public function getId()
-    {
-        if ($this->__isInitialized__ === false) {
-            return (int) $this->_identifier["id"];
-        }
-        $this->__load();
-        return parent::getId();
-    }
-
-    public function addPhysicalInterface(\Entities\PhysicalInterface $physicalInterfaces)
-    {
-        $this->__load();
-        return parent::addPhysicalInterface($physicalInterfaces);
-    }
-
-    public function removePhysicalInterface(\Entities\PhysicalInterface $physicalInterfaces)
-    {
-        $this->__load();
-        return parent::removePhysicalInterface($physicalInterfaces);
-    }
-
-    public function getPhysicalInterfaces()
-    {
-        $this->__load();
-        return parent::getPhysicalInterfaces();
-    }
-
-    public function addVlanInterface(\Entities\VlanInterface $vlanInterfaces)
-    {
-        $this->__load();
-        return parent::addVlanInterface($vlanInterfaces);
-    }
-
-    public function removeVlanInterface(\Entities\VlanInterface $vlanInterfaces)
-    {
-        $this->__load();
-        return parent::removeVlanInterface($vlanInterfaces);
-    }
-
-    public function getVlanInterfaces()
-    {
-        $this->__load();
-        return parent::getVlanInterfaces();
-    }
-
-    public function addMACAddresse(\Entities\MACAddress $mACAddresses)
-    {
-        $this->__load();
-        return parent::addMACAddresse($mACAddresses);
-    }
-
-    public function removeMACAddresse(\Entities\MACAddress $mACAddresses)
-    {
-        $this->__load();
-        return parent::removeMACAddresse($mACAddresses);
-    }
-
-    public function getMACAddresses()
-    {
-        $this->__load();
-        return parent::getMACAddresses();
-    }
-
-    public function setCustomer(\Entities\Customer $customer = NULL)
-    {
-        $this->__load();
-        return parent::setCustomer($customer);
-    }
-
-    public function getCustomer()
-    {
-        $this->__load();
-        return parent::getCustomer();
-    }
-
-
-    public function __sleep()
-    {
-        return array('__isInitialized__', 'name', 'description', 'mtu', 'trunk', 'channelgroup', 'id', 'PhysicalInterfaces', 'VlanInterfaces', 'MACAddresses', 'Customer');
-    }
-
-    public function __clone()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-            $class = $this->_entityPersister->getClassMetadata();
-            $original = $this->_entityPersister->load($this->_identifier);
-            if ($original === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            foreach ($class->reflFields as $field => $reflProperty) {
-                $reflProperty->setValue($this, $reflProperty->getValue($original));
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-        
-    }
-}
\ No newline at end of file
diff --git a/application/Proxies/__CG__EntitiesVlan.php b/application/Proxies/__CG__EntitiesVlan.php
deleted file mode 100644
index 6606a926c..000000000
--- a/application/Proxies/__CG__EntitiesVlan.php
+++ /dev/null
@@ -1,196 +0,0 @@
-_entityPersister = $entityPersister;
-        $this->_identifier = $identifier;
-    }
-    /** @private */
-    public function __load()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-
-            if (method_exists($this, "__wakeup")) {
-                // call this after __isInitialized__to avoid infinite recursion
-                // but before loading to emulate what ClassMetadata::newInstance()
-                // provides.
-                $this->__wakeup();
-            }
-
-            if ($this->_entityPersister->load($this->_identifier, $this) === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-    }
-
-    /** @private */
-    public function __isInitialized()
-    {
-        return $this->__isInitialized__;
-    }
-
-    
-    public function setName($name)
-    {
-        $this->__load();
-        return parent::setName($name);
-    }
-
-    public function getName()
-    {
-        $this->__load();
-        return parent::getName();
-    }
-
-    public function setNumber($number)
-    {
-        $this->__load();
-        return parent::setNumber($number);
-    }
-
-    public function getNumber()
-    {
-        $this->__load();
-        return parent::getNumber();
-    }
-
-    public function setRcvrfname($rcvrfname)
-    {
-        $this->__load();
-        return parent::setRcvrfname($rcvrfname);
-    }
-
-    public function getRcvrfname()
-    {
-        $this->__load();
-        return parent::getRcvrfname();
-    }
-
-    public function setNotes($notes)
-    {
-        $this->__load();
-        return parent::setNotes($notes);
-    }
-
-    public function getNotes()
-    {
-        $this->__load();
-        return parent::getNotes();
-    }
-
-    public function getId()
-    {
-        if ($this->__isInitialized__ === false) {
-            return (int) $this->_identifier["id"];
-        }
-        $this->__load();
-        return parent::getId();
-    }
-
-    public function addVlanInterface(\Entities\VlanInterface $vlanInterfaces)
-    {
-        $this->__load();
-        return parent::addVlanInterface($vlanInterfaces);
-    }
-
-    public function removeVlanInterface(\Entities\VlanInterface $vlanInterfaces)
-    {
-        $this->__load();
-        return parent::removeVlanInterface($vlanInterfaces);
-    }
-
-    public function getVlanInterfaces()
-    {
-        $this->__load();
-        return parent::getVlanInterfaces();
-    }
-
-    public function addIPv4Addresse(\Entities\IPv4Address $iPv4Addresses)
-    {
-        $this->__load();
-        return parent::addIPv4Addresse($iPv4Addresses);
-    }
-
-    public function removeIPv4Addresse(\Entities\IPv4Address $iPv4Addresses)
-    {
-        $this->__load();
-        return parent::removeIPv4Addresse($iPv4Addresses);
-    }
-
-    public function getIPv4Addresses()
-    {
-        $this->__load();
-        return parent::getIPv4Addresses();
-    }
-
-    public function addIPv6Addresse(\Entities\IPv6Address $iPv6Addresses)
-    {
-        $this->__load();
-        return parent::addIPv6Addresse($iPv6Addresses);
-    }
-
-    public function removeIPv6Addresse(\Entities\IPv6Address $iPv6Addresses)
-    {
-        $this->__load();
-        return parent::removeIPv6Addresse($iPv6Addresses);
-    }
-
-    public function getIPv6Addresses()
-    {
-        $this->__load();
-        return parent::getIPv6Addresses();
-    }
-
-    public function addNetworkInfo(\Entities\NetworkInfo $networkInfo)
-    {
-        $this->__load();
-        return parent::addNetworkInfo($networkInfo);
-    }
-
-    public function removeNetworkInfo(\Entities\NetworkInfo $networkInfo)
-    {
-        $this->__load();
-        return parent::removeNetworkInfo($networkInfo);
-    }
-
-    public function getNetworkInfo()
-    {
-        $this->__load();
-        return parent::getNetworkInfo();
-    }
-
-
-    public function __sleep()
-    {
-        return array('__isInitialized__', 'name', 'number', 'rcvrfname', 'notes', 'id', 'VlanInterfaces', 'IPv4Addresses', 'IPv6Addresses', 'NetworkInfo');
-    }
-
-    public function __clone()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-            $class = $this->_entityPersister->getClassMetadata();
-            $original = $this->_entityPersister->load($this->_identifier);
-            if ($original === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            foreach ($class->reflFields as $field => $reflProperty) {
-                $reflProperty->setValue($this, $reflProperty->getValue($original));
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-        
-    }
-}
\ No newline at end of file
diff --git a/application/Proxies/__CG__EntitiesVlanInterface.php b/application/Proxies/__CG__EntitiesVlanInterface.php
deleted file mode 100644
index d392c1299..000000000
--- a/application/Proxies/__CG__EntitiesVlanInterface.php
+++ /dev/null
@@ -1,340 +0,0 @@
-_entityPersister = $entityPersister;
-        $this->_identifier = $identifier;
-    }
-    /** @private */
-    public function __load()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-
-            if (method_exists($this, "__wakeup")) {
-                // call this after __isInitialized__to avoid infinite recursion
-                // but before loading to emulate what ClassMetadata::newInstance()
-                // provides.
-                $this->__wakeup();
-            }
-
-            if ($this->_entityPersister->load($this->_identifier, $this) === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-    }
-
-    /** @private */
-    public function __isInitialized()
-    {
-        return $this->__isInitialized__;
-    }
-
-    
-    public function setIpv4enabled($ipv4enabled)
-    {
-        $this->__load();
-        return parent::setIpv4enabled($ipv4enabled);
-    }
-
-    public function getIpv4enabled()
-    {
-        $this->__load();
-        return parent::getIpv4enabled();
-    }
-
-    public function setIpv4hostname($ipv4hostname)
-    {
-        $this->__load();
-        return parent::setIpv4hostname($ipv4hostname);
-    }
-
-    public function getIpv4hostname()
-    {
-        $this->__load();
-        return parent::getIpv4hostname();
-    }
-
-    public function setIpv6enabled($ipv6enabled)
-    {
-        $this->__load();
-        return parent::setIpv6enabled($ipv6enabled);
-    }
-
-    public function getIpv6enabled()
-    {
-        $this->__load();
-        return parent::getIpv6enabled();
-    }
-
-    public function setIpv6hostname($ipv6hostname)
-    {
-        $this->__load();
-        return parent::setIpv6hostname($ipv6hostname);
-    }
-
-    public function getIpv6hostname()
-    {
-        $this->__load();
-        return parent::getIpv6hostname();
-    }
-
-    public function setMcastenabled($mcastenabled)
-    {
-        $this->__load();
-        return parent::setMcastenabled($mcastenabled);
-    }
-
-    public function getMcastenabled()
-    {
-        $this->__load();
-        return parent::getMcastenabled();
-    }
-
-    public function setIrrdbfilter($irrdbfilter)
-    {
-        $this->__load();
-        return parent::setIrrdbfilter($irrdbfilter);
-    }
-
-    public function getIrrdbfilter()
-    {
-        $this->__load();
-        return parent::getIrrdbfilter();
-    }
-
-    public function setBgpmd5secret($bgpmd5secret)
-    {
-        $this->__load();
-        return parent::setBgpmd5secret($bgpmd5secret);
-    }
-
-    public function getBgpmd5secret()
-    {
-        $this->__load();
-        return parent::getBgpmd5secret();
-    }
-
-    public function setIpv4bgpmd5secret($ipv4bgpmd5secret)
-    {
-        $this->__load();
-        return parent::setIpv4bgpmd5secret($ipv4bgpmd5secret);
-    }
-
-    public function getIpv4bgpmd5secret()
-    {
-        $this->__load();
-        return parent::getIpv4bgpmd5secret();
-    }
-
-    public function setIpv6bgpmd5secret($ipv6bgpmd5secret)
-    {
-        $this->__load();
-        return parent::setIpv6bgpmd5secret($ipv6bgpmd5secret);
-    }
-
-    public function getIpv6bgpmd5secret()
-    {
-        $this->__load();
-        return parent::getIpv6bgpmd5secret();
-    }
-
-    public function setMaxbgpprefix($maxbgpprefix)
-    {
-        $this->__load();
-        return parent::setMaxbgpprefix($maxbgpprefix);
-    }
-
-    public function getMaxbgpprefix()
-    {
-        $this->__load();
-        return parent::getMaxbgpprefix();
-    }
-
-    public function setRsclient($rsclient)
-    {
-        $this->__load();
-        return parent::setRsclient($rsclient);
-    }
-
-    public function getRsclient()
-    {
-        $this->__load();
-        return parent::getRsclient();
-    }
-
-    public function setIpv4canping($ipv4canping)
-    {
-        $this->__load();
-        return parent::setIpv4canping($ipv4canping);
-    }
-
-    public function getIpv4canping()
-    {
-        $this->__load();
-        return parent::getIpv4canping();
-    }
-
-    public function setIpv6canping($ipv6canping)
-    {
-        $this->__load();
-        return parent::setIpv6canping($ipv6canping);
-    }
-
-    public function getIpv6canping()
-    {
-        $this->__load();
-        return parent::getIpv6canping();
-    }
-
-    public function setIpv4monitorrcbgp($ipv4monitorrcbgp)
-    {
-        $this->__load();
-        return parent::setIpv4monitorrcbgp($ipv4monitorrcbgp);
-    }
-
-    public function getIpv4monitorrcbgp()
-    {
-        $this->__load();
-        return parent::getIpv4monitorrcbgp();
-    }
-
-    public function setIpv6monitorrcbgp($ipv6monitorrcbgp)
-    {
-        $this->__load();
-        return parent::setIpv6monitorrcbgp($ipv6monitorrcbgp);
-    }
-
-    public function getIpv6monitorrcbgp()
-    {
-        $this->__load();
-        return parent::getIpv6monitorrcbgp();
-    }
-
-    public function setAs112client($as112client)
-    {
-        $this->__load();
-        return parent::setAs112client($as112client);
-    }
-
-    public function getAs112client()
-    {
-        $this->__load();
-        return parent::getAs112client();
-    }
-
-    public function setBusyhost($busyhost)
-    {
-        $this->__load();
-        return parent::setBusyhost($busyhost);
-    }
-
-    public function getBusyhost()
-    {
-        $this->__load();
-        return parent::getBusyhost();
-    }
-
-    public function setNotes($notes)
-    {
-        $this->__load();
-        return parent::setNotes($notes);
-    }
-
-    public function getNotes()
-    {
-        $this->__load();
-        return parent::getNotes();
-    }
-
-    public function getId()
-    {
-        if ($this->__isInitialized__ === false) {
-            return (int) $this->_identifier["id"];
-        }
-        $this->__load();
-        return parent::getId();
-    }
-
-    public function setIPv4Address(\Entities\IPv4Address $iPv4Address = NULL)
-    {
-        $this->__load();
-        return parent::setIPv4Address($iPv4Address);
-    }
-
-    public function getIPv4Address()
-    {
-        $this->__load();
-        return parent::getIPv4Address();
-    }
-
-    public function setIPv6Address(\Entities\IPv6Address $iPv6Address = NULL)
-    {
-        $this->__load();
-        return parent::setIPv6Address($iPv6Address);
-    }
-
-    public function getIPv6Address()
-    {
-        $this->__load();
-        return parent::getIPv6Address();
-    }
-
-    public function setVirtualInterface(\Entities\VirtualInterface $virtualInterface = NULL)
-    {
-        $this->__load();
-        return parent::setVirtualInterface($virtualInterface);
-    }
-
-    public function getVirtualInterface()
-    {
-        $this->__load();
-        return parent::getVirtualInterface();
-    }
-
-    public function setVlan(\Entities\Vlan $vlan = NULL)
-    {
-        $this->__load();
-        return parent::setVlan($vlan);
-    }
-
-    public function getVlan()
-    {
-        $this->__load();
-        return parent::getVlan();
-    }
-
-
-    public function __sleep()
-    {
-        return array('__isInitialized__', 'ipv4enabled', 'ipv4hostname', 'ipv6enabled', 'ipv6hostname', 'mcastenabled', 'irrdbfilter', 'bgpmd5secret', 'ipv4bgpmd5secret', 'ipv6bgpmd5secret', 'maxbgpprefix', 'rsclient', 'ipv4canping', 'ipv6canping', 'ipv4monitorrcbgp', 'ipv6monitorrcbgp', 'as112client', 'busyhost', 'notes', 'id', 'IPv4Address', 'IPv6Address', 'VirtualInterface', 'Vlan');
-    }
-
-    public function __clone()
-    {
-        if (!$this->__isInitialized__ && $this->_entityPersister) {
-            $this->__isInitialized__ = true;
-            $class = $this->_entityPersister->getClassMetadata();
-            $original = $this->_entityPersister->load($this->_identifier);
-            if ($original === null) {
-                throw new \Doctrine\ORM\EntityNotFoundException();
-            }
-            foreach ($class->reflFields as $field => $reflProperty) {
-                $reflProperty->setValue($this, $reflProperty->getValue($original));
-            }
-            unset($this->_entityPersister, $this->_identifier);
-        }
-        
-    }
-}
\ No newline at end of file
diff --git a/application/Repositories/BGPSessionData.php b/application/Repositories/BGPSessionData.php
deleted file mode 100644
index 7345fbca7..000000000
--- a/application/Repositories/BGPSessionData.php
+++ /dev/null
@@ -1,130 +0,0 @@
- array(3) {
-     *             ["shortname"] => string(10) "pchanycast"
-     *             ["name"] => string(25) "Packet Clearing House DNS"
-     *             ["peers"] => array(17) {
-     *                   [2110] => string(4) "2110"
-     *                   [2128] => string(4) "2128"
-     *                   ...
-     *             }
-     *         }
-     *         [112] => array(3) {
-     *             ["shortname"] => string(5) "as112"
-     *             ["name"] => string(17) "AS112 Reverse DNS"
-     *             ["peers"] => array(20) {
-     *                   [1213] => string(4) "1213"
-     *                   [2110] => string(4) "2110"
-     *                   ...
-     *             }
-     *         }
-     *         ...
-     *     }
-     *
-     * It also caches the results on a per VLAN, per protocol basis.
-     *
-     * @param int $vlan The VLAN ID of the peering LAN to query
-     * @param int $protocol The IP protocol to query (4 or 6)
-     * @param int|null $asn Optional ASN to limit the query to
-     * @param bool $forceDb Set to true to ignore the cache and force the query to the database
-     * @return array Array of peerings (as described above)
-     * @throws \INEX_Exception Thrown if an invalid protocol or VLAN id is specified
-     */
-    public function getPeers( $vlan = null, $protocol = 6, $asn = null, $forceDb = false )
-    {
-        $key = "pm_sessions_{$vlan}_{$protocol}";
-
-        if( !$forceDb && ( $apeers = \Zend_Registry::get( 'd2cache' )->fetch( $key ) ) )
-            return $apeers;
-        
-        if( !in_array( $protocol, [ 4, 6 ] ) )
-            throw new \INEX_Exception( 'Invalid protocol' );
-        
-        if( $vlan !== null && !( $evlan = $this->getEntityManager()->getRepository( '\Entities\Vlan' )->find( $vlan ) ) )
-            throw new \INEX_Exception( 'Invalid VLAN' );
-            
-        $conn = $this->getEntityManager()->getConnection();
-        $conn->setFetchMode( \PDO::FETCH_ASSOC );
-        
-        // need to construct a raw SQL here due to the schema design by NH
-        $sql = "SELECT bs.*, srcip.*, dstip.*,
-            vlis.virtualinterfaceid as visid, vlid.virtualinterfaceid as vidid,
-            cs.shortname AS csshortname, cs.name AS csname, cs.autsys AS csautsys,
-            cd.shortname AS cdshortname, cd.name AS cdname, cd.autsys AS cdautsys,
-            vlan.id AS vlanid, vlan.name AS vlanname, vlan.number AS vlantag,
-            COUNT( bs.packetcount ) AS packetcount
-                
-            FROM bgpsessiondata AS bs
-                LEFT JOIN ipv{$protocol}address AS srcip ON bs.srcipaddressid = srcip.id
-                LEFT JOIN ipv{$protocol}address AS dstip ON bs.dstipaddressid = dstip.id
-                LEFT JOIN vlaninterface AS vlis ON srcip.id = vlis.ipv4addressid
-                LEFT JOIN vlaninterface AS vlid ON dstip.id = vlid.ipv4addressid
-                LEFT JOIN virtualinterface AS vis ON vlis.virtualinterfaceid = vis.id
-                LEFT JOIN virtualinterface AS vid ON vlid.virtualinterfaceid = vid.id
-                LEFT JOIN cust AS cs ON vis.custid = cs.id
-                LEFT JOIN cust AS cd ON vid.custid = cd.id
-                LEFT JOIN vlan AS vlan ON vlan.number = bs.vlan
-         
-        WHERE
-            bs.protocol = {$protocol}
-            AND packetcount >= 1";
-        
-        if( $vlan !== null && $evlan )
-            $sql .= "\n            AND vlan.id = " . $evlan->getId();
-        
-        if( $asn !== null )
-            $sql .= "\n            AND cs.autsys = " . intval( $asn );
-            
-        $sql .= "\n        GROUP BY bs.srcipaddressid, bs.dstipaddressid";
-        
-        $peers = $conn->fetchAll( $sql );
-        
-        $apeers = [];
-    
-        foreach( $peers as $p )
-        {
-            if( !isset( $apeers[ $p['csautsys'] ] ) )
-            {
-                $apeers[ $p['csautsys'] ] = [];
-                $apeers[ $p['csautsys'] ]['shortname'] = $p['csshortname'];
-                $apeers[ $p['csautsys'] ]['name']      = $p['csname'];
-                $apeers[ $p['csautsys'] ]['peers']     = [];
-            }
-    
-            $apeers[ $p['csautsys'] ]['peers'][ $p['cdautsys'] ] = $p['cdautsys'];
-        }
-    
-        ksort( $apeers, SORT_NUMERIC );
-    
-        foreach( $apeers as $asn => $p )
-            ksort( $apeers[ $asn ][ 'peers' ], SORT_NUMERIC );
-
-        \Zend_Registry::get( 'd2cache' )->save( $key, $apeers, 86400 );
-                
-        return $apeers;
-    }
-    
-}
diff --git a/application/Repositories/Cabinet.php b/application/Repositories/Cabinet.php
deleted file mode 100644
index b0593aec7..000000000
--- a/application/Repositories/Cabinet.php
+++ /dev/null
@@ -1,15 +0,0 @@
-select( 'cl.*' )
-        ->addSelect( 'u.username' )
-        ->from( 'ChangeLog cl' )
-        ->leftJoin( 'cl.User u' )
-        ->where( 'cl.visibility <= ?', $priv )
-        ->orderBy( 'cl.livedate DESC' );
-    
-        if( $dateFrom !== false )
-            $entries->andWhere( 'cl.created_at >= ?', $dateFrom );
-    
-        return $entries->execute( null, $hydrateMethod );
-    }
-    
-    /**
-     * Does the given user have unseen updates on the change log?
-     *
-     * @param $priv int The privilege level to check for (and lesser privileges)
-         * @param $dateFrom string The date and time (YYYY-MM-DD HH:MM:SS) from when to search the change log or false to indicate 'from the start'
-     * @return int The number of entries available (0 can be used to evalaute as false, >0 for true)
-     *
-    public static function hasUpdates( $priv = 0, $dateFrom = false )
-    {
-        $count = Doctrine_Query::create()
-        ->select( 'COUNT(cl.id)' )
-        ->from( 'ChangeLog cl' )
-        ->where( 'cl.visibility <= ?', $priv );
-    
-        if( $dateFrom !== false )
-            $count->andWhere( 'cl.created_at >= ?', $dateFrom );
-    
-        return $count->fetchOne( null, Doctrine_Core::HYDRATE_SINGLE_SCALAR );
-    }
-    */
-}
diff --git a/application/Repositories/ConsoleServerConnection.php b/application/Repositories/ConsoleServerConnection.php
deleted file mode 100644
index fc1e434f4..000000000
--- a/application/Repositories/ConsoleServerConnection.php
+++ /dev/null
@@ -1,15 +0,0 @@
-= CURRENT_DATE() )";
-    
-    /**
-     * DQL for selecting customers that are active (i.e. not suspended)
-     *
-     * @var string DQL for selecting customers that are active (i.e. not suspended)
-     */
-    const DQL_CUST_ACTIVE = "c.status IN ( 1, 2 )";
-    
-    /**
-     * DQL for selecting all customers except for internal / dummy customers
-     *
-     * @var string DQL for selecting all customers except for internal / dummy customers
-     */
-    const DQL_CUST_EXTERNAL = "c.type != 3";
-    
-    /**
-     * DQL for selecting all trafficing customers
-     *
-     * @var string DQL for selecting all trafficing customers
-     */
-    const DQL_CUST_TRAFFICING = "c.type != 2";
-    
-    
-    /**
-     * Utility function to provide a count of different customer types as `type => count`
-     * where type is as defined in Entities\Customer::$CUST_TYPES_TEXT
-     *
-     * @return array Number of customers of each customer type as `[type] => count`
-     */
-    public function getTypeCounts()
-    {
-        $atypes = $this->getEntityManager()->createQuery(
-            "SELECT c.type AS ctype, COUNT( c.type ) AS cnt FROM Entities\\Customer c
-                WHERE " . self::DQL_CUST_CURRENT . " AND " . self::DQL_CUST_ACTIVE . "
-                GROUP BY c.type"
-        )->getArrayResult();
-        
-        $types = [];
-        foreach( $atypes as $t )
-            $types[ $t['ctype'] ] = $t['cnt'];
-    
-        return $types;
-    }
-    
-    
-    /**
-     * Utility function to provide a array of all active and current customers.
-     *
-     * @param bool $asArray If `true`, return an associative array, else an array of Customer objects
-     * @param bool $trafficing If `true`, only include trafficing customers (i.e. no associates)
-     * @param bool $externalOnly If `true`, only include external customers (i.e. no internal types)
-     * @return array
-     */
-    public function getCurrentActive( $asArray = false, $trafficing = false, $externalOnly = false )
-    {
-        $dql = "SELECT c FROM \\Entities\\Customer c
-                WHERE " . self::DQL_CUST_CURRENT . " AND " . self::DQL_CUST_ACTIVE;
-
-        if( $trafficing )
-            $dql .= " AND " . self::DQL_CUST_TRAFFICING;
-        
-        if( $externalOnly )
-            $dql .= " AND " . self::DQL_CUST_EXTERNAL;
-            
-        $dql .= " ORDER BY c.name ASC";
-        
-        $custs = $this->getEntityManager()->createQuery( $dql );
-        
-        return $asArray ? $custs->getArrayResult() : $custs->getResult();
-    }
-    
-    
-    /**
-     * Return an array of all customer names where the array key is the customer id.
-     *
-     * @return array An array of all customer names with the customer id as the key.
-     */
-    public function getNames()
-    {
-        $acusts = $this->getEntityManager()->createQuery(
-            "SELECT c.id AS id, c.name AS name FROM Entities\\Customer c"
-        )->getResult();
-        
-        $customers = [];
-        foreach( $acusts as $c )
-            $customers[ $c['id'] ] = $c['name'];
-        
-        return $customers;
-    }
-    
-    /**
-     * Return an array of the must recent customers (who are current,
-     * external, active and trafficing).
-     *
-     * @param $limit int The number of customers to get
-     * @return array An array of all customer names with the customer id as the key.
-     */
-    public function getRecent( $limit = 3 )
-    {
-        return $this->getEntityManager()->createQuery(
-                "SELECT c
-                 FROM \\Entities\\Customer c
-                 WHERE " . self::DQL_CUST_CURRENT . " AND " . self::DQL_CUST_ACTIVE . "
-                     AND " . self::DQL_CUST_EXTERNAL . " AND " . self::DQL_CUST_TRAFFICING . "
-                ORDER BY c.datejoin DESC"
-            )
-            ->setMaxResults( $limit )
-            ->useResultCache( true, 3600 )
-            ->getResult();
-    }
-    
-    /**
-     * Return an array of the customer's peers as listed in the `PeeringManager`
-     * table.
-     *
-     * @param $cid int The customer ID
-     * @return array An array of all the customer's PeeringManager entries
-     */
-    public function getPeers( $cid )
-    {
-        $tmpPeers = $this->getEntityManager()->createQuery(
-            "SELECT pm.id AS id, c.id AS custid, p.id AS peerid,
-                pm.email_last_sent AS email_last_sent, pm.emails_sent AS emails_sent,
-                pm.peered AS peered, pm.rejected AS rejected, pm.notes AS notes,
-                pm.created AS created, pm.updated AS updated
-        
-             FROM \\Entities\\PeeringManager pm
-                 LEFT JOIN pm.Customer c
-                 LEFT JOIN pm.Peer p
-        
-             WHERE c.id = ?1"
-        )
-        ->setParameter( 1, $cid )
-        ->getArrayResult();
-
-        $peers = [];
-        foreach( $tmpPeers as $p )
-            $peers[ $p['peerid'] ] = $p;
-        
-        return $peers;
-    }
-    
-    
-    /**
-     * Utility function to load all customers suitable for inclusion in the peering manager
-     *
-     */
-    public function getForPeeringManager()
-    {
-        $customers = $this->getEntityManager()->createQuery(
-                "SELECT c
-        
-                 FROM \\Entities\\Customer c
-
-                 WHERE " . self::DQL_CUST_ACTIVE . " AND " . self::DQL_CUST_CURRENT . "
-                     AND " . self::DQL_CUST_EXTERNAL . " AND " . self::DQL_CUST_TRAFFICING . "
-
-                ORDER BY c.name ASC"
-        
-            )->getResult();
-        
-    
-        $custs = array();
-    
-        foreach( $customers as $c )
-        {
-            $custs[ $c->getAutsys() ] = [];
-    
-            $custs[ $c->getAutsys() ]['id']            = $c->getId();
-            $custs[ $c->getAutsys() ]['name']          = $c->getName();
-            $custs[ $c->getAutsys() ]['shortname']     = $c->getShortname();
-            $custs[ $c->getAutsys() ]['autsys']        = $c->getAutsys();
-            $custs[ $c->getAutsys() ]['maxprefixes']   = $c->getMaxprefixes();
-            $custs[ $c->getAutsys() ]['peeringemail']  = $c->getPeeringemail();
-            $custs[ $c->getAutsys() ]['peeringpolicy'] = $c->getPeeringpolicy();
-    
-            $custs[ $c->getAutsys() ]['vlaninterfaces'] = array();
-    
-            foreach( $c->getVirtualInterfaces() as $vi )
-            {
-                foreach( $vi->getVlanInterfaces() as $vli )
-                {
-                    if( !isset( $custs[ $c->getAutsys() ]['vlaninterfaces'][ $vli->getVlan()->getNumber() ] ) )
-                    {
-                        $custs[ $c->getAutsys() ]['vlaninterfaces'][ $vli->getVlan()->getNumber() ] = [];
-                        $cnt = 0;
-                    }
-                    else
-                        $cnt = count( $custs[ $c->getAutsys() ]['vlaninterfaces'][ $vli->getVlan()->getNumber() ] );
-                        
-                    $custs[ $c->getAutsys() ]['vlaninterfaces'][ $vli->getVlan()->getNumber() ][ $cnt ]['ipv4enabled'] = $vli->getIpv4enabled();
-                    $custs[ $c->getAutsys() ]['vlaninterfaces'][ $vli->getVlan()->getNumber() ][ $cnt ]['ipv6enabled'] = $vli->getIpv6enabled();
-                    $custs[ $c->getAutsys() ]['vlaninterfaces'][ $vli->getVlan()->getNumber() ][ $cnt ]['rsclient']    = $vli->getRsclient();
-                }
-            }
-                         
-        }
-                        
-        return $custs;
-    }
-                        
-    
-}
diff --git a/application/Repositories/CustomerEquipment.php b/application/Repositories/CustomerEquipment.php
deleted file mode 100644
index ce0c5e9f0..000000000
--- a/application/Repositories/CustomerEquipment.php
+++ /dev/null
@@ -1,15 +0,0 @@
-getEntityManager()->createQuery( "SELECT m FROM Entities\\Meeting m" )->getResult();
-        
-        $meetings = [];
-        foreach( $objs as $o )
-            $meetings[ $o->getId() ] = $o->getTitle();
-    
-        return $meetings;
-    }
-    
-    /*
-     * OLD DOCTRINE1 CODE FOR POSSIBLE PORTING
-    
-     * Return the futuremost available meeting as an array
-     *
-     * This is essentially a search for an upcoming meeting and returns it if found.
-     *
-     * @return array|bool The Meeting object array or false
-     *
-    public static function getUpcomingMeeting()
-    {
-        return Doctrine_Query::create()
-                 ->from( 'Meeting m' )
-                 ->where( 'm.date > ?', date( 'Y-m-d' ) )
-                 ->orderBy( 'm.date DESC' )
-                 ->setHydrationMode( Doctrine_Core::HYDRATE_ARRAY )
-                 ->fetchOne();
-    }
-     */
-}
diff --git a/application/Repositories/MeetingItem.php b/application/Repositories/MeetingItem.php
deleted file mode 100644
index d5a5ebb46..000000000
--- a/application/Repositories/MeetingItem.php
+++ /dev/null
@@ -1,15 +0,0 @@
- array(2) {
-     *       [4] => array(9) {
-     *         ["id"] => string(1) "1"
-     *           ["protocol"] => string(1) "4"
-     *           ["network"] => string(13) "193.242.111.0"
-     *           ["masklen"] => string(2) "25"
-     *           ["rs1address"] => string(13) "193.242.111.8"
-     *           ["rs2address"] => string(13) "193.242.111.9"
-     *           ["dnsfile"] => string(44) "/opt/bind/zones/reverse-vlan-10-ipv4.include"
-     *           ["Vlan"] => array(5) {
-     *             ["id"] => string(1) "2"
-     *             ["name"] => string(15) "Peering VLAN #1"
-     *             ["number"] => string(2) "10"
-     *             ["rcvrfname"] => string(0) ""
-     *             ["notes"] => string(0) ""
-     *           }
-     *       }
-     *       [6] => array(9) {
-     *         ["id"] => string(1) "2"
-     *           ["vlanid"] => string(1) "2"
-     *           ["protocol"] => string(1) "6"
-     *           ["network"] => string(16) "2001:07F8:0018::"
-     *           ["masklen"] => string(2) "64"
-     *           ["rs1address"] => string(14) "2001:7f8:18::8"
-     *           ["rs2address"] => string(14) "2001:7f8:18::9"
-     *           ["dnsfile"] => string(44) "/opt/bind/zones/reverse-vlan-10-ipv6.include"
-     *           ["Vlan"] => array(5) {
-     *             ["id"] => string(1) "2"
-     *             ["name"] => string(15) "Peering VLAN #1"
-     *             ["number"] => string(2) "10"
-     *             ["rcvrfname"] => string(0) ""
-     *             ["notes"] => string(0) ""
-     *           }
-     *         }
-     *     }
-     *
-     * @return array As described above
-     */
-    
-    public function asVlanProtoArray()
-    {
-        $networkInfo = $this->getEntityManager()->createQuery(
-                "SELECT n, v
-                FROM \\Entities\\NetworkInfo n
-                LEFT JOIN n.Vlan v"
-            )->useResultCache( true, 3600 )
-            ->getArrayResult();
-
-        $data = array();
-        foreach( $networkInfo as $ni )
-        {
-            $data[ $ni['Vlan']['id'] ][ $ni['protocol'] ] = $ni;
-        }
-        
-        return $data;
-    }
-
-}
diff --git a/application/Repositories/PeeringManager.php b/application/Repositories/PeeringManager.php
deleted file mode 100644
index ccf3bb4b3..000000000
--- a/application/Repositories/PeeringManager.php
+++ /dev/null
@@ -1,15 +0,0 @@
-getVirtualInterfaces() as $vi )
-        {
-            foreach( $vi->getPhysicalInterfaces() as $pi )
-            {
-                if( $pi->getMonitorIndex() > $maxMonIndex )
-                    $maxMonIndex = $pi->getMonitorIndex();
-            }
-        }
-        
-        return $maxMonIndex + 1;
-    }
-}
diff --git a/application/Repositories/RSDroppedPrefix.php b/application/Repositories/RSDroppedPrefix.php
deleted file mode 100644
index 80eca27c5..000000000
--- a/application/Repositories/RSDroppedPrefix.php
+++ /dev/null
@@ -1,15 +0,0 @@
-getEntityManager()->createQuery( $dql )
-            ->useResultCache( true, 3600, $key )
-            ->getResult();
-    }
-    
-    /**
-     * Return an array of all switch names where the array key is the switch id
-     *
-     * @param bool $active If `true`, return only active switches
-     * @param int $type If `0`, all types otherwise limit to specific type
-     * @return array An array of all switch names with the switch id as the key.
-     */
-    public function getNames( $active = false, $type = 0 )
-    {
-        $switches = [];
-        foreach( $this->getAndCache( $active, $type ) as $a )
-            $switches[ $a->getId() ] = $a->getName();
-    
-        return $switches;
-    }
-    
-    
-    public function getConfiguration( $switchid = null, $vlanid = null )
-    {
-        $q =
-            "SELECT s.name AS switchname, s.id AS switchid,
-                    sp.name AS portname,
-                    pi.speed AS speed, pi.duplex AS duplex, pi.status AS portstatus,
-                    c.name AS customer, c.id AS custid, c.autsys AS asn,
-                    vli.rsclient AS rsclient,
-                    v.name AS vlan,
-                    ipv4.address AS ipv4address, ipv6.address AS ipv6address
-        
-            FROM \\Entities\\VlanInterface vli
-                JOIN vli.IPv4Address ipv4
-                LEFT JOIN vli.IPv6Address ipv6
-                LEFT JOIN vli.Vlan v
-                LEFT JOIN vli.VirtualInterface vi
-                LEFT JOIN vi.Customer c
-                LEFT JOIN vi.PhysicalInterfaces pi
-                LEFT JOIN pi.SwitchPort sp
-                LEFT JOIN sp.Switcher s
-            
-            WHERE 1=1 ";
-        
-        if( $switchid !== null )
-            $q .= 'AND s.id = ' . intval( $switchid ) . ' ';
-        
-        if( $vlanid !== null )
-            $q .= 'AND v.id = ' . intval( $vlanid ) . ' ';
-                            
-        $q .= "ORDER BY customer ASC";
-        
-        return $this->getEntityManager()->createQuery( $q )->getArrayResult();
-    }
-    
-}
diff --git a/application/Repositories/Traffic95th.php b/application/Repositories/Traffic95th.php
deleted file mode 100644
index 4e24f508c..000000000
--- a/application/Repositories/Traffic95th.php
+++ /dev/null
@@ -1,63 +0,0 @@
-from( 'Traffic95th tm')
-        ->select( 'COUNT( tm.datetime )' )
-        ->where( "tm.datetime >= ?" )
-        ->andWhere( "tm.datetime < ?" )
-        ->andWhere( 'tm.cust_id = ?' )
-        ->execute( array( $start, $end, $custid ), Doctrine_Core::HYDRATE_SINGLE_SCALAR );
-    
-        if( $count > 20 )
-        {
-            // we want the 95% percentile
-            $index = (int)floor( $count * 0.05 );
-    
-            return Doctrine_Query::create()
-            ->from( 'Traffic95TH tm' )
-            ->select( 'tm.max' )
-            ->where( "tm.datetime >= ?", $start )
-            ->andWhere( "tm.datetime < ?", $end )
-            ->andWhere( 'tm.cust_id = ?', $custid )
-            ->limit( 1 )
-            ->offset( $index )
-            ->orderBy( 'tm.datetime DESC' )
-            ->execute( null, Doctrine_Core::HYDRATE_SINGLE_SCALAR );
-    
-        }
-        else
-        {
-            return 0;
-        }
-    }
-    */
-}
diff --git a/application/Repositories/Traffic95thMonthly.php b/application/Repositories/Traffic95thMonthly.php
deleted file mode 100644
index b66ba39c3..000000000
--- a/application/Repositories/Traffic95thMonthly.php
+++ /dev/null
@@ -1,15 +0,0 @@
- array(28) {
-     *          ["day"] => object(DateTime)#286 (3) {
-     *              ....
-     *          }
-     *          ["category"] => string(4) "bits"
-     *          ["day_avg_in"] => string(8) "32732583"
-     *          ...
-     *          ["year_tot_out"] => string(16) "1430530473953106"
-     *          ["id"] => string(6) "156931"
-     *          ["Customer"] => array(31) {
-     *            ["name"] => string(10) "A Name"
-     *            ["type"] => int(1)
-     *            ...
-     *            ["id"] => int(4)
-     *          }
-     *        }
-     *        [1] => array(28) {
-     *          ["day"] => object(DateTime)#292 (3) {
-     *              ...
-     *          }
-     *          ["category"] => string(4) "bits"
-     *          ...
-     *        }
-     *      }
-     *
-     *
-     * @see \INEX_Mrtg::$CATEGORIES
-     * @param \DateTime $day The day to load records for
-     * @param string $category The category of records to load (one of \INEX_Mrtg::$CATEGORIES)
-     * @return array An array of all switch objects
-     */
-    public function load( $day, $category )
-    {
-        return $this->getEntityManager()->createQuery(
-                "SELECT td, c
-                 FROM Entities\\TrafficDaily td
-                 LEFT JOIN td.Customer c
-                 WHERE td.day = ?1 AND td.category = ?2"
-            )
-            ->setParameter( 1, $day )
-            ->setParameter( 2, $category )
-            ->getArrayResult();
-    }
-    
-    
-
-    /**
-     * Delete all entries for a given day
-     *
-     * @param string $day The day to delete all entries for
-     * @return int The number of entries removed
-     */
-    public function deleteForDay( $day )
-    {
-        return $this->getEntityManager()->createQuery( "DELETE \\Entities\\TrafficDaily td WHERE td.day = ?1" )
-            ->setParameter( 1, $day )
-            ->execute();
-    }
-    
-    
-    /**
-     * Get the given number of recent traffic stats for a given customer
-     *
-     * @param \Entities\Customer $customer The customer to get entries for
-     * @param int $rows The number of entries for fetch
-     * @param string $category The traffic category (bits, packets, etc)
-     * @return array The entries found
-     */
-    public function getAsArray( $customer, $rows, $category )
-    {
-        return $this->getEntityManager()->createQuery(
-                "SELECT td
-                 FROM Entities\\TrafficDaily td
-                 WHERE td.Customer = ?1 AND td.category = ?2
-                 ORDER BY td.day DESC"
-            )
-            ->setParameter( 1, $customer )
-            ->setParameter( 2, $category )
-            ->setMaxResults( $rows )
-            ->getArrayResult();
-    }
-    
-    
-}
diff --git a/application/Repositories/User.php b/application/Repositories/User.php
deleted file mode 100644
index 033e1bd1d..000000000
--- a/application/Repositories/User.php
+++ /dev/null
@@ -1,73 +0,0 @@
- array(6) {
-     *         ["attribute"] => string(18) "auth.last_login_at"
-     *         ["lastlogin"] => string(10) "1338329771"
-     *         ["username"]  => string(4) "auser"
-     *         ["email"]     => string(12) "auser@example.com"
-     *         ["cust_name"] => string(4) "INEX"
-     *         ["cust_id"]   => string(2) "15"
-     *     }
-     *
-     *
-     * @param int $limit Set this to limit the results to the last `$limit` users
-     * @return array Users with their last login time ordered from most recent to oldest.
-     */
-    public function getLastLogins( $limit = null )
-    {
-        $q = $this->getEntityManager()->createQuery(
-                "SELECT up.attribute AS attribute, up.value AS lastlogin, u.username AS username,
-                        u.email AS email, c.name AS cust_name, c.id AS cust_id
-                    FROM \\Entities\\UserPreference up
-                        JOIN up.User u
-                        JOIN u.Customer c
-                    WHERE up.attribute = ?1
-                    ORDER BY up.value DESC"
-            )
-            ->setParameter( 1, 'auth.last_login_at' );
-        
-        if( $limit != null && is_numeric( $limit ) && $limit > 0 )
-            $q->setMaxResults( $limit );
-        
-        return $q->getScalarResult();
-    }
-
-
-
-    /**
-     * Return an array of users subscribed (or not) to a given mailing list
-     *
-     * @param string $list The mailing list handle
-     * @param int Set to '0' to get a list of users not subscribed
-     * @return array Array of array of emails
-     */
-    public function getMailingListSubscribers( $list, $subscribed = 1 )
-    {
-        $sql = "SELECT u.email AS email, u.password AS password
-                    FROM \\Entities\\User u LEFT JOIN u.Preferences up
-                    WHERE up.attribute = ?1 AND up.value = ?2";
-        
-        return $this->getEntityManager()->createQuery( $sql )
-            ->setParameter( 1, "mailinglist.{$list}.subscribed" )
-            ->setParameter( 2, $subscribed )
-            ->getScalarResult();
-    }
-}
diff --git a/application/Repositories/UserPreference.php b/application/Repositories/UserPreference.php
deleted file mode 100644
index d7a8678d2..000000000
--- a/application/Repositories/UserPreference.php
+++ /dev/null
@@ -1,15 +0,0 @@
- count`
-     * where type is as defined in Entities\Customer::$CUST_TYPES_TEXT
-     *
-     * @return array Number of customers of each customer type as `[type] => count`
-     */
-    public function getByLocation()
-    {
-        return $ints = $this->getEntityManager()->createQuery(
-            "SELECT vi.id AS id, pi.speed AS speed, sw.infrastructure AS infrastructure, l.name AS locationname
-                FROM Entities\\VirtualInterface vi
-                    JOIN vi.Customer c
-                    JOIN vi.PhysicalInterfaces pi
-                    JOIN pi.SwitchPort sp
-                    JOIN sp.Switcher sw
-                    JOIN sw.Cabinet ca
-                    JOIN ca.Location l
-                WHERE
-                    " . Customer::DQL_CUST_EXTERNAL
-        )->getArrayResult();
-    }
-}
diff --git a/application/Repositories/Vlan.php b/application/Repositories/Vlan.php
deleted file mode 100644
index eb3054bb8..000000000
--- a/application/Repositories/Vlan.php
+++ /dev/null
@@ -1,195 +0,0 @@
-getEntityManager()->createQuery(
-                "SELECT v FROM Entities\\Vlan v"
-            )
-            ->useResultCache( true, 3600, self::ALL_CACHE_KEY )
-            ->getResult();
-    }
-    
-    /**
-     * Return an array of all VLAN names where the array key is the VLAN id (**not tag**).
-     *
-     * @return array An array of all VLAN names with the vlan id as the key.
-     */
-    public function getNames()
-    {
-        $vlans = [];
-        foreach( $this->getAndCache() as $a )
-            $vlans[ $a->getId() ] = $a->getName();
-    
-        return $vlans;
-    }
-    
-    /**
-     * Return all active, trafficing and external VLAN interfaces on a given VLAN for a given protocol
-     * (including customer details)
-     *
-     * Here's an example of the return:
-     *
-     *     array(56) {
-     *         [0] => array(21) {
-     *            ["ipv4enabled"] => bool(true)
-     *            ["ipv4hostname"] => string(17) "inex.woodynet.net"
-     *            ["ipv6enabled"] => bool(true)
-     *            ["ipv6hostname"] => string(20) "inex-v6.woodynet.net"
-     *            ....
-     *            ["id"] => int(109)
-     *                ["Vlan"] => array(5) {
-     *                      ["name"] => string(15) "Peering VLAN #1"
-     *                      ...
-     *                }
-     *                ["VirtualInterface"] => array(7) {
-     *                    ["id"] => int(39)
-     *                    ...
-     *                }
-     *                ["Customer"] => array(31) {
-     *                    ["name"] => string(25) "Packet Clearing House DNS"
-     *                   ...
-     *                }
-     *            }
-     *         [1] => array(21) {
-     *            ...
-     *            }
-     *        ...
-     *     }
-     *
-     * @param int $vid The VLAN ID to find interfaces on
-     * @param int $protocol The protocol to find interfaces on ( `4` or `6`)
-     * @param bool $forceDb Set to true to ignore the cache and force the query to the database
-     * @return An array as described above
-     * @throws \INEX_Exception Thrown if an invalid protocol is specified
-     */
-    public function getInterfaces( $vid, $protocol, $forceDb = false )
-    {
-        if( !in_array( $protocol, [ 4, 6 ] ) )
-            throw new \INEX_Exception( 'Invalid protocol' );
-        
-        $interfaces = $this->getEntityManager()->createQuery(
-                "SELECT vli, v, vi, c
-         
-                FROM \\Entities\\VlanInterface vli
-                    LEFT JOIN vli.Vlan v
-                    LEFT JOIN vli.VirtualInterface vi
-                    LEFT JOIN vi.Customer c
-        
-                WHERE
-                    
-                    " . Customer::DQL_CUST_CURRENT . "
-                    AND " . Customer::DQL_CUST_TRAFFICING . "
-                    AND " . Customer::DQL_CUST_EXTERNAL . "
-                    AND c.activepeeringmatrix = 1
-                    AND v.id = ?1
-                    AND vli.ipv{$protocol}enabled = 1
-                    
-                GROUP BY vi.Customer
-                ORDER BY c.autsys ASC"
-            )
-            ->setParameter( 1, $vid );
-        
-        if( !$forceDb )
-            $interfaces->useResultCache( true, 3600 );
-        
-        return $interfaces->getArrayResult();
-    }
-    
-    /**
-     * Return all active, trafficing and external customers on a given VLAN for a given protocol
-     * (indexed by ASN)
-     *
-     * Here's an example of the return:
-     *
-     *     array(56) {
-     *         [42] => array(5) {
-     *             ["autsys"] => int(42)
-     *             ["name"] => string(25) "Packet Clearing House DNS"
-     *             ["shortname"] => string(10) "pchanycast"
-     *             ["rsclient"] => bool(true)
-     *             ["custid"] => int(72)
-     *         }
-     *         [112] => array(5) {
-     *             ["autsys"] => int(112)
-     *             ...
-     *         }
-     *         ...
-     *     }
-     *
-     * @see getInterfaces()
-     * @param int $vid The VLAN ID to find interfaces on
-     * @param int $protocol The protocol to find interfaces on ( `4` or `6`)
-     * @param bool $forceDb Set to true to ignore the cache and force the query to the database
-     * @return An array as described above
-     * @throws \INEX_Exception Thrown if an invalid protocol is specified
-     */
-    public function getCustomers( $vid, $protocol, $forceDb = false )
-    {
-        $key = "vlan_customers_{$vid}_{$protocol}";
-        
-        if( !$forceDb && ( $custs = \Zend_Registry::get( 'd2cache' )->fetch( $key ) ) )
-            return $custs;
-        
-        $acusts = $this->getInterfaces( $vid, $protocol, $forceDb );
-        
-        $custs = [];
-    
-        foreach( $acusts as $c )
-        {
-            $custs[ $c['VirtualInterface']['Customer']['autsys'] ] = [];
-            $custs[ $c['VirtualInterface']['Customer']['autsys'] ]['autsys']    = $c['VirtualInterface']['Customer']['autsys'];
-            $custs[ $c['VirtualInterface']['Customer']['autsys'] ]['name']      = $c['VirtualInterface']['Customer']['name'];
-            $custs[ $c['VirtualInterface']['Customer']['autsys'] ]['shortname'] = $c['VirtualInterface']['Customer']['shortname'];
-            $custs[ $c['VirtualInterface']['Customer']['autsys'] ]['rsclient']  = $c['rsclient'];
-            $custs[ $c['VirtualInterface']['Customer']['autsys'] ]['custid']    = $c['VirtualInterface']['Customer']['id'];
-        }
-        
-        \Zend_Registry::get( 'd2cache' )->save( $key, $custs, 86400 );
-            
-        return $custs;
-    }
-    
-    /**
-     * Tempory INEX function until I have a better way of doing this. Used by the
-     * `PeeringManagerController`. FIXME.
-     */
-    public function getPeeringVLANs()
-    {
-        return $this->getEntityManager()->createQuery(
-                "SELECT v
-         
-                    FROM \\Entities\\Vlan v
-            
-                    WHERE
-            
-                        v.number IN ( 10, 12 )
-    
-                ORDER BY v.number ASC"
-            )
-            ->getResult();
-    }
-    
-}
diff --git a/application/Repositories/VlanInterface.php b/application/Repositories/VlanInterface.php
deleted file mode 100644
index ffd71224c..000000000
--- a/application/Repositories/VlanInterface.php
+++ /dev/null
@@ -1,15 +0,0 @@
-
- * @category   INEX
- * @package    INEX_Controller
- * @copyright  Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd
- * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
- */
-class AdminController extends INEX_Controller_AuthRequiredAction
-{
-
-    public function preDispatch()
-    {
-        if( $this->getUser()->getPrivs() != \Entities\User::AUTH_SUPERUSER )
-	    {
-	        $this->getLogger()->notice( "{$this->getUser()->getUsername()} tried to access the admin controller without sufficient permissions" );
-	        $this->redirectAndEnsureDie( 'error/insufficient-permissions' );
-	    }
-    }
-
-
-    /**
-     * The default action - show the home page
-     */
-    public function indexAction()
-    {
-        $this->_publicPeeringGraphs();
-        $this->_dashboardStats();
-    }
-
-    
-    /**
-     * Get public peering graphs
-     *
-     */
-    private function _publicPeeringGraphs()
-    {
-        // only do this once every five minutes
-        if( $admin_home_stats = $this->getD2Cache()->fetch( 'admin_home_stats' ) )
-        {
-            $this->view->graphs = $admin_home_stats['graphs'];
-            $this->view->stats  = $admin_home_stats['stats'];
-        }
-        else
-        {
-            $admin_home_stats = [];
-            
-            foreach( $this->_options['mrtg']['traffic_graphs'] as $g )
-            {
-                $p = explode( '::', $g );
-                $graphs[$p[0]] = $p[1];
-                $images[]      = $p[0];
-                
-                $mrtg = new INEX_Mrtg(
-                    $this->_options['mrtg']['path']
-                        . DIRECTORY_SEPARATOR . 'ixp_peering-' . $p[0]
-                        . '-' . INEX_Mrtg::CATEGORY_BITS . '.log'
-                );
-                
-                $stats[$p[0]] = $mrtg->getValues( INEX_Mrtg::PERIOD_MONTH, INEX_Mrtg::CATEGORY_BITS );
-            }
-            
-            $admin_home_stats['graphs'] = $this->view->graphs     = $graphs;
-            $admin_home_stats['stats']  = $this->view->stats      = $stats;
-            
-            $this->getD2Cache()->save( 'admin_home_stats', $admin_home_stats, 300 );
-        }
-    }
-
-    /**
-     * Get type counts
-     *
-     */
-    private function _dashboardStats()
-    {
-        // only do this once every 60 minutes
-        if( !( $admin_home_ctypes = $this->getD2Cache()->fetch( 'admin_home_ctypes' ) ) )
-        {
-            $admin_home_ctypes['types'] = $this->getD2EM()->getRepository( 'Entities\\Customer' )->getTypeCounts();
-            
-            $ints = $this->getD2EM()->getRepository( 'Entities\\VirtualInterface' )->getByLocation();
-            
-            $speeds = array();
-            $bylocation = array();
-            $bylan = array();
-            foreach( $ints as $int )
-            {
-                if( !isset( $bylocation[ $int['locationname'] ] ) )
-                    $bylocation[ $int['locationname'] ] = array();
-
-                if( !isset( $bylan[ $int['infrastructure'] ] ) )
-                    $bylan[ $int['infrastructure'] ] = array();
-
-                if( !isset( $speeds[ $int['speed'] ] ) )
-                    $speeds[ $int['speed'] ] = 1;
-                else
-                    $speeds[ $int['speed'] ]++;
-                                    
-                if( !isset( $bylocation[ $int['locationname'] ][ $int['speed'] ] ) )
-                    $bylocation[ $int['locationname'] ][ $int['speed'] ] = 1;
-                else
-                    $bylocation[ $int['locationname'] ][ $int['speed'] ] = $bylocation[ $int['locationname'] ][ $int['speed'] ] + 1;
-
-                if( !isset( $bylan[ $int['infrastructure'] ][ $int['speed'] ] ) )
-                    $bylan[ $int['infrastructure'] ][ $int['speed'] ] = 1;
-                else
-                    $bylan[ $int['infrastructure'] ][ $int['speed'] ] = $bylan[ $int['infrastructure'] ][ $int['speed'] ] + 1;
-            }
-            
-            ksort( $speeds, SORT_NUMERIC );
-            $this->view->speeds      = $admin_home_ctypes['speeds']      = $speeds;
-            $this->view->bylocation  = $admin_home_ctypes['bylocation']  = $bylocation;
-            $this->view->bylan       = $admin_home_ctypes['bylan']       = $bylan;
-            
-            $this->getD2Cache()->save( 'admin_home_ctypes', $admin_home_ctypes, 3600 );
-        }
-        
-        $this->view->ctypes      = $admin_home_ctypes['types'];
-        $this->view->speeds      = $admin_home_ctypes['speeds'];
-        $this->view->bylocation  = $admin_home_ctypes['bylocation'];
-        $this->view->bylan       = $admin_home_ctypes['bylan'];
-    }
-    
-    public function staticAction()
-    {
-        $page = $this->_request->getParam( 'page', null );
-
-        if( $page == null )
-            return( $this->_redirect( 'index' ) );
-
-        // does the requested static page exist? And if so, display it
-        if( preg_match( '/^[a-zA-Z0-9\-]+$/', $page ) > 0
-                && file_exists( APPLICATION_PATH . "/views/admin/static/{$page}.tpl" ) )
-        {
-            $this->view->display( "admin/static/{$page}.tpl" );
-        }
-        else
-        {
-            $this->session->message = new INEX_Message(
-                "The requested page was not found.",
-                INEX_Message::MESSAGE_TYPE_ERROR
-            );
-            $this->_redirect( 'index' );
-        }
-    }
-}
-
diff --git a/application/controllers/AuthController.php b/application/controllers/AuthController.php
deleted file mode 100644
index 6d08b948a..000000000
--- a/application/controllers/AuthController.php
+++ /dev/null
@@ -1,138 +0,0 @@
-
- * @category   INEX
- * @package    INEX_Controller
- * @copyright  Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd
- * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
- */
-class AuthController extends INEX_Controller_Action
-{
-    use OSS_Controller_Trait_Auth;
-
-    /**
-     * Return the appropriate login form for your application
-     */
-    protected function _getFormLogin()
-    {
-        return new INEX_Form_Auth_Login();
-    }
-
-    /**
-     * Return the appropriate lost password form for your application
-     */
-    protected function _getFormLostPassword()
-    {
-        return new INEX_Form_Auth_LostPassword();
-    }
-
-    /**
-     * Return the appropriate reset password form for your application
-     */
-    protected function _getFormResetPassword()
-    {
-        return new INEX_Form_Auth_ResetPassword();
-    }
-    
-    /**
-     * Return the appropriate lost username form for your application
-     */
-    protected function _getFormLostUsername()
-    {
-        return new INEX_Form_Auth_LostUsername();
-    }
-    
-    
-    
-    
-    /**
-     * Create a Drupal login button for admin users
-     */
-    protected function drupalLoginAction()
-    {
-        // let's be clear - you have to be an INEX member to access this!
-        if( !$this->getAuth()->hasIdentity() || $this->getUser()->getPrivs() != \Entities\User::AUTH_SUPERUSER )
-            $this->redirectAndEnsureDie( 'error/insufficient-privileges' );
-    }
-
-    
-    
-    /**
-     * This function is called just before `switchUserAction()` processes anything.
-     *
-     * @return bool True unless you want the switch to fail.
-     */
-    protected function _switchUserPreCheck()
-    {
-        if( $this->getUser()->getPrivs() != \Entities\User::AUTH_SUPERUSER )
-        {
-            $this->getLogger()->notice( 'User ' . $this->getUser()->getUsername() . ' illegally tried to switch to user with ID '
-                . $this->_getParam( 'id', '[unknown]' )
-            );
-            
-            $this->addMessage(
-                'You are not allowed to switch users! This attempt has been logged and the administrators notified.',
-                OSS_Message::ERROR
-            );
-            
-            $this->redirectAndEnsureDie( '' );
-        }
-        
-        return true;
-    }
-
-    /**
-     * This function is called after `switchUserAction()` loads the requested user object.
-     *
-     * @param \Entities\User $nuser The user to switch to
-     * @return bool True unless you want the switch to fail.
-     */
-    protected function _switchUserCheck( $nuser )
-    {
-        return true;
-    }
-    
-    /**
-     * This function is called just before `switchUserBackAction()` actually switches
-     * the user back.
-     *
-     * @param \Entities\User $subUser The current user we have switched to (substituted to)
-     * @param \Entities\User $origUser The original user that we switched from
-     * @return bool|array False if you want the switch back to fail.
-     */
-    protected function _switchUserBackCheck( $subUser, $origUser )
-    {
-        // record current user customer ID
-        $custid = $this->getUser()->custid;
-        
-        $params['url'] = 'customer/overview/id/' . $subUser->getCustomer()->getId();
-        
-        return $params;
-    }
-    
-}
diff --git a/application/controllers/CabinetController.php b/application/controllers/CabinetController.php
deleted file mode 100644
index 432d4c903..000000000
--- a/application/controllers/CabinetController.php
+++ /dev/null
@@ -1,142 +0,0 @@
-
- * @category   INEX
- * @package    INEX_Controller
- * @copyright  Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd
- * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
- */
-class CabinetController extends INEX_Controller_FrontEnd
-{
-    /**
-     * This function sets up the frontend controller
-     */
-    protected function _feInit()
-    {
-        $this->assertPrivilege( \Entities\User::AUTH_SUPERUSER );
-    
-        $this->view->feParams = $this->_feParams = (object)[
-            'entity'        => '\\Entities\\Cabinet',
-            'form'          => 'INEX_Form_Cabinet',
-            'pagetitle'     => 'Cabinets',
-        
-            'titleSingular' => 'Cabinet',
-            'nameSingular'  => 'a cabinet',
-        
-            'defaultAction' => 'list',                    // OPTIONAL; defaults to 'list'
-        
-            'listOrderBy'    => 'name',
-            'listOrderByDir' => 'ASC',
-        
-            'listColumns'    => [
-            
-                'id'        => [ 'title' => 'UID', 'display' => false ],
-            
-                'location'  => [
-                    'title'      => 'Location',
-                    'type'       => self::$FE_COL_TYPES[ 'HAS_ONE' ],
-                    'controller' => 'location',
-                    'action'     => 'view',
-                    'idField'    => 'locationid'
-                ],
-            
-                'name'         => 'Name',
-                'cololocation' => 'Colo Location',
-                'height'       => 'Height'
-            ]
-        ];
-    
-        // display the same information in the view as the list
-        $this->_feParams->viewColumns = array_merge(
-            $this->_feParams->listColumns,
-            [
-                'type'       => 'Type',
-                'notes'      => 'Notes'
-            ]
-        );
-    }
-    
-    
-    /**
-     * Provide array of users for the listAction and viewAction
-     *
-     * @param int $id The `id` of the row to load for `viewAction`. `null` if `listAction`
-     */
-    protected function listGetData( $id = null )
-    {
-        $qb = $this->getD2EM()->createQueryBuilder()
-            ->select( 'c.id as id, c.name as name, c.cololocation as cololocation, c.height AS height,
-                c.type AS type, c.notes AS notes, l.id AS locationid, l.name AS location'
-            )
-        ->from( '\\Entities\\Cabinet', 'c' )
-        ->leftJoin( 'c.Location', 'l' );
-    
-        if( isset( $this->_feParams->listOrderBy ) )
-            $qb->orderBy( $this->_feParams->listOrderBy, isset( $this->_feParams->listOrderByDir ) ? $this->_feParams->listOrderByDir : 'ASC' );
-    
-        if( $id !== null )
-            $qb->andWhere( 'c.id = ?1' )->setParameter( 1, $id );
-    
-        return $qb->getQuery()->getResult();
-    }
-    
-    
-    /**
-     *
-     * @param INEX_Form_Cabinet $form The form object
-     * @param \Entities\Cabinet $object The Doctrine2 entity (being edited or blank for add)
-     * @param bool $isEdit True of we are editing an object, false otherwise
-     * @param array $options Options passed onto Zend_Form
-     * @param string $cancelLocation Where to redirect to if 'Cancal' is clicked
-     * @return void
-     */
-    protected function formPostProcess( $form, $object, $isEdit, $options = null, $cancelLocation = null )
-    {
-        if( $isEdit )
-            $form->getElement( 'locationid' )->setValue( $object->getLocation()->getId() );
-    }
-    
-    
-    /**
-     *
-     * @param INEX_Form_Cabinet $form The form object
-     * @param \Entities\Cabinet $object The Doctrine2 entity (being edited or blank for add)
-     * @param bool $isEdit True of we are editing an object, false otherwise
-     * @return void
-     */
-    protected function addPostValidate( $form, $object, $isEdit )
-    {
-        $object->setLocation(
-            $this->getD2EM()->getRepository( '\\Entities\\Location' )->find( $form->getElement( 'locationid' )->getValue() )
-        );
-    
-        return true;
-    }
-    
-}
-
diff --git a/application/controllers/CliController.php b/application/controllers/CliController.php
deleted file mode 100644
index 188b205c2..000000000
--- a/application/controllers/CliController.php
+++ /dev/null
@@ -1,530 +0,0 @@
-
- * @category   INEX
- * @package    INEX_Controller
- * @copyright  Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd
- * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
- */
-class CliController extends INEX_Controller_Action
-{
-
-    /**
-     * Verbose flag
-     */
-    private $_verbose = false;
-
-
-    public function preDispatch()
-    {
-        //Zend_Controller_Action_HelperBroker::removeHelper('viewRenderer');
-        if ( php_sapi_name() != 'cli' ) die( 'Unauthorised access to action!' );
-
-        $this->_verbose = $this->getFrontController()->getParam( 'verbose' );
-    }
-
-
-    public function postDispatch()
-    {
-        print "\n";
-    }
-
-
-    /**
-     * Demo action to demonstrate CLI action
-     *
-     */
-    public function testAction()
-    {
-        print "This is a demo action.\n";
-    }
-
-
-
-    public function uploadTrafficStatsToDbAction()
-    {
-        // This should only be done once a day and if values already exist for 'today',
-        // just delete them.
-        $day = date( 'Y-m-d' );
-        $this->getD2EM()->getRepository( '\\Entities\\TrafficDaily' )->deleteForDay( $day );
-
-        $custs = $this->getD2EM()->getRepository( '\\Entities\\Customer' )->getCurrentActive( false, true, true );
-
-        foreach( $custs as $cust )
-        {
-            $stats = array();
-
-            foreach( INEX_Mrtg::$CATEGORIES as $category )
-            {
-	            $mrtg = new INEX_Mrtg(
-                    INEX_Mrtg::getMrtgFilePath( $this->_options['mrtg']['path'] . '/members',
-                        'LOG', 'aggregate', $category,
-                        $cust->getShortname()
-                    )
-	            );
-
-                $td = new \Entities\TrafficDaily();
-                $td->setDay( new DateTime( $day ) );
-                $td->setCategory( $category );
-                $td->setCustomer( $cust );
-
-                foreach( INEX_Mrtg::$PERIODS as $name => $period )
-                {
-                    $stats = $mrtg->getValues( $period, $category, false );
-
-                    $fn = "set{$name}AvgIn";  $td->$fn( $stats['averagein']  );
-                    $fn = "set{$name}AvgOut"; $td->$fn( $stats['averageout'] );
-                    $fn = "set{$name}MaxIn";  $td->$fn( $stats['maxin']      );
-                    $fn = "set{$name}MaxOut"; $td->$fn( $stats['maxout']     );
-                    $fn = "set{$name}TotIn";  $td->$fn( $stats['totalin']    );
-                    $fn = "set{$name}TotOut"; $td->$fn( $stats['totalout']   );
-                }
-
-                $this->getD2EM()->persist( $td );
-            }
-            $this->getD2EM()->flush();
-        }
-    }
-
-
-
-    /**
-     * This function looks for members who have changed their traffic patterns significantly
-     * when comparing 'yesterday' to the last month.
-     */
-    public function examineTrafficDeltasAction()
-    {
-        $custs = $this->getD2EM()->getRepository( '\\Entities\\Customer' )->getCurrentActive( false, true, true );
-        
-        $mail = $this->getMailer();
-        $mail->setFrom( $this->_options['cli']['traffic_differentials']['from_email'], $this->_options['cli']['traffic_differentials']['from_name'] )
-             ->setSubject( $this->_options['cli']['traffic_differentials']['subject'] )
-             ->setType( Zend_Mime::MULTIPART_RELATED );
-
-        foreach( $this->_options['cli']['traffic_differentials']['recipients'] as $r )
-            $mail->addTo( $r );
-
-        $mailHtml = $this->view->render( 'customer/email/diff-header.phtml' );
-
-        $numWithExceededThreshold = 0;
-
-        foreach( $custs as $c )
-        {
-            $tds = $this->getD2EM()->getRepository( '\\Entities\\TrafficDaily' )
-                ->getAsArray( $c, $this->_options['cli']['traffic_differentials']['stddev_calc_length'] + 1, INEX_Mrtg::CATEGORY_BITS );
-
-    	    $firstDone = false;
-            $meanIn  = 0.0; $stddevIn  = 0.0;
-            $meanOut = 0.0; $stddevOut = 0.0;
-            $count = 0.0;
-
-            foreach( $tds as $t )
-            {
-    	        if( !$firstDone )
-	            {
-	                $todayAvgIn  = $t['day_avg_in'];
-	                $todayAvgOut = $t['day_avg_out'];
-	                $firstDone = true;
-	                continue;
-	            }
-
-    	        $count     += 1.0;
-                $meanIn    += $t['day_avg_in'];
-                $meanOut   += $t['day_avg_out'];
-            }
-
-            if( $count > 1 )
-            {
-                $meanIn  /= $count;
-                $meanOut /= $count;
-
-                foreach( $tds as $t )
-                {
-                    $stddevIn  += ( $t['day_avg_in']  - $meanIn  ) * ( $t['day_avg_in']  - $meanIn  );
-                    $stddevOut += ( $t['day_avg_out'] - $meanOut ) * ( $t['day_avg_out'] - $meanOut );
-                }
-
-                $stddevIn  = sqrt( $stddevIn  / ( $count - 1 ) );
-                $stddevOut = sqrt( $stddevOut / ( $count - 1 ) );
-            }
-            
-            // so, is yesterday's traffic outside of the standard deviation? And is it an increase or decrease?
-            $sIn  = ( $todayAvgIn  - $meanIn   ) > 0 ? 'increase' : 'decrease';
-            $sOut = ( $todayAvgOut - $meanOut  ) > 0 ? 'increase' : 'decrease';
-            $dIn  = abs( $todayAvgIn  - $meanIn  );
-            $dOut = abs( $todayAvgOut - $meanOut );
-
-            $thresholdIn  = 1.5*$stddevIn;
-            $thresholdOut = 1.5*$stddevOut;
-
-            if( $this->_verbose )
-            {
-	            echo $c->getName() . "\n";
-	            printf( "\tIN  M: %d\tSD: %d\tDiff: %d\tT: %d\tR: %s\n",
-	                intval( $meanIn ), intval( $stddevIn ), intval( $dIn ), $thresholdIn, ( $dIn > $thresholdIn ? 'OUT' : 'IN' )
-	            );
-	            printf( "\tOUT M: %d\tSD: %d\tDiff: %d\tT: %d\tR: %s\n\n",
-	                intval( $meanOut ), intval( $stddevOut ), intval( $dOut ), $thresholdOut, ( $dOut > $thresholdOut ? 'OUT' : 'IN' )
-	            );
-            }
-
-            if( $dIn > $thresholdIn || $dOut > $thresholdOut )
-            {
-                $this->view->cust          = $c;
-                $this->view->in            = $todayAvgIn;
-                $this->view->out           = $todayAvgOut;
-                $this->view->stddevIn      = $stddevIn;
-                $this->view->stddevOut     = $stddevOut;
-                $this->view->meanIn        = $meanIn;
-                $this->view->meanOut       = $meanOut;
-                $this->view->dIn           = $dIn;
-                $this->view->dOut          = $dOut;
-                $this->view->sIn           = $sIn;
-                $this->view->sOut          = $sOut;
-                $this->view->threasholdIn  = $thresholdIn;
-                $this->view->threasholdOut = $thresholdOut;
-                $this->view->percentIn     = $meanIn  ? intval( ( $dIn  / $meanIn  ) * 100 ) : 'NONE';
-                $this->view->percentOut    = $meanOut ? intval( ( $dOut / $meanOut ) * 100 ) : 'NONE';
-                $this->view->days          = $this->_options['cli']['traffic_differentials']['stddev_calc_length'];
-
-                $mrtg = $mail->createAttachment(
-                    @file_get_contents(
-                        INEX_Mrtg::getMrtgFilePath(
-                            $this->_options['mrtg']['path'] . '/members',
-                            'PNG',
-                            'aggregate',
-                            'bits',
-                            $c->getShortname(),
-                            'month'
-                        )
-                    ),
-                    "image/png",
-                    Zend_Mime::DISPOSITION_INLINE,
-                    Zend_Mime::ENCODING_BASE64,
-                    $c->getShortname() . ".png"
-                );
-                $mrtg->id = $c->getShortname();
-
-                $mailHtml .= $this->view->render( 'customer/email/diff-member.phtml' );
-
-                $numWithExceededThreshold++;
-            }
-
-        }
-
-        $this->view->numWithExceededThreshold = $numWithExceededThreshold;
-
-        $mailHtml .= $this->view->render( 'customer/email/diff-footer.phtml' );
-
-        $mail->setBodyHtml( $mailHtml  );
-        $mail->send();
-    }
-
-    /**
-     * This function looks for members who are reaching or exceeding 80% port utilisation
-     */
-    public function examinePortUtilisationAction()
-    {
-        $custs = $this->getD2EM()->getRepository( '\\Entities\\Customer' )->getCurrentActive( false, true, false );
-        
-        $mail = $this->getMailer();
-        $mail->setFrom( $this->_options['cli']['port_utilisation']['from_email'], $this->_options['cli']['traffic_differentials']['from_name'] )
-             ->setSubject( $this->_options['cli']['port_utilisation']['subject'] )
-             ->setType( Zend_Mime::MULTIPART_RELATED );
-
-        foreach( $this->_options['cli']['port_utilisation']['recipients'] as $r )
-            $mail->addTo( $r );
-
-        $this->view->threshold = $this->_options['cli']['port_utilisation']['threshold'];
-        $mailHtml = $this->view->render( 'customer/email/util-header.phtml' );
-
-        $numIntsWithExcessUtil = 0;
-
-        foreach( $custs as $c )
-        {
-            foreach( $c->getVirtualInterfaces() as $vi )
-            {
-                foreach( $vi->getPhysicalInterfaces() as $pi )
-                {
-                    $speed = $pi->getSpeed() * 1024 * 1024;
-
-                    $mrtg = new INEX_Mrtg(
-                        INEX_Mrtg::getMrtgFilePath( $this->_options['mrtg']['path'] . '/members',
-                            'LOG', $pi->getMonitorindex(), INEX_Mrtg::CATEGORY_BITS,
-                            $c->getShortname()
-                        )
-                    );
-
-                    $stats = $mrtg->getValues( INEX_Mrtg::PERIOD_WEEK, INEX_Mrtg::CATEGORY_BITS, false );
-
-                    $maxIn  = $stats['maxin'] * 8.0;
-                    $maxOut = $stats['maxout'] * 8.0;
-
-                    $switch_port = $pi->getSwitchport()->getSwitcher()->getName() . ' :: ' . $pi->getSwitchport()->getName();
-
-                    $utilIn  = $maxIn  / $speed;
-                    $utilOut = $maxOut / $speed;
-
-                    if( $this->_verbose )
-                    {
-                        echo $c->getName() . "\n";
-                        printf( "\tIN %0.2f%%\tOUT: %0.2f%%\n", $utilIn * 100.0, $utilOut * 100.0 );
-                    }
-                    
-	                if( $utilIn > $this->_options['cli']['port_utilisation']['threshold'] || $utilOut > $this->_options['cli']['port_utilisation']['threshold'] )
-	                {
-	                    $this->view->cust       = $c;
-	                    $this->view->utilIn     = $utilIn;
-	                    $this->view->utilOut    = $utilOut;
-	                    $this->view->switchport = $switch_port;
-
-		                $mrtg = $mail->createAttachment(
-		                    file_get_contents(
-		                       INEX_Mrtg::getMrtgFilePath(
-		                            $this->_options['mrtg']['path'] . '/members',
-		                            'PNG',
-		                            $pi->getMonitorindex(),
-		                            INEX_Mrtg::CATEGORY_BITS,
-		                            $c->getShortname(),
-		                            INEX_Mrtg::PERIOD_WEEK
-		                        )
-		                    ),
-		                    "image/png",
-		                    Zend_Mime::DISPOSITION_INLINE,
-		                    Zend_Mime::ENCODING_BASE64,
-		                    $c->getShortname() . ".png"
-		                );
-
-	                    $mrtg->id = $c->getShortname();
-
-	                    $mailHtml .= $this->view->render( 'customer/email/util-member.phtml' );
-
-	                    $numIntsWithExcessUtil++;
-	                }
-                }
-            }
-        }
-
-        $this->view->numWithExcessUtil = $numIntsWithExcessUtil;
-
-        $mailHtml .= $this->view->render( 'customer/email/util-footer.phtml' );
-
-        $mail->setBodyHtml( $mailHtml  );
-        $mail->send();
-    }
-
-
-    /**
-     * Generates a Nagios configuration for supported switches in the database
-     */
-    public function generateNagiosConfigAction()
-    {
-        $switches = $this->getD2EM()->getRepository( '\\Entities\\Switcher' )->getAndCache( true );
-        
-        echo $this->view->render( 'cli/nagios/switch-definitions.phtml' );
-
-        $brocade = array();
-        $cisco   = array();
-        $mrv     = array();
-
-        $all     = [];
-
-        foreach( $switches as $s )
-        {
-            $this->view->sw = $s;
-            echo $this->view->render( 'cli/nagios/switch-hosts.phtml' );
-
-            switch( $s->getVendor()->getName() )
-            {
-                case 'Foundry Networks':
-                    $brocade[] = $s->getName();
-                    break;
-
-                case 'Cisco Systems':
-                    $cisco[] = $s->getName();
-                    break;
-
-                case 'MRV':
-                    $mrv[] = $s->getName();
-                    break;
-            }
-
-            $all[] = $s->getName();
-
-            if( isset( $locations[ $s->getCabinet()->getLocation()->getShortname() ] ) )
-                $locations[ $s->getCabinet()->getLocation()->getShortname() ] .= ", " . $s->getName();
-            else
-                $locations[ $s->getCabinet()->getLocation()->getShortname() ] = $s->getName();
-        }
-
-        $this->view->all = implode( ', ', $all );
-
-        $this->view->locations = $locations;
-        
-        $this->view->vendor_brocade = implode( ', ', $brocade );
-        $this->view->vendor_cisco   = implode( ', ', $cisco   );
-        $this->view->vendor_mrv     = implode( ', ', $mrv     );
-
-        echo $this->view->render( 'cli/nagios/switch-templates.phtml' );
-    }
-    
-    
-    /**
-     * Mailing list initialisation script
-     *
-     * First sets a user preference for ALL users *WITHOUT* a mailing list sub for this list to unsub'd.
-     *
-     * Then takes a list of *existing* mailing list addresses from stdin and:
-     *   - is a user does not exist with same email, skips
-     *   - if a user does exist with same email, sets his mailing list preference
-     *
-     * NB: This function is NON-DESTRUCTIVE. It will *NOT* affect any users with *EXISTING* settings
-     * but set those without a setting to on / off as appropriate.
-     *
-     */
-    public function mailingListInitAction()
-    {
-        $list = $this->_getMailingList();
-
-        $stdin = fopen( "php://stdin","r" );
-        $addresses = array();
-        
-        while( $address = strtolower( trim( fgets( $stdin ) ) ) )
-            $addresses[] = $address;
-
-        fclose( $stdin );
-        
-        if( $this->_verbose ) echo "Setting mailing list subscription for all users without a subscription setting...\n";
-        $users = $this->getD2EM()->getRepository( '\\Entities\\User' )->findAll();
-
-        foreach( $users as $u )
-        {
-            if( $u->hasPreference( "mailinglist.{$list}.subscribed" ) )
-                continue;
-
-            if( in_array( $u->getEmail(), $addresses ) )
-                $u->setPreference( "mailinglist.{$list}.subscribed", 1 );
-            else
-                $u->setPreference( "mailinglist.{$list}.subscribed", 0 );
-        }
-        
-        $this->getD2EM()->flush();
-    }
-
-    /**
-     * Mailing list subscribed action - list all addresses subscribed to the given list
-     */
-    public function mailingListSubscribedAction()
-    {
-        $list = $this->_getMailingList();
-
-        $users = $this->getD2EM()->getRepository( '\\Entities\\User' )->getMailingListSubscribers( $list, 1 );
-
-        foreach( $users as $user )
-            echo "{$user['email']}\n";
-    }
-    
-    /**
-     * Mailing list unsubscribed action - list all addresses not subscribed to the given list
-     */
-    public function mailingListUnsubscribedAction()
-    {
-        $list = $this->_getMailingList();
-
-        $users = $this->getD2EM()->getRepository( '\\Entities\\User' )->getMailingListSubscribers( $list, 0 );
-
-        foreach( $users as $user )
-            echo "{$user['email']}\n";
-    }
-    
-    /**
-     * Mailing list password sync - create and execute commands to set mailing list p/w of subscribers
-     */
-    public function mailingListPasswordSyncAction()
-    {
-        $list = $this->_getMailingList();
-
-        // we'll sync by default so only if we're told not to will the following be true:
-        if( isset( $this->_options['mailinglists'][$list]['syncpws'] ) && !$this->_options['mailinglists'][$list]['syncpws'] )
-        {
-            if( $this->_verbose )
-                die( "{$list}: Password sync for the given mailing list is disabled" );
-            die();
-        }
-
-        $users = $this->getD2EM()->getRepository( '\\Entities\\User' )->getMailingListSubscribers( $list, 1 );
-                
-        foreach( $users as $user )
-        {
-            $cmd = sprintf( "{$this->_options['mailinglist']['cmd']['changepw']} %s %s %s",
-                    escapeshellarg( $list ), escapeshellarg( $user['email'] ), escapeshellarg( $user['password'] )
-            );
-            
-            if( $this->_verbose ) echo "$cmd\n";
-            exec( $cmd );
-        }
-    }
-    
-    /**
-     * Mailing list syncronisation - generates a shell script for all mailing lists
-     */
-    public function mailingListSyncScriptAction()
-    {
-        // do we have mailing lists defined?
-        if( !isset( $this->_options['mailinglists'] ) || !count( $this->_options['mailinglists'] ) )
-            die( "ERR: No valid mailing lists defined in your application.ini\n" );
-        
-        $this->view->apppath = APPLICATION_PATH;
-        $this->view->date = date( 'Y-m-d H:i:s' );
-
-        echo $this->view->render( 'cli/mailing-list-sync-script.sh' );
-    }
-    
-    
-    private function _getMailingList()
-    {
-        // do we have mailing lists defined?
-        if( !isset( $this->_options['mailinglist']['enabled'] ) || !$this->_options['mailinglist']['enabled'] )
-            die( "ERR: Mailing lists disabled in configuration( use: mailinglist.enabled = 1 to enabled)\n" );
-        
-        if( !( $list = $this->getFrontController()->getParam( 'param1', false ) ) )
-            die( "ERR: You must specify a list name (e.g. --p1 listname)\n" );
-        
-        // do we have mailing lists defined?
-        if( !isset( $this->_options['mailinglists'] ) || !count( $this->_options['mailinglists'] ) )
-            die( "ERR: No valid mailing lists defined in your application.ini\n" );
-        
-        // is it a valid list?
-        if( !isset( $this->_options['mailinglists'][$list] ) )
-            die( "ERR: The specifed list ({$list}) is not defined in your application.ini\n" );
-
-        return $list;
-    }
-}
-
-
diff --git a/application/controllers/ConsoleServerConnectionController.php b/application/controllers/ConsoleServerConnectionController.php
deleted file mode 100644
index 7108a0937..000000000
--- a/application/controllers/ConsoleServerConnectionController.php
+++ /dev/null
@@ -1,165 +0,0 @@
-
- * @category   INEX
- * @package    INEX_Controller
- * @copyright  Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd
- * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
- */
-class ConsoleServerConnectionController extends INEX_Controller_FrontEnd
-{
-    
-    /**
-     * This function sets up the frontend controller
-     */
-    protected function _feInit()
-    {
-        $this->assertPrivilege( \Entities\User::AUTH_SUPERUSER );
-    
-        $this->view->feParams = $this->_feParams = (object)[
-            'entity'        => '\\Entities\\ConsoleServerConnection',
-            'form'          => 'INEX_Form_ConsoleServerConnection',
-            'pagetitle'     => 'Console Server Connections',
-        
-            'titleSingular' => 'Console Server Connection',
-            'nameSingular'  => 'a console server connection',
-        
-            'defaultAction' => 'list',                    // OPTIONAL; defaults to 'list'
-        
-            'listOrderBy'    => 'description',
-            'listOrderByDir' => 'ASC',
-        
-            'listColumns'    => [
-        
-                'id'        => [ 'title' => 'UID', 'display' => false ],
-            
-                'customer'  => [
-                    'title'      => 'Customer',
-                    'type'       => self::$FE_COL_TYPES[ 'HAS_ONE' ],
-                    'controller' => 'customer',
-                    'action'     => 'view',
-                    'idField'    => 'customerid'
-                ],
-            
-                'description'  => 'Description',
-                
-                'switch'  => [
-                    'title'      => 'Console Server',
-                    'type'       => self::$FE_COL_TYPES[ 'HAS_ONE' ],
-                    'controller' => 'switch',
-                    'action'     => 'view',
-                    'idField'    => 'switchid'
-                ],
-                
-                'port'    => 'Port'
-            ]
-        ];
-    
-        // display the same information in the view as the list
-        $this->_feParams->viewColumns = array_merge(
-            $this->_feParams->listColumns,
-            [
-                'speed'       => 'Speed',
-                'parity'      => 'Parity',
-                'stopbits'    => 'Stopbits',
-                'flowcontrol' => 'Flow Control',
-                'autobaud'    => 'Autobaud',
-                'notes'       => 'Notes'
-            ]
-        );
-    }
-    
-    /**
-     * Provide array of users for the listAction and viewAction
-     *
-     * @param int $id The `id` of the row to load for `viewAction`. `null` if `listAction`
-     */
-    protected function listGetData( $id = null )
-    {
-        $qb = $this->getD2EM()->createQueryBuilder()
-            ->select( 'csc.id AS id, csc.description AS description, csc.port AS port,
-                csc.speed AS speed, csc.parity AS parity, csc.stopbits AS stopbits,
-                csc.flowcontrol AS flowcontrol, csc.autobaud AS autobaud,
-                csc.notes AS notes, c.name AS customer, c.id AS customerid,
-                s.name AS switch, s.id AS switchid'
-            )
-            ->from( '\\Entities\\ConsoleServerConnection', 'csc' )
-            ->leftJoin( 'csc.Customer', 'c' )
-            ->leftJoin( 'csc.Switcher', 's' );
-    
-        if( isset( $this->_feParams->listOrderBy ) )
-            $qb->orderBy( $this->_feParams->listOrderBy, isset( $this->_feParams->listOrderByDir ) ? $this->_feParams->listOrderByDir : 'ASC' );
-    
-        if( $id !== null )
-            $qb->andWhere( 'csc.id = ?1' )->setParameter( 1, $id );
-        
-        return $qb->getQuery()->getResult();
-    }
-    
-    
-    /**
-     *
-     * @param INEX_Form_ConsoleServerConnection $form The form object
-     * @param \Entities\ConsoleServerConnection $object The Doctrine2 entity (being edited or blank for add)
-     * @param bool $isEdit True of we are editing an object, false otherwise
-     * @param array $options Options passed onto Zend_Form
-     * @param string $cancelLocation Where to redirect to if 'Cancal' is clicked
-     * @return void
-     */
-    protected function formPostProcess( $form, $object, $isEdit, $options = null, $cancelLocation = null )
-    {
-        if( $isEdit )
-        {
-            $form->getElement( 'custid' )->setValue( $object->getCustomer()->getId() );
-            $form->getElement( 'switchid' )->setValue( $object->getSwitcher()->getId() );
-        }
-    }
-    
-    
-    /**
-     *
-     * @param INEX_Form_ConsoleServerConnection $form The form object
-     * @param \Entities\ConsoleServerConnection $object The Doctrine2 entity (being edited or blank for add)
-     * @param bool $isEdit True of we are editing an object, false otherwise
-     * @return void
-     */
-    protected function addPostValidate( $form, $object, $isEdit )
-    {
-        $object->setCustomer(
-            $this->getD2EM()->getRepository( '\\Entities\\Customer' )->find( $form->getElement( 'custid' )->getValue() )
-        );
-    
-        $object->setSwitcher(
-            $this->getD2EM()->getRepository( '\\Entities\\Switcher' )->find( $form->getElement( 'switchid' )->getValue() )
-        );
-    
-        return true;
-    }
-    
-}
-
diff --git a/application/controllers/ContactController.php b/application/controllers/ContactController.php
deleted file mode 100644
index 0b2193257..000000000
--- a/application/controllers/ContactController.php
+++ /dev/null
@@ -1,167 +0,0 @@
-
- * @category   INEX
- * @package    INEX_Controller
- * @copyright  Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd
- * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
- */
-class ContactController extends INEX_Controller_FrontEnd
-{
-    
-    /**
-     * This function sets up the frontend controller
-     */
-    protected function _feInit()
-    {
-        $this->assertPrivilege( \Entities\User::AUTH_SUPERUSER );
-        
-        $this->view->feParams = $this->_feParams = (object)[
-            'entity'        => '\\Entities\\Contact',
-            'form'          => 'INEX_Form_Contact',
-            'pagetitle'     => 'Contacts',
-        
-            'titleSingular' => 'Contact',
-            'nameSingular'  => 'a contact',
-        
-            'defaultAction' => 'list',                    // OPTIONAL; defaults to 'list'
-        
-            'listOrderBy'    => 'name',
-            'listOrderByDir' => 'ASC',
-    
-            'listColumns'    => [
-            
-                'id'        => [ 'title' => 'UID', 'display' => false ],
-    
-                'customer'  => [
-                    'title'      => 'Customer',
-                    'type'       => self::$FE_COL_TYPES[ 'HAS_ONE' ],
-                    'controller' => 'customer',
-                    'action'     => 'overview',
-                    'idField'    => 'custid'
-                ],
-    
-                'name'      => 'Name',
-                'email'     => 'Email',
-                'phone'     => 'Phone',
-                'mobile'    => 'Mobile'
-            ]
-        ];
-    
-        // display the same information in the view as the list
-        $this->_feParams->viewColumns = array_merge(
-            $this->_feParams->listColumns,
-            [
-                'facilityaccess' => 'Facility Access',
-                'mayauthorize'   => 'May Authorize',
-                'lastupdated'    => [
-                    'title'         => 'Last Updated',
-                    'type'          => self::$FE_COL_TYPES[ 'DATETIME' ]
-                ],
-                'lastupdatedby'  => 'Last Updated By',
-                'creator'        => 'Creator',
-                'created'        => [
-                    'title'         => 'Created',
-                    'type'          => self::$FE_COL_TYPES[ 'DATETIME' ]
-                ]
-            ]
-        );
-    }
-
-
-    /**
-     * Provide array of users for the listAction and viewAction
-     *
-     * @param int $id The `id` of the row to load for `viewAction`. `null` if `listAction`
-     */
-    protected function listGetData( $id = null )
-    {
-        $qb = $this->getD2EM()->createQueryBuilder()
-        ->select( 'c.id as id, c.name as name, c.email as email, c.phone AS phone, c.mobile AS mobile,
-                c.facilityaccess AS facilityaccess, c.mayauthorize AS mayauthorize,
-                c.lastupdated AS lastupdated, c.lastupdatedby AS lastupdatedby,
-                c.creator AS creator, c.created AS created, cust.name AS customer, cust.id AS custid'
-            )
-        ->from( '\\Entities\\Contact', 'c' )
-        ->leftJoin( 'c.Customer', 'cust' );
-    
-        if( isset( $this->_feParams->listOrderBy ) )
-            $qb->orderBy( $this->_feParams->listOrderBy, isset( $this->_feParams->listOrderByDir ) ? $this->_feParams->listOrderByDir : 'ASC' );
-    
-        if( $id !== null )
-            $qb->andWhere( 'c.id = ?1' )->setParameter( 1, $id );
-    
-        return $qb->getQuery()->getResult();
-    }
-    
-    
-    /**
-     *
-     * @param INEX_Form_Contact $form The form object
-     * @param \Entities\Contact $object The Doctrine2 entity (being edited or blank for add)
-     * @param bool $isEdit True of we are editing an object, false otherwise
-     * @param array $options Options passed onto Zend_Form
-     * @param string $cancelLocation Where to redirect to if 'Cancal' is clicked
-     * @return void
-     */
-    protected function formPostProcess( $form, $object, $isEdit, $options = null, $cancelLocation = null )
-    {
-        if( $isEdit )
-            $form->getElement( 'custid' )->setValue( $object->getCustomer()->getId() );
-    }
-    
-    
-    /**
-     *
-     * @param INEX_Form_Contact $form The form object
-     * @param \Entities\Contact $object The Doctrine2 entity (being edited or blank for add)
-     * @param bool $isEdit True of we are editing an object, false otherwise
-     * @return void
-     */
-    protected function addPostValidate( $form, $object, $isEdit )
-    {
-        $object->setCustomer(
-            $this->getD2EM()->getRepository( '\\Entities\\Customer' )->find( $form->getElement( 'custid' )->getValue() )
-        );
-    
-        if( $isEdit )
-        {
-            $object->setLastupdated( new DateTime() );
-            $object->setLastupdatedby( $this->getUser()->getId() );
-        }
-        else
-        {
-            $object->setCreated( new DateTime() );
-            $object->setCreator( $this->getUser()->getUsername() );
-        }
-    
-        return true;
-    }
-    
-    
-}
diff --git a/application/controllers/CustKitController.php b/application/controllers/CustKitController.php
deleted file mode 100644
index 7f4b0c109..000000000
--- a/application/controllers/CustKitController.php
+++ /dev/null
@@ -1,157 +0,0 @@
-
- * @category   INEX
- * @package    INEX_Controller
- * @copyright  Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd
- * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
- */
-class CustKitController extends INEX_Controller_FrontEnd
-{
-    /**
-     * This function sets up the frontend controller
-     */
-    protected function _feInit()
-    {
-        $this->assertPrivilege( \Entities\User::AUTH_SUPERUSER );
-    
-        $this->view->feParams = $this->_feParams = (object)[
-            'entity'        => '\\Entities\\CustomerEquipment',
-            'form'          => 'INEX_Form_CustKit',
-            'pagetitle'     => 'Customer Equipment',
-        
-            'titleSingular' => 'Customer Equipment',
-            'nameSingular'  => 'customer equipment',
-        
-            'defaultAction' => 'list',                    // OPTIONAL; defaults to 'list'
-        
-            'listOrderBy'    => 'name',
-            'listOrderByDir' => 'ASC',
-        
-            'listColumns'    => [
-        
-                'id'        => [ 'title' => 'UID', 'display' => false ],
-            
-                'customer'  => [
-                    'title'      => 'Customer',
-                    'type'       => self::$FE_COL_TYPES[ 'HAS_ONE' ],
-                    'controller' => 'customer',
-                    'action'     => 'overview',
-                    'idField'    => 'custid'
-                ],
-            
-                'cabinet'  => [
-                    'title'      => 'Cabinet',
-                    'type'       => self::$FE_COL_TYPES[ 'HAS_ONE' ],
-                    'controller' => 'cabinet',
-                    'action'     => 'view',
-                    'idField'    => 'cabinetid'
-                ],
-            
-                'name'      => 'Name'
-            ]
-        ];
-    
-        // display the same information in the view as the list
-        $this->_feParams->viewColumns = array_merge(
-            $this->_feParams->listColumns,
-            [
-                'descr'      => 'Description'
-            ]
-        );
-    }
-    
-    
-    /**
-     * Provide array of users for the listAction and viewAction
-     *
-     * @param int $id The `id` of the row to load for `viewAction`. `null` if `listAction`
-     */
-    protected function listGetData( $id = null )
-    {
-        $qb = $this->getD2EM()->createQueryBuilder()
-            ->select( 'ck.id AS id, ck.name AS name, ck.descr AS descr,
-                c.name AS customer, c.id AS custid,
-                cab.name AS cabinet, cab.id AS cabinetid'
-            )
-            ->from( '\\Entities\\CustomerEquipment', 'ck' )
-            ->leftJoin( 'ck.Customer', 'c' )
-            ->leftJoin( 'ck.Cabinet', 'cab' );
-    
-        if( isset( $this->_feParams->listOrderBy ) )
-            $qb->orderBy( $this->_feParams->listOrderBy, isset( $this->_feParams->listOrderByDir ) ? $this->_feParams->listOrderByDir : 'ASC' );
-    
-        if( $id !== null )
-            $qb->andWhere( 'ck.id = ?1' )->setParameter( 1, $id );
-    
-        return $qb->getQuery()->getResult();
-    }
-    
-    
-    /**
-     *
-     * @param INEX_Form_Contact $form The form object
-     * @param \Entities\Contact $object The Doctrine2 entity (being edited or blank for add)
-     * @param bool $isEdit True of we are editing an object, false otherwise
-     * @param array $options Options passed onto Zend_Form
-     * @param string $cancelLocation Where to redirect to if 'Cancal' is clicked
-     * @return void
-     */
-    protected function formPostProcess( $form, $object, $isEdit, $options = null, $cancelLocation = null )
-    {
-        if( $isEdit )
-        {
-            $form->getElement( 'custid' )->setValue( $object->getCustomer()->getId() );
-            $form->getElement( 'cabinetid' )->setValue( $object->getCabinet()->getId() );
-        }
-    }
-    
-    
-    /**
-     *
-     * @param INEX_Form_Contact $form The form object
-     * @param \Entities\Contact $object The Doctrine2 entity (being edited or blank for add)
-     * @param bool $isEdit True of we are editing an object, false otherwise
-     * @return void
-     */
-    protected function addPostValidate( $form, $object, $isEdit )
-    {
-        $object->setCustomer(
-            $this->getD2EM()->getRepository( '\\Entities\\Customer' )->find( $form->getElement( 'custid' )->getValue() )
-        );
-        
-        $object->setCabinet(
-            $this->getD2EM()->getRepository( '\\Entities\\Cabinet' )->find( $form->getElement( 'cabinetid' )->getValue() )
-        );
-    
-        return true;
-    }
-    
-    
-}
-
diff --git a/application/controllers/CustomerController.php b/application/controllers/CustomerController.php
deleted file mode 100644
index 84db872e1..000000000
--- a/application/controllers/CustomerController.php
+++ /dev/null
@@ -1,393 +0,0 @@
-
- * @category   INEX
- * @package    INEX_Controller
- * @copyright  Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd
- * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
- */
-class CustomerController extends INEX_Controller_FrontEnd
-{
-    /**
-     * This function sets up the frontend controller
-     */
-    protected function _feInit()
-    {
-        $this->view->feParams = $this->_feParams = (object)[
-            'entity'        => '\\Entities\\Customer',
-            'form'          => 'INEX_Form_Customer',
-            'pagetitle'     => 'Customers',
-        
-            'titleSingular' => 'Customer',
-            'nameSingular'  => 'a customer',
-        
-            'defaultAction' => 'list',                    // OPTIONAL; defaults to 'list'
-        
-            'listOrderBy'    => 'c.name',
-            'listOrderByDir' => 'ASC',
-        ];
-    
-        switch( $this->getUser()->getPrivs() )
-        {
-            case \Entities\User::AUTH_SUPERUSER:
-                $this->_feParams->listColumns = [
-                    'id' => [ 'title' => 'UID', 'display' => false ],
-        
-                    'name'        => [
-                        'title'      => 'Name',
-                        'type'       => self::$FE_COL_TYPES[ 'HAS_ONE' ],
-                        'controller' => 'customer',
-                        'action'     => 'overview',
-                        'idField'    => 'id'
-                    ],
-        
-                    'autsys'      => 'AS',
-                    
-                    'shortname'   => [
-                        'title'      => 'Shortname',
-                        'type'       => self::$FE_COL_TYPES[ 'HAS_ONE' ],
-                        'controller' => 'customer',
-                        'action'     => 'overview',
-                        'idField'    => 'id'
-                    ],
-                    
-                    'peeringemail'   => 'Peering Email',
-                    'noc24hphone'    => 'NOC 24h Phone',
-                    
-                    'type'            => [
-                        'title'         => 'Type',
-                        'type'          => self::$FE_COL_TYPES[ 'XLATE' ],
-                        'xlator'        => \Entities\Customer::$CUST_TYPES_TEXT
-                    ],
-                    
-                    'datejoin'       => [
-                        'title'     => 'Joined',
-                        'type'      => self::$FE_COL_TYPES[ 'DATETIME' ]
-                    ]
-                ];
-                break;
-    
-            case \Entities\User::AUTH_CUSTUSER:
-                $this->_feParams->listColumns = [];
-                $this->_feParams->allowedActions = [ 'details', 'detail' ];
-                $this->_feParams->defaultAction = 'details';
-                break;
-                
-            default:
-                $this->redirectAndEnsureDie( 'error/insufficient-permissions' );
-        }
-    
-        // display the same information in the view as the list
-        $this->_feParams->viewColumns = array_merge(
-            $this->_feParams->listColumns,
-            [
-                'maxprefixes'     => 'Max Prefixes',
-                'nocphone'        => 'NOC Phone',
-                'nocfax'          => 'NOC Fax',
-                'nochours'        => 'NOC Hours',
-                'nocemail'        => 'NOC Email',
-                'nocwww'          => 'NOC WWW',
-                'irrdb'           => 'IRRDB',
-                'status'          => [
-                    'title'         => 'Status',
-                    'type'          => self::$FE_COL_TYPES[ 'XLATE' ],
-                    'xlator'        => \Entities\Customer::$CUST_STATUS_TEXT
-                ],
-                'activepeeringmatrix' => 'Active Peering Matrix',
-                'peeringmacro'    => 'Peering Macro',
-                'peeringpolicy'   => 'Peering Policy',
-                'billingContact'  => 'Billing Contact',
-                'billingAddress1' => 'Billing Address1',
-                'billingAddress2' => 'Billing Address2',
-                'billingCity'     => 'Billing City',
-                'billingCountry'  => 'Billing Country',
-                'corpwww'         => 'Corporate WWW',
-                'dateleave'       => [
-                        'title'     => 'Left',
-                        'type'      => self::$FE_COL_TYPES[ 'DATETIME' ]
-                ],
-                'notes'           => 'Notes',
-                'lastupdated'     => 'Last Updated',
-                'lastupdatedby'   => 'Last Updated By',
-                'creator'         => 'Created By',
-                'created'         => 'Created'
-            ]
-        );
-    }
-    
-    
-    
-    
-    /**
-     * Provide array of customers for the listAction and viewAction
-     *
-     * @param int $id The `id` of the row to load for `viewAction`. `null` if `listAction`
-     */
-    protected function listGetData( $id = null )
-    {
-        $qb = $this->getD2EM()->createQueryBuilder()
-                ->select( 'c.id AS id, c.name AS name, c.shortname AS shortname, c.type AS type,
-                            c.autsys AS autsys, c.maxprefixes AS maxprefixes, c.peeringemail AS peeringemail,
-                            c.nocphone AS nocphone, c.noc24hphone AS noc24hphone, c.nocfax AS nocfax,
-                            c.nochours AS nochours, c.nocemail AS nocemail, c.nocwww AS nocwww,
-                            c.irrdb AS irrdb, c.status AS status, c.activepeeringmatrix AS activepeeringmatrix,
-                            c.peeringmacro AS peeringmacro, c.peeringpolicy AS peeringpolicy,
-                            c.billingContact AS billingContact, c.billingAddress1 AS billingAddress1,
-                            c.billingAddress2 AS billingAddress2, c.billingCity AS billingCity, c.billingCountry AS billingCountry,
-                            c.corpwww AS corpwww, c.datejoin AS datejoin, c.dateleave AS dateleave,
-                            c.notes AS notes, c.lastupdated AS lastupdated, c.lastupdatedby AS lastupdatedby,
-                            c.creator AS creator, c.created AS created'
-                        )
-                ->from( '\\Entities\\Customer', 'c' );
-    
-        if( isset( $this->_feParams->listOrderBy ) )
-            $qb->orderBy( $this->_feParams->listOrderBy, isset( $this->_feParams->listOrderByDir ) ? $this->_feParams->listOrderByDir : 'ASC' );
-    
-        if( $id !== null )
-            $qb->andWhere( 'c.id = ?3' )->setParameter( 3, $id );
-    
-        return $qb->getQuery()->getResult();
-    }
-    
-    
-    public function viewAction()
-    {
-        $this->forward( 'overview' );
-    }
-
-    
-    /**
-     * The Customer Overview
-     */
-    public function overviewAction()
-    {
-        $this->view->cust = $cust = $this->_loadCustomer();
-    }
-    
-    
-    /**
-     *
-     * @param INEX_Form_Customer $form The Send form object
-     * @param \Entities\Customer $object The Doctrine2 entity (being edited or blank for add)
-     * @param bool $isEdit True if we are editing, otherwise false
-     * @return bool If false, the form is not processed
-     */
-    protected function addPostValidate( $form, $object, $isEdit )
-    {
-        if( !$isEdit && $this->getD2EM()->getRepository( '\\Entities\\Customer' )->findOneBy( [ 'shortname' => $form->getValue( 'shortname' ) ] ) )
-        {
-            $form->getElement( 'shortname' )->addError( 'This shortname is not available' );
-            return false;
-        }
-            
-        if( $isEdit )
-        {
-            $object->setLastupdated( new DateTime() );
-            $object->setLastupdatedby( $this->getUser()->getId() );
-        }
-        else
-        {
-            $object->setCreated( new DateTime() );
-            $object->setCreator( $this->getUser()->getUsername() );
-        }
-        
-        return true;
-    }
-
-    /**
-     *
-     * @param INEX_Form_Customer $form The Send form object
-     * @param \Entities\Customer $object The Doctrine2 entity (being edited or blank for add)
-     * @param bool $isEdit True if we are editing, otherwise false
-     * @return bool If false, the form is not processed
-     */
-    protected function addPreFlush( $form, $object, $isEdit )
-    {
-        
-        if( !( $object->getDatejoin() instanceof DateTime ) )
-            $object->setDatejoin( new DateTime( $form->getValue( 'datejoin' ) ) );
-        
-        if( !( $object->getDateleave() instanceof DateTime ) )
-        {
-            if( !$form->getValue( 'dateleave' ) )
-                $object->setDateleave( null );
-            else
-                $object->setDateleave( new DateTime( $form->getValue( 'dateleave' ) ) );
-        }
-            
-        return true;
-    }
-
-
-
-    /**
-     * Post process hook that can be overridden by subclasses for add and edit actions.
-     *
-     * This is called immediately after the initstantiation of the form object and, if
-     * editing, includes the Doctrine2 entity `$object`.
-     *
-     * If you need to have, for example, edit values set in the form, then use the
-     * `addPrepare()` hook rather than this one.
-     *
-     * @see addPrepare()
-     * @param OSS_Form $form The form object
-     * @param object $object The Doctrine2 entity (being edited or blank for add)
-     * @param bool $isEdit True of we are editing an object, false otherwise
-     * @param array $options Options passed onto Zend_Form
-     * @param string $cancelLocation Where to redirect to if 'Cancal' is clicked
-     */
-     protected function formPostProcess( $form, $object, $isEdit, $options = null, $cancelLocation = null )
-     {
-         if( !$isEdit && isset( $this->getOptions()['identity']['default_country'] ) )
-             $form->getElement( 'billingCountry' )->setValue( $this->getOptions()['identity']['default_country'] );
-             
-         return true;
-     }
-                                                                                               
-
-    /**
-     * Send the member an operations welcome mail
-     *
-     */
-    public function welcomeEmailAction()
-    {
-        $this->view->customer = $c = $this->_loadCustomer();
-        $this->view->admins = $c->getAdminUsers();
-        $this->view->form = $form = new INEX_Form_Customer_SendEmail();
-        
-        $form->getElement( 'to' )->setValue( $c->getNocemail() );
-
-        $emails = array();
-        foreach( $c->getUsers() as $user )
-            if( Zend_Validate::is( $user->getEmail(), 'EmailAddress' ) )
-                $emails[] = $user->getEmail();
-
-        $form->getElement( 'cc' )->setValue( implode( ',', $emails ) );
-        $form->getElement( 'bcc' )->setValue( $this->_options['identity']['email'] );
-        $form->getElement( 'subject' )->setValue( $this->_options['identity']['name'] . ' :: Welcome Mail' );
-        $form->getElement( 'message' )->setValue( $this->view->render( "customer/email/welcome-email.phtml" ) );
-        
-        // Let's get the information we need for the welcome mail from the database.
-        $this->view->netinfo = $this->getD2EM()->getRepository( '\\Entities\\NetworkInfo' )->asVlanProtoArray();
-        
-        // Process a submitted form if it passes initial validation
-        if( $this->getRequest()->isPost() && $form->isValid( $_POST ) )
-        {
-            $mail = $this->_processSendEmailForm( $form );
-            if( $mail )
-            {
-                $mail->setBodyText( $form->getValue( 'message' ) );
-                $mail->setFrom( $this->_options['identity']['email'], $this->_options['identity']['name'] );
-                $mail->setSubject( $form->getValue( 'subject' ) );
-                $mail->send();
-
-                $this->getLogger()->info( "Welcome email sent for {$c->getName()}" );
-                $this->addMessage( "Welcome email successfully sent to {$c->getName()}", OSS_Message::SUCCESS );
-                return $this->redirect( 'customer/overview/id/' . $c->getId() );
-            }
-        }
-    }
-
-
-    
-    public function detailsAction()
-    {
-        $this->view->details = $this->getD2EM()->getRepository( '\\Entities\\Customer' )->getCurrentActive( true );
-    }
-        
-    public function detailAction()
-    {
-        $this->view->cust = $c = $this->_loadCustomer( $this->getParam( 'id', null ), 'customer/details' );
-        $this->view->netinfo = $this->getD2EM()->getRepository( '\\Entities\\NetworkInfo' )->asVlanProtoArray();
-    }
-        
-    
-    /**
-     * Load a customer from the database with the given ID (or ID in request) but
-     * redirect to `customer/list` if no ID or no such customer.
-     *
-     * @param int|bool $id The customer `$id` to load (or, if false, look for an ID parameter)
-     * @param string $redirect Alternative location to redirect to
-     * @return \Entities\Customer The customer object
-     */
-    protected function _loadCustomer( $id = false, $redirect = null )
-    {
-        if( $id === false )
-            $id = $this->getParam( 'id', false );
-        
-        if( $id )
-            $c = $this->getD2EM()->getRepository( '\\Entities\\Customer' )->find( $id );
-        
-        if( !$id || !$c )
-        {
-            $this->addMessage( 'Invalid customer ID', OSS_Message::ERROR );
-            $this->redirect( $redirect === null ? 'customer/list' : $redirect );
-        }
-        
-        return $c;
-    }
-    
-    /**
-     * A utility function to process the To / CC / BCC fields of the Send Email
-     * form and return a populated Zend_Mail object.
-     *
-     * @see welcomeEmailAction() for an example
-     * @param INEX_Form_Customer_SendEmail $form The Send Email form
-     * @return Zend_Mail|bool The Zend_Mail object on success
-     */
-    protected function _processSendEmailForm( $form )
-    {
-        $emailsOkay = null;
-        $mail = $this->getMailer();
-        // Validate all e-mail addresses
-        foreach( [ 'to' => 'To', 'cc' => 'Cc', 'bcc' => 'Bcc' ] as $element => $function )
-        {
-            if( ( $v = $form->getValue( $element ) ) != '' )
-            {
-                foreach( explode( ',', $v ) as $email )
-                {
-                    if( !Zend_Validate::is( $email, 'EmailAddress' ) )
-                    {
-                        $form->getElement( $element )->addError( 'Invalid e-mail address: ' . $email );
-                        $emailsOkay = false;
-                    }
-                    else if( $emailsOkay === null || $emailsOkay === true )
-                    {
-                        $fn = "add{$function}";
-                        $mail->$fn( $email );
-                        $emailsOkay = true;
-                    }
-                }
-            }
-        }
-        
-        return $emailsOkay ? $mail : false;
-    }
-    
-}
-
diff --git a/application/controllers/DashboardController.php b/application/controllers/DashboardController.php
deleted file mode 100644
index 7e6d89a68..000000000
--- a/application/controllers/DashboardController.php
+++ /dev/null
@@ -1,145 +0,0 @@
-
- * @category   INEX
- * @package    INEX_Controller
- * @copyright  Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd
- * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
- */
-class DashboardController extends INEX_Controller_AuthRequiredAction
-{
-    
-    public function preDispatch()
-    {
-        if( $this->getUser()->getPrivs() != \Entities\User::AUTH_CUSTUSER )
-            $this->_redirect( '' );
-    }
-    
-    public function indexAction()
-    {
-        // Get the three most recent members
-        $this->view->recentMembers = $this->getD2EM()->getRepository( '\\Entities\\Customer' )->getRecent( 3 );
-
-        /*
-            // is there a meeting available to register for?
-            $this->view->meeting = false;
-    
-            if( ( $meeting = MeetingTable::getUpcomingMeeting() ) !== false
-                && ( !isset( $this->session->dashboard_skip_meeting ) || !$this->session->dashboard_skip_meeting )
-            )
-            {
-                $rsvp = $this->getUser()->hasPreference( 'meeting.attending.' . $meeting['id'] );
-    
-                if( $rsvp === false )
-                {
-                    $this->view->meeting = $meeting;
-                    $this->view->meeting_pref = $rsvp;
-                }
-            }
-        */
-
-        if( !$this->getCustomer()->isTypeAssociate() )
-        {
-            $this->view->netinfo = $this->getD2EM()->getRepository( '\\Entities\\NetworkInfo' )->asVlanProtoArray();
-	        $this->view->categories = INEX_Mrtg::$CATEGORIES;
-
-	        $this->getNocDetailsForm();
-	        $this->getBillingDetailsForm();
-        }
-    }
-    
-    
-    
-    public function updateNocAction()
-    {
-        $form = $this->getNocDetailsForm();
-        
-        if( $this->getRequest()->isPost() )
-        {
-            if( $form->isValid( $_POST ) )
-            {
-                $form->assignFormToEntity( $this->getCustomer(), $this, true );
-                $this->getD2EM()->flush();
-                $this->addMessage( 'Your NOC details have been updated', OSS_Message::SUCCESS );
-            }
-            else
-            {
-                $this->addMessage( 'There was an error updating your NOC details', OSS_Message::ERROR );
-            }
-        }
-        
-        $this->forward( 'index' );
-    }
-    
-    protected function getNocDetailsForm()
-    {
-        $form = new INEX_Form_Customer_NocDetails();
-        $form->assignEntityToForm( $this->getCustomer(), $this, true );
-        $form->setAction( OSS_Utils::genUrl( 'dashboard', 'update-noc' ) );
-        
-        if( !isset( $this->view->nocDetails ) )
-            $this->view->nocDetails = $form;
-        
-        return $form;
-    }
-
-
-    public function updateBillingAction()
-    {
-        $form = $this->getBillingDetailsForm();
-        
-        if( $this->getRequest()->isPost() )
-        {
-            if( $form->isValid( $_POST ) )
-            {
-                $form->assignFormToEntity( $this->getCustomer(), $this, true );
-                $this->getD2EM()->flush();
-                $this->addMessage( 'Your billing details have been updated', OSS_Message::SUCCESS );
-            }
-            else
-            {
-                $this->addMessage( 'There was an error updating your billing details', OSS_Message::ERROR );
-            }
-        }
-        
-        $this->forward( 'index' );
-    }
-    
-    protected function getBillingDetailsForm()
-    {
-        $form = new INEX_Form_Customer_BillingDetails();
-        
-        if( !isset( $this->view->billingDetails ) )
-            $this->view->billingDetails = $form;
-        
-        $form->assignEntityToForm( $this->getCustomer(), $this, true );
-        $form->setAction( OSS_Utils::genUrl( 'dashboard', 'update-billing' ) );
-        return $form;
-    }
-}
-
-
diff --git a/application/controllers/ErrorController.php b/application/controllers/ErrorController.php
deleted file mode 100644
index 9ffd717df..000000000
--- a/application/controllers/ErrorController.php
+++ /dev/null
@@ -1,38 +0,0 @@
-
- * @category   INEX
- * @package    INEX_Controller
- * @copyright  Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd
- * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
- */
-class ErrorController extends INEX_Controller_Action
-{
-    use OSS_Controller_Trait_Error;
-}
-
diff --git a/application/controllers/IndexController.php b/application/controllers/IndexController.php
deleted file mode 100644
index b5e8b09e0..000000000
--- a/application/controllers/IndexController.php
+++ /dev/null
@@ -1,58 +0,0 @@
-
- * @category   INEX
- * @package    INEX_Controller
- * @copyright  Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd
- * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
- */
-class IndexController extends INEX_Controller_Action
-{
-
-    public function indexAction()
-    {
-        if( !$this->getAuth()->hasIdentity() )
-            $this->redirectAndEnsureDie( 'auth/login' );
-        
-        if( $this->getUser()->getPrivs() == \Entities\User::AUTH_SUPERUSER )
-            $this->_redirect( 'admin/index' );
-        else if( $this->getUser()->getPrivs() == \Entities\User::AUTH_CUSTADMIN )
-            $this->_redirect( 'user/list' );
-        else
-            $this->forward( 'index', 'dashboard' );
-    }
-
-    public function controllerDisabledAction()
-    {
-        $this->view->display( 'index/controller-disabled.tpl' );
-    }
-
-    public function aboutAction()
-    {}
-
-}
diff --git a/application/controllers/Ipv4AddressController.php b/application/controllers/Ipv4AddressController.php
deleted file mode 100644
index 1473d3f63..000000000
--- a/application/controllers/Ipv4AddressController.php
+++ /dev/null
@@ -1,216 +0,0 @@
-
- * @category   INEX
- * @package    INEX_Controller
- * @copyright  Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd
- * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
- */
-class Ipv4AddressController extends INEX_Controller_FrontEnd
-{
-    /**
-     * This function sets up the frontend controller
-     */
-    protected function _feInit()
-    {
-        $this->assertPrivilege( \Entities\User::AUTH_SUPERUSER );
-    
-        $this->view->feParams = $this->_feParams = (object)[
-            'entity'        => '\\Entities\\IPv4Address',
-            'pagetitle'     => 'IPv4 Addresses',
-        
-            'titleSingular' => 'IPv4 Address',
-            'nameSingular'  => 'an IPv4 address',
-        
-            'defaultAction' => 'list',                    // OPTIONAL; defaults to 'list'
-            
-            'readonly'      => true,
-        
-            'listOrderBy'    => 'id',
-            'listOrderByDir' => 'ASC',
-        
-            'listColumns'    => [
-        
-                'id'        => [ 'title' => 'UID', 'display' => false ],
-                'address'   => 'Address',
-                'hostname'  => 'Hostname',
-                'customer'  => [
-                    'title'      => 'Customer',
-                    'type'       => self::$FE_COL_TYPES[ 'HAS_ONE' ],
-                    'controller' => 'customer',
-                    'action'     => 'view',
-                    'idField'    => 'customerid'
-                ],
-            ]
-        ];
-            
-        // display the same information in the view as the list
-        $this->_feParams->viewColumns = array_merge(
-            $this->_feParams->listColumns,
-            [
-                'vlan'  => [
-                    'title'      => 'VLAN',
-                    'type'       => self::$FE_COL_TYPES[ 'HAS_ONE' ],
-                    'controller' => 'vlan',
-                    'action'     => 'view',
-                    'idField'    => 'vlanid'
-                ]
-            ]
-        );
-    }
-    
-    
-    /**
-     * Provide array of users for the listAction and viewAction
-     *
-     * @param int $id The `id` of the row to load for `viewAction`. `null` if `listAction`
-     */
-    protected function listGetData( $id = null )
-    {
-        $this->view->vlans = $vlans = $this->getD2EM()->getRepository( '\\Entities\\Vlan' )->getNames();
-
-        $qb = $this->getD2EM()->createQueryBuilder()
-            ->select( 'ip.id as id, ip.address as address,
-                v.name AS vlan,
-                vli.' . ( $this->_feParams->entity == '\\Entities\\IPv4Address' ? 'ipv4' : 'ipv6' ) . 'hostname AS hostname,
-                c.name AS customer, c.id AS customerid'
-            )
-            ->from( $this->_feParams->entity, 'ip' )
-            ->leftJoin( 'ip.Vlan', 'v' )
-            ->leftJoin( 'ip.VlanInterface', 'vli' )
-            ->leftJoin( 'vli.VirtualInterface', 'vi' )
-            ->leftJoin( 'vi.Customer', 'c' );
-    
-        if( isset( $this->_feParams->listOrderBy ) )
-            $qb->orderBy( $this->_feParams->listOrderBy, isset( $this->_feParams->listOrderByDir ) ? $this->_feParams->listOrderByDir : 'ASC' );
-    
-        if( $id !== null )
-            $qb->andWhere( 'ip.id = ?1' )->setParameter( 1, $id );
-    
-        $vid = false;
-        if( !( ( $vid = $this->getParam( 'vlan', false ) ) && isset( $vlans[$vid] ) ) )
-        {
-            if( isset( $this->_options['identity']['vlans']['default'] ) && isset( $vlans[ $this->_options['identity']['vlans']['default'] ] ) )
-                $vid = $this->_options['identity']['vlans']['default'];
-        }
-        
-        if( $vid )
-        {
-            $this->view->vid = $vid;
-            $qb->where( 'v.id = ?2' )->setParameter( 2, $vid );
-        }
-        
-        return $qb->getQuery()->getResult();
-    }
-    
-    public function addAction()
-    {
-        $this->view->form = $form = new INEX_Form_AddAddresses();
-        
-        if( $this->getRequest()->isPost() && $form->isValid( $_POST ) )
-        {
-            $addrfam = $form->getValue( 'type' );
-            $numaddrs = intval( $_POST['numaddrs'] );
-            
-            if( !( $vlan = $this->getD2EM()->getRepository( '\\Entities\\Vlan' )->find( $form->getValue( 'vlanid' ) ) ) )
-                throw new INEX_Exception( 'Unknown VLAN in request' );
-            
-            for( $i = 0; $i < $numaddrs; $i++ )
-            {
-                if( $addrfam == 'IPv4' )
-                    $ip = new \Entities\IPv4Address();
-                else if( $addrfam == 'IPv6' )
-                    $ip = new \Entities\IPv6Address();
-                else
-                    throw new INEX_Exception( 'Invalid address family' );
-
-                $ip->setVlan( $vlan );
-                $ip->setAddress( trim( $_POST[ 'np_name' . $i ] ) );
-                
-                $this->getD2EM()->persist( $ip );
-            }
-            
-            $this->getD2EM()->flush();
-                             
-            $msg = "{$numaddrs} new {$addrfam} addresses created for VLAN {$vlan->getName()}.";
-            $this->getLogger()->info( $msg );
-            $this->addMessage( $msg, OSS_Message::SUCCESS );
-            
-            if( $addrfam == 'IPv4' )
-                $redir = 'ipv4';
-            else
-                $redir = 'ipv6';
-
-            $this->redirect( strtolower( $addrfam ) . '-address/list/vlan/' . $vlan->getId() );
-        }
-    }
-
-    public function ajaxGetForVlanAction()
-    {
-        if( $this->getRequest()->getControllerName() == 'ipv6-address' )
-        {
-            $af = 'ipv6'; $entity = 'IPv6Address';
-        }
-        else
-        {
-            $af = 'ipv4'; $entity = 'IPv4Address';
-        }
-        
-        $dql = "SELECT {$af}.id AS id, {$af}.address AS address
-                    FROM \\Entities\\{$entity} {$af}
-                        LEFT JOIN {$af}.Vlan v
-                        LEFT JOIN {$af}.VlanInterface vli
-                    WHERE
-                        v.id = ?1 ";
-    
-        if( $this->getParam( 'vliid', null ) !== null )
-            $dql .= 'AND ( vli.id IS NULL OR vli.id = ?2 )';
-        else
-            $dql .= 'AND vli.id IS NULL';
-    
-        $dql .= " ORDER BY {$af}.id ASC";
-    
-        $query = $this->getD2EM()->createQuery( $dql );
-        $query->setParameter( 1, $this->getParam( 'vlanid', 0 ) );
-    
-        if( $this->getParam( 'vliid', null ) !== null )
-            $query->setParameter( 2, $this->getParam( 'vliid' ) );
-    
-        $ips = $query->getArrayResult();
-
-        $this->getResponse()
-            ->setHeader('Content-Type', 'application/json')
-            ->setBody( Zend_Json::encode( $ips ) )
-            ->sendResponse();
-    
-        die(); //FIXME I shouldn't have to die() here...
-    }
-    
-
-}
-
diff --git a/application/controllers/Ipv6AddressController.php b/application/controllers/Ipv6AddressController.php
deleted file mode 100644
index 0090ca10e..000000000
--- a/application/controllers/Ipv6AddressController.php
+++ /dev/null
@@ -1,55 +0,0 @@
-
- * @category   INEX
- * @package    INEX_Controller
- * @copyright  Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd
- * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
- */
-class Ipv6AddressController extends Ipv4AddressController
-{
-    /**
-     * This function sets up the frontend controller
-     */
-    protected function _feInit()
-    {
-        parent::_feInit();
-        
-        $this->_feParams->entity        = '\\Entities\\IPv6Address';
-        $this->_feParams->pagetitle     = 'IPv6 Addresses';
-        $this->_feParams->titleSingular = 'IPv6 Address';
-        $this->_feParams->nameSingular  = 'an IPv6 address';
-    }
-    
-    public function addAction()
-    {
-        $this->forward( 'add', 'ipv4-address' );
-    }
-}
-
diff --git a/application/controllers/IrrdbConfigController.php b/application/controllers/IrrdbConfigController.php
deleted file mode 100644
index 56d6d12bc..000000000
--- a/application/controllers/IrrdbConfigController.php
+++ /dev/null
@@ -1,94 +0,0 @@
-
- * @category   INEX
- * @package    INEX_Controller
- * @copyright  Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd
- * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
- */
-class IrrdbConfigController extends INEX_Controller_FrontEnd
-{
-    /**
-     * This function sets up the frontend controller
-     */
-    protected function _feInit()
-    {
-        $this->assertPrivilege( \Entities\User::AUTH_SUPERUSER );
-    
-        $this->view->feParams = $this->_feParams = (object)[
-            'entity'        => '\\Entities\\IRRDBConfig',
-            'form'          => 'INEX_Form_IrrdbConfig',
-            'pagetitle'     => 'IRRDB Sources',
-        
-            'titleSingular' => 'IRRDB Source',
-            'nameSingular'  => 'an IRRDB source',
-        
-            'defaultAction' => 'list',                    // OPTIONAL; defaults to 'list'
-        
-            'listOrderBy'    => 'host',
-            'listOrderByDir' => 'ASC',
-        
-            'listColumns'    => [
-                'id'        => [ 'title' => 'UID', 'display' => false ],
-                'host'      => 'Host',
-                'protocol'  => 'Protocol',
-                'source'    => 'Source'
-            ]
-        ];
-    
-        // display the same information in the view as the list
-        $this->_feParams->viewColumns = array_merge(
-            $this->_feParams->listColumns,
-            [ 'notes' => 'Notes' ]
-        );
-    }
-    
-    /**
-     * Provide array of objects for the listAction and viewAction
-     *
-     * @param int $id The `id` of the row to load for `viewAction`. `null` if `listAction`
-     */
-    protected function listGetData( $id = null )
-    {
-        $qb = $this->getD2EM()->createQueryBuilder()
-        ->select( 'o.id AS id, o.host AS host, o.protocol AS protocol,
-                    o.source AS source, o.notes AS notes'
-        )
-        ->from( '\\Entities\\IRRDBConfig', 'o' );
-    
-        if( isset( $this->_feParams->listOrderBy ) )
-            $qb->orderBy( $this->_feParams->listOrderBy, isset( $this->_feParams->listOrderByDir ) ? $this->_feParams->listOrderByDir : 'ASC' );
-    
-        if( $id !== null )
-            $qb->andWhere( 'o.id = ?1' )->setParameter( 1, $id );
-    
-        return $qb->getQuery()->getResult();
-    }
-    
- }
-
diff --git a/application/controllers/LocationController.php b/application/controllers/LocationController.php
deleted file mode 100644
index ead992070..000000000
--- a/application/controllers/LocationController.php
+++ /dev/null
@@ -1,109 +0,0 @@
-
- * @category   INEX
- * @package    INEX_Controller
- * @copyright  Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd
- * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
- */
-class LocationController extends INEX_Controller_FrontEnd
-{
-    
-    /**
-     * This function sets up the frontend controller
-     */
-    protected function _feInit()
-    {
-        $this->assertPrivilege( \Entities\User::AUTH_SUPERUSER );
-    
-        $this->view->feParams = $this->_feParams = (object)[
-            'entity'        => '\\Entities\\Location',
-            'form'          => 'INEX_Form_Location',
-            'pagetitle'     => 'Locations',
-        
-            'titleSingular' => 'Location',
-            'nameSingular'  => 'a location',
-        
-            'defaultAction' => 'list',                    // OPTIONAL; defaults to 'list'
-        
-            'listOrderBy'    => 'name',
-            'listOrderByDir' => 'ASC',
-        
-            'listColumns'    => [
-        
-                'id'        => [ 'title' => 'UID', 'display' => false ],
-                'name'      => 'Name',
-                'shortname' => 'Shortname',
-                'tag'       => 'Tag',
-                'nocphone'  => 'NOC Phone',
-                'nocemail'  => 'NOC Email'
-            ]
-        ];
-    
-        // display the same information in the view as the list
-        $this->_feParams->viewColumns = array_merge(
-            $this->_feParams->listColumns,
-            [
-                'address'     => 'Address',
-                'nocfax'      => 'NOC Fax',
-                'officephone' => 'Office Phone',
-                'officefax'   => 'Office Fax',
-                'officeemail' => 'Office Email',
-                'notes'       => 'Notes'
-            ]
-        );
-    }
-    
-    
-    /**
-     * Provide array of users for the listAction and viewAction
-     *
-     * @param int $id The `id` of the row to load for `viewAction`. `null` if `listAction`
-     */
-    protected function listGetData( $id = null )
-    {
-        $qb = $this->getD2EM()->createQueryBuilder()
-        ->select(
-                'l.id AS id, l.name AS name, l.shortname AS shortname, l.tag AS tag,
-                l.nocphone AS nocphone, l.nocemail AS nocemail, l.address AS address,
-                l.nocfax AS nocfax, l.officephone AS officephone, l.officefax AS officefax,
-                l.officeemail AS officeemail, l.notes AS notes'
-            )
-        ->from( '\\Entities\\Location', 'l' );
-    
-        if( isset( $this->_feParams->listOrderBy ) )
-            $qb->orderBy( $this->_feParams->listOrderBy, isset( $this->_feParams->listOrderByDir ) ? $this->_feParams->listOrderByDir : 'ASC' );
-    
-        if( $id !== null )
-            $qb->andWhere( 'l.id = ?1' )->setParameter( 1, $id );
-    
-        return $qb->getQuery()->getResult();
-    }
-    
-}
-
diff --git a/application/controllers/MacAddressController.php b/application/controllers/MacAddressController.php
deleted file mode 100644
index 030e4b665..000000000
--- a/application/controllers/MacAddressController.php
+++ /dev/null
@@ -1,131 +0,0 @@
-
- * @category   INEX
- * @package    INEX_Controller
- * @copyright  Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd
- * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
- */
-class MacAddressController extends INEX_Controller_FrontEnd
-{
-    /**
-     * This function sets up the frontend controller
-     */
-    protected function _feInit()
-    {
-        $this->assertPrivilege( \Entities\User::AUTH_SUPERUSER );
-    
-        $this->view->feParams = $this->_feParams = (object)[
-            'entity'        => '\\Entities\\MacAddress',
-            'pagetitle'     => 'Known MAC Addresses',
-    
-            'titleSingular' => 'MAC Address',
-            'nameSingular'  => 'a MAC address',
-    
-            'defaultAction' => 'list',                    // OPTIONAL; defaults to 'list'
-        
-            'readonly'      => true,
-            
-            'listOrderBy'    => 'customer',
-            'listOrderByDir' => 'ASC',
-        
-            'listColumns'    => [
-                'id'        => [ 'title' => 'UID', 'display' => false ],
-                
-                'customer'  => [
-                    'title'      => 'Customer',
-                    'type'       => self::$FE_COL_TYPES[ 'HAS_ONE' ],
-                    'controller' => 'customer',
-                    'action'     => 'view',
-                    'idField'    => 'customerid'
-                ],
-                                
-                'interface'  => [
-                    'title'      => 'Interface',
-                    'type'       => self::$FE_COL_TYPES[ 'HAS_ONE' ],
-                    'controller' => 'virtual-interface',
-                    'action'     => 'edit',
-                    'idField'    => 'interfaceid'
-                ],
-                
-                'ipv4'           => 'IPv4',
-                'ipv6'           => 'IPv6',
-                'mac'            => 'MAC Address',
-                
-                'firstseen'      => [
-                    'title'          => 'First Seen',
-                    'type'           => self::$FE_COL_TYPES[ 'DATETIME' ]
-                ],
-                
-                'lastseen'      => [
-                    'title'          => 'Last Seen',
-                    'type'           => self::$FE_COL_TYPES[ 'DATETIME' ]
-                ]
-            ]
-        ];
-    
-        // display the same information in the view as the list
-        $this->_feParams->viewColumns = $this->_feParams->listColumns;
-    }
-
-    /**
-     * Provide array of MAC addresses for the listAction and viewAction
-     *
-     * @param int $id The `id` of the row to load for `viewAction`. `null` if `listAction`
-     */
-    protected function listGetData( $id = null )
-    {
-        $qb = $this->getD2EM()->createQueryBuilder()
-            ->select(
-                'm.id AS id, m.firstseen AS firstseen, m.lastseen AS lastseen, m.mac AS mac,
-                c.id AS customerid, c.name AS customer,
-                vi.id AS interfaceid,
-                CONCAT( CONCAT( s.name, \' - \' ),  sp.name ) AS interface,
-                ip4.address AS ipv4, ip6.address AS ipv6'
-            )
-            ->from( '\\Entities\\MACAddress', 'm' )
-            ->join( 'm.VirtualInterface', 'vi' )
-            ->join( 'vi.VlanInterfaces', 'vli' )
-            ->join( 'vli.IPv4Address', 'ip4' )
-            ->join( 'vli.IPv6Address', 'ip6' )
-            ->join( 'vi.Customer', 'c' )
-            ->join( 'vi.PhysicalInterfaces', 'pi' )
-            ->join( 'pi.SwitchPort', 'sp' )
-            ->join( 'sp.Switcher', 's' );
-            
-        if( isset( $this->_feParams->listOrderBy ) )
-            $qb->orderBy( $this->_feParams->listOrderBy, isset( $this->_feParams->listOrderByDir ) ? $this->_feParams->listOrderByDir : 'ASC' );
-    
-        if( $id !== null )
-            $qb->andWhere( 'm.id = ?1' )->setParameter( 1, $id );
-    
-        return $qb->getQuery()->getResult();
-    }
-    
-}
-
diff --git a/application/controllers/MeetingController.php b/application/controllers/MeetingController.php
deleted file mode 100644
index c06d9ae37..000000000
--- a/application/controllers/MeetingController.php
+++ /dev/null
@@ -1,346 +0,0 @@
-
- * @category   INEX
- * @package    INEX_Controller
- * @copyright  Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd
- * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
- */
-class MeetingController extends INEX_Controller_FrontEnd
-{
-    
-    /**
-     * This function sets up the frontend controller
-     */
-    protected function _feInit()
-    {
-        $this->view->feParams = $this->_feParams = (object)[
-            'entity'        => '\\Entities\\Meeting',
-            'form'          => 'INEX_Form_Meeting',
-            'pagetitle'     => 'Meetings',
-        
-            'titleSingular' => 'Meeting',
-            'nameSingular'  => 'a meeting',
-        
-            'listOrderBy'    => 'date',
-            'listOrderByDir' => 'DESC'
-        ];
-    
-        switch( $this->getUser()->getPrivs() )
-        {
-            case \Entities\User::AUTH_SUPERUSER:
-                $this->_feParams->listColumns = [
-                    'id'        => [ 'title' => 'UID', 'display' => false ],
-                    
-                    'title'     => 'Title',
-        
-                    'date'      => [
-                        'title'     => 'Date',
-                        'type'      => self::$FE_COL_TYPES[ 'DATE' ]
-                    ],
-                    
-                    'time'      => [
-                        'title'     => 'Time',
-                        'type'      => self::$FE_COL_TYPES[ 'TIME' ]
-                    ],
-                    
-                    'created_by'  => [
-                        'title'      => 'Created By',
-                        'type'       => self::$FE_COL_TYPES[ 'HAS_ONE' ],
-                        'controller' => 'customer',
-                        'action'     => 'overview',
-                        'idField'    => 'userid'
-                    ]
-                ];
-    
-                $this->_feParams->defaultAction = 'list';
-                break;
-    
-            case \Entities\User::AUTH_CUSTUSER:
-                $this->_feParams->allowedActions = [ 'read', 'rsvp' ];
-                $this->_feParams->defaultAction = 'read';
-                break;
-    
-            default:
-                $this->_feParams->allowedActions = [ 'simple' ];
-                $this->_feParams->defaultAction = 'simple';
-                break;
-        }
-    }
-    
-    /**
-     * Provide array of users for the listAction and viewAction
-     *
-     * @param int $id The `id` of the row to load for `viewAction`. `null` if `listAction`
-     */
-    protected function listGetData( $id = null )
-    {
-        $qb = $this->getD2EM()->createQueryBuilder()
-            ->select( 'm.id AS id, m.title AS title, m.date AS date, m.time AS time,
-                        m.before_text AS before_text, m.after_text AS after_text,
-                        m.venue AS venue, m.venue_url AS venue_url, m.created_at AS created_at,
-                        m.updated_at AS updated_at,
-                        u.id AS userid, u.username AS created_by'
-            )
-            ->from( '\\Entities\\Meeting', 'm' )
-            ->leftJoin( 'm.CreatedBy', 'u' );
-    
-        if( isset( $this->_feParams->listOrderBy ) )
-            $qb->orderBy( $this->_feParams->listOrderBy, isset( $this->_feParams->listOrderByDir ) ? $this->_feParams->listOrderByDir : 'ASC' );
-    
-        if( $id !== null )
-            $qb->andWhere( 'm.id = ?1' )->setParameter( 1, $id );
-    
-        return $qb->getQuery()->getResult();
-    }
-    
-
-    /**
-     * Preparation hook that can be overridden by subclasses for add and edit.
-     *
-     * This is called just before we process a possible POST / submission and
-     * will allow us to change / alter the form or object.
-     *
-     * @param INEX_Form_Meeting $form The Send form object
-     * @param \Entities\Meeting $object The Doctrine2 entity (being edited or blank for add)
-     * @param bool $isEdit True if we are editing, otherwise false
-     */
-    protected function addPrepare( $form, $object, $isEdit )
-    {
-        if( $isEdit )
-        {
-            $form->getElement( 'date' )->setValue( $object->getDate()->format( 'Y-m-d' ) );
-            $form->getElement( 'time' )->setValue( $object->getTime()->format( 'H:i' ) );
-        }
-        
-        return true;
-    }
-    
-    /**
-     *
-     * @param INEX_Form_Meeting $form The form object
-     * @param \Entities\Meeting $object The Doctrine2 entity (being edited or blank for add)
-     * @param bool $isEdit True of we are editing an object, false otherwise
-     * @return void
-     */
-    protected function addPostValidate( $form, $object, $isEdit )
-    {
-        $object->setUpdatedBy( $this->getUser()->getId() );
-        $object->setUpdatedAt( new DateTime() );
-        
-        if( !$isEdit )
-        {
-            $object->setCreatedBy( $this->getUser() );
-            $object->setCreatedAt( new DateTime() );
-        }
-            
-        return true;
-    }
-    
-    
-    /**
-     *
-     * @param INEX_Form_Meeting $form The form object
-     * @param \Entities\Meeting $object The Doctrine2 entity (being edited or blank for add)
-     * @param bool $isEdit True if we are editing, otherwise false
-     * @return bool If false, the form is not processed
-     */
-    protected function addPreFlush( $form, $object, $isEdit )
-    {
-    
-        if( !( $object->getDate() instanceof DateTime ) )
-            $object->setDate( new DateTime( $form->getValue( 'date' ) ) );
-    
-        if( !( $object->getTime() instanceof DateTime ) )
-            $object->setTime( new DateTime( $form->getValue( 'time' ) ) );
-    
-        return true;
-    }
-    
-    
-    public function readAction()
-    {
-        $this->view->entries = $this->getD2EM()->createQuery(
-                'SELECT m, mi FROM \\Entities\\Meeting m LEFT JOIN m.MeetingItems mi ORDER BY m.date DESC, mi.other_content ASC'
-            )
-            ->execute();
-
-        $this->view->simple  = false;
-    }
-
-    /**
-     * A simple HTML snippet for display on other websites
-     */
-    public function simpleAction()
-    {
-        $this->view->entries = $this->getD2EM()->createQuery(
-                'SELECT m, mi FROM \\Entities\\Meeting m LEFT JOIN m.MeetingItems mi ORDER BY m.date DESC, mi.other_content ASC'
-            )
-            ->execute();
-        
-        $this->view->simple  = true;
-
-        if( $this->getParam( 'nostyle', false ) )
-        {
-            Zend_Controller_Action_HelperBroker::removeHelper( 'viewRenderer' );
-            $this->view->display( 'meeting/simple2.phtml' );
-        }
-        else
-            $this->view->display( 'meeting/simple.phtml' );
-    }
-
-
-    public function rsvpAction()
-    {
-        die( "Needs update to IXP V2 Rewrite with Doctrine2" );
-        /*
-        $meeting = Doctrine_Core::getTable( 'Meeting' )->find( $this->_request->getParam( 'id', null ) );
-
-        if( !$meeting )
-            exit;
-
-        $answer = $this->_getParam( 'answer', null );
-
-        if( !in_array( $answer, array( 'attend', 'noattend', 'skip', 'dontask' ) ) )
-            exit;
-
-        $response = 1;
-        $msg = false;
-
-        switch( $answer )
-        {
-            case 'skip':
-                $this->getLogger()->debug( 'User skipped meeting RSVP request' );
-                $this->session->dashboard_skip_meeting = true;
-                break;
-
-            case 'dontask':
-                $this->getLogger()->debug( 'User asked not to be asked to RSVP again' );
-                $this->getUser()->setPreference( 'meeting.attending.' . $meeting['id'], 'DONT_ASK' );
-                break;
-
-            case 'attend':
-                $msg = 'ATTEND';
-                $this->getLogger()->debug( 'User will be attending this meeting' );
-                $this->getUser()->setPreference( 'meeting.attending.' . $meeting['id'], 'ATTENDING' );
-                break;
-
-            case 'noattend':
-                $msg = 'NOT ATTEND';
-                $this->getLogger()->debug( 'User will not be attending this meeting' );
-                $this->getUser()->setPreference( 'meeting.attending.' . $meeting['id'], 'NOT_ATTENDING' );
-                break;
-
-        }
-
-        if( $msg !== false )
-        {
-            $mail = new Zend_Mail();
-            $mail->addTo( $this->config['meeting']['rsvp_to_email'], $this->config['meeting']['rsvp_to_name'] )
-                 ->setSubject( '[Meeting RSVP] ' . $msg . ': ' . $this->getUser()->email . '/' . $this->customer['name'] )
-                 ->setBodyText( "\nThis is an automated message from the IXP Manager.\n\n"
-                        . "The following person has indicated that they will $msg the meeting scheduled for {$meeting['date']}\n\n"
-                        . "{$this->getUser()->username} / {$this->getUser()->email} / {$this->customer['name']}\n\n"
-                 );
-            $mail->setFrom( $this->_config['identity']['autobot']['email'] );
-
-            try {
-                $mail->send();
-            } catch( Zend_Mail_Exception $e ) {
-                $response = 0;
-                $this->getLogger()->err( $e->getMessage() );
-            }
-        }
-
-        $this->getResponse()
-            ->setHeader( 'Content-Type', 'text/html' )
-            ->setBody( Zend_Json::encode( array( 'response' => $response ) ) )
-            ->sendResponse();
-
-        exit;
-        */
-    }
-
-
-    public function composeAction()
-    {
-        $this->view->meeting = $meeting = $this->getD2EM()->getRepository( '\\Entities\\Meeting' )->find( $this->getParam( 'id' ) );
-        
-        if( !$meeting )
-        {
-            $this->addMessage( "Invalid meeting selected", OSS_Message::ERROR );
-            $this->redirectAndEnsureDie( 'meeting/list' );
-        }
-
-        do
-        {
-
-	        if( $this->getParam( 'send', false ) )
-	        {
-                $this->view->to      = $this->getParam( 'to' );
-                $this->view->from    = $this->getParam( 'from' );
-                $this->view->bcc     = $this->getParam( 'bcc' );
-                $this->view->subject = trim( stripslashes( $this->getParam( 'subject' ) ) );
-                $this->view->body    = trim( stripslashes( $this->getParam( 'body' ) ) );
-
-	            foreach( array( 'to', 'from', 'bcc' ) as $p )
-	            {
-	                $v = trim( $this->_getParam( $p ) );
-	                $$p = $v;
-
-	                if( $p == 'bcc' && $v == '' ) continue;
-
-	                if( !Zend_Validate::is( $v, 'EmailAddress' ) )
-	                {
-	                    $this->addMessage( "Invalid email address in the '$p' field", OSS_Message::ERROR );
-	                    break 2;
-	                }
-	            }
-
-                $mail = $this->getMailer();
-                $mail->addTo( $to );
-                $mail->setFrom( $from );
-                if( $bcc != '' ) $mail->addBcc( $bcc );
-                $mail->setSubject( trim( stripslashes( $this->getParam( 'subject' ) ) ) );
-                $mail->setBodyHtml( $this->view->render( 'meeting/email/meeting.phtml' ), 'utf8' );
-
-	            try {
-	                $mail->send();
-	                $this->addMessage( "Email sent successfully", OSS_Message::SUCCESS );
-	            } catch( Zend_Mail_Exception $e ) {
-                    $thisaddMessage( "Error: Could not send email.", OSS_Message::ERROR );
-	                $this->getLogger()->err( $e->getMessage() );
-	            }
-
-	        }
-
-        }while( false );
-    }
-
-}
-
diff --git a/application/controllers/MeetingItemController.php b/application/controllers/MeetingItemController.php
deleted file mode 100644
index f7bd9502d..000000000
--- a/application/controllers/MeetingItemController.php
+++ /dev/null
@@ -1,305 +0,0 @@
-
- * @category   INEX
- * @package    INEX_Controller
- * @copyright  Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd
- * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
- */
-class MeetingItemController extends INEX_Controller_FrontEnd
-{
-    /**
-     * This function sets up the frontend controller
-     */
-    protected function _feInit()
-    {
-        $this->view->feParams = $this->_feParams = (object)[
-            'entity'        => '\\Entities\\MeetingItem',
-            'form'          => 'INEX_Form_Meeting_Item',
-            'pagetitle'     => 'Presentations',
-        
-            'titleSingular' => 'Presentation',
-            'nameSingular'  => 'a presentation',
-        
-            'listOrderBy'    => 'name',
-            'listOrderByDir' => 'DESC'
-        ];
-    
-        switch( $this->getUser()->getPrivs() )
-        {
-            case \Entities\User::AUTH_SUPERUSER:
-                $this->_feParams->listColumns = [
-                    'id'        => [ 'title' => 'UID', 'display' => false ],
-                    'title'     => 'Title',
-                    'name'      => 'Name',
-                    'company'   => 'Company',
-                    
-                    'mtitle'  => [
-                        'title'      => 'Meeting',
-                        'type'       => self::$FE_COL_TYPES[ 'HAS_ONE' ],
-                        'controller' => 'meeting',
-                        'action'     => 'view',
-                        'idField'    => 'mid'
-                    ]
-                ];
-                $this->_feParams->defaultAction = 'list';
-                break;
-    
-            case \Entities\User::AUTH_CUSTUSER:
-                $this->_feParams->allowedActions = [ 'get-presentation' ];
-                break;
-    
-            default:
-                $this->redirectAndEnsureDie( 'error/insufficient-permissions' );
-                break;
-        }
-    }
-
-
-    /**
-     * Provide array of presentations
-     *
-     * @param int $id The `id` of the row to load for `viewAction`. `null` if `listAction`
-     */
-    protected function listGetData( $id = null )
-    {
-        $this->view->meetings = $meetings = $this->getD2EM()->getRepository( '\\Entities\\Meeting' )->getTitles();
-        
-        $qb = $this->getD2EM()->createQueryBuilder()
-            ->select( 'mi.id AS id, mi.title AS title, mi.name AS name, mi.role AS role,
-                        mi.email AS email, mi.company AS company, mi.company_url AS company_url,
-                        mi.summary AS summary, mi.presentation AS presentation, mi.filename AS filename,
-                        mi.created_by AS created_by, mi.created_at AS created_at,
-                        mi.updated_by AS updated_by, mi.updated_at AS updated_at,
-                        m.id AS mid, m.title AS mtitle'
-        )
-        ->from( '\\Entities\\MeetingItem', 'mi' )
-        ->leftJoin( 'mi.Meeting', 'm' );
-    
-        if( isset( $this->_feParams->listOrderBy ) )
-            $qb->orderBy( $this->_feParams->listOrderBy, isset( $this->_feParams->listOrderByDir ) ? $this->_feParams->listOrderByDir : 'ASC' );
-    
-        if( $id !== null )
-            $qb->andWhere( 'mi.id = ?1' )->setParameter( 1, $id );
-    
-        if( ( $mid = $this->getParam( 'mid', false ) ) && isset( $meetings[$mid] ) )
-        {
-            $this->view->mid = $mid;
-            $qb->where( 'm.id = ?2' )->setParameter( 2, $mid );
-        }
-        
-        return $qb->getQuery()->getResult();
-    }
-    
-    /**
-     * Return the presentation file
-     */
-    protected function getPresentationAction()
-    {
-        Zend_Controller_Action_HelperBroker::removeHelper( 'viewRenderer' );
-        
-        if( !( $pres = $this->getD2EM()->getRepository( '\\Entities\\MeetingItem' )->find( $this->getParam( 'id', null ) ) ) )
-        {
-            $this->addMessage(
-                'The requested presentation does not exist or does not have an associated file attached to it.',
-                OSS_Message::ERROR
-            );
-            $this->redirect( 'meeting/read' );
-        }
-
-        $fn = "INEX_Members_Meeting_{$pres->getMeeting()->getDate()}_({$pres->getId()}).";
-
-        // What kind of file do we have?
-        if( preg_match( '/pdf$/i', $pres->getFilename() ) ) {
-            header('Content-type: application/pdf');
-            $fn .= 'pdf';
-        }
-        else if( preg_match( '/ppt$/i', $pres->getFilename() ) ) {
-            header('Content-type: application/vnd.ms-powerpoint');
-            $fn .= 'ppt';
-        }
-        else if( preg_match( '/pps$/i', $pres->getFilename() ) ) {
-            header('Content-type: application/vnd.ms-powerpoint');
-            $fn .= 'pps';
-        }
-        else if( preg_match( '/pptx$/i', $pres->getFilename() ) ) {
-            header( 'Content-type: application/vnd.ms-powerpoint' );
-            $fn .= 'pptx';
-        }
-        else {
-            header( 'Content-type: application/octet-stream' );
-            $fn .= substr( $pres->getFilename(), strrpos( $pres->getFilename(), '.' ) );
-        }
-
-
-        header( 'Content-Disposition: attachment; filename="' . $fn . '"' );
-
-        echo @file_get_contents( self::getMeetingsDirectory() . DIRECTORY_SEPARATOR
-                . $pres->getMeeting()->getId() . DIRECTORY_SEPARATOR . $pres->getPresentation()
-        );
-    }
-
-
-    /**
-     *
-     * @param INEX_Form_Meeting_Item $form
-     * @param \Entities\MeetingItem $object
-     * @param bool $isEdit
-     * @param array $options Options passed onto Zend_Form
-     * @param string $cancelLocation Where to redirect to if 'Cancal' is clicked
-     * @return void
-     */
-    protected function formPostProcess( $form, $object, $isEdit, $options = null, $cancelLocation = null )
-    {
-        if( $isEdit )
-            $form->getElement( 'meeting_id' )->setValue( $object->getMeeting()->getId() );
-    }
-    
-    
-    /**
-     *
-     * @param INEX_Form_Meeting_Item $form
-     * @param \Entities\MeetingItem $object
-     * @param bool $isEdit
-     * @return void
-     */
-    protected function addPostValidate( $form, $object, $isEdit )
-    {
-        $object->setMeeting(
-            $this->getD2EM()->getRepository( '\\Entities\\Meeting' )->find( $form->getElement( 'meeting_id' )->getValue() )
-        );
-    
-        return true;
-    }
-    
-    /**
-     *
-     * @param INEX_Form_Meeting_Item $form
-     * @param \Entities\MeetingItem $object
-     * @param bool $isEdit
-     */
-    protected function addPreFlush( $form, $object, $isEdit )
-    {
-        $object->setUpdatedBy( $this->getUser()->getId() );
-        $object->setUpdatedAt( new DateTime() );
-        
-        if( !$isEdit )
-        {
-            $object->setCreatedBy( $this->getUser()->getId() );
-            $object->setCreatedAt( new DateTime() );
-        }
-
-        // is there a file upload?
-        if( $form->getValue( 'presentation' ) != '' )
-        {
-            // lets make more memory available for large files
-            ini_set( 'memory_limit', '512M' );
-
-            $this->getLogger()->debug( 'Received upload of file: ' . $form->getValue( 'presentation' ) );
-
-            // Zend sticks the original filename in the form variable
-            $object->setFilename( $form->getValue( 'presentation' ) );
-
-            // make sure meetings exists
-            if( !is_dir( self::getMeetingsDirectory() ) && !@mkdir( self::getMeetingsDirectory() ) )
-            {
-                $this->getLogger()->crit( 'Could not create presentations directory.' );
-                throw new INEX_Exception( 'Presentations directory does not exist and could not be created.' );
-            }
-
-            // now, create a directory for this meeting if it does not already exists
-            $meeting_dir = self::getMeetingsDirectory() . DIRECTORY_SEPARATOR . $object->getMeeting()->getId();
-
-            if( !is_dir( $meeting_dir ) && !@mkdir( $meeting_dir ) )
-            {
-                $this->getLogger()->crit( 'Could not create meeting directory.' );
-                throw new INEX_Exception( 'Meeting directory does not exist and could not be created.' );
-            }
-
-            // get the extension for this presentation
-
-            if( strrpos( $object->getFilename(), '.' ) === false )
-                $exten = '';
-            else
-                $exten = substr( $object->getFilename(), strrpos( $object->getFilename(), '.' ) );
-
-            // we need the row ID so we'll do a save
-            if( !$isEdit )
-                $this->getD2EM()->persist( $object );
-            $this->getD2EM()->flush();
-            
-            $object->setPresentation( $object->getId() . $exten );
-            $this->getLogger()->debug( 'Uploaded file will be saved as: ' . $object->getPresentation() );
-
-            // delete an existing file in case we're updating
-            $ePres = $meeting_dir . DIRECTORY_SEPARATOR . $object->getPresentation();
-            if( @file_exists( $ePres ) )
-            {
-                $this->getLogger()->debug( 'Pre-existing file exists so deleteing' );
-                @unlink( $ePres );
-            }
-
-            @rename( $form->getElement( 'presentation' )->getFilename(), $ePres );
-        }
-        
-        return true;
-    }
-
-    /**
-     * Before deleting a meeting, delete meeting items.
-     *
-     * @param \Entities\MeetingItem $object
-     */
-    protected function preDelete( $object )
-    {
-        // if a presentation exists, remove it
-        $dir = self::getMeetingsDirectory() . DIRECTORY_SEPARATOR . $object->getMeeting()->getId();
-        
-        $file = $dir . DIRECTORY_SEPARATOR . $object->getPresentation();
-        
-        if( file_exists( $file ) )
-            @unlink( $file );
-        
-        // remove the directory also if it is empty
-        if( count( @scandir( $dir ) ) == 2 )
-            @rmdir( $dir );
-        
-        return true;
-    }
-    
-    
-    /**
-     * Return the path where meeting presentations are stored
-     * @return string The path where meeting presentations are stored
-     */
-    public static function getMeetingsDirectory()
-    {
-        // We're going to store presentations in the var directory under meetings.
-        return APPLICATION_PATH . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR
-                . 'var' . DIRECTORY_SEPARATOR . 'meetings';
-    }
-}
diff --git a/application/controllers/MrtgController.php b/application/controllers/MrtgController.php
deleted file mode 100644
index 32681ecf6..000000000
--- a/application/controllers/MrtgController.php
+++ /dev/null
@@ -1,227 +0,0 @@
-
- * @category   INEX
- * @package    INEX_Controller
- * @copyright  Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd
- * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
- */
-class MrtgController extends INEX_Controller_AuthRequiredAction
-{
-
-    public static $GRAPH_CATEGORIES = array (
-        'bits' => 'Bits',
-        'pkts' => 'Packets',
-        'errs' => 'Errors',
-        'discs' => 'Discards',
-    );
-
-    protected $_flock = null;
-
-    
-    public function preDispatch()
-    {
-        Zend_Controller_Action_HelperBroker::removeHelper( 'viewRenderer' );
-    }
-
-    private function checkShortname( $shortname )
-    {
-        return $this->getD2EM()->getRepository( '\\Entities\\Customer' )->findOneBy( [ 'shortname' => $shortname ] );
-    }
-
-
-    function retrieveImageAction()
-    {
-        header( 'Content-Type: image/png' );
-        header( 'Expires: Thu, 01 Jan 1970 00:00:00 GMT' );
-
-        $monitorindex = $this->getRequest()->getParam( 'monitorindex', 'aggregate' );
-        $period       = $this->getRequest()->getParam( 'period', INEX_Mrtg::$PERIODS['Day'] );
-        $shortname    = $this->getRequest()->getParam( 'shortname' );
-        $category     = $this->getRequest()->getParam( 'category', INEX_Mrtg::$CATEGORIES['Bits'] );
-        $graph        = $this->getRequest()->getParam( 'graph', '' );
-
-        $this->getLogger()->debug( "Request for {$shortname}-{$monitorindex}-{$category}-{$period}-{$graph} by {$this->getUser()->getUsername()}" );
-
-        if( $shortname == 'X_Trunks' )
-        {
-            $filename = $this->_options['mrtg']['path']
-                . '/trunks/' . $graph . '-' . $period . '.png';
-        }
-        else if( $shortname == 'X_SwitchAggregate' )
-        {
-            $filename = $this->_options['mrtg']['path']
-                . '/switches/switch-aggregate-' . $graph . '-'
-                . $category . '-' . $period . '.png';
-        }
-        else if( $shortname == 'X_Peering' )
-        {
-            $filename = $this->_options['mrtg']['path']
-                . '/ixp_peering-' . $graph . '-'
-                . $category . '-' . $period . '.png';
-        }
-        else
-        {
-            if( $this->getUser()->getPrivs() != \Entities\User::AUTH_SUPERUSER || !$this->checkShortname( $shortname ) )
-                $shortname = $this->getCustomer()->getShortname();
-
-            $filename = INEX_Mrtg::getMrtgFilePath( $this->_options['mrtg']['path'] . '/members'    , 'PNG',
-                $monitorindex, $category, $shortname, $period
-            );
-        }
-
-        $this->getLogger()->debug( "Serving {$filename} to {$this->getUser()->getUsername()}" );
-
-        $stat = @readfile( $filename );
-
-        if( $stat === false )
-        {
-            $this->getLogger()->debug( "Could not load {$filename} for mrtg/retrieveImageAction" );
-            echo readfile(
-                APPLICATION_PATH . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR
-                    . 'public' . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR
-                    . 'image-missing.png'
-            );
-        }
-    }
-
-
-    function retrieveP2pImageAction()
-    {
-        header( 'Content-Type: image/png' );
-        header( 'Expires: Thu, 01 Jan 1970 00:00:00 GMT' );
-
-        $period       = $this->getRequest()->getParam( 'period',    INEX_Mrtg::$PERIODS['Day'] );
-        $shortname    = $this->getRequest()->getParam( 'shortname', false );
-        $svid         = $this->getRequest()->getParam( 'svid',      false );
-        $dvid         = $this->getRequest()->getParam( 'dvid',      false );
-        $category     = $this->getRequest()->getParam( 'category',  INEX_Mrtg::$CATEGORIES['Bits'] );
-        $proto        = $this->getRequest()->getParam( 'proto',     INEX_Mrtg::PROTOCOL_IPV4 );
-        $infra        = $this->getRequest()->getParam( 'infra',     INEX_Mrtg::INFRASTRUCTURE_PRIMARY );
-        $period       = $this->getRequest()->getParam( 'period',    INEX_Mrtg::PERIOD_DAY );
-        
-        if( !$this->identity )
-            exit(0);
-
-        $_cust = $this->checkShortname( $shortname );
-        
-        if( $this->user['privs'] < User::AUTH_SUPERUSER || !$_cust )
-        {
-            $shortname = $this->customer['shortname'];
-            $_cust = $this->customer;
-        }
-        
-        // make sure the svid and dvid is valid
-        if( !$svid || !$dvid )
-        {
-            $this->logger->alert( "P2P file request with svid={$svid} and pvid={$pvid}" );
-            die();
-        }
-        
-        $svidOk = false;
-        foreach( $_cust->Virtualinterface as $vint )
-        {
-            if( $vint['id'] == $svid )
-            {
-                $svidOk = true;
-                break;
-            }
-        }
-
-        // make sure the svid and dvid is valid
-        if( !$svidOk )
-        {
-            $this->logger->alert( "P2P file request with illegal svid={$svid} for {$shortname}" );
-            die();
-        }
-        
-        // find the possible virtual interfaces that this customer peers with
-        $dvidOk = false;
-        $dshortname = '';
-        $customersWithVirtualInterfaces = Doctrine_Query::create()
-        ->select( '
-                c.id, c.name, c.shortname, vi.id, pi.id, vint.id, sp.id, s.id
-                ' )
-        ->from( 'Cust c' )
-        ->leftJoin( 'c.Virtualinterface vi' )
-        ->leftJoin( 'vi.Physicalinterface pi' )
-        ->leftJoin( 'vi.Vlaninterface vint' )
-        ->leftJoin( 'pi.Switchport sp' )
-        ->leftJoin( 'sp.SwitchTable s' )
-        ->where( 's.infrastructure = ?', $infra )
-        ->andWhere( 'vint.ipv' . $proto . 'enabled = 1' )
-        ->andWhere( 'c.shortname != ?', $shortname )
-        ->andWhere( 'c.type != ?', Cust::TYPE_INTERNAL )
-        ->orderBy( 'c.name ASC' )
-        ->fetchArray();
-        
-        foreach( $customersWithVirtualInterfaces as $c )
-        {
-            foreach( $c['Virtualinterface'] as $cvint )
-            {
-                if( $cvint['id'] == $dvid )
-                {
-                    $dshortname = $c['shortname'];
-                    $dvidOk = true;
-                    break 2;
-                }
-            }
-        }
-        
-        // make sure the svid and dvid is valid
-        if( !$dvidOk )
-        {
-            $this->logger->alert( "P2P file request with illegal pvid={$dvid} for {$shortname}" );
-            die();
-        }
-        
-        $filename = INEX_Mrtg::getMrtgP2pFilePath( $this->_options['mrtg']['p2ppath'],
-            $svid, $dvid, $category, $period, $proto
-        );
-        
-        $this->logger->debug( "Serving $filename to {$this->user->username}" );
-
-        $this->logger->info( "P2P request for {$shortname}-{$dshortname}-{$category}-{$period}-ipv{$proto} by {$this->user->username}" );
-        
-        $stat = @readfile( $filename );
-
-        if( !$stat )
-        {
-            $this->logger->debug( 'Could not load ' . $filename . ' for mrtg/retrieveImageAction' );
-            readfile(
-                APPLICATION_PATH . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR
-                    . 'public' . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR
-                    . '300x1.png'
-            );
-        }
-    }
-
-
-}
-
-
diff --git a/application/controllers/PeeringManagerController.php b/application/controllers/PeeringManagerController.php
deleted file mode 100644
index 82dd3a32c..000000000
--- a/application/controllers/PeeringManagerController.php
+++ /dev/null
@@ -1,433 +0,0 @@
-
- * @category   INEX
- * @package    INEX_Controller
- * @copyright  Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd
- * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
- */
-class PeeringManagerController extends INEX_Controller_AuthRequiredAction
-{
-
-    public function preDispatch()
-    {
-        // we should only be available to CUSTUSERs
-        if( $this->getUser()->getPrivs() != \Entities\User::AUTH_CUSTUSER )
-        {
-            $this->addMessage( "You must be logged in as a standard user to access the peering manager.",
-                OSS_Message::ERROR
-            );
-            $this->_redirect( '' );
-        }
-    }
-    
-
-    public function indexAction()
-    {
-        $this->view->vlans  = $vlans  = $this->getD2EM()->getRepository( '\\Entities\\Vlan' )->getPeeringVLANs();
-        $this->view->protos = $protos = [ 4, 6 ];
-        
-        $bilat = array();
-        foreach( $vlans as $vlan )
-            foreach( $protos as $proto )
-                $bilat[ $vlan->getNumber() ][$proto ] = $this->getD2EM()->getRepository( '\\Entities\\BGPSessionData' )->getPeers( $vlan->getId(), $proto );
-        
-        $this->view->bilat = $bilat;
-
-        $peers = $this->getD2EM()->getRepository( '\\Entities\\Customer' )->getPeers( $this->getCustomer()->getId() );
-        foreach( $peers as $i => $p )
-        {
-            // days since last peering request email sent
-            if( !$p['email_last_sent'] )
-                $peers[ $i ]['email_days'] = 0;
-            else
-                $peers[ $i ]['email_days'] = floor( ( time() - $p['email_last_sent']->getTimestamp() ) / 86400 );
-        }
-        $this->view->peers = $peers;
-        
-        $custs = $this->getD2EM()->getRepository( '\\Entities\\Customer' )->getForPeeringManager();
-
-        $this->view->me = $me = $custs[ $this->getCustomer()->getAutsys() ];
-        $this->view->myasn = $this->getCustomer()->getAutsys();
-        unset( $custs[ $this->getCustomer()->getAutsys() ] );
-        
-        $potential       = [];
-        $potential_bilat = [];
-        $peered          = [];
-        $rejected        = [];
-        
-        foreach( $custs as $c )
-        {
-            $custs[ $c['autsys' ] ]['ispotential'] = false;
-            
-            foreach( $vlans as $vlan )
-            {
-                if( isset( $me['vlaninterfaces'][ $vlan->getNumber() ] ) )
-                {
-                    if( isset( $c['vlaninterfaces'][$vlan->getNumber()] ) )
-                    {
-                        foreach( $protos as $proto )
-                        {
-                            if( $me['vlaninterfaces'][$vlan->getNumber()][0]["ipv{$proto}enabled"] && $c['vlaninterfaces'][$vlan->getNumber()][0]["ipv{$proto}enabled"] )
-                            {
-                                if( in_array( $c['autsys'], $bilat[$vlan->getNumber()][4][$me['autsys']]['peers'] ) )
-                                    $custs[ $c['autsys'] ][$vlan->getNumber()][$proto] = 2;
-                                else if( $me['vlaninterfaces'][$vlan->getNumber()][0]['rsclient'] && $c['vlaninterfaces'][$vlan->getNumber()][0]['rsclient'] )
-                                {
-                                    $custs[ $c['autsys'] ][$vlan->getNumber()][$proto] = 1;
-                                    $custs[ $c['autsys' ] ]['ispotential'] = true;
-                                }
-                                else
-                                {
-                                    $custs[ $c['autsys'] ][$vlan->getNumber()][$proto] = 0;
-                                    $custs[ $c['autsys' ] ]['ispotential'] = true;
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-        }
-        
-        foreach( $custs as $c )
-        {
-            $peered[          $c['autsys' ] ] = false;
-            $potential_bilat[ $c['autsys' ] ] = false;
-            $potential[       $c['autsys' ] ] = false;
-            $rejected[        $c['autsys' ] ] = false;
-            
-            foreach( $vlans as $vlan )
-            {
-                foreach( $protos as $proto )
-                {
-                    if( isset( $c[$vlan->getNumber()][$proto] ) )
-                    {
-                        switch( $c[$vlan->getNumber()][$proto] )
-                        {
-                            case 2:
-                                $peered[ $c['autsys' ] ] = true;
-                                break;
-                                
-                            case 1:
-                                $peered[          $c['autsys' ] ] = true;
-                                $potential_bilat[ $c['autsys' ] ] = true;
-                                break;
-                                
-                            case 0:
-                                $potential[       $c['autsys' ] ] = true;
-                                $potential_bilat[ $c['autsys' ] ] = true;
-                                break;
-                                
-                        }
-                    }
-                }
-            }
-        }
-
-        foreach( $custs as $c )
-        {
-            if( isset( $peers[ $c['id'] ] ) )
-            {
-                if( isset( $peers[ $c['id'] ]['peered'] ) && $peers[ $c['id'] ]['peered'] )
-                {
-                    $peered[ $c['autsys' ] ] = true;
-                    $rejected[ $c['autsys' ] ] = false;
-                    $potential[ $c['autsys' ] ] = false;
-                    $potential_bilat[ $c['autsys' ] ] = false;
-                }
-                else if( isset( $peers[ $c['id'] ]['rejected'] ) && $peers[ $c['id'] ]['rejected'] )
-                {
-                    $peered[ $c['autsys' ] ] = false;
-                    $rejected[ $c['autsys' ] ] = true;
-                    $potential[ $c['autsys' ] ] = false;
-                    $potential_bilat[ $c['autsys' ] ] = false;
-                }
-            }
-        }
-        
-        $this->view->custs = $custs;
-        
-        $this->view->potential       = $potential;
-        $this->view->potential_bilat = $potential_bilat;
-        $this->view->peered          = $peered;
-        $this->view->rejected        = $rejected;
-        
-        //echo '
'; print_r( $custs ); die();
-        
-        $this->view->date = date( 'Y-m-d' );
-    }
-
-
-
-
-    public function peeringRequestAction()
-    {
-        $TESTMODE = false;
-        
-        $peer = $this->_loadPeer( $this->getParam( 'custid', null ) );
-        $f = new INEX_Form_PeeringRequest();
-        
-        // potential peerings
-        $pp = array(); $count = 0;
-        
-        foreach( $this->getCustomer()->getVirtualInterfaces() as $myvis )
-        {
-            foreach( $myvis->getVlanInterfaces() as $myvli )
-            {
-                // does b member have one (or more than one)?
-                foreach( $peer->getVirtualInterfaces() as $pvis )
-                {
-                    foreach( $pvis->getVlanInterfaces() as $pvli )
-                    {
-                        if( $myvli->getVlan()->getId() == $pvli->getVlan()->getId() )
-                        {
-                            $pp[$count]['my']   = $myvli;
-                            $pp[$count]['your'] = $pvli;
-                            $count++;
-                        }
-                    }
-                }
-            }
-        }
-        
-        // INEX_Debug::dd( $pp );
-        $this->view->pp = $pp;
-        
-        $f->getElement( 'to' )->setValue( $peer->getPeeringemail() );
-        $f->getElement( 'cc' )->setValue( $this->getCustomer()->getPeeringemail() );
-
-        if( $this->getRequest()->isPost() )
-        {
-            if( $f->isValid( $_POST ) )
-            {
-                $sendtome = $f->getValue( 'sendtome' ) == '1' ? true : false;
-                $marksent = $f->getValue( 'marksent' ) == '1' ? true : false;
-                
-                $bccOk = true;
-                $bcc = [];
-                if( !$sendtome )
-                {
-                    if( strlen( $bccs = $f->getValue( 'bcc' ) ) )
-                    {
-                        foreach( explode( ',', $bccs ) as $b )
-                        {
-                            $b = trim( $b );
-                            if( !Zend_Validate::is( $b, 'EmailAddress' ) )
-                            {
-                                $f->getElement( 'bcc' )->addError( 'One or more email address(es) here are invalid' );
-                                $bccOk = false;
-                            }
-                            else
-                                $bcc[] = $b;
-                        }
-                    }
-                }
-                                
-                if( $bccOk )
-                {
-                
-                    $mail = new Zend_Mail();
-                    $mail->setFrom( 'no-reply@inex.ie', $this->getCustomer()->getName() . ' Peering Team' )
-                         ->setReplyTo( $this->getCustomer()->getPeeringemail(), $this->getCustomer()->getName() . ' Peering Team' )
-                         ->setSubject( $f->getValue( 'subject' ) )
-                         ->setBodyText( $f->getValue( 'message' ) );
-
-                    if( $sendtome )
-                    {
-                        $mail->addTo( $this->getUser()->getEmail() );
-                    }
-                    else
-                    {
-                        $mail->addTo( $TESTMODE ? 'barryo@inex.ie' : $peer->getPeeringemail(), "{$peer->getName()} Peering Team" )
-                             ->addCc( $TESTMODE ? 'barryo@inex.ie' : $this->getCustomer()->getPeeringemail(), "{$this->getCustomer()->getName()} Peering Team" );
-                    }
-                    
-                    if( count( $bcc ) )
-                        foreach( $bcc as $b )
-                            $mail->addBcc( $b );
-                    
-                    try {
-                        if( !$marksent )
-                            $mail->send();
-                        
-                        if( !$sendtome )
-                        {
-                            // get this customer/peer peering manager table entry
-                            $pm = $this->_loadPeeringManagerEntry( $this->getCustomer(), $peer );
-                            $pm->setEmailLastSent( new DateTime() );
-                            $pm->setEmailsSent( $pm->getEmailsSent() + 1 );
-                            $pm->setUpdated( new DateTime() );
-                            $pm->setNotes(
-                                date( 'Y-m-d' ) . " [{$this->getUser()->getUsername()}]: peering request " . ( $marksent ? 'marked ' : '' ) . "sent\n\n" . $pm->getNotes()
-                            );
-                                                                
-                            $this->getD2EM()->flush();
-                        }
-                    }
-                    catch( Zend_Exception $e )
-                    {
-                        $this->getLogger()->err( $e->getMessage() . "\n\n" . $e->getTraceAsString() );
-                        echo "ERR:Could not send the peering email. Please send manually yourself or contact support.";
-                        return true;
-                    }
-                    
-                    if( $sendtome )
-                        echo "OK:Peering request sample sent to your own email address ({$this->getUser()->getEmail()}).";
-                    else if( $marksent )
-                        echo "OK:Peering request marked as sent in your Peering Manager.";
-                    else
-                        echo "OK:Peering request sent to {$peer->getName()} Peering Team.";
-                    
-                    return true;
-                }
-            }
-        }
-        else
-        {
-            $f->getElement( 'bcc' )->setValue( $this->getUser()->getEmail() );
-            $f->getElement( 'subject' )->setValue( "[INEX] Peering Request from {$this->getCustomer()->getName()} (ASN{$this->getCustomer()->getAutsys()})" );
-            $f->getElement( 'message' )->setValue( $this->view->render( 'peering-manager/peering-request-message.phtml' ) );
-        }
-        
-        $this->view->form = $f;
-    }
-    
-    public function peeringNotesAction()
-    {
-        Zend_Controller_Action_HelperBroker::removeHelper( 'viewRenderer' );
-        
-        $peer = $this->_loadPeer( $this->getParam( 'custid', null ) );
-        $pm = $this->_loadPeeringManagerEntry( $this->getCustomer(), $peer );
-        
-        if( $this->getRequest()->isPost() )
-        {
-            $pm->setUpdated( new DateTime() );
-            
-            if( trim( stripslashes( $this->getParam( 'message', '' ) ) ) )
-                $pm->setNotes( trim( stripslashes( $this->getParam( 'message' ) ) ) );
-            
-            try
-            {
-                $this->getD2EM()->flush();
-            }
-            catch( Exception $e )
-            {
-                $this->getLogger()->err( $e->getMessage() . "\n\n" . $e->getTraceAsString() );
-                echo "ERR:Could not update peering notes due to an unexpected error.";
-                return true;
-            }
-            
-            echo "OK:Peering notes updated for {$peer->getName()}.";
-        }
-        else
-        {
-            echo 'OK:' . $pm->getNotes();
-        }
-    }
-    
-    
-    public function markPeeredAction()
-    {
-        $peer = $this->_loadPeer( $this->getParam( 'custid' ) );
-        $pm = $this->_loadPeeringManagerEntry( $this->getCustomer(), $peer );
-    
-        $pm->setPeered( $pm->getPeered() ? false : true );
-        if( $pm->getPeered() && $pm->getRejected() )
-            $pm->setRejected( false );
-        
-        $this->getD2EM()->flush();
-        
-        $this->addMessage( "Peered flag " . ( $pm->getPeered() ? 'set' : 'cleared' ) . " for {$peer->getName()}.", OSS_Message::SUCCESS );
-        return $this->_redirect( 'peering-manager/index' );
-    }
-    
-    
-    public function markRejectedAction()
-    {
-        $peer = $this->_loadPeer( $this->getParam( 'custid' ) );
-        $pm = $this->_loadPeeringManagerEntry( $this->getCustomer(), $peer );
-    
-        $pm->setRejected( $pm->getRejected() ? false : true );
-        if( $pm->getPeered() && $pm->getRejected() )
-            $pm->setPeered( false );
-    
-        $this->getD2EM()->flush();
-            
-        $this->addMessage( "Ignored / rejected flag " . ( $pm->getRejected() ? 'set' : 'cleared' ) . " for {$peer->getName()}.", OSS_Message::SUCCESS );
-        return $this->_redirect( 'peering-manager/index' );
-    }
-    
-    
-    /**
-     * Utility function to load a peer from a submitted ID and issue an error and die() if not found.
-     *
-     * @return \Entities\Customer
-     */
-    private function _loadPeer()
-    {
-        $this->view->peer = $peer = $this->getD2EM()->getRepository( '\\Entities\\Customer' )->find( $this->getParam( 'custid', null ) );
-        
-        if( !$peer )
-        {
-            echo "ERR:Could not find peer's information in the database. Please contact support.";
-            die;
-        }
-        
-        return $peer;
-    }
-
-    
-    /**
-     * Utility function to load a PeeringManager entity and initialise one if not found
-     *
-     * @return \Entities\PeeringManager
-     */
-    private function _loadPeeringManagerEntry( $cust, $peer )
-    {
-        // get this customer/peer peering manager table entry
-        $pm = $this->getD2EM()->getRepository( '\\Entities\\PeeringManager' )->findOneBy(
-            [ 'Customer' => $cust, 'Peer' => $peer ]
-        );
-        
-        if( !$pm )
-        {
-            $pm = new \Entities\PeeringManager();
-            $pm->setCustomer( $cust );
-            $pm->setPeer( $peer );
-            $pm->setCreated( new DateTime() );
-            $pm->setPeered( false );
-            $pm->setRejected( false );
-            $pm->setNotes( '' );
-            $this->getD2EM()->persist( $pm );
-            $this->getD2EM()->flush();
-        }
-        
-        return $pm;
-    }
-
-}
diff --git a/application/controllers/PeeringMatrixController.php b/application/controllers/PeeringMatrixController.php
deleted file mode 100644
index 209b9b6d6..000000000
--- a/application/controllers/PeeringMatrixController.php
+++ /dev/null
@@ -1,70 +0,0 @@
-
- * @category   INEX
- * @package    INEX_Controller
- * @copyright  Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd
- * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
- */
-class PeeringMatrixController extends INEX_Controller_Action
-{
-
-    public function indexAction()
-    {
-        $this->view->protos = array(
-            4 => 'IPv4',
-            6 => 'IPv6'
-        );
-        
-        $proto = $this->getParam( 'proto', 4 );
-        if( !isset( $this->view->protos[$proto] ) || !in_array( $proto, $this->view->protos ) )
-            $proto = 4;
-
-        $this->view->vlans = $vlans = $this->getD2EM()->getRepository( '\\Entities\\Vlan' )->getNames();
-        
-        $vid = $this->getParam( 'vid', $this->_options['identity']['vlans']['default'] );
-        if( !isset( $vlans[ $vid ] ) )
-            $vid = $this->_options['identity']['vlans']['default'];
-        
-        $this->view->vid   = $vid;
-        $this->view->proto = $proto;
-                
-        $this->view->sessions = $this->getD2EM()->getRepository( '\\Entities\\BGPSessionData' )->getPeers( $vid, $proto );
-        $this->view->custs    = $this->getD2EM()->getRepository( '\\Entities\\Vlan' )->getCustomers( $vid, $proto );
-        
-        $this->view->jsessions = json_encode( $this->view->sessions );
-        $this->view->jcusts    = json_encode( $this->view->custs );
-        
-        $asns = array_keys( $this->view->custs );
-        $maxLenOfASN = strlen( $asns[ count( $asns ) - 1 ] );
-        $this->view->asnStringFormat = "% {$maxLenOfASN}s";
-    }
-                 
-}
-
-
diff --git a/application/controllers/PhysicalInterfaceController.php b/application/controllers/PhysicalInterfaceController.php
deleted file mode 100644
index be330a47a..000000000
--- a/application/controllers/PhysicalInterfaceController.php
+++ /dev/null
@@ -1,252 +0,0 @@
-
- * @category   INEX
- * @package    INEX_Controller
- * @copyright  Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd
- * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
- */
-class PhysicalInterfaceController extends INEX_Controller_FrontEnd
-{
-    /**
-     * This function sets up the frontend controller
-     */
-    protected function _feInit()
-    {
-        $this->view->feParams = $this->_feParams = (object)[
-            'entity'        => '\\Entities\\PhysicalInterface',
-            'form'          => 'INEX_Form_Interface_Physical',
-            'pagetitle'     => 'Physical Interfaces',
-        
-            'titleSingular' => 'Physical Interface',
-            'nameSingular'  => 'a physical interface',
-        
-            'defaultAction' => 'list',
-        
-            'listOrderBy'    => 'customer',
-            'listOrderByDir' => 'ASC',
-        ];
-    
-        switch( $this->getUser()->getPrivs() )
-        {
-            case \Entities\User::AUTH_SUPERUSER:
-                $this->_feParams->listColumns = [
-                    'id' => [ 'title' => 'UID', 'display' => false ],
-        
-                    'customer'  => [
-                        'title'      => 'Customer',
-                        'type'       => self::$FE_COL_TYPES[ 'HAS_ONE' ],
-                        'controller' => 'customer',
-                        'action'     => 'overview',
-                        'idField'    => 'custid'
-                    ],
-        
-                    'location'  => [
-                        'title'      => 'Location',
-                        'type'       => self::$FE_COL_TYPES[ 'HAS_ONE' ],
-                        'controller' => 'location',
-                        'action'     => 'view',
-                        'idField'    => 'locid'
-                    ],
-        
-                    'switch'  => [
-                        'title'      => 'Switch',
-                        'type'       => self::$FE_COL_TYPES[ 'HAS_ONE' ],
-                        'controller' => 'switch',
-                        'action'     => 'view',
-                        'idField'    => 'switchid'
-                    ],
-                    
-                    'port'          => 'Port',
-                    
-                    'status'        => [
-                        'title'          => 'Status',
-                        'type'           => self::$FE_COL_TYPES[ 'XLATE' ],
-                        'xlator'         => \Entities\PhysicalInterface::$STATES
-                    ],
-                    
-        
-                    //'location'      => 'Location',
-                    //'switch'        => 'Switch',
-                    'speed'         => 'Speed',
-                    'duplex'        => 'Duplex'
-                ];
-                
-                $this->_feParams->viewColumns = array_merge(
-                    $this->_feParams->listColumns,
-                    [ 'monitorindex' => 'Monitor Index', 'notes' => 'Notes' ]
-                );
-                break;
-    
-            case \Entities\User::AUTH_CUSTADMIN:
-            default:
-                $this->redirectAndEnsureDie( 'error/insufficient-permissions' );
-        }
-    
-    }
-    
-    
-    
-    /**
-     * Provide array of virtual interfaces for the listAction
-     *
-     * @param int $id The `id` of the row to load for `viewAction`. `null` if `listAction`
-     */
-    protected function listGetData( $id = null )
-    {
-        $qb = $this->getD2EM()->createQueryBuilder()
-            ->select(
-                    'pi.id AS id, pi.speed AS speed, pi.duplex AS duplex, pi.status AS status,
-                    pi.monitorindex AS monitorindex, pi.notes AS notes,
-                    c.name AS customer, c.id AS custid,
-                    s.name AS switch, s.id AS switchid,
-                    vi.id AS vintid,
-                    sp.name AS port, l.id AS locid, l.name AS location'
-                )
-            ->from( '\\Entities\\PhysicalInterface', 'pi' )
-            ->leftJoin( 'pi.VirtualInterface', 'vi' )
-            ->leftJoin( 'vi.Customer', 'c' )
-            ->leftJoin( 'pi.SwitchPort', 'sp' )
-            ->leftJoin( 'sp.Switcher', 's' )
-            ->leftJoin( 's.Cabinet', 'cab' )
-            ->leftJoin( 'cab.Location', 'l' );
-
-        
-        if( $id !== null )
-            $qb->where( 'pi.id = ' . intval( $id ) );
-        
-        return $qb->getQuery()->getArrayResult();
-    }
-    
-    
-    /**
-     * @param INEX_Form_Interface_Physical $form The form object
-     * @param \Entities\PhysicalInterface $object The Doctrine2 entity (being edited or blank for add)
-     * @param bool $isEdit True of we are editing an object, false otherwise
-     * @param array $options Options passed onto Zend_Form
-     * @param string $cancelLocation Where to redirect to if 'Cancal' is clicked
-     * @return void
-     */
-    protected function formPostProcess( $form, $object, $isEdit, $options = null, $cancelLocation = null )
-    {
-        if( $isEdit )
-        {
-            $form->getElement( 'switchid' )->setValue( $object->getSwitchPort()->getSwitcher()->getId() );
-            $form->getElement( 'switchportid' )->setValue( $object->getSwitchPort()->getId() );
-            $form->getElement( 'preselectSwitchPort' )->setValue( $object->getSwitchPort()->getId() );
-            $form->getElement( 'preselectPhysicalInterface' )->setValue( $object->getId() );
-            $form->getElement( 'virtualinterfaceid' )->setValue( $object->getVirtualInterface()->getId() );
-            
-            if( $this->getParam( 'rtn', false ) == 'pi' )
-                $form->setAction( OSS_Utils::genUrl( 'physical-interface', 'edit', false, [ 'id' => $object->getId(), 'rtn' => 'pi' ] ) );
-            else
-                $form->getElement( 'cancel' )->setAttrib( 'href', OSS_Utils::genUrl( 'virtual-interface', 'edit', false, [ 'id' => $object->getVirtualInterface()->getId() ] ) );
-        }
-        else // not editing
-        {
-            if( $this->getRequest()->isPost() && ( $vintid = ( isset( $_POST['virtualinterfaceid'] ) && $_POST['virtualinterfaceid'] ) ) )
-                $vint = $this->getD2EM()->getRepository( '\\Entities\\VirtualInterface' )->find( $_POST['virtualinterfaceid'] );
-            else if( ( $vintid = $this->getRequest()->getParam( 'vintid' ) ) !== null )
-                $vint = $this->getD2EM()->getRepository( '\\Entities\\VirtualInterface' )->find( $vintid );
-            
-            if( !isset( $vint ) || !$vint )
-                throw new INEX_Exception( 'Not sure how you would add a physical interface without a containing virtual interface');
-            
-            $form->getElement( 'virtualinterfaceid' )->setValue( $vint->getId() );
-            $form->getElement( 'cancel' )->setAttrib( 'href', OSS_Utils::genUrl( 'virtual-interface', 'edit', false, [ 'id' => $vint->getId() ] ) );
-            
-            if( !$object->getMonitorindex() )
-            {
-                $form->getElement( 'monitorindex' )->setValue(
-                    $this->getD2EM()->getRepository( '\\Entities\\PhysicalInterface' )->getNextMonitorIndex( $vint->getCustomer() )
-                );
-            }
-        }
-    }
-    
-    
-    /**
-     * @param INEX_Form_Interface_Physical $form The form object
-     * @param \Entities\PhysicalInterface $object The Doctrine2 entity (being edited or blank for add)
-     * @param bool $isEdit True of we are editing an object, false otherwise
-     * @return void
-     */
-    protected function addPostValidate( $form, $object, $isEdit )
-    {
-        $object->setSwitchPort(
-            $this->getD2EM()->getRepository( '\\Entities\\SwitchPort' )->find( $form->getElement( 'switchportid' )->getValue() )
-        );
-        
-        $object->setVirtualInterface(
-            $this->getD2EM()->getRepository( '\\Entities\\VirtualInterface' )->find( $form->getElement( 'virtualinterfaceid' )->getValue() )
-        );
-
-        return true;
-    }
-    
-    /**
-     * You can add `OSS_Message`s here and redirect to a custom destination after a
-     * successful add / edit operation.
-     *
-     * @param INEX_Form_Interface_Physical $form The form object
-     * @param \Entities\PhysicalInterface $object The Doctrine2 entity (being edited or blank for add)
-     * @param bool $isEdit True of we are editing an object, false otherwise
-     * @return bool `false` for standard message and redirection, otherwise redirect within this function
-     */
-    protected function addDestinationOnSuccess( $form, $object, $isEdit  )
-    {
-        if( $this->getParam( 'rtn', false ) == 'pi' )
-            return false;
-        
-        $this->addMessage(
-            'Physical interface successfuly ' . ( $isEdit ? 'edited.' : 'added.' ), OSS_Message::SUCCESS
-        );
-        
-        $this->redirectAndEnsureDie( 'virtual-interface/edit/id/' . $object->getVirtualInterface()->getId() );
-    }
-    
-    /**
-     * You can add `OSS_Message`s here and redirect to a custom destination after a
-     * successful deletion operation.
-     *
-     * @return bool `false` for standard message and redirection, otherwise redirect within this function
-     */
-    protected function deleteDestinationOnSuccess()
-    {
-        if( $this->getParam( 'rtn', false ) == 'pi' )
-            return false;
-        
-        $this->addMessage(
-            'Physical interface deleted successfuly.', OSS_Message::SUCCESS
-        );
-        
-        $this->redirectAndEnsureDie( 'virtual-interface/edit/id/' . $this->getParam( 'vintid' ) );
-    }
-    
-}
-
diff --git a/application/controllers/ProfileController.php b/application/controllers/ProfileController.php
deleted file mode 100644
index af520ea4e..000000000
--- a/application/controllers/ProfileController.php
+++ /dev/null
@@ -1,150 +0,0 @@
-
- * @category   INEX
- * @package    INEX_Controller
- * @copyright  Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd
- * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
- */
-
-class ProfileController extends INEX_Controller_AuthRequiredAction
-{
-    use OSS_Controller_Trait_Profile;
-    
-    /**
-     * Users mailing list subs as set via init() -> _initMailingListSubs()
-     *
-     * @var array
-     */
-    protected $_mailinglists;
-    
-
-    /**
-     * Return the appropriate change password form for your application
-     */
-    protected function _getFormChangePassword()
-    {
-        return new INEX_Form_ChangePassword();
-    }
-    
-    /**
-     * Return the appropriate change profile form for your application
-     */
-    protected function _getFormProfile()
-    {
-        $pf = new INEX_Form_Profile();
-        
-        $pf->getElement( 'username' )->setValue( $this->getUser()->getUsername() );
-        $pf->getElement( 'mobile'   )->setValue( $this->getUser()->getAuthorisedMobile() );
-        $pf->getElement( 'email'    )->setValue( $this->getUser()->getEmail() );
-        
-        return $pf;
-    }
-    
-    
-    
-    public function init()
-    {
-        $this->_initMailingListSubs();
-    }
-
-    public function indexAction()
-    {
-        if( !isset( $this->view->profileForm ) )
-            $this->view->profileForm = $this->_getFormProfile();
-        
-        if( !isset( $this->view->passwordForm ) )
-            $this->view->passwordForm = $this->_getFormChangePassword();
-    }
-
-    protected function changePasswordPostFlush()
-    {
-        $this->clearUserFromCache();
-    }
-    
-    /**
-     * Action to allow a user to change their profile
-     *
-     */
-    public function changeProfileAction()
-    {
-        $this->view->profileForm = $form = $this->_getFormProfile();
-        
-        if( $this->getRequest()->isPost() && $form->isValid( $_POST ) )
-        {
-            // update the users profile
-            $this->getUser()->setAuthorisedMobile( $form->getValue( 'mobile' ) );
-            $this->getUser()->setLastUpdated( new DateTime() );
-            $this->getUser()->setLastUpdatedBy( $this->getUser()->getId() );
-            $this->getD2EM()->flush();
-            $this->clearUserFromCache();
-            
-            $this->getLogger()->info( "User {$this->getUser()->getUsername()} updated own profile" );
-            $this->addMessage( _( 'Your profile has been changed.' ), OSS_Message::SUCCESS );
-            $this->redirect( 'profile/index' );
-        }
-    
-        $this->forward( 'index' );
-    }
-    
-
-    
-    public function updateMailingListsAction()
-    {
-        // need to capture all users with the given email
-        $users = $this->getD2EM()->getRepository( '\\Entities\\User' )->findBy( [ 'email' => $this->getUser()->getEmail() ] );
-        
-        foreach( $this->_options['mailinglists'] as $name => $ml )
-        {
-            if( isset( $_POST["ml_{$name}"] ) && $_POST["ml_{$name}"] )
-                foreach( $users as $u )
-                    $u->setPreference( "mailinglist.{$name}.subscribed", 1 );
-            else
-                foreach( $users as $u )
-                    $u->setPreference( "mailinglist.{$name}.subscribed", 0 );
-        }
-        
-        $this->getD2EM()->flush();
-        $this->addMessage( 'Your mailing list subscriptions have been updated and will take effect within 12 hours.', OSS_Message::SUCCESS );
-        $this->redirect( 'profile/index' );
-    }
-    
-    private function _initMailingListSubs()
-    {
-        // are we using mailing lists?
-        if( !isset( $this->_options['mailinglist']['enabled'] ) || !$this->_options['mailinglist']['enabled'] )
-            return;
-        
-        $mlsubs = [];
-        
-        foreach( $this->_options['mailinglists'] as $name => $ml )
-            $mlsubs[$name] = $this->getUser()->getPreference( "mailinglist.{$name}.subscribed" );
-        
-        $this->view->mlsubs = $mlsubs;
-    }
-}
-
diff --git a/application/controllers/StaticController.php b/application/controllers/StaticController.php
deleted file mode 100644
index ccc3bb67b..000000000
--- a/application/controllers/StaticController.php
+++ /dev/null
@@ -1,129 +0,0 @@
-
- * @category   INEX
- * @package    INEX_Controller
- * @copyright  Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd
- * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
- */
-class StaticController extends INEX_Controller_Action
-{
-
-
-    public function preDispatch()
-    {
-        if( substr( $this->getRequest()->getActionName(), 0, 4 ) == 'auth' )
-            $this->_requireAuth();
-    }
-    
-    private function _requireAuth( $priv = \Entities\User::AUTH_CUSTUSER )
-    {
-        if( !$this->getAuth()->hasIdentity() || $this->getUser()->getPrivs() < $priv )
-        {
-            if( $this->traitIsInitialised( 'OSS_Controller_Action_Trait_Messages' ) )
-                $this->addMessage( "Please login below.", OSS_Message::ERROR );
-        
-            if( $this->traitIsInitialised( 'OSS_Controller_Action_Trait_Namespace' ) )
-                $this->getSessionNamespace()->postAuthRedirect = $this->getRequest()->getPathInfo();
-        
-            $this->redirectAndEnsureDie( 'auth/login' );
-        }
-        
-        
-    }
-    
-    public function supportAction()
-    {}
-
-    public function exampleAction()
-    {}
-
-    public function feesAction()
-    {
-        $this->_requireAuth();
-    }
-
-    public function housingAction()
-    {
-        $this->_requireAuth();
-    }
-
-    public function meetingsInstructionsAction()
-    {
-        $this->_requireAuth();
-    }
-    
-    public function miscBenefitsAction()
-    {
-        $this->_requireAuth();
-    }
-
-    public function switchesAction()
-    {
-        $this->_requireAuth();
-    }
-
-    public function portSecurityAction()
-    {
-        $this->_requireAuth();
-    }
-    
-    public function routeServersAction()
-    {
-        $this->_requireAuth();
-        
-        // just find out if the user has route servers enabled or not
-        $this->view->rsclient = false;
-        foreach( $this->getCustomer()->getVirtualInterfaces() as $vi )
-            foreach( $vi->getVlanInterfaces() as $vli )
-                if( $vli->getRsclient() )
-                    $this->view->rsclient = true;
-    }
-    
-    public function as112Action()
-    {
-        $this->_requireAuth();
-        
-        // just find out if the user AS112 enabled or not
-        $this->view->as112 = false;
-        $this->view->rsclient = false;
-        foreach( $this->getCustomer()->getVirtualInterfaces() as $vi )
-        {
-            foreach( $vi->getVlanInterfaces() as $vli )
-            {
-                if( $vli->getAs112client() )
-                    $this->view->as112 = true;
-                if( $vli->getRsclient() )
-                    $this->view->rsclient = true;
-            }
-        }
-    }
-    
-}
-
-
diff --git a/application/controllers/StatisticsController.php b/application/controllers/StatisticsController.php
deleted file mode 100644
index 38e38940e..000000000
--- a/application/controllers/StatisticsController.php
+++ /dev/null
@@ -1,435 +0,0 @@
-
- * @category   INEX
- * @package    INEX_Controller
- * @copyright  Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd
- * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
- */
-class StatisticsController extends INEX_Controller_AuthRequiredAction
-{
-
-    public function preDispatch()
-    {}
-
-    
-    public function listAction()
-    {
-        $this->assertPrivilege( \Entities\User::AUTH_SUPERUSER, true );
-        $this->view->custs = $this->getD2EM()->getRepository( '\\Entities\\Customer')->getCurrentActive( true, true, true );
-    }
-    
-    public function leagueTableAction()
-    {
-        $this->assertPrivilege( \Entities\User::AUTH_SUPERUSER, true );
-        
-        $this->view->metrics = $metrics = [
-            'Total'   => 'data',
-            'Max'     => 'max',
-            'Average' => 'average'
-        ];
-
-        $metric = $this->getParam( 'metric', $metrics['Total'] );
-        if( !in_array( $metric, $metrics ) )
-            $metric = $metrics['Total'];
-        $this->view->metric     = $metric;
-        
-        $day = $this->getParam( 'day', date( 'Y-m-d' ) );
-        if( !Zend_Date::isDate( $day, 'Y-m-d' ) )
-            $day = date( 'Y-m-d' );
-        $this->view->day = $day = new \DateTime( $day );
-        
-        $category = $this->_setCategory();
-                
-        $this->view->trafficDaily = $this->getD2EM()->getRepository( '\\Entities\\TrafficDaily' )->load( $day, $category );
-    }
-    
-    
-    public function publicAction()
-    {
-        // get the available graphs
-        foreach( $this->_options['mrtg']['traffic_graphs'] as $g )
-        {
-            $p = explode( '::', $g );
-            $graphs[$p[0]] = $p[1];
-            $images[]      = $p[0];
-        }
-        $this->view->graphs     = $graphs;
-        
-        $graph = $this->getParam( 'graph', $images[0] );
-        if( !in_array( $graph, $images ) )
-            $graph = $images[0];
-        $this->view->graph      = $graph;
-        
-        $category = $this->_setCategory();
-    
-        $stats = array();
-        foreach( INEX_Mrtg::$PERIODS as $period )
-        {
-            $mrtg = new INEX_Mrtg( $this->_options['mrtg']['path'] . '/ixp_peering-' . $graph . '-' . $category . '.log' );
-            $stats[$period] = $mrtg->getValues( $period, $category );
-        }
-        $this->view->stats      = $stats;
-        
-        $this->view->periods    = INEX_Mrtg::$PERIODS;
-    }
-    
-    public function trunksAction()
-    {
-        // get the available graphs
-        foreach( $this->_options['mrtg']['trunk_graphs'] as $g )
-        {
-            $p = explode( '::', $g );
-            $graphs[$p[0]] = $p[1];
-            $images[]      = $p[0];
-        }
-        $this->view->graphs  = $graphs;
-        
-        $graph = $this->getParam( 'trunk', $images[0] );
-        if( !in_array( $graph, $images ) )
-            $graph = $images[0];
-        $this->view->graph   = $graph;
-        
-        $stats = array();
-        foreach( INEX_Mrtg::$PERIODS as $period )
-        {
-            $mrtg = new INEX_Mrtg( $this->_options['mrtg']['path'] . '/trunks/' . $graph . '.log' );
-            $stats[$period] = $mrtg->getValues( $period, INEX_Mrtg::CATEGORY_BITS );
-        }
-        $this->view->stats   = $stats;
-        
-        $this->view->periods = INEX_Mrtg::$PERIODS;
-    }
-    
-    public function switchesAction()
-    {
-        $switches = $this->view->switches
-            = $this->getD2EM()->getRepository( '\\Entities\\Switcher' )->getNames( true, \Entities\Switcher::TYPE_SWITCH );
-    
-        $switch = $this->getParam( 'switch', array_keys( $switches )[0] );
-        if( !in_array( $switch, array_keys( $switches ) ) )
-            $switch = array_keys( $switches )[0];
-        $this->view->switch     = $switch;
-        
-        $category = $this->_setCategory();
-        $this->_setPeriod();
-        
-        $stats = array();
-        foreach( INEX_Mrtg::$PERIODS as $period )
-        {
-            $mrtg = new INEX_Mrtg(
-                $this->_options['mrtg']['path'] . '/switches/' . 'switch-aggregate-'
-                    . $switches[$switch] . '-' . $category . '.log'
-            );
-    
-            $stats[$period] = $mrtg->getValues( $period, $category );
-        }
-        $this->view->stats      = $stats;
-        
-    }
-    
-    
-    public function membersAction()
-    {
-        $this->assertPrivilege( \Entities\User::AUTH_SUPERUSER, true );
-        
-        $this->view->infras = $infras = INEX_Mrtg::$INFRASTRUCTURES_TEXT;
-        $this->view->infra  = $infra  = $this->getParam( 'infra', 'aggregate' );
-
-        if( $infra != 'aggregate' && !in_array( $infra, $infras ) )
-            $infra = 'aggregate';
-        
-        $this->_setCategory();
-        $this->_setPeriod();
-        $this->view->custs = $this->getD2EM()->getRepository( '\\Entities\\Customer' )->getCurrentActive( false, true, true );
-    }
-    
-    public function memberAction()
-    {
-        if( $this->getUser()->getPrivs() < \Entities\User::AUTH_SUPERUSER )
-            $shortname = $this->getCustomer()->getShortname();
-        else
-            $shortname = $this->getParam( 'shortname', $this->getCustomer()->getShortname() );
-    
-        $this->view->cust = $cust = $this->loadCustomerByShortname( $shortname );  // redirects on failure
-        
-        $this->_setCategory();
-    }
-    
-    public function memberDrilldownAction()
-    {
-        $category = $this->_setCategory();
-        $this->view->monitorindex = $monitorindex = $this->getParam( 'monitorindex', 1 );
-        
-        if( $this->getUser()->getPrivs() != \Entities\User::AUTH_SUPERUSER )
-            $shortname = $this->getCustomer()->getShortname();
-        else
-            $shortname = $this->getParam( 'shortname', $this->getCustomer()->getShortname() );
-    
-        $this->view->cust = $cust = $this->loadCustomerByShortname( $shortname );  // redirects on failure
-    
-        if( $monitorindex != 'aggregate' )
-        {
-            $vint = false;
-            $pi = null;
-            foreach( $this->getCustomer()->getVirtualInterfaces() as $vi )
-            {
-                foreach( $vi->getPhysicalInterfaces() as $pi )
-                {
-                    if( $pi->getMonitorindex() == $monitorindex )
-                    {
-                        $vint = $vi;
-                        break 2;
-                    }
-                }
-            }
-            
-            if( !$vint )
-                throw new INEX_Exception( 'Member statistics drilldown requested for unknown monitor index' );
-    
-            $this->view->switchname = $pi->getSwitchPort()->getSwitcher()->getName();
-            $this->view->portname   = $pi->getSwitchPort()->getName();
-        }
-        else
-        {
-            $this->view->switchname = '';
-            $this->view->portname   = '';
-        }
-    
-        $this->view->periods      = INEX_Mrtg::$PERIODS;
-    
-        $stats = array();
-        foreach( INEX_Mrtg::$PERIODS as $period )
-        {
-            $mrtg = new INEX_Mrtg(
-                INEX_Mrtg::getMrtgFilePath( $this->_options['mrtg']['path'] . '/members', 'LOG', $monitorindex, $category, $cust->getShortname() )
-            );
-    
-            $stats[$period] = $mrtg->getValues( $period, $this->view->category );
-        }
-        $this->view->stats     = $stats;
-    
-        if( $this->_request->getParam( 'mini', false ) )
-        {
-            Zend_Controller_Action_HelperBroker::removeHelper( 'viewRenderer' );
-            $this->view->display( 'statistics/member-drilldown-mini.phtml' );
-        }
-    }
-    
-    /**
-     * sFlow Peer to Peer statistics
-     */
-    public function p2pAction()
-    {
-        if( $this->getUser()->getPrivs() != \Entities\User::AUTH_SUPERUSER )
-            $shortname = $this->getCustomer()->getShortname();
-        else
-            $shortname = $this->getParam( 'shortname', $this->getCustomer()->getShortname() );
-    
-        $this->view->cust = $cust = $this->loadCustomerByShortname( $shortname );  // redirects on failure
-
-        $category = $this->_setCategory();
-        $period   = $this->_setPeriod();
-        $infra    = $this->_setInfrastructure();
-        $proto    = $this->_setProtocol();
-        $dvid     = $this->view->dvid = $this->getParam( 'dvid', false );
-    
-        // find the possible virtual interfaces that this customer peers with
-        $vints = [];
-        foreach( $cust->getVirtualInterfaces() as $vi )
-        {
-            $enabled = false;
-            foreach( $vi->getVlanInterfaces() as $vli )
-            {
-                $fn = "getIpv{$proto}enabled";
-                if( $vli->$fn() )
-                {
-                    $enabled = true;
-                    break;
-                }
-            }
-            
-            if( !$enabled )
-                continue;
-            
-            foreach( $vi->getPhysicalInterfaces() as $pi )
-            {
-                if( $pi->getSwitchPort()->getSwitcher()->getInfrastructure() == $infra )
-                    $vints[ $vi->getId() ] = $vi;
-            }
-        }
-            
-        $this->view->vints = $vints;
-        $this->view->customersWithVirtualInterfaces = false;
-        
-        if( count( $vints ) )
-        {
-            if( count( $vints ) > 1 )
-            {
-                $interfaces = array();
-                foreach( $vints as $vi )
-                    $interfaces[] = $vi->getId();
-    
-                $interface = $this->view->interface = $this->getParam( 'interface', $interfaces[0] );
-                if( !in_array( $interface, $interfaces ) )
-                    $interface = $this->view->interface = $interfaces[0];
-    
-                $this->view->svid = $interface;
-            }
-            else
-                $this->view->svid = $vints[ ( array_keys( $vints )[0] ) ]->getId();
-    
-            // find the possible virtual interfaces that this customer peers with
-            $dql = "SELECT c.id AS cid, c.name AS cname, c.shortname AS cshortname,
-                        vi.id AS viid, pi.id AS piid, vli.id AS vlidid, sp.id AS spid, s.id AS sid
-            
-                    FROM \\Entities\\Customer c
-                        LEFT JOIN c.VirtualInterfaces vi
-                        LEFT JOIN vi.PhysicalInterfaces pi
-                        LEFT JOIN vi.VlanInterfaces vli
-                        LEFT JOIN pi.SwitchPort sp
-                        LEFT JOIN sp.Switcher s
-                        
-                    WHERE
-                        s.infrastructure = {$infra}
-                        AND vli.ipv{$proto}enabled = 1
-                        AND c.shortname != ?1
-                        AND c.type IN ( " . \Entities\Customer::TYPE_FULL . ", " . \Entities\Customer::TYPE_PROBONO . " )
-                        AND c.status = " . \Entities\Customer::STATUS_NORMAL . "
-                        AND ( c.dateleave IS NULL OR c.dateleave = '0000-00-00' )
-                        AND pi.status = " . \Entities\PhysicalInterface::STATUS_CONNECTED;
-                        
-            
-    
-            if( $dvid )
-                $dql .= " AND WHERE vi.id = {$dvid}";
-            
-            $dql .= "  ORDER BY c.name";
-            
-           
-            $q  = $this->getD2EM()->createQuery( $dql )->setParameter( 1, $shortname );
-            
-            $this->view->customersWithVirtualInterfaces = $q->getArrayResult();
-        }
-    
-        if( $dvid )
-        {
-            Zend_Controller_Action_HelperBroker::removeHelper( 'viewRenderer' );
-            $this->view->display( 'statistics/p2p-single.phtml' );
-        }
-    }
-    
-    /**
-     * Utility function to extract, validate (and default if necessary) a
-     * category from request parameters.
-     *
-     * Sets the view variables `$category` to the chosen / defaulted category
-     * and `$categories` to all available categories.
-     *
-     * @param string $pname The name of the parameter to extract the category from
-     * @return string The chosen / defaulted category
-     */
-    protected function _setCategory( $pname = 'category' )
-    {
-        $category = $this->getParam( $pname, INEX_Mrtg::$CATEGORIES['Bits'] );
-        if( !in_array( $category, INEX_Mrtg::$CATEGORIES ) )
-            $category = INEX_Mrtg::$CATEGORIES['Bits'];
-        $this->view->category   = $category;
-        $this->view->categories = INEX_Mrtg::$CATEGORIES;
-        return $category;
-    }
-    
-    /**
-     * Utility function to extract, validate (and default if necessary) a
-     * period from request parameters.
-     *
-     * Sets the view variables `$period` to the chosen / defaulted category
-     * and `$periods` to all available periods.
-     *
-     * @param string $pname The name of the parameter to extract the period from
-     * @return string The chosen / defaulted period
-     */
-    protected function _setPeriod( $pname = 'period' )
-    {
-        $period = $this->getParam( $pname, INEX_Mrtg::$PERIODS['Day'] );
-        if( !in_array( $period, INEX_Mrtg::$PERIODS ) )
-            $period = INEX_Mrtg::$PERIODS['Day'];
-        $this->view->period     = $period;
-        $this->view->periods    = INEX_Mrtg::$PERIODS;
-        return $period;
-    }
-    
-    /**
-     * Utility function to extract, validate (and default if necessary) an
-     * infrastructure from request parameters.
-     *
-     * Sets the view variables `$infra` to the chosen / defaulted infrastructure
-     * and `$infrastructures` to all available infrastructures.
-     *
-     * @param string $pname The name of the parameter to extract the infrastructure from
-     * @return string The chosen / defaulted infrastructure
-     */
-    protected function _setInfrastructure( $pname = 'infra' )
-    {
-        $infra = $this->view->infra = $this->getParam( $pname, 1 );
-        if( !in_array( $infra, INEX_Mrtg::$INFRASTRUCTURES ) )
-            $infra = INEX_Mrtg::INFRASTRUCTURE_PRIMARY;
-        
-        $this->view->infra      = $infra;
-        $this->view->infrastructures = INEX_Mrtg::$INFRASTRUCTURES;
-        
-        return $infra;
-    }
-    
-    
-    /**
-     * Utility function to extract, validate (and default if necessary) a
-     * protocol from request parameters.
-     *
-     * Sets the view variables `$proto` to the chosen / defaulted protocol
-     * and `$protocols` to all available protocols.
-     *
-     * @param string $pname The name of the parameter to extract the protocol from
-     * @return string The chosen / defaulted protocol
-     */
-    protected function _setProtocol( $pname = 'proto' )
-    {
-        $proto = $this->getParam( $pname, 4 );
-        if( !in_array( $proto, INEX_Mrtg::$PROTOCOLS ) )
-            $proto = INEX_Mrtg::PROTOCOL_IPV4;
-        
-        $this->view->proto     = $proto;
-        $this->view->protocols = INEX_Mrtg::$PROTOCOLS;
-            
-        return $proto;
-    }
-    
-    
-
-}
-
diff --git a/application/controllers/SwitchController.php b/application/controllers/SwitchController.php
deleted file mode 100644
index 896c4889f..000000000
--- a/application/controllers/SwitchController.php
+++ /dev/null
@@ -1,253 +0,0 @@
-
- * @category   INEX
- * @package    INEX_Controller
- * @copyright  Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd
- * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
- */
-class SwitchController extends INEX_Controller_FrontEnd
-{
-    /**
-     * This function sets up the frontend controller
-     */
-    protected function _feInit()
-    {
-        $this->view->feParams = $this->_feParams = (object)[
-            'entity'        => '\\Entities\\Switcher',
-            'form'          => 'INEX_Form_Switch',
-            'pagetitle'     => 'Switches',
-        
-            'titleSingular' => 'Switch',
-            'nameSingular'  => 'a switch',
-        
-            'listOrderBy'    => 'name',
-            'listOrderByDir' => 'ASC'
-        ];
-        
-        switch( $this->getUser()->getPrivs() )
-        {
-            case \Entities\User::AUTH_SUPERUSER:
-                $this->_feParams->listColumns = [
-                    'id'        => [ 'title' => 'UID', 'display' => false ],
-                    'name'           => 'Name',
-                    
-                    'cabinet'  => [
-                        'title'      => 'Cabinet',
-                        'type'       => self::$FE_COL_TYPES[ 'HAS_ONE' ],
-                        'controller' => 'cabinet',
-                        'action'     => 'view',
-                        'idField'    => 'cabinetid'
-                    ],
-                
-                    'vendor'  => [
-                        'title'      => 'Vendor',
-                        'type'       => self::$FE_COL_TYPES[ 'HAS_ONE' ],
-                        'controller' => 'vendor',
-                        'action'     => 'view',
-                        'idField'    => 'vendorid'
-                    ],
-                    
-                    'model'          => 'Model',
-                    'ipv4addr'       => 'IPv4 Address',
-                    'infrastructure' => 'Infrastructure',
-                    'active'         => 'Active'
-                ];
-    
-                // display the same information in the view as the list
-                $this->_feParams->viewColumns = array_merge(
-                    $this->_feParams->listColumns,
-                    [
-                        'ipv6addr'       => 'IPv6 Address',
-                        'snmppasswd'     => 'SNMP Community',
-                        'switchtype'     => 'Type',
-                        'notes'          => 'Notes'
-                    ]
-                );
-                
-                $this->_feParams->defaultAction = 'list';
-                break;
-                
-            case \Entities\User::AUTH_CUSTUSER:
-                $this->_feParams->allowedActions = [ 'configuration' ];
-                $this->_feParams->defaultAction = 'configuration';
-                break;
-                
-            default:
-                $this->redirectAndEnsureDie( 'error/insufficient-permissions' );
-        }
-    }
-    
-    /**
-     * Provide array of users for the listAction and viewAction
-     *
-     * @param int $id The `id` of the row to load for `viewAction`. `null` if `listAction`
-     */
-    protected function listGetData( $id = null )
-    {
-        $qb = $this->getD2EM()->createQueryBuilder()
-            ->select( 's.id AS id, s.name AS name,
-                s.ipv4addr AS ipv4addr, s.ipv6addr AS ipv6addr, s.snmppasswd AS snmppasswd,
-                s.infrastructure AS infrastructure, s.switchtype AS switchtype, s.model AS model,
-                s.active AS active, s.notes AS notes,
-                v.id AS vendorid, v.name AS vendor, c.id AS cabinetid, c.name AS cabinet'
-            )
-            ->from( '\\Entities\\Switcher', 's' )
-            ->leftJoin( 's.Cabinet', 'c' )
-            ->leftJoin( 's.Vendor', 'v' );
-    
-        if( isset( $this->_feParams->listOrderBy ) )
-            $qb->orderBy( $this->_feParams->listOrderBy, isset( $this->_feParams->listOrderByDir ) ? $this->_feParams->listOrderByDir : 'ASC' );
-    
-        if( $id !== null )
-            $qb->andWhere( 's.id = ?1' )->setParameter( 1, $id );
-    
-        return $qb->getQuery()->getResult();
-    }
-    
-    
-    /**
-     *
-     * @param INEX_Form_Cabinet $form The form object
-     * @param \Entities\Cabinet $object The Doctrine2 entity (being edited or blank for add)
-     * @param bool $isEdit True of we are editing an object, false otherwise
-     * @param array $options Options passed onto Zend_Form
-     * @param string $cancelLocation Where to redirect to if 'Cancal' is clicked
-     * @return void
-     */
-    protected function formPostProcess( $form, $object, $isEdit, $options = null, $cancelLocation = null )
-    {
-        if( $isEdit )
-        {
-            $form->getElement( 'cabinetid' )->setValue( $object->getCabinet()->getId() );
-            $form->getElement( 'vendorid'  )->setValue( $object->getVendor()->getId()  );
-        }
-    }
-    
-    
-    /**
-     *
-     * @param INEX_Form_Cabinet $form The form object
-     * @param \Entities\Cabinet $object The Doctrine2 entity (being edited or blank for add)
-     * @param bool $isEdit True of we are editing an object, false otherwise
-     * @return void
-     */
-    protected function addPostValidate( $form, $object, $isEdit )
-    {
-        $object->setCabinet(
-            $this->getD2EM()->getRepository( '\\Entities\\Cabinet' )->find( $form->getElement( 'cabinetid' )->getValue() )
-        );
-    
-        $object->setVendor(
-            $this->getD2EM()->getRepository( '\\Entities\\Vendor' )->find( $form->getElement( 'vendorid' )->getValue() )
-        );
-    
-        return true;
-    }
-    
-    /**
-     * Clear the cache after a change to a switch
-     *
-     * @param \Entities\Switcher $object
-     * @return boolean
-     */
-    protected function postFlush( $object )
-    {
-        // this is created in Repositories\Switcher::getAndCache()
-        $this->getD2Cache()->delete( \Repositories\Switcher::ALL_CACHE_KEY );
-        return true;
-    }
-    
-
-    function portReportAction()
-    {
-        $this->view->switches = $switches = $this->getD2EM()->getRepository( '\\Entities\\Switcher' )->getNames();
-        $this->view->switch   = $switch   = $this->getD2EM()->getRepository( '\\Entities\\Switcher' )->find( $this->getParam( 'id', 0 ) );
-
-        if( $switch === null )
-        {
-            $this->addMessage( 'Unknown switch.', OSS_Message::ERROR );
-            return $this->redirect( 'switch/list' );
-        }
-        
-        $allports = $this->getD2EM()->createQuery(
-                'SELECT sp.id AS spid, sp.name AS portname, sp.type AS porttype
-                FROM \\Entities\\SwitchPort sp
-                WHERE sp.Switcher = ?1
-                ORDER BY spid ASC'
-            )
-            ->setParameter( 1, $switch )
-            ->getResult();
-        
-        $ports = $this->getD2EM()->createQuery(
-                'SELECT sp.id AS spid, sp.name AS portname, sp.type AS porttype,
-                        pi.speed AS speed, pi.duplex AS duplex, c.name AS custname
-            
-                FROM \\Entities\\SwitchPort sp
-                    JOIN sp.PhysicalInterface pi
-                    JOIN pi.VirtualInterface vi
-                    JOIN vi.Customer c
-            
-                WHERE sp.Switcher = ?1
-        
-                ORDER BY spid ASC'
-            )
-            ->setParameter( 1, $switch )
-            ->getArrayResult();
-            
-        foreach( $allports as $id => $port )
-        {
-            if( isset( $ports[0] ) && $ports[0][ 'portname' ] == $port[ 'portname' ] )
-                $allports[ $port[ 'portname' ] ] = array_shift( $ports );
-            else
-                $allports[ $port[ 'portname' ] ] = $port;
-            
-            $allports[ $port[ 'portname' ] ]['porttype'] = \Entities\SwitchPort::$TYPES[ $allports[ $port[ 'portname' ] ]['porttype'] ];
-            
-            unset( $allports[ $id ] );
-        }
-        
-        $this->view->ports = $allports;
-    }
-    
-    public function configurationAction()
-    {
-        $this->view->registerClass( 'PHYSICALINTERFACE', '\\Entities\\PhysicalInterface' );
-        $this->view->states   = \Entities\PhysicalInterface::$STATES;
-        $this->view->vlans    = $vlans    = $this->getD2EM()->getRepository( '\\Entities\\Vlan'     )->getNames();
-        $this->view->switches = $switches = $this->getD2EM()->getRepository( '\\Entities\\Switcher' )->getNames();
-        
-        $this->view->switchid = $sid = ( isset( $_POST['sid'] ) && isset( $switches[ $_POST['sid'] ] ) ) ? $_POST['sid'] : null;
-        $this->view->vlanid   = $vid = ( isset( $_POST['vid'] ) && isset( $vlans[    $_POST['vid'] ] ) ) ? $_POST['vid'] : null;
-        
-        $this->view->config = $this->getD2EM()->getRepository( '\\Entities\\Switcher' )->getConfiguration( $sid, $vid );
-    }
-    
-    
-}
-
diff --git a/application/controllers/SwitchPortController.php b/application/controllers/SwitchPortController.php
deleted file mode 100644
index 219325235..000000000
--- a/application/controllers/SwitchPortController.php
+++ /dev/null
@@ -1,277 +0,0 @@
-
- * @category   INEX
- * @package    INEX_Controller
- * @copyright  Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd
- * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
- */
-class SwitchPortController extends INEX_Controller_FrontEnd
-{
-    /**
-     * This function sets up the frontend controller
-     */
-    protected function _feInit()
-    {
-        $this->assertPrivilege( \Entities\User::AUTH_SUPERUSER );
-    
-        $this->view->feParams = $this->_feParams = (object)[
-            'entity'        => '\\Entities\\SwitchPort',
-            'form'          => 'INEX_Form_Switch_Port',
-            'pagetitle'     => 'Switch Ports',
-        
-            'titleSingular' => 'Switch Port',
-            'nameSingular'  => 'a switch port',
-        
-            'defaultAction' => 'list',                    // OPTIONAL; defaults to 'list'
-        
-            'listOrderBy'    => 'name',
-            'listOrderByDir' => 'ASC',
-        
-            'listColumns'    => [
-        
-                'id'        => [ 'title' => 'UID', 'display' => false ],
-                
-                'switch'  => [
-                    'title'      => 'Switch',
-                    'type'       => self::$FE_COL_TYPES[ 'HAS_ONE' ],
-                    'controller' => 'switch',
-                    'action'     => 'view',
-                    'idField'    => 'switchid'
-                ],
-            
-                'name'           => 'Name',
-                
-                'type'  => [
-                    'title'    => 'Type',
-                    'type'     => self::$FE_COL_TYPES[ 'XLATE' ],
-                    'xlator'   => \Entities\SwitchPort::$TYPES
-                ]
-            ]
-        ];
-    
-        // display the same information in the view as the list
-        $this->_feParams->viewColumns = $this->_feParams->listColumns;
-    }
-    
-    
-    /**
-     * Provide array of users for the listAction and viewAction
-     *
-     * @param int $id The `id` of the row to load for `viewAction`. `null` if `listAction`
-     */
-    protected function listGetData( $id = null )
-    {
-        $this->view->switches = $switches = $this->getD2EM()->getRepository( '\\Entities\\Switcher' )->getNames();
-        
-        $qb = $this->getD2EM()->createQueryBuilder()
-        ->select( 'sp.id AS id, sp.name AS name,
-            sp.type AS type, s.name AS switch, s.id AS switchid'
-        )
-        ->from( '\\Entities\\SwitchPort', 'sp' )
-        ->leftJoin( 'sp.Switcher', 's' );
-    
-        if( isset( $this->_feParams->listOrderBy ) )
-            $qb->orderBy( $this->_feParams->listOrderBy, isset( $this->_feParams->listOrderByDir ) ? $this->_feParams->listOrderByDir : 'ASC' );
-    
-        if( $id !== null )
-            $qb->andWhere( 'sp.id = ?1' )->setParameter( 1, $id );
-    
-        if( ( $sid = $this->getParam( 'switch', false ) ) && isset( $switches[$sid] ) )
-        {
-            $this->view->sid = $sid;
-            $qb->where( 's.id = ?2' )->setParameter( 2, $sid );
-        }
-        
-        return $qb->getQuery()->getResult();
-    }
-    
-    
-    /**
-     *
-     * @param INEX_Form_SwitchPort $form The form object
-     * @param \Entities\SwitchPort $object The Doctrine2 entity (being edited or blank for add)
-     * @param bool $isEdit True of we are editing an object, false otherwise
-     * @param array $options Options passed onto Zend_Form
-     * @param string $cancelLocation Where to redirect to if 'Cancal' is clicked
-     * @return void
-     */
-    protected function formPostProcess( $form, $object, $isEdit, $options = null, $cancelLocation = null )
-    {
-        if( $isEdit )
-            $form->getElement( 'switchid' )->setValue( $object->getSwitcher()->getId() );
-    }
-    
-
-    /**
-     * Prevalidation hook that can be overridden by subclasses for add and edit.
-     *
-     * This is called if the user POSTs a form just before the form is validated by Zend
-     *
-     * @param OSS_Form $form The Send form object
-     * @param object $object The Doctrine2 entity (being edited or blank for add)
-     * @param bool $isEdit True if we are editing, otherwise false
-     * @return bool If false, the form is not validated or processed
-     */
-    protected function addPreValidate( $form, $object, $isEdit )
-    {
-        // ensure the port name is unique for a given switch
-        // FIXME - for add and edit
-        
-        return true;
-    }
-    
-    /**
-     *
-     * @param INEX_Form_SwitchPort $form The form object
-     * @param \Entities\SwitchPort $object The Doctrine2 entity (being edited or blank for add)
-     * @param bool $isEdit True of we are editing an object, false otherwise
-     * @return void
-     */
-    protected function addPostValidate( $form, $object, $isEdit )
-    {
-        $object->setSwitcher(
-            $this->getD2EM()->getRepository( '\\Entities\\Switcher' )->find( $form->getElement( 'switchid' )->getValue() )
-        );
-    
-        return true;
-    }
-    
-        
-    public function addAction()
-    {
-        $this->view->form = $form = new INEX_Form_Switch_AddPorts();
-    
-        if( $this->getRequest()->isPost() && $form->isValid( $_POST ) )
-        {
-            if( !( $switch = $this->getD2EM()->getRepository( '\\Entities\\Switcher' )->find( $form->getValue( 'switchid' ) ) ) )
-                throw new INEX_Exception( 'Unknown switch in request' );
-            
-            for( $i = 0; $i < $form->getValue( 'numports' ); $i++ )
-            {
-                $sp = new \Entities\SwitchPort();
-                $sp->setSwitcher( $switch );
-                $sp->setType( intval( $_POST[ 'np_type' . $i ] ) );
-                $sp->setName( trim( stripslashes( $_POST[ 'np_name' . $i ] ) ) );
-                $this->getD2EM()->persist( $sp );
-            }
-
-            $this->getD2EM()->flush();
-
-            
-            $msg = "{$form->getValue( 'numports' )} new ports created for switch {$switch->getName()}.";
-            $this->getLogger()->info( $msg );
-            $this->addMessage( $msg, OSS_Message::SUCCESS );
-            
-            $this->redirect( 'switch-port/list/switch/' . $switch->getId() );
-        }
-        
-        $this->render( 'add-ports' );
-    }
-    
-    
-    // we have overridden the standard addAction() and so we need a dedicated editAction():
-    public function editAction()
-    {
-        $this->view->isEdit = $isEdit = true;
-    
-        $eid = $this->editResolveId();
-    
-        if( !$eid || !is_numeric( $eid ) )
-            throw new INEX_Exception( 'Bad switch port id for switch-port/edit' );
-        
-        $this->view->object = $object = $this->loadObject( $eid );
-        $this->view->form = $form = $this->getForm( $isEdit, $object );
-        $form->assignEntityToForm( $object, $this );
-        if( $form->getElement( 'submit' ) )
-            $form->getElement( 'submit' )->setLabel( 'Save Changes' );
-    
-        $this->addPrepare( $form, $object, $isEdit );
-    
-        if( $this->getRequest()->isPost() && $this->addPreValidate( $form, $object, $isEdit ) && $form->isValid( $_POST ) )
-        {
-            if( $this->addProcessForm( $form, $object, $isEdit ) )
-            {
-                if( $this->addDestinationOnSuccess( $form, $object, $isEdit ) === false )
-                {
-                    $this->addMessage( $this->feGetParam( 'titleSingular' ) . ( $isEdit ? ' edited.' : ' added.' ), OSS_Message::SUCCESS );
-                    $this->redirectAndEnsureDie( $this->_getBaseUrl() . "/index" );
-                }
-            }
-        }
-    
-        $this->view->addPreamble  = $this->_resolveTemplate( 'add-preamble.phtml'  );
-        $this->view->addPostamble = $this->_resolveTemplate( 'add-postamble.phtml' );
-        $this->view->addToolbar   = $this->_resolveTemplate( 'add-toolbar.phtml' );
-        $this->view->addScript    = $this->_resolveTemplate( 'js/add.js' );
-    
-        $this->_display( 'add.phtml' );
-    }
-    
-    
-    
-    public function ajaxGetAction()
-    {
-        $dql = "SELECT sp.name AS name, sp.type AS type, sp.id AS id
-                    FROM \\Entities\\SwitchPort sp
-                        LEFT JOIN sp.Switcher s
-                        LEFT JOIN sp.PhysicalInterface pi
-                    WHERE
-                        s.id = ?1 ";
-        
-        if( $this->getParam( 'id', null ) !== null )
-            $dql .= 'AND ( pi.id IS NULL OR pi.id = ?2 )';
-        else
-            $dql .= 'AND pi.id IS NULL';
-
-        $dql .= " ORDER BY sp.id ASC";
-        
-        $query = $this->getD2EM()->createQuery( $dql );
-        $query->setParameter( 1, $this->getParam( 'switchid', 0 ) );
-        
-        if( $this->getParam( 'id', null ) !== null )
-            $query->setParameter( 2, $this->getParam( 'id' ) );
-        
-        $ports = $query->getArrayResult();
-    
-        foreach( $ports as $id => $port )
-            $ports[$id]['type'] = \Entities\SwitchPort::$TYPES[ $port['type'] ];
-    
-        $this->getResponse()
-            ->setHeader('Content-Type', 'application/json')
-            ->setBody( Zend_Json::encode( $ports ) )
-            ->sendResponse();
-        
-        die(); //FIXME I shouldn't have to die() here...
-    }
-    
-    
-}
-
-
-
diff --git a/application/controllers/UserController.php b/application/controllers/UserController.php
deleted file mode 100644
index 19a910486..000000000
--- a/application/controllers/UserController.php
+++ /dev/null
@@ -1,443 +0,0 @@
-
- * @category   INEX
- * @package    INEX_Controller
- * @copyright  Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd
- * @license    http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0
- */
-class UserController extends INEX_Controller_FrontEnd
-{
-
-    /**
-     * This function sets up the frontend controller
-     */
-    protected function _feInit()
-    {
-        $this->view->feParams = $this->_feParams = (object)[
-            'entity'        => '\\Entities\\User',
-            'form'          => 'INEX_Form_User',
-            'pagetitle'     => 'Users',
-
-            'titleSingular' => 'User',
-            'nameSingular'  => 'a user',
-
-            'defaultAction' => 'list',                    // OPTIONAL; defaults to 'list'
-
-            'listOrderBy'    => 'username',
-            'listOrderByDir' => 'ASC',
-        ];
-
-        switch( $this->getUser()->getPrivs() )
-        {
-            case \Entities\User::AUTH_SUPERUSER:
-                $this->_feParams->listColumns = [
-                    'id' => [ 'title' => 'UID', 'display' => false ],
-
-                    'customer'  => [
-                        'title'      => 'Customer',
-                        'type'       => self::$FE_COL_TYPES[ 'HAS_ONE' ],
-                        'controller' => 'customer',
-                        'action'     => 'overview',
-                        'idField'    => 'custid'
-                    ],
-
-                    'username'      => 'Userame',
-                    'email'         => 'Email',
-                    
-                    'privileges'    => [
-                        'title'     => 'Privileges',
-                        'type'      => self::$FE_COL_TYPES[ 'XLATE' ],
-                        'xlator'    => \Entities\User::$PRIVILEGES_TEXT
-                    ],
-
-                    'enabled'       => [
-                        'title'         => 'Enabled',
-                        'type'          => self::$FE_COL_TYPES[ 'SCRIPT' ],
-                        'script'        => 'user/list-column-enabled.phtml'
-                    ],
-
-                    'created'       => [
-                        'title'     => 'Created',
-                        'type'      => self::$FE_COL_TYPES[ 'DATETIME' ]
-                    ]
-                ];
-                break;
-
-            case \Entities\User::AUTH_CUSTADMIN:
-                $this->_feParams->pagetitle = 'User Admin for ' . $this->getUser()->getCustomer()->getName();
-
-                $this->_feParams->listColumns = [
-                    'id' => [ 'title' => 'UID', 'display' => false ],
-                    'username'      => 'Userame',
-                    'email'         => 'Email',
-
-                    'enabled'       => [
-                        'title'         => 'Enabled',
-                        'type'          => self::$FE_COL_TYPES[ 'SCRIPT' ],
-                        'script'        => 'user/list-column-enabled.phtml'
-                    ],
-
-                    'created'       => [
-                        'title'         => 'Created',
-                        'type'          => self::$FE_COL_TYPES[ 'DATETIME' ]
-                    ]
-                ];
-                break;
-
-            default:
-                $this->redirectAndEnsureDie( 'error/insufficient-permissions' );
-        }
-
-        // display the same information in the view as the list
-        $this->_feParams->viewColumns = $this->_feParams->listColumns;
-    }
-
-    
-    
-    protected function listPreamble()
-    {
-        if( $this->getUser()->getPrivs() == \Entities\User::AUTH_CUSTADMIN )
-        {
-            if( !isset( $this->getSessionNamespace()->custadminInstructions ) || !$this->getSessionNamespace()->custadminInstructions )
-            {
-                $this->getSessionNamespace()->custadminInstructions = true;
-                
-                $this->addMessage(
-                    "

Remember! This admin account is only intended for creating users for your organisation.

" - . "

For full IXP Manager functionality, graphs and member information, log in under one of your user accounts

", - OSS_Message::INFO, - OSS_Message::TYPE_BLOCK - ); - } - } - } - - - - /** - * Provide array of users for the listAction and viewAction - * - * @param int $id The `id` of the row to load for `viewAction`. `null` if `listAction` - */ - protected function listGetData( $id = null ) - { - $qb = $this->getD2EM()->createQueryBuilder() - ->select( 'u.id as id, u.username as username, u.email as email, u.privs AS privileges, - u.created as created, u.disabled as disabled, c.id as custid, c.name as customer' ) - ->from( '\\Entities\\User', 'u' ) - ->leftJoin( 'u.Customer', 'c' ); - - if( $this->getUser()->getPrivs() == \Entities\User::AUTH_CUSTADMIN ) - { - $qb->where( 'u.Customer = ?1' ) - ->andWhere( 'u.privs = ?2' ) - ->setParameter( 1, $this->getUser()->getCustomer() ) - ->setParameter( 2, \Entities\User::AUTH_CUSTUSER ); - } - - if( isset( $this->_feParams->listOrderBy ) ) - $qb->orderBy( $this->_feParams->listOrderBy, isset( $this->_feParams->listOrderByDir ) ? $this->_feParams->listOrderByDir : 'ASC' ); - - if( $id !== null ) - $qb->andWhere( 'u.id = ?3' )->setParameter( 3, $id ); - - return $qb->getQuery()->getResult(); - } - - - /** - * - * @param INEX_Form_User $form The form object - * @param \Entities\User $object The Doctrine2 entity (being edited or blank for add) - * @param bool $isEdit True of we are editing an object, false otherwise - * @param array $options Options passed onto Zend_Form - * @param string $cancelLocation Where to redirect to if 'Cancal' is clicked - * @return void - */ - protected function formPostProcess( $form, $object, $isEdit, $options = null, $cancelLocation = null ) - { - switch( $this->getUser()->getPrivs() ) - { - case \Entities\User::AUTH_SUPERUSER: - $form->removeElement( 'name' ); - $form->getElement( 'username' )->removeValidator( 'stringLength' ); - if( !$isEdit && !$this->getRequest()->isPost() ) - $form->getElement( 'password' )->setValue( OSS_String::random( 12 ) ); - if( $isEdit ) - $form->getElement( 'custid' )->setValue( $object->getCustomer()->getId() ); - break; - - case \Entities\User::AUTH_CUSTADMIN: - $form->removeElement( 'password' ); - $form->removeElement( 'privs' ); - $form->removeElement( 'custid' ); - if( $isEdit ) - { - $form->removeElement( 'name' ); - $form->getElement( 'username' )->setAttrib( 'readonly', 'readonly' ); - } - break; - - default: - throw new OSS_Exception( 'Unhandled user type' ); - } - - if( !$isEdit ) - { - $form->getElement( 'username' )->addValidator( 'OSSDoctrine2Uniqueness', true, - [ 'entity' => '\\Entities\\User', 'property' => 'username' ] - ); - } - } - - - /** - * - * @param INEX_Form_User $form The form object - * @param \Entities\User $object The Doctrine2 entity (being edited or blank for add) - * @param bool $isEdit True of we are editing an object, false otherwise - * @return bool - */ - protected function addPreValidate( $form, $object, $isEdit ) - { - // is this user allowed to edit this object? - if( $isEdit && $this->getUser()->getPrivs() != \Entities\User::AUTH_SUPERUSER ) - { - if( $this->getUser()->getCustomer() != $object->getCustomer() ) - { - $this->addMessage( 'Illegal attempt to edit a user not under your control. The security team have been notified.' ); - $this->getLogger()->alert( "User {$this->getUser()->getUsername()} illegally tried to edit {$object->getUsername()}" ); - $this->redirect( 'user/list' ); - } - } - - return true; - } - - /** - * - * @param INEX_Form_User $form The form object - * @param \Entities\User $object The Doctrine2 entity (being edited or blank for add) - * @param bool $isEdit True of we are editing an object, false otherwise - * @return void - */ - protected function addPostValidate( $form, $object, $isEdit ) - { - - if( $this->getUser()->getPrivs() == \Entities\User::AUTH_SUPERUSER ) - { - $object->setCustomer( - $this->getD2EM()->getRepository( '\\Entities\\Customer' )->find( $form->getElement( 'custid' )->getValue() ) - ); - } - - if( $isEdit ) - { - $object->setLastupdated( new DateTime() ); - $object->setLastupdatedby( $this->getUser()->getId() ); - } - else - { - $object->setCreated( new DateTime() ); - $object->setCreator( $this->getUser()->getUsername() ); - - if( $this->getUser()->getPrivs() == \Entities\User::AUTH_CUSTADMIN ) - { - $object->setCustomer( $this->getUser()->getCustomer() ); - $object->setParent( $this->getUser() ); - $object->setPrivs( \Entities\User::AUTH_CUSTUSER ); - $object->setPassword( OSS_String::random( 16 ) ); - - $c = new \Entities\Contact(); - $c->setCustomer( $this->getUser()->getCustomer() ); - $c->setName( $form->getElement( 'name' )->getValue() ); - $c->setEmail( $form->getElement( 'email' )->getValue() ); - $c->setMobile( $form->getElement( 'authorisedMobile' )->getValue() ); - $c->setCreator( $this->getUser()->getUsername() ); - $c->setCreated( new DateTime() ); - $this->getD2EM()->persist( $c ); - } - else - { - try - { - $object->setParent( - $this->getD2EM()->createQuery( - 'SELECT u FROM \\Entities\\User u WHERE u.privs = ?1 AND u.Customer = ?2' - ) - ->setParameter( 1, \Entities\User::AUTH_CUSTADMIN ) - ->setParameter( 2, $object->getCustomer() ) - ->setMaxResults( 1 ) - ->getSingleResult() - ); - } - catch( \Doctrine\ORM\NoResultException $e ) - { - $object->setParent( $object ); - } - } - } - - return true; - } - - - /** - * - * @param INEX_Form_User $form The form object - * @param \Entities\User $object The Doctrine2 entity (being edited or blank for add) - * @param bool $isEdit True of we are editing an object, false otherwise - * @return void - */ - protected function addPostFlush( $form, $object, $isEdit ) - { - if( !$isEdit ) - { - $this->view->newuser = $object; - $this->sendWelcomeEmail( $object ); - } - else - { - // users are cached so we should delete any existing cache entry for an edited user - $this->clearUserFromCache( $object->getId() ); - } - - return true; - } - - - /** - * Function which can be over-ridden to perform any pre-deletion tasks - * - * @param \Entities\User $object The Doctrine2 entity to delete - * @return bool Return false to stop / cancel the deletion - */ - protected function preDelete( $object ) - { - // if I'm not an admin, then make sure I have permission! - if( $this->getUser()->getPrivs() != \Entities\User::AUTH_SUPERUSER ) - { - if( $object->getCustomer() != $this->getUser()->getCustomer() ) - { - $this->getLogger()->notice( "{$this->getUser()->getUsername()} tried to delete other customer user {$object->getUsername()}" ); - $this->addMessage( 'You are not authorised to delete this user. The administrators have been notified.' ); - return false; - } - } - - // now delete all the users privileges also - foreach( $object->getPreferences() as $pref ) - { - $object->removePreference( $pref ); - $this->getD2EM()->remove( $pref ); - } - - $this->getLogger()->info( "{$this->getUser()->getUsername()} deleted user {$object->getUsername()}" ); - return true; - } - - - /** - * Show the last users to login - * - * Named for the UNIX 'last' command - */ - public function lastAction() - { - $this->assertPrivilege( \Entities\User::AUTH_SUPERUSER ); - $this->view->last = $this->getD2EM()->getRepository( '\\Entities\\User' )->getLastLogins( 100 ); - } - - - public function welcomeEmailAction() - { - $query = 'SELECT u FROM \\Entities\\User u WHERE u.id = ?1'; - - if( $this->getUser()->getPrivs() == \Entities\User::AUTH_CUSTADMIN ) - $query .= ' AND u.Customer = ?2 AND u.privs = ?3'; - - $q = $this->getD2EM()->createQuery( $query ) - ->setParameter( 1, $this->getParam( 'id', 0 ) ); - - if( $this->getUser()->getPrivs() == \Entities\User::AUTH_CUSTADMIN ) - { - $q->setParameter( 2, $this->getUser()->getCustomer() ) - ->setParameter( 3, \Entities\User::AUTH_CUSTUSER ); - } - - try - { - $user = $q->getSingleResult(); - } - catch( Doctrine\ORM\NoResultException $e ) - { - $this->addMessage( "Unknown or invalid user.", OSS_Message::ERROR ); - return $this->_forward( 'list' ); - } - - $this->view->resend = true; - $this->view->newuser = $user; - - if( $this->sendWelcomeEmail( $user ) ) - $this->addMessage( "Welcome email has been resent to {$user->getEmail()}", OSS_Message::SUCCESS ); - else - $this->addMessage( "Due to a system error, we could not resend the welcome email to {$user->getEmail()}", OSS_Message::ERROR ); - - $this->redirect( 'user/list' ); - } - - - /** - * Send a welcome email to a new user - * - * @param \Entities\User $user The recipient of the email - * @return bool True if the mail was sent successfully - */ - private function sendWelcomeEmail( $user ) - { - try - { - $mail = $this->getMailer(); - $mail->setFrom( $this->_options['identity']['email'], $this->_options['identity']['name'] ) - ->setSubject( $this->_options['identity']['sitename'] . ' - ' . _( 'Your Access Details' ) ) - ->addTo( $user->getEmail(), $user->getUsername() ) - ->setBodyHtml( $this->view->render( 'user/email/html/welcome.phtml' ) ) - ->send(); - } - catch( Zend_Mail_Exception $e ) - { - $this->getLogger()->alert( "Could not send welcome email for new user!\n\n" . $e->toString() ); - return false; - } - - return true; - } - -} - diff --git a/application/controllers/UtilsController.php b/application/controllers/UtilsController.php deleted file mode 100644 index 892bba4f2..000000000 --- a/application/controllers/UtilsController.php +++ /dev/null @@ -1,78 +0,0 @@ - - * @category INEX - * @package INEX_Controller - * @copyright Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd - * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 - */ -class UtilsController extends INEX_Controller_AuthRequiredAction -{ - - public function preDispatch() - { - if( $this->getUser()->getPrivs() != \Entities\User::AUTH_SUPERUSER ) - return $this->forward( 'insufficient-permissions', 'error' ); - } - - - /** - * Display apcinfo() - */ - public function apcinfoAction() - {} - - /** - * Display apcinfo() - */ - public function realApcinfoAction() - { - Zend_Controller_Action_HelperBroker::removeHelper( 'viewRenderer' ); - @ob_end_clean(); - $BU = Zend_Controller_Front::getInstance()->getBaseUrl() . '/utils/apcinfo'; - require( APPLICATION_PATH . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'library' . DIRECTORY_SEPARATOR . 'apcinfo.php' ); - } - - - /** - * Display phpinfo - */ - public function phpinfoAction() - {} - - /** - * Display real phpinfo() - */ - public function realPhpinfoAction() - { - Zend_Controller_Action_HelperBroker::removeHelper( 'viewRenderer' ); - @ob_end_clean(); - phpinfo(); - } -} - diff --git a/application/controllers/VendorController.php b/application/controllers/VendorController.php deleted file mode 100644 index 3bc2e5857..000000000 --- a/application/controllers/VendorController.php +++ /dev/null @@ -1,86 +0,0 @@ - - * @category INEX - * @package INEX_Controller - * @copyright Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd - * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 - */ -class VendorController extends INEX_Controller_FrontEnd -{ - /** - * This function sets up the frontend controller - */ - protected function _feInit() - { - $this->assertPrivilege( \Entities\User::AUTH_SUPERUSER ); - - $this->view->feParams = $this->_feParams = (object)[ - 'entity' => '\\Entities\\Vendor', - 'form' => 'INEX_Form_Vendor', - 'pagetitle' => 'Vendors', - - 'titleSingular' => 'Vendor', - 'nameSingular' => 'a vendor', - - 'defaultAction' => 'list', // OPTIONAL; defaults to 'list' - - 'listOrderBy' => 'name', - 'listOrderByDir' => 'ASC', - - 'listColumns' => [ - 'id' => [ 'title' => 'UID', 'display' => false ], - 'name' => 'Name' - ] - ]; - - // display the same information in the view as the list - $this->_feParams->viewColumns = $this->_feParams->listColumns; - } - - /** - * Provide array of users for the listAction and viewAction - * - * @param int $id The `id` of the row to load for `viewAction`. `null` if `listAction` - */ - protected function listGetData( $id = null ) - { - $qb = $this->getD2EM()->createQueryBuilder() - ->select( 'v.id AS id, v.name AS name' ) - ->from( '\\Entities\\Vendor', 'v' ); - - if( isset( $this->_feParams->listOrderBy ) ) - $qb->orderBy( $this->_feParams->listOrderBy, isset( $this->_feParams->listOrderByDir ) ? $this->_feParams->listOrderByDir : 'ASC' ); - - if( $id !== null ) - $qb->andWhere( 'v.id = ?1' )->setParameter( 1, $id ); - - return $qb->getQuery()->getResult(); - } -} - diff --git a/application/controllers/VirtualInterfaceController.php b/application/controllers/VirtualInterfaceController.php deleted file mode 100644 index 0caa3f4ca..000000000 --- a/application/controllers/VirtualInterfaceController.php +++ /dev/null @@ -1,289 +0,0 @@ - - * @category INEX - * @package INEX_Controller - * @copyright Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd - * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 - */ -class VirtualInterfaceController extends INEX_Controller_FrontEnd -{ - /** - * This function sets up the frontend controller - */ - protected function _feInit() - { - $this->view->feParams = $this->_feParams = (object)[ - 'entity' => '\\Entities\\VirtualInterface', - 'form' => 'INEX_Form_Interface_Virtual', - 'pagetitle' => '(Virtual) Interfaces', - - 'titleSingular' => 'Virtual Interface', - 'nameSingular' => 'a virtual interface', - - 'defaultAction' => 'list', - - 'listOrderBy' => 'customer', - 'listOrderByDir' => 'ASC', - ]; - - switch( $this->getUser()->getPrivs() ) - { - case \Entities\User::AUTH_SUPERUSER: - $this->_feParams->listColumns = [ - 'id' => [ 'title' => 'UID', 'display' => false ], - - 'customer' => [ - 'title' => 'Customer', - 'type' => self::$FE_COL_TYPES[ 'HAS_ONE' ], - 'controller' => 'customer', - 'action' => 'overview', - 'idField' => 'custid' - ], - - 'shortname' => [ - 'title' => 'Shortname', - 'type' => self::$FE_COL_TYPES[ 'HAS_ONE' ], - 'controller' => 'customer', - 'action' => 'overview', - 'idField' => 'custid' - ], - - 'location' => 'Location', - 'switch' => 'Switch', - - 'port' => [ - 'title' => 'Port', - 'type' => self::$FE_COL_TYPES[ 'SCRIPT' ], - 'script' => 'virtual-interface/list-column-port.phtml' - ], - - 'speed' => 'Speed' - ]; - break; - - case \Entities\User::AUTH_CUSTADMIN: - default: - $this->redirectAndEnsureDie( 'error/insufficient-permissions' ); - } - - } - - public function viewAction() - { - $this->forward( 'add' ); - } - - /** - * Provide array of virtual interfaces for the listAction - * - * @param int $id The `id` of the row to load for `viewAction`. `null` if `listAction` - */ - protected function listGetData( $id = null ) - { - $qb = $this->getD2EM()->createQueryBuilder() - ->select( - 'vi.id, - c.name AS customer, c.id AS custid, c.shortname AS shortname, - l.name AS location, s.name AS switch, - sp.name AS port, SUM( pi.speed ) AS speed, COUNT( pi.id ) AS ports' - ) - ->from( '\\Entities\\VirtualInterface', 'vi' ) - ->leftJoin( 'vi.Customer', 'c' ) - ->leftJoin( 'vi.PhysicalInterfaces', 'pi' ) - ->leftJoin( 'pi.SwitchPort', 'sp' ) - ->leftJoin( 'sp.Switcher', 's' ) - ->leftJoin( 's.Cabinet', 'cab' ) - ->leftJoin( 'cab.Location', 'l' ) - ->groupBy( 'vi' ); - - return $qb->getQuery()->getArrayResult(); - } - - - - /* - * If deleting a virtual interface, we should also the delete the physical and vlan interfaces - * if they exist. - * - * @param \Entities\VirtualInterface $vi The virtual interface to delete - */ - protected function preDelete( $vi ) - { - foreach( $vi->getPhysicalInterfaces() as $pi ) - { - $this->getLogger()->info( "Deleting physical interface with id #{$pi->getId()} while deleting virtual interface #{$vi->getId()}" ); - $vi->removePhysicalInterface( $pi ); - $this->getD2EM()->remove( $pi ); - } - - foreach( $vi->getVlanInterfaces() as $vli ) - { - $this->getLogger()->info( "Deleting VLAN interface with id #{$vli->getId()} while deleting virtual interface #{$vi->getId()}" ); - $vi->removeVlanInterface( $vli ); - $this->getD2EM()->remove( $vli ); - } - - foreach( $vi->getMACAddresses() as $ma ) - { - $this->getLogger()->info( "Deleting MAC Address record #{$ma->getMac()} while deleting virtual interface #{$vi->getId()}" ); - $vi->removeMACAddresse( $ma ); - $this->getD2EM()->remove( $ma ); - } - - return true; - } - - - /** - * @param INEX_Form_Interface_Virtual $form The form object - * @param \Entities\VirtualInterface $object The Doctrine2 entity (being edited or blank for add) - * @param bool $isEdit True of we are editing an object, false otherwise - * @param array $options Options passed onto Zend_Form - * @param string $cancelLocation Where to redirect to if 'Cancal' is clicked - * @return void - */ - protected function formPostProcess( $form, $object, $isEdit, $options = null, $cancelLocation = null ) - { - if( $isEdit ) - { - $form->getElement( 'custid' )->setValue( $object->getCustomer()->getId() ); - - $this->view->cust = $object->getCustomer(); - $this->view->physInts = $object->getPhysicalInterfaces(); - $this->view->vlanInts = $object->getVlanInterfaces(); - } - } - - - /** - * @param INEX_Form_Interface_Virtual $form The form object - * @param \Entities\VirtualInterface $object The Doctrine2 entity (being edited or blank for add) - * @param bool $isEdit True of we are editing an object, false otherwise - * @return void - */ - protected function addPostValidate( $form, $object, $isEdit ) - { - $object->setCustomer( - $this->getD2EM()->getRepository( '\\Entities\\Customer' )->find( $form->getElement( 'custid' )->getValue() ) - ); - - return true; - } - - - public function addWizardAction() - { - $this->view->form = $form = new INEX_Form_Interface_AddWizard(); - - // Process a submitted form if it passes initial validation - if( $this->getRequest()->isPost() && $form->isValid( $_POST ) ) - { - // check customer information - if( !( $cust = $this->getD2EM()->getRepository( '\\Entities\\Customer' )->find( $form->getValue( 'custid' ) ) ) ) - { - $form->getElement( 'custid' )->addError( 'Invalid customer' ); - } - else - { - $vi = new \Entities\VirtualInterface(); - $form->assignFormToEntity( $vi, $this, false ); - $vi->setCustomer( $cust ); - $this->getD2EM()->persist( $vi ); - - $pi = new \Entities\PhysicalInterface(); - $form->assignFormToEntity( $pi, $this, false ); - $pi->setVirtualInterface( $vi ); - $pi->setSwitchPort( - $this->getD2EM()->getRepository( '\\Entities\\SwitchPort' )->find( $form->getValue( 'switchportid' ) ) - ); - $pi->setMonitorindex( - $this->getD2EM()->getRepository( '\\Entities\\PhysicalInterface' )->getNextMonitorIndex( $cust ) - ); - $this->getD2EM()->persist( $pi ); - - - - $vli = new \Entities\VlanInterface(); - $form->assignFormToEntity( $vli, $this, false ); - $vli->setIPv4Address( - $this->getD2EM()->getRepository( '\\Entities\\IPv4Address' )->find( $form->getElement( 'ipv4addressid' )->getValue() ) - ); - $vli->setIPv6Address( - $this->getD2EM()->getRepository( '\\Entities\\IPv6Address' )->find( $form->getElement( 'ipv6addressid' )->getValue() ) - ); - $vli->setVlan( - $this->getD2EM()->getRepository( '\\Entities\\Vlan' )->find( $form->getElement( 'vlanid' )->getValue() ) - ); - $vli->setVirtualInterface( $vi ); - $this->getD2EM()->persist( $vli ); - - $this->getD2EM()->flush(); - - $this->getLogger()->info( 'New virtual, physical and VLAN interface created for ' . $cust->getName() ); - $this->addMessage( "New interface created!", OSS_Message::SUCCESS ); - - - if( $this->getParam( 'rtn', false ) == 'ov' ) - $this->_redirect( 'customer/view/id/' . $cust->getId() ); - - $this->_redirect( 'virtual-interface/edit/id/' . $vi->getId() ); - } - } - - if( !isset( $cust ) && ( $cid = $this->getParam( 'custid', false ) ) ) - $cust = $this->getD2EM()->getRepository( '\\Entities\\Customer' )->find( $cid ); - - if( !$this->getRequest()->isPost() ) - { - // make BGP MD5 easy - $form->getElement( 'ipv4bgpmd5secret' )->setValue( OSS_String::random() ); - $form->getElement( 'ipv6bgpmd5secret' )->setValue( $form->getElement( 'ipv4bgpmd5secret' )->getValue() ); - - if( isset( $cust ) ) - { - $form->getElement( 'custid' )->setValue( $cust->getId() ); - $form->getElement( 'maxbgpprefix' )->setValue( $cust->getMaxprefixes() ); - } - } - - if( $this->getParam( 'rtn', false ) == 'ov' ) - { - $form->getElement( 'custid' )->setAttrib( 'disabled', 'disabled' ); - $form->getElement( 'cancel' )->setAttrib( 'href', - OSS_Utils::genUrl( 'customer', 'view', false, [ 'id' => $this->getParam( 'custid' ) ] ) - ); - } - else - { - $form->getElement( 'cancel' )->setAttrib( 'href', OSS_Utils::genUrl( 'virtual-interface', 'list' ) ); - } - } - -} - diff --git a/application/controllers/VlanController.php b/application/controllers/VlanController.php deleted file mode 100644 index 00a968e84..000000000 --- a/application/controllers/VlanController.php +++ /dev/null @@ -1,108 +0,0 @@ - - * @category INEX - * @package INEX_Controller - * @copyright Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd - * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 - */ -class VlanController extends INEX_Controller_FrontEnd -{ - /** - * This function sets up the frontend controller - */ - protected function _feInit() - { - $this->assertPrivilege( \Entities\User::AUTH_SUPERUSER ); - - $this->view->feParams = $this->_feParams = (object)[ - 'entity' => '\\Entities\\Vlan', - 'form' => 'INEX_Form_Vlan', - 'pagetitle' => 'VLANs', - - 'titleSingular' => 'VLAN', - 'nameSingular' => 'a VLAN', - - 'defaultAction' => 'list', // OPTIONAL; defaults to 'list' - - 'listOrderBy' => 'number', - 'listOrderByDir' => 'ASC', - - 'listColumns' => [ - - 'id' => [ 'title' => 'UID', 'display' => false ], - 'name' => 'Name', - 'number' => 'Tag', - 'rcvrfname' => 'VRF Name' - ] - ]; - - // display the same information in the view as the list - $this->_feParams->viewColumns = array_merge( - $this->_feParams->listColumns, - [ 'notes' => 'Notes' ] - ); - } - - /** - * Provide array of VLANs for the listAction and viewAction - * - * @param int $id The `id` of the row to load for `viewAction`. `null` if `listAction` - */ - protected function listGetData( $id = null ) - { - $qb = $this->getD2EM()->createQueryBuilder() - ->select( 'v.id AS id, v.name AS name, v.number AS number, - v.rcvrfname AS rcvrfname, v.notes AS notes' - ) - ->from( '\\Entities\\Vlan', 'v' ); - - if( isset( $this->_feParams->listOrderBy ) ) - $qb->orderBy( $this->_feParams->listOrderBy, isset( $this->_feParams->listOrderByDir ) ? $this->_feParams->listOrderByDir : 'ASC' ); - - if( $id !== null ) - $qb->andWhere( 'v.id = ?1' )->setParameter( 1, $id ); - - return $qb->getQuery()->getResult(); - } - - /** - * Clear the cache after a change to a VLAN - * - * @param \Entities\Vlan $object - * @return boolean - */ - protected function postFlush( $object ) - { - // this is created in Repositories\Vlan::getNames() - $this->getD2Cache()->delete( \Repositories\Vlan::ALL_CACHE_KEY ); - return true; - } - -} - diff --git a/application/controllers/VlanInterfaceController.php b/application/controllers/VlanInterfaceController.php deleted file mode 100644 index ad1c1c27f..000000000 --- a/application/controllers/VlanInterfaceController.php +++ /dev/null @@ -1,267 +0,0 @@ - - * @category INEX - * @package INEX_Controller - * @copyright Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd - * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 - */ -class VlanInterfaceController extends INEX_Controller_FrontEnd -{ - /** - * This function sets up the frontend controller - */ - protected function _feInit() - { - $this->view->feParams = $this->_feParams = (object)[ - 'entity' => '\\Entities\\VlanInterface', - 'form' => 'INEX_Form_Interface_Vlan', - 'pagetitle' => 'VLAN Interfaces', - - 'titleSingular' => 'VLAN Interface', - 'nameSingular' => 'a VLAN interface', - - 'defaultAction' => 'list', - - 'listOrderBy' => 'customer', - 'listOrderByDir' => 'ASC', - ]; - - switch( $this->getUser()->getPrivs() ) - { - case \Entities\User::AUTH_SUPERUSER: - $this->_feParams->listColumns = [ - 'id' => [ 'title' => 'UID', 'display' => false ], - - 'customer' => [ - 'title' => 'Customer', - 'type' => self::$FE_COL_TYPES[ 'HAS_ONE' ], - 'controller' => 'customer', - 'action' => 'overview', - 'idField' => 'custid' - ], - - 'vlan' => [ - 'title' => 'VLAN Name', - 'type' => self::$FE_COL_TYPES[ 'HAS_ONE' ], - 'controller' => 'vlan', - 'action' => 'list', - 'idField' => 'vlanid' - ], - - 'rsclient' => 'Route Server', - 'ipv4' => 'ipv4', - 'ipv6' => 'ipv6' - ]; - - $this->_feParams->viewColumns = array_merge( - $this->_feParams->listColumns, - [ - 'ipv4enabled' => 'IPv4 Enabled', - 'ipv4hostname' => 'IPv4 Hostname', - 'ipv6enabled' => 'IPv6 Enabled', - 'ipv6hostname' => 'IPv6 Hostname', - 'mcastenabled' => 'Multicast Enabled', - 'irrdbfilter' => 'IRRDB Filter', - 'bgpmd5secret' => 'BGP MD5 Secret (deprecated)', - 'ipv4bgpmd5secret' => 'IPv4 BGP MD5 Secret', - 'ipv6bgpmd5secret' => 'IPv6 BGP MD5 Secret', - 'maxbgpprefix' => 'Max BGP Prefixes', - 'rsclient' => 'Route Server Client', - 'ipv4canping' => 'Monitoring Enabled via IPv4 ICMP', - 'ipv6canping' => 'Monitoring Enabled via IPv6 ICMP', - 'ipv4monitorrcbgp' => 'Monitor Route Collector IPv4 BGP Session', - 'ipv6monitorrcbgp' => 'Monitor Route Collector IPv6 BGP Session', - 'as112client' => 'AS112 Client', - 'busyhost' => 'Bust Host?', - 'notes' => 'Notes' - ] - ); - - break; - - case \Entities\User::AUTH_CUSTADMIN: - default: - $this->redirectAndEnsureDie( 'error/insufficient-permissions' ); - } - - } - - - - /** - * Provide array of virtual interfaces for the listAction - * - * @param int $id The `id` of the row to load for `viewAction`. `null` if `listAction` - */ - protected function listGetData( $id = null ) - { - $qb = $this->getD2EM()->createQueryBuilder() - ->select( - 'vli.id AS id, vli.mcastenabled AS mcastenabled, - vli.ipv4enabled AS ipv4enabled, vli.ipv4hostname AS ipv4hostname, vli.ipv4canping AS ipv4canping, - vli.ipv4monitorrcbgp AS ipv4monitorrcbgp, vli.ipv4bgpmd5secret AS ipv4bgpmd5secret, - vli.ipv6enabled AS ipv6enabled, vli.ipv6hostname AS ipv6hostname, vli.ipv6canping AS ipv6canping, - vli.ipv6monitorrcbgp AS ipv6monitorrcbgp, vli.ipv6bgpmd5secret AS ipv6bgpmd5secret, - vli.irrdbfilter AS irrdbfilter, vli.bgpmd5secret AS bgpmd5secret, vli.maxbgpprefix AS maxbgpprefix, - vli.as112client AS as112client, vli.busyhost AS busyhost, vli.notes AS notes, - vli.rsclient AS rsclient, - ip4.address AS ipv4, ip6.address AS ipv6, - v.id AS vlanid, v.name AS vlan, - vi.id AS vintid, - c.name AS customer, c.id AS custid' - ) - ->from( '\\Entities\\VlanInterface', 'vli' ) - ->leftJoin( 'vli.VirtualInterface', 'vi' ) - ->leftJoin( 'vli.Vlan', 'v' ) - ->leftJoin( 'vli.IPv4Address', 'ip4' ) - ->leftJoin( 'vli.IPv6Address', 'ip6' ) - ->leftJoin( 'vi.Customer', 'c' ); - - if( $id !== null ) - $qb->where( 'vli.id = ' . intval( $id ) ); - - return $qb->getQuery()->getArrayResult(); - } - - - /** - * @param INEX_Form_Interface_Vlan $form The form object - * @param \Entities\VlanInterface $object The Doctrine2 entity (being edited or blank for add) - * @param bool $isEdit True of we are editing an object, false otherwise - * @param array $options Options passed onto Zend_Form - * @param string $cancelLocation Where to redirect to if 'Cancal' is clicked - * @return void - */ - protected function formPostProcess( $form, $object, $isEdit, $options = null, $cancelLocation = null ) - { - if( $isEdit ) - { - $form->getElement( 'ipv4addressid' )->setValue( $object->getIPv4Address() ? $object->getIPv4Address()->getAddress() : null ); - $form->getElement( 'ipv6addressid' )->setValue( $object->getIPv6Address() ? $object->getIPv6Address()->getAddress() : null ); - $form->getElement( 'virtualinterfaceid' )->setValue( $object->getVirtualInterface()->getId() ); - $form->getElement( 'vlanid' )->setValue( $object->getVlan()->getId() ); - - $form->getElement( 'preselectIPv4Address' )->setValue( $object->getIPv4Address() ? $object->getIPv4Address()->getId() : null ); - $form->getElement( 'preselectIPv6Address' )->setValue( $object->getIPv6Address() ? $object->getIPv6Address()->getId() : null ); - $form->getElement( 'preselectVlanInterface' )->setValue( $object->getId() ); - - if( $this->getParam( 'rtn', false ) == 'vli' ) - $form->setAction( OSS_Utils::genUrl( 'vlan-interface', 'edit', false, [ 'id' => $object->getId(), 'rtn' => 'vli' ] ) ); - else - $form->getElement( 'cancel' )->setAttrib( 'href', OSS_Utils::genUrl( 'virtual-interface', 'edit', false, [ 'id' => $object->getVirtualInterface()->getId() ] ) ); - } - else // not editing - { - if( $this->getRequest()->isPost() && ( $vintid = ( isset( $_POST['virtualinterfaceid'] ) && $_POST['virtualinterfaceid'] ) ) ) - $vint = $this->getD2EM()->getRepository( '\\Entities\\VirtualInterface' )->find( $_POST['virtualinterfaceid'] ); - else if( ( $vintid = $this->getRequest()->getParam( 'vintid' ) ) !== null ) - $vint = $this->getD2EM()->getRepository( '\\Entities\\VirtualInterface' )->find( $vintid ); - - if( !isset( $vint ) || !$vint ) - throw new INEX_Exception( 'Not sure how you would add a VLAN interface without a containing virtual interface'); - - // make BGP MD5 easy - $form->getElement( 'ipv4bgpmd5secret' )->setValue( OSS_String::random() ); - $form->getElement( 'ipv6bgpmd5secret' )->setValue( $form->getElement( 'ipv4bgpmd5secret' )->getValue() ); - $form->getElement( 'maxbgpprefix' )->setValue( $vint->getCustomer()->getMaxprefixes() ); - - $form->getElement( 'virtualinterfaceid' )->setValue( $vint->getId() ); - $form->getElement( 'cancel' )->setAttrib( 'href', OSS_Utils::genUrl( 'virtual-interface', 'edit', false, [ 'id' => $vint->getId() ] ) ); - } - } - - /** - * @param INEX_Form_Interface_Vlan $form The form object - * @param \Entities\VlanInterface $object The Doctrine2 entity (being edited or blank for add) - * @param bool $isEdit True of we are editing an object, false otherwise - * @return void - */ - protected function addPostValidate( $form, $object, $isEdit ) - { - $object->setIPv4Address( - $this->getD2EM()->getRepository( '\\Entities\\IPv4Address' )->find( $form->getElement( 'ipv4addressid' )->getValue() ) - ); - - $object->setIPv6Address( - $this->getD2EM()->getRepository( '\\Entities\\IPv6Address' )->find( $form->getElement( 'ipv6addressid' )->getValue() ) - ); - - $object->setVlan( - $this->getD2EM()->getRepository( '\\Entities\\Vlan' )->find( $form->getElement( 'vlanid' )->getValue() ) - ); - - $object->setVirtualInterface( - $this->getD2EM()->getRepository( '\\Entities\\VirtualInterface' )->find( $form->getElement( 'virtualinterfaceid' )->getValue() ) - ); - - return true; - } - - - /** - * You can add `OSS_Message`s here and redirect to a custom destination after a - * successful add / edit operation. - * - * @param INEX_Form_Interface_Vlan $form The form object - * @param \Entities\VlanInterface $object The Doctrine2 entity (being edited or blank for add) - * @param bool $isEdit True of we are editing an object, false otherwise - * @return bool `false` for standard message and redirection, otherwise redirect within this function - */ - protected function addDestinationOnSuccess( $form, $object, $isEdit ) - { - if( $this->getParam( 'rtn', false ) == 'vli' ) - return false; - - $this->addMessage( - 'VLAN interface successfuly ' . ( $isEdit ? 'edited.' : 'added.' ), OSS_Message::SUCCESS - ); - - $this->redirectAndEnsureDie( 'virtual-interface/edit/id/' . $object->getVirtualInterface()->getId() ); - } - - /** - * You can add `OSS_Message`s here and redirect to a custom destination after a - * successful deletion operation. - * - * @return bool `false` for standard message and redirection, otherwise redirect within this function - */ - protected function deleteDestinationOnSuccess() - { - if( $this->getParam( 'rtn', false ) == 'vli' ) - return false; - - $this->addMessage( - 'VLAN interface deleted successfuly.', OSS_Message::SUCCESS - ); - - $this->redirectAndEnsureDie( 'virtual-interface/edit/id/' . $this->getParam( 'vintid' ) ); - } - -} - diff --git a/application/controllers/WeatherMapController.php b/application/controllers/WeatherMapController.php deleted file mode 100644 index 8392c8848..000000000 --- a/application/controllers/WeatherMapController.php +++ /dev/null @@ -1,53 +0,0 @@ - - * @category INEX - * @package INEX_Controller - * @copyright Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd - * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 - */ -class WeatherMapController extends INEX_Controller_Action -{ - - public function indexAction() - { - // do we have a valid key - $key = $this->getParam( 'id', null ); - - if( $key === null || !isset( $this->_options['weathermap'][$key] ) ) - { - $this->addMessage( 'Unknown weathermap requested', OSS_Message::ERROR ); - $this->view->weathermap = false; - } - - $this->view->weathermaps = $this->_options['weathermap']; - $this->view->weathermap = $this->_options['weathermap'][$key]; - } - -} - diff --git a/application/modules/.hello_git b/application/modules/.hello_git deleted file mode 100644 index 2183f4509..000000000 --- a/application/modules/.hello_git +++ /dev/null @@ -1 +0,0 @@ -Hello ;) diff --git a/application/views/_skins/example/footer-content.phtml b/application/views/_skins/example/footer-content.phtml deleted file mode 100644 index 8096a184c..000000000 --- a/application/views/_skins/example/footer-content.phtml +++ /dev/null @@ -1,44 +0,0 @@ - -
- -

- IXP Manager V{$smarty.const.APPLICATION_VERSION} -   |   - Copyright © 2010 - {$smarty.now|date_format:'%Y'} Internet Neutral Exchange Association Ltd. - http://www.inex.ie/ -  |  - {mailto address=$options.identity.email encode='javascript' text="Contact Us"} -

- -

- Licensed under GPL v2.0. -  |  - This Program is provided AS IS, without warranty. -  |  - {assign var="ENDTIME" value=microtime(1)} - {assign var="RUNNINGTIME" value="`$ENDTIME-$smarty.const.APPLICATION_STARTTIME`"} - Generated in {$RUNNINGTIME|string_format:"%0.3f"} seconds -

- -

- Keep up with - - INEX - -  |  - Get the source at - - GitHub - -  |  - We're - - IPv6 Ready - -  Are you? -  |  - Join the - - LinkedIn Group - -

-
\ No newline at end of file diff --git a/application/views/_skins/inex/customer/email/welcome-email.phtml b/application/views/_skins/inex/customer/email/welcome-email.phtml deleted file mode 100644 index fb069fe0d..000000000 --- a/application/views/_skins/inex/customer/email/welcome-email.phtml +++ /dev/null @@ -1,347 +0,0 @@ - -Dear New INEX Member, - -Firstly, welcome to {$options.identity.orgname}! This is your {$options.identity.name} welcome e-mail. - -Please take some time to read this email -- it contains important information concerning your {$options.identity.orgname} membership. - -Several sections of this email require your input; these are all clearly marked by the heading "Your Input". We would appreciate if you could provide us with all the details requested as this will allow us to provide you and other {$options.identity.orgname} members with a better quality of service. - - -Connection Details -================== - -You have opted to connect to {$options.identity.orgname} using {$customer->getVirtualInterfaces()|@count} Ethernet ports. - -Unless you have been advised otherwise, you have been assigned an untagged port on the primary peering LAN(s). - -We have assigned the following IP addresses and switch-ports for your exclusive use: - -{foreach $customer->getVirtualInterfaces() as $connection} -============================================ -Connection {counter name=numconnections} -============================================ - -LAG Port: {if $connection->getPhysicalinterfaces()|@count > 1}Yes, comprising of:{else}No{/if} - -{foreach $connection->getPhysicalinterfaces() as $pi} -Switch Port: {$pi->getSwitchPort()->getSwitcher()->getName()}.inex.ie, {$pi->getSwitchPort()->getName()} -Speed: {$pi->getSpeed()}Mbps -Duplex: {$pi->getDuplex()} -Location: {$pi->getSwitchPort()->getSwitcher()->getCabinet()->getLocation()->getName()} -Colo Cabinet ID: {$pi->getSwitchPort()->getSwitcher()->getCabinet()->getName()} - -{/foreach} - -{foreach $connection->getVlanInterfaces() as $vi} -{assign var='vlanid' value=$vi->getVlan()->getId()} -{$vi->getVlan()->getName()} ---------------------------------------------- - -{if $vi->getIpv6enabled()} -IPv6 Address: {$vi->getIPv6Address()->getAddress()}/{$netinfo.$vlanid.6.masklen} -IPv6 Hostname: {$vi->getIpv6hostname()} -{else} -IPv6: Please contact us at {$options.identity.email} to enable IPv6 -{/if} - -{if $vi->getIpv4enabled()} -IPv4 Address: {$vi->getIPv4Address()->getAddress()}/{$netinfo.$vlanid.4.masklen} -IPv4 Hostname: {$vi->getIpv4hostname()} -{else} -IPv4: Please contact us at {$options.identity.email} to enable IPv4 -{/if} - -{if not isset( $ipv4md5 )} - {assign var='ipv4md5' value=$vi->getIpv4bgpmd5secret()} - {assign var='ipv6md5' value=$vi->getIpv6bgpmd5secret()} -{/if} - -{/foreach} - -{/foreach} - -Your Input ----------- - -As a matter of policy, {$options.identity.orgname} hard-wires all switch ports to a specific speed and duplex setting. Should you require a different duplex setting to the one specified above, or should you require your reverse DNS hostnames changed for either IPv4 or IPv6, please contact {$options.identity.email}. - -**If you are connecting to {$options.identity.orgname} via a switch using UTP, please ensure to use a cross over cable on your end.** - - -Member Portal :: IXP Manager -============================ - -{$options.identity.orgname} provides a portal for members which provides traffic graphs for your ports, the ability to self-provision some services, the contact and peering details of all other members, a Peering Manager tool, documentation, support information, mailing list subscription management and much more. - -Every member is assigned an Administration account with which you then create individual user accounts. The Administration account is only meant for this purpose and as such, all functionality is only available through user accounts. - -{if $admins|@count} -We have created your administration account(s) with the following username(s) and email address(es): - -{foreach $admins as $a} - * {$a->getUsername()} <{$a->getEmail()}> -{/foreach} - -Please browse to the following page and use the 'Lost Password' facility to set a new password for this account. - -{genUrl controller="auth" action="lost-password"} -{else} -Please contact us for your account details at {$options.identity.email}. -{/if} - - - -Route Servers -============= - -{$options.identity.orgname} operates a Route Server cluster; this facility allows all members who connect to the cluster to see all routing prefixes sent to the cluster by any other member. I.e. it provides an quick, safe and easy way to peer with any other route server user. - -The {$options.identity.orgname} route server cluster is aimed at: - -o small to medium sized members of the exchange who don't have the time or resources to aggressively manage their peering relationships. - -o larger members of the exchange who have an open peering policy, but where it may not be worth their while managing peering relationships with smaller members of the exchange. - -If you don't have any good reasons not to use the route server cluster, you should probably use it. - -The service is designed to be reliable. It operates on two physical servers, each located in a different data centre. The service is available on all {$options.identity.orgname} networks (public peering lans #1 and #2, and voip peering lans #1 and #2), on both ipv4 and ipv6. The route servers also filter inbound routing prefixes based on published RIPE IRR policies, which means that using the route servers for peering is generally much safer than peering directly with other members. - -See {genUrl controller="dashboard" action="rs-info"} for more information. - -Your Input ----------- - -If you wish to use the {$options.identity.orgname} route server system, please email {$options.identity.email} or you can enable the sessions yourself via the IXP Manager. - - -Hosting Routers in the {$options.identity.orgname} Cage -================================ - -If your company does not have a presence in any of the {$options.identity.orgname}'s co-location facilities, {$options.identity.orgname} can provide rack space for the purpose of housing routers, NTU's and other equipment whose purpose is to facilitate the transmission of IP packets through the exchange. {$options.identity.orgname} also permits its members to install modems for the purpose of out-of-band access to their equipment. Each member is assigned 4U of cabinet space by default. If your company needs more space than this, {$options.identity.orgname} needs to know about it. - - -Your Input ----------- - -If you wish to house router equipment in the {$options.identity.orgname} cage, please inform {$options.identity.orgname} operations immediately and provide full details on all the equipment you intend to house in the facility. We can liaise with you directly to get your equipment installed safely. - -If you wish to bring a PSTN phone line into the {$options.identity.orgname} cage to connect a modem to your routing equipment, please let {$options.identity.orgname} Operations know immediately. - ------------ - - -External Connections to the {$options.identity.orgname} Infrastructure -=============================================== - -Many {$options.identity.orgname} members already have a presence in the co-location facilities which house {$options.identity.orgname} points of presence. If you are such a member and wish to keep your routing equipment in your own cabinet space, {$options.identity.orgname} is happy to accept cross-connects from your cabinet to the {$options.identity.orgname} cage. Please note that all connections into the {$options.identity.orgname} cage are paid for directly by {$options.identity.orgname} members, not by {$options.identity.orgname} itself. - -{$options.identity.orgname} considers metro ethernet connections as standard external connections. When ordering your metro ethernet circuit, please specify the correct {$options.identity.orgname} termination point to your provider. - -Cross-connect cabinet termination points are provided in the "Connection Details" section above. - - -Your Input ----------- - -If you want to connect to {$options.identity.orgname} using a cross-connect, you must order it directly from your co-lo / metro ethernet provider and inform {$options.identity.orgname} Operations immediately. Please ensure that you include full details on the connection type and termination cabinet when ordering the cross-connect from your co-lo provider. - ----------- - - -Mailing Lists -============= - -To encourage co-operation between {$options.identity.orgname} members, {$options.identity.orgname} provides mailing list services. There are currently three mailing lists: - -Name: tech@inex.ie -Purpose: INEX technical discussion -Subscription Policy: individuals, roles, aliases, - no limit per organisation -Distribution: private - -Name: committee@inex.ie -Purpose: The INEX Executive Committee -Subscription Policy: elected committee members only -Distribution: private & confidential - -Name: members@inex.ie -Purpose: Non-technical discussions relevant to INEX -Subscription Policy: individuals only, no limit per organisation -Distribution: private & confidential - - -Your Input ----------- - -Should you wish to subscribe to either or both mailing lists, please create a user account on the IXP Manager (as explained above) and then browse to the 'Profile' page where you can subscribe to the above and more mailing lists. - ----------- - - -INEX Operations -=============== - -Technical Operations for INEX are provided by Network Ability Ltd (with additional support from Open Source Solutions Ltd). Technical support contact details for INEX can be found at: - - {genUrl controller="static" action="support"} - -In general, the best way to contact INEX operations is by email at: operations@inex.ie. If there is an emergency requiring immediate assistance, please contact one of us on the mobile phones listed on the web page. - - -Peering -======= - -INEX facilitates peering between its members, but other than the minimum current peering requirements (4 members or 10%, whichever is larger) does not mandate peering with any particular member apart from INEX itself. - -You will find a full list of members on the IXP Manager, along with the correct email addresses to use for peering requests. - -When emailing other INEX members about peering requests, please include all technical details relevant to the peering session, including your IP address, your AS number, and an estimate of the number of prefixes you intend to announce to that candidate peer. Several members require written legal contracts to be signed as a part of their peering procedures. If you require a written contract, please specify this on your peering request; similarly, it may be often useful to indicate your willingness (or otherwise) to sign legal contracts when approaching other members about peering. - -The My Peering Manager tool in the IXP Manager will compose mails with the above details for you automatically. - -Please note that INEX members are required to reply to peering requests within a reasonable time frame. If your emails to other INEX members about peering go unanswered, please let us know and we will do what we can. - -INEX requires that all new members peer and share routes with the INEX route collectors for administrative purposes. We would be obliged if you could set up your router(s) and make the necessary arrangements for this as soon as possible. - -INEX's details are: - -remote-as: AS2128 -AS Macro: AS-INEXIE - -Peering VLAN #1 - IPv6 address: 2001:7F8:18::F:0:1 - IPv6 session MD5: {$ipv6md5} - -IPv4 address: 193.242.111.126 - IPv4 session MD5: {$ipv4md5} - -Peering VLAN #2 - IPv6 address: 2001:7F8:18:12::9999 - IPv6 session MD5: {$ipv6md5} - - IPv4 address : 194.88.240.126 - IPv4 session MD5: {$ipv4md5} - - -INEX currently announces two prefixes over IPv4 and one prefix over IPv6 from AS2128: - - 2001:7F8:18::/48 - - 193.242.111.0/24 - 194.88.240.0/23 - - -NOC Details -=========== - -For the convenience of its members, INEX maintains a list of NOC and peering contact details for its members. These details are held on a private INEX database, and are available only from the IXP Manager on the following URL: - - {genUrl controller="customer" action="details"} - -This area of the INEX website is password protected and SSL secured. Passwords are only provided to current INEX members. This information is considered private and will not be passed on to other third parties by INEX. - -We would appreciate if you could take the time to ensure that the following details we hold on file are correct: - -Your Input ----------- - -Member name: {$customer->getName()} -Primary corporate web page: {$customer->getCorpwww()} -Peering Email Address: {$customer->getPeeringemail()} -NOC Phone number: {$customer->getNocphone()} -NOC Fax number: {$customer->getNocfax()} -General NOC email address: {$customer->getNocemail()} -NOC Hours: {$customer->getNochours()} -Dedicated NOC web page: {$customer->getNocwww()} -AS Number: {$customer->getAutsys()} - ----------- - - -Router Configuration -==================== - -If you are new to internet exchanges, we would ask you to note that all members are expected to adhere to the technical requirements of the INEX MoU. In particular, we would draw your attention to section 2 of these requirements which outline what types of traffic may and may not be forwarded to the INEX peering LAN. - -For Cisco IOS based routers, we recommend the following interface configuration commands: - - no ip redirects - no ip proxy-arp - no ip directed-broadcast - no mop enabled - no cdp enable - udld port disable - -If you intend to use IPv6 with a Cisco IOS based router, please also consider the following interface commands: - - no ipv6 redirects - ipv6 nd suppress-ra - - -Connecting Switches to INEX -=========================== - -Many members choose to connect their INEX port to a layer 2 switch and then forward their peering traffic to a router virtual interface hosted elsewhere on their network. While connecting layer 2 switches to the INEX peering LAN is not actively discouraged, incorrect configuration can cause serious and unexpected connectivity problems. - -The primary concern is to ensure that only traffic from the router subinterface is presented to the INEX port. INEX implements per port mac address counting: if more than 1 mac address is seen on any switch port at any time, that port will automatically be disabled for a cooling off period, and your connectivity to INEX will temporarily be lost. - -This policy prevents two potential problems: firstly, it ensures that layer 2 traffic loops are prevented and secondly, it ensures that no other traffic escapes to the INEX peering LAN which shouldn't be seen there. - -If you choose to connect your INEX port or ports to a switch, it is critically important to assign one unique vlan for each INEX connection. If you share an INEX facing VLAN between multiple INEX ports or share a INEX-facing VLAN with any other network, your connection will automatically be shut down due to the security mechanisms implemented by INEX. - -Please also note that by default, several switch models send link-local traffic to all ports. On Cisco switches, this can be disabled using the following interface commands: - -interface GigabitEthernetx/x - spanning-tree bpdufilter enable - no keepalive - no cdp enable - udld port disable - -For further details please see the following URL: - - {genUrl controller="static" action="switches"} - - -Monitoring -========== - -By default, INEX actively monitors all ports on its peering LANs using ICMP PING for both connectivity and host latency. This monitoring causes about 25 PING packets to be sent to each IP address on the peering LAN every 5 minutes. If you do not wish for your router to be actively monitored, please mail operations@inex.ie and we can disable this facility. - - -IRC -=== - -INEX member staff and other INEX member employees may regularly be seen on irc.inex.ie (port 6697, SSL only), channel #inex-ops. This channel is password protected; the password for the channel may be found on the following web page: - - {genUrl controller="static" action="misc-benefits"} - -Although this IRC server is secured with SSL, INEX does not recommend swapping passwords or any other private / confidential information on this facility. - - -AS112 Service -============= - -For the benefit of its members, INEX hosts an AS112 nameserver which answers bogus requests to private IP address space. This service is available as a regular peering host on both INEX peering LANs. Its IP addreses are: 193.242.111.6 and 194.88.240.6. Should you wish to peer directly with the AS112 server, please contact INEX operations, and we can set up a peering session on the unit. Otherwise, AS112 is also visible on the INEX route server system. - -Please see {genUrl controller="dashboard" action="as112"} for more details and further explanation. - - - -PeeringDB -========= - -PeeringDB ( http://www.peeringdb.com/ ) facilitates the exchange of information related to peering. Specifically, what networks are peering, where they are peering, and if they are likely to peer with you. - -More and more organisations are using PeeringDB to make decisions on where they should open POPs, provision new links, etc. - -We would very much appreciate it if you could mark your new INEX peering under the "Public Peering Locations" section of your PeeringDB page. We are listed as 'INEX'. If you do not yet have a PeeringDB account, we would suggest that you register for one on their site. - - -Welcome to INEX, Ireland's Internet hub. - - -INEX Operations -INEX - Internet Neutral Exchange Association - - diff --git a/application/views/_skins/inex/footer-content.phtml b/application/views/_skins/inex/footer-content.phtml deleted file mode 100644 index b3b843716..000000000 --- a/application/views/_skins/inex/footer-content.phtml +++ /dev/null @@ -1,56 +0,0 @@ - -
- -

- IXP Manager V{$smarty.const.APPLICATION_VERSION} -   |   - Copyright © 2010 - {$smarty.now|date_format:'%Y'} Internet Neutral Exchange Association Ltd. - http://www.inex.ie/ -  |  - Contact Us -

- -

- Licensed under GPL v2.0. -  |  - This Program is provided AS IS, without warranty. -  |  - {assign var="ENDTIME" value=microtime(1)} - {assign var="RUNNINGTIME" value="`$ENDTIME-$smarty.const.APPLICATION_STARTTIME`"} - Generated in {$RUNNINGTIME|string_format:"%0.3f"} seconds -

- -

- Keep up with - - INEX - -  |  - Get the source at - - GitHub - -  |  - We're - - IPv6 Ready - -  Are you? -  |  - Join the - - LinkedIn Group - - -

- - - {if $smarty.const.APPLICATION_ENV != 'production' and ( not isset( $hasIdentity ) or !$hasIdentity)} -

- - THE IXP IS RUNNING IN NON-PRODUCTION MODE AND INFORMATION CAN BE LEAKED VIA DEBUGGING - UTILITIES. ENSURE HTACCESS IS IN PLACE. - -

- {/if} - -
\ No newline at end of file diff --git a/application/views/_skins/inex/header-documentation.phtml b/application/views/_skins/inex/header-documentation.phtml deleted file mode 100644 index b52a9ea44..000000000 --- a/application/views/_skins/inex/header-documentation.phtml +++ /dev/null @@ -1,27 +0,0 @@ - - diff --git a/application/views/_skins/inex/staff-links.phtml b/application/views/_skins/inex/staff-links.phtml deleted file mode 100644 index 746131014..000000000 --- a/application/views/_skins/inex/staff-links.phtml +++ /dev/null @@ -1,20 +0,0 @@ -{* Override this file (via skinning) to add customer staff links for ADMINs *} - - - diff --git a/application/views/_skins/inex/static/as112.phtml b/application/views/_skins/inex/static/as112.phtml deleted file mode 100644 index d66c1052b..000000000 --- a/application/views/_skins/inex/static/as112.phtml +++ /dev/null @@ -1,124 +0,0 @@ -{tmplinclude file="header.phtml"} - -{if $user->getPrivs() eq 3} - -
-{else} -
- -{/if} - - -{OSS_Message} - -{if $user->getPrivs() eq 1} -
- {if $as112} -
- You are enabled to use INEX's AS112 service. Please see configuration details below. -
- {elseif $rsclient and not $as112} -
- There are no bilateral BGP sessions configured for you on the AS112 server. However, as - you have route server sessions, you receive the AS112 prefixes via this. If you would like - to additionally enable bilateral peering, please - contact INEX Operations. -
- {else} -
- You are not enabled to use INEX's AS112 service. Please - contact INEX Operations - to enable the AS112 service. -
- {/if} -
-{/if} - - -

Overview

- -

-From http://public.as112.net/: -

- -
- -

-Because most answers generated by the Internet's root name server system are negative, -and many of those negative answers are in response to PTR queries for RFC1918, dynamic -DNS updates and other ambiguous addresses, as follows: -

- -
    -
  • 10.0.0.0/8
  • -
  • 172.16.0.0/12
  • -
  • 169.254.0.0/16
  • -
  • 192.168.0.0/16
  • -
- -

-There are now separate (non-root) servers for these queries [such as INEX's AS112 service]. -

- -

-As a way to distribute the load across the Internet for RFC1918-related queries, we use -IPv4 anycast addressing. The address block is 192.175.48.0/24 and its origin AS is 112. -This address block is advertised from multiple points around the Internet, and these -distributed servers coordinate their responses and back end statistical analyses. -

-
- -

-For the benefit of its members, INEX hosts an AS112 nameserver which answers bogus -requests to private IP address space. This service is available as a regular peering -host on both INEX peering LANs. Its IP addreses are: 193.242.111.6 and -194.88.240.6. -

- -

Configuration Details

- -

-For Cisco routers, you will need something like the following example BGP configuration for peering LAN #1: -

- -
-    router bgp 99999
-
-     ! INEX Peering LAN #1
-
-     neighbor 193.242.111.6 remote-as 112
-     neighbor 193.242.111.6 description INEX AS112 Service
-     address-family ipv4
-     neighbor 193.242.111.6 password s00persekr1t
-     neighbor 193.242.111.6 activate
-     neighbor 193.242.111.6 filter-list 100 out
-
-
- -

-You should also use route-maps (or distribute-lists) to control -outgoing prefix announcements to allow only the prefixes which you indend to announce. -

- -

More Information on the AS112 Project

- -

-Please see http://public.as112.net/ for more information -on the AS112 project, The Nameservers at the End of the Universe. -

- -
- -{tmplinclude file="footer.phtml"} diff --git a/application/views/_skins/inex/static/fees.phtml b/application/views/_skins/inex/static/fees.phtml deleted file mode 100644 index b48ace5f8..000000000 --- a/application/views/_skins/inex/static/fees.phtml +++ /dev/null @@ -1,178 +0,0 @@ -{tmplinclude file="header.phtml"} - -{if $user->getPrivs() eq 3} - - -
-{else} -
- -{/if} - -

-INEX is a member supported organisation and operates on a not-for-profit basis. -Revenue for the organisation comes from two primary sources, membership fees and -port charges. -

- -

2012 Fees and Charges

- -

-

Membership Fees

- -

- -
-
- - - - - - - - - - - - - - - - - - - - -
DescriptionCharge
Associate Membership€1,000 p.a.
Full Membership€2,000 p.a.
Joining FeeNo charge
-
-
- - -

-

Port Charges

-

- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
DescriptionFirst PortSecond PortSubsequent Port    Term
10Mbit/s portWaived for 2012     n/an/aper annum
100Mbit/s port€2,500Free (on LAN2)     €1,666per annum
1Gbit/s port€6,000 (inc. free 100Mb)     €4,000€4,000per annum
10Gbit/s port€16,500€11,000€11,000per annum
-
-
- -

-

Miscellaneous Charges

-

- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
DescriptionCharge
Private Interconnection link within INEX Cage€120 p.a.
Private Interconnection link to external cross-connect€250 p.a.
Installation fee for long-haul (ZX) gigabit optical ethernet link€1,500 once off
Installation fee for long-haul (ZX) ten gigabit optical ethernet link€4,500 once off
Rack space in INEX colocation cages above 3U€250 p.a. per U
Private VLAN between two members€240 p.a. per party
-
-
- -

-

Notes

-

-
    -
  • Free second 100Mbps port only applicable to connections to the secondary peering LAN.
  • -
  • Private Interconnections may only be installed between INEX members. Each party is liable to the charge noted in the table above.
  • -
  • Port and private interconnect charges are not applicable to Associate Members, as associate members have no connectivity entitlements.
  • -
  • There is no port connection charge, except for ZX gigabit and ten gigabit links
  • -
  • VAT is charged on invoices to Irish based companies at the prevailing rate at the time of invoice (from January 2012: 23%). -
- - - -
- - - - -{tmplinclude file="footer.phtml"} diff --git a/application/views/_skins/inex/static/housing.phtml b/application/views/_skins/inex/static/housing.phtml deleted file mode 100644 index e06b47aa4..000000000 --- a/application/views/_skins/inex/static/housing.phtml +++ /dev/null @@ -1,125 +0,0 @@ -{tmplinclude file="header.phtml"} - -{if $user->getPrivs() eq 3} - - -
-{else} -
- -{/if} - -

-For members who do not have co-location space in any of INEX's locations and who use -traditional leased circuits for remote connectivity, INEX can host routers / switches -within INEX cages. We provide a number of facilities for members who avail of this option. -

- -

Inbound Connectivity

- -

-INEX supports inbound leased circuits which can use either UTP or single mode fibre. -This means that the following circuit types are supported: -

- -
    -
  • PSTN – regular phone lines for remote console connectivity
  • -
  • E1 – 2Mb circuits
  • -
  • STM-1 – 155Mb/s
  • -
  • STM-4 – 622Mb/s
  • -
  • STM-16 – 2.5Gb/s
  • -
  • Metro Ethernet over UTP – 10Mb/s or 100Mb/s carrier
  • -
  • Metro Ethernet over fibre – 1Gb/s or 10Gb/s carrier
  • -
- -

-In practice, we do not encourage E1 connections, as there is no logical upgrade path and -they require extra termination equipment. INEX also does not support connections which -require co-axial cabling (i.e. E3). We would encourage members who require connectivity -slower than STM-1 to use metro ethernet connectivity. Fibre cables at INEX cages are -terminated as SC/PC. We support single mode and 50μ multi mode connections. -

- -

Console Connectivity

- -

-INEX uses MRV console servers for its own management purposes and if required can -provide password controlled secure access to members' console ports using this equipment. -Should you require this service, please contact operations@inex.ie. -

- - -

Access to Equipment

- -

-INEX members who host equipment in INEX cages may nominate up to 5 individuals who will be -given emergency grade 24x7x365 access to INEX facilities without prior notification to INEX -staff. INEX operations can also authorise access to INEX cage facilities on a next-day basis -for non-emergency works. Should you require routine access to an INEX cage, please email -operations@inex.ie. Please note that all INEX -co-location providers require photo identity. Member employees who fail to produce photo ID -will be denied access to the co-location floor. -

- -

Space Allocation

- -

-Members who house equipment in INEX cages are allocated 3U by default. More space can be -allocated on request, although this may require moving the equipment to a different rack -location or splitting the equipment between two locations. Please see the INEX pricing -information page for details on pricing for more than 3U. -

- -

Power Supply

- -

-INEX provides A+B power at 220VAC, 50Hz, with 13A 3-prong UK+Ireland style sockets. -

- - -

Private Interconnections

- - -

-Members who house equipment in INEX cages are entitled to order and use cross-connects -to other INEX members, both inside and outside INEX cages. Please see the INEX pricing -information page for further details. -

- -

Limitations

- -

-INEX provides router/switch housing facilities to its members for the purposes of -connecting to the INEX peering LAN. We are unable to permit members to use INEX -housing for: -

- -
    -
  • - Connecting their customers (unless providing transit over the INEX peering LAN, or - unless using the INEX private interconnection service to another INEX member) -
  • -
  • - Connecting to upstream service providers, where these providers are not members of INEX -
  • -
  • - Hosting general servers (although specific exclusions apply for root, TLD and ENUM tier 1 - DNS servers). -
  • -
- -
- -{tmplinclude file="footer.phtml"} diff --git a/application/views/_skins/inex/static/misc-benefits.phtml b/application/views/_skins/inex/static/misc-benefits.phtml deleted file mode 100644 index 6f6744a5b..000000000 --- a/application/views/_skins/inex/static/misc-benefits.phtml +++ /dev/null @@ -1,172 +0,0 @@ -{tmplinclude file="header.phtml"} - -{if $user->getPrivs() eq 3} - - -
-{else} -
- -{/if} - -

IRC Channel

- - -

-Members of the INEX operations team, along with many NOC personnel from INEX member organisations -can often be found on the #inex-ops IRC channel. This facility has proved itself to be a useful -communications medium for operational matters. The #inex-ops IRC channel is password protected -and access to the server is encrypted using SSL. -

- - - - - - - - - - - - - - - - - - - - -
Server name:irc.inex.ie, port 6697, SSL enabled
Channel:#inex-ops
Password:{$options.identity.misc.irc_password}
- - -

Route Collector

- -

-INEX runs a route collector, whose purpose is to allow exchange members to debug routing issues. -While there is public access to the route collector available on the INEX looking glass, INEX -members have CLI access to the route-collector. -

- - - - - - - - - - - - - - - - - - - - - - - - - - -
Server name:route-collector.inex.ie
Access protocol:SSH1
Username:Your IXP Manager username
Password:Your IXP Manager password
- -

-Please note that all commands issued to the INEX route collector are temporarily logged -for security purposes. These logs are not archived into long term storage, but are deleted -after a week or two. -

- - -

Console Server

- -

-INEX can provide remote console access services for those members who house equipment -within INEX's facilities. These console servers provide access to housed equipment -using RS-232 serial ports. Access to the servers are provided on a per-port, per-user, -password-authenticated basis, thereby providing secure emergency management access. -

- -

-Access to the servers is available using the following parameters: -

- - - - - - - - - - - - - - - - - - - - - - - - - - - -
Server name: - conserver-tcy1.inex.ie (Telecity)
- conserver-deg1.inex.ie (DEG Kilcarbery)
- conserver-ixdub1-1.inex.ie (Interxion DUB1)
- conserver-ixdub2-1.inex.ie (Interxion DUB2) -
Access protocol:SSH2
Username:Your IXP Manager username
Password:Your IXP Manager password
- -

-Only INEX members with equipment hosted at INEX have access to these servers. -

- -

-When connected to a console server, the commands are: -

- -

-Menu mode: -

- -
    -
  • <arrow-up> and <arrow-down> to select which device you want to connect to;
  • -
  • <cr> to connect to your switch;
  • -
  • Q (i.e. upper case q) to disconnect from the console server
  • -
- -

-When connected to the management port on the hosted device: -

- -
    -
  • <ctrl-y>, followed by e to go back to menu mode
  • -
- -
- -{tmplinclude file="footer.phtml"} diff --git a/application/views/_skins/inex/static/port-security.phtml b/application/views/_skins/inex/static/port-security.phtml deleted file mode 100644 index fbac16936..000000000 --- a/application/views/_skins/inex/static/port-security.phtml +++ /dev/null @@ -1,143 +0,0 @@ -{tmplinclude file="header.phtml"} - -{if $user->getPrivs() eq 3} - - -
-{else} -
- -{/if} - - - -

For the purposes of ensuring layer 2 stability, INEX implements three -port security policies.

- -
    - -
  • - Broadcast Traffic Storm Control — INEX restricts - broadcast traffic received on any particular port to be no more than - 0.33% of the total traffic received on that port.
    -
    - While it is normal to see a small amount of Layer 2 broadcast traffic - for certain types of traffic (ARP), large amounts of broadcast traffic - typically indicate a problem with the connecting router or switch, - caused by incorrect configuration, failed hardware or - hardware/microcode bugs. Because broadcast traffic frames are forwarded - to all ports on a flat layer 2 LAN, this sort of traffic could - potentially disrupt service for other connections into the INEX switch - fabric. Beyond the limit of 0.33%, inbound broadcast traffic is simply - dropped, and will not be forwarded to the relevant INEX port. -
    -
    -
  • - -
  • - Multicast Traffic Storm Control — on ports which are - not enabled for multicast traffic, INEX throttles multicast traffic - received on any particular port to be no more than 0.33% of the total - traffic received on that port.
    -
    - It is also normal to see small amounts of inbound multicast - traffic on ports (e.g. IPv6 neighbour discovery), where the port has - not been enabled for regular multicast traffic. However, as with - broadcast traffic, excessive amounts of multicast traffic on a - non-multicast enabled port are indicative of configuration problems. - For this reason, the INEX switches are configured to drop multicast - frames which exceed the 0.33% rate limit. -
    -
    -
  • - -
  • - One MAC Address per Port — INEX expects that all traffic - coming in from a particular port will all - be configured with the same source MAC address, which INEX switches - will then dynamically associate with that port.
    -
    - If frames are seen on a - port with a source MAC address which differs from the dynamically - learned address, then the port will either shut the port down - automatically or else drop frames with the unknown MAC address. If the - port is shut down, it will automatically be re-enabled after 300 - seconds (5 minutes). INEX ports will relearn a new dynamic MAC - addresses after 5 minutes of inactivity, allowing scheduled maintenance - with relatively little interruption.
    -
    - INEX provides access to a range of - flat layer 2 networks, over which providers may run IP traffic. Because - of this, there is no reason to allow more than a single MAC address per - configured port. When multiple MAC addresses are seen on a particular - port, it generally means one of the following things: - -
      -
    • the connected router or switch has been misconfigured to forward link-local frames to - the INEX peering -
    • -
    • LAN member has accidentally set up a traffic loop - between two INEX switch ports -
    • -
    • a metro ethernet provider has - accidentally leaked bogus frames into a member's connection link -
    • -
    • the member is using faulty hardware -
    • -
    - - Because several of these possibilities - could cause catastrophic layer 2 network instability affecting all INEX - members on a particular peering LAN, and because all of them can be - obviated by using a one MAC-address per port policy, INEX aggressively - implements and polices this policy. - -
  • -
- - -

Multicast PIM / IGMP snooping

- -

-In order not to flood 3rd party ports with unnecessary multicast traffic, -INEX implements both PIM and IGMP snooping. This procedure ensures that -only ports which issue PIM or IGMP join or leave messages will actually -receive specific multicast traffic flows. This policy increases both -port security and link utilisation efficiency. -

- -

Broadcast Traffic Monitoring

- -

-For the purposes of ensuring that no unnecessary broadcast -traffic is forwarded to an INEX peering LAN, INEX monitors all peering -LANs for broadcast traffic, and archives this data. -

- -

-This data consists purely of traffic which is broadcast to all INEX peering LAN ports. -Whenever unauthorised traffic is detected, the INEX operations team is -notified, who will normally follow the issue up with the source of the -traffic. -

- -

-Traffic monitoring is implemented using ixpwatch, a program -designed and coded by the London Internet Exchange (LINX). -

- -
- -{tmplinclude file="footer.phtml"}# diff --git a/application/views/_skins/inex/static/route-servers.phtml b/application/views/_skins/inex/static/route-servers.phtml deleted file mode 100644 index 7affd4472..000000000 --- a/application/views/_skins/inex/static/route-servers.phtml +++ /dev/null @@ -1,275 +0,0 @@ -{tmplinclude file="header.phtml"} - -{if $user->getPrivs() eq 3} - -
-{else} -
- -{/if} - -{OSS_Message} - -{if $user->getPrivs() eq 1} -
- {if not $rsclient} -
- You are not using INEX's robust route server cluster. - Please contact INEX Operations - to have this service enabled. -
- {else} -
- You are enabled to use INEX's robust route server cluster. -
- {/if} -
-{/if} - - -

Overview

- -

-Normally on a peering exchange, all connected parties will establish bilateral peering relationships -with each other member port connected to the exchange. As the number of connected parties increases, -it becomes increasingly more difficult to manage peering relationships with members of the exchange. -A typical peering exchange full-mesh eBGP configuration might look something similar to the diagram -on the left hand side. -

- - - - - - - - - - - - -
- [ IXP full mesh peering relationships ] - - [  IXP route server peering relationships ] -
- IXP full mesh peering relationships - - IXP route server peering relationships -
- -

-
-The full-mesh BGP session relationship scenario requires that each BGP speaker configure and manage -BGP sessions to every other BGP speaker on the exchange. In this example, a full-mesh setup requires -7 BGP sessions per member router, and this increases every time a new member connects to the exchange. -

- -

-However, by using a route server for all peering relationships, the number of BGP sessions per router -stays at two: one for each route server. Clearly this is a more sustainable way of maintaining IXP -peering relationships with a large number of participants. -

- - -

Should I use this service?

- -

-The INEX route server cluster is aimed at: -

- -
    -
  • small to medium sized members of the exchange who don't have the time or resources to - aggressively manage their peering relationships -
  • -
  • larger members of the exchange who have an open peering policy, but where it may not - be worth their while managing peering relationships with smaller members of the exchange. -
  • -
- -

-As a rule of thumb: If you don't have any good reasons not to use the route server cluster, you should probably use it. -

- -

-The service is designed to be reliable. It operates on two physical servers, each located in a -different data centre. The service is available on all INEX networks (public peering lans #1 and #2, -and voip peering lans #1 and #2), on both ipv4 and ipv6. Each server runs a separate routing daemon -per vlan and per L3 protocol. Should a single BGP server die for some unlikely reason, no other BGP -server is likely to be affected. If one of the physical servers becomes unavailable, the second server -will continue to provide BGP connectivity. -

- -

-INEX has also implemented inbound prefix filtering on its route-server cluster. This uses internet -routing registry data from the RIPE IRR database to allow connected members announce only the address -prefixes which they have registered publicly. -

- -

-INEX uses Quagga running on FreeBSD for its route server cluster. Quagga is widely used at Internet -exchanges for route server clusters (e.g. LINX, AMS-IX, DE-CIX), and has been found to be reliable -in production. -

- - -

How do I use the service?

- -

-If enabled, the route servers are set up to accept BGP connections from your router. Once this has -been done, you will need to configure a BGP peering session to the correct internet address. The -IP addresses of the route servers are listed as follows: -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Peering LANRoute Server #1Route Server #2
IPv4 AddressIPv6 AddressIPv4 AddressIPv6 Address
Public Peering LAN #1193.242.111.82001:7f8:18::8193.242.111.92001:7f8:18::9
Public Peering LAN #2194.88.240.82001:7f8:18:12::8194.88.240.92001:7f8:18:12::9
VoIP Peering LAN #1194.88.241.82001:7f8:18:70::8194.88.241.92001:7f8:18:70::9
VoIP Peering LAN #2194.88.241.722001:7f8:18:72::8194.88.241.732001:7f8:18:72::9
- - - -

-

-For Cisco routers, you will need something like the following bgp configuration: -

- -
-    router bgp 99999
-     no bgp enforce-first-as
-
-     ! Route server #1
-
-     neighbor 193.242.111.8 remote-as 43760
-     neighbor 193.242.111.8 description INEX Route Server
-     address-family ipv4
-     neighbor 193.242.111.8 password s00persekr1t
-     neighbor 193.242.111.8 activate
-     neighbor 193.242.111.8 filter-list 100 out
-
-     ! Route server #2
-
-     neighbor 193.242.111.9 remote-as 43760
-     neighbor 193.242.111.9 description INEX Route Server
-     address-family ipv4
-     neighbor 193.242.111.9 password s00persekr1t
-     neighbor 193.242.111.9 activate
-     neighbor 193.242.111.9 filter-list 100 out
-
- -

-You should also use route-maps (or distribute-lists) to control -outgoing prefix announcements to allow only the prefixes which you indend to announce. -

- -

-Note that the route server system depends on information in the RIPE IRR database. If you -have not published correct route: and route6: objects in this database, -your prefix announcements will be ignored by the route server and your peers will not route their -traffic to you via the exchange. -

- -

Community based prefix filtering

- -

-The INEX route server system also provides well known communities to allow members to -control the distribution of their prefixes. These communities are defined as follows: -

- - - - - - - - - - - - - - - - - - - - - - - - - - - -
DescriptionCommunity
Prevent announcement of a prefix to a peer0:peer-as
Announce a route to a certain peer43760:peer-as
Prevent announcement of a prefix to all peers0:43760
Announce a route to all peers43760:43760
- - -

-

-So, for example, to instruct the route server to distribute a particular prefix only to -AS64111 and AS64222, the prefix should be tagged with communities: 0:43760, 43760:64111 -and 43760:64222. -

- -

-Alternatively, to announce a prefix to all INEX members, excluding AS64333, the prefix -should be tagged with community 0:64333. -

- -
- -{tmplinclude file="footer.phtml"} diff --git a/application/views/_skins/inex/static/support.phtml b/application/views/_skins/inex/static/support.phtml deleted file mode 100644 index c15d402e7..000000000 --- a/application/views/_skins/inex/static/support.phtml +++ /dev/null @@ -1,99 +0,0 @@ -{tmplinclude file="header.phtml"} - -{if isset( $user ) and $user->getPrivs() eq 3} - -{else} -
- -{/if} - -
-

- Techical Support: {mailto address="operations@inex.ie" encode="javascript"} -    |    - Billing / Accounts: {mailto address="accounts@inex.ie" encode="javascript"} -    |    - Sales / Marketing: {mailto address="sales@inex.ie" encode="javascript"} -

-
- -

-Regular technical support at INEX is provided on an office hours basis from 08:00 to 18:00 GMT, -Monday through Friday. The normal communications channel for technical support is email to -{mailto address="operations@inex.ie" encode="javascript"}. INEX aims for 4 hour turnaround on all -email support requests. INEX operations staff are also available by telephone on +353 1 6169698 -and +353 1 685 4220. -

-

- -

Emergency 24x7x365 Support

- -

-An 24-hour support hotline is available on +353 86 822 9854 or +353 86 801 7669 for emergency -calls which fall outside normal office hours. This support facility is intended for emergencies -only, including: -

- -
    -
  • INEX critical system failures causing loss of service to members
  • -
  • Emergency out-of-hours access to INEX cages for members who house routers there
  • -
- -

-If there is no immediate answer from this phone, please leave a message and it will be -attended to immediately. -

- -

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

Technical Support Summary

Email:{mailto address="operations@inex.ie" encode="javascript"}
Phone:+353 1 616 9698 or +353 1 685 4220
Hours:09:00 to 18:00 GMT, Monday to Friday
24h Emergency:+353 86 822 9854 and/or +353 86 801 7669
-
- -{if not isset( $user ) or $user->getPrivs() neq 3} -
-{/if} - -{tmplinclude file="footer.phtml"} diff --git a/application/views/_skins/inex/static/switches.phtml b/application/views/_skins/inex/static/switches.phtml deleted file mode 100644 index 9c237ac64..000000000 --- a/application/views/_skins/inex/static/switches.phtml +++ /dev/null @@ -1,138 +0,0 @@ -{tmplinclude file="header.phtml"} - -{if $user->getPrivs() eq 3} - - -
-{else} -
- -{/if} - - -

-Many members choose to connect their INEX port to a layer 2 switch and then forward their peering -traffic to a router virtual interface hosted elsewhere on their network. While connecting layer -2 switches to the INEX peering LAN is not actively discouraged, incorrect configuration can cause -serious and unexpected connectivity problems. -

- -

-The primary concern is to ensure that only traffic from the router subinterface is presented to -the INEX port. INEX implements per port mac address counting: if more than 1 mac address is seen -on any switch port at any time, that port will automatically be disabled for a cooling off period, -and your connectivity to INEX will temporarily be lost. -

- -

-This policy prevents two potential problems: firstly, it ensures that layer 2 traffic loops are -prevented and secondly, it ensures that no other traffic escapes to the INEX peering LAN which -shouldn't be seen there. -

- -

-If you choose to connect your INEX port or ports to a switch, it is critically important to assign -one unique vlan for each INEX connection. If you share an INEX facing VLAN between multiple INEX -ports or share a INEX-facing VLAN with any other network, your connection may automatically be shut -down due to the security mechanisms implemented by INEX. -

- -

-It is also important to disable all switch-generated link-local traffic on your switch port. -Typical link-local traffic will include spanning tree BPDUs, keepalive packets and discovery -protocols. This traffic is particular problematic because these packets are typically forwarded -on a port when the link is first brought up. -

- -

-If multiple mac addresses are seen on any particular port, one of two things will happen. -Either the switch port will shut down for a cooling-down period of 5 minutes, or else the equiment -on the client-side of the switch port will have very poor quality connectivity, where arbitrary -packets will appear to be dropped without any apparent reason. -

- -

Recommended Cisco Configuration

- -

-By default, all Cisco switches will broadcast CDP, Spanning Tree Protocol and keepalive packets -on all ports. In addition, higher speed switches can default to using UDLD (unidirectional link -detection). On IOS-enabled switches, these packets can be disabled using the following commands: -

- -

-

-  interface GigabitEthernetx/x
-    spanning-tree bpdufilter enable
-    no keepalive
-    no cdp enable
-    udld port disable
-    no lldp transmit
-
-

- -

-Some older Cisco switches do not support the "spanning-tree bpdufilter enable" command. On these -units, it may be necessary to specify the following command: -

- -

-

-spanning-tree bpduguard enable
-
-

- -

Recommended Extreme Configuration

- -

-By default, Extreme switches will broadcast EDP on all ports. These packets can be disabled -using the following commands: -

- -

-

-  disable edp ports <portname>
-
-

- -

-If Spanning Tree Protocol is enabled on a particular port, it can be disabled using: -

- -

-

-  disable stpd <stpd_name> ports <portname>
-
-

- -

Recommended Brocade / Foundry Configuration

- -

-On trunk ports, Foundry switches will broadcast FDP (Foundry Discovery protocol) and by -default on all ports, Foundry switches will broadcast Spanning Tree BPDUs. These packets -can be disabled on a per-interface basis using the following command: -

- -

-

-  no link-keepalive ethernet x/y
-  interface ethernet x/y
-    no fdp enable
-    no spanning-tree
-
-

- -
- -{tmplinclude file="footer.phtml"} diff --git a/application/views/_skins/inex/user/email/html/welcome.phtml b/application/views/_skins/inex/user/email/html/welcome.phtml deleted file mode 100644 index 64405a143..000000000 --- a/application/views/_skins/inex/user/email/html/welcome.phtml +++ /dev/null @@ -1,140 +0,0 @@ - - - Welcome to {$options.identity.sitename} - - -

{$options.identity.sitename} - Your Access Details

- -

-To whom it may concern, -

- -{if isset( $resend ) and $resend} -

- This email is being sent to you because either you requested a reminder of - your account details or an administrator thought it appropriate to send you a - reminder. -

-{else} -

- A new user account has been created for you on the {$options.identity.sitename}. -

-{/if} - -

-You can login to it using the following details: -

- - - - - - - - - - - - - - - - - - - - -
URL:{$options.identity.url}
Username:{$newuser->getUsername()}
Password:(see below)
- -

-Once logged in, you will have access to a number of features including: -

- -
    -
  • list of IXP members and peering contact details;
  • -
  • the peering manager tool;
  • -
  • your port and member to member traffic graphs;
  • -
  • ability to view and edit your company details;
  • -
  • your port configuration details;
  • -
  • the peering matrix;
  • -
  • route server, AS112 and other service information.
  • -
- -

-If you require any assistance, please contact {$options.identity.name} on -{$options.identity.email}. -

- - -

Getting Your Password

- -

-To get your new password (or reset it), please use the lost password procedure by visiting -the following link and entering your username as above: -

- -
- {genUrl controller="auth" action="lost-password"} -
- - -

Additional/Miscellaneous Benefits

- -

Route Collector

- -

-INEX runs a route collector, whose purpose is to allow exchange members to debug routing issues. -While there is public access to the route collector available on the INEX looking glass, INEX -members have CLI access to the route-collector. -

- -

-It can be accessed via SSH to route-collector.inex.ie using your new username and password. -

- - -

IRC Channel

- -

-Members of the INEX operations team, along with many NOC personnel from INEX member -organisations can often be found on the #inex-ops IRC channel. This facility -has proved itself to be a useful communications medium for operational matters. The -#inex-ops IRC channel is password protected and access to the server is -encrypted using SSL. -

- - - - - - - - - - - - - - - - - - - - -
Server name:irc.inex.ie, port 6697, SSL enabled
Channel:#inex-ops
Password:{$options.identity.misc.irc_password}
- - - -

-Thanks and kind regards, -

- -

-{$options.identity.name}
-{$options.identity.email} -

- - - - diff --git a/application/views/admin/index.phtml b/application/views/admin/index.phtml deleted file mode 100644 index 907f6e148..000000000 --- a/application/views/admin/index.phtml +++ /dev/null @@ -1,174 +0,0 @@ -{tmplinclude file="header.phtml" pageTitle="IXP Manager :: Administrator's Home"} - - - -{OSS_Message} - -
- -
- -
- - - - - - - - - {foreach from=$ctypes key=t item=c} - - - - - - - {/foreach} - - -
Customer TypeCount
{CUSTOMER::$CUST_TYPES_TEXT.$t}{$c}
-
- -
-

Customer Ports by Location

- -
- - - - - - {foreach from=$speeds key=k item=i} - - {/foreach} - - - - - - {assign var=colcount value=0} - {foreach from=$bylocation key=n item=spds} - {assign var=rowcount value=0} - - - - {foreach from=$speeds key=k item=i} - - {/foreach} - - - {assign var=colcount value=$rowcount+$colcount} - - {/foreach} - - - - {foreach from=$speeds key=k item=i} - - {/foreach} - - - - -
Location{$k}Total
{$n} - {if isset( $spds.$k )} - {$spds.$k} - {assign var=rowcount value=$rowcount+$spds.$k} - {else} - 0 - {/if} - {$rowcount}
Totals - {$i} - {$colcount}
-
- -
-

Customer Ports by Infrastructure

- -
- - - - - - {foreach from=$speeds key=k item=i} - - {/foreach} - - - - - - {assign var=colcount value=0} - {foreach from=$bylan key=n item=spds} - - {assign var=rowcount value=0} - - - - {foreach from=$speeds key=k item=i} - - {/foreach} - - - {assign var=colcount value=$rowcount+$colcount} - {/foreach} - - - - {foreach from=$speeds key=k item=i} - - {/foreach} - - - - - -
Infrastructure{$k}Total
LAN #{$n} - {if isset( $spds.$k )} - {$spds.$k} - {assign var=rowcount value=$rowcount+$spds.$k} - {else} - 0 - {/if} - {$rowcount}
Totals - {$i} - {$colcount}
-
- -
- - - -
- - {foreach from=$graphs key=k item=graph} -
-
-

{$graphs.$k}

- -

- {genMrtgGraphBox - shortname='X_Peering' - period='month' - category='bits' - values=$stats.$k - graph=$k - } -

-
-
- {/foreach} -
-
- - - - - -{tmplinclude file="footer.phtml"} diff --git a/application/views/auth/drupal-login.phtml b/application/views/auth/drupal-login.phtml deleted file mode 100644 index 34bddf231..000000000 --- a/application/views/auth/drupal-login.phtml +++ /dev/null @@ -1,41 +0,0 @@ -{tmplinclude file="header.phtml" pageTitle="IXP Manager :: Drupal Login"} - -{OSS_Message} - -
- -
- -

Drupal Login

- -

Use this form to login to the INEX's Drupal content management system.

- -

For security reasons, we require you to re-enter your password.

- -
- -
- -
- -
-
- -
- -
- -
-
- -
- - -
- -
-
-
- - -{tmplinclude file="footer.phtml"} diff --git a/application/views/auth/email/html/lost-password.phtml b/application/views/auth/email/html/lost-password.phtml deleted file mode 100644 index 0708d4c63..000000000 --- a/application/views/auth/email/html/lost-password.phtml +++ /dev/null @@ -1,40 +0,0 @@ - - - {$options.identity.sitename} - Password Reset Information - - -

{$options.identity.sitename} - Password Reset Information

- -

-To whom it may concern, -

-

-You, or someone purporting to be you, has requested a password reset -for {$options.identity.sitename}. -

- -

-If you wish to proceed, please click on the following link: -

- -

-getUsername() token=$token}"> - {genUrl controller="auth" action="reset-password" username=$user->getUsername() token=$token} - -

- -

-If you did not make this request, please ignore this mail. -

- -

-Thanks and kind regards, -

- -

-{$options.identity.name}
-{$options.identity.email} -

- - - diff --git a/application/views/auth/email/html/lost-username.phtml b/application/views/auth/email/html/lost-username.phtml deleted file mode 100644 index 2a1e874b0..000000000 --- a/application/views/auth/email/html/lost-username.phtml +++ /dev/null @@ -1,40 +0,0 @@ - - - {$options.identity.sitename} - Your Accounts - - -

{$options.identity.sitename} - Your Accounts

- -

-To whom it may concern, -

-

-You, or someone purporting to be you, has requested a username -reminder for your email address for {$options.identity.sitename}. -

- -

-The usernames linked to your account are: -

- -
    - {foreach $users as $u} -
  • {$u->getUsername()}     (for {$u->getCustomer()->getName()})
  • - {/foreach} -
- -

-If you did not make this request, please ignore this mail. -

- -

-Thanks and kind regards, -

- -

-{$options.identity.name}
-{$options.identity.email} -

- - - diff --git a/application/views/auth/email/html/reset-password.phtml b/application/views/auth/email/html/reset-password.phtml deleted file mode 100644 index 2a893dd70..000000000 --- a/application/views/auth/email/html/reset-password.phtml +++ /dev/null @@ -1,32 +0,0 @@ - - - {$options.identity.sitename} - Your Password Has Been Reset - - -

{$options.identity.sitename} - Your Password Has Been Reset

- -

-To whom it may concern, -

- -

-Your password for {$options.identity.sitename} has been reset by the user -initiated password reset procedure. -

- -

-If you did not make this request, please contact us immediately. -

- -

-Thanks and kind regards, -

- - -

-{$options.identity.name}
-{$options.identity.email} -

- - - diff --git a/application/views/auth/login.phtml b/application/views/auth/login.phtml deleted file mode 100644 index 60b399e3e..000000000 --- a/application/views/auth/login.phtml +++ /dev/null @@ -1,42 +0,0 @@ -{tmplinclude file="header.phtml"} - -
- - - -{OSS_Message} - -
- - {if !isset( $options.identity.biglogo )} -
-

[Your Logo Here]

-

- Configure identity.ixp.biglogo in application.ini. -



-

- {else} -
-

- -

- {/if} -
- -
- -
-
- {$form} -
-
- -

- For help, please contact {mailto address=$options.identity.email encode='javascript' text=$options.identity.name}. -

- -
- -{tmplinclude file="footer.phtml"} diff --git a/application/views/auth/lost-password.phtml b/application/views/auth/lost-password.phtml deleted file mode 100644 index 1fb1b9595..000000000 --- a/application/views/auth/lost-password.phtml +++ /dev/null @@ -1,37 +0,0 @@ -{tmplinclude file="header.phtml"} - -
- - - -{OSS_Message} - -
- -
- -
- -
- -
- -
- -

- Please enter your username and we will send you a password reset token by email. -

- - {$form} -
-
- -

- For help please contact {mailto address=$options.identity.email encode='javascript' text=$options.identity.name}. -

- -
- -{tmplinclude file="footer.phtml"} diff --git a/application/views/auth/lost-username.phtml b/application/views/auth/lost-username.phtml deleted file mode 100644 index 600b4cadb..000000000 --- a/application/views/auth/lost-username.phtml +++ /dev/null @@ -1,40 +0,0 @@ -{tmplinclude file="header.phtml"} - -
- - - - -{OSS_Message} - - -
- -
- -
- -
- -
- -
- -

- Please enter your email address and we will send you any related username(s) by email. -

- - {$form} - -
-
- -

- For help please contact {mailto address=$options.identity.email encode='javascript' text=$options.identity.name}. -

- -
- -{tmplinclude file="footer.phtml"} diff --git a/application/views/auth/reset-password.phtml b/application/views/auth/reset-password.phtml deleted file mode 100644 index 6521c2c37..000000000 --- a/application/views/auth/reset-password.phtml +++ /dev/null @@ -1,38 +0,0 @@ -{tmplinclude file="header.phtml"} - -
- - - -{OSS_Message} - -
- -
- -
- -
- -
- -
- -

- Please enter your username, the token that was emailed to you and a new password: -

- - {$form} - -
-
- -

- For help please contact {mailto address=$options.identity.email encode='javascript' text=$options.identity.name}. -

- -
- -{tmplinclude file="footer.phtml"} diff --git a/application/views/cli/mailing-list-sync-script.sh b/application/views/cli/mailing-list-sync-script.sh deleted file mode 100644 index 2b7933087..000000000 --- a/application/views/cli/mailing-list-sync-script.sh +++ /dev/null @@ -1,34 +0,0 @@ -#! /bin/sh - -# -# Script for syncronising subscriptions between mailing lists and IXP Manager. -# -# Does not affect any subscriptions with email addresses that do not match a user -# in IXP Manager. -# -# Generated: {$date} -# - -{foreach $options.mailinglists as $name => $ml} - -####################################################################################################################################### -## -## {$name} - {$ml.name} -## - -# Set default subsciption settings for any new IXP Manager users -{$options.mailinglist.cmd.list_members} {$name} | {$apppath}/../bin/ixptool.php -a cli.mailing-list-init --p1={$name} - -# Add new subscriptions to the list -{$apppath}/../bin/ixptool.php -a cli.mailing-list-subscribed --p1={$name} | {$options.mailinglist.cmd.add_members} {$name} >/dev/null - -# Remove subscriptions from the list -{$apppath}/../bin/ixptool.php -a cli.mailing-list-unsubscribed --p1={$name} | {$options.mailinglist.cmd.remove_members} {$name} >/dev/null - -# Sync passwords -{$apppath}/../bin/ixptool.php -a cli.mailing-list-password-sync --p1={$name} >/dev/null - - -{/foreach} - - diff --git a/application/views/cli/nagios/switch-definitions.phtml b/application/views/cli/nagios/switch-definitions.phtml deleted file mode 100644 index 8ef5911be..000000000 --- a/application/views/cli/nagios/switch-definitions.phtml +++ /dev/null @@ -1,77 +0,0 @@ -# -# This file contains static definitions for use with the IXP Manager -# Nagios configuration templates and is following by dynamic host -# configurations. -# -# To edit the static definitions, edit: -# applicationviews/cli/nagios/switch-definitions.tpl -# rather than this file directly as it is automatically generated. -# - -define host{ldelim} - name inex-production-switch - notifications_enabled 1 ; Host notifications are enabled - event_handler_enabled 0 ; Host event handler is enabled - flap_detection_enabled 1 ; Flap detection is enabled - process_perf_data 1 ; Process performance data - retain_status_information 1 ; Retain status information across program restarts - retain_nonstatus_information 1 ; Retain non-status information across program restarts - - checks_enabled 1 - check_command check-host-alive - max_check_attempts 3 ; number of not 'UP' checks to register as hard - check_interval 5 ; time between checks - retry_interval 1 ; time between checks if host is not 'UP' - - check_period 24x7 - - notification_interval 60 - notification_period 24x7 - notification_options u,d,r - - low_flap_threshold 0 - high_flap_threshold 0 - - register 0 ; DONT REGISTER THIS DEFINITION - ITS NOT A REAL HOST, JUST A TEMPLATE! - - contact_groups inex-operations -{rdelim} - - -define service{ldelim} - name inex-production-switch-service ; The 'name' of this service template, referenced in other service definitions - active_checks_enabled 1 ; Active service checks are enabled - passive_checks_enabled 1 ; Passive service checks are enabled/accepted - parallelize_check 1 ; Active service checks should be parallelized (disabling this can lead to major performance problems) - obsess_over_service 1 ; We should obsess over this service (if necessary) - check_freshness 0 ; Default is to NOT check service 'freshness' - notifications_enabled 1 ; Service notifications are enabled - event_handler_enabled 0 ; Service event handler is enabled - flap_detection_enabled 1 ; Flap detection is enabled - process_perf_data 1 ; Process performance data - retain_status_information 1 ; Retain status information across program restarts - retain_nonstatus_information 1 ; Retain non-status information across program restarts - contact_groups inex-operations - - max_check_attempts 3 - normal_check_interval 5 - retry_check_interval 1 - check_period 24x7 - - notification_interval 60 - notification_period 24x7 - notification_options w,u,c,r - - low_flap_threshold 0 - high_flap_threshold 0 - - is_volatile 0 - - register 0 ; DONT REGISTER THIS DEFINITION - ITS NOT A REAL SERVICE, JUST A TEMPLATE! -{rdelim} - - -# -# Dynamically generated configurations follow: -# - diff --git a/application/views/cli/nagios/switch-hosts.phtml b/application/views/cli/nagios/switch-hosts.phtml deleted file mode 100644 index 4b45a777b..000000000 --- a/application/views/cli/nagios/switch-hosts.phtml +++ /dev/null @@ -1,12 +0,0 @@ -# -# {$sw->getName()} - {$sw->getCabinet()->getCololocation()}, {$sw->getCabinet()->getLocation()->getName()} -# - -define host { - use inex-production-switch - host_name {$sw->getName()} - alias {$sw->getName()} - address {$sw->getIpv4addr()} -} - - diff --git a/application/views/cli/nagios/switch-templates.phtml b/application/views/cli/nagios/switch-templates.phtml deleted file mode 100644 index 15d83763e..000000000 --- a/application/views/cli/nagios/switch-templates.phtml +++ /dev/null @@ -1,76 +0,0 @@ - -{foreach $locations as $name => $switches} - -define hostgroup { - hostgroup_name INEX-Switches-{$name} - alias INEX Switches at {$name} - members {$switches} - -} - -{/foreach} - - -define hostgroup { - hostgroup_name INEX-Production-Switches - alias INEX Production Switches (all) - members {$all} - -} - - -define hostgroup { - hostgroup_name INEX-Switches-Brocade - alias INEX Brocade Switches - members {$vendor_brocade} - -} - - -define hostgroup { - hostgroup_name INEX-Switches-Cisco - alias INEX Cisco Switches - members {$vendor_cisco} - -} - - -define hostgroup { - hostgroup_name INEX-Switches-MRV - alias INEX MRV Switches - members {$vendor_mrv} - -} - - - -define service{ldelim} - use inex-production-switch-service - hostgroup_name INEX-Switches-Brocade - service_description Chassis - check_command check_foundry_chassis!fjvrGzHqr -{rdelim} - -define service {ldelim} - use inex-production-switch-service - service_description Temperature - hostgroup_name INEX-Switches-Cisco - check_command check_cisco_temperature!fjvrGzHqr!32!38 -{rdelim} - - - -define service{ldelim} - use inex-production-switch-service - hostgroup_name INEX-Production-Switches - service_description ping - IPv4 - check_command check_ping_ipv4!10!100.0,10%!200.0,20% -{rdelim} - -define service {ldelim} - use inex-production-switch-service - service_description SSH - hostgroup_name INEX-Production-Switches - check_command check_ssh -{rdelim} - diff --git a/application/views/customer/detail.phtml b/application/views/customer/detail.phtml deleted file mode 100644 index d6df74b42..000000000 --- a/application/views/customer/detail.phtml +++ /dev/null @@ -1,246 +0,0 @@ -{tmplinclude file="header.phtml" pageTitle="IXP Manager :: Member Dashboard"} - -{if $user->getPrivs() eq 3} - - -
-{else} -
- - -{/if} - -{OSS_Message} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Member Type:{CUSTOMER::$CUST_TYPES_TEXT[ $cust->getType() ]}Member Status:{CUSTOMER::$CUST_STATUS_TEXT[ $cust->getStatus() ]}
 
AS Number:{$cust->getAutsys()|asnumber}Peering Macro:{$cust->getPeeringmacro()}
Peering Policy:{$cust->getPeeringpolicy()}
 
Peering Email:{$cust->getPeeringemail()}NOC Email{$cust->getNocemail()}
NOC Phone:{$cust->getNocphone()}NOC 24 Hour Phone{$cust->getNoc24hphone()}
Dedicated NOC Web:{$cust->getNocwww()}NOC Fax{$cust->getNocfax()}
NOC Hours:{$cust->getNochours()}
 
Corporate Web:{$cust->getCorpwww()}
 
- - -{foreach $cust->getVirtualInterfaces() as $vi} - -
- -

- Connection {counter name=numconnections} - {assign var='vlanints' value=$vi->getVlanInterfaces()} - {assign var='vlanint' value=$vlanints.0} - -         Infrastructure #{if $vlanint->getVlan()->getNumber() % 10 == 0}1{else}2{/if} - {if $vi->getPhysicalInterfaces()|@count > 1} - {assign var='isLAG' value=1} -         LAG Port - {else} - {assign var='isLAG' value=0} - {/if} - -

- -{foreach $vi->getPhysicalInterfaces() as $pi} - - {if $isLAG}
Port {counter name=numphysports} of {$vi->getPhysicalInterfaces()|@count} in LAG
{/if} - - - - - - - - - - - - - - - - - - - - - - - - - - -
Switch:{$pi->getSwitchPort()->getSwitcher()->getName()}.inex.ieSwitch Port:{$pi->getSwitchPort()->getName()}
Speed:{$pi->getSpeed()} MbpsDuplex:{$pi->getDuplex()}
Location:{$pi->getSwitchPort()->getSwitcher()->getCabinet()->getLocation()->getName()}Colo Cabinet ID:{$pi->getSwitchPort()->getSwitcher()->getCabinet()->getName()}
-{/foreach} - -

- - -{foreach $vi->getVlanInterfaces() as $vli} -{assign var='vlanid' value=$vli->getVlan()->getId()} - -

      {$vli->getVlan()->getName()}:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
IPv6 Address: - {if $vli->getIpv6enabled() and $vli->getIpv6address()} - {$vli->getIPv6Address()->getAddress()}/{$netinfo.$vlanid.6.masklen} - {else} - IPv6 not enabled. - {/if} - IPv4 Address: - {if $vli->getIpv4enabled() and $vli->getIpv4address()} - {$vli->getIPv4Address()->getAddress()}/{$netinfo.$vlanid.4.masklen} - {else} - IPv4 not enabled. - {/if} -
Multicast Enabled:{if $vli->getMcastenabled()}Yes{else}No{/if}
Route Server Client:{if $vli->getRsclient()}Yes{else}No{/if}AS112 Client:{if $vli->getAs112client()}Yes{else}No{/if}
- -{/foreach} - -

- -{/foreach} - -
- -{tmplinclude file="footer.phtml"} diff --git a/application/views/customer/details.phtml b/application/views/customer/details.phtml deleted file mode 100644 index a0024c3d4..000000000 --- a/application/views/customer/details.phtml +++ /dev/null @@ -1,74 +0,0 @@ -{tmplinclude file="header.phtml" pageTitle="IXP Manager :: Member Dashboard"} - -{if $user->getPrivs() eq 3} - - -
-{else} -
- - -{/if} - -{OSS_Message} - - - - - - - - - - - - - - - {foreach $details as $md} - - - - - - - - - - - {/foreach} - - -
MemberPeering EmailASNNOC PhoneNOC Hours
{$md.name}{$md.peeringemail}{if $md.type eq CUSTOMER::TYPE_ASSOCIATE}(associate){else}{$md.autsys|asnumber}{/if}{$md.nocphone}{$md.nochours}view
- - - -
- -{tmplinclude file="footer.phtml"} diff --git a/application/views/customer/email/diff-footer.phtml b/application/views/customer/email/diff-footer.phtml deleted file mode 100644 index 96ac65701..000000000 --- a/application/views/customer/email/diff-footer.phtml +++ /dev/null @@ -1,9 +0,0 @@ - -{if $numWithExceededThreshold eq 0} -

-There were no members with unusual traffic growth yesterday. -

-{/if} - - - diff --git a/application/views/customer/email/diff-header.phtml b/application/views/customer/email/diff-header.phtml deleted file mode 100644 index 8b5891841..000000000 --- a/application/views/customer/email/diff-header.phtml +++ /dev/null @@ -1,12 +0,0 @@ - - - INEX Traffic Differentials - - - - -

INEX Member Traffic Differentials

- - - - diff --git a/application/views/customer/email/diff-member.phtml b/application/views/customer/email/diff-member.phtml deleted file mode 100644 index 3ed200fb9..000000000 --- a/application/views/customer/email/diff-member.phtml +++ /dev/null @@ -1,41 +0,0 @@ - - -

{$cust->getName()}

- -{if $dIn gt $threasholdIn} -

-INBOUND: - - {if $percentIn neq 'NONE'} - There has been a {$percentIn}% {$sIn} is this - member's traffic as recorded yesterday ({mrtgScale value=$in}) - compared to the average over the past {$days} days - ({mrtgScale value=$meanIn}). (Standard deviation: {mrtgScale value=$stddevIn}). - {else} - No previous records - possibly a new connection brought live? - {/if} - -

-{/if} - -{if $dOut gt $threasholdOut} -

-OUTBOUND: - - {if $percentIn neq 'NONE'} - There has been a {$percentOut}% {$sOut} is this member's - traffic as recorded yesterday ({mrtgScale value=$out}) compared to the - average over the past {$days} days ({mrtgScale value=$meanOut}). (Standard - deviation: {mrtgScale value=$stddevOut}). - {else} - No previous records - possibly a new connection brought live? - {/if} -

-{/if} - -

- - [{$cust->getShortname()}] - -

- diff --git a/application/views/customer/email/util-footer.phtml b/application/views/customer/email/util-footer.phtml deleted file mode 100644 index 3e5ff2598..000000000 --- a/application/views/customer/email/util-footer.phtml +++ /dev/null @@ -1,9 +0,0 @@ - -{if $numWithExcessUtil eq 0} -

-No ports were found with excess utilisation over the past week. -

-{/if} - - - diff --git a/application/views/customer/email/util-header.phtml b/application/views/customer/email/util-header.phtml deleted file mode 100644 index 47a8bcfbd..000000000 --- a/application/views/customer/email/util-header.phtml +++ /dev/null @@ -1,15 +0,0 @@ - - - Port Utilisation Report - - - - -

Port Utilisation Report

- -

-The following ports are at or have exceeded {$threshold*100|string_format:"%.1f"}% utilisation. -

- - - diff --git a/application/views/customer/email/util-member.phtml b/application/views/customer/email/util-member.phtml deleted file mode 100644 index 3808df15e..000000000 --- a/application/views/customer/email/util-member.phtml +++ /dev/null @@ -1,22 +0,0 @@ - - -

{$cust->getName()} :: {$switchport}

- -{if $utilIn gt $threshold} -

-INBOUND: Traffic inbound on this port reached a maximum of {$utilIn*100|string_format:"%.2f"}%. -

-{/if} - -{if $utilOut gt $threshold} -

-OUTBOUND: Traffic outbound on this port reached a maximum of {$utilOut*100|string_format:"%.2f"}%. -

-{/if} - -

- - [{$cust->getShortname()}] - -

- diff --git a/application/views/customer/email/welcome-email.phtml b/application/views/customer/email/welcome-email.phtml deleted file mode 100644 index e0ca68694..000000000 --- a/application/views/customer/email/welcome-email.phtml +++ /dev/null @@ -1,18 +0,0 @@ - -Dear New Member, - -Firstly, welcome to {$options.identity.orgname}! This is your {$options.identity.name} welcome e-mail. - -Please take some time to read this email -- it contains important information concerning your {$options.identity.orgname} membership. - -Several sections of this email require your input; these are all clearly marked by the heading "Your Input". We would appreciate if you could provide us with all the details requested as this will allow us to provide you and other {$options.identity.orgname} members with a better quality of service. - - ----- COMPLETE YOURSELF VIA SKIN ---- - - -Welcome to {$options.identity.orgname}. - - - - diff --git a/application/views/customer/forms/edit.phtml b/application/views/customer/forms/edit.phtml deleted file mode 100644 index 2c6afb336..000000000 --- a/application/views/customer/forms/edit.phtml +++ /dev/null @@ -1,128 +0,0 @@ - -
getId()}" - {else} - action="{genUrl controller="customer" action="add"}" - {/if}> - -
- -
- -
- Customer Details - - {$element->name} - {$element->type} - {$element->shortname} - {$element->corpwww} - {$element->datejoin} - {$element->dateleave} - {$element->status} - -
- -
- -
- -
- Billing Details - - {$element->billingContact} - {$element->billingAddress1} - {$element->billingAddress2} - {$element->billingCity} - {$element->billingCountry} - -
- -
- - -
- -
- -
- -
- Peering Details - - {$element->autsys} - {$element->maxprefixes} - {$element->peeringemail} - {$element->peeringmacro} - {$element->peeringpolicy} - {$element->irrdb} - {$element->activepeeringmatrix} - -
-
- -
- -
- NOC Details - - {$element->nocphone} - {$element->noc24hphone} - {$element->nocfax} - {$element->nocemail} - {$element->nochours} - {$element->nocwww} - -
-
- -
- - - -
- - Cancel - - -
- - -
- - - - diff --git a/application/views/customer/forms/send-email.phtml b/application/views/customer/forms/send-email.phtml deleted file mode 100644 index f8c7968af..000000000 --- a/application/views/customer/forms/send-email.phtml +++ /dev/null @@ -1,60 +0,0 @@ - -
- -
- -
- -
- Send Email to Customer -

-
-
-
- -
- -
- - {$element->to} - {$element->subject} - {$element->cc} - {$element->bcc} - {$element->message} - -
-
- -
- - getId()}">Cancel - - -
- - -
- - - - - - diff --git a/application/views/customer/list-row-menu.phtml b/application/views/customer/list-row-menu.phtml deleted file mode 100644 index aa82841dc..000000000 --- a/application/views/customer/list-row-menu.phtml +++ /dev/null @@ -1,4 +0,0 @@ - -
- -
diff --git a/application/views/customer/overview.phtml b/application/views/customer/overview.phtml deleted file mode 100644 index afbc78619..000000000 --- a/application/views/customer/overview.phtml +++ /dev/null @@ -1,364 +0,0 @@ -{tmplinclude file="header.phtml" pageTitle="IXP Manager :: Administrator's Home"} - - - -{OSS_Message} - -
- -
- -
- -

- {$cust->getName()} - {if $cust->getType() eq CUSTOMER::TYPE_ASSOCIATE} - ASSOCIATE MEMBER - {elseif $cust->getType() eq CUSTOMER::TYPE_PROBONO} - PROBONO MEMBER - {elseif $cust->getType() eq CUSTOMER::TYPE_INTERNAL} - INTERNAL INFRASTRUCTURE - {elseif $cust->getType() eq CUSTOMER::TYPE_FULL} - FULL MEMBER - {else} - UNKNOWN MEMBER TYPE - {/if} - {if $cust->hasLeft()} - ACCOUNT CLOSED - {/if} -

-

- {if $cust->getCorpwww()}{$cust->getCorpwww()}{/if} - {if $cust->getPeeringemail()} - {mailto address=$cust->getPeeringemail()}{/if} -

-
- - - - - - - - - - - - - - - - - {if $cust->getType() neq CUSTOMER::TYPE_ASSOCIATE} - - - - - - - {/if} - - - - - - - - -
Status{CUSTOMER::$CUST_STATUS_TEXT[$cust->getStatus()]}Joined{$cust->getDatejoin()->format( 'Y-m-d' )}
Type{CUSTOMER::$CUST_TYPES_TEXT[$cust->getType()]}Left{if $cust->hasLeft()}{$cust->getDateleave()->format( 'Y-m-d' )}{/if}
Peering Policy{$cust->getPeeringpolicy()}ASN{$cust->getAutsys()|asnumber} {if $cust->getPeeringmacro()}({$cust->getPeeringmacro()}){/if}
{if $cust->getType() neq CUSTOMER::TYPE_ASSOCIATE}NOC Details{/if} - {if $cust->getType() neq CUSTOMER::TYPE_ASSOCIATE} - {if $cust->getNochours()} {$cust->getNochours()}
{/if} - {if $cust->getNocemail()} {mailto address=$cust->getNocemail()}
{/if} - {if $cust->getNocwww()} {$cust->getNocwww()}
{/if} - {if $cust->getNocphone()} {$cust->getNocphone()}
{/if} - {if $cust->getNoc24hphone()} {$cust->getNoc24hphone()} (24h) {/if} - {/if} -
Billing Details - {if $cust->getBillingContact()} {$cust->getBillingContact()}
{/if} - {if $cust->getBillingAddress1()} {$cust->getBillingAddress1()}
{/if} - {if $cust->getBillingAddress2()} {$cust->getBillingAddress2()}
{/if} - {if $cust->getBillingCity()} {$cust->getBillingCity()}
{/if} - {if $cust->getBillingCountry()} {$cust->getBillingCountry()}
{/if} -
- - - - {if $cust->getType() neq CUSTOMER::TYPE_ASSOCIATE and ( not $cust->hasLeft() )} - -

-

Interfaces getId() rtn="ov"}">

- - {if count( $cust->getVirtualInterfaces() )} - - - - - - - - - - - - - {foreach $cust->getVirtualInterfaces() as $vi} - - {assign var="pis" value=$vi->getPhysicalinterfaces()} - - - - - - - - - - - - - {/foreach} - - - -
InfrastructureLocationSwitchPortSpeed
- LAN #{$pis[0]->getSwitchPort()->getSwitcher()->getInfrastructure()} - - {$pis[0]->getSwitchPort()->getSwitcher()->getCabinet()->getLocation()->getName()} - {foreach from=$pis item=pi name=pis1} - {$pi->getSwitchPort()->getSwitcher()->getName()}{if not $smarty.foreach.pis1.last}
{/if} - {/foreach} -
- {foreach from=$pis item=pi name=pis2} - getMonitorindex() category='bits' shortname=$cust->getShortname()}"> - {$pi->getSwitchPort()->getName()} - {if not $smarty.foreach.pis2.last}
{/if} - {/foreach} -
- {foreach from=$pis item=pi name=pis3} - {$pi->getSpeed()}/{$pi->getDuplex()}{if not $smarty.foreach.pis3.last}
{/if} - {/foreach} -
- -
- - {else} - -

No interfaces found.

- - {/if} - - {/if} {* end dateleave *} - - - - -

-

User Accounts getId()}">

- - {if count( $cust->getUsers() )} - - - - - - - - - - - {foreach $cust->getUsers() as $u} - - - - - - - - {/foreach} - -
UsernameTypeEmailMobile
{$u->getUsername()}{USER::$PRIVILEGES[$u->getPrivs()]}{$u->getEmail()}{$u->getAuthorisedMobile()} - -
- - {else} - -

No users found.

- - {/if} - - -

-

Contacts getId()}">

- - - {if count( $cust->getContacts() )} - - - - - - - - - - - {foreach $cust->getContacts() as $c} - - - - - - - - {/foreach} - -
NameEmailPhoneMobile
{$c->getName()}{$c->getEmail()}{$c->getPhone()}{$c->getMobile()} - -
- - - {else} - -

No contacts found.

- - {/if} - - - -
-
- - - -
- - {if $cust->getType() neq CUSTOMER::TYPE_ASSOCIATE and not $cust->hasLeft()} - - - - - {foreach $cust->getVirtualInterfaces() as $vi} - - {foreach $vi->getPhysicalInterfaces() as $pi} - -
- -
- -

- {$pi->getSwitchPort()->getSwitcher()->getCabinet()->getLocation()->getName()} - / {$pi->getSwitchPort()->getSwitcher()->getName()} - / {$pi->getSwitchPort()->getName()} ({$pi->getSpeed()}Mbps) -

- - -

-
- - {genMrtgImgUrlTag shortname=$cust->getShortname() category='bits' monitorindex=$pi->getMonitorindex()} - -

- -
- -
- - {/foreach} - - {/foreach} - - - {/if} {* end dateleave *} - -
- - - -
- - - - - -{tmplinclude file="footer.phtml"} diff --git a/application/views/customer/send-email.phtml b/application/views/customer/send-email.phtml deleted file mode 100644 index 0f66d8fd9..000000000 --- a/application/views/customer/send-email.phtml +++ /dev/null @@ -1,23 +0,0 @@ -{tmplinclude file="header.phtml"} - - - -{OSS_Message} - -{$form} - - -{tmplinclude file="footer.phtml"} diff --git a/application/views/customer/welcome-email.phtml b/application/views/customer/welcome-email.phtml deleted file mode 100644 index 5c0a961d1..000000000 --- a/application/views/customer/welcome-email.phtml +++ /dev/null @@ -1 +0,0 @@ -{tmplinclude file="customer/send-email.phtml"} diff --git a/application/views/dashboard/index-tab-associate.phtml b/application/views/dashboard/index-tab-associate.phtml deleted file mode 100644 index 7431e719f..000000000 --- a/application/views/dashboard/index-tab-associate.phtml +++ /dev/null @@ -1,29 +0,0 @@ - - - -

Recent Members

- -

Our three most recent members are listed below.

- - - - - - - - - - - - {foreach from=$recentMembers item=member} - - - - - - {/foreach} - - -
NameAS NumberDate Joined
{$member->getName()}{$member->getAutsys()|asnumber}{$member->getDatejoin()->format( 'Y-m-d' )}
- - diff --git a/application/views/dashboard/index-tab-connections.phtml b/application/views/dashboard/index-tab-connections.phtml deleted file mode 100644 index b6370306b..000000000 --- a/application/views/dashboard/index-tab-connections.phtml +++ /dev/null @@ -1,135 +0,0 @@ - -{foreach $user->getCustomer()->getVirtualInterfaces() as $vi} - -
- -
- -

- Connection {counter name=numconnections} - {assign var='vlanints' value=$vi->getVlanInterfaces()} - {assign var='vlanint' value=$vlanints.0} - - {assign var=physInts value=$vi->getPhysicalInterfaces()} -         Infrastructure #{$physInts[0]->getSwitchPort()->getSwitcher()->getInfrastructure()} - {if $vi->getPhysicalInterfaces()|@count > 1} - {assign var='isLAG' value=1} -         LAG Port - {else} - {assign var='isLAG' value=0} - {/if} - -

- - {foreach $vi->getPhysicalInterfaces() as $pi} - - {if $isLAG}
Port {counter name=numphysports} of {$vi->getPhysicalInterfaces()|@count} in LAG
{/if} - - - - - - - - - - - - - - - - - - - - - - - - - - -
Switch:{$pi->getSwitchPort()->getSwitcher()->getName()}{$options.identity.switch_domain}Switch Port:{$pi->getSwitchPort()->getName()}
Speed:{$pi->getSpeed()} MbpsDuplex:{$pi->getDuplex()}
Location:{$pi->getSwitchPort()->getSwitcher()->getCabinet()->getLocation()->getName()}Colo Cabinet ID:{$pi->getSwitchPort()->getSwitcher()->getCabinet()->getName()}
- {/foreach} - -

- - - {foreach $vi->getVlanInterfaces() as $vli} - {assign var='vlanid' value=$vli->getVlan()->getId()} - -

      {$vli->getVlan()->getName()}:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
IPv6 Address: - {if $vli->getIpv6enabled() and $vli->getIpv6address()} - {$vli->getIPv6Address()->getAddress()}/{$netinfo.$vlanid.6.masklen} - {else} - IPv6 not enabled. - {/if} - IPv4 Address: - {if $vli->getIpv4enabled() and $vli->getIpv4address()} - {$vli->getIPv4Address()->getAddress()}/{$netinfo.$vlanid.4.masklen} - {else} - IPv4 not enabled. - {/if} -
Multicast Enabled:{if $vli->getMcastenabled()}Yes{else}No{/if}
Route Server Client:{if $vli->getRsclient()}Yes{else}No{/if}AS112 Client:{if $vli->getAs112client()}Yes{else}No{/if}
- - {/foreach} - -

- -
-
- - {foreach $vi->getPhysicalInterfaces() as $pi} -

- - {/foreach} -
-
- -{/foreach} diff --git a/application/views/dashboard/index-tab-details.phtml b/application/views/dashboard/index-tab-details.phtml deleted file mode 100644 index 3277b3a39..000000000 --- a/application/views/dashboard/index-tab-details.phtml +++ /dev/null @@ -1,17 +0,0 @@ - -
- -
- - {$nocDetails} - -
- -
- - {$billingDetails} - -
- -
- diff --git a/application/views/dashboard/index-tab-overview-news.phtml b/application/views/dashboard/index-tab-overview-news.phtml deleted file mode 100644 index a3c5c69d9..000000000 --- a/application/views/dashboard/index-tab-overview-news.phtml +++ /dev/null @@ -1,42 +0,0 @@ -
- -
- -

Recents Changes and Updates to IXP Manager

-
- -
    -
  • - Peer to peer (member to member) traffic graphs are now available.. - All feed back greatly appreciated. - Check yours out here. -
  • -
  • - New Peering Manager now available. - Check it out here. -
  • -
  • - Peering matrices are now working again - with added IPv6 support - and lots - of new bells and whistles. - Check them out here. -
  • -
  • - You can now manage your mailing list subscriptions via - your profile page. -
  • -
  • Redesigned statistics pages making them clearer and more intuitive.
  • -
  • - Added weather maps for peering LAN 1 - and LAN 2 showing inter-PoP - trunk untilisation and other information based on feedback from members. -
  • - -
- -
- -
-   -
-
- diff --git a/application/views/dashboard/index-tab-overview.phtml b/application/views/dashboard/index-tab-overview.phtml deleted file mode 100644 index c47c14ab6..000000000 --- a/application/views/dashboard/index-tab-overview.phtml +++ /dev/null @@ -1,53 +0,0 @@ - -
- - - -
- -

Recent Members

- -

Our three most recent members are listed below. {if not $user->getCustomer()->isTypeAssociate()}Have you arranged peering with them yet?{/if}

- - - - - - - - {if not $user->getCustomer()->isTypeAssociate()} - - {/if} - - - - - {foreach $recentMembers as $rm} - - - - - {if not $user->getCustomer()->isTypeAssociate()} - - {/if} - - {/foreach} - - -
NameAS NumberDate JoinedPeering Contact
{$rm->getName()}{$rm->getAutsys()|asnumber}{$rm->getDatejoin()->format( 'Y-m-d' )} - {$rm->getPeeringemail()} -
- -
-
- -{tmplinclude file="dashboard/index-tab-overview-news.phtml"} - diff --git a/application/views/dashboard/index.phtml b/application/views/dashboard/index.phtml deleted file mode 100644 index 42e797e11..000000000 --- a/application/views/dashboard/index.phtml +++ /dev/null @@ -1,53 +0,0 @@ -{tmplinclude file="header.phtml" pageTitle="IXP Manager :: Member Dashboard"} - -
- -{OSS_Message} - -{* if $meeting neq false} - {include file="dashboard/popups/meeting.tpl"} -{/if *} - - - -{if not $user->getCustomer()->isTypeAssociate()} - - - -
- -
- - {tmplinclude file="dashboard/index-tab-overview.phtml"} -
- -
- - {tmplinclude file="dashboard/index-tab-details.phtml"} - -
-
- - {tmplinclude file="dashboard/index-tab-connections.phtml"} - -
- -
- -{else} - - {tmplinclude file="dashboard/index-tab-associate.phtml"} - -{/if} - -
- -{tmplinclude file="footer.phtml"} diff --git a/application/views/dashboard/popups/meeting.tpl b/application/views/dashboard/popups/meeting.tpl deleted file mode 100644 index 791d40d4a..000000000 --- a/application/views/dashboard/popups/meeting.tpl +++ /dev/null @@ -1,113 +0,0 @@ - - - - - -{literal} - - - -{/literal} diff --git a/application/views/error/error-404.phtml b/application/views/error/error-404.phtml deleted file mode 100644 index 973379bf1..000000000 --- a/application/views/error/error-404.phtml +++ /dev/null @@ -1,31 +0,0 @@ -{tmplinclude file="header.phtml"} - -
- - - - -{OSS_Message} - -

- - - - - -
-
-

Sorry - the page you are looking for cannot be found

- -

- You may have followed a broken link, an outdated search result, or there may be an - error on our site. If you typed in a URL, please make sure you have typed it in - correctly. In particular, make sure that the URL you typed is all in lower case. -

-
- -

-
-{tmplinclude file="footer.phtml"} diff --git a/application/views/error/error.phtml b/application/views/error/error.phtml deleted file mode 100644 index 251bc0e9b..000000000 --- a/application/views/error/error.phtml +++ /dev/null @@ -1,68 +0,0 @@ -{tmplinclude file="header.phtml"} - - -
- - - - -{OSS_Message} - -

- - - - - -
-
-

Uh oh! Unexpected Error!

- -

We're very sorry but something we didn't expect has just happened.

-

We've alerted the web monkeys and, after a severe flogging, they will get it fixed as soon as possible.

-

Please feel free to email the {mailto address=$options.identity.email encode='javascript' text=$options.identity.name}

-
- -

- -{if isset( $options.phpSettings.display_errors ) and $options.phpSettings.display_errors} - -
-

- Development Platform Detected! Display errors enabled via application.ini. Disable in production! -

-
- - {if isset( $exceptions )} - - {foreach $exceptions as $exception} - -

{get_class( $exception )}

- -
-
File
-
{$exception->getFile()}
- -
Line
-
{$exception->getLine()}
- -
Message
-
{$exception->getMessage()}
- -
Code
-
{$exception->getCode()}
-
- -

Trace

- -
{$exception->getTraceAsString()}
- - {/foreach} - - {/if} -{/if} - -
-{tmplinclude file="footer.phtml"} diff --git a/application/views/error/insufficient-permissions.phtml b/application/views/error/insufficient-permissions.phtml deleted file mode 100644 index a913732d5..000000000 --- a/application/views/error/insufficient-permissions.phtml +++ /dev/null @@ -1,25 +0,0 @@ -{tmplinclude file="header.phtml"} - -
- - - - -{OSS_Message} - -

- You don't have the required permissions to access the requested page or object. -

- -

- This request has been logged. -

- -

- If you believe this is an error, please contact your administrator. -

- -
-{tmplinclude file="footer.phtml"} diff --git a/application/views/footer-content.phtml b/application/views/footer-content.phtml deleted file mode 100644 index 4ed684465..000000000 --- a/application/views/footer-content.phtml +++ /dev/null @@ -1,32 +0,0 @@ - -
- -

- IXP Manager V{$smarty.const.APPLICATION_VERSION} -   |   - Copyright © 2010 - {$smarty.now|date_format:'%Y'} Internet Neutral Exchange Association Ltd. - http://www.inex.ie/ -  |  - Contact Us -

- -

- Licensed under GPL v2.0. -  |  - This Program is provided AS IS, without warranty. -  |  - {assign var="ENDTIME" value=microtime(1)} - {assign var="RUNNINGTIME" value="`$ENDTIME-$smarty.const.APPLICATION_STARTTIME`"} - Generated in {$RUNNINGTIME|string_format:"%0.3f"} seconds -

- - - {if $smarty.const.APPLICATION_ENV != 'production' and ( not isset( $hasIdentity ) or !$hasIdentity)} -

- - THE IXP IS RUNNING IN NON-PRODUCTION MODE AND INFORMATION CAN BE LEAKED VIA DEBUGGING - UTILITIES. ENSURE HTACCESS IS IN PLACE. - -

- {/if} - -
diff --git a/application/views/footer.phtml b/application/views/footer.phtml deleted file mode 100644 index f932df0fb..000000000 --- a/application/views/footer.phtml +++ /dev/null @@ -1,33 +0,0 @@ - -{if isset( $hasIdentity ) and $hasIdentity and $user->getPrivs() eq 3} - -
-
-{/if} - -{tmplinclude file='footer-content.phtml'} - -{if !isset( $mode ) or $mode neq 'fluid'} - -
- -{/if} - - - - - - diff --git a/application/views/frontend/add.phtml b/application/views/frontend/add.phtml deleted file mode 100644 index 4922f3f17..000000000 --- a/application/views/frontend/add.phtml +++ /dev/null @@ -1,74 +0,0 @@ -{tmplinclude file="header.phtml"} - -{if isset( $user ) and $user->getPrivs() eq USER::AUTH_SUPERUSER} - - -
-{else} -
- - -{/if} - - {OSS_Message} - - {* do we have any additional text to put before the table? *} - {if $addPreamble}{tmplinclude file=$addPreamble}{/if} - - {$form} - - {* do we have any additional text to put after the table? *} - {if $addPostamble}{tmplinclude file=$addPostamble}{/if} - -
- - -{if $addScript}{/if} -{tmplinclude file="footer.phtml"} \ No newline at end of file diff --git a/application/views/frontend/ajax-add.phtml b/application/views/frontend/ajax-add.phtml deleted file mode 100644 index d5e2ea1ef..000000000 --- a/application/views/frontend/ajax-add.phtml +++ /dev/null @@ -1,75 +0,0 @@ - - - \ No newline at end of file diff --git a/application/views/frontend/ajax-view.phtml b/application/views/frontend/ajax-view.phtml deleted file mode 100644 index ff453e677..000000000 --- a/application/views/frontend/ajax-view.phtml +++ /dev/null @@ -1,41 +0,0 @@ - - \ No newline at end of file diff --git a/application/views/frontend/index.phtml b/application/views/frontend/index.phtml deleted file mode 100644 index ec611f45b..000000000 --- a/application/views/frontend/index.phtml +++ /dev/null @@ -1,14 +0,0 @@ -{include file="header.phtml"} - -
- - - - {OSS_Message} - - -
- -{include file="footer.phtml"} \ No newline at end of file diff --git a/application/views/frontend/js/list.js b/application/views/frontend/js/list.js deleted file mode 100644 index bf29119a8..000000000 --- a/application/views/frontend/js/list.js +++ /dev/null @@ -1,55 +0,0 @@ - - -var oDataTable; - -$(document).ready(function() { - - $( 'a[id|="list-delete"]' ).on( 'click', function( event ){ - - event.preventDefault(); - url = $(this).attr("href"); - - bootbox.dialog( "Are you sure you want to delete this object?", [{ - "label": "Cancel", - "class": "btn-primary" - }, - { - "label": "Delete", - "class": "btn-danger", - "callback": function() { document.location.href = url; } - }]); - - }); - - - oDataTable = $( '#frontend-list-table' ).dataTable({ - 'fnDrawCallback': function() { - if( oss_prefs != undefined && 'iLength' in oss_prefs && oss_prefs['iLength'] != $( "select[name='frontend-list-table_length']" ).val() ) - { - oss_prefs['iLength'] = parseInt( $( "select[name='frontend-list-table_length']" ).val() ); - $.jsonCookie( 'oss_prefs', oss_prefs, oss_cookie_options ); - } - }, - 'iDisplayLength': ( typeof oss_prefs != 'undefined' && 'iLength' in oss_prefs ) - ? oss_prefs['iLength'] - : {if isset( $options.defaults.table.entries )}{$options.defaults.table.entries}{else}10{/if}, - "sDom": "<'row-fluid'<'span6'l><'span6'f>r>t<'row-fluid'<'span6'i><'span6'p>>", - "sPaginationType": "bootstrap", - "bAutoWidth": false, - 'aoColumns': [ - {foreach $feParams->listColumns as $col => $cconf} - {if not is_array( $cconf ) or not isset( $cconf.display ) or $cconf.display} - null, - {/if} - {/foreach} - { 'bSortable': false, "bSearchable": false, "sWidth": "150px" } - ] - }); - - $( '#frontend-list-table' ).show(); - -}); - - - - diff --git a/application/views/frontend/list.phtml b/application/views/frontend/list.phtml deleted file mode 100644 index fe9e42aed..000000000 --- a/application/views/frontend/list.phtml +++ /dev/null @@ -1,142 +0,0 @@ -{tmplinclude file="header.phtml"} - -{if isset( $user ) and $user->getPrivs() eq USER::AUTH_SUPERUSER} - - -
-{else} -
- - -{/if} - - {OSS_Message} - - {* do we have any additional text to put before the table? *} - {if $listPreamble}{tmplinclude file=$listPreamble}{/if} - - {if count( $data )} - - - - - {foreach $feParams->listColumns as $col => $cconf} - {if not is_array( $cconf ) or not isset( $cconf.display ) or $cconf.display} - - {/if} - {/foreach} - - - - - - {foreach $data as $idx => $row} - - {foreach $feParams->listColumns as $col => $cconf} - {if not is_array( $cconf )} - - {elseif not isset( $cconf.display ) or $cconf.display} - {if isset( $cconf.type )} - {if $cconf.type eq $FE_COL_TYPES.HAS_ONE} - - {elseif $cconf.type eq $FE_COL_TYPES.XLATE} - - {elseif $cconf.type eq $FE_COL_TYPES.DATETIME} - - {elseif $cconf.type eq $FE_COL_TYPES.DATE} - - {elseif $cconf.type eq $FE_COL_TYPES.TIME} - - {elseif $cconf.type eq $FE_COL_TYPES.SCRIPT} - {tmplinclude file=$cconf.script} - {else} - - {/if} - {else} - - {/if} - {/if} - {/foreach} - - - {/foreach} - - -
{if is_array( $cconf )}{$cconf.title}{else}{$cconf}{/if}
{$row.$col} - {assign var=hasOneId value=$cconf.idField} - {$row.$col} - - {$cconf.xlator[ $row.$col ]} - - {$row.$col|date_format:'%Y-%m-%d %H:%M:%S'} - - {$row.$col|date_format:'%Y-%m-%d'} - - {$row.$col|date_format:'%H:%M:%S'} - Type?{$row.$col} - {* do we have a custom menu defined for this table? *} - {if not $listRowMenu} -
- - {if !isset( $feParams->readonly ) or !$feParams->readonly} - - - {/if} -
- {else} - {tmplinclude file=$listRowMenu} - {/if} -
- {else} -

- There are no entries.{if !isset( $feParams->addWhenEmpty ) || $feParams->addWhenEmpty } Do you want to add one...{/if} -

- {/if} {* end if count( $data ) *} - - {* do we have any additional text to put after the table? *} - {if $listPostamble}{tmplinclude file=$listPostamble}{/if} - -
- - -{if $listScript}{/if} -{if $listAddonScript}{/if} -{tmplinclude file="footer.phtml"} \ No newline at end of file diff --git a/application/views/frontend/overview.phtml b/application/views/frontend/overview.phtml deleted file mode 100644 index b9d83f1e6..000000000 --- a/application/views/frontend/overview.phtml +++ /dev/null @@ -1,97 +0,0 @@ -{include file="header.phtml"} - -
- - - - {OSS_Message} - - {* do we have any additional text to put before the view? *} - {if $overviewPreamble}{include file=$overviewPreamble}{/if} - - {assign var="cnt" value=0} - {assign var="per_row" value=$feParams->overviewSpans} - {foreach $feParams->overviewSections as $name=>$section } - {if $section@index == 0 } -
- {else if ( $section@index % $per_row ) == 0 } - {assign var="cnt" value=0} -
-
- {/if} - {assign var="cnt" value=$cnt+1} -
- {if not is_array( $section )} -

{$section}

- {else} - {if $section.haveMenu} -
- {if isset( $section.menu ) && $section.menu } - {include file=$row.menu} - {else} -
-
- {if isset( $section.wideModal ) && $section.wideModal } - - {else} - - {/if} -
-
- {/if} -
- {/if} -

{$section.label}

- {/if} -
    - {if isset( $data.$name ) } - {foreach $data.$name as $row} -
  • - {if not is_array( $row ) } - {$row} - {else} - {if isset( $row.menu )}
    {include file=$row.menu}
    {/if} - {if isset( $row.label) } - {$row.label}: - {/if} - {$row.preview} - {/if} -
  • - {/foreach} - {/if} -
-
- {/foreach} - - - {if $cnt > 0 && $cnt < $per_row} -
-
- {else} - {assign var="cnt" value=0} -
- {/if} - - {* do we have any additional text to put after the view? *} - {if $overviewPostamble}{include file=$overviewPostamble}{/if} - -
- - -{if $overviewScript}{/if} -{include file="footer.phtml"} \ No newline at end of file diff --git a/application/views/frontend/view.phtml b/application/views/frontend/view.phtml deleted file mode 100644 index 02ba279fc..000000000 --- a/application/views/frontend/view.phtml +++ /dev/null @@ -1,97 +0,0 @@ -{tmplinclude file="header.phtml"} - -{if isset( $user ) and $user->getPrivs() eq USER::AUTH_SUPERUSER} - - -
-{else} -
- - -{/if} - - {OSS_Message} - - {* do we have any additional text to put before the view? *} - {if $viewPreamble}{tmplinclude file=$viewPreamble}{/if} - -
- {foreach $feParams->viewColumns as $col => $cconf} - {if not is_array( $cconf ) or not isset( $cconf.display ) or $cconf.display} -
- {if not is_array( $cconf )}{$cconf}{else}{$cconf.title}{/if} -
-
- {if not is_array( $cconf )} - {if $object.$col eq false}0{else}{$object.$col}{/if} - {elseif isset( $cconf.type )} - {if $cconf.type eq $FE_COL_TYPES.HAS_ONE} - {assign var=hasOneId value=$cconf.idField} - {$object.$col} - {elseif $cconf.type eq $FE_COL_TYPES.XLATE} - {$cconf.xlator[ $object.$col ]} - {elseif $cconf.type eq $FE_COL_TYPES.DATETIME} - {if $object.$col}{$object.$col|date_format:'%Y-%m-%d %H:%M:%S'}{/if} - {elseif $cconf.type eq $FE_COL_TYPES.SCRIPT} - {tmplinclude file=$cconf.script} - {else} - Type? - {/if} - {else} - {$object.$col} - {/if} -
- {/if} - {/foreach} -
- - {* do we have any additional text to put after the view? *} - {if $viewPostamble}{tmplinclude file=$viewPostamble}{/if} - -
- - -{if $viewScript}{/if} -{tmplinclude file="footer.phtml"} \ No newline at end of file diff --git a/application/views/header-base.phtml b/application/views/header-base.phtml deleted file mode 100644 index fb8457087..000000000 --- a/application/views/header-base.phtml +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - {$pageTitle|default:"IXP Manager"} - - - - - - - - {tmplinclude file="header-css.phtml"} - {tmplinclude file="header-js.phtml"} - - - - - {if ( not isset( $hasIdentity ) or not $hasIdentity or $user->getPrivs() neq 3 ) and ( not isset( $mode ) or $mode neq 'fluid' )} - - {/if} - - - - - - - - - diff --git a/application/views/header-css.phtml b/application/views/header-css.phtml deleted file mode 100644 index 8c38fa1e9..000000000 --- a/application/views/header-css.phtml +++ /dev/null @@ -1,14 +0,0 @@ -{if isset( $config.use_minified_css ) and $config.use_minified_css} - -{else} - - - - - - - - - - -{/if} diff --git a/application/views/header-documentation.phtml b/application/views/header-documentation.phtml deleted file mode 100644 index 6d5bd6176..000000000 --- a/application/views/header-documentation.phtml +++ /dev/null @@ -1,8 +0,0 @@ - diff --git a/application/views/header-js.phtml b/application/views/header-js.phtml deleted file mode 100644 index 12f0f3f41..000000000 --- a/application/views/header-js.phtml +++ /dev/null @@ -1,19 +0,0 @@ -{if isset( $config.use_minified_js ) and $config.use_minified_js} - -{else} - - - - - - - - - - - - - - - -{/if} diff --git a/application/views/header.phtml b/application/views/header.phtml deleted file mode 100644 index 097fea4ab..000000000 --- a/application/views/header.phtml +++ /dev/null @@ -1,155 +0,0 @@ -{tmplinclude file="header-base.phtml"} - - - -{if isset( $hasIdentity ) and $hasIdentity and $user->getPrivs() eq 3} - -
- - {tmplinclude file="menu.phtml"} - -{elseif isset( $mode ) and $mode eq 'fluid'} - -
- -{else} - -
- -{/if} diff --git a/application/views/index/about.phtml b/application/views/index/about.phtml deleted file mode 100644 index 44b26f6b4..000000000 --- a/application/views/index/about.phtml +++ /dev/null @@ -1,80 +0,0 @@ -{tmplinclude file="header.phtml"} - -{if isset( $user ) and $user->getPrivs() eq 3} - -{else} -
- -{/if} - -

- IXP Manager is primarily a web application with associated - scripts and utilities which allows Internet Exchange Points (IXPs) to - manage new members (or customers), provision new connections and services and - monitor traffic usage. It also has a self contained customer portal allowing - IXP members to view their IXP traffic statistics and a unique tool called - Peering Manager enabling IXP members to request, manage and track - peerings with other members. -

- -

- IXP Manager was built entirely in house by the INEX operations team - to support the running of the exchange. INEX - (Internet Neutral Exchange Association) is Ireland's IP peering hub. -

- -

- INEX are pleased to to be able to release IXP Manager under an open source license - (the GNU Public License V2) - which we hope will benefit the wider IXP community, and especially new and small - IXPs looking to expand. -

- -

- IXP Manager is written in PHP using the Zend Framework, the Doctrine ORM, the - Smarty templating engine, Open Solutions' - extensions to Zend - the - OSS Framework, JQuery and many other smaller components. The project website - and source code can be viewed at - https://github.com/inex/IXP-Manager. -

- -

- There is a mailing / discussion list for IXP Manager. You can join and view the - archives at: https://www.inex.ie/mailman/listinfo/ixpmanager. -

- -

Software License

- -

- - Copyright (C) 2009 - {$smarty.now|date_format:"%Y"} Internet Neutral Exchange Association Limited.
- All Rights Reserved. -
-

- -
-IXP Manager is free software: you can redistribute it and/or modify it
-under the terms of the GNU General Public License as published by the Free
-Software Foundation, version v2.0 of the License.
-
-IXP Manager is distributed in the hope that it will be useful, but WITHOUT
-ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
-more details.
-
- -{if not isset( $user ) or $user->getPrivs() neq 3} -
-{/if} - -{tmplinclude file="footer.phtml"} diff --git a/application/views/ipv4-address/add.phtml b/application/views/ipv4-address/add.phtml deleted file mode 100644 index 58bc912de..000000000 --- a/application/views/ipv4-address/add.phtml +++ /dev/null @@ -1,28 +0,0 @@ -{tmplinclude file="header.phtml"} - - - -{OSS_Message} - -

Please note:

- -
    -
  • IPv6 addresses assume that the first address number is given in hex. IPv4 assume decimal.
  • -
  • The number of addresses to create is always expressed in decimal.
  • -
  • Clicking generate will show you the addresses that will be created. A further action is required to commit.
  • -
- -{$form} - - -{tmplinclude file="footer.phtml"} diff --git a/application/views/ipv4-address/forms/add-addresses.phtml b/application/views/ipv4-address/forms/add-addresses.phtml deleted file mode 100644 index 3667321e9..000000000 --- a/application/views/ipv4-address/forms/add-addresses.phtml +++ /dev/null @@ -1,135 +0,0 @@ -
getName() != ''}name="{$element->getName()}"{/if} - {if $element->getAttrib('target')}target="{$element->getAttrib('target')}"{/if} - class="form"> - - {$element->vlanid} - {$element->type} - -
- - -
- - ? -
-
- -
- - -
- - ? -
-
- -
- - -
- - ? -
-
- - -
- -
- -
-
- -
- -
- - Cancel - - -
- -
- - - diff --git a/application/views/ipv4-address/list-toolbar.phtml b/application/views/ipv4-address/list-toolbar.phtml deleted file mode 100644 index c2bcc2c1b..000000000 --- a/application/views/ipv4-address/list-toolbar.phtml +++ /dev/null @@ -1,22 +0,0 @@ -
  • -
    - -
    - - {if isset( $vid ) and $vid}{$vlans.$vid}{else}Limit to VLAN...{/if} - - - -
    - -
    - -
    -
    -
  • - - diff --git a/application/views/ipv6-address/list-toolbar.phtml b/application/views/ipv6-address/list-toolbar.phtml deleted file mode 100644 index 0505d8f66..000000000 --- a/application/views/ipv6-address/list-toolbar.phtml +++ /dev/null @@ -1,21 +0,0 @@ -
  • -
    - -
    - - {if isset( $vid ) and $vid}{$vlans.$vid}{else}Limit to VLAN...{/if} - - - -
    - -
    - -
    -
    -
  • - diff --git a/application/views/meeting-item/add-preamble.phtml b/application/views/meeting-item/add-preamble.phtml deleted file mode 100644 index 7c9001fa7..000000000 --- a/application/views/meeting-item/add-preamble.phtml +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/application/views/meeting-item/js/add.js b/application/views/meeting-item/js/add.js deleted file mode 100644 index 594b4b15a..000000000 --- a/application/views/meeting-item/js/add.js +++ /dev/null @@ -1,5 +0,0 @@ - -$(function() { - $('#summary').wysiwyg(); -}); - \ No newline at end of file diff --git a/application/views/meeting-item/list-pretable.tpl b/application/views/meeting-item/list-pretable.tpl deleted file mode 100644 index 55bde152b..000000000 --- a/application/views/meeting-item/list-pretable.tpl +++ /dev/null @@ -1,24 +0,0 @@ - - - -
    -
    -

    -

    - Meeting:  - -   - -
    -

    -
    -
    - diff --git a/application/views/meeting-item/list-toolbar.phtml b/application/views/meeting-item/list-toolbar.phtml deleted file mode 100644 index 745431cb1..000000000 --- a/application/views/meeting-item/list-toolbar.phtml +++ /dev/null @@ -1,21 +0,0 @@ -
  • -
    - -
    - - {if isset( $mid ) and $mid}{$meetings.$mid}{else}Limit to meeting...{/if} - - - -
    - -
    - -
    -
    -
  • - diff --git a/application/views/meeting/add-preamble.phtml b/application/views/meeting/add-preamble.phtml deleted file mode 100644 index 53e533ac9..000000000 --- a/application/views/meeting/add-preamble.phtml +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/application/views/meeting/compose.phtml b/application/views/meeting/compose.phtml deleted file mode 100644 index a087308ad..000000000 --- a/application/views/meeting/compose.phtml +++ /dev/null @@ -1,112 +0,0 @@ -{tmplinclude file="header.phtml"} - - - -{OSS_Message} - -

    Composing Email for Members' Meeting of {$meeting->getDate()->format( 'Y-m-d H:i' )}

    - -

    -The form below allows you to enter some pre-amble to the email detailing -the members' meeting. If you are unsure of how this works, enter some text and send the mail -which, by default, only sends it to yourself. -

    - -

    -When you're happy with the result, you can send it to {$options.mailinglists.members.email}. -

    - -
    - -
    getId()}"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - From:   - - -
    - To:   - - -
    - BCC:   - - -
    - Subject:   - - -
    - -
    - -
    - - - -
    - -
    - - - - - - - - -{tmplinclude file="footer.phtml"} diff --git a/application/views/meeting/core.phtml b/application/views/meeting/core.phtml deleted file mode 100644 index 846e9e836..000000000 --- a/application/views/meeting/core.phtml +++ /dev/null @@ -1,128 +0,0 @@ - -
    - - {foreach from=$entries item=e} - - -
    - -
    -

    {$e->getTitle()} – {$e->getDate()->format( 'l, F jS, Y' )}

    -

    - In {if $e->getVenueUrl() neq ''}{$e->getVenue()}{else}{$e->getVenue()}{/if} at {$e->getTime()->format( 'H:i' )} - {if not $simple and $e->getVenueUrl() neq ''} -    - [LINK] - - {/if} -

    -
    - -
    {$e->getBeforeText()}
    - -
    - -
    - - {assign var='inOtherContent' value=0} - - {foreach $e->getMeetingItems() as $id => $mi} - - {if $mi->getOtherContent() and not $inOtherContent} -
    - -

    - Other meeting content also includes: -

    - -
    - - {assign var='inOtherContent' value=1} - {/if} - -
    - -
    - -
    - {if not $simple} - {if $mi->getPresentation() neq ''} - - [VIDEO] - - {/if} - {if $mi->getVideoUrl() neq ''} - - [VIDEO] - - {/if} - {/if} -
    - -

    - {$mi->getTitle()} – - - {if not $simple and $mi->getEmail() neq ''} - {mailto address=$mi->getEmail() encode='javascript' text=$mi->getName()} - {else} - {$mi->getName()} - {/if} -

    - -

    - {if $mi->getRole() neq ''}{$mi->getRole()}, {/if} - {if $mi->getCompanyUrl() neq ''} - {$mi->getCompany()} - {else} - {$mi->getCompany()} - {/if} -

    -
    - -
    - -
    - {$mi->getSummary()} -
    - - {/foreach} - -
    - -
    - -
    {$e->getAfterText()}
    - - {if not $simple and $smarty.now < $e->getDate()->format( 'U' ) and $user->getPrivs() eq 1} -
    - - {if $user->getPreference( 'meeting.attending.'|cat:$e->getId() ) eq 'ATTENDING' - or $user->getPreference( 'meeting.attending.'|cat:$e->getId() ) eq 'NOT_ATTENDING'} -

    - You have already RSVP'd for this meeting and told us that you will - {if $user->getPreference( 'meeting.attending.'|cat:$e->getId() ) eq 'NOT_ATTENDING'}not{/if} - be attending. Please email - {mailto text=$config.meeting.rsvp_to_name address=$config.meeting.rsvp_to_email} - directly to change this. -

    - {else} -

    - Please RSVP for this meeting: - - -

    - {/if} -
    - - - - {/if} - -
    - - {/foreach} - -
    diff --git a/application/views/meeting/email/meeting.phtml b/application/views/meeting/email/meeting.phtml deleted file mode 100644 index 1a1530b73..000000000 --- a/application/views/meeting/email/meeting.phtml +++ /dev/null @@ -1,213 +0,0 @@ - - - - - - - - - - - - - -
    - -
    - -
    {$body}
    - -
    -

    {$meeting->getTitle()} – {$meeting->getDate()->format( 'l, F jS, Y' )}

    -

    - In {$meeting->getVenue()} at {$meeting->getTime()->format( 'H:i' )} -

    -
    - -
    {$meeting->getBeforeText()}
    - -
    - -
    - - {assign var='inOtherContent' value=0} - - {foreach $meeting->getMeetingItems() as $mi} - - {if $mi->getOtherContent() and not $inOtherContent} -
    - -

    - Other meeting content also includes: -

    - -
    - - {assign var='inOtherContent' value=1} - {/if} - -
    - -
    - -
    -
    - -

    - {$mi->getTitle()} – - - {if $mi->getEmail() neq ''} - {mailto address=$mi->getEmail() encode='none' text=$mi->getName()} - {else} - {$mi->getName()} - {/if} -

    - -

    - {if $mi->getRole() neq ''}{$mi->getRole()}, {/if} - {if $mi->getCompanyUrl() neq ''} - {$mi->getCompany()} - {else} - {$mi->getCompany()} - {/if} -

    -
    - -
    - -
    - {$mi->getSummary()} -
    - - {/foreach} - -
    - -
    - -
    {$meeting->getAfterText()}
    - - -
    -
    - - - diff --git a/application/views/meeting/js/add.js b/application/views/meeting/js/add.js deleted file mode 100644 index c4869e9ce..000000000 --- a/application/views/meeting/js/add.js +++ /dev/null @@ -1,12 +0,0 @@ - -$(function() { - $('#before_text').wysiwyg(); - $('#after_text').wysiwyg(); - - $('#date').datepicker({ - changeMonth: true, - changeYear: true, - dateFormat: 'yy-mm-dd' - }); -}); - diff --git a/application/views/meeting/js/core.js b/application/views/meeting/js/core.js deleted file mode 100644 index 3438fac72..000000000 --- a/application/views/meeting/js/core.js +++ /dev/null @@ -1,41 +0,0 @@ - -$( '#rsvp_attending_{$e->getId()}' ).click( function() { - - $( '#rsvp_div_{$e->getId()}' ).hide( 400 ); - $( '#rsvp_div_{$e->getId()}' ).html(''); - - $.getJSON( "{genUrl controller='meeting' action='rsvp'}/id/{$e->getId()}/answer/attend", - function( data ) { - switch( data['response'] ) - { - case 1: - alert( "We have recorded your intention to attend this meeting. Thanks!" ); - break; - case 0: - alert( "ERROR: We could no record your intention to attend this meeting. Please email {$config.meeting.rsvp_to_name} directly at {$config.meeting.rsvp_to_email}." ); - break; - } - } - ); -}); - -$( '#rsvp_not_attending_{$e->getId()}' ).click( function() { - - $( '#rsvp_div_{$e->getId()}' ).hide( 400 ); - $( '#rsvp_div_{$e->getId()}' ).html(''); - - $.getJSON( "{genUrl controller='meeting' action='rsvp'}/id/{$e->getId()}/answer/noattend", - function( data ) { - switch( data['response'] ) - { - case 1: - alert( "We have recorded your intention to not attend this meeting. We hope you can make the next one." ); - break; - case 0: - alert( "ERROR: We could no record your intention to not attend this meeting. Please email {$config.meeting.rsvp_to_name} directly at {$config.meeting.rsvp_to_email}." ); - break; - } - } - ); -}); - diff --git a/application/views/meeting/list-row-menu.phtml b/application/views/meeting/list-row-menu.phtml deleted file mode 100644 index 0f1081ef6..000000000 --- a/application/views/meeting/list-row-menu.phtml +++ /dev/null @@ -1,17 +0,0 @@ - diff --git a/application/views/meeting/read.phtml b/application/views/meeting/read.phtml deleted file mode 100644 index 08f8dd4a5..000000000 --- a/application/views/meeting/read.phtml +++ /dev/null @@ -1,54 +0,0 @@ -{tmplinclude file="header.phtml"} - -{if $user->getPrivs() eq USER::AUTH_SUPERUSER} - -{else} -
    - - -{/if} - -{OSS_Message} - -
    -

    -

    - Jump to:  - - -
    -

    -
    - - - -{tmplinclude file='meeting/core.phtml'} - - -{if $user->getPrivs() neq USER::AUTH_SUPERUSER} -
    -{/if} - -{tmplinclude file="footer.phtml"} diff --git a/application/views/meeting/simple.phtml b/application/views/meeting/simple.phtml deleted file mode 100644 index ab4ba2c3c..000000000 --- a/application/views/meeting/simple.phtml +++ /dev/null @@ -1,130 +0,0 @@ - - - -

    - - This is public list of INEX Members' Meetings. INEX members should log into the IXP Manager - where they can download presentations, view recorded presentations and access speaker contact - details. - -

    - -{tmplinclude file='meeting/core.phtml'} - diff --git a/application/views/meeting/simple2.phtml b/application/views/meeting/simple2.phtml deleted file mode 100644 index 7933f2adf..000000000 --- a/application/views/meeting/simple2.phtml +++ /dev/null @@ -1,64 +0,0 @@ -

    - - This is public list of INEX Members' Meetings. INEX members should log into the IXP Manager - where they can download presentations and view recorded presentations that were requested - to not be made public as well as access speaker contact details. - -

    - - - -{foreach from=$entries item=e} - -

    {$e->getTitle()} – {$e->getDate()->format( 'l, F jS, Y' )}

    - -

    In {if $e->getVenueUrl() neq ''}{$e->getVenue()}{else}{$e->getVenue()}{/if} at {$e->getTime()->format( 'H:i' )}

    - -
    {$e->getBeforeText()}
    - -
    - - {assign var='inOtherContent' value=0} - - {foreach $e->getMeetingItems() as $id => $mi} - - {if $mi->getOtherContent() and not $inOtherContent} -
    - -

    - Other meeting content also includes: -

    - -
    - - {assign var='inOtherContent' value=1} - {/if} - -
    -

    - {$mi->getName()}: {if $mi->getRole() neq ''}{$mi->getRole()}, {/if}{if $mi->getCompanyUrl() neq ''}{$mi->getCompany()}{else}{$mi->getCompany()}{/if} -

    -
    - -
    - {$mi->getTitle()} - - {if $mi->getPresentation() neq ''} -     [PRES] - {/if} - {if $mi->getVideoUrl() neq ''} -     [VIDEO] - {/if} - - {if $mi->getSummary() neq ''} {$mi->getSummary()}{/if} -
    - - {/foreach} - -
    - - -
    {$e->getAfterText()}
    - -{/foreach} - diff --git a/application/views/menu.phtml b/application/views/menu.phtml deleted file mode 100644 index dff30356d..000000000 --- a/application/views/menu.phtml +++ /dev/null @@ -1,154 +0,0 @@ - -
    -
    - -
    -
    diff --git a/application/views/peering-manager/index-peers.phtml b/application/views/peering-manager/index-peers.phtml deleted file mode 100644 index 7a52558df..000000000 --- a/application/views/peering-manager/index-peers.phtml +++ /dev/null @@ -1,14 +0,0 @@ - - -

    - You exchange routes by some mechanism (router server and / or bilateral peerings) with the following members. -

    - -

    - Any members shown with a red badge indicates that you can potentially improve your peering - with that member on the shown LAN and protocol. -

    - -{assign var=listOfCusts value=$peered} -{include file='peering-manager/index-table.phtml'} - diff --git a/application/views/peering-manager/index-potential-bilateral.phtml b/application/views/peering-manager/index-potential-bilateral.phtml deleted file mode 100644 index 97467fd9d..000000000 --- a/application/views/peering-manager/index-potential-bilateral.phtml +++ /dev/null @@ -1,19 +0,0 @@ - - -

    - Using INEX's redundant route server server means that you do not need to goto the effort of - establishing bilateral peering sessions with each member of the exchange. -

    - -

    - Should you wish to not use the route servers or prefer direct peering also, then the following - table shows the members that we have failed to detect a bilateral peering session with you. -

    - -

    - Any members shown with a green badge indicates that you exchange routes with that member via the route servers. -

    - -{assign var=listOfCusts value=$potential_bilat} -{include file='peering-manager/index-table.phtml'} - diff --git a/application/views/peering-manager/index-potential.phtml b/application/views/peering-manager/index-potential.phtml deleted file mode 100644 index 0a3e65c90..000000000 --- a/application/views/peering-manager/index-potential.phtml +++ /dev/null @@ -1,15 +0,0 @@ - - -

    - You currently do not exchange any routes in any way with the following members of the exchange - over the highlighted - in red - protocol(s) and LAN(s) because: -

    - -
      -
    • either you, they or both of you are not route server clients; and
    • -
    • you do not have a bilateral (direct) peering session that we have detected with them.
    • -
    - -{assign var=listOfCusts value=$potential} -{include file='peering-manager/index-table.phtml'} - diff --git a/application/views/peering-manager/index-rejected.phtml b/application/views/peering-manager/index-rejected.phtml deleted file mode 100644 index f8b4abce7..000000000 --- a/application/views/peering-manager/index-rejected.phtml +++ /dev/null @@ -1,11 +0,0 @@ - - -

    -Any peers appearing below are here because you (or one of your colleagues) selected to have -them ignored in the drop down actions. -

    - - -{assign var=listOfCusts value=$rejected} -{include file='peering-manager/index-table.phtml'} - diff --git a/application/views/peering-manager/index-table.phtml b/application/views/peering-manager/index-table.phtml deleted file mode 100644 index d65a22899..000000000 --- a/application/views/peering-manager/index-table.phtml +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - - - - - - - {if isset( $me.vlaninterfaces.10 )} - - {/if} - - {if isset( $me.vlaninterfaces.12 )} - - {/if} - - - - - - - - {foreach from=$listOfCusts key=as item=p} - - {assign var=c value=$custs.$as} - {assign var=cid value=$c.id} - - {if $p} - - - - - - {foreach $vlans as $avlan} - {$vlan = $avlan->getNumber()} - {if isset( $c.$vlan )} - - {elseif isset( $me.vlaninterfaces.$vlan )} - - {/if} - {/foreach} - - - - {/if} - - {/foreach} - - -
    MemberASNPolicyLAN 1LAN 2
    {$c.name}{$c.autsys}{$c.peeringpolicy} - {foreach from=$protos item=proto} - {if isset( $c.$vlan.$proto )} - {if $c.$vlan.$proto} - IPv{$proto} - {else} - IPv{$proto} - {/if} - {/if} - {/foreach} - - -
    - - diff --git a/application/views/peering-manager/index.phtml b/application/views/peering-manager/index.phtml deleted file mode 100644 index 4f63c20bf..000000000 --- a/application/views/peering-manager/index.phtml +++ /dev/null @@ -1,82 +0,0 @@ -{tmplinclude file="header.phtml" pageTitle="IXP Manager :: Peering Manager"} - - - -
    - -{OSS_Message} - - - -
    - -
    - {tmplinclude file="peering-manager/index-potential.phtml"} -
    - -
    - {tmplinclude file="peering-manager/index-potential-bilateral.phtml"} -
    - -
    - {tmplinclude file="peering-manager/index-peers.phtml"} -
    - -
    - {tmplinclude file="peering-manager/index-rejected.phtml"} -
    - -
    - - - - - -
    - - - -{tmplinclude file="footer.phtml"} diff --git a/application/views/peering-manager/js/index.js b/application/views/peering-manager/js/index.js deleted file mode 100644 index f418eb2ed..000000000 --- a/application/views/peering-manager/js/index.js +++ /dev/null @@ -1,227 +0,0 @@ - -$( '#modal-peering-request' ).modal( { 'show': false, 'keyboard': false } ); -$( '#modal-peering-notes' ).modal( { 'show': false, 'keyboard': false } ); - -function ixpOpenPeeringRequestDialog( custid ) { - // make sure we're "clean" - $( '#modal-peering-request-body' ).html( "

    Please wait... loading...

    " ); - - $( '#modal-peering-request-footer-close' ).removeAttr( 'disabled' ).removeClass( 'disabled' ); - $( '#modal-peering-request-footer-send' ).removeAttr( 'disabled' ).removeClass( 'disabled' ); - $( '#modal-peering-request-footer-marksent' ).removeAttr( 'disabled' ).removeClass( 'disabled' ); - $( '#modal-peering-request-footer-sendtome' ).removeAttr( 'disabled' ).removeClass( 'disabled' ); - - $( '#modal-peering-request' ).modal( 'show' ); - - $.get( '{genUrl controller="peering-manager" action="peering-request"}/custid/' + custid, function( data ) { - - if( substr( data, 0, 4 ) == 'ERR:' ) { - bootbox.dialog( substr( data, 4 ), { "OK": function() {} }, { "animate": false } ); - $( '#modal-peering-request' ).modal( 'hide' ); - return; - } - - if( substr( data, 0, 11 ) != '' ) { - bootbox.dialog( "Unexpected error. Please contact support.", { "OK": function() {} }, { "animate": false } ); - $( '#modal-peering-request' ).modal( 'hide' ); - return; - } - - $( '#modal-peering-request-body' ).html( data ); - $( '#peering-request-form-custid' ).val( custid ); - }); -} - -function ixpSendPeeringRequest( event ) { - - $( '#modal-peering-request-footer-close' ).attr('disabled', 'disabled' ).addClass( 'disabled' ); - $( '#modal-peering-request-footer-sendtome' ).attr('disabled', 'disabled' ).addClass( 'disabled' ); - $( '#modal-peering-request-footer-marksent' ).attr('disabled', 'disabled' ).addClass( 'disabled' ); - $( '#modal-peering-request-footer-send' ).attr('disabled', 'disabled' ).addClass( 'disabled' ); - - // close all tooltips - $("[rel=tooltip]").tooltip( 'hide' ); - - var Throb = tt_throbberWithOverlay( 200, 15, 5, "#peering-request-container" ); - var custid = $( '#peering-request-form-custid' ).val(); - - $.post( '{genUrl controller="peering-manager" action="peering-request"}', $( '#peering-request-form' ).serialize(), function( data ) { - - if( substr( data, 0, 4 ) == 'ERR:' ) { - bootbox.dialog( substr( data, 4 ), { "OK": function() { - Throb.stop(); - $("#overlay").fadeOut( "slow", function(){ $("#overlay").remove(); }); - $( '#modal-peering-request-footer-close' ).removeAttr( 'disabled' ).removeClass( 'disabled' ); - $( '#modal-peering-request-footer-sendtome' ).attr('disabled', 'disabled' ).addClass( 'disabled' ); - $( '#modal-peering-request-footer-marksent' ).attr('disabled', 'disabled' ).addClass( 'disabled' ); - $( '#modal-peering-request-footer-send' ).removeAttr( 'disabled' ).removeClass( 'disabled' ); - } }, { "animate": false } - ); - return; - } - - if( substr( data, 0, 3 ) == 'OK:' ) { - Throb.stop(); - $("#overlay").remove(); - $( '#modal-peering-request' ).modal( 'hide' ); - - if( $( '#peering-request-form-sendtome' ).val() == '0' ) { - $( '#peering-request-' + custid ).attr( 'data-days', 0 ); - $( '#peering-request-icon-' + custid ).attr( 'class', 'icon-repeat' ); - $( '#peering-notes-icon-' + custid ).attr( 'class', 'icon-star' ); - } - - bootbox.alert( substr( data, 3 ) ); - return; - } - - $( '#modal-peering-request-body' ).html( data ); - Throb.stop(); - $("#overlay").fadeOut( "slow", function(){ $("#overlay").remove(); }); - $( '#modal-peering-request-footer-close' ).removeAttr( 'disabled' ).removeClass( 'disabled' ); - $( '#modal-peering-request-footer-sendtome' ).attr('disabled', 'disabled' ).addClass( 'disabled' ); - $( '#modal-peering-request-footer-marksent' ).attr('disabled', 'disabled' ).addClass( 'disabled' ); - $( '#modal-peering-request-footer-send' ).removeAttr( 'disabled' ).removeClass( 'disabled' ); - - - }); -} - -$(document).ready( function() { - - $( '#modal-peering-request-footer-close' ).on( 'click', function( event ){ - $( '#modal-peering-request' ).modal( 'hide' ); - }); - - $( '#modal-peering-notes-footer-close' ).on( 'click', function( event ){ - $( '#modal-peering-notes' ).modal( 'hide' ); - }); - - $( 'button[id|="peering-request"]' ).on( 'click', function( event ){ - - var custid = substr( event.target.id, 16 ); - - var days = $( '#' + event.target.id ).attr( 'data-days' ); - if( days >= 0 && days < 30 ) { - bootbox.confirm( "Are you sure you want to send a peering request to this member? You already sent one only " + days + " days ago.", - function( result ) { - if( result ) { - return ixpOpenPeeringRequestDialog( custid ); - } else { - return; - } - } - ); - } - else - ixpOpenPeeringRequestDialog( custid ); - }); - - $( 'button[id|="peering-notes"]' ).on( 'click', function( event ){ - - var custid = substr( event.target.id, 14 ); - - // make sure we're "clean" - $( '#modal-peering-notes-message' ).val( "Please wait... loading..." ); - - $( '#modal-peering-notes-footer-close' ).removeAttr( 'disabled' ).removeClass( 'disabled' ); - $( '#modal-peering-notes-footer-save' ).removeAttr( 'disabled' ).removeClass( 'disabled' ); - $( '#modal-peering-notes-message' ).attr( 'disabled', 'disabled' ).addClass( 'disabled' ); - - $( '#modal-peering-notes-header-h3' ).html( "Peering Notes for " + $( '#peer-name-' + custid ).html() ); - - $( '#modal-peering-notes' ).modal( 'show' ); - - $.get( '{genUrl controller="peering-manager" action="peering-notes"}/custid/' + custid, function( data ) { - - if( substr( data, 0, 4 ) == 'ERR:' ) { - bootbox.dialog( substr( data, 4 ), { "OK": function() {} }, { "animate": false } ); - $( '#modal-peering-notes' ).modal( 'hide' ); - return; - } - - if( substr( data, 0, 3 ) != 'OK:' ) { - bootbox.dialog( "Unexpected error. Please contact support.", { "OK": function() {} }, { "animate": false } ); - $( '#modal-peering-notes' ).modal( 'hide' ); - return; - } - - if( strlen( data ) > 3 ) - $( '#modal-peering-notes-message' ).val( substr( data, 3 ) ); - else - $( '#modal-peering-notes-message' ).val( '' ); - - $( '#modal-peering-notes-message' ).off( 'focus' ); - $( '#modal-peering-notes-message' ).one( 'focus', function( event ){ - var prmt = '{$date} [{$user->getUsername()}]: '; - $( '#modal-peering-notes-message' ).val( prmt + "\n\n" + $( '#modal-peering-notes-message' ).val() ); - $( '#modal-peering-notes-message' ).caretTo( strlen( prmt ) ); - }); - - - $( '#modal-peering-notes-custid' ).val( custid ); - $( '#modal-peering-notes-message' ).removeAttr( 'disabled' ).removeClass( 'disabled' ); - }); - }); - - $( '#modal-peering-notes-footer-save' ).on( 'click', function( event ) { - $( '#modal-peering-notes-footer-close' ).attr('disabled', 'disabled' ).addClass( 'disabled' ); - $( '#modal-peering-notes-footer-save' ).attr('disabled', 'disabled' ).addClass( 'disabled' ); - - var Throb = tt_throbberWithOverlay( 200, 15, 5, "#peering-notes-container" ); - var custid = $( '#modal-peering-notes-custid' ).val(); - - $.post( '{genUrl controller="peering-manager" action="peering-notes"}', $( '#peering-notes-form' ).serialize(), function( data ) { - - if( substr( data, 0, 4 ) == 'ERR:' ) { - bootbox.dialog( substr( data, 4 ), { "OK": function() { - Throb.stop(); - $("#overlay").fadeOut( "slow", function(){ $("#overlay").remove(); }); - $( '#modal-peering-notes-footer-close' ).removeAttr( 'disabled' ).removeClass( 'disabled' ); - $( '#modal-peering-notes-footer-save' ).removeAttr( 'disabled' ).removeClass( 'disabled' ); - } }, { "animate": false } - ); - return; - } - - if( substr( data, 0, 3 ) == 'OK:' ) { - Throb.stop(); - $("#overlay").remove(); - $( '#modal-peering-notes' ).modal( 'hide' ); - $( '#peering-notes-icon-' + custid ).attr( 'class', 'icon-star' ); - - bootbox.alert( substr( data, 3 ) ); - return; - } - - Throb.stop(); - $("#overlay").fadeOut( "slow", function(){ $("#overlay").remove(); }); - $( '#modal-peering-notes-footer-close' ).removeAttr( 'disabled' ).removeClass( 'disabled' ); - $( '#modal-peering-notes-footer-save' ).removeAttr( 'disabled' ).removeClass( 'disabled' ); - - }); - }); - - - - - $( '#modal-peering-request-footer-send' ).on( 'click', function( event ){ - $( '#peering-request-form-sendtome' ).val( '0' ); - $( '#peering-request-form-marksent' ).val( '0' ); - ixpSendPeeringRequest( event ); - }); - - $( '#modal-peering-request-footer-sendtome' ).on( 'click', function( event ){ - $( '#peering-request-form-sendtome' ).val( '1' ); - $( '#peering-request-form-marksent' ).val( '0' ); - ixpSendPeeringRequest( event ); - }); - - $( '#modal-peering-request-footer-marksent' ).on( 'click', function( event ){ - $( '#peering-request-form-sendtome' ).val( '0' ); - $( '#peering-request-form-marksent' ).val( '1' ); - ixpSendPeeringRequest( event ); - }); - - -}); diff --git a/application/views/peering-manager/peering-request-form.phtml b/application/views/peering-manager/peering-request-form.phtml deleted file mode 100644 index 9fe88e921..000000000 --- a/application/views/peering-manager/peering-request-form.phtml +++ /dev/null @@ -1,12 +0,0 @@ - - -{$element->to} -{$element->cc} -{$element->bcc} -{$element->subject} -{$element->message} - -{$element->custid} -{$element->marksent} -{$element->sendtome} - diff --git a/application/views/peering-manager/peering-request-message.phtml b/application/views/peering-manager/peering-request-message.phtml deleted file mode 100644 index b1404b2bc..000000000 --- a/application/views/peering-manager/peering-request-message.phtml +++ /dev/null @@ -1,56 +0,0 @@ - -Dear {$peer->getName()} Peering Team, - -We are {$user->getCustomer()->getName()} ({$user->getCustomer()->getCorpwww()}) and we are fellow members of INEX, Ireland's IXP. - -We would like to arrange peering session(s) with you on the following interface(s): - -{foreach $pp as $p} - -{assign value=$p.my var='pmy'} -{assign value=$p.your var='pyour'} - -{$pmy->getVlan()->getName()|underline} - -{if $pmy->getIpv4enabled() and $pyour->getIpv4enabled()} -Our IPv4 Address: {$pmy->getIpv4address()->getAddress()} -{if $pmy->getIpv6enabled() and $pyour->getIpv6enabled()} -Our IPv6 Address: {$pmy->getIpv6address()->getAddress()} -{/if} -Our AS Number: {$user->getCustomer()->getAutsys()} -{if $user->getCustomer()->getPeeringmacro()}Our AS Macro: {$user->getCustomer()->getPeeringmacro()} -{/if} - -Your IPv4 Address: {$pyour->getIpv4address()->getAddress()} -{if $pmy->getIpv6enabled() and $pyour->getIpv6enabled()} -Your IPv6 Address: {$pyour->getIpv6address()->getAddress()} -{/if} -Your AS Number: {$peer->getAutsys()} -{/if} - -{/foreach} - - -{"NOC Details for "|cat:$user->getCustomer()->getName()|underline:"="} - -The following are our NOC details for your reference: - -NOC Hours: {$user->getCustomer()->getNochours()} -NOC Phone: {$user->getCustomer()->getNocphone()} -{if $user->getCustomer()->getNoc24hphone()}NOC 24h Phone: {$user->getCustomer()->getNoc24hphone()} -{/if} -{if $user->getCustomer()->getNocfax()}NOC Fax: {$user->getCustomer()->getNocfax()} -{/if} -NOC Email: {$user->getCustomer()->getNocemail()} -{if $user->getCustomer()->getNocwww()}NOC WWW: {$user->getCustomer()->getNocwww()}{/if} - - -Kind regards, -The {$user->getCustomer()->getName()} Peering Team - - --- - -INEX (https://www.inex.ie/) is Ireland's IXP. This email was composed with the assistance of INEX's Peering Manager which is part of your member area at: {genUrl}. - - diff --git a/application/views/peering-manager/peering-request.phtml b/application/views/peering-manager/peering-request.phtml deleted file mode 100644 index cddd7bb52..000000000 --- a/application/views/peering-manager/peering-request.phtml +++ /dev/null @@ -1,4 +0,0 @@ - -
    -{if isset( $form )}{$form}{/if} -
    diff --git a/application/views/peering-matrix/index.js b/application/views/peering-matrix/index.js deleted file mode 100644 index d75f3dd85..000000000 --- a/application/views/peering-matrix/index.js +++ /dev/null @@ -1,202 +0,0 @@ - -sessions = {$jsessions}; - -custs = {$jcusts}; - -jQuery.expr[':'].regex = function(elem, index, match) { - var matchParams = match[3].split(','), - validLabels = /^(data|css):/, - attr = { - method: matchParams[0].match(validLabels) ? - matchParams[0].split(':')[0] : 'attr', - property: matchParams.shift().replace(validLabels,'') - }, - regexFlags = 'ig', - regex = new RegExp(matchParams.join('').replace(/^\s+|\s+$/g,''), regexFlags); - return regex.test(jQuery(elem)[attr.method](attr.property)); -} - -$( 'document' ).ready( function(){ - - columnClicked = false; - mouseLocked = false; - - $( "#table-pm" ).delegate( 'td', 'mouseover mouseout click', function( event ) { - - if( columnClicked ) return; - - if( this.id.indexOf( 'td-asn-' ) == 0 ) return; - if( this.id.indexOf( 'td-name-' ) == 0 ) return; - - var yasn = this.id.substr( this.id.lastIndexOf( '-' ) + 1 ); - var xasn = this.id.substr( 3, this.id.lastIndexOf( '-' ) - 3 ); - - if( event.type == 'mouseover' && !mouseLocked) - { - //$(this).parent().addClass( "hover" ); - $( "colgroup" ).eq( $(this ).index() ).addClass("hover"); - $( '.col-yasn-' + yasn ).addClass( 'hover2' ); - - $( '#td-name-' + xasn ).addClass( "highlight2" ); - $( '#td-asn-' + xasn ).addClass( "highlight2" ); - - $( '#td-name-' + yasn ).addClass( "highlight" ); - $( '#td-asn-' + yasn ).addClass( "highlight" ); - $( '#th-as-' + yasn ).addClass( "highlight" ); - } - else if( event.type == 'click' ) - { - if( mouseLocked ) - { - //$("#tbody-pm").find( "tr" ).removeClass( "hover" ); - $("#table-pm").find( "colgroup" ).removeClass( "hover" ); - $( '.col-yasn-' + yasn ).removeClass( 'hover2' ); - - $( '[id|="td-name"]' ).removeClass( 'highlight' ); - $( '[id|="td-asn"]' ).removeClass( 'highlight' ); - $( '[id|="td-name"]' ).removeClass( 'highlight2' ); - $( '[id|="td-asn"]' ).removeClass( 'highlight2' ); - $( '[id|="th"]' ).removeClass( 'highlight' ); - mouseLocked = false; - } - else - mouseLocked = true; - } - else if( event.type == 'mouseout' && !mouseLocked) - { - //$(this).parent().removeClass("hover"); - - $("colgroup").eq($(this).index()).removeClass("hover"); - - $( '.col-yasn-' + yasn ).removeClass( 'hover2' ); - - $( '#td-name-' + xasn ).removeClass( "highlight2" ); - $( '#td-asn-' + xasn ).removeClass( "highlight2" ); - - $( '#td-name-' + yasn ).removeClass( "highlight" ); - $( '#td-asn-' + yasn ).removeClass( "highlight" ); - $( '#th-as-' + yasn ).removeClass( "highlight" ); - } - }); - - $( "#table-pm" ).delegate( 'th', 'mouseover mouseout', function( event ) { - - if( columnClicked ) return; - if( this.id == 'th-asn' ) return; - if( this.id == 'th-name' ) return; - - if( event.type == 'mouseover' && !mouseLocked ) - { - var yasn = this.id.substr( this.id.lastIndexOf( '-' ) + 1 ); - $( '#td-name-' + yasn ).addClass( "highlight" ); - $( '#td-asn-' + yasn ).addClass( "highlight" ); - $( '#th-as-' + yasn ).addClass( "highlight" ); - } - else if( event.type == 'mouseout' && !mouseLocked ) - { - var yasn = this.id.substr( this.id.lastIndexOf( '-' ) + 1 ); - $( '#td-name-' + yasn ).removeClass( "highlight" ); - $( '#td-asn-' + yasn ).removeClass( "highlight" ); - $( '#th-as-' + yasn ).removeClass( "highlight" ); - } - }); - - - $( '[id|="th-as"]' ).on( "click", function( e ){ - - if( columnClicked ) { - columnClicked = false; - $( '[id|="th-as"]' ).show(); - $( 'td:regex(id,td\-[0-9]+\-[0-9]+)' ).show(); - - var yasn = this.id.substr( this.id.lastIndexOf( '-' ) + 1 ); - $( '#td-name-' + yasn ).removeClass( "highlight" ); - $( '#td-asn-' + yasn ).removeClass( "highlight" ); - $( '#th-as-' + yasn ).removeClass( "highlight" ); - } - else { - columnClicked = true; - clickedCol = this; - clickedAsn = this.id.substr( this.id.lastIndexOf( '-' ) + 1 ); - - $( '[id|="th-as"]' ).each( function( index, element ) { - asn = this.id.substr( this.id.lastIndexOf( '-' ) + 1 ); - - if( asn == clickedAsn ) - return; - - $( '#th-as-' + asn ).hide(); - $( 'td:regex(id,td\-[0-9]+\-' + asn + ')' ).hide(); - - }); - - $( '#td-name-' + clickedAsn ).addClass( "highlight" ); - $( '#td-asn-' + clickedAsn ).addClass( "highlight" ); - $( '#th-as-' + clickedAsn ).addClass( "highlight" ); - } - - }); - - $( '[id|="btn-zoom"]' ).on( "click", function( e ){ - var i, zoom = 0; - for( i = 1; i <= 5; i++ ) - { - if( $( '#tbody-pm' ).hasClass( 'zoom' + i ) ) - { - zoom = i; - break; - } - } - - if( zoom != 0 ) - { - var nzoom = ( this.id == 'btn-zoom-out' ) ? zoom - 1 : zoom + 1; - if( nzoom > 5 ) nzoom = 5; - if( nzoom < 1 ) nzoom = 1; - - $( '.zoom' + zoom ).removeClass( 'zoom' + zoom ).addClass( 'zoom' + nzoom ); - } - }); - - - $( '[id|="peer-filter"]' ).on( "click", function( e ){ - var filter = this.id.substr( this.id.lastIndexOf( '-' ) + 1 ); - - $( 'td.bilateral-rs' ).removeClass( 'peered' ); - $( 'td.bilateral-only' ).removeClass( 'peered' ); - $( 'td.rs-only' ).removeClass( 'peered' ); - $( 'td.bilateral-only' ).removeClass( 'not-peered' ); - $( 'td.rs-only' ).removeClass( 'not-peered' ); - - switch( filter ) { - case 'all': - $( 'td.bilateral-rs' ).addClass( 'peered' ); - $( 'td.bilateral-only' ).addClass( 'peered' ); - $( 'td.rs-only' ).addClass( 'peered' ); - $( '#peer-dd-text' ).html( 'All Peerings' ); - break; - - case 'bi': - $( 'td.bilateral-rs' ).addClass( 'peered' ); - $( 'td.bilateral-only' ).addClass( 'peered' ); - $( 'td.rs-only' ).addClass( 'not-peered' ); - $( '#peer-dd-text' ).html( 'Bilateral Peerings' ); - break; - - case 'rs': - $( 'td.bilateral-rs' ).addClass( 'peered' ); - $( 'td.bilateral-only' ).addClass( 'not-peered' ); - $( 'td.rs-only' ).addClass( 'peered' ); - $( '#peer-dd-text' ).html( 'Route Server Peerings' ); - break; - - } - - $( '#peer-btn-group' ).removeClass('open'); - - return false; - }); - - -}); - diff --git a/application/views/peering-matrix/index.phtml b/application/views/peering-matrix/index.phtml deleted file mode 100644 index bcb7d5f10..000000000 --- a/application/views/peering-matrix/index.phtml +++ /dev/null @@ -1,193 +0,0 @@ -{if isset( $hasIdentity ) and $hasIdentity} - {tmplinclude file="header.phtml" mode="fluid"} -{else} - {tmplinclude file="header.phtml" mode="fluid" brand="INEX - Internet Neutral Exchange - Peering Matrix"} -{/if} - - - -
    - - - - - - - -{foreach from=$custs key=x_as item=peers} - -{/foreach} - - - - - - - - - {foreach from=$custs key=x_as item=peers} - - - - {/foreach} - - - - - - - -{assign var=outer value=0} - -{foreach from=$custs key=x_as item=x} - - - - - - - - {assign var=inner value=0} - - {foreach from=$custs key=y_as item=y} - - - - {assign var=inner value=$inner+1} - - {* for the last cell of the last row, we add a empty cell *} - {if $outer eq $custs|@count and $inner eq $custs|@count} - - {/if} - {/foreach} - - - -{assign var=outer value=$outer+1} - -{/foreach} - - - -
    - {assign var=asn value=$x_as|string_format:$asnStringFormat} - {assign var=len value=strlen( $asn )} - {for $pos=0 to $len} - {$asn|truncate:1:''}{if $pos < $len}
    {/if} - {assign var=asn value=substr( $asn, 1 )} - {/for} -
    {$x.name|replace:" ":" "}{$x.autsys} -
    - -
    - -
     
    - -
    - -
    - -

    Notes on the Peering Matrix

    - -
      -
    • - Clicking the AS number in the table header will isolate that column. Clicking individual - cells in the body will freeze the dynamic highlighting. -
    • -
    • - Where an INEX member is not listed on this peering matrix, it is because they are - currently not actively peering at INEX, or because they have opted out of presenting - their peering information in this database. -
    • -
    • - This peering matrix is based on Netflow traffic accounting data from the INEX peering - LANs and route server BGP peerings. -
    • -
    • - This peering matrix only detects if there is bidirectional TCP flow between routers at - INEX. It cannot detect whether there are actually prefixes swapped betwen routers. -
    • -
    - -
    - -
    - -
     
    - -
    - -{if isset( $user ) and $user->getPrivs() eq 3} -
    -{/if} - - - -{tmplinclude file="footer.phtml"} - diff --git a/application/views/physical-interface/js/add.js b/application/views/physical-interface/js/add.js deleted file mode 100644 index 7c24e3acd..000000000 --- a/application/views/physical-interface/js/add.js +++ /dev/null @@ -1,33 +0,0 @@ - -$( "#switchid" ).on( 'change', function( event ) { - - $( "#switchid" ).attr( 'disabled', 'disabled' ); - - if( $(this).val() != '0' ) { - tt_chosenClear( "#switchportid", "" ); - - $.getJSON( "{genUrl controller='switch-port' action='ajax-get'}/id/" - + $( "#preselectPhysicalInterface" ).val() + "/switchid/" + $(this).val(), function( j ) { - - var options = "\n"; - - for( var i = 0; i < j.length; i++ ) - options += "\n"; - - // do we have a preselect? - if( $( "#preselectSwitchPort" ).val() ) { - tt_chosenSet( "#switchportid", options, $( "#preselectSwitchPort" ).val() ); - } else { - tt_chosenSet( "#switchportid", options ); - } - }); - } - - $("#switchid").removeAttr( 'disabled' ); - -}); - -$(document).ready(function(){ - // trigger a change on switch ID to populate ports - $("#switchid").trigger( 'change' ); -}); diff --git a/application/views/physical-interface/list-row-menu.phtml b/application/views/physical-interface/list-row-menu.phtml deleted file mode 100644 index 656d51ddd..000000000 --- a/application/views/physical-interface/list-row-menu.phtml +++ /dev/null @@ -1,6 +0,0 @@ -
    - - - - -
    diff --git a/application/views/physical-interface/list-toolbar.phtml b/application/views/physical-interface/list-toolbar.phtml deleted file mode 100644 index c2353d137..000000000 --- a/application/views/physical-interface/list-toolbar.phtml +++ /dev/null @@ -1,4 +0,0 @@ -
  • -
    -
    -
  • diff --git a/application/views/physical-interface/view-toolbar.phtml b/application/views/physical-interface/view-toolbar.phtml deleted file mode 100644 index 5b3f6be58..000000000 --- a/application/views/physical-interface/view-toolbar.phtml +++ /dev/null @@ -1,6 +0,0 @@ -
    - - {if !isset( $feParams->readonly ) or !$feParams->readonly} - - {/if} -
    diff --git a/application/views/profile/index.phtml b/application/views/profile/index.phtml deleted file mode 100644 index 78512f3fe..000000000 --- a/application/views/profile/index.phtml +++ /dev/null @@ -1,96 +0,0 @@ -{tmplinclude file="header.phtml"} - -{if $user->getPrivs() eq 3} - -{else} -
    - -{/if} - -{OSS_Message} - -

    -This is your IXP Manager user profile where you can change you contact preferences and password. -

    - -
    -
    - -

    Change Your Profile

    -
    - {$profileForm} - -
    -
    - -

    Change Your Password

    -
    - {$passwordForm} - -
    -
    - -{if isset( $options.mailinglist.enabled ) and $options.mailinglist.enabled} -
    -
    -

    Your Mailing List Subscriptions

    -

    -
    - {$options.identity.orgname} operates the below mailing lists to help us interact with our - members and for our members to interact with each other. -

    -

    - There are also links below to the list archives - for which your username is - {$user->getEmail()} and your password is the same as your IXP Manager password. -

    -

    - The below are your subscriptions for {$user->getEmail()}. -

    -
    -
    -
    - - {foreach $options.mailinglists as $name => $ml} - - {if $user->getCustomer()->getType() neq CUSTOMER::TYPE_ASSOCIATE or ( isset( $ml.associates ) and $ml.associates )} - -
    - -
    - {/if} - - {/foreach} - -
    - -
    - -
    -
    - -
    - -
    -   -
    -
    -{/if} - -{if not isset( $user ) or $user->getPrivs() neq 3} -
    -{/if} - -{tmplinclude file="footer.phtml"} diff --git a/application/views/staff-links.phtml b/application/views/staff-links.phtml deleted file mode 100644 index 781009d2c..000000000 --- a/application/views/staff-links.phtml +++ /dev/null @@ -1,10 +0,0 @@ -{* Override this file (via skinning) to add customer staff links for ADMINs *} - - - diff --git a/application/views/static/example.phtml b/application/views/static/example.phtml deleted file mode 100644 index 198e4f089..000000000 --- a/application/views/static/example.phtml +++ /dev/null @@ -1,39 +0,0 @@ -{tmplinclude file="header.phtml"} - -{if isset( $user ) and $user->getPrivs() eq 3} - -{else} -
    - -{/if} - -

    -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer tristique diam pretium ipsum fermentum ut sollicitudin mauris blandit. Mauris luctus, ipsum id feugiat bibendum, felis nulla dignissim sem, vehicula sollicitudin tortor turpis ut eros. Sed sit amet orci arcu, et varius velit. Vestibulum et nulla tortor, quis tincidunt augue. Vestibulum fringilla tempus faucibus. Donec non mauris neque. Nam urna odio, facilisis id tincidunt eu, dictum sed sapien. Pellentesque porttitor viverra nunc a porttitor. Fusce tempor, elit tempus dignissim accumsan, nisi metus eleifend ipsum, vel pellentesque lectus dolor at orci. Suspendisse potenti. -

    - -

    -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer tristique diam pretium ipsum fermentum ut sollicitudin mauris blandit. Mauris luctus, ipsum id feugiat bibendum, felis nulla dignissim sem, vehicula sollicitudin tortor turpis ut eros. Sed sit amet orci arcu, et varius velit. Vestibulum et nulla tortor, quis tincidunt augue. Vestibulum fringilla tempus faucibus. Donec non mauris neque. Nam urna odio, facilisis id tincidunt eu, dictum sed sapien. Pellentesque porttitor viverra nunc a porttitor. Fusce tempor, elit tempus dignissim accumsan, nisi metus eleifend ipsum, vel pellentesque lectus dolor at orci. Suspendisse potenti. -

    - -

    -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer tristique diam pretium ipsum fermentum ut sollicitudin mauris blandit. Mauris luctus, ipsum id feugiat bibendum, felis nulla dignissim sem, vehicula sollicitudin tortor turpis ut eros. Sed sit amet orci arcu, et varius velit. Vestibulum et nulla tortor, quis tincidunt augue. Vestibulum fringilla tempus faucibus. Donec non mauris neque. Nam urna odio, facilisis id tincidunt eu, dictum sed sapien. Pellentesque porttitor viverra nunc a porttitor. Fusce tempor, elit tempus dignissim accumsan, nisi metus eleifend ipsum, vel pellentesque lectus dolor at orci. Suspendisse potenti. -

    - - -{if not isset( $user ) or $user->getPrivs() neq 3} -
    -{/if} - -{tmplinclude file="footer.phtml"} diff --git a/application/views/static/meetings-instructions.phtml b/application/views/static/meetings-instructions.phtml deleted file mode 100644 index 19529ce1b..000000000 --- a/application/views/static/meetings-instructions.phtml +++ /dev/null @@ -1,159 +0,0 @@ -{tmplinclude file="header.phtml"} - - - -{OSS_Message} - -

    To add new meetings, you must do two things:

    - -
      -
    • add a new meeting via the menu Admin->Meetings->Add / Edit; and then
    • -
    • add all the meeting presentations via the menu Admin->Meetings->Presentations.
    • -
    - -

    -You can view how the meeting entries look by accessing the meeting page via either of -Admin -> Meetings -> Member View or Member Information -> Meetings. -

    - -

    Adding a New Meeting

    - -

    -The general layout of a meeting entry is shown below. In square brackets I have identified the -entires required in the Add New meeting form to show how and where they are used. -

    - -
    - -
    -

    [Title] – [Date]

    -

    In [Venue] at [Time]

    -
    - -

    [Preamble]

    - -

    - List of Presentations -

    - -

    - Other meeting content also includes: -

    - -

    - List of Other Presentations -

    - -

    [Postamble]

    - -
    - - -

    -Note that [Date] should be selected from the pop up and is entered as YYYY-MM-DD but -is formatted on the page to something such as: Thursday, June 24, 2010. -

    - -

    -When entering the venue, note that it will be preseeded by In. -

    - -

    -Look at existing entries as an example. To see the form for an existing entry, right -click on its table row and select Edit. -

    - - - -

    Adding Meeting Items

    - -The general format for a meeting item / presentation is shown below detailing how the entry is made up -from the form. - -
    - -
    -
    -
    - - - -

    - [Title] – [Name (with link to email address if provided] -

    - -

    - [Role], [Company with link to site of URL provided] -

    -
    -
    -
    - [Summary, if provided] -
    - -
    - -
    - -

    -Note the following: -

    - -
      -
    • If an email is provided, the presenter's name will be an email link;
    • -
    • If a company URL is provided (such as http://www.inex.ie/), the company name will link to the company site;
    • -
    • If a video link is provided, the video camera icon will be visable and link to the video;
    • -
    • If the Other Content? box is ticked, the presentation is shown under the Other meeting content text; -
    • If a presentation is uploaded, the projector icon will allow a user to download it.
    • -
    - -

    -Again, the best way to fully understand this is to look at entries for existing presentations. -

    - - -

    Viewing Meeting Information for Members

    - -

    -Members can view meeting details including links to presentations, videos and speaker email addresses through the IXP Manager at -Member Information -> Meetings. This page is automatically generated -based on the entries above. -

    - - - -

    Viewing Meeting Information for the Public

    - -

    -There is a publically access HTML feed of the meetings at https://www.inex.ie/ixp/meeting/simple. -This feed is then included in an iframe on the main INEX website at https://www.inex.ie/media/meetings-public. -Again, this page is automatically generated from the entries above but links to presentations, video and email addresses of speakers are not included. -

    - -

    Composing Meeting Information Emails

    - -

    -One is often required to send information of a meeting to someone by email. There is now an email composition feature available which will also -automatically generate the email content based on the entries above. In the Admin -> Meetings -> Add / Edit section, -right click on the meeting you wish to compose a mail about and select Compose mail for this meeting.... -

    - - - -{tmplinclude file="footer.phtml"} diff --git a/application/views/static/support.phtml b/application/views/static/support.phtml deleted file mode 100644 index 68ac8e424..000000000 --- a/application/views/static/support.phtml +++ /dev/null @@ -1,83 +0,0 @@ -{tmplinclude file="header.phtml"} - -{if isset( $user ) and $user->getPrivs() eq 3} - -{else} -
    - -{/if} - -
    -

    - Techical Support: {mailto address="email@example.com" encode="javascript"} -    |    - Billing / Accounts: {mailto address="email@example.com" encode="javascript"} -    |    - Sales / Marketing: {mailto address="email@example.com" encode="javascript"} -

    -
    - -

    -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer tristique diam pretium ipsum fermentum ut sollicitudin mauris blandit. Mauris luctus, ipsum id feugiat bibendum, felis nulla dignissim sem, vehicula sollicitudin tortor turpis ut eros. Sed sit amet orci arcu, et varius velit. Vestibulum et nulla tortor, quis tincidunt augue. Vestibulum fringilla tempus faucibus. Donec non mauris neque. Nam urna odio, facilisis id tincidunt eu, dictum sed sapien. Pellentesque porttitor viverra nunc a porttitor. Fusce tempor, elit tempus dignissim accumsan, nisi metus eleifend ipsum, vel pellentesque lectus dolor at orci. Suspendisse potenti. -

    -

    - -

    Emergency 24x7x365 Support

    - -

    -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer tristique diam pretium ipsum fermentum ut sollicitudin mauris blandit. Mauris luctus, ipsum id feugiat bibendum, felis nulla dignissim sem, vehicula sollicitudin tortor turpis ut eros. Sed sit amet orci arcu, et varius velit. Vestibulum et nulla tortor, quis tincidunt augue. Vestibulum fringilla tempus faucibus. Donec non mauris neque. Nam urna odio, facilisis id tincidunt eu, dictum sed sapien. Pellentesque porttitor viverra nunc a porttitor. Fusce tempor, elit tempus dignissim accumsan, nisi metus eleifend ipsum, vel pellentesque lectus dolor at orci. Suspendisse potenti. -

    - -

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

    Technical Support Summary

    Email:{mailto address="email@example.com" encode="javascript"}
    Phone:+353 1 123 4567
    Hours:09:00 to 18:00 GMT, Monday to Friday
    24h Emergency:+353 1 987 6541
    -
    - -{if not isset( $user ) or $user->getPrivs() neq 3} -
    -{/if} - -{tmplinclude file="footer.phtml"} diff --git a/application/views/statistics/league-table.phtml b/application/views/statistics/league-table.phtml deleted file mode 100644 index 1a9630a30..000000000 --- a/application/views/statistics/league-table.phtml +++ /dev/null @@ -1,266 +0,0 @@ -{tmplinclude file="header.phtml"} - - - -
    - -{OSS_Message} - -

    -

    - - - - - - - - - - - - - -
    Metric: - - Statistics Type: - - Day: - -
    -
    -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -{foreach from=$trafficDaily item=td} - - - - - {if $metric eq 'max'} - - - - - - - - - - - - - {elseif $metric eq 'average'} - - - - - - - - - - - - - {else} - - - - - - - - - - - - - {/if} - - -{/foreach} - - -
    DayWeekMonthYear
    MemberInOutTotalInOutTotalInOutTotalInOutTotal
    {$td.Customer.shortname}{$td.Customer.name}{$td.day_max_in}{$td.day_max_out}{$td.day_max_in+$td.day_max_out}{$td.week_max_in}{$td.week_max_out}{$td.week_max_in+$td.week_max_out}{$td.month_max_in}{$td.month_max_out}{$td.month_max_in+$td.month_max_out}{$td.year_max_in}{$td.year_max_out}{$td.year_max_in+$td.year_max_out}{$td.day_avg_in}{$td.day_avg_out}{$td.day_avg_in+$td.day_avg_out}{$td.week_avg_in}{$td.week_avg_out}{$td.week_avg_in+$td.week_avg_out}{$td.month_avg_in}{$td.month_avg_out}{$td.month_avg_in+$td.month_avg_out}{$td.year_avg_in}{$td.year_avg_out}{$td.year_avg_in+$td.year_avg_out}{$td.day_tot_in}{$td.day_tot_out}{$td.day_tot_in+$td.day_tot_out}{$td.week_tot_in}{$td.week_tot_out}{$td.week_tot_in+$td.week_tot_out}{$td.month_tot_in}{$td.month_tot_out}{$td.month_tot_in+$td.month_tot_out}{$td.year_tot_in}{$td.year_tot_out}{$td.year_tot_in+$td.year_tot_out}
    - - -
    - -{if $metric eq 'max'} - {assign var='scalefn' value='myScale'} -{elseif $metric eq 'average'} - {assign var='scalefn' value='myScale'} -{else} - {assign var='scalefn' value='myScaleTotal'} -{/if} - - - - -{tmplinclude file="footer.phtml"} \ No newline at end of file diff --git a/application/views/statistics/list.phtml b/application/views/statistics/list.phtml deleted file mode 100644 index 513405078..000000000 --- a/application/views/statistics/list.phtml +++ /dev/null @@ -1,52 +0,0 @@ -{tmplinclude file="header.phtml"} - - - - - -{OSS_Message} - -
    - -{assign var='count' value=0} - -
    - -
      - - {foreach $custs as $cust} - - {if count( $custs ) > 12 and $count >= count( $custs )/4} - -
    -
    -
    - {assign var='count' value=0} - {/if} - -
  • - - {$cust.name} - -
  • - - {assign var='count' value=$count+1} - - {/foreach} - - - -
    -
    - -{tmplinclude file="footer.phtml"} diff --git a/application/views/statistics/member-drilldown-mini.phtml b/application/views/statistics/member-drilldown-mini.phtml deleted file mode 100644 index a56432cad..000000000 --- a/application/views/statistics/member-drilldown-mini.phtml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - {tmplinclude file="header-css.phtml"} - - - - -
    -

    IXP Interface Statistics :: {$cust->getName()} :: {foreach from=$categories key=cname item=cvalue}{if $category eq $cvalue}{$cname}{/if}{/foreach}

    - -{foreach from=$periods key=pname item=pvalue} - -

    {$pname} Graph

    - -

    - {genMrtgGraphBox - shortname=$cust->getShortname() - category=$category - monitorindex=$monitorindex - period=$pvalue - values=$stats.$pvalue - } -

    - - -{/foreach} - -
    - - - diff --git a/application/views/statistics/member-drilldown.phtml b/application/views/statistics/member-drilldown.phtml deleted file mode 100644 index 85de03ddf..000000000 --- a/application/views/statistics/member-drilldown.phtml +++ /dev/null @@ -1,104 +0,0 @@ -{tmplinclude file="header.phtml" pageTitle="IXP Manager :: Member Dashboard"} - -{if $user->getPrivs() eq 3} - -
    -{else} -
    - -{/if} - - -{OSS_Message} - -
    - -
    - - {if $switchname eq ''} -

    Aggregate Statistics for All Ports

    - {else} -

    Port: {$switchname} / {$portname}

    - {/if} - -
    -
    - -
    getShortname() monitorindex=$monitorindex}" method="post" class="form-horizontal"> - - Graph Type:    - - -        - getPrivs() eq 3}/shortname/{$cust->getShortname()}{/if}"> - Back to Overview - - -
    - -
    -
    - -
    - -{assign var='count' value=0} - - {foreach from=$periods key=pname item=pvalue} - -
    - -
    - -

    {$pname} Graph

    - -

    - {genMrtgGraphBox - shortname=$cust->getShortname() - category=$category - monitorindex=$monitorindex - period=$pvalue - values=$stats.$pvalue - } -

    - -
    -
    - - {assign var='count' value=$count+1} - - {if $count%2 eq 0} -

    - {/if} - - - {/foreach} - -{if $count%2 neq 0} -
    -{/if} - -
    -
    - -{tmplinclude file="footer.phtml"} - diff --git a/application/views/statistics/member.phtml b/application/views/statistics/member.phtml deleted file mode 100644 index 2e5a7a29c..000000000 --- a/application/views/statistics/member.phtml +++ /dev/null @@ -1,123 +0,0 @@ -{tmplinclude file="header.phtml" pageTitle="IXP Manager :: Member Dashboard"} - -{if $user->getPrivs() eq 3} - - -
    -{else} -
    - -{/if} - -{OSS_Message} - -
    - - - -
    - -

    -


    - Click on a graphs for longer term statistics or change the graph time in the drop down below. -

    - -
    - - Graph Type:     - -
    - -
    - -
    - - -
    - -{assign var='count' value=0} - -{foreach $cust->getVirtualInterfaces() as $vi} - - {foreach $vi->getPhysicalInterfaces() as $pi} - -
    - -
    - -

    - {$pi->getSwitchPort()->getSwitcher()->getCabinet()->getLocation()->getName()} - / {$pi->getSwitchPort()->getSwitcher()->getName()} - / {$pi->getSwitchPort()->getName()} ({$pi->getSpeed()}Mbps) -

    - - -

    -
    - - {genMrtgImgUrlTag shortname=$cust->getShortname() category=$category monitorindex=$pi->getMonitorindex()} - -

    - -
    - -
    - - {assign var='count' value=$count+1} - - {if $count%2 eq 0} -

    - {/if} - - {/foreach} - -{/foreach} - -{if $count%2 neq 0} -
    -{/if} - -
    -
    - -{tmplinclude file="footer.phtml"} - diff --git a/application/views/statistics/members.phtml b/application/views/statistics/members.phtml deleted file mode 100644 index 6a9057eba..000000000 --- a/application/views/statistics/members.phtml +++ /dev/null @@ -1,161 +0,0 @@ -{tmplinclude file="header.phtml"} - - - -{OSS_Message} - -
    -
    -
    - Graph Type: - - Period: - -
    -
    -
    - - -
    - -{assign var='count' value=0} -{foreach from=$custs item=cust} - - {* identify if this customer has an interface on the selected peering LAN *} - - {$monindexes = []} - {$portdetails = []} - {if $infra eq 'aggregate'} - - {$monindexes[] = 'aggregate'} - - {else} - - {* does this customer have interfaces on the requested LAN *} - - {foreach $cust->getVirtualInterfaces() as $vi} - - {foreach $vi->getPhysicalInterfaces() as $pi} - - {if $pi->getSwitchPort()->getSwitcher()->getInfrastructure() == $infra} - - {$monindexes[] = $pi->getMonitorindex()} - {$mi = $pi->getMonitorindex()} - - {$portdetails.$mi.switch = $pi->getSwitchPort()->getSwitcher()->getName()} - {$portdetails.$mi.switchport = $pi->getSwitchPort()->getName()} - - {/if} - - {/foreach} - - - {/foreach} - - {/if} - - {foreach $monindexes as $monindex} - -
    - -
    -

    - {$cust->getName()} - {if $category eq 'bits' or $category eq 'pkts'} - - getShortname() category=$category period=$period}"> - - {/if} -

    - - {if $infra neq 'aggregate'} -
    {$portdetails.$monindex.switch} / {$portdetails.$monindex.switchport}
    - {/if} - -

    -
    - getShortname() monitorindex=aggregate category=$category}"> - - -

    -
    - -
    - - {assign var='count' value=$count+1} - - {if $count%4 eq 0} -

    - {/if} - - {/foreach} - -{/foreach} - -{if $count%4 neq 0} -
    - {assign var='count' value=$count+1} - {if $count%4 neq 0} -
    - {assign var='count' value=$count+1} - {if $count%4 neq 0} -
    - {assign var='count' value=$count+1} - {/if} - {/if} -{/if} - -
    - - -{tmplinclude file="footer.phtml"} \ No newline at end of file diff --git a/application/views/statistics/p2p-single.phtml b/application/views/statistics/p2p-single.phtml deleted file mode 100644 index dc4c4dc72..000000000 --- a/application/views/statistics/p2p-single.phtml +++ /dev/null @@ -1,151 +0,0 @@ -{tmplinclude file="header.phtml"} - -{if $user->getPrivs() eq 3} - -{else} -
    - -{/if} - -{OSS_Message} - -

    -

    - - - - - - - - - {* - - - - *} - {if count( $customersVirtualInterfaces.Virtualinterface ) > 1} - - - - {/if} - - - -
    Graph Type: -   - - Protocol: -   - - Infrastructure: -   - - Interface: -   - - - - {if isset( $dvid )}{/if} - -
    -
    -

    - - -{if count( $vints ) > 1} -
    -

    Multiple Interfaces on this Infrastructure Found

    - We've detected that you have multiple interfaces on this infrastructure. You can view you other - interface(s) by changing the interface index above. -
    -{/if} - -{if isset( $dcust ) and $dcust} -
    - - {assign var='count' value=0} - {foreach from=$periods key=pname item=pid} - -
    - -
    -

    - {$pname} -

    - -

    -
    - -

    -
    - -
    - - {assign var='count' value=$count+1} - - {if $count%2 eq 0} -

    - {/if} - - {/foreach} - - {if $count%2 neq 0} -
    - {assign var='count' value=$count+1} - {/if} - -
    - -{else} {* customer has an interface for given infra and proto *} - -
    -

    Uh oh! You or your peer do not have any ports for the selected infrastructure and / or protocol.

    - If you'd like to take to us about enabling IPv6 or getting a port on the secondary infrastructure, please - contact us. -
    - -{/if} {* customer has an interface for given infra and proto *} - -{tmplinclude file="footer.phtml"} \ No newline at end of file diff --git a/application/views/statistics/p2p.phtml b/application/views/statistics/p2p.phtml deleted file mode 100644 index 2826db59d..000000000 --- a/application/views/statistics/p2p.phtml +++ /dev/null @@ -1,214 +0,0 @@ -{tmplinclude file="header.phtml"} - -{if $user->getPrivs() eq USER::AUTH_SUPERUSER} - -
    -{else} -
    - -{/if} - -{OSS_Message} - -

    -

    - - - - - - - - - - - - - - - {if count( $vints ) > 1} - - - - {/if} - - - -
    Graph Type: -   - - Period: -   - - Protocol: -   - - Infrastructure: -   - - Interface: -   - - - - -
    -
    -

    - - -{if count( $vints ) > 1} -
    -

    Multiple Interfaces on this Infrastructure Found

    - We've detected that you have multiple interfaces on this infrastructure. You can view you other - interface(s) by changing the interface index above. -
    -{/if} - -{if isset( $vints ) and is_array( $vints ) and count( $vints )} -
    - - {assign var='count' value=0} - {if $user->getPrivs() eq USER::AUTH_SUPERUSER} - - - {foreach $customersWithVirtualInterfaces as $vcust} - - - {assign var='count' value=$count+1} - - {if $count%4 eq 0} -

    - {/if} - {/foreach} - - {if $count%4 neq 0} -
    - {assign var='count' value=$count+1} - {if $count%4 neq 0} -
    - {assign var='count' value=$count+1} - {if $count%4 neq 0} -
    - {assign var='count' value=$count+1} - {/if} - {/if} - {/if} - - - {else} - - - {foreach $customersWithVirtualInterfaces as $vcust} - - - - {assign var='count' value=$count+1} - - {if $count%2 eq 0} -

    - {/if} - - {/foreach} - - {if $count%2 neq 0} -
    - {assign var='count' value=$count+1} - {/if} - - - {/if} - -
    - -{else} {* customer has an interface for given infra and proto *} - -
    -

    Uh oh! You (or your peer(s)) do not have any ports for the selected infrastructure and / or protocol.

    - If you'd like to take to us about enabling IPv6 or getting a port on the secondary infrastructure, please - contact us. -
    - -{/if} {* customer has an interface for given infra and proto *} - -
    - -{tmplinclude file="footer.phtml"} \ No newline at end of file diff --git a/application/views/statistics/public.phtml b/application/views/statistics/public.phtml deleted file mode 100644 index 3382d0c2c..000000000 --- a/application/views/statistics/public.phtml +++ /dev/null @@ -1,90 +0,0 @@ -{tmplinclude file="header.phtml"} - -{if $user->getPrivs() eq 3} - -{else} -
    - -{/if} - -

    {$graphs.$graph} :: {foreach from=$categories key=cname item=cvalue}{if $category eq $cvalue}{$cname}{/if}{/foreach}

    - -{OSS_Message} - -

    -

    - - - - - - - - - -
    Select Infrastructure: - - Category: - -
    -
    -

    - - -
    - -{assign var='count' value=0} - -{foreach from=$periods key=pname item=pvalue} - -
    - -
    -

    {$pname} Graph

    - -

    - {genMrtgGraphBox - shortname='X_Peering' - period=$pvalue - category=$category - values=$stats.$pvalue - graph=$graph - } -

    -
    -
    - - {assign var='count' value=$count+1} - - {if $count%2 eq 0} -

    - {/if} - -{/foreach} - -{if $count%2 neq 0} -
    -{/if} - -
    - - -{tmplinclude file="footer.phtml"} - diff --git a/application/views/statistics/switches.phtml b/application/views/statistics/switches.phtml deleted file mode 100644 index 273338285..000000000 --- a/application/views/statistics/switches.phtml +++ /dev/null @@ -1,86 +0,0 @@ -{tmplinclude file="header.phtml"} - - -{if $user->getPrivs() eq 3} - -{else} -
    - -{/if} - - -{OSS_Message} - -
    -
    -
    - Select Switch: - - Category: - -
    -
    -
    - - -
    - -{assign var='count' value=0} - -{foreach from=$periods key=pname item=pvalue} - - -
    - -
    - -

    {$pname} Graph

    - -

    - {genMrtgGraphBox - shortname='X_SwitchAggregate' - period=$pvalue - category=$category - values=$stats.$pvalue - graph=$switches.$switch - } -

    -
    -
    - - {assign var='count' value=$count+1} - - {if $count%2 eq 0} -

    - {/if} - - -{/foreach} - -{if $count%2 neq 0} -
    -{/if} - -
    - -{tmplinclude file="footer.phtml"} diff --git a/application/views/statistics/trunks.phtml b/application/views/statistics/trunks.phtml deleted file mode 100644 index 8a3874c64..000000000 --- a/application/views/statistics/trunks.phtml +++ /dev/null @@ -1,76 +0,0 @@ -{tmplinclude file="header.phtml"} - -{if $user->getPrivs() eq 3} - -{else} -
    - -{/if} - -{OSS_Message} - -
    -
    -
    - Select Trunk:   - -
    -
    -
    - -
    - -{assign var='count' value=0} - - - {foreach from=$periods key=pname item=pvalue} - -
    - -
    - -

    {$pname} Graph

    - -

    - {genMrtgGraphBox - shortname='X_Trunks' - period=$pvalue - values=$stats.$pvalue - graph=$graph - } -

    -
    -
    - - {assign var='count' value=$count+1} - - {if $count%2 eq 0} -

    - {/if} - -{/foreach} - -{if $count%2 neq 0} -
    -{/if} - -
    - -{tmplinclude file="footer.phtml"} - diff --git a/application/views/switch-port/add-ports.phtml b/application/views/switch-port/add-ports.phtml deleted file mode 100644 index 3468bdcd0..000000000 --- a/application/views/switch-port/add-ports.phtml +++ /dev/null @@ -1,18 +0,0 @@ -{tmplinclude file="header.phtml"} - - - -{$form} - - -{tmplinclude file="footer.phtml"} diff --git a/application/views/switch-port/forms/add-ports.phtml b/application/views/switch-port/forms/add-ports.phtml deleted file mode 100644 index 0caefcfc7..000000000 --- a/application/views/switch-port/forms/add-ports.phtml +++ /dev/null @@ -1,106 +0,0 @@ -
    getName() != ''}name="{$element->getName()}"{/if} - {if $element->getAttrib('target')}target="{$element->getAttrib('target')}"{/if} - class="form"> - -{$element->switchid} -{$element->deftype} -{$element->numfirst} -{$element->numports} - -
    - - -
    - - ? -
    -
    - -
    - -
    - -
    -
    - -
    - -
    -
    - {$element->submit} - {$element->cancel} -
    -
    - -
    - - - - - - - - - diff --git a/application/views/switch-port/list-toolbar.phtml b/application/views/switch-port/list-toolbar.phtml deleted file mode 100644 index 8c16ae3f0..000000000 --- a/application/views/switch-port/list-toolbar.phtml +++ /dev/null @@ -1,20 +0,0 @@ -
  • -
    - -
    - - {if isset( $sid ) and $sid}{$switches.$sid}{else}Limit to switch...{/if} - - - -
    - -
    - -
    -
    -
  • diff --git a/application/views/switch/configuration.phtml b/application/views/switch/configuration.phtml deleted file mode 100644 index 3d6ee7d30..000000000 --- a/application/views/switch/configuration.phtml +++ /dev/null @@ -1,103 +0,0 @@ -{tmplinclude file="header.phtml"} - -{if isset( $user ) and $user->getPrivs() eq USER::AUTH_SUPERUSER} - - -
    -{else} -
    - - -{/if} - - {OSS_Message} - - - - - - - {if not isset( $switchid )}{/if} - - - {if not isset( $vlanid )}{/if} - - - - - - - - - - {foreach $config as $idx => $row} - - - {if not isset( $switchid )}{/if} - - - {if not isset( $vlanid )}{/if} - - - - - - - {/foreach} - - -
    CustomerSwitchPortSpeedPeering LANASNRoute ServerIPv4 AddressIPv6 AddressStatus
    {$row.customer}{$row.switchname}{$row.portname}{$row.speed}{$row.vlan}{$row.asn|asnumber}{if $row.rsclient}Yes{else}No{/if}{$row.ipv4address}{$row.ipv6address}{if isset( $states[ $row.portstatus ] )}{$states[ $row.portstatus ]}{/if}
    -
    - - -{tmplinclude file="footer.phtml"} diff --git a/application/views/switch/js/configuration.js b/application/views/switch/js/configuration.js deleted file mode 100644 index 0f818e478..000000000 --- a/application/views/switch/js/configuration.js +++ /dev/null @@ -1,30 +0,0 @@ - - -var oDataTable; - -$(document).ready(function() { - - oDataTable = $( '#frontend-list-table' ).dataTable({ - "sDom": "<'row-fluid'<'span6'l><'span6'f>r>t<'row-fluid'<'span6'i><'span6'p>>", - "iDisplayLength": 50, - "sPaginationType": "bootstrap", - "aoColumns": [ - null, - {if not isset( $switchid )}null,{/if} - null, - { "sSortDataType": "dom-text", "sType": "numeric" }, - {if not isset( $vlanid )}null,{/if} - { "sSortDataType": "dom-text", "sType": "html" }, - null, - null, - null, - null - ] - }); - $( '#frontend-list-table' ).show(); - -}); - - - - diff --git a/application/views/switch/list-row-menu.phtml b/application/views/switch/list-row-menu.phtml deleted file mode 100644 index 4a7f0f1c7..000000000 --- a/application/views/switch/list-row-menu.phtml +++ /dev/null @@ -1,17 +0,0 @@ - diff --git a/application/views/switch/port-report.phtml b/application/views/switch/port-report.phtml deleted file mode 100644 index 27878ba15..000000000 --- a/application/views/switch/port-report.phtml +++ /dev/null @@ -1,91 +0,0 @@ -{tmplinclude file="header.phtml" pageTitle="IXP Manager :: "|cat:$feParams->pagetitle} - - - -{OSS_Message} - - - - - - - - - - - - - {foreach from=$ports item=p} - - - - - {if isset( $p.speed )} - - - {else} - - - {/if} - - - {/foreach} - - -
    Port NameTypeSpeed/DuplexCustomer
    {$p.portname}{$p.porttype}{$p.speed}/{$p.duplex}{$p.custname}
    - - - - - -{tmplinclude file="footer.phtml"} diff --git a/application/views/user/email/html/welcome.phtml b/application/views/user/email/html/welcome.phtml deleted file mode 100644 index 60f4316aa..000000000 --- a/application/views/user/email/html/welcome.phtml +++ /dev/null @@ -1,87 +0,0 @@ - - - Welcome to {$options.identity.sitename} - - -

    {$options.identity.sitename} - Your Access Details

    - -

    -To whom it may concern, -

    - -{if isset( $resend ) and $resend} -

    - This email is being sent to you because either you requested a reminder of - your account details or an administrator thought it appropriate to send you a - reminder. -

    -{else} -

    - A new user account has been created for you on the {$options.identity.sitename}. -

    -{/if} - -

    -You can login to your account using the following details: -

    - - - - - - - - - - - - - - - - - - - - -
    URL:{$options.identity.url}
    Username:{$newuser->getUsername()}
    Password:(see below)
    - -

    -Once logged in, you will have access to a number of features including: -

    - -
      -
    • ---- COMPLETE YOURSELF VIA SKIN ----
    • -
    - -

    -If you require any assistance, please contact {$options.identity.name} on -{$options.identity.email}. -

    - - -

    Getting Your Password

    - -

    -To get your new password (or reset it), please use the lost password procedure by visiting -the following link and entering your username as above: -

    - -
    - {genUrl controller="auth" action="lost-password"} -
    - - - -

    -Thanks and kind regards, -

    - -

    -{$options.identity.name}
    -{$options.identity.email} -

    - - - - diff --git a/application/views/user/last.phtml b/application/views/user/last.phtml deleted file mode 100644 index 95bff4a9c..000000000 --- a/application/views/user/last.phtml +++ /dev/null @@ -1,58 +0,0 @@ -{tmplinclude file="header.phtml" pageTitle="IXP Manager :: Member Dashboard"} - - - - - - - - - - - - - - - - {foreach from=$last item=l} - - - - - - - {/foreach} - - - - - - -{tmplinclude file="footer.phtml"} diff --git a/application/views/user/list-column-enabled.phtml b/application/views/user/list-column-enabled.phtml deleted file mode 100644 index bfa858c7a..000000000 --- a/application/views/user/list-column-enabled.phtml +++ /dev/null @@ -1,7 +0,0 @@ - - {if $row.disabled} - - {else} - - {/if} - diff --git a/application/views/user/list-row-menu.phtml b/application/views/user/list-row-menu.phtml deleted file mode 100644 index def514d3e..000000000 --- a/application/views/user/list-row-menu.phtml +++ /dev/null @@ -1,10 +0,0 @@ -
    - - - - - -
    diff --git a/application/views/utils/apcinfo.phtml b/application/views/utils/apcinfo.phtml deleted file mode 100644 index 55894ccfb..000000000 --- a/application/views/utils/apcinfo.phtml +++ /dev/null @@ -1,28 +0,0 @@ -{tmplinclude file="header.phtml"} - - - -
    - -{OSS_Message} - -
    - -
    - -
    - -{tmplinclude file="utils/js/iframe.js"} -{tmplinclude file="footer.phtml"} diff --git a/application/views/utils/js/iframe.js b/application/views/utils/js/iframe.js deleted file mode 100644 index f263f5ee5..000000000 --- a/application/views/utils/js/iframe.js +++ /dev/null @@ -1,51 +0,0 @@ - diff --git a/application/views/utils/phpinfo.phtml b/application/views/utils/phpinfo.phtml deleted file mode 100644 index 5d438f218..000000000 --- a/application/views/utils/phpinfo.phtml +++ /dev/null @@ -1,29 +0,0 @@ -{tmplinclude file="header.phtml"} - - - -
    - -{OSS_Message} - -
    - -
    - -
    - -{tmplinclude file="utils/js/iframe.js"} -{tmplinclude file="footer.phtml"} diff --git a/application/views/virtual-interface/add-postamble.phtml b/application/views/virtual-interface/add-postamble.phtml deleted file mode 100644 index b4a914131..000000000 --- a/application/views/virtual-interface/add-postamble.phtml +++ /dev/null @@ -1,147 +0,0 @@ -
    - -{if $isEdit} - -
    - -

    - Physical Interfaces - -

    - -
    - -
    - -
    - {if count( $physInts )} - - - - - - - - - - - - - - {foreach $physInts as $int} - - - - - - - - - - {/foreach} - - - -
    LocationSwitchPortSpeed/Duplex
    - {$int->getSwitchPort()->getSwitcher()->getCabinet()->getLocation()->getName()} - - {$int->getSwitchPort()->getSwitcher()->getName()} - - {$int->getSwitchPort()->getName()} - - {$int->getSpeed()}/{$int->getDuplex()} - -
    - - -
    -
    - - {else} - -

    - There are no physical interfaces defined for this virtual interface. - getId()}">Add one now... -

    - - {/if} - -
    -
    -
    - - -
    - -

    - VLAN Interfaces - -

    -
    - -
    -
    - {if count( $vlanInts )} - - - - - - - - - - - - - - - {foreach $vlanInts as $int} - - - - - - - - - - {/foreach} - - - -
    VLAN NameVLAN IDIPv4 AddressIPv6 Address
    - {$int->getVlan()->getName()} - - {$int->getVlan()->getNumber()} - - {if $int->getIPv4Address()} - {$int->getIPv4Address()->getAddress()} - {/if} - - {if $int->getIPv6Address()} - {$int->getIPv6Address()->getAddress()} - {/if} - -
    - - -
    -
    - - {else} - -

    - There are no VLAN interfaces defined for this virtual interface. - Add one now... -

    - - {/if} -
    -
    - -{/if} diff --git a/application/views/virtual-interface/add-preamble.phtml b/application/views/virtual-interface/add-preamble.phtml deleted file mode 100644 index 3f38f8259..000000000 --- a/application/views/virtual-interface/add-preamble.phtml +++ /dev/null @@ -1 +0,0 @@ -
    diff --git a/application/views/virtual-interface/add-toolbar.phtml b/application/views/virtual-interface/add-toolbar.phtml deleted file mode 100644 index 74e0868c6..000000000 --- a/application/views/virtual-interface/add-toolbar.phtml +++ /dev/null @@ -1,15 +0,0 @@ -
  • - -
  • - diff --git a/application/views/virtual-interface/add-wizard.phtml b/application/views/virtual-interface/add-wizard.phtml deleted file mode 100644 index 74c6de5c6..000000000 --- a/application/views/virtual-interface/add-wizard.phtml +++ /dev/null @@ -1,19 +0,0 @@ -{tmplinclude file="header.phtml"} - - - -{$form} - - - -{tmplinclude file="footer.phtml"} diff --git a/application/views/virtual-interface/forms/add-wizard.phtml b/application/views/virtual-interface/forms/add-wizard.phtml deleted file mode 100644 index 02d6191a1..000000000 --- a/application/views/virtual-interface/forms/add-wizard.phtml +++ /dev/null @@ -1,85 +0,0 @@ - -
    - -
    -
    - -
    - General Interface Settings - - {$element->custid} - {$element->vlanid} - {$element->trunk} - {$element->ipv4enabled} - {$element->ipv6enabled} - -
    - -
    -
    - -
    - Physical Interface Settings - - {$element->switchid} - {$element->switchportid} - {$element->status} - {$element->speed} - {$element->duplex} - -
    - -
    - -
    - -
    - General VLAN Settings - - {$element->maxbgpprefix} - {$element->irrdbfilter} - {$element->mcastenabled} - {$element->rsclient} - {$element->as112client} - -
    - -
    -
    - -
    - -
    -
    - {$element->ipv4DisplayGroup} -
    -
    - -
    -
    - {$element->ipv6DisplayGroup} -
    -
    - -
    - -
    - - -{$element->preselectIPv4Address} -{$element->preselectIPv6Address} -{$element->preselectVlanInterface} -{$element->preselectSwitchPort} -{$element->preselectPhysicalInterface} - -
    - {$element->cancel} - {$element->submit} -
    - - -
    - diff --git a/application/views/virtual-interface/forms/virtual-interface.phtml b/application/views/virtual-interface/forms/virtual-interface.phtml deleted file mode 100644 index f4fe26120..000000000 --- a/application/views/virtual-interface/forms/virtual-interface.phtml +++ /dev/null @@ -1,77 +0,0 @@ - -
    - -
    - -
    - -
    - Virtual Interface Details - - {$element->custid} - {$element->trunk} - -
    - -
    - -
    - -
    -   - -
    - - {$element->name} - {$element->description} - {$element->channelgroup} - {$element->mtu} - -
    - -
    - -
    - - -
    - - - -
    - - {if isset( $cust )} - Return - {else} - Wizard Add - Return - {/if} - - -
    - - -
    - - - - diff --git a/application/views/virtual-interface/js/add-wizard.js b/application/views/virtual-interface/js/add-wizard.js deleted file mode 100644 index 378dd80e6..000000000 --- a/application/views/virtual-interface/js/add-wizard.js +++ /dev/null @@ -1,118 +0,0 @@ - -$( "#switchid" ).on( 'change', function( event ) { - $( "#switchid" ).attr( 'disabled', 'disabled' ); - - if( $(this).val() != '0' ) { - tt_chosenClear( "#switchportid", "" ); - - $.getJSON( "{genUrl controller='switch-port' action='ajax-get'}/id/" - + $( "#preselectPhysicalInterface" ).val() + "/switchid/" + $(this).val(), function( j ) { - - var options = "\n"; - - for( var i = 0; i < j.length; i++ ) - options += "\n"; - - // do we have a preselect? - if( $( "#preselectSwitchPort" ).val() ) { - tt_chosenSet( "#switchportid", options, $( "#preselectSwitchPort" ).val() ); - } else { - tt_chosenSet( "#switchportid", options ); - } - }); - } - - $("#switchid").removeAttr( 'disabled' ); -}); - - -$( "#switchportid" ).change( function() { - $( "#preselectSwitchPort" ).val( $( "#switchportid" ).val() ); -}); - - -$( "#vlanid" ).on( 'change', function( event ) { - - $( "#vlanid" ).attr( 'disabled', 'disabled' ); - - if( $(this).val() != '0' ) { - - tt_chosenClear( "#ipv4addressid", "" ); - tt_chosenClear( "#ipv6addressid", "" ); - - $.getJSON( "{genUrl controller='ipv4-address' action='ajax-get-for-vlan'}/vliid/" - + $( "#preselectVlanInterface" ).val() + "/vlanid/" + $(this).val(), null, function( j ){ - - var options = "\n"; - - for( var i = 0; i < j.length; i++ ) - options += "\n"; - - // do we have a preselect? - if( $( "#preselectIPv4Address" ).val() ) { - tt_chosenSet( "#ipv4addressid", options, $( "#preselectIPv4Address" ).val() ); - } else { - tt_chosenSet( "#ipv4addressid", options ); - } - }); - - $.getJSON( "{genUrl controller='ipv6-address' action='ajax-get-for-vlan'}/vliid/" - + $( "#preselectVlanInterface" ).val() + "/vlanid/" + $(this).val(), null, function( j ){ - - var options = "\n"; - - for( var i = 0; i < j.length; i++ ) - options += "\n"; - - // do we have a preselect? - if( $( "#preselectIPv6Address" ).val() ) { - tt_chosenSet( "#ipv6addressid", options, $( "#preselectIPv6Address" ).val() ); - } else { - tt_chosenSet( "#ipv6addressid", options ); - } - }); - - } - - $("#vlanid").removeAttr( 'disabled' ); - -}); - - -$( "#ipv4addressid" ).change( function() { - $( "#preselectIPv4Address" ).val( $( "#ipv4addressid" ).val() ); -}); - -$( "#ipv6addressid" ).change( function() { - $( "#preselectIPv6Address" ).val( $( "#ipv6addressid" ).val() ); -}); - -$(document).ready( function() { - - // trigger a change on selects to populate dependant fields - $("#switch_id").trigger( 'change' ); - $("#vlanid").trigger( 'change' ); - - $( '#ipv4enabled' ).on( 'click', function( event ){ - - if( $( '#ipv4enabled' ).is(':checked') ) - $( '#ipv4details' ).slideDown(); - else - $( '#ipv4details' ).slideUp(); - }); - - $( '#ipv6enabled' ).on( 'click', function( event ){ - - if( $( '#ipv6enabled' ).is(':checked') ) - $( '#ipv6details' ).slideDown(); - else - $( '#ipv6details' ).slideUp(); - }); - - if( $( '#ipv4enabled' ).is(':checked') ) - $( '#ipv4details' ).show(); - - if( $( '#ipv6enabled' ).is(':checked') ) - $( '#ipv6details' ).show(); - -}); diff --git a/application/views/virtual-interface/js/add.js b/application/views/virtual-interface/js/add.js deleted file mode 100644 index 85d194904..000000000 --- a/application/views/virtual-interface/js/add.js +++ /dev/null @@ -1,25 +0,0 @@ - - -$(document).ready(function() { - - $( 'a[id|="object-delete"]' ).on( 'click', function( event ){ - - event.preventDefault(); - url = $(this).attr( "data-url" ); - - bootbox.dialog( "Are you sure you want to delete this object?", [{ - "label": "Cancel", - "class": "btn-primary" - }, - { - "label": "Delete", - "class": "btn-danger", - "callback": function() { document.location.href = url; } - }]); - - }); -}); - - - - diff --git a/application/views/virtual-interface/list-column-port.phtml b/application/views/virtual-interface/list-column-port.phtml deleted file mode 100644 index fb730e482..000000000 --- a/application/views/virtual-interface/list-column-port.phtml +++ /dev/null @@ -1,7 +0,0 @@ - - {if $row.ports gt 1} - LAG with {$row.ports} ports - {else} - {$row.port} - {/if} - \ No newline at end of file diff --git a/application/views/virtual-interface/list-row-menu.phtml b/application/views/virtual-interface/list-row-menu.phtml deleted file mode 100644 index f4364aa0a..000000000 --- a/application/views/virtual-interface/list-row-menu.phtml +++ /dev/null @@ -1,5 +0,0 @@ -
    - - - -
    diff --git a/application/views/virtual-interface/list-toolbar.phtml b/application/views/virtual-interface/list-toolbar.phtml deleted file mode 100644 index c2f438940..000000000 --- a/application/views/virtual-interface/list-toolbar.phtml +++ /dev/null @@ -1,14 +0,0 @@ -
  • - -
  • - diff --git a/application/views/vlan-interface/forms/vlan-interface.phtml b/application/views/vlan-interface/forms/vlan-interface.phtml deleted file mode 100644 index 98f801986..000000000 --- a/application/views/vlan-interface/forms/vlan-interface.phtml +++ /dev/null @@ -1,59 +0,0 @@ -
    - -
    -
    - - {$element->virtualinterfaceid} - {$element->vlanid} - {$element->irrdbfilter} - {$element->mcastenabled} - -
    -
    - - {$element->maxbgpprefix} - {$element->rsclient} - {$element->as112client} - {$element->busyhost} - -
    -
    - -
    - -
    - {$element->ipv4enabled} - -
    - {$element->ipv4DisplayGroup} -
    -
    - -
    - {$element->ipv6enabled} - -
    - {$element->ipv6DisplayGroup} -
    -
    - - -
    - - -{$element->preselectIPv4Address} -{$element->preselectIPv6Address} -{$element->preselectVlanInterface} - -
    - {$element->cancel} - {$element->submit} -
    - - -
    - - - diff --git a/application/views/vlan-interface/js/add.js b/application/views/vlan-interface/js/add.js deleted file mode 100644 index 8519e5619..000000000 --- a/application/views/vlan-interface/js/add.js +++ /dev/null @@ -1,76 +0,0 @@ - -$( "#vlanid" ).change( function() { - - $( "#vlanid" ).attr( 'disabled', 'disabled' ); - - if( $(this).val() != '0' ) { - - tt_chosenClear( "#ipv4addressid", "" ); - tt_chosenClear( "#ipv6addressid", "" ); - - $.getJSON( "{genUrl controller='ipv4-address' action='ajax-get-for-vlan'}/vliid/" - + $( "#preselectVlanInterface" ).val() + "/vlanid/" + $(this).val(), null, function( j ){ - - var options = "\n"; - - for( var i = 0; i < j.length; i++ ) - options += "\n"; - - // do we have a preselect? - if( $( "#preselectIPv4Address" ).val() ) { - tt_chosenSet( "#ipv4addressid", options, $( "#preselectIPv4Address" ).val() ); - } else { - tt_chosenSet( "#ipv4addressid", options ); - } - }); - - $.getJSON( "{genUrl controller='ipv6-address' action='ajax-get-for-vlan'}/vliid/" - + $( "#preselectVlanInterface" ).val() + "/vlanid/" + $(this).val(), null, function( j ){ - - var options = "\n"; - - for( var i = 0; i < j.length; i++ ) - options += "\n"; - - // do we have a preselect? - if( $( "#preselectIPv6Address" ).val() ) { - tt_chosenSet( "#ipv6addressid", options, $( "#preselectIPv6Address" ).val() ); - } else { - tt_chosenSet( "#ipv6addressid", options ); - } - }); - - } - - $("#vlanid").removeAttr( 'disabled' ); - -}); - -$(document).ready( function() { - - // trigger a change on switch ID to populate ports - $("#vlanid").trigger( 'change' ); - - $( '#ipv4enabled' ).on( 'click', function( event ){ - - if( $( '#ipv4enabled' ).is(':checked') ) - $( '#ipv4details' ).slideDown(); - else - $( '#ipv4details' ).slideUp(); - }); - - $( '#ipv6enabled' ).on( 'click', function( event ){ - - if( $( '#ipv6enabled' ).is(':checked') ) - $( '#ipv6details' ).slideDown(); - else - $( '#ipv6details' ).slideUp(); - }); - - if( $( '#ipv4enabled' ).is(':checked') ) - $( '#ipv4details' ).show(); - - if( $( '#ipv6enabled' ).is(':checked') ) - $( '#ipv6details' ).show(); - -}); diff --git a/application/views/vlan-interface/list-row-menu.phtml b/application/views/vlan-interface/list-row-menu.phtml deleted file mode 100644 index cfaf26c9f..000000000 --- a/application/views/vlan-interface/list-row-menu.phtml +++ /dev/null @@ -1,6 +0,0 @@ -
    - - - - -
    diff --git a/application/views/vlan-interface/list-toolbar.phtml b/application/views/vlan-interface/list-toolbar.phtml deleted file mode 100644 index c2353d137..000000000 --- a/application/views/vlan-interface/list-toolbar.phtml +++ /dev/null @@ -1,4 +0,0 @@ -
  • -
    -
    -
  • diff --git a/application/views/vlan-interface/view-toolbar.phtml b/application/views/vlan-interface/view-toolbar.phtml deleted file mode 100644 index 5b3f6be58..000000000 --- a/application/views/vlan-interface/view-toolbar.phtml +++ /dev/null @@ -1,6 +0,0 @@ -
    - - {if !isset( $feParams->readonly ) or !$feParams->readonly} - - {/if} -
    diff --git a/application/views/weather-map/index.phtml b/application/views/weather-map/index.phtml deleted file mode 100644 index 50c51cd6b..000000000 --- a/application/views/weather-map/index.phtml +++ /dev/null @@ -1,46 +0,0 @@ -{tmplinclude file="header.phtml"} - -{if isset( $user ) and $user->getPrivs() eq 3} - -
    -{else} -
    - - - -{/if} - -{OSS_Message} - -{if $weathermap} - -{/if} - -{if isset( $weathermaps ) and is_array( $weathermaps )} -

    Available Weathermaps

    - -
      - {foreach $weathermaps as $id => $wp} -
    • {$wp.name}
    • - {/foreach} -
    -{/if} - -
    - -{tmplinclude file="footer.phtml"} diff --git a/artisan b/artisan new file mode 100755 index 000000000..3d91931f4 --- /dev/null +++ b/artisan @@ -0,0 +1,57 @@ +#!/usr/bin/env php +make('Illuminate\Contracts\Console\Kernel'); + +$status = $kernel->handle( + $input = new Symfony\Component\Console\Input\ArgvInput, + new Symfony\Component\Console\Output\ConsoleOutput +); + +/* +|-------------------------------------------------------------------------- +| Shutdown The Application +|-------------------------------------------------------------------------- +| +| Once Artisan has finished running, we will fire off the shutdown events +| so that any final work may be done by the application before we shut +| down the process. This is the last thing to happen to the request. +| +*/ + +$kernel->terminate($input, $status); + +exit($status); diff --git a/bin/build-phpdoc.sh b/bin/build-phpdoc.sh index fb716bc04..02acf058a 100755 --- a/bin/build-phpdoc.sh +++ b/bin/build-phpdoc.sh @@ -1,8 +1,27 @@ -# /bin/bash +#!/bin/sh -ROOT=$(dirname $0)/.. +# Copyright (C) 2009 - 2019 Internet Neutral Exchange Association Company Limited By Guarantee. +# All Rights Reserved. +# +# This file is part of IXP Manager. +# +# IXP Manager is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the Free +# Software Foundation, version v2.0 of the License. +# +# IXP Manager is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GpNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License v2.0 +# along with IXP Manager. If not, see: +# +# http://www.gnu.org/licenses/gpl-2.0.html -/usr/local/bin/phpdoc -d $ROOT/application/models,$ROOT/library/INEX -t $ROOT/data/phpdoc/ -ti 'INEX IXP Manager :: Auto Generated Documentation' \ - -dc 'IXP-Undefiend' -dn 'IXP-Undefined' -s -o "HTML:frames:earthli" +ROOT=`dirname $0`/.. + +/usr/local/bin/phpdoc -d $ROOT/application/models,$ROOT/library/IXP -t $ROOT/data/phpdoc/ -ti 'IXP Manager :: Auto Generated Documentation' \ + -dc 'IXP-Undefined' -dn 'IXP-Undefined' -s -o "HTML:frames:earthli" diff --git a/bin/doctrine2-cli.php b/bin/doctrine2-cli.php deleted file mode 100755 index 695f2ca83..000000000 --- a/bin/doctrine2-cli.php +++ /dev/null @@ -1,85 +0,0 @@ -#!/usr/bin/env php -getBootstrap()->bootstrap( 'OSSAutoLoader' ); - -$config = $application->getOption( 'resources' ); - -$plugin = new OSS_Resource_Doctrine2( $config['doctrine2'] ); -$application->getBootstrap()->registerPluginResource( $plugin ); -$em = $plugin->getDoctrine2( $db ); - - -$helpers = array( - 'db' => new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper( $em->getConnection() ), - 'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper( $em ) -); - -$cli = new \Symfony\Component\Console\Application( 'Doctrine Command Line Interface', Doctrine\Common\Version::VERSION ); -$cli->setCatchExceptions(true); -$helperSet = $cli->getHelperSet(); -foreach ($helpers as $name => $helper) { - $helperSet->set($helper, $name); -} - -Doctrine\ORM\Tools\Console\ConsoleRunner::addCommands( $cli ); - -$cli->run(); - - diff --git a/bin/find-files-with-no-copyright-notice.sh b/bin/find-files-with-no-copyright-notice.sh new file mode 100755 index 000000000..462853b2f --- /dev/null +++ b/bin/find-files-with-no-copyright-notice.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +find . -type f \ + \! -path ./.\* \ + \! -path ./resources/\* \ + \! -path \*/library/OSS/\* \ + \! -path \*/jwysiwyg/\* \ + \! -path \*/database/Proxies/\* \ + \! -path \*/database/xml/\* \ + \! -path \*/public/fonts/\* \ + \! -name \*.foil.php \ + \! -name \*.png \ + \! -name \*.gif \ + \! -name \*.jpg \ + \! -name \*.ttf \ + \! -name \*.svg \ + \! -name \*.pdf \ + \! -name \*.log \ + \! -name .gitignore \ + \! -name .hellogit \ + \! -name README.md \ + -print0 | \ + xargs -0 grep -Li 'copyright.*internet neutral exchange' diff --git a/bin/fixtures.php.dist b/bin/fixtures.php.dist deleted file mode 100755 index c5b45ef8f..000000000 --- a/bin/fixtures.php.dist +++ /dev/null @@ -1,252 +0,0 @@ -#!/usr/bin/env php -getBootstrap() ); - -$application->getBootstrap()->bootstrap( 'OSSAutoLoader' ); -$application->getBootstrap()->bootstrap( 'doctrine2' ); - -$em = $application->getBootstrap()->getResource( 'doctrine2' ); -$config = $application->getOption( 'resources' ); - -echo "Installing fixtures\n"; - - -##################################################################### -### -### MODIFY YOUR FIXTURES HERE -### -### -### First you need a "customer" object for your own IXP. -### Alter the following to suit - -$c = new \Entities\Customer(); - -$c->setName( "Somecity Internet Exchange Point" ); -$c->setShortname( "siep" ); // lowercase abbreviation (e.g. inex/ linx / lonap) -$c->setAutsys( 12345 ); // your ASN -$c->setMaxprefixes( 1000 ); // set appropriately if you peer with other members on the - // exchange. e.g friendly member providing transit - -$c->setPeeringemail( 'peering@siep.com' ); -$c->setPeeringmacro( 'AS-SIEP' ); -$c->setPeeringpolicy( \Entities\Customer::PEERING_POLICY_MANDATORY ); - -$c->setNocphone( '+353 1 123 4567' ); -$c->setNoc24hphone( '+353 1 123 4567' ); -$c->setNocfax( '+353 1 123 4568' ); -$c->setNocemail( 'noc@siep.com' ); -$c->setNochours( \Entities\Customer::NOC_HOURS_24x7 ); -$c->setNocwww( 'http://www.siep.com/noc/' ); - -$c->setCorpwww( 'http://www.siep.com/' ); - -$c->setDatejoin( new DateTime() ); - -$c->setStatus( \Entities\Customer::STATUS_NORMAL ); - -$c->setActivepeeringmatrix( true ); - -$c->setType( \Entities\Customer::TYPE_INTERNAL ); // do not change this -$c->setCreated( new DateTime() ); - -$em->persist( $c ); - - -// now you need your admin user! - -$u = new \Entities\User(); - -$u->setUsername( 'username' ); -$u->setPassword( 'letmein1' ); // if you're not using plaintext passwords, put anything here and - // use the forgotten password facility -$u->setEmail( 'username@siep.com' ); -$u->setPrivs( \Entities\User::AUTH_SUPERUSER ); -$u->setDisabled( false ); -$u->setCreator( $u->getUsername() ); -$u->setCreated( new DateTime() ); -$u->setCustomer( $c ); -$u->setParent( $u ); - -$em->persist( $u ); - -$c->setCreator( $u->getUsername() ); - -$em->flush(); - - - -##################################################################### -### -### OTHER RECOMMENDED FIXTURES -### -### No need to edit beyond this point -### - -## Vendors - -$vendors = [ - "Cisco Systems", - "Foundry Networks", - "Extreme Networks", - "Force10 Networks", - "Glimmerglass", - "Allied Telesyn", - "Enterasys", - "Dell", - "Hitachi Cable", - "MRV", - "Transmode", - "Brocade" -]; - -foreach( $vendors as $vendor ) -{ - $e = new \Entities\Vendor(); - $e->setName( $vendor ); - $em->persist( $e ); -} - -$em->flush(); - - -## IRRDBs - -$irrdbs = [ - - [ - 'host' => 'whois.ripe.net', - 'protocol' => 'ripe', - 'source' => 'RIPE', - 'notes' => 'RIPE Query from RIPE Database' - ], - - [ - 'host' => 'whois.radb.net', - 'protocol' => 'irrd', - 'source' => 'RADB', - 'notes' => 'RADB Query from RADB Database' - ], - - [ - 'host' => 'whois.lacnic.net', - 'protocol' => 'ripe', - 'source' => 'LACNIC', - 'notes' => 'LACNIC Query from LACNIC Database' - ], - - [ - 'host' => 'whois.apnic.net', - 'protocol' => 'ripe', - 'source' => 'APNIC', - 'notes' => 'APNIC Query from APNIC Database' - ], - - [ - 'host' => 'rr.level3.net', - 'protocol' => 'ripe', - 'source' => 'LEVEL3', - 'notes' => 'Level3 Query from Level3 Database' - ], - - [ - 'host' => 'whois.radb.net', - 'protocol' => 'irrd', - 'source' => 'ARIN', - 'notes' => 'ARIN Query from RADB Database' - ], - - [ - 'host' => 'whois.radb.net', - 'protocol' => 'irrd', - 'source' => 'RADB,ARIN', - 'notes' => 'RADB+ARIN Query from RADB Database' - ], - - [ - 'host' => 'whois.radb.net', - 'protocol' => 'irrd', - 'source' => 'ALTDB', - 'notes' => 'ALTDB Query from RADB Database' - ], - - [ - 'host' => 'whois.radb.net', - 'protocol' => 'irrd', - 'source' => 'RADB,RIPE', - 'notes' => 'RADB+RIPE Query from RADB Database' - ], - - [ - 'host' => 'whois.radb.net', - 'protocol' => 'irrd', - 'source' => 'RADB,APNIC,ARIN', - 'notes' => 'RADB+APNIC+ARIN Query from RADB Database' - ] - -]; - - - -foreach( $irrdbs as $irrdb ) -{ - $e = new \Entities\IRRDBConfig(); - $e->setHost( $irrdb['host'] ); - $e->setProtocol( $irrdb['protocol'] ); - $e->setSource( $irrdb['source'] ); - $e->setNotes( $irrdb['notes'] ); - $em->persist( $e ); -} - -$em->flush(); - - -echo "Fixtures installed successfully\n"; - diff --git a/bin/gen-sql-ipv4-inserts.sh b/bin/gen-sql-ipv4-inserts.sh deleted file mode 100755 index 8f61a4e98..000000000 --- a/bin/gen-sql-ipv4-inserts.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash - -if [[ $# -ne 4 ]]; then - echo "IXP Manager - A web application to assist in the management of IXPs" - echo "Copyright (C) 2009-2011 Internet Neutral Exchange Association Limited." - echo - echo "gen-sql-ipv4-inserts.sh - a utility script to generate SQL INSERT statements" - echo " to populate the IPv4 table." - echo - echo "Usage: $0 [vlanid] [start of address] [start octet] [end octet]" - echo - echo "E.g. $0 1 192.168.0. 10 20" - echo -fi; - -for i in `seq $3 $4`; do - echo "INSERT INTO \`ipv4address\` ( \`address\`, \`vlanid\` ) VALUES ( '$2$i', '$1' );" -done - - \ No newline at end of file diff --git a/bin/gen-sql-ipv6-decimal-inserts.sh b/bin/gen-sql-ipv6-decimal-inserts.sh deleted file mode 100755 index b576a9a98..000000000 --- a/bin/gen-sql-ipv6-decimal-inserts.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -if [[ $# -ne 4 ]]; then - echo "IXP Manager - A web application to assist in the management of IXPs" - echo "Copyright (C) 2009-2011 Internet Neutral Exchange Association Limited." - echo - echo "gen-sql-ipv6-decimal-inserts.sh - a utility script to generate SQL INSERT statements" - echo " to populate the IPv6 table with DECIMAL NUMBERS ONLY. useful for making like for" - echo " last octet addresses with IPv4." - echo - echo "Usage: $0 [vlanid] [start of address] [start octet] [end octet]" - echo - echo "E.g. $0 1 2001:db8:85a3::8a2e:370: 10 20" - echo -fi; - -for i in `seq $3 $4`; do - echo "INSERT INTO \`ipv6address\` ( \`address\`, \`vlanid\` ) VALUES ( '$2$i', '$1' );" -done - - \ No newline at end of file diff --git a/bin/gen-sql-ipv6-inserts.php b/bin/gen-sql-ipv6-inserts.php deleted file mode 100755 index 3a18343c1..000000000 --- a/bin/gen-sql-ipv6-inserts.php +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/php - - * - * http://www.inex.ie/ - * (c) Copyright 2009 Internet Neutral Exchange Association Ltd (INEX) - * - */ - -date_default_timezone_set( 'Europe/Dublin' ); - -require_once( dirname( __FILE__ ) . '/utils.inc' ); -define( 'APPLICATION_ENV', scriptutils_get_application_env() ); - -define( 'SCRIPT_NAME', 'ixptool - IXP Manager CLI Management Tool' ); -define( 'SCRIPT_COPY', '(c) Copyright 2010 - ' . date( 'Y' ) . ' Internet Neutral Exchange Association Ltd' ); - -error_reporting( E_ALL|E_STRICT ); - -ini_set( 'display_errors', true ); - -defined( 'APPLICATION_PATH' ) || define( 'APPLICATION_PATH', realpath(dirname(__FILE__) . '/../application' ) ); - -// Ensure library/ is on include_path -set_include_path( implode( PATH_SEPARATOR, - array( - realpath( APPLICATION_PATH . '/../library' ), - get_include_path() - ) - ) -); - -/** Zend_Application */ -require_once 'Zend/Application.php'; - -// Create application, bootstrap, and run -$application = new Zend_Application( - APPLICATION_ENV, - APPLICATION_PATH . '/configs/application.ini' -); - -try { - $application->bootstrap(); - - $bootstrap = $application->getBootstrap(); - $bootstrap->bootstrap( 'frontController' ); -} -catch( Exception $e ) -{ - die( print_r( $e, true ) ); -} - -try -{ - $opts = new Zend_Console_Getopt( - array( - 'help|h' => 'Displays usage information.', - 'action|a=s' => 'Action to perform in format of module.controller.action', - 'verbose|v' => 'Verbose messages will be dumped to the default output.', - 'development|d' => 'Enables development mode.', - 'p1=s' => 'Generic paramater #1 for various actions' - ) - ); - - $opts->parse(); -} -catch( Zend_Console_Getopt_Exception $e ) -{ - exit( $e->getMessage() ."\n\n". $e->getUsageMessage() ); -} - -if( isset( $opts->h ) ) -{ - echo SCRIPT_NAME . "\n" . SCRIPT_COPY . "\n\n"; - - echo $opts->getUsageMessage(); - exit; -} - -if( isset( $opts->a ) ) -{ - try - { - $reqRoute = array_reverse( explode( '.', $opts->a ) ); - - @list( $action, $controller, $module ) = $reqRoute; - - $front = $bootstrap->frontController; - - $front->throwExceptions( true ); - - $front->setRequest( new Zend_Controller_Request_Simple( $action, $controller, $module ) ); - $front->setRouter( new INEX_Controller_Router_Cli() ); - $front->setResponse( new Zend_Controller_Response_Cli() ); - - $front->setParam( 'noViewRenderer', true ) - ->setParam( 'disableOutputBuffering', true ); - - if( $opts->v ) - $front->setParam( 'verbose', true ); - else - $front->setParam( 'verbose', false ); - - if( $opts->p1 ) - $front->setParam( 'param1', $opts->p1 ); - - // $front->addModuleDirectory( APPLICATION_PATH . '/modules'); - - $application->run(); - } - catch( Exception $e ) - { - echo "ERROR: " . $e->getMessage() . "\n\n"; - - if( $opts->v ) - { - echo $e->getTraceAsString(); - } - } -} - diff --git a/bin/library-init.sh b/bin/library-init.sh deleted file mode 100755 index 9e8604322..000000000 --- a/bin/library-init.sh +++ /dev/null @@ -1,83 +0,0 @@ -#! /usr/bin/env bash - -# This file will set up Git / SVN externals in library/ - - -# Is SVN installed and in the path? - -svn &>/dev/null - -if [[ $? -eq 127 ]]; then - echo ERROR: SVN not installed or not in the path - exit -fi - -git &>/dev/null - -if [[ $? -eq 127 ]]; then - echo ERROR: Git not installed or not in the path - exit -fi - -LIBDIR=`dirname "$0"`/../library -TOPDIR=`dirname "$0"`/.. - -# Smarty - -if [[ -e $LIBDIR/Smarty ]]; then - echo Smarty exists - skipping! -else - svn co http://smarty-php.googlecode.com/svn/trunk/distribution/libs/ $LIBDIR/Smarty -fi - - -# Twitter form decorators -if [[ -e $LIBDIR/Bootstrap-Zend-Framework ]]; then - echo Bootstrap-Zend-Framework exists - skipping! -else - git clone git://github.com/inex/Bootstrap-Zend-Framework.git $LIBDIR/Bootstrap-Zend-Framework -fi - -# Minifier -if [[ -e $LIBDIR/Minify ]]; then - echo Minify exists - skipping! -else - git clone git://github.com/opensolutions/Minify.git $LIBDIR/Minify -fi - -# Bootbox -if [[ -e $LIBDIR/Bootbox ]]; then - echo Bootbox exists - skipping! -else - git clone https://github.com/makeusabrew/bootbox.git $LIBDIR/Bootbox -fi - -# Throbber.js -if [[ -e $LIBDIR/Throbber.js ]]; then - echo Throbber.js exists - skipping! -else - git clone https://github.com/aino/throbber.js.git $LIBDIR/Throbber.js -fi - - -# Zend - -if [[ -e $LIBDIR/Zend ]]; then - echo Zend exists - skipping! -else - svn co http://framework.zend.com/svn/framework/standard/branches/release-1.12/library/Zend/ $LIBDIR/Zend -fi - - -# OSS-Framework -if [[ -e $LIBDIR/OSS-Framework.git ]]; then - echo OSS-Framework.git exists - skipping! -else - git clone git://github.com/opensolutions/OSS-Framework.git $LIBDIR/OSS-Framework.git -fi - - -# Doctrine2 - -# INSTALL VIA PEAR - http://www.doctrine-project.org/projects/orm.html - diff --git a/bin/library-update.sh b/bin/library-update.sh deleted file mode 100755 index 1086cd7c9..000000000 --- a/bin/library-update.sh +++ /dev/null @@ -1,46 +0,0 @@ -#! /usr/bin/env bash - -# This file will set up SVN / Git externals in library/ - -# Is SVN installed and in the path? - -svn &>/dev/null - -if [[ $? -eq 127 ]]; then - echo ERROR: SVN not installed or not in the path - exit -fi - -git &>/dev/null - -if [[ $? -eq 127 ]]; then - echo ERROR: Git not installed or not in the path - exit -fi - -LIBDIR=`dirname "$0"`/../library -TOPDIR=`dirname "$0"`/.. - -cd $LIBDIR/Bootstrap-Zend-Framework -git pull -cd - - -cd $LIBDIR/Minify -git pull -cd - - -cd $LIBDIR/Bootbox -git pull -cd - - -cd $LIBDIR/Throbber.js -git pull -cd - - -for name in Smarty Zend Doctrine; do - echo -e "\n\n\n\n\n-------------\n\nUpdating $name..." - cd $LIBDIR/$name - svn up - cd - -done - diff --git a/bin/minify-options.php b/bin/minify-options.php deleted file mode 100644 index 7d796f716..000000000 --- a/bin/minify-options.php +++ /dev/null @@ -1,145 +0,0 @@ -#!/usr/bin/env php -. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted - * provided that the following conditions are met: - * - * - Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE - * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - /** - * This file contains configurable options for minify.php which should sit in the same directory - * as minify.php - */ - -// By default, compress both JS and CSS - can be over ridden by the command line -$whatToCompress = 'all'; - -// by default, be quiet -$verbose = true; - -// We use APPLICATION_PATH as per the Zend framework. Feel free to remove as it's only used for the paths defined below here -defined( 'APPLICATION_PATH' ) || define( 'APPLICATION_PATH', realpath( SCRIPTDIR . '/../../application' ) ); - - -///////////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////////// -// -// JS Configuration -$js_compiler = "java -jar " . SCRIPTDIR . "/compiler.jar --compilation_level WHITESPACE_ONLY --warning_level QUIET"; - - -// JavaScript files to compress -// -// We name all files with a 3 digit prefix such as: -// 001-a-js-file.js -// 800-another.js -// and then glob() the following and sort numerically when creating the bundle: -$js_files = APPLICATION_PATH . '/../public/js/[0-9][0-9][0-9]-*.js'; - -// stick the files here -$js_dest = APPLICATION_PATH . '/../public/js'; - -// http reference as to where to find your JS files. We have a defined Smarty -// function called {genUrl} which builds up the URL and takes account of http/s -// automatically. You can just as easily put '/myapp/js/' here for example -$http_js = '{genUrl}/js'; - -// In our application, we define a var as 0 or 1 where 1 means use the bundle and 0 -// means use the individual uncompress files. I.e. production vs development. The -// script then spits out a Smarty template file we can include which is aware of the -// var variable meaning we don't need to keep our list of JS files up to date manually -// and also it means that versioned bundles get updated automatically. - -$mini_js_conditional_if = '{if isset( $config.use_minified_js ) and $config.use_minified_js}'; -$mini_js_conditional_else = '{else}'; -$mini_js_conditional_end = '{/if}'; - - -// -// set the following to false to not use this functionality and maintain it yourself - -// $js_header_file = false; - -$js_header_file = APPLICATION_PATH . '/views/header-js.phtml'; - -// We create a minified version of each JS file found. These can safely be deleted: -$del_mini_js = true; - -// do we want to keep older minified JS files? If you have old installs taking JS/CSS -// from a CDN / central repository you may want to keep these and delete manually -$del_old_js_bundles = true; - - - - - -///////////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////////// -// -// CSS Configuration -$css_compiler = "java -jar " . SCRIPTDIR . "/yuicompressor.jar -v --charset utf-8"; - -// JavaScript files to compress -// -// We name all files with a 3 digit prefix such as: -// 001-a-css-file.css -// 800-another.css -// and then glob() the following and sort numerically when creating the bundle: -$css_files = APPLICATION_PATH . '/../public/css/[0-9][0-9][0-9]-*.css'; - -// stick the files here -$css_dest = APPLICATION_PATH . '/../public/css'; - -// http reference as to where to find your CSS files. We have a defined Smarty -// function called {genUrl} which builds up the URL and takes account of http/s -// automatically. You can just as easily put '/myapp/js/' here for example -$http_css = '{genUrl}/css'; - -// In our application, we define a var as 0 or 1 where 1 means use the bundle and 0 -// means use the individual uncompress files. I.e. production vs development. The -// script then spits out a Smarty template file we can include which is aware of the -// var variable meaning we don't need to keep our list of CSS files up to date manually -// and also it means that versioned bundles get updated automatically. - -$mini_css_conditional_if = '{if isset( $config.use_minified_css ) and $config.use_minified_css}'; -$mini_css_conditional_else = '{else}'; -$mini_css_conditional_end = '{/if}'; - -// -// set the following to false to not use this functionality and maintain it yourself - -// $css_header_file = false; - -$css_header_file = APPLICATION_PATH . '/views/header-css.phtml'; - -// We create a minified version of each CSS file found. These can safely be deleted: -$del_mini_css = true; - -// do we want to keep older minified CSS files? If you have old installs taking JS/CSS -// from a CDN / central repository you may want to keep these and delete manually -$del_old_css_bundles = true; - - diff --git a/bin/schema-update.sh b/bin/schema-update.sh deleted file mode 100755 index bf317443e..000000000 --- a/bin/schema-update.sh +++ /dev/null @@ -1,14 +0,0 @@ -#! /bin/bash - -sudo /etc/init.d/memcached restart -rm ../doctrine/schema/* -cp ~/Shared/Customers/INEX/ORM/xml/*xml ../doctrine/schema/ -./doctrine2-cli.php orm:generate-entities ../application/ -./doctrine2-cli.php orm:generate-proxies -./doctrine2-cli.php orm:generate-repositories ../application/ - - -echo "#### ./doctrine2-cli.php orm:schema-tool:drop --force && ./doctrine2-cli.php orm:schema-tool:create " -echo "#### ./doctrine2-cli.php orm:schema-tool:create " -echo "#### mysqldump --single-transaction --no-create-info --complete-insert --skip-comments --disable-keys -u root inex | mysql -u root inex2 " - diff --git a/bin/sec-processor.php b/bin/sec-processor.php deleted file mode 100755 index e49631eed..000000000 --- a/bin/sec-processor.php +++ /dev/null @@ -1,297 +0,0 @@ -#! /usr/bin/env php - - * - * http://www.inex.ie/ - * (c) Copyright 2009 Internet Neutral Exchange Association Ltd (INEX) - * - */ - - -/** - * Instructions: - * ============= - * - * In sec.conf files, call this script with an action such as: - * - * action=pipe 'type=%s:switch=$1:mac=$2:port=$3' /home/barryo/ixp-manager-v2/bin/sec-test.php - * - * Note how the parameters are piped to this script. This script reads a line from stdin and - * parses for param1=val1:param2=val2:... - * - * If you pass the switch= param (where the value is, for example, sw01), the $switch variable - * will be populated from the INEX database with the row of the relevent switch. Passing a - * switch port as port= (where the value might be GigabitEthernet1/6) will additionally - * populate $switchPort, $physicalInterface, $virtualInterface and $cust with the - * appropriate details. - * - * Lastly, if you parse the log file date as follows: - * - * Log file line: Feb 25 06:42:51 ... - * Reg exp: ^(\w+)\s(\d+)\s(\d\d):(\d\d):(\d\d) - * - * and pass in those parameters as: - * - * 'month=$1:day=$2:hour=$3:minute=$4:second=$5' - * - * then $date will contain (in this example) "Feb 25 06:42:51" - * - * NB: only parameters explicitly ALLOWED in the switch() below will be permitted. Please add new - * parameters there. - * - */ - - - -error_reporting( E_ALL|E_STRICT ); - -ini_set( 'display_errors', true ); - -define('APPLICATION_ENV', 'production'); -defined( 'APPLICATION_PATH' ) || define( 'APPLICATION_PATH', realpath(dirname(__FILE__) . '/../application' ) ); - -set_include_path( implode( PATH_SEPARATOR, array( - realpath( APPLICATION_PATH . '/../library' ), - get_include_path(), -))); - -date_default_timezone_set( 'Europe/Dublin' ); - - -require_once 'Zend/Application.php'; - -// Create application, bootstrap, and run -$application = new Zend_Application( - APPLICATION_ENV, - APPLICATION_PATH . '/configs/application.ini' -); - -try -{ - $application->bootstrap(); - $bootstrap = $application->getBootstrap(); - $bootstrap->bootstrap( 'frontController' ); - $bootstrap->bootstrap(); - - if( $bootstrap->hasResource( 'zfdebug' ) ) - $bootstrap->unregisterPluginResource( 'zfdebug' ); - - $namespace = $bootstrap->getResource( 'namespace' ); -} -catch( Exception $e ) -{ - die( print_r( $e, true ) ); -} - -define( 'MAILTO', 'barryo@inex.ie' ); - -// Read the parameters from STDIN -if( !defined( 'STDIN' ) ) -{ - define( 'STDIN', fopen( 'php://stdin', 'r' ) ); -} - -while( !feof( STDIN ) ) -{ - $in = fgets( STDIN, 4096 ); - - $bootstrap->getResource( 'logger' )->debug( "STDIN: $in" ); - break; -} - -// Now process the parameters only allowing those explicitly listed -$args = split( '&', $in ); - -foreach( $args as $arg ) -{ - $temp = split( '=', $arg ); - - switch( $temp[0] ) - { - case 'day': - case 'hour': - case 'minute': - case 'month': - case 'second': - case 'switch': - $$temp[0] = trim( $temp[1] ); - break; - - case 'ip': - case 'mac': - case 'port': - case 'router': - case 'state': - case 'type': - $namespace->$temp[0] = trim( $temp[1] ); - break; - - default: - die( 'Illegal parameter: ' . $temp[0] . "\n" ); - break; - } -} - -// die( print_r( $namespace->getIterator(), true ) ); - -if( isset( $hour ) ) -{ - $namespace->date = ( isset( $month ) ? $month : '' ) - . ' ' . ( isset( $day ) ? $day : '' ) - . " $hour:$minute:$second"; -} -else - $namespace->date = ''; - - -// -// Populate $switch, $switchPort, $physicalInterface, $virtualInterface, and $cust -// variables if we have that information -// - -if( isset( $switch ) ) -{ - $namespace->switch = Doctrine::getTable('SwitchTable')->findOneByName( $switch ); - - if( !$namespace->switch ) - die( "No such switch: $switch\n" ); - - // Weed out core ports (and also Port-channel interfaces which are all currently core) - // FIXME Port-channel could also be a customer but we don't have any yet - - $namespace->isCorePort = false; - if( preg_match( "/^Port-channel/", $namespace->port ) ) - { - $namespace->isCorePort = true; - $namespace->switchPort = null; - } - else - { - $namespace->switchPort = Doctrine_Query::create() - ->from( 'Switchport sp' ) - ->where( 'sp.switchid = ? AND sp.name = ?', array( $namespace->switch['id'], $namespace->port ) ) - ->fetchOne(); - } - - if( $namespace->isCorePort || $namespace->switchPort['type'] == Switchport::TYPE_CORE ) - { - $namespace->isCorePort = true; - $namespace->physicalInterface = null; - $namespace->virtualInterface = null; - $namespace->cust = Doctrine::getTable( 'Cust' )->findOneByShortname( 'inex' ); - } - else - { - $namespace->physicalInterface = Doctrine::getTable( 'Physicalinterface' )->findOneBySwitchportid( $namespace->switchPort['id'] ); - $namespace->virtualInterface = Doctrine::getTable( 'Virtualinterface' )->find( $namespace->physicalInterface['virtualinterfaceid'] ); - $namespace->cust = Doctrine::getTable( 'Cust' )->find( $namespace->virtualInterface['custid'] ); - $namespace->user = Doctrine::getTable( 'User' )->findOneByCustidAndPrivs( $namespace->cust['id'], User::AUTH_CUSTADMIN ); - } -} - -if( isset( $namespace->ip ) ) -{ - $ip = $namespace->ip; - - // is it an IPv4 or a v6 address? - if( ip2long( $ip ) === false ) - { - $namespace->ipv = 6; - - // FIXME: Yeah, this isn't good. We need a better way to reliably find an IPv6 address in the DB - if( $namespace->ip = Doctrine::getTable('Ipv6address')->findOneByAddress( strtolower( $ip ) ) ) - $namespace->vlaninterface = Doctrine::getTable( 'Vlaninterface' )->findOneByIpv6addressid( $namespace->ip['id'] ); - } - else - { - $namespace->ipv = 4; - - if( $namespace->ip = Doctrine::getTable('Ipv4address')->findOneByAddress( $ip ) ) - $namespace->vlaninterface = Doctrine::getTable( 'Vlaninterface' )->findOneByIpv4addressid( $namespace->ip['id'] ); - } - - $namespace->virtualinterface = Doctrine::getTable( 'Virtualinterface' )->find( $namespace->vlaninterface['virtualinterfaceid'] ); - $namespace->cust = Doctrine::getTable( 'Cust' )->find( $namespace->virtualinterface['custid'] ); - $namespace->vlan = Doctrine::getTable( 'Vlan' )->find( $namespace->vlaninterface->vlanid ); - $namespace->user = Doctrine::getTable( 'User' )->findOneByCustidAndPrivs( $namespace->cust['id'], User::AUTH_CUSTADMIN ); -} - -// -// Now act on individual log messages -// - -$front = $bootstrap->getResource( 'frontController' ); - -$front->throwExceptions( true ); - -$front->setRouter( new INEX_Controller_Router_Cli() ); -$front->setResponse( new Zend_Controller_Response_Cli() ); - -switch( $namespace->type ) -{ - case 'BGP_AUTH': - $front->setRequest( new INEX_Controller_Request_Simple( 'bgp-auth', 'sec', null ) ); - break; - - case 'PORT_UPDOWN': - case 'LINEPROTO_UPDOWN': - $front->setRequest( new INEX_Controller_Request_Simple( 'port-updown', 'sec', null ) ); - break; - - case 'SECURITY_VIOLATION': - $front->setRequest( new INEX_Controller_Request_Simple( 'security-violation', 'sec', null ) ); - break; - - default: - // FIXME!! - break; -} - -$front->setParam( 'noViewRenderer', true ) - ->setParam( 'disableOutputBuffering', true ); - -$application->run(); - - -function switch_port_updown( $switch, $switchPort, $cust, $state, $date ) -{ - $mail = new Zend_Mail(); - $mail->setBodyText( "\n=== TEST MODE :: NO MAIL GONE TO CUSTOMERS ===\n\n" - . "Port $state alert:\n\n Switch {$switch['name']}\n Interface {$switchPort['name']}\n Date: $date\n\nPort is owned by {$cust['name']}.\n\n" - ); - $mail->setFrom( 'operations@inex.ie', 'INEX Operations' ); - $mail->addTo( MAILTO, 'INEX Operations' ); - $mail->setSubject( "Port $state alert :: {$cust['name']} :: {$switch['name']}/{$switchPort['name']}" ); - $mail->send(); -} - - -?> \ No newline at end of file diff --git a/bin/sec/SEC-README b/bin/sec/SEC-README deleted file mode 100644 index 881ab959d..000000000 --- a/bin/sec/SEC-README +++ /dev/null @@ -1,4 +0,0 @@ - -To test on a local machine: - -sec -conf=sec-test.conf -quoting -input=cisco-test.log -debug 4 -fromstart diff --git a/bin/sec/cisco-test.log b/bin/sec/cisco-test.log deleted file mode 100644 index 4dd195989..000000000 --- a/bin/sec/cisco-test.log +++ /dev/null @@ -1,2 +0,0 @@ -Feb 11 22:05:18 sw04 376333: Feb 11 22:05:18 GMT: %LINK-3-UPDOWN: TenGigabitEthernet3/3, changed state to down -Feb 11 22:05:27 sw04 376340: Feb 11 22:05:27 GMT: %LINK-3-UPDOWN: Interface TenGigabitEthernet3/3, changed state to up diff --git a/bin/sec/cisco-test.log2 b/bin/sec/cisco-test.log2 deleted file mode 100644 index 10fdfa388..000000000 --- a/bin/sec/cisco-test.log2 +++ /dev/null @@ -1,23 +0,0 @@ -Feb 11 22:05:18 sw04 376333: Feb 11 22:05:18 GMT: %LINK-3-UPDOWN: GigabitEthernet5/1, changed state to down -Feb 11 22:05:19 sw03 376335: Feb 11 22:05:18 GMT: %EC-SP-5-UNBUNDLE: Interface TenGigabitEthernet3/3 left the port-channel Port-channel3 -Feb 11 22:05:21 sw03 376338: Feb 11 22:05:19 GMT: %DTP-SP-5-NONTRUNKPORTON: Port Te3/3 has become non-trunk -Feb 11 22:05:27 sw04 376340: Feb 11 22:05:27 GMT: %LINK-3-UPDOWN: Interface GigabitEthernet5/1, changed state to up -Feb 11 22:05:27 sw03 376341: Feb 11 22:05:27 GMT: %LINEPROTO-5-UPDOWN: Line protocol on Interface Port-channel3, changed state to up -Feb 11 22:05:27 sw03 376343: Feb 11 22:05:27 GMT: %EC-SP-5-BUNDLE: Interface TenGigabitEthernet3/3 joined port-channel Port-channel3 -Feb 11 22:05:29 sw03 376346: Feb 11 22:05:28 GMT: %DTP-SP-5-TRUNKPORTON: Port Te3/3 has become dot1q trunk -Feb 11 22:09:22 sw03 376350: Feb 11 22:09:21 GMT: %LINEPROTO-5-UPDOWN: Line protocol on Interface Port-channel3, changed state to down -Feb 11 22:09:22 sw03 376351: Feb 11 22:09:21 GMT: %LINK-3-UPDOWN: Interface Port-channel3, changed state to down -Feb 11 22:09:22 sw03 376353: Feb 11 22:09:21 GMT: %EC-SP-5-UNBUNDLE: Interface TenGigabitEthernet3/3 left the port-channel Port-channel3 -Feb 11 22:09:24 sw03 376356: Feb 11 22:09:22 GMT: %DTP-SP-5-NONTRUNKPORTON: Port Te3/3 has become non-trunk -Feb 11 22:09:35 sw03 376358: Feb 11 22:09:35 GMT: %LINK-3-UPDOWN: Interface Port-channel3, changed state to up -Feb 11 22:09:35 sw03 376359: Feb 11 22:09:35 GMT: %LINEPROTO-5-UPDOWN: Line protocol on Interface Port-channel3, changed state to up -Feb 11 22:09:35 sw03 376361: Feb 11 22:09:35 GMT: %EC-SP-5-BUNDLE: Interface TenGigabitEthernet3/3 joined port-channel Port-channel3 -Feb 11 22:09:37 sw03 376364: Feb 11 22:09:36 GMT: %DTP-SP-5-TRUNKPORTON: Port Te3/3 has become dot1q trunk -Feb 11 22:27:30 sw03 376374: Feb 11 22:27:30 GMT: %LINEPROTO-5-UPDOWN: Line protocol on Interface Port-channel3, changed state to down -Feb 11 22:27:30 sw03 376375: Feb 11 22:27:30 GMT: %LINK-3-UPDOWN: Interface Port-channel3, changed state to down -Feb 11 22:27:31 sw03 376377: Feb 11 22:27:30 GMT: %EC-SP-5-UNBUNDLE: Interface TenGigabitEthernet3/3 left the port-channel Port-channel3 -Feb 11 22:27:33 sw03 376380: Feb 11 22:27:31 GMT: %DTP-SP-5-NONTRUNKPORTON: Port Te3/3 has become non-trunk -Feb 11 22:27:40 sw03 376382: Feb 11 22:27:40 GMT: %LINK-3-UPDOWN: Interface Port-channel3, changed state to up -Feb 11 22:27:40 sw03 376383: Feb 11 22:27:40 GMT: %LINEPROTO-5-UPDOWN: Line protocol on Interface Port-channel3, changed state to up -Feb 11 22:27:40 sw03 376385: Feb 11 22:27:40 GMT: %EC-SP-5-BUNDLE: Interface TenGigabitEthernet3/3 joined port-channel Port-channel3 -Feb 11 22:27:42 sw03 376388: Feb 11 22:27:40 GMT: %DTP-SP-5-TRUNKPORTON: Port Te3/3 has become dot1q trunk diff --git a/bin/sec/sec-test.conf b/bin/sec/sec-test.conf deleted file mode 100644 index ddb274ab0..000000000 --- a/bin/sec/sec-test.conf +++ /dev/null @@ -1,102 +0,0 @@ - - - - -######################################################################### -# -# Rules for BGP bad auth -# -# Will only issue one alert per port per day -# -# Feb 10 13:38:27 rc1 520169: Feb 10 13:38:26 GMT: %TCP-6-BADAUTH: No MD5 digest from 193.242.111.37(179) to 193.242.111.126(52164) (RST) -# Feb 10 13:38:27 rc1 520170: Feb 10 13:38:26 GMT: %TCP-6-BADAUTH: No MD5 digest from 2001:7F8:18::33(179) to 2001:7F8:18::F:0:1(53686) (RST) -# -# barryo@inex.ie 20100210 -######################################################################### - -type=Suppress -desc=BGP_AUTH -ptype=regexp -pattern=^(\w+)\s(\d+)\s(\d\d):(\d\d):(\d\d)\s.+rc1.*TCP-6-BADAUTH:\sNo\sMD5\sdigest\sfrom\s([0-9A-F\.:]+)\(179\).+ -context=BGP_AUTH_$6 - -type=Single -continue=TakeNext -desc=BGP_AUTH -ptype=regexp -pattern=^(\w+)\s(\d+)\s(\d\d):(\d\d):(\d\d)\s.+rc1.*TCP-6-BADAUTH:\sNo\sMD5\sdigest\sfrom\s([0-9A-F\.:]+)\(179\).+ -action=create BGP_AUTH_$6 86400 - -type=Single -continue=dontcont -desc=BGP_AUTH -ptype=regexp -pattern=^(\w+)\s(\d+)\s(\d\d):(\d\d):(\d\d)\s.+rc1.*TCP-6-BADAUTH:\sNo\sMD5\sdigest\sfrom\s([0-9A-F\.:]+)\(179\).+ -action=pipe 'type=%s&router=rc1&month=$1&day=$2&hour=$3&minute=$4&second=$5&ip=$6' /home/barryo/Zend/workspaces/DefaultWorkspace7/INEX_IXP/bin/sec-processor.php -context=BGP_AUTH_$6 - - -######################################################################### -# -# Rules for PORT UPDOWN and LINEPROTO UPDOWN -# -# Will supress line protocol up / down alerts that occur within 60 secs -# of a port up / down for the same switch / port. -# -# barryo@inex.ie 20090225 -# -######################################################################### - - -# Feb 25 06:42:50 sw01 37304: Feb 25 06:42:49 GMT: %LINEPROTO-5-UPDOWN: Line protocol on Interface GigabitEthernet1/6, changed state to up -# %LINK-3-UPDOWN: Interface Port-channel3, changed state to down -rem=Let's ignore LINEPROTO-UPDOWN notices if the port is down/up (duh!). -type=Suppress -desc=LINEPROTO_UPDOWN -ptype=regexp -pattern=^(\w+)\s(\d+)\s(\d\d):(\d\d):(\d\d)\s.+(sw\d\d).*LINEPROTO-\d-UPDOWN:\sLine\sprotocol\son\sInterface\s([a-zA-Z0-9/\-]+).* -context=PORT_UPDOWN_$6_$7 - -# Feb 25 06:39:26 sw01 37298: Feb 25 06:39:24 GMT: %LINK-SP-3-UPDOWN: Interface GigabitEthernet2/6, changed state to down -type=Single -continue=TakeNext -desc=PORT_UPDOWN -ptype=regexp -pattern=^(\w+)\s(\d+)\s(\d\d):(\d\d):(\d\d)\s.+(sw\d\d).*LINK-\d-UPDOWN:\sInterface\s([a-zA-Z0-9/\-]+),\schanged\sstate\sto\s(\w+) -action=create PORT_UPDOWN_$6_$7 60 - -type=Single -continue=DontCont -desc=PORT_UPDOWN -ptype=regexp -pattern=^(\w+)\s(\d+)\s(\d\d):(\d\d):(\d\d)\s.+(sw\d\d).*LINK-\d-UPDOWN:\sInterface\s([a-zA-Z0-9/\-]+),\schanged\sstate\sto\s(\w+) -action=pipe 'type=%s&switch=$6&port=$7&state=$8&month=$1&day=$2&hour=$3&minute=$4&second=$5' /home/barryo/Zend/workspaces/DefaultWorkspace7/INEX_IXP/bin/sec-processor.php -context=PORT_UPDOWN_$6_$7 - -type=Single -continue=DontCont -desc=LINEPROTO_UPDOWN -ptype=regexp -pattern=^(\w+)\s(\d+)\s(\d\d):(\d\d):(\d\d)\s.+(sw\d\d).*LINEPROTO-\d-UPDOWN:\sLine\sprotocol\son\sInterface\s([a-zA-Z0-9/\-]+),\schanged\sstate\sto\s(\w+) -action=pipe 'type=%s&switch=$6&port=$7&state=$8&month=$1&day=$2&hour=$3&minute=$4&second=$5' /home/barryo/Zend/workspaces/DefaultWorkspace7/INEX_IXP/bin/sec-processor.php - -# Suppress similar / duplicate UPDOWN messages (of type 'SP') for LINK and LINEPROTO -# These should be all duplicates so we'll use the CONTEXT from above to match and allow a -# default handler at the end of this config catch anything else. -type=Single -continue=DontCont -desc=PORT_UPDOWN -ptype=regexp -pattern=^(\w+)\s(\d+)\s(\d\d):(\d\d):(\d\d)\s.+(sw\d\d).*LINK-SP-\d-UPDOWN:\sInterface\s([a-zA-Z0-9/\-]+),\schanged\sstate\sto\s(\w+) -context=PORT_UPDOWN_$6_$7 -action=none - -type=Single -continue=DontCont -desc=PORT_UPDOWN -ptype=regexp -pattern=^(\w+)\s(\d+)\s(\d\d):(\d\d):(\d\d)\s.+(sw\d\d).*LINEPROTO-SP-\d-UPDOWN:\sLine\sprotocol\son\sInterface\s([a-zA-Z0-9/\-]+).* -context=PORT_UPDOWN_$6_$7 -action=none - - diff --git a/bin/update-copyright-notice.sh b/bin/update-copyright-notice.sh new file mode 100755 index 000000000..c64cfb622 --- /dev/null +++ b/bin/update-copyright-notice.sh @@ -0,0 +1,27 @@ +#!/bin/sh +# +# update-copyright-notice.sh +# +# Copyright (C) 2009 - 2019 Internet Neutral Exchange Association Company Limited By Guarantee. +# All Rights Reserved. +# +# This file is part of IXP Manager. +# +# IXP Manager is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the Free +# Software Foundation, version v2.0 of the License. +# +# IXP Manager is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License v2.0 +# along with IXP Manager. If not, see: +# +# http://www.gnu.org/licenses/gpl-2.0.html +# + +DATE=`date +%Y` + +grep --null -lir 'copyright.*internet neutral' . | xargs -0 perl -pni.bak -e 's/(Copyright.*\d{4})\s*-\s*\d{4}\s(Internet Neutral)/\1 - '${DATE}' \2/' diff --git a/bin/utils.inc b/bin/utils.inc deleted file mode 100755 index 10158478f..000000000 --- a/bin/utils.inc +++ /dev/null @@ -1,56 +0,0 @@ -singleton( + Illuminate\Contracts\Http\Kernel::class, + IXP\Http\Kernel::class +); + +$app->singleton( + Illuminate\Contracts\Console\Kernel::class, + IXP\Console\Kernel::class +); + +$app->singleton( + Illuminate\Contracts\Debug\ExceptionHandler::class, + IXP\Exceptions\Handler::class +); + +/* +|-------------------------------------------------------------------------- +| Return The Application +|-------------------------------------------------------------------------- +| +| This script returns the application instance. The instance is given to +| the calling script so we can separate the building of the instances +| from the actual running of the application and sending responses. +| +*/ + +return $app; diff --git a/bootstrap/cache/.gitignore b/bootstrap/cache/.gitignore new file mode 100644 index 000000000..d6b7ef32c --- /dev/null +++ b/bootstrap/cache/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/composer.json b/composer.json new file mode 100644 index 000000000..eed05d13f --- /dev/null +++ b/composer.json @@ -0,0 +1,124 @@ +{ + "name": "inex/ixp-manager", + "description": "An application by INEX to manage Internet Exchange Points (IXPs) - https://www.ixpmanager.org/", + "type": "project", + "keywords": ["ixp manager", "ixpm", "inex", "ixp"], + "homepage": "https://github.com/inex/IXP-Manager/wiki", + "license": "GPL-2.0", + "authors": [ + { + "name": "Barry O'Donovan", + "email": "barry@opensolutions.ie", + "homepage": "https://www.ixpmanager.org/" + }, + { + "name": "Nick Hilliard", + "email": "nick@inex.ie", + "homepage": "https://www.ixpmanager.org/" + } + ], + "repositories":[ + { + "type": "vcs", + "url": "git@github.com:barryo/Purifier.git" + } + ], + "require": { + "php": "^8.4", + "ext-json": "*", + "laravel/framework": "^12.0", + "anahkiasen/former": "^5.1.1", + "bacon/bacon-qr-code": "^3.0.0", + "barryvdh/laravel-dompdf": "^3.0.0", + "erusev/parsedown": "^1.7", + "opensolutions/foil": "^0.7.2", + "intervention/image": "^2.7", + "laravel/horizon": "^5.7", + "laravel/telescope": "^5.0", + "laravel/tinker": "^2.10.1", + "laravel/ui": "^4.0", + "mews/purifier": "^3.4", + "opensolutions/oss-snmp": "^1.0", + "php-ds/php-ds": "^1.7.0", + "pragmarx/google2fa-laravel": "^2.3.0", + "s1lentium/iptools": "^1.2", + "socialiteproviders/manager": "^4.6", + "webpatser/laravel-countries": "^1.5.4", + "wolfcast/browser-detection": "^2.9.5", + "zendesk/zendesk_api_client_php": "^2.2.10" + }, + "require-dev": { + "barryvdh/laravel-ide-helper": "^3.0", + "fakerphp/faker": "^1.23", + "laravel/boost": "^1.0", + "laravel/dusk": "^8.0", + "mockery/mockery": "^1.6", + "phpunit/phpunit": "^11.5.3", + "psalm/plugin-laravel": "^3.0.3", + "staudenmeir/dusk-updater": "^1.5", + "vimeo/psalm": "^6.0" + }, + "support": { + "issues": "https://www.ixpmanager.org/support", + "forum": "https://www.inex.ie/mailman/listinfo/ixpmanager", + "docs": "https://docs.ixpmanager.org/", + "source": "https://github.com/inex/IXP-Manager" + }, + "autoload": { + "psr-4": { + "IXP\\": "app/", + "Database\\Seeders\\": "database/seeders/", + "SocialiteProviders\\PeeringDB\\": "data/SocialiteProviders/PeeringDB/" + }, + "files": [ + "app/Support/helpers.php" + ] + }, + "autoload-dev": { + "classmap": [ + "tests/TestCase.php" + ], + "psr-4": { + "Tests\\": "tests/" + } + }, + "extra": { + "laravel": { + "dont-discover": [] + } + }, + "scripts": { + "post-autoload-dump": [ + "@php artisan config:clear", + "@php artisan clear-compiled", + "@php artisan package:discover" + ], + + "post-install-cmd": [ + "@php artisan clear-compiled" + ], + + "post-update-cmd": [ + "@php artisan clear-compiled", + "Illuminate\\Foundation\\ComposerScripts::postUpdate", + "@php artisan ide-helper:generate", + "@php artisan ide-helper:meta --filename=.phpstorm.meta.php/laravel.meta.php", + "@php artisan ide-helper:models -W", + "@php artisan telescope:publish", + "@php artisan horizon:install" + ] + }, + "config": { + "optimize-autoloader": true, + "preferred-install": "dist", + "sort-packages": true, + "allow-plugins": { + "pestphp/pest-plugin": true + }, + "platform": { + "php": "8.4.11" + } + }, + "minimum-stability": "dev", + "prefer-stable": true +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 000000000..4c7aa4b34 --- /dev/null +++ b/composer.lock @@ -0,0 +1,13257 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "1c228264bc28a4ffeb815a57cc92a2dc", + "packages": [ + { + "name": "anahkiasen/former", + "version": "5.1.1", + "source": { + "type": "git", + "url": "https://github.com/formers/former.git", + "reference": "7f2e8489ee019ff4142dc73c0da177d0796935d2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/formers/former/zipball/7f2e8489ee019ff4142dc73c0da177d0796935d2", + "reference": "7f2e8489ee019ff4142dc73c0da177d0796935d2", + "shasum": "" + }, + "require": { + "illuminate/config": "^5.1.3|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/container": "^5.1.3|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/contracts": "^5.1.3|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/http": "^5.1.3|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/routing": "^5.1.3|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/session": "^5.1.3|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/support": "^5.1.3|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/translation": "^5.1.3|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "kylekatarnls/html-object": "^1.5", + "php": "^7.2|^8.0" + }, + "require-dev": { + "illuminate/database": "^5.1.3|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "mockery/mockery": "^1.3", + "phpunit/phpunit": "^8.5" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Former": "Former\\Facades\\Former" + }, + "providers": [ + "Former\\FormerServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Former\\": [ + "src/Former", + "tests" + ], + "Laravel\\": "src/Laravel" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Maxime Fabre", + "email": "ehtnam6@gmail.com" + } + ], + "description": "A powerful form builder", + "homepage": "http://formers.github.io/former/", + "keywords": [ + "bootstrap", + "form", + "foundation", + "laravel" + ], + "support": { + "issues": "https://github.com/formers/former/issues", + "source": "https://github.com/formers/former/tree/5.1.1" + }, + "time": "2025-07-18T19:58:25+00:00" + }, + { + "name": "aura/html", + "version": "2.6.0", + "source": { + "type": "git", + "url": "https://github.com/auraphp/Aura.Html.git", + "reference": "2b1a53cd8d07a734a57d21a6492a915c74d04d56" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/auraphp/Aura.Html/zipball/2b1a53cd8d07a734a57d21a6492a915c74d04d56", + "reference": "2b1a53cd8d07a734a57d21a6492a915c74d04d56", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "yoast/phpunit-polyfills": "~1.0" + }, + "type": "library", + "extra": { + "aura": { + "type": "library", + "config": { + "common": "Aura\\Html\\_Config\\Common" + } + } + }, + "autoload": { + "psr-4": { + "Aura\\Html\\": "src/", + "Aura\\Html\\_Config\\": "config/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Aura.Html Contributors", + "homepage": "https://github.com/auraphp/Aura.Html/contributors" + } + ], + "description": "Provides HTML escapers and helpers, including form input helpers.", + "homepage": "https://github.com/auraphp/Aura.Html", + "keywords": [ + "Escape", + "escaper", + "escapers", + "escaping", + "form", + "helper", + "helpers", + "html", + "input" + ], + "support": { + "issues": "https://github.com/auraphp/Aura.Html/issues", + "source": "https://github.com/auraphp/Aura.Html/tree/2.6.0" + }, + "time": "2022-02-19T07:18:21+00:00" + }, + { + "name": "bacon/bacon-qr-code", + "version": "v3.0.1", + "source": { + "type": "git", + "url": "https://github.com/Bacon/BaconQrCode.git", + "reference": "f9cc1f52b5a463062251d666761178dbdb6b544f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/f9cc1f52b5a463062251d666761178dbdb6b544f", + "reference": "f9cc1f52b5a463062251d666761178dbdb6b544f", + "shasum": "" + }, + "require": { + "dasprid/enum": "^1.0.3", + "ext-iconv": "*", + "php": "^8.1" + }, + "require-dev": { + "phly/keep-a-changelog": "^2.12", + "phpunit/phpunit": "^10.5.11 || 11.0.4", + "spatie/phpunit-snapshot-assertions": "^5.1.5", + "squizlabs/php_codesniffer": "^3.9" + }, + "suggest": { + "ext-imagick": "to generate QR code images" + }, + "type": "library", + "autoload": { + "psr-4": { + "BaconQrCode\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Ben Scholzen 'DASPRiD'", + "email": "mail@dasprids.de", + "homepage": "https://dasprids.de/", + "role": "Developer" + } + ], + "description": "BaconQrCode is a QR code generator for PHP.", + "homepage": "https://github.com/Bacon/BaconQrCode", + "support": { + "issues": "https://github.com/Bacon/BaconQrCode/issues", + "source": "https://github.com/Bacon/BaconQrCode/tree/v3.0.1" + }, + "time": "2024-10-01T13:55:55+00:00" + }, + { + "name": "barryvdh/laravel-dompdf", + "version": "v3.1.1", + "source": { + "type": "git", + "url": "https://github.com/barryvdh/laravel-dompdf.git", + "reference": "8e71b99fc53bb8eb77f316c3c452dd74ab7cb25d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/barryvdh/laravel-dompdf/zipball/8e71b99fc53bb8eb77f316c3c452dd74ab7cb25d", + "reference": "8e71b99fc53bb8eb77f316c3c452dd74ab7cb25d", + "shasum": "" + }, + "require": { + "dompdf/dompdf": "^3.0", + "illuminate/support": "^9|^10|^11|^12", + "php": "^8.1" + }, + "require-dev": { + "larastan/larastan": "^2.7|^3.0", + "orchestra/testbench": "^7|^8|^9|^10", + "phpro/grumphp": "^2.5", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "PDF": "Barryvdh\\DomPDF\\Facade\\Pdf", + "Pdf": "Barryvdh\\DomPDF\\Facade\\Pdf" + }, + "providers": [ + "Barryvdh\\DomPDF\\ServiceProvider" + ] + }, + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Barryvdh\\DomPDF\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Barry vd. Heuvel", + "email": "barryvdh@gmail.com" + } + ], + "description": "A DOMPDF Wrapper for Laravel", + "keywords": [ + "dompdf", + "laravel", + "pdf" + ], + "support": { + "issues": "https://github.com/barryvdh/laravel-dompdf/issues", + "source": "https://github.com/barryvdh/laravel-dompdf/tree/v3.1.1" + }, + "funding": [ + { + "url": "https://fruitcake.nl", + "type": "custom" + }, + { + "url": "https://github.com/barryvdh", + "type": "github" + } + ], + "time": "2025-02-13T15:07:54+00:00" + }, + { + "name": "brick/math", + "version": "0.13.1", + "source": { + "type": "git", + "url": "https://github.com/brick/math.git", + "reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/brick/math/zipball/fc7ed316430118cc7836bf45faff18d5dfc8de04", + "reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.2", + "phpunit/phpunit": "^10.1", + "vimeo/psalm": "6.8.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "Brick\\Math\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Arbitrary-precision arithmetic library", + "keywords": [ + "Arbitrary-precision", + "BigInteger", + "BigRational", + "arithmetic", + "bigdecimal", + "bignum", + "bignumber", + "brick", + "decimal", + "integer", + "math", + "mathematics", + "rational" + ], + "support": { + "issues": "https://github.com/brick/math/issues", + "source": "https://github.com/brick/math/tree/0.13.1" + }, + "funding": [ + { + "url": "https://github.com/BenMorel", + "type": "github" + } + ], + "time": "2025-03-29T13:50:30+00:00" + }, + { + "name": "carbonphp/carbon-doctrine-types", + "version": "3.2.0", + "source": { + "type": "git", + "url": "https://github.com/CarbonPHP/carbon-doctrine-types.git", + "reference": "18ba5ddfec8976260ead6e866180bd5d2f71aa1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/CarbonPHP/carbon-doctrine-types/zipball/18ba5ddfec8976260ead6e866180bd5d2f71aa1d", + "reference": "18ba5ddfec8976260ead6e866180bd5d2f71aa1d", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "conflict": { + "doctrine/dbal": "<4.0.0 || >=5.0.0" + }, + "require-dev": { + "doctrine/dbal": "^4.0.0", + "nesbot/carbon": "^2.71.0 || ^3.0.0", + "phpunit/phpunit": "^10.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Carbon\\Doctrine\\": "src/Carbon/Doctrine/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "KyleKatarn", + "email": "kylekatarnls@gmail.com" + } + ], + "description": "Types to use Carbon in Doctrine", + "keywords": [ + "carbon", + "date", + "datetime", + "doctrine", + "time" + ], + "support": { + "issues": "https://github.com/CarbonPHP/carbon-doctrine-types/issues", + "source": "https://github.com/CarbonPHP/carbon-doctrine-types/tree/3.2.0" + }, + "funding": [ + { + "url": "https://github.com/kylekatarnls", + "type": "github" + }, + { + "url": "https://opencollective.com/Carbon", + "type": "open_collective" + }, + { + "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon", + "type": "tidelift" + } + ], + "time": "2024-02-09T16:56:22+00:00" + }, + { + "name": "dasprid/enum", + "version": "1.0.6", + "source": { + "type": "git", + "url": "https://github.com/DASPRiD/Enum.git", + "reference": "8dfd07c6d2cf31c8da90c53b83c026c7696dda90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/8dfd07c6d2cf31c8da90c53b83c026c7696dda90", + "reference": "8dfd07c6d2cf31c8da90c53b83c026c7696dda90", + "shasum": "" + }, + "require": { + "php": ">=7.1 <9.0" + }, + "require-dev": { + "phpunit/phpunit": "^7 || ^8 || ^9 || ^10 || ^11", + "squizlabs/php_codesniffer": "*" + }, + "type": "library", + "autoload": { + "psr-4": { + "DASPRiD\\Enum\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Ben Scholzen 'DASPRiD'", + "email": "mail@dasprids.de", + "homepage": "https://dasprids.de/", + "role": "Developer" + } + ], + "description": "PHP 7.1 enum implementation", + "keywords": [ + "enum", + "map" + ], + "support": { + "issues": "https://github.com/DASPRiD/Enum/issues", + "source": "https://github.com/DASPRiD/Enum/tree/1.0.6" + }, + "time": "2024-08-09T14:30:48+00:00" + }, + { + "name": "dflydev/dot-access-data", + "version": "v3.0.3", + "source": { + "type": "git", + "url": "https://github.com/dflydev/dflydev-dot-access-data.git", + "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/a23a2bf4f31d3518f3ecb38660c95715dfead60f", + "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f", + "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": [ + "MIT" + ], + "authors": [ + { + "name": "Dragonfly Development Inc.", + "email": "info@dflydev.com", + "homepage": "http://dflydev.com" + }, + { + "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": "Given a deep data structure, access data by dot notation.", + "homepage": "https://github.com/dflydev/dflydev-dot-access-data", + "keywords": [ + "access", + "data", + "dot", + "notation" + ], + "support": { + "issues": "https://github.com/dflydev/dflydev-dot-access-data/issues", + "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.3" + }, + "time": "2024-07-08T12:26:09+00:00" + }, + { + "name": "doctrine/inflector", + "version": "2.1.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/inflector.git", + "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/6d6c96277ea252fc1304627204c3d5e6e15faa3b", + "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^12.0 || ^13.0", + "phpstan/phpstan": "^1.12 || ^2.0", + "phpstan/phpstan-phpunit": "^1.4 || ^2.0", + "phpstan/phpstan-strict-rules": "^1.6 || ^2.0", + "phpunit/phpunit": "^8.5 || ^12.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Inflector\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "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", + "inflector", + "lowercase", + "manipulation", + "php", + "plural", + "singular", + "strings", + "uppercase", + "words" + ], + "support": { + "issues": "https://github.com/doctrine/inflector/issues", + "source": "https://github.com/doctrine/inflector/tree/2.1.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%2Finflector", + "type": "tidelift" + } + ], + "time": "2025-08-10T19:31:58+00:00" + }, + { + "name": "doctrine/lexer", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "doctrine/coding-standard": "^12", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.5", + "psalm/plugin-phpunit": "^0.18.3", + "vimeo/psalm": "^5.21" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Lexer\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "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", + "php" + ], + "support": { + "issues": "https://github.com/doctrine/lexer/issues", + "source": "https://github.com/doctrine/lexer/tree/3.0.1" + }, + "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": "2024-02-05T11:56:58+00:00" + }, + { + "name": "dompdf/dompdf", + "version": "v3.1.0", + "source": { + "type": "git", + "url": "https://github.com/dompdf/dompdf.git", + "reference": "a51bd7a063a65499446919286fb18b518177155a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dompdf/dompdf/zipball/a51bd7a063a65499446919286fb18b518177155a", + "reference": "a51bd7a063a65499446919286fb18b518177155a", + "shasum": "" + }, + "require": { + "dompdf/php-font-lib": "^1.0.0", + "dompdf/php-svg-lib": "^1.0.0", + "ext-dom": "*", + "ext-mbstring": "*", + "masterminds/html5": "^2.0", + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "ext-gd": "*", + "ext-json": "*", + "ext-zip": "*", + "mockery/mockery": "^1.3", + "phpunit/phpunit": "^7.5 || ^8 || ^9 || ^10 || ^11", + "squizlabs/php_codesniffer": "^3.5", + "symfony/process": "^4.4 || ^5.4 || ^6.2 || ^7.0" + }, + "suggest": { + "ext-gd": "Needed to process images", + "ext-gmagick": "Improves image processing performance", + "ext-imagick": "Improves image processing performance", + "ext-zlib": "Needed for pdf stream compression" + }, + "type": "library", + "autoload": { + "psr-4": { + "Dompdf\\": "src/" + }, + "classmap": [ + "lib/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1" + ], + "authors": [ + { + "name": "The Dompdf Community", + "homepage": "https://github.com/dompdf/dompdf/blob/master/AUTHORS.md" + } + ], + "description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter", + "homepage": "https://github.com/dompdf/dompdf", + "support": { + "issues": "https://github.com/dompdf/dompdf/issues", + "source": "https://github.com/dompdf/dompdf/tree/v3.1.0" + }, + "time": "2025-01-15T14:09:04+00:00" + }, + { + "name": "dompdf/php-font-lib", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/dompdf/php-font-lib.git", + "reference": "6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dompdf/php-font-lib/zipball/6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d", + "reference": "6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "^3 || ^4 || ^5 || ^6" + }, + "type": "library", + "autoload": { + "psr-4": { + "FontLib\\": "src/FontLib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1-or-later" + ], + "authors": [ + { + "name": "The FontLib Community", + "homepage": "https://github.com/dompdf/php-font-lib/blob/master/AUTHORS.md" + } + ], + "description": "A library to read, parse, export and make subsets of different types of font files.", + "homepage": "https://github.com/dompdf/php-font-lib", + "support": { + "issues": "https://github.com/dompdf/php-font-lib/issues", + "source": "https://github.com/dompdf/php-font-lib/tree/1.0.1" + }, + "time": "2024-12-02T14:37:59+00:00" + }, + { + "name": "dompdf/php-svg-lib", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/dompdf/php-svg-lib.git", + "reference": "eb045e518185298eb6ff8d80d0d0c6b17aecd9af" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dompdf/php-svg-lib/zipball/eb045e518185298eb6ff8d80d0d0c6b17aecd9af", + "reference": "eb045e518185298eb6ff8d80d0d0c6b17aecd9af", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^7.1 || ^8.0", + "sabberworm/php-css-parser": "^8.4" + }, + "require-dev": { + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Svg\\": "src/Svg" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "The SvgLib Community", + "homepage": "https://github.com/dompdf/php-svg-lib/blob/master/AUTHORS.md" + } + ], + "description": "A library to read, parse and export to PDF SVG files.", + "homepage": "https://github.com/dompdf/php-svg-lib", + "support": { + "issues": "https://github.com/dompdf/php-svg-lib/issues", + "source": "https://github.com/dompdf/php-svg-lib/tree/1.0.0" + }, + "time": "2024-04-29T13:26:35+00:00" + }, + { + "name": "dragonmantank/cron-expression", + "version": "v3.4.0", + "source": { + "type": "git", + "url": "https://github.com/dragonmantank/cron-expression.git", + "reference": "8c784d071debd117328803d86b2097615b457500" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/8c784d071debd117328803d86b2097615b457500", + "reference": "8c784d071debd117328803d86b2097615b457500", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0", + "webmozart/assert": "^1.0" + }, + "replace": { + "mtdowling/cron-expression": "^1.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.0", + "phpunit/phpunit": "^7.0|^8.0|^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Cron\\": "src/Cron/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Tankersley", + "email": "chris@ctankersley.com", + "homepage": "https://github.com/dragonmantank" + } + ], + "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due", + "keywords": [ + "cron", + "schedule" + ], + "support": { + "issues": "https://github.com/dragonmantank/cron-expression/issues", + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.4.0" + }, + "funding": [ + { + "url": "https://github.com/dragonmantank", + "type": "github" + } + ], + "time": "2024-10-09T13:47:03+00:00" + }, + { + "name": "egulias/email-validator", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/egulias/EmailValidator.git", + "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa", + "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa", + "shasum": "" + }, + "require": { + "doctrine/lexer": "^2.0 || ^3.0", + "php": ">=8.1", + "symfony/polyfill-intl-idn": "^1.26" + }, + "require-dev": { + "phpunit/phpunit": "^10.2", + "vimeo/psalm": "^5.12" + }, + "suggest": { + "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Egulias\\EmailValidator\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eduardo Gulias Davis" + } + ], + "description": "A library for validating emails against several RFCs", + "homepage": "https://github.com/egulias/EmailValidator", + "keywords": [ + "email", + "emailvalidation", + "emailvalidator", + "validation", + "validator" + ], + "support": { + "issues": "https://github.com/egulias/EmailValidator/issues", + "source": "https://github.com/egulias/EmailValidator/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/egulias", + "type": "github" + } + ], + "time": "2025-03-06T22:45:56+00:00" + }, + { + "name": "erusev/parsedown", + "version": "1.7.4", + "source": { + "type": "git", + "url": "https://github.com/erusev/parsedown.git", + "reference": "cb17b6477dfff935958ba01325f2e8a2bfa6dab3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/erusev/parsedown/zipball/cb17b6477dfff935958ba01325f2e8a2bfa6dab3", + "reference": "cb17b6477dfff935958ba01325f2e8a2bfa6dab3", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35" + }, + "type": "library", + "autoload": { + "psr-0": { + "Parsedown": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Emanuil Rusev", + "email": "hello@erusev.com", + "homepage": "http://erusev.com" + } + ], + "description": "Parser for Markdown.", + "homepage": "http://parsedown.org", + "keywords": [ + "markdown", + "parser" + ], + "support": { + "issues": "https://github.com/erusev/parsedown/issues", + "source": "https://github.com/erusev/parsedown/tree/1.7.x" + }, + "time": "2019-12-30T22:54:17+00:00" + }, + { + "name": "evenement/evenement", + "version": "v2.1.0", + "source": { + "type": "git", + "url": "https://github.com/igorw/evenement.git", + "reference": "6ba9a777870ab49f417e703229d53931ed40fd7a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/igorw/evenement/zipball/6ba9a777870ab49f417e703229d53931ed40fd7a", + "reference": "6ba9a777870ab49f417e703229d53931ed40fd7a", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0||^5.7||^4.8.35" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "psr-0": { + "Evenement": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + } + ], + "description": "Événement is a very simple event dispatching library for PHP", + "keywords": [ + "event-dispatcher", + "event-emitter" + ], + "support": { + "issues": "https://github.com/igorw/evenement/issues", + "source": "https://github.com/igorw/evenement/tree/master" + }, + "time": "2017-07-17T17:39:19+00:00" + }, + { + "name": "ezyang/htmlpurifier", + "version": "v4.18.0", + "source": { + "type": "git", + "url": "https://github.com/ezyang/htmlpurifier.git", + "reference": "cb56001e54359df7ae76dc522d08845dc741621b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/cb56001e54359df7ae76dc522d08845dc741621b", + "reference": "cb56001e54359df7ae76dc522d08845dc741621b", + "shasum": "" + }, + "require": { + "php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" + }, + "require-dev": { + "cerdic/css-tidy": "^1.7 || ^2.0", + "simpletest/simpletest": "dev-master" + }, + "suggest": { + "cerdic/css-tidy": "If you want to use the filter 'Filter.ExtractStyleBlocks'.", + "ext-bcmath": "Used for unit conversion and imagecrash protection", + "ext-iconv": "Converts text to and from non-UTF-8 encodings", + "ext-tidy": "Used for pretty-printing HTML" + }, + "type": "library", + "autoload": { + "files": [ + "library/HTMLPurifier.composer.php" + ], + "psr-0": { + "HTMLPurifier": "library/" + }, + "exclude-from-classmap": [ + "/library/HTMLPurifier/Language/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1-or-later" + ], + "authors": [ + { + "name": "Edward Z. Yang", + "email": "admin@htmlpurifier.org", + "homepage": "http://ezyang.com" + } + ], + "description": "Standards compliant HTML filter written in PHP", + "homepage": "http://htmlpurifier.org/", + "keywords": [ + "html" + ], + "support": { + "issues": "https://github.com/ezyang/htmlpurifier/issues", + "source": "https://github.com/ezyang/htmlpurifier/tree/v4.18.0" + }, + "time": "2024-11-01T03:51:45+00:00" + }, + { + "name": "firebase/php-jwt", + "version": "v6.11.1", + "source": { + "type": "git", + "url": "https://github.com/firebase/php-jwt.git", + "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/firebase/php-jwt/zipball/d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", + "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", + "shasum": "" + }, + "require": { + "php": "^8.0" + }, + "require-dev": { + "guzzlehttp/guzzle": "^7.4", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", + "psr/cache": "^2.0||^3.0", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0" + }, + "suggest": { + "ext-sodium": "Support EdDSA (Ed25519) signatures", + "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present" + }, + "type": "library", + "autoload": { + "psr-4": { + "Firebase\\JWT\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Neuman Vong", + "email": "neuman+pear@twilio.com", + "role": "Developer" + }, + { + "name": "Anant Narayanan", + "email": "anant@php.net", + "role": "Developer" + } + ], + "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.", + "homepage": "https://github.com/firebase/php-jwt", + "keywords": [ + "jwt", + "php" + ], + "support": { + "issues": "https://github.com/firebase/php-jwt/issues", + "source": "https://github.com/firebase/php-jwt/tree/v6.11.1" + }, + "time": "2025-04-09T20:32:01+00:00" + }, + { + "name": "fruitcake/php-cors", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/fruitcake/php-cors.git", + "reference": "3d158f36e7875e2f040f37bc0573956240a5a38b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/3d158f36e7875e2f040f37bc0573956240a5a38b", + "reference": "3d158f36e7875e2f040f37bc0573956240a5a38b", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0", + "symfony/http-foundation": "^4.4|^5.4|^6|^7" + }, + "require-dev": { + "phpstan/phpstan": "^1.4", + "phpunit/phpunit": "^9", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-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.3.0" + }, + "funding": [ + { + "url": "https://fruitcake.nl", + "type": "custom" + }, + { + "url": "https://github.com/barryvdh", + "type": "github" + } + ], + "time": "2023-10-12T05:21:21+00:00" + }, + { + "name": "graham-campbell/result-type", + "version": "v1.1.3", + "source": { + "type": "git", + "url": "https://github.com/GrahamCampbell/Result-Type.git", + "reference": "3ba905c11371512af9d9bdd27d99b782216b6945" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/3ba905c11371512af9d9bdd27d99b782216b6945", + "reference": "3ba905c11371512af9d9bdd27d99b782216b6945", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "phpoption/phpoption": "^1.9.3" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28" + }, + "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.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type", + "type": "tidelift" + } + ], + "time": "2024-07-20T21:45:45+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "7.10.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^2.3", + "guzzlehttp/psr7": "^2.8", + "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.2", + "ext-curl": "*", + "guzzle/client-integration-tests": "3.0.2", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^8.5.39 || ^9.6.20", + "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 + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "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": "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", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.10.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": "2025-08-23T22:36:01+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "2.3.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "481557b130ef3790cf82b713667b43030dc9c957" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/481557b130ef3790cf82b713667b43030dc9c957", + "reference": "481557b130ef3790cf82b713667b43030dc9c957", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.44 || ^9.6.25" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "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" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/2.3.0" + }, + "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": "2025-08-22T14:34:08+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.8.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "21dc724a0583619cd1652f673303492272778051" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/21dc724a0583619cd1652f673303492272778051", + "reference": "21dc724a0583619cd1652f673303492272778051", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "http-interop/http-factory-tests": "0.9.0", + "phpunit/phpunit": "^8.5.44 || ^9.6.25" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "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" + }, + { + "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" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.8.0" + }, + "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": "2025-08-23T21:21:41+00:00" + }, + { + "name": "guzzlehttp/uri-template", + "version": "v1.0.5", + "source": { + "type": "git", + "url": "https://github.com/guzzle/uri-template.git", + "reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/uri-template/zipball/4f4bbd4e7172148801e76e3decc1e559bdee34e1", + "reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "symfony/polyfill-php80": "^1.24" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.44 || ^9.6.25", + "uri-template/tests": "1.0.0" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "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.5" + }, + "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": "2025-08-22T14:27:06+00:00" + }, + { + "name": "igorw/get-in", + "version": "v1.0.3", + "source": { + "type": "git", + "url": "https://github.com/igorw/get-in.git", + "reference": "170ded831f49abc6a6061f655aba9bdbcf7b8111" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/igorw/get-in/zipball/170ded831f49abc6a6061f655aba9bdbcf7b8111", + "reference": "170ded831f49abc6a6061f655aba9bdbcf7b8111", + "shasum": "" + }, + "require": { + "php": ">=5.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "files": [ + "src/get_in.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + } + ], + "description": "Functions for for hash map (assoc array) traversal.", + "keywords": [ + "assoc-array", + "hash-map" + ], + "support": { + "issues": "https://github.com/igorw/get-in/issues", + "source": "https://github.com/igorw/get-in/tree/v1.0.3" + }, + "time": "2014-12-15T23:03:51+00:00" + }, + { + "name": "intervention/image", + "version": "2.7.2", + "source": { + "type": "git", + "url": "https://github.com/Intervention/image.git", + "reference": "04be355f8d6734c826045d02a1079ad658322dad" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Intervention/image/zipball/04be355f8d6734c826045d02a1079ad658322dad", + "reference": "04be355f8d6734c826045d02a1079ad658322dad", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "guzzlehttp/psr7": "~1.1 || ^2.0", + "php": ">=5.4.0" + }, + "require-dev": { + "mockery/mockery": "~0.9.2", + "phpunit/phpunit": "^4.8 || ^5.7 || ^7.5.15" + }, + "suggest": { + "ext-gd": "to use GD library based image processing.", + "ext-imagick": "to use Imagick based image processing.", + "intervention/imagecache": "Caching extension for the Intervention Image library" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Image": "Intervention\\Image\\Facades\\Image" + }, + "providers": [ + "Intervention\\Image\\ImageServiceProvider" + ] + }, + "branch-alias": { + "dev-master": "2.4-dev" + } + }, + "autoload": { + "psr-4": { + "Intervention\\Image\\": "src/Intervention/Image" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Oliver Vogel", + "email": "oliver@intervention.io", + "homepage": "https://intervention.io/" + } + ], + "description": "Image handling and manipulation library with support for Laravel integration", + "homepage": "http://image.intervention.io/", + "keywords": [ + "gd", + "image", + "imagick", + "laravel", + "thumbnail", + "watermark" + ], + "support": { + "issues": "https://github.com/Intervention/image/issues", + "source": "https://github.com/Intervention/image/tree/2.7.2" + }, + "funding": [ + { + "url": "https://paypal.me/interventionio", + "type": "custom" + }, + { + "url": "https://github.com/Intervention", + "type": "github" + } + ], + "time": "2022-05-21T17:30:32+00:00" + }, + { + "name": "kylekatarnls/html-object", + "version": "1.5.2", + "source": { + "type": "git", + "url": "https://github.com/kylekatarnls/html-object.git", + "reference": "7e06624fa9aac2a33e683491abea163089087a88" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/kylekatarnls/html-object/zipball/7e06624fa9aac2a33e683491abea163089087a88", + "reference": "7e06624fa9aac2a33e683491abea163089087a88", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "replace": { + "anahkiasen/html-object": "self.version" + }, + "require-dev": { + "madewithlove/php-cs-fixer-config": "^1.3", + "phpunit/phpunit": "^4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.32 || ^9.6.3 || ^10.0.11", + "phpunit/phpunit-dom-assertions": "^0.1.0 || ^2.6.0", + "symfony/css-selector": "^2.6 || ^4.4.27 || ^5.4.0 || ^6.0.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "HtmlObject\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Anahkiasen", + "email": "ehtnam6@gmail.com" + }, + { + "name": "kylekatanls", + "homepage": "https://github.com/kylekatarnls" + } + ], + "description": "A set of classes to create and manipulate HTML objects abstractions", + "support": { + "source": "https://github.com/kylekatarnls/html-object/tree/1.5.2" + }, + "funding": [ + { + "url": "https://github.com/kylekatarnls", + "type": "github" + }, + { + "url": "https://opencollective.com/Carbon", + "type": "open_collective" + }, + { + "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon", + "type": "tidelift" + } + ], + "time": "2023-03-02T15:22:44+00:00" + }, + { + "name": "laravel/framework", + "version": "v12.25.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/framework.git", + "reference": "2ee2ba94ae60efd24c7a787cbb1a2f82f714bb20" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/framework/zipball/2ee2ba94ae60efd24c7a787cbb1a2f82f714bb20", + "reference": "2ee2ba94ae60efd24c7a787cbb1a2f82f714bb20", + "shasum": "" + }, + "require": { + "brick/math": "^0.11|^0.12|^0.13", + "composer-runtime-api": "^2.2", + "doctrine/inflector": "^2.0.5", + "dragonmantank/cron-expression": "^3.4", + "egulias/email-validator": "^3.2.1|^4.0", + "ext-ctype": "*", + "ext-filter": "*", + "ext-hash": "*", + "ext-mbstring": "*", + "ext-openssl": "*", + "ext-session": "*", + "ext-tokenizer": "*", + "fruitcake/php-cors": "^1.3", + "guzzlehttp/guzzle": "^7.8.2", + "guzzlehttp/uri-template": "^1.0", + "laravel/prompts": "^0.3.0", + "laravel/serializable-closure": "^1.3|^2.0", + "league/commonmark": "^2.7", + "league/flysystem": "^3.25.1", + "league/flysystem-local": "^3.25.1", + "league/uri": "^7.5.1", + "monolog/monolog": "^3.0", + "nesbot/carbon": "^3.8.4", + "nunomaduro/termwind": "^2.0", + "php": "^8.2", + "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": "^7.2.0", + "symfony/error-handler": "^7.2.0", + "symfony/finder": "^7.2.0", + "symfony/http-foundation": "^7.2.0", + "symfony/http-kernel": "^7.2.0", + "symfony/mailer": "^7.2.0", + "symfony/mime": "^7.2.0", + "symfony/polyfill-php83": "^1.31", + "symfony/polyfill-php84": "^1.31", + "symfony/polyfill-php85": "^1.31", + "symfony/process": "^7.2.0", + "symfony/routing": "^7.2.0", + "symfony/uid": "^7.2.0", + "symfony/var-dumper": "^7.2.0", + "tijsverkoyen/css-to-inline-styles": "^2.2.5", + "vlucas/phpdotenv": "^5.6.1", + "voku/portable-ascii": "^2.0.2" + }, + "conflict": { + "tightenco/collect": "<5.5.33" + }, + "provide": { + "psr/container-implementation": "1.1|2.0", + "psr/log-implementation": "1.0|2.0|3.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/concurrency": "self.version", + "illuminate/conditionable": "self.version", + "illuminate/config": "self.version", + "illuminate/console": "self.version", + "illuminate/container": "self.version", + "illuminate/contracts": "self.version", + "illuminate/cookie": "self.version", + "illuminate/database": "self.version", + "illuminate/encryption": "self.version", + "illuminate/events": "self.version", + "illuminate/filesystem": "self.version", + "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", + "spatie/once": "*" + }, + "require-dev": { + "ably/ably-php": "^1.0", + "aws/aws-sdk-php": "^3.322.9", + "ext-gmp": "*", + "fakerphp/faker": "^1.24", + "guzzlehttp/promises": "^2.0.3", + "guzzlehttp/psr7": "^2.4", + "laravel/pint": "^1.18", + "league/flysystem-aws-s3-v3": "^3.25.1", + "league/flysystem-ftp": "^3.25.1", + "league/flysystem-path-prefixing": "^3.25.1", + "league/flysystem-read-only": "^3.25.1", + "league/flysystem-sftp-v3": "^3.25.1", + "mockery/mockery": "^1.6.10", + "orchestra/testbench-core": "^10.6.0", + "pda/pheanstalk": "^5.0.6|^7.0.0", + "php-http/discovery": "^1.15", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^10.5.35|^11.5.3|^12.0.1", + "predis/predis": "^2.3|^3.0", + "resend/resend-php": "^0.10.0", + "symfony/cache": "^7.2.0", + "symfony/http-client": "^7.2.0", + "symfony/psr-http-message-bridge": "^7.2.0", + "symfony/translation": "^7.2.0" + }, + "suggest": { + "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.322.9).", + "brianium/paratest": "Required to run tests in parallel (^7.0|^8.0).", + "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.", + "ext-redis": "Required to use the Redis cache and queue drivers (^4.0|^5.0|^6.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).", + "laravel/tinker": "Required to use the tinker console command (^2.0).", + "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.25.1).", + "league/flysystem-ftp": "Required to use the Flysystem FTP driver (^3.25.1).", + "league/flysystem-path-prefixing": "Required to use the scoped driver (^3.25.1).", + "league/flysystem-read-only": "Required to use read-only disks (^3.25.1)", + "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.25.1).", + "mockery/mockery": "Required to use mocking (^1.6).", + "pda/pheanstalk": "Required to use the beanstalk queue driver (^5.0).", + "php-http/discovery": "Required to use PSR-7 bridging features (^1.15).", + "phpunit/phpunit": "Required to use assertions and run tests (^10.5.35|^11.5.3|^12.0.1).", + "predis/predis": "Required to use the predis connector (^2.3|^3.0).", + "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).", + "resend/resend-php": "Required to enable support for the Resend mail transport (^0.10.0).", + "symfony/cache": "Required to PSR-6 cache bridge (^7.2).", + "symfony/filesystem": "Required to enable support for relative symbolic links (^7.2).", + "symfony/http-client": "Required to enable support for the Symfony API mail transports (^7.2).", + "symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^7.2).", + "symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^7.2).", + "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^7.2)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "12.x-dev" + } + }, + "autoload": { + "files": [ + "src/Illuminate/Collections/functions.php", + "src/Illuminate/Collections/helpers.php", + "src/Illuminate/Events/functions.php", + "src/Illuminate/Filesystem/functions.php", + "src/Illuminate/Foundation/helpers.php", + "src/Illuminate/Log/functions.php", + "src/Illuminate/Support/functions.php", + "src/Illuminate/Support/helpers.php" + ], + "psr-4": { + "Illuminate\\": "src/Illuminate/", + "Illuminate\\Support\\": [ + "src/Illuminate/Macroable/", + "src/Illuminate/Collections/", + "src/Illuminate/Conditionable/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Laravel Framework.", + "homepage": "https://laravel.com", + "keywords": [ + "framework", + "laravel" + ], + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2025-08-18T22:20:52+00:00" + }, + { + "name": "laravel/horizon", + "version": "v5.33.3", + "source": { + "type": "git", + "url": "https://github.com/laravel/horizon.git", + "reference": "aabcd425b34005182acc4c22aae48692684cb765" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/horizon/zipball/aabcd425b34005182acc4c22aae48692684cb765", + "reference": "aabcd425b34005182acc4c22aae48692684cb765", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-pcntl": "*", + "ext-posix": "*", + "illuminate/contracts": "^9.21|^10.0|^11.0|^12.0", + "illuminate/queue": "^9.21|^10.0|^11.0|^12.0", + "illuminate/support": "^9.21|^10.0|^11.0|^12.0", + "nesbot/carbon": "^2.17|^3.0", + "php": "^8.0", + "ramsey/uuid": "^4.0", + "symfony/console": "^6.0|^7.0", + "symfony/error-handler": "^6.0|^7.0", + "symfony/polyfill-php83": "^1.28", + "symfony/process": "^6.0|^7.0" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "orchestra/testbench": "^7.0|^8.0|^9.0|^10.0", + "phpstan/phpstan": "^1.10|^2.0", + "phpunit/phpunit": "^9.0|^10.4|^11.5|^12.0", + "predis/predis": "^1.1|^2.0|^3.0" + }, + "suggest": { + "ext-redis": "Required to use the Redis PHP driver.", + "predis/predis": "Required when not using the Redis PHP driver (^1.1|^2.0|^3.0)." + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Horizon": "Laravel\\Horizon\\Horizon" + }, + "providers": [ + "Laravel\\Horizon\\HorizonServiceProvider" + ] + }, + "branch-alias": { + "dev-master": "6.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\Horizon\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Dashboard and code-driven configuration for Laravel queues.", + "keywords": [ + "laravel", + "queue" + ], + "support": { + "issues": "https://github.com/laravel/horizon/issues", + "source": "https://github.com/laravel/horizon/tree/v5.33.3" + }, + "time": "2025-08-11T14:55:49+00:00" + }, + { + "name": "laravel/prompts", + "version": "v0.3.6", + "source": { + "type": "git", + "url": "https://github.com/laravel/prompts.git", + "reference": "86a8b692e8661d0fb308cec64f3d176821323077" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/prompts/zipball/86a8b692e8661d0fb308cec64f3d176821323077", + "reference": "86a8b692e8661d0fb308cec64f3d176821323077", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.2", + "ext-mbstring": "*", + "php": "^8.1", + "symfony/console": "^6.2|^7.0" + }, + "conflict": { + "illuminate/console": ">=10.17.0 <10.25.0", + "laravel/framework": ">=10.17.0 <10.25.0" + }, + "require-dev": { + "illuminate/collections": "^10.0|^11.0|^12.0", + "mockery/mockery": "^1.5", + "pestphp/pest": "^2.3|^3.4", + "phpstan/phpstan": "^1.11", + "phpstan/phpstan-mockery": "^1.1" + }, + "suggest": { + "ext-pcntl": "Required for the spinner to be animated." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "0.3.x-dev" + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Laravel\\Prompts\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Add beautiful and user-friendly forms to your command-line applications.", + "support": { + "issues": "https://github.com/laravel/prompts/issues", + "source": "https://github.com/laravel/prompts/tree/v0.3.6" + }, + "time": "2025-07-07T14:17:42+00:00" + }, + { + "name": "laravel/serializable-closure", + "version": "v2.0.4", + "source": { + "type": "git", + "url": "https://github.com/laravel/serializable-closure.git", + "reference": "b352cf0534aa1ae6b4d825d1e762e35d43f8a841" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/b352cf0534aa1ae6b4d825d1e762e35d43f8a841", + "reference": "b352cf0534aa1ae6b4d825d1e762e35d43f8a841", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "illuminate/support": "^10.0|^11.0|^12.0", + "nesbot/carbon": "^2.67|^3.0", + "pestphp/pest": "^2.36|^3.0", + "phpstan/phpstan": "^2.0", + "symfony/var-dumper": "^6.2.0|^7.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.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": "2025-03-19T13:51:03+00:00" + }, + { + "name": "laravel/socialite", + "version": "v5.23.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/socialite.git", + "reference": "e9e0fc83b9d8d71c8385a5da20e5b95ca6234cf5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/socialite/zipball/e9e0fc83b9d8d71c8385a5da20e5b95ca6234cf5", + "reference": "e9e0fc83b9d8d71c8385a5da20e5b95ca6234cf5", + "shasum": "" + }, + "require": { + "ext-json": "*", + "firebase/php-jwt": "^6.4", + "guzzlehttp/guzzle": "^6.0|^7.0", + "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/http": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "league/oauth1-client": "^1.11", + "php": "^7.2|^8.0", + "phpseclib/phpseclib": "^3.0" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "orchestra/testbench": "^4.0|^5.0|^6.0|^7.0|^8.0|^9.0|^10.0", + "phpstan/phpstan": "^1.12.23", + "phpunit/phpunit": "^8.0|^9.3|^10.4|^11.5" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Socialite": "Laravel\\Socialite\\Facades\\Socialite" + }, + "providers": [ + "Laravel\\Socialite\\SocialiteServiceProvider" + ] + }, + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\Socialite\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Laravel wrapper around OAuth 1 & OAuth 2 libraries.", + "homepage": "https://laravel.com", + "keywords": [ + "laravel", + "oauth" + ], + "support": { + "issues": "https://github.com/laravel/socialite/issues", + "source": "https://github.com/laravel/socialite" + }, + "time": "2025-07-23T14:16:08+00:00" + }, + { + "name": "laravel/telescope", + "version": "v5.11.3", + "source": { + "type": "git", + "url": "https://github.com/laravel/telescope.git", + "reference": "7684604e104e7755b70dcacfeee06888e2470689" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/telescope/zipball/7684604e104e7755b70dcacfeee06888e2470689", + "reference": "7684604e104e7755b70dcacfeee06888e2470689", + "shasum": "" + }, + "require": { + "ext-json": "*", + "laravel/framework": "^8.37|^9.0|^10.0|^11.0|^12.0", + "php": "^8.0", + "symfony/console": "^5.3|^6.0|^7.0", + "symfony/var-dumper": "^5.0|^6.0|^7.0" + }, + "require-dev": { + "ext-gd": "*", + "guzzlehttp/guzzle": "^6.0|^7.0", + "laravel/octane": "^1.4|^2.0|dev-develop", + "orchestra/testbench": "^6.40|^7.37|^8.17|^9.0|^10.0", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.0|^10.5|^11.5" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Telescope\\TelescopeServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Telescope\\": "src/", + "Laravel\\Telescope\\Database\\Factories\\": "database/factories/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Mohamed Said", + "email": "mohamed@laravel.com" + } + ], + "description": "An elegant debug assistant for the Laravel framework.", + "keywords": [ + "debugging", + "laravel", + "monitoring" + ], + "support": { + "issues": "https://github.com/laravel/telescope/issues", + "source": "https://github.com/laravel/telescope/tree/v5.11.3" + }, + "time": "2025-08-21T14:25:40+00:00" + }, + { + "name": "laravel/tinker", + "version": "v2.10.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/tinker.git", + "reference": "22177cc71807d38f2810c6204d8f7183d88a57d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/tinker/zipball/22177cc71807d38f2810c6204d8f7183d88a57d3", + "reference": "22177cc71807d38f2810c6204d8f7183d88a57d3", + "shasum": "" + }, + "require": { + "illuminate/console": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "php": "^7.2.5|^8.0", + "psy/psysh": "^0.11.1|^0.12.0", + "symfony/var-dumper": "^4.3.4|^5.0|^6.0|^7.0" + }, + "require-dev": { + "mockery/mockery": "~1.3.3|^1.4.2", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^8.5.8|^9.3.3|^10.0" + }, + "suggest": { + "illuminate/database": "The Illuminate Database package (^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0)." + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Tinker\\TinkerServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Tinker\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Powerful REPL for the Laravel framework.", + "keywords": [ + "REPL", + "Tinker", + "laravel", + "psysh" + ], + "support": { + "issues": "https://github.com/laravel/tinker/issues", + "source": "https://github.com/laravel/tinker/tree/v2.10.1" + }, + "time": "2025-01-27T14:24:01+00:00" + }, + { + "name": "laravel/ui", + "version": "v4.6.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/ui.git", + "reference": "7d6ffa38d79f19c9b3e70a751a9af845e8f41d88" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/ui/zipball/7d6ffa38d79f19c9b3e70a751a9af845e8f41d88", + "reference": "7d6ffa38d79f19c9b3e70a751a9af845e8f41d88", + "shasum": "" + }, + "require": { + "illuminate/console": "^9.21|^10.0|^11.0|^12.0", + "illuminate/filesystem": "^9.21|^10.0|^11.0|^12.0", + "illuminate/support": "^9.21|^10.0|^11.0|^12.0", + "illuminate/validation": "^9.21|^10.0|^11.0|^12.0", + "php": "^8.0", + "symfony/console": "^6.0|^7.0" + }, + "require-dev": { + "orchestra/testbench": "^7.35|^8.15|^9.0|^10.0", + "phpunit/phpunit": "^9.3|^10.4|^11.5" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Ui\\UiServiceProvider" + ] + }, + "branch-alias": { + "dev-master": "4.x-dev" + } + }, + "autoload": { + "psr-4": { + "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.6.1" + }, + "time": "2025-01-28T15:15:29+00:00" + }, + { + "name": "league/commonmark", + "version": "2.7.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/commonmark.git", + "reference": "10732241927d3971d28e7ea7b5712721fa2296ca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/10732241927d3971d28e7ea7b5712721fa2296ca", + "reference": "10732241927d3971d28e7ea7b5712721fa2296ca", + "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.31.1", + "commonmark/commonmark.js": "0.31.1", + "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 || ^10.5.9 || ^11.0.0", + "scrutinizer/ocular": "^1.8.1", + "symfony/finder": "^5.3 | ^6.0 | ^7.0", + "symfony/process": "^5.4 | ^6.0 | ^7.0", + "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 | ^7.0", + "unleashedtech/php-coding-standard": "^3.1.1", + "vimeo/psalm": "^4.24.0 || ^5.0.0 || ^6.0.0" + }, + "suggest": { + "symfony/yaml": "v2.3+ required if using the Front Matter extension" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.8-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": "2025-07-20T12:47:49+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.30.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem.git", + "reference": "2203e3151755d874bb2943649dae1eb8533ac93e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/2203e3151755d874bb2943649dae1eb8533ac93e", + "reference": "2203e3151755d874bb2943649dae1eb8533ac93e", + "shasum": "" + }, + "require": { + "league/flysystem-local": "^3.0.0", + "league/mime-type-detection": "^1.0.0", + "php": "^8.0.2" + }, + "conflict": { + "async-aws/core": "<1.19.0", + "async-aws/s3": "<1.14.0", + "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 || ^2.0", + "async-aws/simple-s3": "^1.1 || ^2.0", + "aws/aws-sdk-php": "^3.295.10", + "composer/semver": "^3.0", + "ext-fileinfo": "*", + "ext-ftp": "*", + "ext-mongodb": "^1.3|^2", + "ext-zip": "*", + "friendsofphp/php-cs-fixer": "^3.5", + "google/cloud-storage": "^1.23", + "guzzlehttp/psr7": "^2.6", + "microsoft/azure-storage-blob": "^1.1", + "mongodb/mongodb": "^1.2|^2", + "phpseclib/phpseclib": "^3.0.36", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5.11|^10.0", + "sabre/dav": "^4.6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "File storage abstraction for PHP", + "keywords": [ + "WebDAV", + "aws", + "cloud", + "file", + "files", + "filesystem", + "filesystems", + "ftp", + "s3", + "sftp", + "storage" + ], + "support": { + "issues": "https://github.com/thephpleague/flysystem/issues", + "source": "https://github.com/thephpleague/flysystem/tree/3.30.0" + }, + "time": "2025-06-25T13:29:59+00:00" + }, + { + "name": "league/flysystem-local", + "version": "3.30.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem-local.git", + "reference": "6691915f77c7fb69adfb87dcd550052dc184ee10" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/6691915f77c7fb69adfb87dcd550052dc184ee10", + "reference": "6691915f77c7fb69adfb87dcd550052dc184ee10", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "league/flysystem": "^3.0.0", + "league/mime-type-detection": "^1.0.0", + "php": "^8.0.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\Local\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "Local filesystem adapter for Flysystem.", + "keywords": [ + "Flysystem", + "file", + "files", + "filesystem", + "local" + ], + "support": { + "source": "https://github.com/thephpleague/flysystem-local/tree/3.30.0" + }, + "time": "2025-05-21T10:34:19+00:00" + }, + { + "name": "league/mime-type-detection", + "version": "1.16.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/mime-type-detection.git", + "reference": "2d6702ff215bf922936ccc1ad31007edc76451b9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/2d6702ff215bf922936ccc1ad31007edc76451b9", + "reference": "2d6702ff215bf922936ccc1ad31007edc76451b9", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.2", + "phpstan/phpstan": "^0.12.68", + "phpunit/phpunit": "^8.5.8 || ^9.3 || ^10.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\MimeTypeDetection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "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.16.0" + }, + "funding": [ + { + "url": "https://github.com/frankdejonge", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/flysystem", + "type": "tidelift" + } + ], + "time": "2024-09-21T08:32:55+00:00" + }, + { + "name": "league/oauth1-client", + "version": "v1.11.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/oauth1-client.git", + "reference": "f9c94b088837eb1aae1ad7c4f23eb65cc6993055" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/oauth1-client/zipball/f9c94b088837eb1aae1ad7c4f23eb65cc6993055", + "reference": "f9c94b088837eb1aae1ad7c4f23eb65cc6993055", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-openssl": "*", + "guzzlehttp/guzzle": "^6.0|^7.0", + "guzzlehttp/psr7": "^1.7|^2.0", + "php": ">=7.1||>=8.0" + }, + "require-dev": { + "ext-simplexml": "*", + "friendsofphp/php-cs-fixer": "^2.17", + "mockery/mockery": "^1.3.3", + "phpstan/phpstan": "^0.12.42", + "phpunit/phpunit": "^7.5||9.5" + }, + "suggest": { + "ext-simplexml": "For decoding XML-based responses." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev", + "dev-develop": "2.0-dev" + } + }, + "autoload": { + "psr-4": { + "League\\OAuth1\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ben Corlett", + "email": "bencorlett@me.com", + "homepage": "http://www.webcomm.com.au", + "role": "Developer" + } + ], + "description": "OAuth 1.0 Client Library", + "keywords": [ + "Authentication", + "SSO", + "authorization", + "bitbucket", + "identity", + "idp", + "oauth", + "oauth1", + "single sign on", + "trello", + "tumblr", + "twitter" + ], + "support": { + "issues": "https://github.com/thephpleague/oauth1-client/issues", + "source": "https://github.com/thephpleague/oauth1-client/tree/v1.11.0" + }, + "time": "2024-12-10T19:59:05+00:00" + }, + { + "name": "league/uri", + "version": "7.5.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri.git", + "reference": "81fb5145d2644324614cc532b28efd0215bda430" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri/zipball/81fb5145d2644324614cc532b28efd0215bda430", + "reference": "81fb5145d2644324614cc532b28efd0215bda430", + "shasum": "" + }, + "require": { + "league/uri-interfaces": "^7.5", + "php": "^8.1" + }, + "conflict": { + "league/uri-schemes": "^1.0" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-fileinfo": "to create Data URI from file contennts", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "jeremykendall/php-domain-parser": "to resolve Public Suffix and Top Level Domain", + "league/uri-components": "Needed to easily manipulate URI objects components", + "php-64bit": "to improve IPV4 host parsing", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Uri\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "URI manipulation library", + "homepage": "https://uri.thephpleague.com", + "keywords": [ + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "middleware", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc3986", + "rfc3987", + "rfc6570", + "uri", + "uri-template", + "url", + "ws" + ], + "support": { + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri/tree/7.5.1" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2024-12-08T08:40:02+00:00" + }, + { + "name": "league/uri-interfaces", + "version": "7.5.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri-interfaces.git", + "reference": "08cfc6c4f3d811584fb09c37e2849e6a7f9b0742" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/08cfc6c4f3d811584fb09c37e2849e6a7f9b0742", + "reference": "08cfc6c4f3d811584fb09c37e2849e6a7f9b0742", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^8.1", + "psr/http-factory": "^1", + "psr/http-message": "^1.1 || ^2.0" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "php-64bit": "to improve IPV4 host parsing", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Uri\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "Common interfaces and classes for URI representation and interaction", + "homepage": "https://uri.thephpleague.com", + "keywords": [ + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc3986", + "rfc3987", + "rfc6570", + "uri", + "url", + "ws" + ], + "support": { + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri-interfaces/tree/7.5.0" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2024-12-08T08:18:47+00:00" + }, + { + "name": "masterminds/html5", + "version": "2.10.0", + "source": { + "type": "git", + "url": "https://github.com/Masterminds/html5-php.git", + "reference": "fcf91eb64359852f00d921887b219479b4f21251" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/fcf91eb64359852f00d921887b219479b4f21251", + "reference": "fcf91eb64359852f00d921887b219479b4f21251", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Masterminds\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matt Butcher", + "email": "technosophos@gmail.com" + }, + { + "name": "Matt Farina", + "email": "matt@mattfarina.com" + }, + { + "name": "Asmir Mustafic", + "email": "goetas@gmail.com" + } + ], + "description": "An HTML5 parser and serializer.", + "homepage": "http://masterminds.github.io/html5-php", + "keywords": [ + "HTML5", + "dom", + "html", + "parser", + "querypath", + "serializer", + "xml" + ], + "support": { + "issues": "https://github.com/Masterminds/html5-php/issues", + "source": "https://github.com/Masterminds/html5-php/tree/2.10.0" + }, + "time": "2025-07-25T09:04:22+00:00" + }, + { + "name": "mews/purifier", + "version": "3.4.3", + "source": { + "type": "git", + "url": "https://github.com/mewebstudio/Purifier.git", + "reference": "acc71bc512dcf9b87144546d0e3055fc76d244ff" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mewebstudio/Purifier/zipball/acc71bc512dcf9b87144546d0e3055fc76d244ff", + "reference": "acc71bc512dcf9b87144546d0e3055fc76d244ff", + "shasum": "" + }, + "require": { + "ezyang/htmlpurifier": "^4.16.0", + "illuminate/config": "^5.8|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/filesystem": "^5.8|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/support": "^5.8|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "php": "^7.2|^8.0" + }, + "require-dev": { + "graham-campbell/testbench": "^3.2|^5.5.1|^6.1", + "mockery/mockery": "^1.3.3", + "phpunit/phpunit": "^8.0|^9.0|^10.0" + }, + "suggest": { + "laravel/framework": "To test the Laravel bindings", + "laravel/lumen-framework": "To test the Lumen bindings" + }, + "type": "package", + "extra": { + "laravel": { + "aliases": { + "Purifier": "Mews\\Purifier\\Facades\\Purifier" + }, + "providers": [ + "Mews\\Purifier\\PurifierServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Mews\\Purifier\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Muharrem ERİN", + "email": "me@mewebstudio.com", + "homepage": "https://github.com/mewebstudio", + "role": "Developer" + } + ], + "description": "Laravel 5/6/7/8/9/10 HtmlPurifier Package", + "homepage": "https://github.com/mewebstudio/purifier", + "keywords": [ + "Laravel Purifier", + "Laravel Security", + "Purifier", + "htmlpurifier", + "laravel HtmlPurifier", + "security", + "xss" + ], + "support": { + "issues": "https://github.com/mewebstudio/Purifier/issues", + "source": "https://github.com/mewebstudio/Purifier/tree/3.4.3" + }, + "time": "2025-02-24T16:00:29+00:00" + }, + { + "name": "mmucklo/inflect", + "version": "v0.3.0", + "source": { + "type": "git", + "url": "https://github.com/mmucklo/inflect.git", + "reference": "b665bcd3d4c23b6aa1990b6405ff96dd437689e9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mmucklo/inflect/zipball/b665bcd3d4c23b6aa1990b6405ff96dd437689e9", + "reference": "b665bcd3d4c23b6aa1990b6405ff96dd437689e9", + "shasum": "" + }, + "require": { + "php": ">=5.3.17" + }, + "require-dev": { + "phpunit/phpunit": "3.7.*" + }, + "type": "library", + "autoload": { + "psr-0": { + "Inflect": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sho Kuwamoto", + "email": "sho@kuwamoto.org" + }, + { + "name": "Matthew J. Mucklo", + "email": "mmucklo@gmail.com" + } + ], + "description": "inflect - a memoizing inflector for php", + "keywords": [ + "inflect", + "inflector", + "pluralize", + "singularize", + "urlify" + ], + "support": { + "issues": "https://github.com/mmucklo/inflect/issues", + "source": "https://github.com/mmucklo/inflect/tree/v0.3.0" + }, + "time": "2015-05-16T04:16:08+00:00" + }, + { + "name": "monolog/monolog", + "version": "3.9.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "10d85740180ecba7896c87e06a166e0c95a0e3b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/10d85740180ecba7896c87e06a166e0c95a0e3b6", + "reference": "10d85740180ecba7896c87e06a166e0c95a0e3b6", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/log": "^2.0 || ^3.0" + }, + "provide": { + "psr/log-implementation": "3.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "^3.0", + "doctrine/couchdb": "~1.0@dev", + "elasticsearch/elasticsearch": "^7 || ^8", + "ext-json": "*", + "graylog2/gelf-php": "^1.4.2 || ^2.0", + "guzzlehttp/guzzle": "^7.4.5", + "guzzlehttp/psr7": "^2.2", + "mongodb/mongodb": "^1.8", + "php-amqplib/php-amqplib": "~2.4 || ^3", + "php-console/php-console": "^3.1.8", + "phpstan/phpstan": "^2", + "phpstan/phpstan-deprecation-rules": "^2", + "phpstan/phpstan-strict-rules": "^2", + "phpunit/phpunit": "^10.5.17 || ^11.0.7", + "predis/predis": "^1.1 || ^2", + "rollbar/rollbar": "^4.0", + "ruflin/elastica": "^7 || ^8", + "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-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 library)", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Monolog\\": "src/Monolog" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "https://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "support": { + "issues": "https://github.com/Seldaek/monolog/issues", + "source": "https://github.com/Seldaek/monolog/tree/3.9.0" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", + "type": "tidelift" + } + ], + "time": "2025-03-24T10:02:05+00:00" + }, + { + "name": "nesbot/carbon", + "version": "3.10.2", + "source": { + "type": "git", + "url": "https://github.com/CarbonPHP/carbon.git", + "reference": "76b5c07b8a9d2025ed1610e14cef1f3fd6ad2c24" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/76b5c07b8a9d2025ed1610e14cef1f3fd6ad2c24", + "reference": "76b5c07b8a9d2025ed1610e14cef1f3fd6ad2c24", + "shasum": "" + }, + "require": { + "carbonphp/carbon-doctrine-types": "<100.0", + "ext-json": "*", + "php": "^8.1", + "psr/clock": "^1.0", + "symfony/clock": "^6.3.12 || ^7.0", + "symfony/polyfill-mbstring": "^1.0", + "symfony/translation": "^4.4.18 || ^5.2.1 || ^6.0 || ^7.0" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "require-dev": { + "doctrine/dbal": "^3.6.3 || ^4.0", + "doctrine/orm": "^2.15.2 || ^3.0", + "friendsofphp/php-cs-fixer": "^3.75.0", + "kylekatarnls/multi-tester": "^2.5.3", + "phpmd/phpmd": "^2.15.0", + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^2.1.17", + "phpunit/phpunit": "^10.5.46", + "squizlabs/php_codesniffer": "^3.13.0" + }, + "bin": [ + "bin/carbon" + ], + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Carbon\\Laravel\\ServiceProvider" + ] + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + }, + "branch-alias": { + "dev-2.x": "2.x-dev", + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Carbon\\": "src/Carbon/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Nesbitt", + "email": "brian@nesbot.com", + "homepage": "https://markido.com" + }, + { + "name": "kylekatarnls", + "homepage": "https://github.com/kylekatarnls" + } + ], + "description": "An API extension for DateTime that supports 281 different languages.", + "homepage": "https://carbon.nesbot.com", + "keywords": [ + "date", + "datetime", + "time" + ], + "support": { + "docs": "https://carbon.nesbot.com/docs", + "issues": "https://github.com/CarbonPHP/carbon/issues", + "source": "https://github.com/CarbonPHP/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": "2025-08-02T09:36:06+00:00" + }, + { + "name": "nette/schema", + "version": "v1.3.2", + "source": { + "type": "git", + "url": "https://github.com/nette/schema.git", + "reference": "da801d52f0354f70a638673c4a0f04e16529431d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/schema/zipball/da801d52f0354f70a638673c4a0f04e16529431d", + "reference": "da801d52f0354f70a638673c4a0f04e16529431d", + "shasum": "" + }, + "require": { + "nette/utils": "^4.0", + "php": "8.1 - 8.4" + }, + "require-dev": { + "nette/tester": "^2.5.2", + "phpstan/phpstan-nette": "^1.0", + "tracy/tracy": "^2.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-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.3.2" + }, + "time": "2024-10-06T23:10:23+00:00" + }, + { + "name": "nette/utils", + "version": "v4.0.8", + "source": { + "type": "git", + "url": "https://github.com/nette/utils.git", + "reference": "c930ca4e3cf4f17dcfb03037703679d2396d2ede" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/utils/zipball/c930ca4e3cf4f17dcfb03037703679d2396d2ede", + "reference": "c930ca4e3cf4f17dcfb03037703679d2396d2ede", + "shasum": "" + }, + "require": { + "php": "8.0 - 8.5" + }, + "conflict": { + "nette/finder": "<3", + "nette/schema": "<1.2.2" + }, + "require-dev": { + "jetbrains/phpstorm-attributes": "^1.2", + "nette/tester": "^2.5", + "phpstan/phpstan-nette": "^2.0@stable", + "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()" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "psr-4": { + "Nette\\": "src" + }, + "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.8" + }, + "time": "2025-08-06T21:43:34+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.6.1", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", + "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.1" + }, + "time": "2025-08-13T20:13:15+00:00" + }, + { + "name": "nunomaduro/termwind", + "version": "v2.3.1", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/termwind.git", + "reference": "dfa08f390e509967a15c22493dc0bac5733d9123" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/dfa08f390e509967a15c22493dc0bac5733d9123", + "reference": "dfa08f390e509967a15c22493dc0bac5733d9123", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^8.2", + "symfony/console": "^7.2.6" + }, + "require-dev": { + "illuminate/console": "^11.44.7", + "laravel/pint": "^1.22.0", + "mockery/mockery": "^1.6.12", + "pestphp/pest": "^2.36.0 || ^3.8.2", + "phpstan/phpstan": "^1.12.25", + "phpstan/phpstan-strict-rules": "^1.6.2", + "symfony/var-dumper": "^7.2.6", + "thecodingmachine/phpstan-strict-rules": "^1.0.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Termwind\\Laravel\\TermwindServiceProvider" + ] + }, + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "files": [ + "src/Functions.php" + ], + "psr-4": { + "Termwind\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Its like Tailwind CSS, but for the console.", + "keywords": [ + "cli", + "console", + "css", + "package", + "php", + "style" + ], + "support": { + "issues": "https://github.com/nunomaduro/termwind/issues", + "source": "https://github.com/nunomaduro/termwind/tree/v2.3.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": "2025-05-08T08:14:37+00:00" + }, + { + "name": "opensolutions/foil", + "version": "0.7.4", + "source": { + "type": "git", + "url": "https://github.com/opensolutions/foil.git", + "reference": "d5ac10e5266fa5badd8a94656f7f117363213ffb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/opensolutions/foil/zipball/d5ac10e5266fa5badd8a94656f7f117363213ffb", + "reference": "d5ac10e5266fa5badd8a94656f7f117363213ffb", + "shasum": "" + }, + "require": { + "aura/html": "~2.4", + "evenement/evenement": "~2.0", + "igorw/get-in": "~1.0", + "php": ">=8.1", + "pimple/pimple": "~3.0" + }, + "require-dev": { + "brain/monkey": "~1.1", + "mockery/mockery": "~1", + "phpunit/phpunit": "~10" + }, + "type": "library", + "autoload": { + "files": [ + "inc/functions.php" + ], + "psr-4": { + "Foil\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Giuseppe Mazzapica", + "email": "giuseppe.mazzapica@gmail.com", + "homepage": "http://gm.zoomlab.it", + "role": "Developer" + } + ], + "description": "PHP template engine for native PHP templates", + "homepage": "https://github.com/opensolutions/foil", + "keywords": [ + "template engine", + "templates" + ], + "support": { + "source": "https://github.com/opensolutions/foil/tree/0.7.4" + }, + "time": "2025-08-24T14:23:29+00:00" + }, + { + "name": "opensolutions/oss-snmp", + "version": "v1.0.5", + "source": { + "type": "git", + "url": "https://github.com/opensolutions/OSS_SNMP.git", + "reference": "70b7e31af21eb75ed9e405e8e31ce1fcf9193c8e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/opensolutions/OSS_SNMP/zipball/70b7e31af21eb75ed9e405e8e31ce1fcf9193c8e", + "reference": "70b7e31af21eb75ed9e405e8e31ce1fcf9193c8e", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "9.*" + }, + "type": "library", + "autoload": { + "psr-0": { + "Tests": "tests/", + "OSS_SNMP": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "A PHP SNMP library for people who hate SNMP, MIBs and OIDs!", + "keywords": [ + "opensolutions", + "oss", + "oss-snmp", + "oss_snmp", + "snmp" + ], + "support": { + "issues": "https://github.com/opensolutions/OSS_SNMP/issues", + "source": "https://github.com/opensolutions/OSS_SNMP/tree/v1.0.5" + }, + "time": "2024-11-24T13:09:03+00:00" + }, + { + "name": "paragonie/constant_time_encoding", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/paragonie/constant_time_encoding.git", + "reference": "df1e7fde177501eee2037dd159cf04f5f301a512" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/df1e7fde177501eee2037dd159cf04f5f301a512", + "reference": "df1e7fde177501eee2037dd159cf04f5f301a512", + "shasum": "" + }, + "require": { + "php": "^8" + }, + "require-dev": { + "phpunit/phpunit": "^9", + "vimeo/psalm": "^4|^5" + }, + "type": "library", + "autoload": { + "psr-4": { + "ParagonIE\\ConstantTime\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com", + "role": "Maintainer" + }, + { + "name": "Steve 'Sc00bz' Thomas", + "email": "steve@tobtu.com", + "homepage": "https://www.tobtu.com", + "role": "Original Developer" + } + ], + "description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)", + "keywords": [ + "base16", + "base32", + "base32_decode", + "base32_encode", + "base64", + "base64_decode", + "base64_encode", + "bin2hex", + "encoding", + "hex", + "hex2bin", + "rfc4648" + ], + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/constant_time_encoding/issues", + "source": "https://github.com/paragonie/constant_time_encoding" + }, + "time": "2024-05-08T12:36:18+00:00" + }, + { + "name": "paragonie/random_compat", + "version": "v9.99.100", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a", + "shasum": "" + }, + "require": { + "php": ">= 7" + }, + "require-dev": { + "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", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "polyfill", + "pseudorandom", + "random" + ], + "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": "php-ds/php-ds", + "version": "v1.7.0", + "source": { + "type": "git", + "url": "https://github.com/php-ds/polyfill.git", + "reference": "017fb5cdfa52a1f13126c94987b04b884c44f9cd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-ds/polyfill/zipball/017fb5cdfa52a1f13126c94987b04b884c44f9cd", + "reference": "017fb5cdfa52a1f13126c94987b04b884c44f9cd", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": ">=7.4" + }, + "provide": { + "ext-ds": "1.5.0" + }, + "require-dev": { + "php-ds/tests": "^1.5" + }, + "suggest": { + "ext-ds": "to improve performance and reduce memory usage" + }, + "type": "library", + "autoload": { + "psr-4": { + "Ds\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Rudi Theunissen", + "email": "rudolf.theunissen@gmail.com" + } + ], + "description": "Specialized data structures as alternatives to the PHP array", + "keywords": [ + "data structures", + "ds", + "php", + "polyfill" + ], + "support": { + "issues": "https://github.com/php-ds/polyfill/issues", + "source": "https://github.com/php-ds/polyfill/tree/v1.7.0" + }, + "time": "2025-05-18T04:50:53+00:00" + }, + { + "name": "phpoption/phpoption", + "version": "1.9.4", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/php-option.git", + "reference": "638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d", + "reference": "638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.44 || ^9.6.25 || ^10.5.53 || ^11.5.34" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + }, + "branch-alias": { + "dev-master": "1.9-dev" + } + }, + "autoload": { + "psr-4": { + "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.4" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", + "type": "tidelift" + } + ], + "time": "2025-08-21T11:53:16+00:00" + }, + { + "name": "phpseclib/phpseclib", + "version": "3.0.46", + "source": { + "type": "git", + "url": "https://github.com/phpseclib/phpseclib.git", + "reference": "56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6", + "reference": "56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6", + "shasum": "" + }, + "require": { + "paragonie/constant_time_encoding": "^1|^2|^3", + "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.46" + }, + "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": "2025-06-26T16:29:55+00:00" + }, + { + "name": "pimple/pimple", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/silexphp/Pimple.git", + "reference": "a94b3a4db7fb774b3d78dad2315ddc07629e1bed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/silexphp/Pimple/zipball/a94b3a4db7fb774b3d78dad2315ddc07629e1bed", + "reference": "a94b3a4db7fb774b3d78dad2315ddc07629e1bed", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "psr/container": "^1.1 || ^2.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "^5.4@dev" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4.x-dev" + } + }, + "autoload": { + "psr-0": { + "Pimple": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Pimple, a simple Dependency Injection Container", + "homepage": "https://pimple.symfony.com", + "keywords": [ + "container", + "dependency injection" + ], + "support": { + "source": "https://github.com/silexphp/Pimple/tree/v3.5.0" + }, + "time": "2021-10-28T11:13:42+00:00" + }, + { + "name": "pragmarx/google2fa", + "version": "v8.0.3", + "source": { + "type": "git", + "url": "https://github.com/antonioribeiro/google2fa.git", + "reference": "6f8d87ebd5afbf7790bde1ffc7579c7c705e0fad" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/antonioribeiro/google2fa/zipball/6f8d87ebd5afbf7790bde1ffc7579c7c705e0fad", + "reference": "6f8d87ebd5afbf7790bde1ffc7579c7c705e0fad", + "shasum": "" + }, + "require": { + "paragonie/constant_time_encoding": "^1.0|^2.0|^3.0", + "php": "^7.1|^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.9", + "phpunit/phpunit": "^7.5.15|^8.5|^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "PragmaRX\\Google2FA\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Antonio Carlos Ribeiro", + "email": "acr@antoniocarlosribeiro.com", + "role": "Creator & Designer" + } + ], + "description": "A One Time Password Authentication package, compatible with Google Authenticator.", + "keywords": [ + "2fa", + "Authentication", + "Two Factor Authentication", + "google2fa" + ], + "support": { + "issues": "https://github.com/antonioribeiro/google2fa/issues", + "source": "https://github.com/antonioribeiro/google2fa/tree/v8.0.3" + }, + "time": "2024-09-05T11:56:40+00:00" + }, + { + "name": "pragmarx/google2fa-laravel", + "version": "v2.3.0", + "source": { + "type": "git", + "url": "https://github.com/antonioribeiro/google2fa-laravel.git", + "reference": "60f363c16db1e94263e0560efebe9fc2e302b7ef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/antonioribeiro/google2fa-laravel/zipball/60f363c16db1e94263e0560efebe9fc2e302b7ef", + "reference": "60f363c16db1e94263e0560efebe9fc2e302b7ef", + "shasum": "" + }, + "require": { + "laravel/framework": "^5.4.36|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "php": ">=7.0", + "pragmarx/google2fa-qrcode": "^1.0|^2.0|^3.0" + }, + "require-dev": { + "bacon/bacon-qr-code": "^2.0", + "orchestra/testbench": "3.4.*|3.5.*|3.6.*|3.7.*|4.*|5.*|6.*|7.*|8.*|9.*|10.*", + "phpunit/phpunit": "~5|~6|~7|~8|~9|~10|~11" + }, + "suggest": { + "bacon/bacon-qr-code": "Required to generate inline QR Codes.", + "pragmarx/recovery": "Generate recovery codes." + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Google2FA": "PragmaRX\\Google2FALaravel\\Facade" + }, + "providers": [ + "PragmaRX\\Google2FALaravel\\ServiceProvider" + ] + }, + "component": "package", + "frameworks": [ + "Laravel" + ], + "branch-alias": { + "dev-master": "0.2-dev" + } + }, + "autoload": { + "psr-4": { + "PragmaRX\\Google2FALaravel\\": "src/", + "PragmaRX\\Google2FALaravel\\Tests\\": "tests/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Antonio Carlos Ribeiro", + "email": "acr@antoniocarlosribeiro.com", + "role": "Creator & Designer" + } + ], + "description": "A One Time Password Authentication package, compatible with Google Authenticator.", + "keywords": [ + "Authentication", + "Two Factor Authentication", + "google2fa", + "laravel" + ], + "support": { + "issues": "https://github.com/antonioribeiro/google2fa-laravel/issues", + "source": "https://github.com/antonioribeiro/google2fa-laravel/tree/v2.3.0" + }, + "time": "2025-02-26T19:39:35+00:00" + }, + { + "name": "pragmarx/google2fa-qrcode", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/antonioribeiro/google2fa-qrcode.git", + "reference": "ce4d8a729b6c93741c607cfb2217acfffb5bf76b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/antonioribeiro/google2fa-qrcode/zipball/ce4d8a729b6c93741c607cfb2217acfffb5bf76b", + "reference": "ce4d8a729b6c93741c607cfb2217acfffb5bf76b", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "pragmarx/google2fa": ">=4.0" + }, + "require-dev": { + "bacon/bacon-qr-code": "^2.0", + "chillerlan/php-qrcode": "^1.0|^2.0|^3.0|^4.0", + "khanamiryan/qrcode-detector-decoder": "^1.0", + "phpunit/phpunit": "~4|~5|~6|~7|~8|~9" + }, + "suggest": { + "bacon/bacon-qr-code": "For QR Code generation, requires imagick", + "chillerlan/php-qrcode": "For QR Code generation" + }, + "type": "library", + "extra": { + "component": "package", + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "PragmaRX\\Google2FAQRCode\\": "src/", + "PragmaRX\\Google2FAQRCode\\Tests\\": "tests/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Antonio Carlos Ribeiro", + "email": "acr@antoniocarlosribeiro.com", + "role": "Creator & Designer" + } + ], + "description": "QR Code package for Google2FA", + "keywords": [ + "2fa", + "Authentication", + "Two Factor Authentication", + "google2fa", + "qr code", + "qrcode" + ], + "support": { + "issues": "https://github.com/antonioribeiro/google2fa-qrcode/issues", + "source": "https://github.com/antonioribeiro/google2fa-qrcode/tree/v3.0.0" + }, + "time": "2021-08-15T12:53:48+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/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "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.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.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": "https://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" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.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": "https://www.php-fig.org/" + } + ], + "description": "PSR-17: 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" + }, + "time": "2024-04-15T12:06:14+00:00" + }, + { + "name": "psr/http-message", + "version": "2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+00:00" + }, + { + "name": "psr/log", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "time": "2024-09-11T13:17:53+00:00" + }, + { + "name": "psr/simple-cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "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.12.10", + "source": { + "type": "git", + "url": "https://github.com/bobthecow/psysh.git", + "reference": "6e80abe6f2257121f1eb9a4c55bf29d921025b22" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/6e80abe6f2257121f1eb9a4c55bf29d921025b22", + "reference": "6e80abe6f2257121f1eb9a4c55bf29d921025b22", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-tokenizer": "*", + "nikic/php-parser": "^5.0 || ^4.0", + "php": "^8.0 || ^7.4", + "symfony/console": "^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4", + "symfony/var-dumper": "^7.0 || ^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" + }, + "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." + }, + "bin": [ + "bin/psysh" + ], + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": false, + "forward-command": false + }, + "branch-alias": { + "dev-main": "0.12.x-dev" + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Psy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Justin Hileman", + "email": "justin@justinhileman.info" + } + ], + "description": "An interactive shell for modern PHP.", + "homepage": "https://psysh.org", + "keywords": [ + "REPL", + "console", + "interactive", + "shell" + ], + "support": { + "issues": "https://github.com/bobthecow/psysh/issues", + "source": "https://github.com/bobthecow/psysh/tree/v0.12.10" + }, + "time": "2025-08-04T12:39:37+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "ramsey/collection", + "version": "2.1.1", + "source": { + "type": "git", + "url": "https://github.com/ramsey/collection.git", + "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/collection/zipball/344572933ad0181accbf4ba763e85a0306a8c5e2", + "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "captainhook/plugin-composer": "^5.3", + "ergebnis/composer-normalize": "^2.45", + "fakerphp/faker": "^1.24", + "hamcrest/hamcrest-php": "^2.0", + "jangregor/phpstan-prophecy": "^2.1", + "mockery/mockery": "^1.6", + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.4", + "phpspec/prophecy-phpunit": "^2.3", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-mockery": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^10.5", + "ramsey/coding-standard": "^2.3", + "ramsey/conventional-commits": "^1.6", + "roave/security-advisories": "dev-latest" + }, + "type": "library", + "extra": { + "captainhook": { + "force-install": true + }, + "ramsey/conventional-commits": { + "configFile": "conventional-commits.json" + } + }, + "autoload": { + "psr-4": { + "Ramsey\\Collection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ben Ramsey", + "email": "ben@benramsey.com", + "homepage": "https://benramsey.com" + } + ], + "description": "A PHP library for representing and manipulating collections.", + "keywords": [ + "array", + "collection", + "hash", + "map", + "queue", + "set" + ], + "support": { + "issues": "https://github.com/ramsey/collection/issues", + "source": "https://github.com/ramsey/collection/tree/2.1.1" + }, + "time": "2025-03-22T05:38:12+00:00" + }, + { + "name": "ramsey/uuid", + "version": "4.9.0", + "source": { + "type": "git", + "url": "https://github.com/ramsey/uuid.git", + "reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/4e0e23cc785f0724a0e838279a9eb03f28b092a0", + "reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0", + "shasum": "" + }, + "require": { + "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13", + "php": "^8.0", + "ramsey/collection": "^1.2 || ^2.0" + }, + "replace": { + "rhumsaa/uuid": "self.version" + }, + "require-dev": { + "captainhook/captainhook": "^5.25", + "captainhook/plugin-composer": "^5.3", + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "ergebnis/composer-normalize": "^2.47", + "mockery/mockery": "^1.6", + "paragonie/random-lib": "^2", + "php-mock/php-mock": "^2.6", + "php-mock/php-mock-mockery": "^1.5", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "phpbench/phpbench": "^1.2.14", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-mockery": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^9.6", + "slevomat/coding-standard": "^8.18", + "squizlabs/php_codesniffer": "^3.13" + }, + "suggest": { + "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", + "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.", + "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.", + "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter", + "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." + }, + "type": "library", + "extra": { + "captainhook": { + "force-install": true + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Ramsey\\Uuid\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).", + "keywords": [ + "guid", + "identifier", + "uuid" + ], + "support": { + "issues": "https://github.com/ramsey/uuid/issues", + "source": "https://github.com/ramsey/uuid/tree/4.9.0" + }, + "time": "2025-06-25T14:20:11+00:00" + }, + { + "name": "s1lentium/iptools", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/S1lentium/IPTools.git", + "reference": "88be1aaaab3c50fc131ebe778e246215ff006d8e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/S1lentium/IPTools/zipball/88be1aaaab3c50fc131ebe778e246215ff006d8e", + "reference": "88be1aaaab3c50fc131ebe778e246215ff006d8e", + "shasum": "" + }, + "require": { + "ext-bcmath": "*", + "php": "^8.0" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.5", + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "IPTools\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Safarov Alisher", + "email": "alisher.safarov@outlook.com", + "homepage": "https://github.com/S1lentium" + } + ], + "description": "PHP Library for manipulating network addresses (IPv4 and IPv6)", + "keywords": [ + "IP", + "IP-Tools", + "cidr", + "ipv4", + "ipv6", + "network", + "subnet" + ], + "support": { + "issues": "https://github.com/S1lentium/IPTools/issues", + "source": "https://github.com/S1lentium/IPTools/tree/v1.2.0" + }, + "time": "2022-08-17T14:28:59+00:00" + }, + { + "name": "sabberworm/php-css-parser", + "version": "v8.9.0", + "source": { + "type": "git", + "url": "https://github.com/MyIntervals/PHP-CSS-Parser.git", + "reference": "d8e916507b88e389e26d4ab03c904a082aa66bb9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/d8e916507b88e389e26d4ab03c904a082aa66bb9", + "reference": "d8e916507b88e389e26d4ab03c904a082aa66bb9", + "shasum": "" + }, + "require": { + "ext-iconv": "*", + "php": "^5.6.20 || ^7.0.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" + }, + "require-dev": { + "phpunit/phpunit": "5.7.27 || 6.5.14 || 7.5.20 || 8.5.41", + "rawr/cross-data-providers": "^2.0.0" + }, + "suggest": { + "ext-mbstring": "for parsing UTF-8 CSS" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "9.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Sabberworm\\CSS\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Raphael Schweikert" + }, + { + "name": "Oliver Klee", + "email": "github@oliverklee.de" + }, + { + "name": "Jake Hotson", + "email": "jake.github@qzdesign.co.uk" + } + ], + "description": "Parser for CSS Files written in PHP", + "homepage": "https://www.sabberworm.com/blog/2010/6/10/php-css-parser", + "keywords": [ + "css", + "parser", + "stylesheet" + ], + "support": { + "issues": "https://github.com/MyIntervals/PHP-CSS-Parser/issues", + "source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v8.9.0" + }, + "time": "2025-07-11T13:20:48+00:00" + }, + { + "name": "socialiteproviders/manager", + "version": "v4.8.1", + "source": { + "type": "git", + "url": "https://github.com/SocialiteProviders/Manager.git", + "reference": "8180ec14bef230ec2351cff993d5d2d7ca470ef4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/SocialiteProviders/Manager/zipball/8180ec14bef230ec2351cff993d5d2d7ca470ef4", + "reference": "8180ec14bef230ec2351cff993d5d2d7ca470ef4", + "shasum": "" + }, + "require": { + "illuminate/support": "^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0", + "laravel/socialite": "^5.5", + "php": "^8.1" + }, + "require-dev": { + "mockery/mockery": "^1.2", + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "SocialiteProviders\\Manager\\ServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "SocialiteProviders\\Manager\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andy Wendt", + "email": "andy@awendt.com" + }, + { + "name": "Anton Komarev", + "email": "a.komarev@cybercog.su" + }, + { + "name": "Miguel Piedrafita", + "email": "soy@miguelpiedrafita.com" + }, + { + "name": "atymic", + "email": "atymicq@gmail.com", + "homepage": "https://atymic.dev" + } + ], + "description": "Easily add new or override built-in providers in Laravel Socialite.", + "homepage": "https://socialiteproviders.com", + "keywords": [ + "laravel", + "manager", + "oauth", + "providers", + "socialite" + ], + "support": { + "issues": "https://github.com/socialiteproviders/manager/issues", + "source": "https://github.com/socialiteproviders/manager" + }, + "time": "2025-02-24T19:33:30+00:00" + }, + { + "name": "symfony/clock", + "version": "v7.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/clock.git", + "reference": "b81435fbd6648ea425d1ee96a2d8e68f4ceacd24" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/clock/zipball/b81435fbd6648ea425d1ee96a2d8e68f4ceacd24", + "reference": "b81435fbd6648ea425d1ee96a2d8e68f4ceacd24", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/clock": "^1.0", + "symfony/polyfill-php83": "^1.28" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/now.php" + ], + "psr-4": { + "Symfony\\Component\\Clock\\": "" + }, + "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": "Decouples applications from the system clock", + "homepage": "https://symfony.com", + "keywords": [ + "clock", + "psr20", + "time" + ], + "support": { + "source": "https://github.com/symfony/clock/tree/v7.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" + }, + { + "name": "symfony/console", + "version": "v7.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "5f360ebc65c55265a74d23d7fe27f957870158a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/5f360ebc65c55265a74d23d7fe27f957870158a1", + "reference": "5f360ebc65c55265a74d23d7fe27f957870158a1", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^7.2" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "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": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v7.3.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-07-30T17:13:41+00:00" + }, + { + "name": "symfony/css-selector", + "version": "v7.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/css-selector.git", + "reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/601a5ce9aaad7bf10797e3663faefce9e26c24e2", + "reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\CssSelector\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "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": "Converts CSS selectors to XPath expressions", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/css-selector/tree/v7.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "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.6.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": "2024-09-25T14:21:43+00:00" + }, + { + "name": "symfony/error-handler", + "version": "v7.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/error-handler.git", + "reference": "0b31a944fcd8759ae294da4d2808cbc53aebd0c3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/0b31a944fcd8759ae294da4d2808cbc53aebd0c3", + "reference": "0b31a944fcd8759ae294da4d2808cbc53aebd0c3", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/log": "^1|^2|^3", + "symfony/var-dumper": "^6.4|^7.0" + }, + "conflict": { + "symfony/deprecation-contracts": "<2.5", + "symfony/http-kernel": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/serializer": "^6.4|^7.0", + "symfony/webpack-encore-bundle": "^1.0|^2.0" + }, + "bin": [ + "Resources/bin/patch-type-declarations" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\ErrorHandler\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to manage errors and ease debugging PHP code", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/error-handler/tree/v7.3.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-07-07T08:17:57+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v7.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "497f73ac996a598c92409b44ac43b6690c4f666d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/497f73ac996a598c92409b44ac43b6690c4f666d", + "reference": "497f73ac996a598c92409b44ac43b6690c4f666d", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/event-dispatcher-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/service-contracts": "<2.5" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/error-handler": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "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": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v7.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-04-22T09:11:45+00:00" + }, + { + "name": "symfony/event-dispatcher-contracts", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "59eb412e93815df44f05f342958efa9f46b1e586" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/59eb412e93815df44f05f342958efa9f46b1e586", + "reference": "59eb412e93815df44f05f342958efa9f46b1e586", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/event-dispatcher": "^1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "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.6.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": "2024-09-25T14:21:43+00:00" + }, + { + "name": "symfony/finder", + "version": "v7.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "2a6614966ba1074fa93dae0bc804227422df4dfe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/2a6614966ba1074fa93dae0bc804227422df4dfe", + "reference": "2a6614966ba1074fa93dae0bc804227422df4dfe", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "symfony/filesystem": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "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": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v7.3.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-07-15T13:41:35+00:00" + }, + { + "name": "symfony/http-foundation", + "version": "v7.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-foundation.git", + "reference": "6877c122b3a6cc3695849622720054f6e6fa5fa6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/6877c122b3a6cc3695849622720054f6e6fa5fa6", + "reference": "6877c122b3a6cc3695849622720054f6e6fa5fa6", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3.0", + "symfony/polyfill-mbstring": "~1.1", + "symfony/polyfill-php83": "^1.27" + }, + "conflict": { + "doctrine/dbal": "<3.6", + "symfony/cache": "<6.4.12|>=7.0,<7.1.5" + }, + "require-dev": { + "doctrine/dbal": "^3.6|^4", + "predis/predis": "^1.1|^2.0", + "symfony/cache": "^6.4.12|^7.1.5", + "symfony/clock": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/mime": "^6.4|^7.0", + "symfony/rate-limiter": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpFoundation\\": "" + }, + "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": "Defines an object-oriented layer for the HTTP specification", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-foundation/tree/v7.3.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-07-10T08:47:49+00:00" + }, + { + "name": "symfony/http-kernel", + "version": "v7.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-kernel.git", + "reference": "6ecc895559ec0097e221ed2fd5eb44d5fede083c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/6ecc895559ec0097e221ed2fd5eb44d5fede083c", + "reference": "6ecc895559ec0097e221ed2fd5eb44d5fede083c", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/log": "^1|^2|^3", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/error-handler": "^6.4|^7.0", + "symfony/event-dispatcher": "^7.3", + "symfony/http-foundation": "^7.3", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/browser-kit": "<6.4", + "symfony/cache": "<6.4", + "symfony/config": "<6.4", + "symfony/console": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/doctrine-bridge": "<6.4", + "symfony/form": "<6.4", + "symfony/http-client": "<6.4", + "symfony/http-client-contracts": "<2.5", + "symfony/mailer": "<6.4", + "symfony/messenger": "<6.4", + "symfony/translation": "<6.4", + "symfony/translation-contracts": "<2.5", + "symfony/twig-bridge": "<6.4", + "symfony/validator": "<6.4", + "symfony/var-dumper": "<6.4", + "twig/twig": "<3.12" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/cache": "^1.0|^2.0|^3.0", + "symfony/browser-kit": "^6.4|^7.0", + "symfony/clock": "^6.4|^7.0", + "symfony/config": "^6.4|^7.0", + "symfony/console": "^6.4|^7.0", + "symfony/css-selector": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/dom-crawler": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/finder": "^6.4|^7.0", + "symfony/http-client-contracts": "^2.5|^3", + "symfony/process": "^6.4|^7.0", + "symfony/property-access": "^7.1", + "symfony/routing": "^6.4|^7.0", + "symfony/serializer": "^7.1", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/translation": "^6.4|^7.0", + "symfony/translation-contracts": "^2.5|^3", + "symfony/uid": "^6.4|^7.0", + "symfony/validator": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0", + "symfony/var-exporter": "^6.4|^7.0", + "twig/twig": "^3.12" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpKernel\\": "" + }, + "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": "Provides a structured process for converting a Request into a Response", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-kernel/tree/v7.3.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-07-31T10:45:04+00:00" + }, + { + "name": "symfony/mailer", + "version": "v7.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/mailer.git", + "reference": "d43e84d9522345f96ad6283d5dfccc8c1cfc299b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mailer/zipball/d43e84d9522345f96ad6283d5dfccc8c1cfc299b", + "reference": "d43e84d9522345f96ad6283d5dfccc8c1cfc299b", + "shasum": "" + }, + "require": { + "egulias/email-validator": "^2.1.10|^3|^4", + "php": ">=8.2", + "psr/event-dispatcher": "^1", + "psr/log": "^1|^2|^3", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/mime": "^7.2", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<6.4", + "symfony/messenger": "<6.4", + "symfony/mime": "<6.4", + "symfony/twig-bridge": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/twig-bridge": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mailer\\": "" + }, + "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": "Helps sending emails", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/mailer/tree/v7.3.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-07-15T11:36:08+00:00" + }, + { + "name": "symfony/mime", + "version": "v7.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/mime.git", + "reference": "e0a0f859148daf1edf6c60b398eb40bfc96697d1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mime/zipball/e0a0f859148daf1edf6c60b398eb40bfc96697d1", + "reference": "e0a0f859148daf1edf6c60b398eb40bfc96697d1", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "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": "<6.4", + "symfony/serializer": "<6.4.3|>7.0,<7.0.3" + }, + "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": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/property-access": "^6.4|^7.0", + "symfony/property-info": "^6.4|^7.0", + "symfony/serializer": "^6.4.3|^7.0.3" + }, + "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/v7.3.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-07-15T13:41:35+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "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", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "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.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-06-27T09:58:17+00:00" + }, + { + "name": "symfony/polyfill-intl-idn", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/9614ac4d8061dc257ecc64cba1b140873dce8ad3", + "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3", + "shasum": "" + }, + "require": { + "php": ">=7.2", + "symfony/polyfill-intl-normalizer": "^1.10" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "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.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-10T14:38:51+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "3833d7255cc303546435cb650316bff708a1c75c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "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.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", + "shasum": "" + }, + "require": { + "ext-iconv": "*", + "php": ">=7.2" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "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 the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-12-23T08:48:59+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "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" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-01-02T08:10:11+00:00" + }, + { + "name": "symfony/polyfill-php83", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php83.git", + "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/17f6f9a6b1735c0f163024d959f700cfbc5155e5", + "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php83\\": "" + }, + "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 backporting some PHP 8.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php83/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-07-08T02:45:35+00:00" + }, + { + "name": "symfony/polyfill-php84", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php84.git", + "reference": "d8ced4d875142b6a7426000426b8abc631d6b191" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/d8ced4d875142b6a7426000426b8abc631d6b191", + "reference": "d8ced4d875142b6a7426000426b8abc631d6b191", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php84\\": "" + }, + "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 backporting some PHP 8.4+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php84/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-06-24T13:30:11+00:00" + }, + { + "name": "symfony/polyfill-php85", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php85.git", + "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91", + "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php85\\": "" + }, + "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 backporting some PHP 8.5+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php85/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-06-23T16:12:55+00:00" + }, + { + "name": "symfony/polyfill-uuid", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-uuid.git", + "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/21533be36c24be3f4b1669c4725c7d1d2bab4ae2", + "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-uuid": "*" + }, + "suggest": { + "ext-uuid": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "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.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/process", + "version": "v7.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/40c295f2deb408d5e9d2d32b8ba1dd61e36f05af", + "reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "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": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v7.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-04-17T09:11:12+00:00" + }, + { + "name": "symfony/routing", + "version": "v7.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/routing.git", + "reference": "7614b8ca5fa89b9cd233e21b627bfc5774f586e4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/routing/zipball/7614b8ca5fa89b9cd233e21b627bfc5774f586e4", + "reference": "7614b8ca5fa89b9cd233e21b627bfc5774f586e4", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/config": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/yaml": "<6.4" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/yaml": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Routing\\": "" + }, + "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": "Maps an HTTP request to a set of configuration variables", + "homepage": "https://symfony.com", + "keywords": [ + "router", + "routing", + "uri", + "url" + ], + "support": { + "source": "https://github.com/symfony/routing/tree/v7.3.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-07-15T11:36:08+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4", + "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "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.6.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": "2025-04-25T09:37:31+00:00" + }, + { + "name": "symfony/string", + "version": "v7.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "42f505aff654e62ac7ac2ce21033818297ca89ca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/42f505aff654e62ac7ac2ce21033818297ca89ca", + "reference": "42f505aff654e62ac7ac2ce21033818297ca89ca", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "require-dev": { + "symfony/emoji": "^7.1", + "symfony/error-handler": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^6.4|^7.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/v7.3.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-07-10T08:47:49+00:00" + }, + { + "name": "symfony/translation", + "version": "v7.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation.git", + "reference": "81b48f4daa96272efcce9c7a6c4b58e629df3c90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation/zipball/81b48f4daa96272efcce9c7a6c4b58e629df3c90", + "reference": "81b48f4daa96272efcce9c7a6c4b58e629df3c90", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/translation-contracts": "^2.5|^3.0" + }, + "conflict": { + "nikic/php-parser": "<5.0", + "symfony/config": "<6.4", + "symfony/console": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<6.4", + "symfony/service-contracts": "<2.5", + "symfony/twig-bundle": "<6.4", + "symfony/yaml": "<6.4" + }, + "provide": { + "symfony/translation-implementation": "2.3|3.0" + }, + "require-dev": { + "nikic/php-parser": "^5.0", + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/console": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/finder": "^6.4|^7.0", + "symfony/http-client-contracts": "^2.5|^3.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", + "symfony/polyfill-intl-icu": "^1.21", + "symfony/routing": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\Translation\\": "" + }, + "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": "Provides tools to internationalize your application", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/translation/tree/v7.3.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-07-30T17:31:46+00:00" + }, + { + "name": "symfony/translation-contracts", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "df210c7a2573f1913b2d17cc95f90f53a73d8f7d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/df210c7a2573f1913b2d17cc95f90f53a73d8f7d", + "reference": "df210c7a2573f1913b2d17cc95f90f53a73d8f7d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "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.6.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": "2024-09-27T08:32:26+00:00" + }, + { + "name": "symfony/uid", + "version": "v7.3.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/uid.git", + "reference": "a69f69f3159b852651a6bf45a9fdd149520525bb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/uid/zipball/a69f69f3159b852651a6bf45a9fdd149520525bb", + "reference": "a69f69f3159b852651a6bf45a9fdd149520525bb", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-uuid": "^1.15" + }, + "require-dev": { + "symfony/console": "^6.4|^7.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/v7.3.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-06-27T19:55:54+00:00" + }, + { + "name": "symfony/var-dumper", + "version": "v7.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "53205bea27450dc5c65377518b3275e126d45e75" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/53205bea27450dc5c65377518b3275e126d45e75", + "reference": "53205bea27450dc5c65377518b3275e126d45e75", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/console": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/uid": "^6.4|^7.0", + "twig/twig": "^3.12" + }, + "bin": [ + "Resources/bin/var-dump-server" + ], + "type": "library", + "autoload": { + "files": [ + "Resources/functions/dump.php" + ], + "psr-4": { + "Symfony\\Component\\VarDumper\\": "" + }, + "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 mechanisms for walking through any arbitrary PHP variable", + "homepage": "https://symfony.com", + "keywords": [ + "debug", + "dump" + ], + "support": { + "source": "https://github.com/symfony/var-dumper/tree/v7.3.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-07-29T20:02:46+00:00" + }, + { + "name": "tijsverkoyen/css-to-inline-styles", + "version": "v2.3.0", + "source": { + "type": "git", + "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git", + "reference": "0d72ac1c00084279c1816675284073c5a337c20d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/0d72ac1c00084279c1816675284073c5a337c20d", + "reference": "0d72ac1c00084279c1816675284073c5a337c20d", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "php": "^7.4 || ^8.0", + "symfony/css-selector": "^5.4 || ^6.0 || ^7.0" + }, + "require-dev": { + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^8.5.21 || ^9.5.10" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "TijsVerkoyen\\CssToInlineStyles\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Tijs Verkoyen", + "email": "css_to_inline_styles@verkoyen.eu", + "role": "Developer" + } + ], + "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", + "support": { + "issues": "https://github.com/tijsverkoyen/CssToInlineStyles/issues", + "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/v2.3.0" + }, + "time": "2024-12-21T16:25:41+00:00" + }, + { + "name": "vlucas/phpdotenv", + "version": "v5.6.2", + "source": { + "type": "git", + "url": "https://github.com/vlucas/phpdotenv.git", + "reference": "24ac4c74f91ee2c193fa1aaa5c249cb0822809af" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/24ac4c74f91ee2c193fa1aaa5c249cb0822809af", + "reference": "24ac4c74f91ee2c193fa1aaa5c249cb0822809af", + "shasum": "" + }, + "require": { + "ext-pcre": "*", + "graham-campbell/result-type": "^1.1.3", + "php": "^7.2.5 || ^8.0", + "phpoption/phpoption": "^1.9.3", + "symfony/polyfill-ctype": "^1.24", + "symfony/polyfill-mbstring": "^1.24", + "symfony/polyfill-php80": "^1.24" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-filter": "*", + "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2" + }, + "suggest": { + "ext-filter": "Required to use the boolean validator." + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + }, + "branch-alias": { + "dev-master": "5.6-dev" + } + }, + "autoload": { + "psr-4": { + "Dotenv\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "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": "https://github.com/vlucas" + } + ], + "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", + "keywords": [ + "dotenv", + "env", + "environment" + ], + "support": { + "issues": "https://github.com/vlucas/phpdotenv/issues", + "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.2" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv", + "type": "tidelift" + } + ], + "time": "2025-04-30T23:37:27+00:00" + }, + { + "name": "voku/portable-ascii", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/voku/portable-ascii.git", + "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", + "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", + "shasum": "" + }, + "require": { + "php": ">=7.0.0" + }, + "require-dev": { + "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0" + }, + "suggest": { + "ext-intl": "Use Intl for transliterator_transliterate() support" + }, + "type": "library", + "autoload": { + "psr-4": { + "voku\\": "src/voku/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Lars Moelleken", + "homepage": "https://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.3" + }, + "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": "2024-11-21T01:49:47+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.10-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "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" + }, + { + "name": "webpatser/laravel-countries", + "version": "1.5.4", + "source": { + "type": "git", + "url": "https://github.com/webpatser/laravel-countries.git", + "reference": "000d7aaa67a1eb488275feafe6ab74a6b7544e84" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webpatser/laravel-countries/zipball/000d7aaa67a1eb488275feafe6ab74a6b7544e84", + "reference": "000d7aaa67a1eb488275feafe6ab74a6b7544e84", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "Webpatser\\Countries": "src/" + }, + "classmap": [ + "src/commands" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christoph Kempen", + "email": "christoph@downsized.nl", + "homepage": "http://downsized.nl/", + "role": "developer" + }, + { + "name": "Paul Kievits", + "role": "developer" + } + ], + "description": "Laravel Countries is a bundle for Laravel, providing Almost ISO 3166_2, 3166_3, currency, Capital and more for all countries.", + "homepage": "https://github.com/webpatser/laravel-countries", + "keywords": [ + "countries", + "iso_3166_2", + "iso_3166_3", + "laravel" + ], + "support": { + "issues": "https://github.com/webpatser/laravel-countries/issues", + "source": "https://github.com/webpatser/laravel-countries" + }, + "time": "2018-05-16T06:37:24+00:00" + }, + { + "name": "wolfcast/browser-detection", + "version": "2.9.9", + "source": { + "type": "git", + "url": "https://github.com/Wolfcast/BrowserDetection.git", + "reference": "f863a616536850a9713249de70ee60a18ffb9a13" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Wolfcast/BrowserDetection/zipball/f863a616536850a9713249de70ee60a18ffb9a13", + "reference": "f863a616536850a9713249de70ee60a18ffb9a13", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "lib/BrowserDetection.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Alexandre Valiquette", + "homepage": "https://wolfcast.com/", + "role": "Lead developer" + } + ], + "description": "The Wolfcast BrowserDetection PHP class facilitates the identification of the user's environment such as Web browser, version, platform family, platform version or if it's a mobile device or not.", + "homepage": "https://github.com/Wolfcast/BrowserDetection", + "keywords": [ + "browser", + "detection", + "environment", + "mobile", + "platform", + "version" + ], + "support": { + "issues": "https://github.com/Wolfcast/BrowserDetection/issues", + "source": "https://github.com/Wolfcast/BrowserDetection/tree/2.9.9" + }, + "time": "2025-03-23T16:23:10+00:00" + }, + { + "name": "zendesk/zendesk_api_client_php", + "version": "v2.2.17", + "source": { + "type": "git", + "url": "https://github.com/zendesk/zendesk_api_client_php.git", + "reference": "453c9777a0677b57f018c42d13c9c3cce043bf0f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendesk/zendesk_api_client_php/zipball/453c9777a0677b57f018c42d13c9c3cce043bf0f", + "reference": "453c9777a0677b57f018c42d13c9c3cce043bf0f", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^6.0 || ^7.0", + "guzzlehttp/psr7": "^1.7 || ^2.0", + "mmucklo/inflect": "0.3.*", + "php": ">=5.5.0" + }, + "require-dev": { + "fzaninotto/faker": ">=1.5.0", + "phpmd/phpmd": "@stable", + "phpunit/phpunit": "4.5.*", + "psy/psysh": "@stable", + "squizlabs/php_codesniffer": "2.*" + }, + "type": "library", + "autoload": { + "psr-0": { + "Zendesk\\API\\": "src/", + "Zendesk\\Console\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "description": "PHP Client for Zendesk REST API. See https://developer.zendesk.com/rest_api/docs/core/introduction .", + "homepage": "https://github.com/zendesk/zendesk_api_client_php", + "support": { + "issues": "https://github.com/zendesk/zendesk_api_client_php/issues", + "source": "https://github.com/zendesk/zendesk_api_client_php/tree/v2.2.17" + }, + "time": "2023-07-11T04:06:41+00:00" + } + ], + "packages-dev": [ + { + "name": "amphp/amp", + "version": "v3.1.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/amp.git", + "reference": "7cf7fef3d667bfe4b2560bc87e67d5387a7bcde9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/amp/zipball/7cf7fef3d667bfe4b2560bc87e67d5387a7bcde9", + "reference": "7cf7fef3d667bfe4b2560bc87e67d5387a7bcde9", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "phpunit/phpunit": "^9", + "psalm/phar": "5.23.1" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php", + "src/Future/functions.php", + "src/Internal/functions.php" + ], + "psr-4": { + "Amp\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + } + ], + "description": "A non-blocking concurrency framework for PHP applications.", + "homepage": "https://amphp.org/amp", + "keywords": [ + "async", + "asynchronous", + "awaitable", + "concurrency", + "event", + "event-loop", + "future", + "non-blocking", + "promise" + ], + "support": { + "issues": "https://github.com/amphp/amp/issues", + "source": "https://github.com/amphp/amp/tree/v3.1.0" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2025-01-26T16:07:39+00:00" + }, + { + "name": "amphp/byte-stream", + "version": "v2.1.2", + "source": { + "type": "git", + "url": "https://github.com/amphp/byte-stream.git", + "reference": "55a6bd071aec26fa2a3e002618c20c35e3df1b46" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/byte-stream/zipball/55a6bd071aec26fa2a3e002618c20c35e3df1b46", + "reference": "55a6bd071aec26fa2a3e002618c20c35e3df1b46", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/parser": "^1.1", + "amphp/pipeline": "^1", + "amphp/serialization": "^1", + "amphp/sync": "^2", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2.3" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "5.22.1" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php", + "src/Internal/functions.php" + ], + "psr-4": { + "Amp\\ByteStream\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A stream abstraction to make working with non-blocking I/O simple.", + "homepage": "https://amphp.org/byte-stream", + "keywords": [ + "amp", + "amphp", + "async", + "io", + "non-blocking", + "stream" + ], + "support": { + "issues": "https://github.com/amphp/byte-stream/issues", + "source": "https://github.com/amphp/byte-stream/tree/v2.1.2" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2025-03-16T17:10:27+00:00" + }, + { + "name": "amphp/cache", + "version": "v2.0.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/cache.git", + "reference": "46912e387e6aa94933b61ea1ead9cf7540b7797c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/cache/zipball/46912e387e6aa94933b61ea1ead9cf7540b7797c", + "reference": "46912e387e6aa94933b61ea1ead9cf7540b7797c", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/serialization": "^1", + "amphp/sync": "^2", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Amp\\Cache\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + } + ], + "description": "A fiber-aware cache API based on Amp and Revolt.", + "homepage": "https://amphp.org/cache", + "support": { + "issues": "https://github.com/amphp/cache/issues", + "source": "https://github.com/amphp/cache/tree/v2.0.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-04-19T03:38:06+00:00" + }, + { + "name": "amphp/dns", + "version": "v2.4.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/dns.git", + "reference": "78eb3db5fc69bf2fc0cb503c4fcba667bc223c71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/dns/zipball/78eb3db5fc69bf2fc0cb503c4fcba667bc223c71", + "reference": "78eb3db5fc69bf2fc0cb503c4fcba667bc223c71", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/cache": "^2", + "amphp/parser": "^1", + "amphp/process": "^2", + "daverandom/libdns": "^2.0.2", + "ext-filter": "*", + "ext-json": "*", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "5.20" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Dns\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Wright", + "email": "addr@daverandom.com" + }, + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + }, + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + } + ], + "description": "Async DNS resolution for Amp.", + "homepage": "https://github.com/amphp/dns", + "keywords": [ + "amp", + "amphp", + "async", + "client", + "dns", + "resolve" + ], + "support": { + "issues": "https://github.com/amphp/dns/issues", + "source": "https://github.com/amphp/dns/tree/v2.4.0" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2025-01-19T15:43:40+00:00" + }, + { + "name": "amphp/parallel", + "version": "v2.3.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/parallel.git", + "reference": "5113111de02796a782f5d90767455e7391cca190" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/parallel/zipball/5113111de02796a782f5d90767455e7391cca190", + "reference": "5113111de02796a782f5d90767455e7391cca190", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/cache": "^2", + "amphp/parser": "^1", + "amphp/pipeline": "^1", + "amphp/process": "^2", + "amphp/serialization": "^1", + "amphp/socket": "^2", + "amphp/sync": "^2", + "php": ">=8.1", + "revolt/event-loop": "^1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.18" + }, + "type": "library", + "autoload": { + "files": [ + "src/Context/functions.php", + "src/Context/Internal/functions.php", + "src/Ipc/functions.php", + "src/Worker/functions.php" + ], + "psr-4": { + "Amp\\Parallel\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Stephen Coakley", + "email": "me@stephencoakley.com" + } + ], + "description": "Parallel processing component for Amp.", + "homepage": "https://github.com/amphp/parallel", + "keywords": [ + "async", + "asynchronous", + "concurrent", + "multi-processing", + "multi-threading" + ], + "support": { + "issues": "https://github.com/amphp/parallel/issues", + "source": "https://github.com/amphp/parallel/tree/v2.3.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-12-21T01:56:09+00:00" + }, + { + "name": "amphp/parser", + "version": "v1.1.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/parser.git", + "reference": "3cf1f8b32a0171d4b1bed93d25617637a77cded7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/parser/zipball/3cf1f8b32a0171d4b1bed93d25617637a77cded7", + "reference": "3cf1f8b32a0171d4b1bed93d25617637a77cded7", + "shasum": "" + }, + "require": { + "php": ">=7.4" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Amp\\Parser\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A generator parser to make streaming parsers simple.", + "homepage": "https://github.com/amphp/parser", + "keywords": [ + "async", + "non-blocking", + "parser", + "stream" + ], + "support": { + "issues": "https://github.com/amphp/parser/issues", + "source": "https://github.com/amphp/parser/tree/v1.1.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-03-21T19:16:53+00:00" + }, + { + "name": "amphp/pipeline", + "version": "v1.2.3", + "source": { + "type": "git", + "url": "https://github.com/amphp/pipeline.git", + "reference": "7b52598c2e9105ebcddf247fc523161581930367" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/pipeline/zipball/7b52598c2e9105ebcddf247fc523161581930367", + "reference": "7b52598c2e9105ebcddf247fc523161581930367", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "php": ">=8.1", + "revolt/event-loop": "^1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.18" + }, + "type": "library", + "autoload": { + "psr-4": { + "Amp\\Pipeline\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Asynchronous iterators and operators.", + "homepage": "https://amphp.org/pipeline", + "keywords": [ + "amp", + "amphp", + "async", + "io", + "iterator", + "non-blocking" + ], + "support": { + "issues": "https://github.com/amphp/pipeline/issues", + "source": "https://github.com/amphp/pipeline/tree/v1.2.3" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2025-03-16T16:33:53+00:00" + }, + { + "name": "amphp/process", + "version": "v2.0.3", + "source": { + "type": "git", + "url": "https://github.com/amphp/process.git", + "reference": "52e08c09dec7511d5fbc1fb00d3e4e79fc77d58d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/process/zipball/52e08c09dec7511d5fbc1fb00d3e4e79fc77d58d", + "reference": "52e08c09dec7511d5fbc1fb00d3e4e79fc77d58d", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/sync": "^2", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.4" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Process\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A fiber-aware process manager based on Amp and Revolt.", + "homepage": "https://amphp.org/process", + "support": { + "issues": "https://github.com/amphp/process/issues", + "source": "https://github.com/amphp/process/tree/v2.0.3" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-04-19T03:13:44+00:00" + }, + { + "name": "amphp/serialization", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/serialization.git", + "reference": "693e77b2fb0b266c3c7d622317f881de44ae94a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/serialization/zipball/693e77b2fb0b266c3c7d622317f881de44ae94a1", + "reference": "693e77b2fb0b266c3c7d622317f881de44ae94a1", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "phpunit/phpunit": "^9 || ^8 || ^7" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Serialization\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Serialization tools for IPC and data storage in PHP.", + "homepage": "https://github.com/amphp/serialization", + "keywords": [ + "async", + "asynchronous", + "serialization", + "serialize" + ], + "support": { + "issues": "https://github.com/amphp/serialization/issues", + "source": "https://github.com/amphp/serialization/tree/master" + }, + "time": "2020-03-25T21:39:07+00:00" + }, + { + "name": "amphp/socket", + "version": "v2.3.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/socket.git", + "reference": "58e0422221825b79681b72c50c47a930be7bf1e1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/socket/zipball/58e0422221825b79681b72c50c47a930be7bf1e1", + "reference": "58e0422221825b79681b72c50c47a930be7bf1e1", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/dns": "^2", + "ext-openssl": "*", + "kelunik/certificate": "^1.1", + "league/uri": "^6.5 | ^7", + "league/uri-interfaces": "^2.3 | ^7", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "amphp/process": "^2", + "phpunit/phpunit": "^9", + "psalm/phar": "5.20" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php", + "src/Internal/functions.php", + "src/SocketAddress/functions.php" + ], + "psr-4": { + "Amp\\Socket\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Lowrey", + "email": "rdlowrey@gmail.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Non-blocking socket connection / server implementations based on Amp and Revolt.", + "homepage": "https://github.com/amphp/socket", + "keywords": [ + "amp", + "async", + "encryption", + "non-blocking", + "sockets", + "tcp", + "tls" + ], + "support": { + "issues": "https://github.com/amphp/socket/issues", + "source": "https://github.com/amphp/socket/tree/v2.3.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-04-21T14:33:03+00:00" + }, + { + "name": "amphp/sync", + "version": "v2.3.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/sync.git", + "reference": "217097b785130d77cfcc58ff583cf26cd1770bf1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/sync/zipball/217097b785130d77cfcc58ff583cf26cd1770bf1", + "reference": "217097b785130d77cfcc58ff583cf26cd1770bf1", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/pipeline": "^1", + "amphp/serialization": "^1", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "5.23" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Sync\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Stephen Coakley", + "email": "me@stephencoakley.com" + } + ], + "description": "Non-blocking synchronization primitives for PHP based on Amp and Revolt.", + "homepage": "https://github.com/amphp/sync", + "keywords": [ + "async", + "asynchronous", + "mutex", + "semaphore", + "synchronization" + ], + "support": { + "issues": "https://github.com/amphp/sync/issues", + "source": "https://github.com/amphp/sync/tree/v2.3.0" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-08-03T19:31:26+00:00" + }, + { + "name": "barryvdh/laravel-ide-helper", + "version": "v3.5.5", + "source": { + "type": "git", + "url": "https://github.com/barryvdh/laravel-ide-helper.git", + "reference": "8d441ec99f8612b942b55f5183151d91591b618a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/8d441ec99f8612b942b55f5183151d91591b618a", + "reference": "8d441ec99f8612b942b55f5183151d91591b618a", + "shasum": "" + }, + "require": { + "barryvdh/reflection-docblock": "^2.3", + "composer/class-map-generator": "^1.0", + "ext-json": "*", + "illuminate/console": "^11.15 || ^12", + "illuminate/database": "^11.15 || ^12", + "illuminate/filesystem": "^11.15 || ^12", + "illuminate/support": "^11.15 || ^12", + "php": "^8.2" + }, + "require-dev": { + "ext-pdo_sqlite": "*", + "friendsofphp/php-cs-fixer": "^3", + "illuminate/config": "^11.15 || ^12", + "illuminate/view": "^11.15 || ^12", + "mockery/mockery": "^1.4", + "orchestra/testbench": "^9.2 || ^10", + "phpunit/phpunit": "^10.5 || ^11.5.3", + "spatie/phpunit-snapshot-assertions": "^4 || ^5", + "vimeo/psalm": "^5.4", + "vlucas/phpdotenv": "^5" + }, + "suggest": { + "illuminate/events": "Required for automatic helper generation (^6|^7|^8|^9|^10|^11)." + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider" + ] + }, + "branch-alias": { + "dev-master": "3.5-dev" + } + }, + "autoload": { + "psr-4": { + "Barryvdh\\LaravelIdeHelper\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Barry vd. Heuvel", + "email": "barryvdh@gmail.com" + } + ], + "description": "Laravel IDE Helper, generates correct PHPDocs for all Facade classes, to improve auto-completion.", + "keywords": [ + "autocomplete", + "codeintel", + "dev", + "helper", + "ide", + "laravel", + "netbeans", + "phpdoc", + "phpstorm", + "sublime" + ], + "support": { + "issues": "https://github.com/barryvdh/laravel-ide-helper/issues", + "source": "https://github.com/barryvdh/laravel-ide-helper/tree/v3.5.5" + }, + "funding": [ + { + "url": "https://fruitcake.nl", + "type": "custom" + }, + { + "url": "https://github.com/barryvdh", + "type": "github" + } + ], + "time": "2025-02-11T13:59:46+00:00" + }, + { + "name": "barryvdh/reflection-docblock", + "version": "v2.4.0", + "source": { + "type": "git", + "url": "https://github.com/barryvdh/ReflectionDocBlock.git", + "reference": "d103774cbe7e94ddee7e4870f97f727b43fe7201" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/barryvdh/ReflectionDocBlock/zipball/d103774cbe7e94ddee7e4870f97f727b43fe7201", + "reference": "d103774cbe7e94ddee7e4870f97f727b43fe7201", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.14|^9" + }, + "suggest": { + "dflydev/markdown": "~1.0", + "erusev/parsedown": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3.x-dev" + } + }, + "autoload": { + "psr-0": { + "Barryvdh": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "mike.vanriel@naenius.com" + } + ], + "support": { + "source": "https://github.com/barryvdh/ReflectionDocBlock/tree/v2.4.0" + }, + "time": "2025-07-17T06:07:30+00:00" + }, + { + "name": "composer/class-map-generator", + "version": "1.6.2", + "source": { + "type": "git", + "url": "https://github.com/composer/class-map-generator.git", + "reference": "ba9f089655d4cdd64e762a6044f411ccdaec0076" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/class-map-generator/zipball/ba9f089655d4cdd64e762a6044f411ccdaec0076", + "reference": "ba9f089655d4cdd64e762a6044f411ccdaec0076", + "shasum": "" + }, + "require": { + "composer/pcre": "^2.1 || ^3.1", + "php": "^7.2 || ^8.0", + "symfony/finder": "^4.4 || ^5.3 || ^6 || ^7" + }, + "require-dev": { + "phpstan/phpstan": "^1.12 || ^2", + "phpstan/phpstan-deprecation-rules": "^1 || ^2", + "phpstan/phpstan-phpunit": "^1 || ^2", + "phpstan/phpstan-strict-rules": "^1.1 || ^2", + "phpunit/phpunit": "^8", + "symfony/filesystem": "^5.4 || ^6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\ClassMapGenerator\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "Utilities to scan PHP code and generate class maps.", + "keywords": [ + "classmap" + ], + "support": { + "issues": "https://github.com/composer/class-map-generator/issues", + "source": "https://github.com/composer/class-map-generator/tree/1.6.2" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + } + ], + "time": "2025-08-20T18:52:43+00:00" + }, + { + "name": "composer/pcre", + "version": "3.3.2", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<1.11.10" + }, + "require-dev": { + "phpstan/phpstan": "^1.12 || ^2", + "phpstan/phpstan-strict-rules": "^1 || ^2", + "phpunit/phpunit": "^8 || ^9" + }, + "type": "library", + "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + }, + "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": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.3.2" + }, + "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": "2024-11-12T16:29:46+00:00" + }, + { + "name": "composer/semver", + "version": "3.4.4", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/198166618906cb2de69b95d7d47e5fa8aa1b2b95", + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.11", + "symfony/phpunit-bridge": "^3 || ^7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.4.4" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + } + ], + "time": "2025-08-20T19:15:30+00:00" + }, + { + "name": "composer/xdebug-handler", + "version": "3.0.5", + "source": { + "type": "git", + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef", + "shasum": "" + }, + "require": { + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" + }, + "require-dev": { + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without Xdebug.", + "keywords": [ + "Xdebug", + "performance" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/3.0.5" + }, + "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": "2024-05-06T16:37:16+00:00" + }, + { + "name": "danog/advanced-json-rpc", + "version": "v3.2.2", + "source": { + "type": "git", + "url": "https://github.com/danog/php-advanced-json-rpc.git", + "reference": "aadb1c4068a88c3d0530cfe324b067920661efcb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/danog/php-advanced-json-rpc/zipball/aadb1c4068a88c3d0530cfe324b067920661efcb", + "reference": "aadb1c4068a88c3d0530cfe324b067920661efcb", + "shasum": "" + }, + "require": { + "netresearch/jsonmapper": "^5", + "php": ">=8.1", + "phpdocumentor/reflection-docblock": "^4.3.4 || ^5.0.0" + }, + "replace": { + "felixfbecker/php-advanced-json-rpc": "^3" + }, + "require-dev": { + "phpunit/phpunit": "^9" + }, + "type": "library", + "autoload": { + "psr-4": { + "AdvancedJsonRpc\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Felix Becker", + "email": "felix.b@outlook.com" + }, + { + "name": "Daniil Gentili", + "email": "daniil@daniil.it" + } + ], + "description": "A more advanced JSONRPC implementation", + "support": { + "issues": "https://github.com/danog/php-advanced-json-rpc/issues", + "source": "https://github.com/danog/php-advanced-json-rpc/tree/v3.2.2" + }, + "time": "2025-02-14T10:55:15+00:00" + }, + { + "name": "daverandom/libdns", + "version": "v2.1.0", + "source": { + "type": "git", + "url": "https://github.com/DaveRandom/LibDNS.git", + "reference": "b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/DaveRandom/LibDNS/zipball/b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a", + "reference": "b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "Required for IDN support" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "LibDNS\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "DNS protocol implementation written in pure PHP", + "keywords": [ + "dns" + ], + "support": { + "issues": "https://github.com/DaveRandom/LibDNS/issues", + "source": "https://github.com/DaveRandom/LibDNS/tree/v2.1.0" + }, + "time": "2024-04-12T12:12:48+00:00" + }, + { + "name": "dnoegel/php-xdg-base-dir", + "version": "v0.1.1", + "source": { + "type": "git", + "url": "https://github.com/dnoegel/php-xdg-base-dir.git", + "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dnoegel/php-xdg-base-dir/zipball/8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", + "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "~7.0|~6.0|~5.0|~4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "XdgBaseDir\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "implementation of xdg base directory specification for php", + "support": { + "issues": "https://github.com/dnoegel/php-xdg-base-dir/issues", + "source": "https://github.com/dnoegel/php-xdg-base-dir/tree/v0.1.1" + }, + "time": "2019-12-04T15:06:13+00:00" + }, + { + "name": "doctrine/deprecations", + "version": "1.1.5", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", + "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "phpunit/phpunit": "<=7.5 || >=13" + }, + "require-dev": { + "doctrine/coding-standard": "^9 || ^12 || ^13", + "phpstan/phpstan": "1.4.10 || 2.1.11", + "phpstan/phpstan-phpunit": "^1.0 || ^2", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12", + "psr/log": "^1 || ^2 || ^3" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "src" + } + }, + "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/1.1.5" + }, + "time": "2025-04-07T20:06:18+00:00" + }, + { + "name": "fakerphp/faker", + "version": "v1.24.1", + "source": { + "type": "git", + "url": "https://github.com/FakerPHP/Faker.git", + "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", + "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "psr/container": "^1.0 || ^2.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "conflict": { + "fzaninotto/faker": "*" + }, + "require-dev": { + "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", + "autoload": { + "psr-4": { + "Faker\\": "src/Faker/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "François Zaninotto" + } + ], + "description": "Faker is a PHP library that generates fake data for you.", + "keywords": [ + "data", + "faker", + "fixtures" + ], + "support": { + "issues": "https://github.com/FakerPHP/Faker/issues", + "source": "https://github.com/FakerPHP/Faker/tree/v1.24.1" + }, + "time": "2024-11-21T13:46:39+00:00" + }, + { + "name": "felixfbecker/language-server-protocol", + "version": "v1.5.3", + "source": { + "type": "git", + "url": "https://github.com/felixfbecker/php-language-server-protocol.git", + "reference": "a9e113dbc7d849e35b8776da39edaf4313b7b6c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/a9e113dbc7d849e35b8776da39edaf4313b7b6c9", + "reference": "a9e113dbc7d849e35b8776da39edaf4313b7b6c9", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "phpstan/phpstan": "*", + "squizlabs/php_codesniffer": "^3.1", + "vimeo/psalm": "^4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "LanguageServerProtocol\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Felix Becker", + "email": "felix.b@outlook.com" + } + ], + "description": "PHP classes for the Language Server Protocol", + "keywords": [ + "language", + "microsoft", + "php", + "server" + ], + "support": { + "issues": "https://github.com/felixfbecker/php-language-server-protocol/issues", + "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/v1.5.3" + }, + "time": "2024-04-30T00:40:11+00:00" + }, + { + "name": "fidry/cpu-core-counter", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/theofidry/cpu-core-counter.git", + "reference": "db9508f7b1474469d9d3c53b86f817e344732678" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/db9508f7b1474469d9d3c53b86f817e344732678", + "reference": "db9508f7b1474469d9d3c53b86f817e344732678", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "fidry/makefile": "^0.2.0", + "fidry/php-cs-fixer-config": "^1.1.2", + "phpstan/extension-installer": "^1.2.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-deprecation-rules": "^2.0.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^8.5.31 || ^9.5.26", + "webmozarts/strict-phpunit": "^7.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Fidry\\CpuCoreCounter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Théo FIDRY", + "email": "theo.fidry@gmail.com" + } + ], + "description": "Tiny utility to get the number of CPU cores.", + "keywords": [ + "CPU", + "core" + ], + "support": { + "issues": "https://github.com/theofidry/cpu-core-counter/issues", + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.3.0" + }, + "funding": [ + { + "url": "https://github.com/theofidry", + "type": "github" + } + ], + "time": "2025-08-14T07:29:31+00:00" + }, + { + "name": "hamcrest/hamcrest-php", + "version": "v2.1.1", + "source": { + "type": "git", + "url": "https://github.com/hamcrest/hamcrest-php.git", + "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", + "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0" + }, + "replace": { + "cordoval/hamcrest-php": "*", + "davedevelopment/hamcrest-php": "*", + "kodova/hamcrest-php": "*" + }, + "require-dev": { + "phpunit/php-file-iterator": "^1.4 || ^2.0 || ^3.0", + "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + }, + "autoload": { + "classmap": [ + "hamcrest" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "This is the PHP port of Hamcrest Matchers", + "keywords": [ + "test" + ], + "support": { + "issues": "https://github.com/hamcrest/hamcrest-php/issues", + "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.1.1" + }, + "time": "2025-04-30T06:54:44+00:00" + }, + { + "name": "kelunik/certificate", + "version": "v1.1.3", + "source": { + "type": "git", + "url": "https://github.com/kelunik/certificate.git", + "reference": "7e00d498c264d5eb4f78c69f41c8bd6719c0199e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/kelunik/certificate/zipball/7e00d498c264d5eb4f78c69f41c8bd6719c0199e", + "reference": "7e00d498c264d5eb4f78c69f41c8bd6719c0199e", + "shasum": "" + }, + "require": { + "ext-openssl": "*", + "php": ">=7.0" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "phpunit/phpunit": "^6 | 7 | ^8 | ^9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Kelunik\\Certificate\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Access certificate details and transform between different formats.", + "keywords": [ + "DER", + "certificate", + "certificates", + "openssl", + "pem", + "x509" + ], + "support": { + "issues": "https://github.com/kelunik/certificate/issues", + "source": "https://github.com/kelunik/certificate/tree/v1.1.3" + }, + "time": "2023-02-03T21:26:53+00:00" + }, + { + "name": "laravel/boost", + "version": "v1.0.18", + "source": { + "type": "git", + "url": "https://github.com/laravel/boost.git", + "reference": "df2a62b5864759ea8cce8a4b7575b657e9c7d4ab" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/boost/zipball/df2a62b5864759ea8cce8a4b7575b657e9c7d4ab", + "reference": "df2a62b5864759ea8cce8a4b7575b657e9c7d4ab", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^7.9", + "illuminate/console": "^10.0|^11.0|^12.0", + "illuminate/contracts": "^10.0|^11.0|^12.0", + "illuminate/routing": "^10.0|^11.0|^12.0", + "illuminate/support": "^10.0|^11.0|^12.0", + "laravel/mcp": "^0.1.0", + "laravel/prompts": "^0.1.9|^0.3", + "laravel/roster": "^0.2", + "php": "^8.1|^8.2" + }, + "require-dev": { + "laravel/pint": "^1.14|^1.23", + "mockery/mockery": "^1.6", + "orchestra/testbench": "^8.22.0|^9.0|^10.0", + "pestphp/pest": "^2.0|^3.0", + "phpstan/phpstan": "^2.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Boost\\BoostServiceProvider" + ] + }, + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\Boost\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Laravel Boost accelerates AI-assisted development to generate high-quality, Laravel-specific code.", + "homepage": "https://github.com/laravel/boost", + "keywords": [ + "ai", + "dev", + "laravel" + ], + "support": { + "issues": "https://github.com/laravel/boost/issues", + "source": "https://github.com/laravel/boost" + }, + "time": "2025-08-16T09:10:03+00:00" + }, + { + "name": "laravel/dusk", + "version": "v8.3.3", + "source": { + "type": "git", + "url": "https://github.com/laravel/dusk.git", + "reference": "077d448cd993a08f97bfccf0ea3d6478b3908f7e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/dusk/zipball/077d448cd993a08f97bfccf0ea3d6478b3908f7e", + "reference": "077d448cd993a08f97bfccf0ea3d6478b3908f7e", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-zip": "*", + "guzzlehttp/guzzle": "^7.5", + "illuminate/console": "^10.0|^11.0|^12.0", + "illuminate/support": "^10.0|^11.0|^12.0", + "php": "^8.1", + "php-webdriver/webdriver": "^1.15.2", + "symfony/console": "^6.2|^7.0", + "symfony/finder": "^6.2|^7.0", + "symfony/process": "^6.2|^7.0", + "vlucas/phpdotenv": "^5.2" + }, + "require-dev": { + "laravel/framework": "^10.0|^11.0|^12.0", + "mockery/mockery": "^1.6", + "orchestra/testbench-core": "^8.19|^9.0|^10.0", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.1|^11.0|^12.0.1", + "psy/psysh": "^0.11.12|^0.12", + "symfony/yaml": "^6.2|^7.0" + }, + "suggest": { + "ext-pcntl": "Used to gracefully terminate Dusk when tests are running." + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Dusk\\DuskServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Dusk\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Laravel Dusk provides simple end-to-end testing and browser automation.", + "keywords": [ + "laravel", + "testing", + "webdriver" + ], + "support": { + "issues": "https://github.com/laravel/dusk/issues", + "source": "https://github.com/laravel/dusk/tree/v8.3.3" + }, + "time": "2025-06-10T13:59:27+00:00" + }, + { + "name": "laravel/mcp", + "version": "v0.1.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/mcp.git", + "reference": "6d6284a491f07c74d34f48dfd999ed52c567c713" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/mcp/zipball/6d6284a491f07c74d34f48dfd999ed52c567c713", + "reference": "6d6284a491f07c74d34f48dfd999ed52c567c713", + "shasum": "" + }, + "require": { + "illuminate/console": "^10.0|^11.0|^12.0", + "illuminate/contracts": "^10.0|^11.0|^12.0", + "illuminate/http": "^10.0|^11.0|^12.0", + "illuminate/routing": "^10.0|^11.0|^12.0", + "illuminate/support": "^10.0|^11.0|^12.0", + "illuminate/validation": "^10.0|^11.0|^12.0", + "php": "^8.1|^8.2" + }, + "require-dev": { + "laravel/pint": "^1.14", + "orchestra/testbench": "^8.22.0|^9.0|^10.0", + "phpstan/phpstan": "^2.0" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Mcp": "Laravel\\Mcp\\Server\\Facades\\Mcp" + }, + "providers": [ + "Laravel\\Mcp\\Server\\McpServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Mcp\\": "src/", + "Workbench\\App\\": "workbench/app/", + "Laravel\\Mcp\\Tests\\": "tests/", + "Laravel\\Mcp\\Server\\": "src/Server/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "The easiest way to add MCP servers to your Laravel app.", + "homepage": "https://github.com/laravel/mcp", + "keywords": [ + "dev", + "laravel", + "mcp" + ], + "support": { + "issues": "https://github.com/laravel/mcp/issues", + "source": "https://github.com/laravel/mcp" + }, + "time": "2025-08-16T09:50:43+00:00" + }, + { + "name": "laravel/roster", + "version": "v0.2.3", + "source": { + "type": "git", + "url": "https://github.com/laravel/roster.git", + "reference": "caeed7609b02c00c3f1efec52812d8d87c5d4096" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/roster/zipball/caeed7609b02c00c3f1efec52812d8d87c5d4096", + "reference": "caeed7609b02c00c3f1efec52812d8d87c5d4096", + "shasum": "" + }, + "require": { + "illuminate/console": "^10.0|^11.0|^12.0", + "illuminate/contracts": "^10.0|^11.0|^12.0", + "illuminate/routing": "^10.0|^11.0|^12.0", + "illuminate/support": "^10.0|^11.0|^12.0", + "php": "^8.1|^8.2", + "symfony/yaml": "^6.4|^7.2" + }, + "require-dev": { + "laravel/pint": "^1.14", + "mockery/mockery": "^1.6", + "orchestra/testbench": "^8.22.0|^9.0|^10.0", + "pestphp/pest": "^2.0|^3.0", + "phpstan/phpstan": "^2.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Roster\\RosterServiceProvider" + ] + }, + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\Roster\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Detect packages & approaches in use within a Laravel project", + "homepage": "https://github.com/laravel/roster", + "keywords": [ + "dev", + "laravel" + ], + "support": { + "issues": "https://github.com/laravel/roster/issues", + "source": "https://github.com/laravel/roster" + }, + "time": "2025-08-13T15:00:25+00:00" + }, + { + "name": "mockery/mockery", + "version": "1.6.12", + "source": { + "type": "git", + "url": "https://github.com/mockery/mockery.git", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mockery/mockery/zipball/1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "shasum": "" + }, + "require": { + "hamcrest/hamcrest-php": "^2.0.1", + "lib-pcre": ">=7.0", + "php": ">=7.3" + }, + "conflict": { + "phpunit/phpunit": "<8.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.5 || ^9.6.17", + "symplify/easy-coding-standard": "^12.1.14" + }, + "type": "library", + "autoload": { + "files": [ + "library/helpers.php", + "library/Mockery.php" + ], + "psr-4": { + "Mockery\\": "library/Mockery" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Pádraic Brady", + "email": "padraic.brady@gmail.com", + "homepage": "https://github.com/padraic", + "role": "Author" + }, + { + "name": "Dave Marshall", + "email": "dave.marshall@atstsolutions.co.uk", + "homepage": "https://davedevelopment.co.uk", + "role": "Developer" + }, + { + "name": "Nathanael Esayeas", + "email": "nathanael.esayeas@protonmail.com", + "homepage": "https://github.com/ghostwriter", + "role": "Lead Developer" + } + ], + "description": "Mockery is a simple yet flexible PHP mock object framework", + "homepage": "https://github.com/mockery/mockery", + "keywords": [ + "BDD", + "TDD", + "library", + "mock", + "mock objects", + "mockery", + "stub", + "test", + "test double", + "testing" + ], + "support": { + "docs": "https://docs.mockery.io/", + "issues": "https://github.com/mockery/mockery/issues", + "rss": "https://github.com/mockery/mockery/releases.atom", + "security": "https://github.com/mockery/mockery/security/advisories", + "source": "https://github.com/mockery/mockery" + }, + "time": "2024-05-16T03:13:13+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.13.4", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2025-08-01T08:46:24+00:00" + }, + { + "name": "netresearch/jsonmapper", + "version": "v5.0.0", + "source": { + "type": "git", + "url": "https://github.com/cweiske/jsonmapper.git", + "reference": "8c64d8d444a5d764c641ebe97e0e3bc72b25bf6c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/8c64d8d444a5d764c641ebe97e0e3bc72b25bf6c", + "reference": "8c64d8d444a5d764c641ebe97e0e3bc72b25bf6c", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "~7.5 || ~8.0 || ~9.0 || ~10.0", + "squizlabs/php_codesniffer": "~3.5" + }, + "type": "library", + "autoload": { + "psr-0": { + "JsonMapper": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "OSL-3.0" + ], + "authors": [ + { + "name": "Christian Weiske", + "email": "cweiske@cweiske.de", + "homepage": "http://github.com/cweiske/jsonmapper/", + "role": "Developer" + } + ], + "description": "Map nested JSON structures onto PHP classes", + "support": { + "email": "cweiske@cweiske.de", + "issues": "https://github.com/cweiske/jsonmapper/issues", + "source": "https://github.com/cweiske/jsonmapper/tree/v5.0.0" + }, + "time": "2024-09-08T10:20:00+00:00" + }, + { + "name": "orchestra/sidekick", + "version": "v1.2.14", + "source": { + "type": "git", + "url": "https://github.com/orchestral/sidekick.git", + "reference": "0f7d1d96d390e7bf9118f280dfae74b8b2fb0a00" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/orchestral/sidekick/zipball/0f7d1d96d390e7bf9118f280dfae74b8b2fb0a00", + "reference": "0f7d1d96d390e7bf9118f280dfae74b8b2fb0a00", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.2", + "php": "^8.1", + "symfony/polyfill-php83": "^1.32" + }, + "require-dev": { + "fakerphp/faker": "^1.21", + "laravel/framework": "^10.48.29|^11.44.7|^12.1.1|^13.0", + "laravel/pint": "^1.4", + "mockery/mockery": "^1.5.1", + "orchestra/testbench-core": "^8.37.0|^9.14.0|^10.0|^11.0", + "phpstan/phpstan": "^2.1.14", + "phpunit/phpunit": "^10.0|^11.0|^12.0", + "symfony/process": "^6.0|^7.0" + }, + "type": "library", + "autoload": { + "files": [ + "src/Eloquent/functions.php", + "src/Http/functions.php", + "src/functions.php" + ], + "psr-4": { + "Orchestra\\Sidekick\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mior Muhammad Zaki", + "email": "crynobone@gmail.com" + } + ], + "description": "Packages Toolkit Utilities and Helpers for Laravel", + "support": { + "issues": "https://github.com/orchestral/sidekick/issues", + "source": "https://github.com/orchestral/sidekick/tree/v1.2.14" + }, + "time": "2025-08-06T23:54:27+00:00" + }, + { + "name": "orchestra/testbench-core", + "version": "v10.6.1", + "source": { + "type": "git", + "url": "https://github.com/orchestral/testbench-core.git", + "reference": "ae197e9bc6a04eb966a44ea88dd9fa53c4d467d9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/orchestral/testbench-core/zipball/ae197e9bc6a04eb966a44ea88dd9fa53c4d467d9", + "reference": "ae197e9bc6a04eb966a44ea88dd9fa53c4d467d9", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.2", + "orchestra/sidekick": "~1.1.16|^1.2.12", + "php": "^8.2", + "symfony/deprecation-contracts": "^2.5|^3.0", + "symfony/polyfill-php83": "^1.32" + }, + "conflict": { + "brianium/paratest": "<7.3.0|>=8.0.0", + "laravel/framework": "<12.8.0|>=13.0.0", + "laravel/serializable-closure": "<1.3.0|>=2.0.0 <2.0.3|>=3.0.0", + "nunomaduro/collision": "<8.0.0|>=9.0.0", + "phpunit/phpunit": "<10.5.35|>=11.0.0 <11.5.3|12.0.0|>=12.4.0" + }, + "require-dev": { + "fakerphp/faker": "^1.24", + "laravel/framework": "^12.8.0", + "laravel/pint": "^1.24", + "laravel/serializable-closure": "^1.3|^2.0.4", + "mockery/mockery": "^1.6.10", + "phpstan/phpstan": "^2.1.19", + "phpunit/phpunit": "^10.5.35|^11.5.3|^12.0.1", + "spatie/laravel-ray": "^1.40.2", + "symfony/process": "^7.2.0", + "symfony/yaml": "^7.2.0", + "vlucas/phpdotenv": "^5.6.1" + }, + "suggest": { + "brianium/paratest": "Allow using parallel testing (^7.3).", + "ext-pcntl": "Required to use all features of the console signal trapping.", + "fakerphp/faker": "Allow using Faker for testing (^1.23).", + "laravel/framework": "Required for testing (^12.8.0).", + "mockery/mockery": "Allow using Mockery for testing (^1.6).", + "nunomaduro/collision": "Allow using Laravel style tests output and parallel testing (^8.0).", + "orchestra/testbench-dusk": "Allow using Laravel Dusk for testing (^10.0).", + "phpunit/phpunit": "Allow using PHPUnit for testing (^10.5.35|^11.5.3|^12.0.1).", + "symfony/process": "Required to use Orchestra\\Testbench\\remote function (^7.2).", + "symfony/yaml": "Required for Testbench CLI (^7.2).", + "vlucas/phpdotenv": "Required for Testbench CLI (^5.6.1)." + }, + "bin": [ + "testbench" + ], + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Orchestra\\Testbench\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mior Muhammad Zaki", + "email": "crynobone@gmail.com", + "homepage": "https://github.com/crynobone" + } + ], + "description": "Testing Helper for Laravel Development", + "homepage": "https://packages.tools/testbench", + "keywords": [ + "BDD", + "TDD", + "dev", + "laravel", + "laravel-packages", + "testing" + ], + "support": { + "issues": "https://github.com/orchestral/testbench/issues", + "source": "https://github.com/orchestral/testbench-core" + }, + "time": "2025-08-20T00:52:46+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "php-webdriver/webdriver", + "version": "1.15.2", + "source": { + "type": "git", + "url": "https://github.com/php-webdriver/php-webdriver.git", + "reference": "998e499b786805568deaf8cbf06f4044f05d91bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-webdriver/php-webdriver/zipball/998e499b786805568deaf8cbf06f4044f05d91bf", + "reference": "998e499b786805568deaf8cbf06f4044f05d91bf", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "ext-zip": "*", + "php": "^7.3 || ^8.0", + "symfony/polyfill-mbstring": "^1.12", + "symfony/process": "^5.0 || ^6.0 || ^7.0" + }, + "replace": { + "facebook/webdriver": "*" + }, + "require-dev": { + "ergebnis/composer-normalize": "^2.20.0", + "ondram/ci-detector": "^4.0", + "php-coveralls/php-coveralls": "^2.4", + "php-mock/php-mock-phpunit": "^2.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpunit/phpunit": "^9.3", + "squizlabs/php_codesniffer": "^3.5", + "symfony/var-dumper": "^5.0 || ^6.0 || ^7.0" + }, + "suggest": { + "ext-SimpleXML": "For Firefox profile creation" + }, + "type": "library", + "autoload": { + "files": [ + "lib/Exception/TimeoutException.php" + ], + "psr-4": { + "Facebook\\WebDriver\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A PHP client for Selenium WebDriver. Previously facebook/webdriver.", + "homepage": "https://github.com/php-webdriver/php-webdriver", + "keywords": [ + "Chromedriver", + "geckodriver", + "php", + "selenium", + "webdriver" + ], + "support": { + "issues": "https://github.com/php-webdriver/php-webdriver/issues", + "source": "https://github.com/php-webdriver/php-webdriver/tree/1.15.2" + }, + "time": "2024-11-21T15:12:59+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "5.6.3", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "94f8051919d1b0369a6bcc7931d679a511c03fe9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94f8051919d1b0369a6bcc7931d679a511c03fe9", + "reference": "94f8051919d1b0369a6bcc7931d679a511c03fe9", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.1", + "ext-filter": "*", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.7", + "phpstan/phpdoc-parser": "^1.7|^2.0", + "webmozart/assert": "^1.9.1" + }, + "require-dev": { + "mockery/mockery": "~1.3.5 || ~1.6.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-webmozart-assert": "^1.2", + "phpunit/phpunit": "^9.5", + "psalm/phar": "^5.26" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.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" + }, + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.3" + }, + "time": "2025-08-01T19:43:32+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.10.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/679e3ce485b99e84c775d28e2e96fade9a7fb50a", + "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.3 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^1.18|^2.0" + }, + "require-dev": { + "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", + "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-1.x": "1.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": "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.10.0" + }, + "time": "2024-11-09T15:12:26+00:00" + }, + { + "name": "phpstan/phpdoc-parser", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "b9e61a61e39e02dd90944e9115241c7f7e76bfd8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/b9e61a61e39e02dd90944e9115241c7f7e76bfd8", + "reference": "b9e61a61e39e02dd90944e9115241c7f7e76bfd8", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^5.3.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.6", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.2.0" + }, + "time": "2025-07-13T07:04:09+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "11.0.10", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "1a800a7446add2d79cc6b3c01c45381810367d76" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/1a800a7446add2d79cc6b3c01c45381810367d76", + "reference": "1a800a7446add2d79cc6b3c01c45381810367d76", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^5.4.0", + "php": ">=8.2", + "phpunit/php-file-iterator": "^5.1.0", + "phpunit/php-text-template": "^4.0.1", + "sebastian/code-unit-reverse-lookup": "^4.0.1", + "sebastian/complexity": "^4.0.1", + "sebastian/environment": "^7.2.0", + "sebastian/lines-of-code": "^3.0.1", + "sebastian/version": "^5.0.2", + "theseer/tokenizer": "^1.2.3" + }, + "require-dev": { + "phpunit/phpunit": "^11.5.2" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "11.0.x-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 that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/show" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage", + "type": "tidelift" + } + ], + "time": "2025-06-18T08:56:18+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "5.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/118cfaaa8bc5aef3287bf315b6060b1174754af6", + "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "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", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-08-27T05:02:59+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "5.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/c1ca3814734c07492b3d4c5f794f4b0995333da2", + "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^11.0" + }, + "suggest": { + "ext-pcntl": "*" + }, + "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", + "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", + "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/5.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:07:44+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/3e0404dc6b300e6bf56415467ebcb3fe4f33e964", + "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "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": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:08:43+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "7.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", + "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.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": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "security": "https://github.com/sebastianbergmann/php-timer/security/policy", + "source": "https://github.com/sebastianbergmann/php-timer/tree/7.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:09:35+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "11.5.34", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "3e4c6ef395f7cb61a6206c23e0e04b31724174f2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3e4c6ef395f7cb61a6206c23e0e04b31724174f2", + "reference": "3e4c6ef395f7cb61a6206c23e0e04b31724174f2", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.13.4", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=8.2", + "phpunit/php-code-coverage": "^11.0.10", + "phpunit/php-file-iterator": "^5.1.0", + "phpunit/php-invoker": "^5.0.1", + "phpunit/php-text-template": "^4.0.1", + "phpunit/php-timer": "^7.0.1", + "sebastian/cli-parser": "^3.0.2", + "sebastian/code-unit": "^3.0.3", + "sebastian/comparator": "^6.3.2", + "sebastian/diff": "^6.0.2", + "sebastian/environment": "^7.2.1", + "sebastian/exporter": "^6.3.0", + "sebastian/global-state": "^7.0.2", + "sebastian/object-enumerator": "^6.0.1", + "sebastian/type": "^5.1.3", + "sebastian/version": "^5.0.2", + "staabm/side-effects-detector": "^1.0.5" + }, + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "11.5-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.34" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2025-08-20T14:41:45+00:00" + }, + { + "name": "psalm/plugin-laravel", + "version": "v3.0.4", + "source": { + "type": "git", + "url": "https://github.com/psalm/psalm-plugin-laravel.git", + "reference": "364a35e28d45cabaece26f2a2e51e50fc43e99b4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/psalm/psalm-plugin-laravel/zipball/364a35e28d45cabaece26f2a2e51e50fc43e99b4", + "reference": "364a35e28d45cabaece26f2a2e51e50fc43e99b4", + "shasum": "" + }, + "require": { + "barryvdh/laravel-ide-helper": "~3.5.4", + "ext-simplexml": "*", + "illuminate/config": "^11.35 || ^12.0", + "illuminate/container": "^11.35 || ^12.0", + "illuminate/contracts": "^11.35 || ^12.0", + "illuminate/database": "^11.35 || ^12.0", + "illuminate/events": "^11.35 || ^12.0", + "illuminate/http": "^11.35 || ^12.0", + "illuminate/routing": "^11.35 || ^12.0", + "illuminate/support": "^11.35 || ^12.0", + "illuminate/view": "^11.35 || ^12.0", + "nikic/php-parser": "^5.0", + "orchestra/testbench-core": "^9.11 || ^10.0", + "php": "^8.2", + "symfony/console": "^7.1", + "symfony/finder": "^7.1", + "vimeo/psalm": "dev-master || ^6.0 || ^7.0.0-beta1" + }, + "require-dev": { + "laravel/framework": "^11.35 || ^12.0", + "phpunit/phpunit": "^10.5 || ^11.5", + "phpyh/psalm-tester": "^0.1.0", + "ramsey/collection": "^1.3", + "rector/rector": "^2.0", + "slevomat/coding-standard": "^8.15", + "squizlabs/php_codesniffer": "^3.11", + "symfony/http-foundation": "^7.1" + }, + "type": "psalm-plugin", + "extra": { + "psalm": { + "pluginClass": "Psalm\\LaravelPlugin\\Plugin" + } + }, + "autoload": { + "psr-4": { + "Psalm\\LaravelPlugin\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthew Brown", + "email": "github@muglug.com" + } + ], + "description": "Psalm plugin for Laravel", + "homepage": "https://github.com/psalm/psalm-plugin-laravel", + "keywords": [ + "dev", + "laravel", + "psalm", + "psalm-plugin" + ], + "support": { + "issues": "https://github.com/psalm/psalm-plugin-laravel/issues", + "source": "https://github.com/psalm/psalm-plugin-laravel/tree/v3.0.4" + }, + "time": "2025-08-12T18:13:40+00:00" + }, + { + "name": "revolt/event-loop", + "version": "v1.0.7", + "source": { + "type": "git", + "url": "https://github.com/revoltphp/event-loop.git", + "reference": "09bf1bf7f7f574453efe43044b06fafe12216eb3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/revoltphp/event-loop/zipball/09bf1bf7f7f574453efe43044b06fafe12216eb3", + "reference": "09bf1bf7f7f574453efe43044b06fafe12216eb3", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "ext-json": "*", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.15" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Revolt\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "ceesjank@gmail.com" + }, + { + "name": "Christian Lück", + "email": "christian@clue.engineering" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Rock-solid event loop for concurrent PHP applications.", + "keywords": [ + "async", + "asynchronous", + "concurrency", + "event", + "event-loop", + "non-blocking", + "scheduler" + ], + "support": { + "issues": "https://github.com/revoltphp/event-loop/issues", + "source": "https://github.com/revoltphp/event-loop/tree/v1.0.7" + }, + "time": "2025-01-25T19:27:39+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/15c5dd40dc4f38794d383bb95465193f5e0ae180", + "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.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", + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:41:36+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/54391c61e4af8078e5b276ab082b6d3c54c9ad64", + "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.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", + "security": "https://github.com/sebastianbergmann/code-unit/security/policy", + "source": "https://github.com/sebastianbergmann/code-unit/tree/3.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-03-19T07:56:08+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "183a9b2632194febd219bb9246eee421dad8d45e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/183a9b2632194febd219bb9246eee421dad8d45e", + "reference": "183a9b2632194febd219bb9246eee421dad8d45e", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "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" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "security": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/security/policy", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:45:54+00:00" + }, + { + "name": "sebastian/comparator", + "version": "6.3.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "85c77556683e6eee4323e4c5468641ca0237e2e8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/85c77556683e6eee4323e4c5468641ca0237e2e8", + "reference": "85c77556683e6eee4323e4c5468641ca0237e2e8", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.2", + "sebastian/diff": "^6.0", + "sebastian/exporter": "^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.4" + }, + "suggest": { + "ext-bcmath": "For comparing BcMath\\Number objects" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.3-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/6.3.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" + } + ], + "time": "2025-08-10T08:07:46+00:00" + }, + { + "name": "sebastian/complexity", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "ee41d384ab1906c68852636b6de493846e13e5a0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/ee41d384ab1906c68852636b6de493846e13e5a0", + "reference": "ee41d384ab1906c68852636b6de493846e13e5a0", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.0", + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "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": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:49:50+00:00" + }, + { + "name": "sebastian/diff", + "version": "6.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/b4ccd857127db5d41a5b676f24b51371d76d8544", + "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.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", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/6.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:53:05+00:00" + }, + { + "name": "sebastian/environment", + "version": "7.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/a5c75038693ad2e8d4b6c15ba2403532647830c4", + "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "https://github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/7.2.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/environment", + "type": "tidelift" + } + ], + "time": "2025-05-21T11:55:47+00:00" + }, + { + "name": "sebastian/exporter", + "version": "6.3.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "3473f61172093b2da7de1fb5782e1f24cc036dc3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/3473f61172093b2da7de1fb5782e1f24cc036dc3", + "reference": "3473f61172093b2da7de1fb5782e1f24cc036dc3", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=8.2", + "sebastian/recursion-context": "^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "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": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/6.3.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-12-05T09:17:50+00:00" + }, + { + "name": "sebastian/global-state", + "version": "7.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "3be331570a721f9a4b5917f4209773de17f747d7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/3be331570a721f9a4b5917f4209773de17f747d7", + "reference": "3be331570a721f9a4b5917f4209773de17f747d7", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "sebastian/object-reflector": "^4.0", + "sebastian/recursion-context": "^6.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/7.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:57:36+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/d36ad0d782e5756913e42ad87cb2890f4ffe467a", + "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.0", + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.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", + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/3.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:58:38+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "6.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "f5b498e631a74204185071eb41f33f38d64608aa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/f5b498e631a74204185071eb41f33f38d64608aa", + "reference": "f5b498e631a74204185071eb41f33f38d64608aa", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "sebastian/object-reflector": "^4.0", + "sebastian/recursion-context": "^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/6.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:00:13+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/6e1a43b411b2ad34146dee7524cb13a068bb35f9", + "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "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" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:01:32+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "6.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/f6458abbf32a6c8174f8f26261475dc133b3d9dc", + "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "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": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" + } + ], + "time": "2025-08-13T04:42:22+00:00" + }, + { + "name": "sebastian/type", + "version": "5.1.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/f77d2d4e78738c98d9a68d2596fe5e8fa380f449", + "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.1-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 types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "security": "https://github.com/sebastianbergmann/type/security/policy", + "source": "https://github.com/sebastianbergmann/type/tree/5.1.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/type", + "type": "tidelift" + } + ], + "time": "2025-08-09T06:55:48+00:00" + }, + { + "name": "sebastian/version", + "version": "5.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c687e3387b99f5b03b6caa64c74b63e2936ff874", + "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "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", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "security": "https://github.com/sebastianbergmann/version/security/policy", + "source": "https://github.com/sebastianbergmann/version/tree/5.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-10-09T05:16:32+00:00" + }, + { + "name": "spatie/array-to-xml", + "version": "3.4.0", + "source": { + "type": "git", + "url": "https://github.com/spatie/array-to-xml.git", + "reference": "7dcfc67d60b0272926dabad1ec01f6b8a5fb5e67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/array-to-xml/zipball/7dcfc67d60b0272926dabad1ec01f6b8a5fb5e67", + "reference": "7dcfc67d60b0272926dabad1ec01f6b8a5fb5e67", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "php": "^8.0" + }, + "require-dev": { + "mockery/mockery": "^1.2", + "pestphp/pest": "^1.21", + "spatie/pest-plugin-snapshots": "^1.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Spatie\\ArrayToXml\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://freek.dev", + "role": "Developer" + } + ], + "description": "Convert an array to xml", + "homepage": "https://github.com/spatie/array-to-xml", + "keywords": [ + "array", + "convert", + "xml" + ], + "support": { + "source": "https://github.com/spatie/array-to-xml/tree/3.4.0" + }, + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" + }, + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2024-12-16T12:45:15+00:00" + }, + { + "name": "staabm/side-effects-detector", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/staabm/side-effects-detector.git", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.6", + "phpunit/phpunit": "^9.6.21", + "symfony/var-dumper": "^5.4.43", + "tomasvotruba/type-coverage": "1.0.0", + "tomasvotruba/unused-public": "1.0.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "lib/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A static analysis tool to detect side effects in PHP code", + "keywords": [ + "static analysis" + ], + "support": { + "issues": "https://github.com/staabm/side-effects-detector/issues", + "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5" + }, + "funding": [ + { + "url": "https://github.com/staabm", + "type": "github" + } + ], + "time": "2024-10-20T05:08:20+00:00" + }, + { + "name": "staudenmeir/dusk-updater", + "version": "v1.5.1", + "source": { + "type": "git", + "url": "https://github.com/staudenmeir/dusk-updater.git", + "reference": "34a5e26ccaf729033c6511f8ebf9e7e1f127fc84" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/staudenmeir/dusk-updater/zipball/34a5e26ccaf729033c6511f8ebf9e7e1f127fc84", + "reference": "34a5e26ccaf729033c6511f8ebf9e7e1f127fc84", + "shasum": "" + }, + "require": { + "php": "^7.1.3|^8.0", + "symfony/process": "^4.2|^5.0|^6.0|^7.0" + }, + "require-dev": { + "orchestra/testbench": "^7.0|^8.0|^9.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Staudenmeir\\DuskUpdater\\DuskServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Staudenmeir\\DuskUpdater\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jonas Staudenmeir", + "email": "mail@jonas-staudenmeir.de" + } + ], + "description": "Updater for Laravel Dusk ChromeDriver binaries", + "support": { + "issues": "https://github.com/staudenmeir/dusk-updater/issues", + "source": "https://github.com/staudenmeir/dusk-updater/tree/v1.5.1" + }, + "time": "2024-08-19T20:14:33+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v7.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "edcbb768a186b5c3f25d0643159a787d3e63b7fd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/edcbb768a186b5c3f25d0643159a787d3e63b7fd", + "reference": "edcbb768a186b5c3f25d0643159a787d3e63b7fd", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "require-dev": { + "symfony/process": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "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": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v7.3.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-07-07T08:17:47+00:00" + }, + { + "name": "symfony/yaml", + "version": "v7.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "b8d7d868da9eb0919e99c8830431ea087d6aae30" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/b8d7d868da9eb0919e99c8830431ea087d6aae30", + "reference": "b8d7d868da9eb0919e99c8830431ea087d6aae30", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3.0", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/console": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0" + }, + "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/v7.3.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-07-10T08:47:49+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.3", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.3" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:36:25+00:00" + }, + { + "name": "vimeo/psalm", + "version": "6.13.1", + "source": { + "type": "git", + "url": "https://github.com/vimeo/psalm.git", + "reference": "1e3b7f0a8ab32b23197b91107adc0a7ed8a05b51" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vimeo/psalm/zipball/1e3b7f0a8ab32b23197b91107adc0a7ed8a05b51", + "reference": "1e3b7f0a8ab32b23197b91107adc0a7ed8a05b51", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/parallel": "^2.3", + "composer-runtime-api": "^2", + "composer/semver": "^1.4 || ^2.0 || ^3.0", + "composer/xdebug-handler": "^2.0 || ^3.0", + "danog/advanced-json-rpc": "^3.1", + "dnoegel/php-xdg-base-dir": "^0.1.1", + "ext-ctype": "*", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-simplexml": "*", + "ext-tokenizer": "*", + "felixfbecker/language-server-protocol": "^1.5.3", + "fidry/cpu-core-counter": "^0.4.1 || ^0.5.1 || ^1.0.0", + "netresearch/jsonmapper": "^5.0", + "nikic/php-parser": "^5.0.0", + "php": "~8.1.31 || ~8.2.27 || ~8.3.16 || ~8.4.3", + "sebastian/diff": "^4.0 || ^5.0 || ^6.0 || ^7.0", + "spatie/array-to-xml": "^2.17.0 || ^3.0", + "symfony/console": "^6.0 || ^7.0", + "symfony/filesystem": "~6.3.12 || ~6.4.3 || ^7.0.3", + "symfony/polyfill-php84": "^1.31.0" + }, + "provide": { + "psalm/psalm": "self.version" + }, + "require-dev": { + "amphp/phpunit-util": "^3", + "bamarni/composer-bin-plugin": "^1.4", + "brianium/paratest": "^6.9", + "danog/class-finder": "^0.4.8", + "dg/bypass-finals": "^1.5", + "ext-curl": "*", + "mockery/mockery": "^1.5", + "nunomaduro/mock-final-classes": "^1.1", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/phpdoc-parser": "^1.6", + "phpunit/phpunit": "^9.6", + "psalm/plugin-mockery": "^1.1", + "psalm/plugin-phpunit": "^0.19", + "slevomat/coding-standard": "^8.4", + "squizlabs/php_codesniffer": "^3.6", + "symfony/process": "^6.0 || ^7.0" + }, + "suggest": { + "ext-curl": "In order to send data to shepherd", + "ext-igbinary": "^2.0.5 is required, used to serialize caching data" + }, + "bin": [ + "psalm", + "psalm-language-server", + "psalm-plugin", + "psalm-refactor", + "psalm-review", + "psalter" + ], + "type": "project", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev", + "dev-2.x": "2.x-dev", + "dev-3.x": "3.x-dev", + "dev-4.x": "4.x-dev", + "dev-5.x": "5.x-dev", + "dev-6.x": "6.x-dev", + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psalm\\": "src/Psalm/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthew Brown" + }, + { + "name": "Daniil Gentili", + "email": "daniil@daniil.it" + } + ], + "description": "A static analysis tool for finding errors in PHP applications", + "keywords": [ + "code", + "inspection", + "php", + "static analysis" + ], + "support": { + "docs": "https://psalm.dev/docs", + "issues": "https://github.com/vimeo/psalm/issues", + "source": "https://github.com/vimeo/psalm" + }, + "time": "2025-08-06T10:10:28+00:00" + } + ], + "aliases": [], + "minimum-stability": "dev", + "stability-flags": {}, + "prefer-stable": true, + "prefer-lowest": false, + "platform": { + "php": "^8.4", + "ext-json": "*" + }, + "platform-dev": {}, + "platform-overrides": { + "php": "8.4.11" + }, + "plugin-api-version": "2.6.0" +} diff --git a/config/app.php b/config/app.php new file mode 100644 index 000000000..86fe58bca --- /dev/null +++ b/config/app.php @@ -0,0 +1,316 @@ + env('APP_NAME', 'IXP Manager'), + + /* + |-------------------------------------------------------------------------- + | Application Environment + |-------------------------------------------------------------------------- + | + | This value determines the "environment" your application is currently + | running in. This may determine how you prefer to configure various + | services your application utilizes. Set this in your ".env" file. + | + */ + 'env' => env('APP_ENV', 'production'), + + /* + |-------------------------------------------------------------------------- + | Application Debug Mode + |-------------------------------------------------------------------------- + | + | When your application is in debug mode, detailed error messages with + | stack traces will be shown on every error that occurs within your + | application. If disabled, a simple generic error page is shown. + | + */ + + 'debug' => env('APP_DEBUG',false), + + /* + |-------------------------------------------------------------------------- + | Application URL + |-------------------------------------------------------------------------- + | + | This URL is used by the console to properly generate URLs when using + | the Artisan command line tool. You should set this to the root of + | your application so that it is used when running Artisan tasks. + | + */ + + 'url' => env( 'APP_URL', 'http://localhost' ), + + 'asset_url' => env('ASSET_URL' ), + + /* + |-------------------------------------------------------------------------- + | Application Timezone + |-------------------------------------------------------------------------- + | + | Here you may specify the default timezone for your application, which + | will be used by the PHP date and date-time functions. We have gone + | ahead and set this to a sensible default for you out of the box. + | + */ + + 'timezone' => env( 'APP_TIMEZONE', 'UTC' ), + + /* + |-------------------------------------------------------------------------- + | Application Locale Configuration + |-------------------------------------------------------------------------- + | + | The application locale determines the default locale that will be used + | by the translation service provider. You are free to set this value + | to any of the locales which will be supported by the application. + | + */ + + 'locale' => env( 'APP_LOCALE', 'en' ), + + /* + |-------------------------------------------------------------------------- + | Application Fallback Locale + |-------------------------------------------------------------------------- + | + | The fallback locale determines the locale to use when the current one + | is not available. You may change the value to correspond to any of + | the language folders that are provided through your application. + | + */ + + 'fallback_locale' => 'en', + + + /* + |-------------------------------------------------------------------------- + | Faker Locale + |-------------------------------------------------------------------------- + | + | This locale will be used by the Faker PHP library when generating fake + | data for your database seeds. For example, this will be used to get + | localized telephone numbers, street address information and more. + | + */ + 'faker_locale' => 'en_US', + + + /* + |-------------------------------------------------------------------------- + | Encryption Key + |-------------------------------------------------------------------------- + | + | This key is used by the Illuminate encrypter service and should be set + | to a random, 32 character string, otherwise these encrypted strings + | will not be safe. Please do this before deploying an application! + | + */ + + 'key' => env('APP_KEY', 'SomeRandomString'), + + 'cipher' => 'AES-256-CBC', + + 'previous_keys' => [ + ...array_filter( + explode(',', (string) env('APP_PREVIOUS_KEYS', '')) + ), + ], + + + /* + |-------------------------------------------------------------------------- + | 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' => env('APP_MAINTENANCE_DRIVER', 'file'), + 'store' => env('APP_MAINTENANCE_STORE', 'database'), + ], + + /* + |-------------------------------------------------------------------------- + | Autoloaded Service Providers + |-------------------------------------------------------------------------- + | + | The service providers listed here will be automatically loaded on the + | request to your application. Feel free to add your own services to + | this array to grant expanded functionality to your applications. + | + */ + + 'providers' => [ + + /* + * Laravel Framework Service Providers... + */ + Illuminate\Auth\AuthServiceProvider::class, + Illuminate\Broadcasting\BroadcastServiceProvider::class, + Illuminate\Bus\BusServiceProvider::class, + Illuminate\Cache\CacheServiceProvider::class, + Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class, + Illuminate\Cookie\CookieServiceProvider::class, + Illuminate\Database\DatabaseServiceProvider::class, + Illuminate\Encryption\EncryptionServiceProvider::class, + Illuminate\Filesystem\FilesystemServiceProvider::class, + Illuminate\Foundation\Providers\FoundationServiceProvider::class, + Illuminate\Hashing\HashServiceProvider::class, + Illuminate\Mail\MailServiceProvider::class, + Illuminate\Notifications\NotificationServiceProvider::class, + Illuminate\Pagination\PaginationServiceProvider::class, + Illuminate\Pipeline\PipelineServiceProvider::class, + Illuminate\Queue\QueueServiceProvider::class, + Illuminate\Redis\RedisServiceProvider::class, + Illuminate\Auth\Passwords\PasswordResetServiceProvider::class, + Illuminate\Session\SessionServiceProvider::class, + Illuminate\Translation\TranslationServiceProvider::class, + Illuminate\Validation\ValidationServiceProvider::class, + Illuminate\View\ViewServiceProvider::class, + + /* + * Application Service Providers... + */ + IXP\Providers\AppServiceProvider::class, + IXP\Providers\AuthServiceProvider::class, + IXP\Providers\EventServiceProvider::class, + IXP\Providers\HorizonServiceProvider::class, + IXP\Providers\TelescopeServiceProvider::class, + IXP\Providers\RouteServiceProvider::class, + + IXP\Providers\HelpdeskServiceProvider::class, + IXP\Providers\GrapherServiceProvider::class, + IXP\Providers\LookingGlassServiceProvider::class, + IXP\Providers\FoilServiceProvider::class, + IXP\Providers\IXFServiceProvider::class, + IXP\Providers\PeeringDbServiceProvider::class, + IXP\Providers\IxpServiceProvider::class, + IXP\Providers\RipeAtlasProvider::class, + + IXP\Providers\DiagnosticsServiceProvider::class, + + /* + * Third party providers + */ + + + IXP\Providers\ParsedownServiceProvider::class, + + Former\FormerServiceProvider::class, + + //LukeTowers\Purifier\PurifierServiceProvider::class, + + Webpatser\Countries\CountriesServiceProvider::class, + + Intervention\Image\ImageServiceProvider::class, + + PragmaRX\Google2FALaravel\ServiceProvider::class, + + //Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class, + + ], + + /* + |-------------------------------------------------------------------------- + | Class Aliases + |-------------------------------------------------------------------------- + | + | 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. + | + */ + + 'aliases' => [ + + 'App' => 'Illuminate\Support\Facades\App', + 'Arr' => Illuminate\Support\Arr::class, + 'Artisan' => 'Illuminate\Support\Facades\Artisan', + 'Auth' => 'Illuminate\Support\Facades\Auth', + 'Blade' => 'Illuminate\Support\Facades\Blade', + 'Broadcast' => Illuminate\Support\Facades\Broadcast::class, + 'Bus' => 'Illuminate\Support\Facades\Bus', + 'Cache' => 'Illuminate\Support\Facades\Cache', + 'Config' => 'Illuminate\Support\Facades\Config', + 'Cookie' => 'Illuminate\Support\Facades\Cookie', + 'Crypt' => 'Illuminate\Support\Facades\Crypt', + 'DB' => 'Illuminate\Support\Facades\DB', + 'Eloquent' => 'Illuminate\Database\Eloquent\Model', + 'Event' => 'Illuminate\Support\Facades\Event', + 'File' => 'Illuminate\Support\Facades\File', + 'Gate' => Illuminate\Support\Facades\Gate::class, + 'Hash' => 'Illuminate\Support\Facades\Hash', + 'Http' => Illuminate\Support\Facades\Http::class, + 'Input' => 'Illuminate\Support\Facades\Input', + 'Lang' => 'Illuminate\Support\Facades\Lang', + 'Log' => 'Illuminate\Support\Facades\Log', + 'Mail' => 'Illuminate\Support\Facades\Mail', + 'Notification' => Illuminate\Support\Facades\Notification::class, + 'Password' => 'Illuminate\Support\Facades\Password', + 'Queue' => 'Illuminate\Support\Facades\Queue', + 'Redirect' => 'Illuminate\Support\Facades\Redirect', + //'Redis' => 'Illuminate\Support\Facades\Redis', + 'Request' => 'Illuminate\Support\Facades\Request', + 'Response' => 'Illuminate\Support\Facades\Response', + 'Route' => 'Illuminate\Support\Facades\Route', + 'Schema' => 'Illuminate\Support\Facades\Schema', + 'Session' => 'Illuminate\Support\Facades\Session', + 'Storage' => 'Illuminate\Support\Facades\Storage', + 'Str' => Illuminate\Support\Str::class, + 'URL' => 'Illuminate\Support\Facades\URL', + 'Validator' => 'Illuminate\Support\Facades\Validator', + 'View' => 'Illuminate\Support\Facades\View', + + 'Grapher' => IXP\Support\Facades\Grapher::class, + 'Image' => Intervention\Image\Facades\Image::class, + + 'Former' => Former\Facades\Former::class, + //'Purifier' => LukeTowers\Purifier\Facades\Purifier::class, + 'PDF' => Barryvdh\DomPDF\Facade::class, + + 'Countries' => Webpatser\Countries\CountriesFacade::class, + + 'Google2FA' => PragmaRX\Google2FALaravel\Facade::class, + ], + +]; diff --git a/config/auth.php b/config/auth.php new file mode 100644 index 000000000..b1fe27101 --- /dev/null +++ b/config/auth.php @@ -0,0 +1,138 @@ + [ + 'guard' => 'web', + 'passwords' => 'users', + ], + + /* + |-------------------------------------------------------------------------- + | Authentication Guards + |-------------------------------------------------------------------------- + | + | Next, you may define every authentication guard for your application. + | Of course, a great default configuration has been defined for you + | here which uses session storage and the Eloquent user provider. + | + | All authentication drivers have a user provider. This defines how the + | users are actually retrieved out of your database or other storage + | mechanisms used by this application to persist your user's data. + | + | Supported: "session", "token" + | + */ + + 'guards' => [ + 'web' => [ + 'driver' => 'session', + 'provider' => 'users', + 'expire' => env( 'AUTH_TOKEN_EXPIRE', 43200 ), // optional token expiration time, in minutes (30 days is the default) + ], + + 'api' => [ + 'driver' => 'token', + 'provider' => 'users', + 'hash' => false, + ], + ], + + /* + |-------------------------------------------------------------------------- + | User Providers + |-------------------------------------------------------------------------- + | + | All authentication drivers have a user provider. This defines how the + | users are actually retrieved out of your database or other storage + | mechanisms used by this application to persist your user's data. + | + | If you have multiple user tables or models you may configure multiple + | sources which represent each model / table. These sources may then + | be assigned to any extra authentication guards you have defined. + | + | Supported: "database", "eloquent" + | + */ + 'providers' => [ + 'users' => [ + 'driver' => 'eloquent', + 'model' => User::class, + ], + +// 'users' => [ +// 'driver' => 'database', +// 'table' => 'user', +// ], + ], + + /* + |-------------------------------------------------------------------------- + | Resetting Passwords + |-------------------------------------------------------------------------- + | + | Here you may set the options for resetting passwords including the view + | that is your password reset e-mail. You may also set the name of the + | table that maintains all of the reset tokens for your application. + | + | You may specify multiple password reset configurations if you have more + | than one user table or model in the application and you want to have + | separate password reset settings based on the specific user types. + | + | The expire time is the number of minutes that the reset token should be + | considered valid. This security feature keeps tokens short-lived so + | they have less time to be guessed. You may change this as needed. + | + */ + 'passwords' => [ + 'users' => [ + 'provider' => 'users', + 'email' => 'auth.emails.password', + 'table' => 'password_resets', + 'expire' => 10080, // 7 days + ], + ], + + /* + |-------------------------------------------------------------------------- + | OAuth via PeeringDB + |-------------------------------------------------------------------------- + | + */ + 'peeringdb' => [ + 'enabled' => env( 'AUTH_PEERINGDB_ENABLED', false ), + 'privs' => env( 'AUTH_PEERINGDB_PRIVS', User::AUTH_CUSTUSER ), + ], +]; \ No newline at end of file diff --git a/config/cache.php b/config/cache.php new file mode 100644 index 000000000..e7278633f --- /dev/null +++ b/config/cache.php @@ -0,0 +1,115 @@ + env('CACHE_DRIVER', 'file'), + + /* + |-------------------------------------------------------------------------- + | Cache Stores + |-------------------------------------------------------------------------- + | + | Here you may define all of the cache "stores" for your application as + | well as their drivers. You may even define multiple stores for the + | same cache driver to group types of items stored in your caches. + | + */ + + 'stores' => [ + + 'apc' => [ + 'driver' => 'apc' + ], + + 'array' => [ + 'driver' => 'array', + 'serialize' => false, + ], + + 'database' => [ + 'driver' => 'database', + 'table' => 'cache', + 'connection' => null, + 'lock_connection' => null, + ], + + 'file' => [ + 'driver' => 'file', + 'path' => storage_path().'/framework/cache/data', + ], + + 'memcached' => [ + 'driver' => 'memcached', + 'servers' => [ + [ + 'host' => '127.0.0.1', 'port' => 11211, 'weight' => 100 + ], + ], + ], + + 'redis' => [ + 'driver' => 'redis', + 'connection' => 'default', + 'lock_connection' => 'default', + ], + + + 'dynamodb' => [ + 'driver' => 'dynamodb', + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), + 'table' => env('DYNAMODB_CACHE_TABLE', 'cache'), + ], + ], + + /* + |-------------------------------------------------------------------------- + | 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. + | + */ + + 'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_cache_'), +]; diff --git a/config/contact_group.php.dist b/config/contact_group.php.dist new file mode 100644 index 000000000..a23fa097f --- /dev/null +++ b/config/contact_group.php.dist @@ -0,0 +1,44 @@ + [ + 'ROLE' => 'Role', + //'MEETING' => 'Meeting Registration', + //'MARKETING_PREFERENCES' => 'Marketing Preferences', + ], +]; diff --git a/config/cors.php b/config/cors.php new file mode 100644 index 000000000..ce3be27ff --- /dev/null +++ b/config/cors.php @@ -0,0 +1,56 @@ + ['api/*', 'sanctum/csrf-cookie'], + + 'allowed_methods' => ['*'], + + 'allowed_origins' => ['*'], + + 'allowed_origins_patterns' => [], + + 'allowed_headers' => ['*'], + + 'exposed_headers' => [], + + 'max_age' => 0, + + 'supports_credentials' => false, + +]; \ No newline at end of file diff --git a/config/countries.php b/config/countries.php new file mode 100644 index 000000000..4da6ec1bb --- /dev/null +++ b/config/countries.php @@ -0,0 +1,34 @@ + 'countries', +]; diff --git a/config/custom.php.dist b/config/custom.php.dist new file mode 100644 index 000000000..45c0d0a51 --- /dev/null +++ b/config/custom.php.dist @@ -0,0 +1,42 @@ + + */ + +return [ + + // 'example' => [ + // 'key' => 'my own config value', + // ], + +]; diff --git a/config/database.php b/config/database.php new file mode 100644 index 000000000..1f7104235 --- /dev/null +++ b/config/database.php @@ -0,0 +1,130 @@ + env('DB_CONNECTION', 'mysql'), + + /* + |-------------------------------------------------------------------------- + | Database Connections + |-------------------------------------------------------------------------- + | + | Here are each of the database connections setup for your application. + | Of course, examples of configuring each database platform that is + | supported by Laravel is shown below to make development simple. + | + | + | All database work in Laravel is done through the PHP PDO facilities + | so make sure you have the driver for your particular database of + | choice installed on your machine before you begin development. + | + */ + + 'connections' => [ + + 'mysql' => [ + 'driver' => 'mysql', + 'host' => env('DB_HOST', 'localhost'), + 'database' => env('DB_DATABASE', 'forge'), + 'username' => env('DB_USERNAME', 'forge'), + 'password' => env('DB_PASSWORD', ''), + 'charset' => 'utf8mb4', + 'collation' => 'utf8mb4_unicode_ci', + 'prefix' => '', + 'prefix_indexes' => true, + 'strict' => false, + 'engine' => null, + 'options' => extension_loaded('pdo_mysql') ? array_filter([ + PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), + ]) : [], + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Migration Repository Table + |-------------------------------------------------------------------------- + | + | This table keeps track of all the migrations that have already run for + | your application. Using this information, we can determine which of + | the migrations on disk haven't actually been run in the database. + | + */ + + 'migrations' => 'migrations', + + /* + |-------------------------------------------------------------------------- + | Redis Databases + |-------------------------------------------------------------------------- + | + | Redis is an open source, fast, and advanced key-value store that also + | provides a richer set of commands than a typical key-value systems + | such as APC or Memcached. Laravel makes it easy to dig right in. + | + */ + + 'redis' => [ + 'client' => env('REDIS_CLIENT', 'phpredis'), + + 'options' => [ + 'cluster' => env('REDIS_CLUSTER', 'redis'), + ], + + 'default' => [ + 'host' => env('REDIS_HOST', '127.0.0.1'), + 'username' => env('REDIS_USERNAME'), + 'password' => env('REDIS_PASSWORD'), + 'port' => env('REDIS_PORT', 6379), + 'database' => env('REDIS_DB', 0), + ], + 'cache' => [ + 'host' => env('REDIS_HOST', '127.0.0.1'), + 'username' => env('REDIS_USERNAME'), + 'password' => env('REDIS_PASSWORD'), + 'port' => env('REDIS_PORT', 6379), + 'database' => env('REDIS_CACHE_DB', 1), + ], + ], + + 'dbal' => [ + 'types' => [ + 'timestamp' => TimestampType::class, + ], + ], +]; diff --git a/config/debugbar.php b/config/debugbar.php new file mode 100644 index 000000000..ae95836fa --- /dev/null +++ b/config/debugbar.php @@ -0,0 +1,228 @@ + env('DEBUGBAR_ENABLED', null), + 'except' => [ + 'telescope*' + ], + + /* + |-------------------------------------------------------------------------- + | 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' => 'file', // redis, file, pdo, custom + 'path' => storage_path('debugbar'), // For file driver + 'connection' => null, // Leave null for default connection (Redis/PDO) + 'provider' => '' // Instance of StorageInterface for custom driver + ], + + /* + |-------------------------------------------------------------------------- + | 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. + | + | Optionally, you can also send ServerTiming headers on ajax requests for the Chrome DevTools. + */ + + 'capture_ajax' => true, + 'add_ajax_timing' => false, + + /* + |-------------------------------------------------------------------------- + | Custom Error Handler for Deprecated warnings + |-------------------------------------------------------------------------- + | + | When enabled, the Debugbar shows deprecated warnings for Symfony components + | in the Messages tab. + | + */ + 'error_handler' => false, + + /* + |-------------------------------------------------------------------------- + | 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) + // --------------------------------------------------------------------------- + // barryo - 20180214 - the following is set to false as Eloquent namespace + // conflicts with Doctrine namespace for the debugbar. So, no Eloquent love + // in IXP Manager on this valentine's day xxx + 'db' => true, // Show database (PDO) queries and bindings + // --------------------------------------------------------------------------- + 'views' => true, // Views with their data + 'route' => true, // Current route information + 'auth' => true, // Display Laravel authentication status + 'gate' => true, // Display Laravel Gate checks + 'session' => true, // Display session data + 'symfony_request' => true, // Only one can be enabled.. + 'mail' => true, // Catch mail messages + 'laravel' => false, // Laravel version and environment + 'events' => false, // All events fired + 'default_request' => false, // Regular or special Symfony request logger + 'logs' => false, // Add the latest log messages + 'files' => false, // Show the included files + 'config' => false, // Display config settings + 'cache' => false, // Display cache events + 'models' => true, // Display models + ], + + /* + |-------------------------------------------------------------------------- + | Extra options + |-------------------------------------------------------------------------- + | + | Configure some DataCollectors + | + */ + + 'options' => [ + 'auth' => [ + 'show_name' => true, // Also show the users name/email in the debugbar + ], + 'db' => [ + 'with_params' => true, // Render SQL with the parameters substituted + 'backtrace' => false, // Use a backtrace to find the origin of the query in your files. + 'timeline' => false, // Add the queries to the timeline + 'explain' => [ // Show EXPLAIN output on queries + 'enabled' => false, + 'types' => ['SELECT'], // ['SELECT', 'INSERT', 'UPDATE', 'DELETE']; for MySQL 5.6.3+ + ], + 'hints' => true, // 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 + ], + 'cache' => [ + 'values' => true // collect cache values + ], + ], + + /* + |-------------------------------------------------------------------------- + | 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', + + /* + |-------------------------------------------------------------------------- + | DebugBar route domain + |-------------------------------------------------------------------------- + | + | By default DebugBar route served from the same domain that request served. + | To override default domain, specify it as a non-empty value. + */ + 'route_domain' => null, +]; \ No newline at end of file diff --git a/config/filesystems.php b/config/filesystems.php new file mode 100644 index 000000000..86c6034f9 --- /dev/null +++ b/config/filesystems.php @@ -0,0 +1,110 @@ + env('FILESYSTEM_DISK', 'local'), + + /* + |-------------------------------------------------------------------------- + | Filesystem Disks + |-------------------------------------------------------------------------- + | + | 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 set up for each driver as an example of the required values. + | + | Supported Drivers: "local", "ftp", "sftp", "s3" + | + */ + + 'disks' => [ + + 'docstore' => [ + 'driver' => 'local', + 'root' => storage_path('docstore'), + 'throw' => false, + ], + + 'docstore_customers' => [ + 'driver' => 'local', + 'root' => storage_path('docstore_customers'), + 'throw' => false, + ], + + 'local' => [ + 'driver' => 'local', + 'root' => storage_path('app'), + 'throw' => false, + ], + + 'public' => [ + 'driver' => 'local', + 'root' => storage_path('app/public'), + 'url' => env('APP_URL').'/storage', + 'visibility' => 'public', + 'throw' => false, + ], + + 's3' => [ + 'driver' => 's3', + 'key' => env('AWS_ACCESS_KEY_ID'), + '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'), + ], + +]; \ No newline at end of file diff --git a/config/former.php b/config/former.php new file mode 100644 index 000000000..6e4175243 --- /dev/null +++ b/config/former.php @@ -0,0 +1,229 @@ + true, + + // The default form type + 'default_form_type' => 'horizontal', + + // Validation + //////////////////////////////////////////////////////////////////// + + // Whether Former should fetch errors from Session + 'fetch_errors' => true, + + // Whether Former should try to apply Validator rules as attributes + 'live_validation' => env( 'FORMER_LIVE_VALIDATION', true ), + + // Whether Former should automatically fetch error messages and + // display them next to the matching fields + 'error_messages' => true, + + // Checkables + //////////////////////////////////////////////////////////////////// + + // Whether checkboxes should always be present in the POST data, + // no matter if you checked them or not + 'push_checkboxes' => true, + + // The value a checkbox will have in the POST array if unchecked + 'unchecked_value' => 0, + + // Required fields + //////////////////////////////////////////////////////////////////// + + // The class to be added to required fields + 'required_class' => 'required', + + // A facultative text to append to the labels of required fields + 'required_text' => '*', + + // Translations + //////////////////////////////////////////////////////////////////// + + // Where Former should look for translations + 'translate_from' => 'validation.attributes', + + // Whether text that comes out of the translated + // should be capitalized (ex: email => Email) automatically + 'capitalize_translations' => false, + + // An array of attributes to automatically translate + 'translatable' => array( + 'help', + 'inlineHelp', + 'blockHelp', + 'placeholder', + 'data_placeholder', + 'label', + ), + + // Framework + //////////////////////////////////////////////////////////////////// + + // The framework to be used by Former + 'framework' => 'TwitterBootstrap4', + + 'TwitterBootstrap4' => array( + // Map Former-supported viewports to Bootstrap 4 equivalents + 'viewports' => array( + 'large' => 'lg', + 'medium' => 'md', + 'small' => 'sm', + 'mini' => 'xs', + ), + // Width of labels for horizontal forms expressed as viewport => grid columns + 'labelWidths' => array( + 'large' => 2, + 'small' => 4, + ), + // HTML markup and classes used by Bootstrap 5 for icons + 'icon' => array( + 'tag' => 'i', + 'set' => 'fa', + 'prefix' => 'fa', + ), + ), + + + 'TwitterBootstrap3' => array( + + // Map Former-supported viewports to Bootstrap 3 equivalents + 'viewports' => array( + 'large' => 'lg', + 'medium' => 'md', + 'small' => 'sm', + 'mini' => 'xs', + ), + // Width of labels for horizontal forms expressed as viewport => grid columns + 'labelWidths' => array( + 'large' => 2, + 'small' => 4, + ), + // HTML markup and classes used by Bootstrap 3 for icons + 'icon' => array( + 'tag' => 'span', + 'set' => 'glyphicon', + 'prefix' => 'glyphicon', + ), + + ), + + 'Nude' => array( // No-framework markup + 'icon' => array( + 'tag' => 'i', + 'set' => null, + 'prefix' => 'icon', + ), + ), + + 'TwitterBootstrap' => array( // Twitter Bootstrap version 2 + 'icon' => array( + 'tag' => 'i', + 'set' => null, + 'prefix' => 'icon', + ), + ), + + 'ZurbFoundation5' => array( + // Map Former-supported viewports to Foundation 5 equivalents + 'viewports' => array( + 'large' => 'large', + 'medium' => null, + 'small' => 'small', + 'mini' => null, + ), + // Width of labels for horizontal forms expressed as viewport => grid columns + 'labelWidths' => array( + 'small' => 3, + ), + // Classes to be applied to wrapped labels in horizontal forms + 'wrappedLabelClasses' => array('right', 'inline'), + // HTML markup and classes used by Foundation 5 for icons + 'icon' => array( + 'tag' => 'i', + 'set' => null, + 'prefix' => 'fi', + ), + // CSS for inline validation errors + 'error_classes' => array('class' => 'error'), + ), + + 'ZurbFoundation4' => array( + // Foundation 4 also has an experimental "medium" breakpoint + // explained at http://foundation.zurb.com/docs/components/grid.html + 'viewports' => array( + 'large' => 'large', + 'medium' => null, + 'small' => 'small', + 'mini' => null, + ), + // Width of labels for horizontal forms expressed as viewport => grid columns + 'labelWidths' => array( + 'small' => 3, + ), + // Classes to be applied to wrapped labels in horizontal forms + 'wrappedLabelClasses' => array('right', 'inline'), + // HTML markup and classes used by Foundation 4 for icons + 'icon' => array( + 'tag' => 'i', + 'set' => 'general', + 'prefix' => 'foundicon', + ), + // CSS for inline validation errors + 'error_classes' => array('class' => 'alert-box radius warning'), + ), + + 'ZurbFoundation' => array( // Foundation 3 + 'viewports' => array( + 'large' => '', + 'medium' => null, + 'small' => 'mobile-', + 'mini' => null, + ), + // Width of labels for horizontal forms expressed as viewport => grid columns + 'labelWidths' => array( + 'large' => 2, + 'small' => 4, + ), + // Classes to be applied to wrapped labels in horizontal forms + 'wrappedLabelClasses' => array('right', 'inline'), + // HTML markup and classes used by Foundation 3 for icons + 'icon' => array( + 'tag' => 'i', + 'set' => null, + 'prefix' => 'fi', + ), + // CSS for inline validation errors + // should work for Zurb 2 and 3 + 'error_classes' => array('class' => 'alert-box alert error'), + ), + + +); diff --git a/config/google2fa.php b/config/google2fa.php new file mode 100644 index 000000000..3d9cb6061 --- /dev/null +++ b/config/google2fa.php @@ -0,0 +1,96 @@ + env( '2FA_ENABLED', true ), + + /* + * Lifetime in minutes. + * In case you need your users to be asked for a new one time passwords from time to time. + * + * For IXP Manager, we leave this as eternal and rely on the maximum session lifetime instead. + */ + 'lifetime' => 0, // 0 = eternal + + /* + * Renew lifetime at every new request. + */ + 'keep_alive' => true, + + /* + * Auth container binding + */ + 'auth' => 'auth', + + /* + * 2FA verified session var + */ + 'session_var' => 'google2fa', + + /* + * One Time Password request input name + */ + 'otp_input' => 'one_time_password', + + /* + * One Time Password Window + */ + 'window' => env( '2FA_WINDOW', 4 ), + + /* + * Forbid user to reuse One Time Passwords. + */ + 'forbid_old_passwords' => false, + + /* + * User's table column for google2fa secret + */ + 'otp_secret_column' => 'secret', + + /* + * One Time Password View + */ + 'view' => 'user.2fa.login-form', + + /* + * One Time Password error message + */ + 'error_messages' => [ + 'wrong_otp' => "The one time password entered was wrong.", + 'cannot_be_empty' => 'One time password cannot be empty.', + 'unknown' => 'An unknown error has occurred. Please try again.', + ], + + /* + * Throw exceptions or just fire events? + */ + 'throw_exceptions' => false, + + /* + * Which image backend to use for generating QR codes? + * + * Supports imagemagick, svg and eps + */ + 'qrcode_image_backend' => Constants::QRCODE_IMAGE_BACKEND_SVG, + + + /* + * Require 2FA authentication for IXP users >= this level. + * + * By default we set it to enforce 2fa for all users. + * + * To force: + * + * - all superusers, set this to \IXP\Models\User::AUTH_SUPERUSER (3); + * - all custadmins and superusers, set this to \IXP\Models\User::AUTH_CUSTADMIN (2); + * - all users set this to \IXP\Models\User::AUTH_CUSTUSER (1 or less); + */ + 'ixpm_2fa_enforce_for_users' => env( '2FA_ENFORCE_FOR_USERS', \IXP\Models\User::AUTH_CUSTUSER ), + +]; diff --git a/config/grapher.php b/config/grapher.php new file mode 100644 index 000000000..0537285b5 --- /dev/null +++ b/config/grapher.php @@ -0,0 +1,204 @@ + [ + 'dummy' => IXP\Services\Grapher\Backend\Dummy::class, + 'mrtg' => IXP\Services\Grapher\Backend\Mrtg::class, + 'sflow' => IXP\Services\Grapher\Backend\Sflow::class, + 'smokeping' => IXP\Services\Grapher\Backend\Smokeping::class, + ], + + /* + |-------------------------------------------------------------------------- + | Default Helpdesk Backend + |-------------------------------------------------------------------------- + | + | Options are defined above in 'providers' + | + | Multiple backends are supported. Separate them in the the .env with '|' + */ + + 'backend' => explode( '|', env( 'GRAPHER_BACKENDS', 'dummy' ) ), + + /* + |-------------------------------------------------------------------------- + | Helpdesk Backends + |-------------------------------------------------------------------------- + | + */ + + 'backends' => [ + + 'dummy' => [ + // where to find the dummy MRTG log files (and png files) + 'logdir' => env( 'GRAPHER_BACKEND_DUMMY_LOGDIR', base_path() . '/data/grapher/dummy' ), + ], + + 'mrtg' => [ + // see: http://oss.oetiker.ch/mrtg/doc/mrtg-rrd.en.html + 'dbtype' => env( 'GRAPHER_BACKEND_MRTG_DBTYPE', 'log' ), // options: log, rrd + + // where to store log/rrd/png files as created above. This is from the perspective + // of the mrtg daemon so should also be local + 'workdir' => env( 'GRAPHER_BACKEND_MRTG_WORKDIR', '/tmp' ), + + // where to find the WORKDIR above from IXP Manager's perspective. This can be a + // local directory or a URL to remote web server + 'logdir' => env( 'GRAPHER_BACKEND_MRTG_LOGDIR', '/tmp' ), + + 'trunks' => call_user_func( function() { + if( file_exists( config_path() . '/grapher_trunks.php' ) ) { + return include( config_path() . '/grapher_trunks.php' ); + } + return null; + }), + + // tmp until we sort out trunks: + 'snmppasswd' => env( 'GRAPHER_BACKEND_MRTG_SNMPPASSWD', 'soopersecret' ), + ], + + 'sflow' => [ + // show sflow / p2p links on the frontend + 'enabled' => env( 'GRAPHER_BACKEND_SFLOW_ENABLED', false ), + + // for larger IXPs, it's quite intensive to display all the graphs + 'show_graphs_on_index_page' => env( 'GRAPHER_BACKEND_SFLOW_SHOW_ON_INDEX', false ), + + // where to find the MRTG rrd files + 'root' => env( 'GRAPHER_BACKEND_SFLOW_ROOT', 'http://www.example.com/' ), + ], + + 'smokeping' => [ + // show smokeping links on the frontend + 'enabled' => env( 'GRAPHER_BACKEND_SMOKEPING_ENABLED', false ), + + // where to find the smokeping files + 'url' => env( 'GRAPHER_BACKEND_SMOKEPING_URL', 'http://www.example.com/' ), + + // per VLAN overrides: + 'overrides' => call_user_func( function() { + if( file_exists( config_path( 'grapher_smokeping_overrides.php' ) ) ) { + return include( config_path( 'grapher_smokeping_overrides.php' ) ); + } else { + return []; + } + }), + + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Cache Settings + |-------------------------------------------------------------------------- + | + | Creating imahes / loading log files are expensive operations. Especially + | when not on the local machine. + | + | As such, we cache these items by default in the local file store. + | + */ + + 'cache' => [ + // cache enabled? + 'enabled' => env( 'GRAPHER_CACHE_ENABLED', 'true' ), + + // cache store to use + 'store' => env( 'GRAPHER_CACHE_STORE', 'file' ), + + // cache lifetime - this is not editable as 5mins in an industry standard + 'lifetime' => 5, + + // namespace -> not a cache namespace but a key namespace. + // All keys are prepended with 'grapher::' + ], + + /* + |-------------------------------------------------------------------------- + | Access permissions + |-------------------------------------------------------------------------- + | + | Most IXPs make aggregate graphs publicly available. Namely IXP, + | infrastructure, switch and trunks. As such, this is our default. + | + | You can alter these settings to change these defaults to a specific user + | level. + | + | NB: if these are not defined, THEY DEFAULT TO PUBLIC ACCESS + | + */ + 'access' => [ + 'ixp' => env( 'GRAPHER_ACCESS_IXP', \IXP\Models\User::AUTH_PUBLIC ), + 'infrastructure' => env( 'GRAPHER_ACCESS_INFRASTRUCTURE', \IXP\Models\User::AUTH_PUBLIC ), + 'location' => env( 'GRAPHER_ACCESS_LOCATION', \IXP\Models\User::AUTH_PUBLIC ), + 'switch' => env( 'GRAPHER_ACCESS_SWITCH', \IXP\Models\User::AUTH_PUBLIC ), + 'trunk' => env( 'GRAPHER_ACCESS_TRUNK', \IXP\Models\User::AUTH_PUBLIC ), + 'vlan' => env( 'GRAPHER_ACCESS_VLAN', \IXP\Models\User::AUTH_PUBLIC ), + + // The follows DO NOT DEFAULT TO PUBLIC but rather customer's are only allowed access + // their own graphs by default. + // + // See: https://docs.ixpmanager.org/latest/grapher/api/#access-to-member-graphs + 'customer' => env( 'GRAPHER_ACCESS_CUSTOMER', 'own_graphs_only' ), + 'p2p' => env( 'GRAPHER_ACCESS_P2P', 'own_graphs_only' ), + 'latency' => env( 'GRAPHER_ACCESS_LATENCY', 'own_graphs_only' ), + ], + + + /* + |-------------------------------------------------------------------------- + | + | CLI Tool Settings + | + | Relate to artisan grapher:xxx + | + | See: FIXME link to docs + | + */ + 'cli' => [ + 'traffic_differentials' => [ + 'stddev_calc_length' => env( 'GRAPHER_CLI_TRAFFICDIFFERENTIALS_CALC_LEN', 60 ), + ], + + 'traffic_daily' => [ + + // the traffic_daily table can get pretty full and most of the long term information + // are in the MRTG / other stats files anyway. If you want to keep this data in the + // database, set the following to false. If it's true, when the daily task runs + // to populate this table, it will also delete any entries older than + // the number of days. + 'delete_old' => env( 'GRAPHER_CLI_TRAFFICDAILY_DELETE_OLD', true ), + + // Remember that the traffic deltas script takes one row per week to build up + // a standard deviation so this needs to be usefully large: + 'delete_old_days' => env( 'GRAPHER_CLI_TRAFFICDAILY_DELETE_OLD_DAYS', 140 ), + ], + ], + +]; diff --git a/config/grapher_trunks.php.dist b/config/grapher_trunks.php.dist new file mode 100644 index 000000000..417d58d7c --- /dev/null +++ b/config/grapher_trunks.php.dist @@ -0,0 +1,39 @@ + [ + 'ixpid' => 1, + 'name' => 'core-sw1-sw2-lan1 is the name as defined in services.grapher.mrtg.trunks', + 'title' => 'PoP 1 to PoP 2 (LAN1)' + ], +]; diff --git a/config/hashing.php b/config/hashing.php new file mode 100644 index 000000000..ff4d12ca1 --- /dev/null +++ b/config/hashing.php @@ -0,0 +1,53 @@ + 'bcrypt', + + /* + |-------------------------------------------------------------------------- + | Bcrypt Options + |-------------------------------------------------------------------------- + | + | Here you may specify the configuration options that should be used when + | passwords are hashed using the Bcrypt algorithm. This will allow you + | to control the amount of time it takes to hash the given password. + | + */ + + 'bcrypt' => [ + 'rounds' => env('BCRYPT_ROUNDS', 12), + ], + + /* + |-------------------------------------------------------------------------- + | Argon Options + |-------------------------------------------------------------------------- + | + | Here you may specify the configuration options that should be used when + | passwords are hashed using the Argon algorithm. These will allow you + | to control the amount of time it takes to hash the given password. + | + */ + + 'argon' => [ + 'memory' => 1024, + 'threads' => 2, + 'time' => 2, + ], + + 'rehash_on_login' => false, +]; diff --git a/config/helpdesk.php b/config/helpdesk.php new file mode 100644 index 000000000..884beb754 --- /dev/null +++ b/config/helpdesk.php @@ -0,0 +1,53 @@ + env('HELPDESK_BACKEND', 'none' ), + + /* + |-------------------------------------------------------------------------- + | Helpdesk Backends + |-------------------------------------------------------------------------- + | + */ + + 'backends' => [ + + 'zendesk' => [ + 'subdomain' => env('HELPDESK_ZENDESK_SUBDOMAIN', 'xxx'), + 'token' => env('HELPDESK_ZENDESK_TOKEN', 'xxx'), + 'email' => env('HELPDESK_ZENDESK_EMAIL', 'xxx') + ], + + ], + +]; diff --git a/config/horizon.php b/config/horizon.php new file mode 100644 index 000000000..69cdc5f0c --- /dev/null +++ b/config/horizon.php @@ -0,0 +1,162 @@ + null, + + /* + |-------------------------------------------------------------------------- + | Horizon Path + |-------------------------------------------------------------------------- + | + | This is the URI path where Horizon will be accessible from. Feel free + | to change this path to anything you like. Note that the URI will not + | affect the paths of its internal API that aren't exposed to users. + | + */ + + 'path' => 'horizon', + + /* + |-------------------------------------------------------------------------- + | Horizon Redis Connection + |-------------------------------------------------------------------------- + | + | This is the name of the Redis connection where Horizon will store the + | meta information required for it to function. It includes the list + | of supervisors, failed jobs, job metrics, and other information. + | + */ + + 'use' => 'default', + + /* + |-------------------------------------------------------------------------- + | Horizon Redis Prefix + |-------------------------------------------------------------------------- + | + | This prefix will be used when storing all Horizon data in Redis. You + | may modify the prefix when you are running multiple installations + | of Horizon on the same server so that they don't have problems. + | + */ + + 'prefix' => env('HORIZON_PREFIX', 'horizon:'), + + /* + |-------------------------------------------------------------------------- + | Horizon Route Middleware + |-------------------------------------------------------------------------- + | + | These middleware will get attached onto each Horizon route, giving you + | the chance to add your own middleware to this list or change any of + | the existing middleware. Or, you can simply stick with this list. + | + */ + + 'middleware' => ['web'], + + /* + |-------------------------------------------------------------------------- + | Queue Wait Time Thresholds + |-------------------------------------------------------------------------- + | + | This option allows you to configure when the LongWaitDetected event + | will be fired. Every connection / queue combination may have its + | own, unique threshold (in seconds) before this event is fired. + | + */ + + 'waits' => [ + 'redis:default' => 60, + ], + + /* + |-------------------------------------------------------------------------- + | Job Trimming Times + |-------------------------------------------------------------------------- + | + | Here you can configure for how long (in minutes) you desire Horizon to + | persist the recent and failed jobs. Typically, recent jobs are kept + | for one hour while all failed jobs are stored for an entire week. + | + */ + + 'trim' => [ + 'recent' => 60, + 'failed' => 10080, + 'monitored' => 10080, + ], + + /* + |-------------------------------------------------------------------------- + | Fast Termination + |-------------------------------------------------------------------------- + | + | When this option is enabled, Horizon's "terminate" command will not + | wait on all of the workers to terminate unless the --wait option + | is provided. Fast termination can shorten deployment delay by + | allowing a new instance of Horizon to start while the last + | instance will continue to terminate each of its workers. + | + */ + + 'fast_termination' => false, + + /* + |-------------------------------------------------------------------------- + | Memory Limit (MB) + |-------------------------------------------------------------------------- + | + | This value describes the maximum amount of memory the Horizon worker + | may consume before it is terminated and restarted. You should set + | this value according to the resources available to your server. + | + */ + + 'memory_limit' => 64, + + /* + |-------------------------------------------------------------------------- + | Queue Worker Configuration + |-------------------------------------------------------------------------- + | + | Here you may define the queue worker settings used by your application + | in all environments. These supervisors and settings handle all your + | queued jobs and will be provisioned by Horizon during deployment. + | + */ + + 'environments' => [ + 'production' => [ + 'supervisor-1' => [ + 'connection' => 'redis', + 'queue' => ['default'], + 'balance' => 'simple', + 'processes' => 10, + 'tries' => 3, + ], + ], + + 'local' => [ + 'supervisor-1' => [ + 'connection' => 'redis', + 'queue' => ['default'], + 'balance' => 'simple', + 'processes' => 3, + 'tries' => 3, + ], + ], + ], +]; diff --git a/config/ide-helper.php b/config/ide-helper.php new file mode 100644 index 000000000..33f230d3f --- /dev/null +++ b/config/ide-helper.php @@ -0,0 +1,353 @@ + '_ide_helper.php', + + /* + |-------------------------------------------------------------------------- + | Models filename + |-------------------------------------------------------------------------- + | + | The default filename for the models helper file. + | + */ + + 'models_filename' => '_ide_helper_models.php', + + /* + |-------------------------------------------------------------------------- + | PhpStorm meta filename + |-------------------------------------------------------------------------- + | + | PhpStorm also supports the directory `.phpstorm.meta.php/` with arbitrary + | files in it, should you need additional files for your project; e.g. + | `.phpstorm.meta.php/laravel_ide_Helper.php'. + | + */ + 'meta_filename' => '.phpstorm.meta.php', + + /* + |-------------------------------------------------------------------------- + | Fluent helpers + |-------------------------------------------------------------------------- + | + | Set to true to generate commonly used Fluent methods. + | + */ + + 'include_fluent' => false, + + /* + |-------------------------------------------------------------------------- + | Factory builders + |-------------------------------------------------------------------------- + | + | Set to true to generate factory generators for better factory() + | method auto-completion. + | + | Deprecated for Laravel 8 or latest. + | + */ + + 'include_factory_builders' => false, + + /* + |-------------------------------------------------------------------------- + | Write model magic methods + |-------------------------------------------------------------------------- + | + | Set to false to disable write magic methods of model. + | + */ + + 'write_model_magic_where' => true, + + /* + |-------------------------------------------------------------------------- + | Write model external Eloquent builder methods + |-------------------------------------------------------------------------- + | + | Set to false to disable write external Eloquent builder methods. + | + */ + + 'write_model_external_builder_methods' => true, + + /* + |-------------------------------------------------------------------------- + | Write model relation count properties + |-------------------------------------------------------------------------- + | + | Set to false to disable writing of relation count properties to model DocBlocks. + | + */ + + 'write_model_relation_count_properties' => true, + + /* + |-------------------------------------------------------------------------- + | Write Eloquent model mixins + |-------------------------------------------------------------------------- + | + | This will add the necessary DocBlock mixins to the model class + | contained in the Laravel framework. This helps the IDE with + | auto-completion. + | + | Please be aware that this setting changes a file within the /vendor directory. + | + */ + + 'write_eloquent_model_mixins' => false, + + /* + |-------------------------------------------------------------------------- + | 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' => true, + + 'helper_files' => [ + base_path() . '/vendor/laravel/framework/src/Illuminate/Support/helpers.php', + base_path() . '/vendor/laravel/framework/src/Illuminate/Foundation/helpers.php', + ], + + /* + |-------------------------------------------------------------------------- + | Model locations to include + |-------------------------------------------------------------------------- + | + | Define in which directories the ide-helper:models command should look + | for models. + | + | glob patterns are supported to easier reach models in sub-directories, + | e.g. `app/Services/* /Models` (without the space). + | + */ + + 'model_locations' => [ + 'app/Models', + 'app/Models/Aggregators', + ], + + /* + |-------------------------------------------------------------------------- + | Models to ignore + |-------------------------------------------------------------------------- + | + | Define which models should be ignored. + | + */ + + 'ignored_models' => [ + // App\MyModel::class, + ], + + /* + |-------------------------------------------------------------------------- + | Models hooks + |-------------------------------------------------------------------------- + | + | Define which hook classes you want to run for models to add custom information. + | + | Hooks should implement Barryvdh\LaravelIdeHelper\Contracts\ModelHookInterface. + | + */ + + 'model_hooks' => [ + // App\Support\IdeHelper\MyModelHook::class + ], + + /* + |-------------------------------------------------------------------------- + | 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' => [], + + /* + |-------------------------------------------------------------------------- + | Interface implementations + |-------------------------------------------------------------------------- + | + | These interfaces will be replaced with the implementing class. Some interfaces + | are detected by the helpers, others can be listed below. + | + */ + + 'interfaces' => [ + // App\MyInterface::class => App\MyImplementation::class, + ], + + /* + |-------------------------------------------------------------------------- + | 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 \Illuminate\Support\Carbon $created_at + | * @property \Illuminate\Support\Carbon $updated_at + | + | With this enabled, the properties will be this: + | + | * @property \Illuminate\Support\Carbon $createdAt + | * @property \Illuminate\Support\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', + ], + + /* + |-------------------------------------------------------------------------- + | Include DocBlocks from classes + |-------------------------------------------------------------------------- + | + | Include DocBlocks from classes to allow additional code inspection for + | magic methods and properties. + | + */ + 'include_class_docblocks' => false, + + /* + |-------------------------------------------------------------------------- + | Force FQN usage + |-------------------------------------------------------------------------- + | + | Use the fully qualified (class) name in DocBlocks, + | even if the class exists in the same namespace + | or there is an import (use className) of the class. + | + */ + 'force_fqn' => false, + + /* + |-------------------------------------------------------------------------- + | Use generics syntax + |-------------------------------------------------------------------------- + | + | Use generics syntax within DocBlocks, + | e.g. `Collection` instead of `Collection|User[]`. + | + */ + 'use_generics_annotations' => true, + + /* + |-------------------------------------------------------------------------- + | Additional relation types + |-------------------------------------------------------------------------- + | + | Sometimes it's needed to create custom relation types. The key of the array + | is the relationship method name. The value of the array is the fully-qualified + | class name of the relationship, e.g. `'relationName' => RelationShipClass::class`. + | + */ + 'additional_relation_types' => [], + + /* + |-------------------------------------------------------------------------- + | Additional relation return types + |-------------------------------------------------------------------------- + | + | When using custom relation types its possible for the class name to not contain + | the proper return type of the relation. The key of the array is the relationship + | method name. The value of the array is the return type of the relation ('many' + | or 'morphTo'). + | e.g. `'relationName' => 'many'`. + | + */ + 'additional_relation_return_types' => [], + + /* + |-------------------------------------------------------------------------- + | Enforce nullable Eloquent relationships on not null columns + |-------------------------------------------------------------------------- + | + | When set to true (default), this option enforces nullable Eloquent relationships. + | However, in cases where the application logic ensures the presence of related + | records it may be desirable to set this option to false to avoid unwanted null warnings. + | + | Default: true + | A not null column with no foreign key constraint will have a "nullable" relationship. + | * @property int $not_null_column_with_no_foreign_key_constraint + | * @property-read BelongsToVariation|null $notNullColumnWithNoForeignKeyConstraint + | + | Option: false + | A not null column with no foreign key constraint will have a "not nullable" relationship. + | * @property int $not_null_column_with_no_foreign_key_constraint + | * @property-read BelongsToVariation $notNullColumnWithNoForeignKeyConstraint + | + */ + + 'enforce_nullable_relationships' => true, + + /* + |-------------------------------------------------------------------------- + | Run artisan commands after migrations to generate model helpers + |-------------------------------------------------------------------------- + | + | The specified commands should run after migrations are finished running. + | + */ + 'post_migrate' => [ + // 'ide-helper:models --nowrite', + ], + + /* + |-------------------------------------------------------------------------- + | Macroable Traits + |-------------------------------------------------------------------------- + | + | Define which traits should be considered capable of adding Macro. + | You can add any custom trait that behaves like the original Laravel one. + | + */ + 'macroable_traits' => [ + Filament\Support\Concerns\Macroable::class, + Spatie\Macroable\Macroable::class, + ], + +]; diff --git a/config/identity.php b/config/identity.php new file mode 100644 index 000000000..95326166d --- /dev/null +++ b/config/identity.php @@ -0,0 +1,67 @@ + env( 'IDENTITY_LEGALNAME', 'IXP' ), + + 'location' => [ + 'city' => env( 'IDENTITY_CITY', 'Cork' ), + 'country' => env( 'IDENTITY_COUNTRY', 'IE' ), + ], + + 'orgname' => env( 'IDENTITY_ORGNAME', 'IXP' ), + 'name' => env( 'IDENTITY_NAME', 'IXP' ), + 'email' => env( 'IDENTITY_EMAIL', 'ixp@example.com' ), + 'testemail' => env( 'IDENTITY_TESTEMAIL', 'ixp@example.com' ), + 'rsvpemail' => env( 'IDENTITY_RSVPEMAIL', 'ixp@example.com' ), + + 'watermark' => env( 'IDENTITY_WATERMARK', 'IXP Manager' ), + + 'support_email' => env( 'IDENTITY_SUPPORT_EMAIL', 'ixp@example.com' ), + 'support_phone' => env( 'IDENTITY_SUPPORT_PHONE', '+353 20 912 2000' ), + 'support_hours' => env( 'IDENTITY_SUPPORT_HOURS', \IXP\Models\Customer::NOC_HOURS_24x7 ), + + 'billing_email' => env( 'IDENTITY_BILLING_EMAIL', 'ixp@example.com' ), + 'billing_phone' => env( 'IDENTITY_BILLING_PHONE', '+353 20 912 2000' ), + 'billing_hours' => env( 'IDENTITY_BILLING_HOURS', \IXP\Models\Customer::NOC_HOURS_8x5 ), + + 'sitename' => env( 'IDENTITY_SITENAME', 'IXP Manager' ), + 'titlename' => env( 'IDENTITY_TITLENAME', env( 'IDENTITY_SITENAME', 'IXP Manager' ) ), + + 'corporate_url' => env( 'IDENTITY_CORPORATE_URL', 'https://www.example.com/' ), + 'url' => env( 'APP_URL', '' ), + 'biglogo' => env( 'IDENTITY_BIGLOGO', 'https://www.ixpmanager.org/images/logos/ixp-manager.png' ), + + 'vlans' => [ + 'default' => env( 'IDENTITY_DEFAULT_VLAN', 1 ), + ], + +]; diff --git a/config/image.php b/config/image.php new file mode 100644 index 000000000..b390ec668 --- /dev/null +++ b/config/image.php @@ -0,0 +1,41 @@ + 'gd' + +); diff --git a/config/ixp.php b/config/ixp.php new file mode 100644 index 000000000..6db3743bf --- /dev/null +++ b/config/ixp.php @@ -0,0 +1,203 @@ + [ + 'enabled' => env( 'IXP_RESELLER_ENABLED', false ), + + // If reseller mode enabled and this is set to true then super admin or customer itself + // can not add/change a resold customer's billing details. + 'no_billing' => env( 'IXP_RESELLER_RESOLD_BILLING', false ), + ], + + + /* ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; Specifies whether to display and enable control of AS112 functionality for customers + ;; + ;; See https://github.com/inex/IXP-Manager/wiki/AS112 + ;; */ + 'as112' => [ + 'ui_active' => env( 'IXP_AS112_UI_ACTIVE', false ), + ], + + + /* ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; Peering Manager + ;; + ;; See: https://github.com/inex/IXP-Manager/wiki/Peering-Manager + ;; + ;; The Peering Manager allows your members to send peering requests to other members + ;; that contain all the necessary peering details. + ;; + ;; For testing / experimentation you can enabled test mode below and, when enabled, all + ;; peering requests will be sent to the testemail. + ;; + ;; Normally, the peering manager adds a note to the peer's notes and sets a request last + ;; sent date when a peering request is sent. In test mode, this will not happen. + ;; If you want this to happen in test mode, set testnote and testdate to true below. + ;; + ;; */ + 'peering_manager' => [ + 'testmode' => env( 'PEERING_MANAGER_TESTMODE', false ), + 'testemail' => env( 'PEERING_MANAGER_TESTEMAIL', "user@example.com" ), + 'testnote' => env( 'PEERING_MANAGER_TESTNOTE', false ), + 'testdate' => env( 'PEERING_MANAGER_TESTDATE', false ), + ], + + + 'peering-matrix' => [ + + // Minimum user auth level for the peering matrix + // See https://docs.ixpmanager.org/latest/usage/users/#types-of-users + 'min-auth' => env( 'PEERING_MATRIX_MIN_AUTH', \IXP\Models\User::AUTH_PUBLIC ), + + // the number of days back we look is not a perfect science. generally, the bigger the interface / more + // traffic, the less likely we'll find bgp sessions recently via sflow sampling. + // 28 days seems good in practice but his can be changed in config/ixp.php + 'lookback_days' => env( 'PEERING_MATRIX_LOOKBACK_DAYS', 28 ), + ], + + + + /* ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; Route Server and IRRDB Filtering + ;; + ;; + ;; */ + + 'irrdb' => [ + 'bgpq3' => [ + 'path' => env( 'IXP_IRRDB_BGPQ3_PATH', false ), + ], + + // ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + // Minimum subnet sizes + // + // Used by the route server/collector/as112 templates and + // the bgpq3 command line. + // Templates: + // - resources/views/api/v4/router/as112/bird/header.foil.php + // - resources/views/api/v4/router/collector/bird/header.foil.php + // - resources/views/api/v4/router/server/bird/header.foil.php + // + 'min_v4_subnet_size' => env( 'IXP_IRRDB_MIN_V4_SUBNET_SIZE', 24 ), + 'min_v6_subnet_size' => env( 'IXP_IRRDB_MIN_V6_SUBNET_SIZE', 48 ), + + ], + + + 'exec' => [ + + // printf style string: + 'ping4' => env( 'IXP_EXEC_PING4', "ping -4 -i 0.01 -w 2 -c 5 %s" ), + 'ping6' => env( 'IXP_EXEC_PING6', "ping -6 -i 0.01 -w 2 -c 5 %s" ), + + // arping is indexed on vlan.id so interfaces can be set appropriately + // prepopulating some entries for ease but will need to find an alternate way + // to handle this + 'arping' => [ + 1 => env( 'IXP_EXEC_ARPING_1', false ), + 2 => env( 'IXP_EXEC_ARPING_2', false ), + 3 => env( 'IXP_EXEC_ARPING_3', false ), + 4 => env( 'IXP_EXEC_ARPING_4', false ), + 5 => env( 'IXP_EXEC_ARPING_5', false ), + 6 => env( 'IXP_EXEC_ARPING_6', false ), + 7 => env( 'IXP_EXEC_ARPING_7', false ), + 8 => env( 'IXP_EXEC_ARPING_8', false ), + 9 => env( 'IXP_EXEC_ARPING_9', false ), + 10 => env( 'IXP_EXEC_ARPING_10', false ), + ], + ], + + + 'rpki' => [ + 'rtr1' => [ + 'host' => env( 'IXP_RPKI_RTR1_HOST', false ), + 'port' => env( 'IXP_RPKI_RTR1_PORT', '3323' ), + ], + 'rtr2' => [ + 'host' => env( 'IXP_RPKI_RTR2_HOST', false ), + 'port' => env( 'IXP_RPKI_RTR2_PORT', false ), + ], + ], + + + // Filter known transit networks + // Inspired by: http://bgpfilterguide.nlnog.net/guides/no_transit_leaks/ + // Overrides: + 'no_transit_asns' => [ + 'override' => call_user_func( function() { + $env = env( 'IXP_NO_TRANSIT_ASNS_OVERRIDE', false ); + + if( $env === false ) { + return false; + } + + if( !$env ) { + return []; + } + + return explode( ',', $env ); + }), + + 'exclude' => explode( ',', env( 'IXP_NO_TRANSIT_ASNS_EXCLUDE', '' ) ), + ], + + + // + 'snmp' => [ + + // See https://docs.ixpmanager.org/latest/usage/switches/#snmp-and-port-types-iftype + // Do not edit this file - set a .env variable of types to override the default. + // The default is equivalent to: + // IXP_SNMP_ALLOWED_INTERFACE_TYPES="6,135,136" + 'allowed_interface_types' => call_user_func( function() { + $env = env( 'IXP_SNMP_ALLOWED_INTERFACE_TYPES', null ); + + if( $env ) { + return explode( ',', $env ); + } + + return [ + // \OSS_SNMP\MIBS\Iface::IF_TYPE_OTHER, // 1 - e.g. Juniper irb.0 + \OSS_SNMP\MIBS\Iface::IF_TYPE_ETHERNETCSMACD, // 6 + \OSS_SNMP\MIBS\Iface::IF_TYPE_L2VLAN, // 135 + \OSS_SNMP\MIBS\Iface::IF_TYPE_L3IPVLAN, // 136 + // \OSS_SNMP\MIBS\Iface::IF_TYPE_IEEE8023ADLAG, // e.g. Juniper ae0.23 + ]; + }), + ], + + +]; diff --git a/config/ixp_api.php b/config/ixp_api.php new file mode 100644 index 000000000..ef93861af --- /dev/null +++ b/config/ixp_api.php @@ -0,0 +1,154 @@ + [ + + // if false, an API key is required + 'public' => env( 'IXP_API_JSONEXPORTSCHEMA_PUBLIC', true ), + + // or - we can set a static key here if we like: + 'access_key' => env( 'IXP_API_JSONEXPORTSCHEMA_ACCESS_KEY', false ), + + // some IXs want to exclude some information: + 'excludes' => [ + 'rfc5398' => env( 'IXP_API_JSONEXPORTSCHEMA_EXCLUDE_RFC5398', true ), + 'rfc6996' => env( 'IXP_API_JSONEXPORTSCHEMA_EXCLUDE_RFC6996', true ), + 'tags' => env( 'IXP_API_JSONEXPORTSCHEMA_EXCLUDE_TAGS', false ), + 'asnum' => env( 'IXP_API_JSONEXPORTSCHEMA_EXCLUDE_ASNUM', false ), + 'switch' => env( 'IXP_API_JSONEXPORTSCHEMA_EXCLUDE_SWITCH', false ), + 'ixp' => env( 'IXP_API_JSONEXPORTSCHEMA_EXCLUDE_IXP', false ), + 'member' => env( 'IXP_API_JSONEXPORTSCHEMA_EXCLUDE_MEMBER', false ), + 'intinfo' => env( 'IXP_API_JSONEXPORTSCHEMA_EXCLUDE_INTINFO', false ), + ], + ], + + /* + |-------------------------------------------------------------------------- + | RIR details for updating RIR AS/AS-SET objects + |-------------------------------------------------------------------------- + | + */ + 'rir' => [ + 'password' => env( 'IXP_API_RIR_PASSWORD', 'xxxxx' ), + 'email' => [ + 'from' => env( 'IXP_API_RIR_EMAIL_FROM', null ), + 'to' => env( 'IXP_API_RIR_EMAIL_TO', null ), + ], + ], + + + /* + |-------------------------------------------------------------------------- + | Nagios Configuration Generation + |-------------------------------------------------------------------------- + | + */ + 'nagios' => [ + 'birdseye_check' => env( 'IXP_API_NAGIOS_BIRDSEYE_CHECK', + "/usr/local/nagios-plugins-other/nagios-check-birdseye.php" ), + 'birdseye_bgp_session_check' => env( 'IXP_API_NAGIOS_BIRDSEYE_BGP_SESSION_CHECK', + "/usr/local/nagios-plugins-other/nagios-check-birdseye-bgp-sessions.php" ), + + 'infra_host' => env( 'IXP_API_NAGIOS_INFRA_HOST', "generic-host" ), + 'customer_host' => env( 'IXP_API_NAGIOS_CUSTOMER_HOST', "generic-host" ), + 'infra_service' => env( 'IXP_API_NAGIOS_INFRA_SERVICE', "generic-service" ), + 'customer_service' => env( 'IXP_API_NAGIOS_CUSTOMER_SERVICE', "generic-service" ), + ], + + /* + |-------------------------------------------------------------------------- + | PEERING DB + |-------------------------------------------------------------------------- + | + */ + 'peeringDB' => [ + 'username' => env( 'IXP_API_PEERING_DB_USERNAME', null ), + 'password' => env( 'IXP_API_PEERING_DB_PASSWORD', null ), + 'api-key' => env( 'IXP_API_PEERING_DB_API_KEY', null ), + // you should not need to change this. The %s is either "$un:$pw@" or an empty string + 'url' => env( 'IXP_API_PEERING_DB_URL', "https://%swww.peeringdb.com/api" ), + + 'fac_api' => env( 'IXP_API_PEERING_DB_FAC_URL', "https://api.peeringdb.com/api/fac" ), + 'ixp_api' => env( 'IXP_API_PEERING_DB_IXP_URL', "https://api.peeringdb.com/api/ix" ), + 'ixp_www' => env( 'IXP_WWW_PEERING_DB_IXP_URL', "https://www.peeringdb.com/ix" ), + + 'api_cache_ttl' => 3600, // how long to cache /ix results + ], + + /* + |-------------------------------------------------------------------------- + | IXP DB + |-------------------------------------------------------------------------- + | + */ + 'IXPDB' => [ + 'ixp_api' => env( 'IXP_API_IXPDB_IXP_URL', "https://api.ixpdb.net/v1/provider/list" ), + 'ixp_www' => env( 'IXP_WWW_IXPDB_IXP_URL', "https://ixpdb.euro-ix.net/en/ixpdb/ixp" ), + ], + + /* + |-------------------------------------------------------------------------- + | Whois servers + |-------------------------------------------------------------------------- + | + */ + + 'whois' => [ + // all responses cached for: + 'cache_ttl' => env( 'IXP_API_WHOIS_CACHE_TTL', 60 * 60 * 12 ), + + + 'asn' => [ + 'host' => env( 'IXP_API_WHOIS_ASN_HOST', 'whois.peeringdb.com' ), + 'port' => env( 'IXP_API_WHOIS_ASN_PORT', 43 ), + ], + + 'asn2' => [ + 'host' => env( 'IXP_API_WHOIS_ASN2_HOST', 'whois.cymru.com' ), + 'port' => env( 'IXP_API_WHOIS_ASN2_PORT', 43 ), + ], + + 'prefix' => [ + 'host' => env( 'IXP_API_WHOIS_PREFIX_HOST', 'whois.radb.net' ), + 'port' => env( 'IXP_API_WHOIS_PREFIX_PORT', 43 ), + ], + ], + + /* + |-------------------------------------------------------------------------- + | Ripe Atlas + |-------------------------------------------------------------------------- + | + */ + 'atlas_measurement_key' => env( 'ATLAS_MEASUREMENT_KEY', '' ), +]; diff --git a/config/ixp_fe.php b/config/ixp_fe.php new file mode 100644 index 000000000..35877ce4c --- /dev/null +++ b/config/ixp_fe.php @@ -0,0 +1,275 @@ + [ + // https://github.com/inex/IXP-Manager/wiki/Skinning : + 'smarty' => env( 'VIEW_SMARTY_SKIN', "" ), + ], + + /* + |-------------------------------------------------------------------------- + | Customer Name Format + |-------------------------------------------------------------------------- + | + | How to display customer names on the front end. Used /most/ places we + | reference a customer / member by name. + | + | The following substitutions are made: + | - %n - the customer name ($cust->getName()) + | - %a - the customer abbreviated name ($cust->getAbbreviatedName()) + | - %s - the customer shortname ($cust->getShortname()) + | - %i - the customer's ASN ($cust->getAutsys()): "XXX" + | - %j - the customer's ASN ($cust->getAutsys()): "[ASXXX]" + | - %k - the customer's ASN ($cust->getAutsys()): "ASXXX - " + | - %l - the customer's ASN ($cust->getAutsys()): " - ASXXX" + | + | The %j,k,l options exist so that the extra characters can be excluded if the customer does not have an ASN (e.g. Associate member) + */ + 'customer_name_format' => env( 'IXP_FE_CUSTOMER_NAME_FORMAT', "%a (AS%i)" ), + + /* + * Customer or Member? + */ + 'lang' => [ + 'customer' => [ + 'one' => env( 'IXP_FE_FRONTEND_CUSTOMER_ONE', 'member' ), + 'many' => env( 'IXP_FE_FRONTEND_CUSTOMER_MANY', 'members' ), + 'owner' => env( 'IXP_FE_FRONTEND_CUSTOMER_OWNER', "member's" ), + 'owners' => env( 'IXP_FE_FRONTEND_CUSTOMER_OWNERS', "members'" ), + ], + ], + + /* + |-------------------------------------------------------------------------- + | Front end components (Zend Framework Controllers) + |-------------------------------------------------------------------------- + | + | Any ZF1 controller extending IXP_Controller_FrontEnd can be disabled by setting it to true below + | + | frontend.disabled.XXX = true + | + | e.g. + | frontend.disabled.cust-kit = true + | frontend.disabled.console-server-connection = true + */ + 'frontend' => [ + 'disabled' => [ + 'console-server-connection' => env( 'IXP_FE_FRONTEND_DISABLED_CONSOLE', false ), + 'cust-kit' => env( 'IXP_FE_FRONTEND_DISABLED_CUSTKIT', false ), + 'docstore' => env( 'IXP_FE_FRONTEND_DISABLED_DOCSTORE', false ), + 'docstore_customer' => env( 'IXP_FE_FRONTEND_DISABLED_DOCSTORE_CUSTOMER', false ), + 'filtered-prefixes' => env( 'IXP_FE_FRONTEND_DISABLED_FILTERED_PREFIXES', true ), + 'logs' => env( 'IXP_FE_FRONTEND_DISABLED_LOGS', false ), + 'logo' => env( 'IXP_FE_FRONTEND_DISABLED_LOGO', true ), + 'lg' => env( 'IXP_FE_FRONTEND_DISABLED_LOOKING_GLASS', true ), + 'peering-manager' => env( 'IXP_FE_FRONTEND_DISABLED_PEERING_MANAGER', false ), + 'peering-matrix' => env( 'IXP_FE_FRONTEND_DISABLED_PEERING_MATRIX', false ), + 'phpinfo' => env( 'IXP_FE_FRONTEND_DISABLED_PHPINFO', true ), + 'ripe-atlas' => true, // not ready for use yet + 'rs-prefixes' => env( 'IXP_FE_FRONTEND_DISABLED_RS_PREFIXES', true ), + 'rs-filters' => env( 'IXP_FE_FRONTEND_DISABLED_RS_FILTERS', true ), + 'settings' => env( 'IXP_FE_FRONTEND_DISABLED_SETTINGS', false ), + ], + ], + + /* + |-------------------------------------------------------------------------- + | Route Server Prefixes Access permissions + |-------------------------------------------------------------------------- + | + | Generally speaking, the filtering of route server prefixes is visible + | on looking glasses / via comparison to route collector / etc. + | + | However, some IXs may wish to restrict access to the Route Server Prefix + | Analysis Tool. + | + | The following options apply: + | + | 1. \IXP\Models\User::AUTH_PUBLIC -> tool is publicly available to all + | 2. \IXP\Models\User::AUTH_CUSTUSER -> tool is available to any logged in user + | 3. \IXP\Models\User::AUTH_SUPERUSER -> summary and any customer access is restricted to superadmins, + | logged in users may see their prefixes. + */ + 'rs-prefixes' => [ + 'access' => env( 'IXP_FE_RS_PREFIXES_ACCESS', \IXP\Models\User::AUTH_SUPERUSER ), + ], + + + + /* + |-------------------------------------------------------------------------- + | Route Server Filters + |-------------------------------------------------------------------------- + | + */ + 'rs-filters' => [ + 'ttl' => env( 'IXP_FE_RS_FILTERS_TIME_TO_LIVE', 'There is no information available as to how often the route servers are updated.' ), + ], + + + /* + |-------------------------------------------------------------------------- + | Customer Controller Options + |-------------------------------------------------------------------------- + | + | + */ + 'customer' => [ + 'form' => [ + 'placeholders' => [ + // sample Irish number reserved for dramatic use + 'phone' => env( 'IXP_FE_CUSTOMER_FORM_PLACEHOLDER_PHONE', '+353 20 910 1234' ), + ] + ], + + // Billing updates notifications + // + // Send email with updated billing details to the following address when billing details + // are updated by an admin or a user. + // + 'billing_updates_notify' => env( 'IXP_FE_CUSTOMER_BILLING_UPDATES_NOTIFY', false ), + + // customer notes - see: https://docs.ixpmanager.org/latest/usage/notes/ + // + // Admin users can opt to get notified when a customer note is added / edited / deleted. + // For testing and demonstration purposes, this can be disabled and all updates + // for all actions can be sent to a single address defined here: + 'notes' => [ + 'only_send_to' => env( 'IXP_FE_CUSTOMER_NOTES_ONLYSENDTO', false ), + ], + + + // public member list and details are shown by default: + 'details_public' => env( 'IXP_FE_CUSTOMER_DETAILS_PUBLIC', true ), + ], + + /* + |-------------------------------------------------------------------------- + | Customer Ability to Change Own MAC Addresses + |-------------------------------------------------------------------------- + | + */ + 'layer2-addresses' => [ + 'customer_can_edit' => env( 'IXP_FE_LAYER2_ADDRESSES_CUST_CAN_EDIT', false ), + + 'customer_params' => [ + 'min_addresses' => env( 'IXP_FE_LAYER2_ADDRESSES_CUST_PARAMS_MIN_ADDRESSES', 1 ), + 'max_addresses' => env( 'IXP_FE_LAYER2_ADDRESSES_CUST_PARAMS_MAX_ADDRESSES', 2 ), + ], + + 'email_on_superuser_change' => env( 'IXP_FE_LAYER2_ADDRESSES_EMAIL_ON_SUPERUSER_CHANGE', false ), + 'email_on_customer_change' => env( 'IXP_FE_LAYER2_ADDRESSES_EMAIL_ON_CUSTOMER_CHANGE', false ), + 'email_on_change_dest' => env( 'IXP_FE_LAYER2_ADDRESSES_EMAIL_ON_CHANGE_DEST', null ), // e.g. 'ops@ixp.example.net' + ], + + /* + |-------------------------------------------------------------------------- + | Admins Dashboard + |-------------------------------------------------------------------------- + | + */ + 'admin_dashboard' => [ + 'default_graph_period' => env( 'IXP_FE_ADMIN_DASHBOARD_DEFAULT_GRAPH_PERIOD', 'week' ), + ], + + /* + |-------------------------------------------------------------------------- + | User Edit + |-------------------------------------------------------------------------- + | + */ + 'user' => [ + 'hide_mobile' => env( 'IXP_FE_USER_HIDE_MOBILE', false ), + ], + + + /* + |-------------------------------------------------------------------------- + | Login History + |-------------------------------------------------------------------------- + | + */ + 'login_history' => [ + 'enabled' => env( 'IXP_FE_LOGIN_HISTORY_ENABLED', true ), + ], + + /* + |-------------------------------------------------------------------------- + | API Keys + |-------------------------------------------------------------------------- + | + */ + 'api_keys' => [ + + // when an API key is created it is only shown once in the UI and there after it is hidden. + // set IXP_FE_API_KEYS_SHOW=true in .env to show the keys + 'show_keys' => env( 'IXP_FE_API_KEYS_SHOW', false ), + + // maximum API keys per user + 'max_keys' => env( 'IXP_FE_API_KEYS_MAX', 10 ), + ], + + /* + |-------------------------------------------------------------------------- + | VLAN Interface + |-------------------------------------------------------------------------- + | + */ + 'vlaninterfaces' => [ + 'hostname_required' => env( 'IXP_FE_VLANINTERFACES_HOSTNAME_REQUIRED', true ), + ], + + /* + |-------------------------------------------------------------------------- + | IX-F Sources + |-------------------------------------------------------------------------- + | + */ + 'ixfsources' => [ + 'INEX LAN1' => [ 'url' => 'https://www.inex.ie/ixp/api/v4/member-export/ixf/1.0', 'ixid' => 1 ], + 'INEX LAN2' => [ 'url' => 'https://www.inex.ie/ixp/api/v4/member-export/ixf/1.0', 'ixid' => 2 ], + 'INEX LAN3' => [ 'url' => 'https://www.inex.ie/ixp/api/v4/member-export/ixf/1.0', 'ixid' => 3 ], + + 'LONAP' => [ 'url' => 'https://portal.lonap.net/api/v4/member-export/ixf/1.0', 'ixid' => 1 ], + + 'LINX LON1' => [ 'url' => 'https://portal.linx.net/members.json', 'ixid' => 0 ], + 'LINX LON2' => [ 'url' => 'https://portal.linx.net/members.json', 'ixid' => 1 ], + 'LINX Manchester' => [ 'url' => 'https://portal.linx.net/members.json', 'ixid' => 2 ], + 'LINX Scotland' => [ 'url' => 'https://portal.linx.net/members.json', 'ixid' => 3 ], + 'LINX NoVA' => [ 'url' => 'https://portal.linx.net/members.json', 'ixid' => 4 ], + 'LINX Wales' => [ 'url' => 'https://portal.linx.net/members.json', 'ixid' => 5 ], + 'LINX JED-IX' => [ 'url' => 'https://portal.linx.net/members.json', 'ixid' => 6 ], + 'LINX ManxIX' => [ 'url' => 'https://portal.linx.net/members.json', 'ixid' => 7 ], + + ], + + + +]; \ No newline at end of file diff --git a/config/ixp_fe_settings.php b/config/ixp_fe_settings.php new file mode 100644 index 000000000..4fe303ebb --- /dev/null +++ b/config/ixp_fe_settings.php @@ -0,0 +1,850 @@ + [ + + + /* + */ + 'frontend_controllers' => [ + 'title' => 'Features', + 'description' => "These are features or modules that can be enabled or disabled. Some are + disabled by default as they may require extra configuration settings. + Disabling a module will remove frontend elements such as menus and links also.", + + 'fields' => [ + + 'as112' => [ + 'config_key' => 'ixp.as112.ui_active', + 'dotenv_key' => 'IXP_AS112_UI_ACTIVE', + 'type' => 'radio', + 'invert' => false, + 'rules' => 'boolean', + 'name' => 'AS112 functionality', + 'docs_url' => 'https://docs.ixpmanager.org/latest/features/as112/', + 'help' => 'AS112 is a service which provides anycast reverse DNS lookup for several prefixes, + particularly rfc1918 space. If you are providing an AS112 service to your members, + this feature enables UI elements for that.', + ], + + 'console-server-connection' => [ + 'config_key' => 'ixp_fe.frontend.disabled.console-server-connection', + 'dotenv_key' => 'IXP_FE_FRONTEND_DISABLED_CONSOLE', + 'type' => 'radio', + 'invert' => true, + 'rules' => 'boolean', + 'name' => 'Console Server Connections', + 'docs_url' => 'https://docs.ixpmanager.org/latest/features/console-servers/', // can be null + 'help' => 'An IXP would typically have out of band access (for emergencies, firmware upgrades, + etc) to critical infrastructure devices by means of a console server. This + module allows you to record what equipment console server ports connect to. + For larger exchanges, a modern DCIM system such as + NetBox + is recrommended.', + ], + + 'cust-kit' => [ + 'config_key' => 'ixp_fe.frontend.disabled.cust-kit', + 'dotenv_key' => 'IXP_FE_FRONTEND_DISABLED_CUSTKIT', + 'type' => 'radio', + 'invert' => true, + 'rules' => 'boolean', + 'name' => 'Colocated Equipment', + 'docs_url' => null, + 'help' => 'If you provide equipment colocation services for members, the module will allow you to + maintain an inventory of this. For larger exchanges, a modern DCIM system such as + NetBox is recrommended.', + ], + + 'logs' => [ + 'config_key' => 'ixp_fe.frontend.disabled.logs', + 'dotenv_key' => 'IXP_FE_FRONTEND_DISABLED_LOGS', + 'type' => 'radio', + 'invert' => true, + 'rules' => 'boolean', + 'name' => 'Database Change Logging', + 'docs_url' => 'https://docs.ixpmanager.org/latest/usage/dblogs/', + 'help' => 'Database change logging for changes made via the UI.', + ], + + 'docstore_customer' => [ + 'config_key' => 'ixp_fe.frontend.disabled.docstore_customer', + 'dotenv_key' => 'IXP_FE_FRONTEND_DISABLED_DOCSTORE_CUSTOMER', + 'type' => 'radio', + 'invert' => true, + 'rules' => 'boolean', + 'name' => 'Customer Document Store', + 'docs_url' => 'https://docs.ixpmanager.org/latest/features/docstore/', + 'help' => 'A per-member document store which allows administrators to upload documents on a + per-member basis. These can be made visible to administrators only or also to + users assigned to that specific member. Example use cases for this are member + application forms / contracts, completed / signed port upgrade forms, etc.', + ], + + 'docstore' => [ + 'config_key' => 'ixp_fe.frontend.disabled.docstore', + 'dotenv_key' => 'IXP_FE_FRONTEND_DISABLED_DOCSTORE', + 'type' => 'radio', + 'invert' => true, + 'rules' => 'boolean', + 'name' => 'Document Store', + 'docs_url' => 'https://docs.ixpmanager.org/latest/features/docstore/', + 'help' => 'A general document store which allows administrators to make documents generally available + for specific user classes (public, customer user, customer admin, superadmin). Example + use cases for this are member upgrade forms, distribution of board or management minutes, etc.', + ], + + 'filtered-prefixes' => [ + 'config_key' => 'ixp_fe.frontend.disabled.filtered-prefixes', + 'dotenv_key' => 'IXP_FE_FRONTEND_DISABLED_FILTERED_PREFIXES', + 'type' => 'radio', + 'invert' => true, + 'rules' => 'boolean', + 'name' => 'Filtered Prefixes', + 'docs_url' => 'https://docs.ixpmanager.org/latest/features/route-servers/#displaying-filtered-prefixes', + 'help' => 'This feature provides member\'s a live view of member prefixes filtered on the IXP\'s route servers. + It requires that you are using IXP Manager\'s Bird v2 route server configuration and + have enabled the looking glass.' + ], + + 'lg' => [ + 'config_key' => 'ixp_fe.frontend.disabled.lg', + 'dotenv_key' => 'IXP_FE_FRONTEND_DISABLED_LOOKING_GLASS', + 'type' => 'radio', + 'invert' => true, + 'rules' => 'boolean', + 'name' => 'Looking Glass', + 'docs_url' => 'https://docs.ixpmanager.org/latest/features/looking-glass/', + 'help' => 'IXP Manager supports full looking glass features when using the Bird BGP daemon and + Bird\'s Eye (a simple secure micro service for querying Bird). This feature is an + required element of some other features such as the filtered prefixes.', + ], + + 'login_history' => [ + 'config_key' => 'ixp_fe.login_history.enabled', + 'dotenv_key' => 'IXP_FE_LOGIN_HISTORY_ENABLED', + 'type' => 'radio', + 'invert' => true, + 'rules' => 'boolean', + 'name' => "Login History", + 'help' => 'Record user logins and view it in the UI. Expunged after six months by default.', + ], + + + 'logo' => [ + 'config_key' => 'ixp_fe.frontend.disabled.logo', + 'dotenv_key' => 'IXP_FE_FRONTEND_DISABLED_LOGO', + 'type' => 'radio', + 'invert' => true, + 'rules' => 'boolean', + 'name' => 'Member Logos', + 'docs_url' => 'https://docs.ixpmanager.org/latest/usage/customers/#customer-logos', + 'help' => 'Allows customer users and administrators to upload and manage their organisation\'s logo.', + ], + + 'peering-manager' => [ + 'config_key' => 'ixp_fe.frontend.disabled.peering-manager', + 'dotenv_key' => 'IXP_FE_FRONTEND_DISABLED_PEERING_MANAGER', + 'type' => 'radio', + 'invert' => true, + 'rules' => 'boolean', + 'name' => 'Peering Manager', + 'docs_url' => 'https://docs.ixpmanager.org/latest/features/peering-manager/', + 'help' => 'The Peering Manager is a fantastic tool that allows your members to view, compose, + request and track their peering requests with other IXP members.', + ], + + 'peering-matrix' => [ + 'config_key' => 'ixp_fe.frontend.disabled.peering-matrix', + 'dotenv_key' => 'IXP_FE_FRONTEND_DISABLED_PEERING_MATRIX', + 'type' => 'radio', + 'invert' => true, + 'rules' => 'boolean', + 'name' => 'Peering Matrix', + 'docs_url' => 'https://docs.ixpmanager.org/latest/features/peering-matrix/', + 'help' => 'The peering matrix system builds up a list of who is peering with whom over your IXP. You + will need sflow running with a + BGP detector running.', + ], + + 'phpinfo' => [ + 'config_key' => 'ixp_fe.frontend.disabled.phpinfo', + 'dotenv_key' => 'IXP_FE_FRONTEND_DISABLED_PHPINFO', + 'type' => 'radio', + 'invert' => true, + 'rules' => 'boolean', + 'name' => 'PHP Info', + 'docs_url' => null, + 'help' => 'The PHP Info option under IXP Utilities on the left hand menu. This is available to + administrators only and shows the output of phpinfo().', + ], + + 'rs-prefixes' => [ + 'config_key' => 'ixp_fe.frontend.disabled.rs-prefixes', + 'dotenv_key' => 'IXP_FE_FRONTEND_DISABLED_RS_PREFIXES', + 'type' => 'radio', + 'invert' => true, + 'rules' => 'boolean', + 'name' => 'RS Prefixes', + 'docs_url' => null, + 'help' => '[DEPRECATED] Filtered Prefixes above should be used instead of this.', + ], + + 'rs-filters' => [ + 'config_key' => 'ixp_fe.frontend.disabled.rs-filters', + 'dotenv_key' => 'IXP_FE_FRONTEND_DISABLED_RS_FILTERS', + 'type' => 'radio', + 'invert' => true, + 'rules' => 'boolean', + 'name' => 'Route Server Filtering UI', + 'docs_url' => 'https://github.com/inex/IXP-Manager/releases/tag/v6.4.0', + 'help' => 'Community-based filtering is the standard way to allow route server participants at an IXP + to control their routing policy. This feature allows IXP members to configure route server filtering in a web-based UI.', + ], + + 'settings' => [ + 'config_key' => 'ixp_fe.frontend.disabled.settings', + 'dotenv_key' => 'IXP_FE_FRONTEND_DISABLED_SETTINGS', + 'type' => 'radio', + 'invert' => true, + 'rules' => 'boolean', + 'name' => 'Settings UI Interface', + 'docs_url' => 'https://docs.ixpmanager.org/latest/features/settings', + 'help' => 'A UI for managing settings in the .env configuration file.', + ], + + + ], + ], + + + 'identity' => [ + + 'title' => 'IXP Identity', + 'description' => 'IXP Identity Information.', + + 'fields' => [ + 'orgname' => [ + 'config_key' => 'identity.orgname', + 'dotenv_key' => 'IDENTITY_ORGNAME', + 'type' => 'text', + 'rules' => 'nullable|max:255', + 'name' => 'Organisation Name', + 'docs_url' => null, + 'help' => 'What your IXP is generally known as. E.g. INEX, LONAP, etc.', + ], + 'name' => [ + 'config_key' => 'identity.name', + 'dotenv_key' => 'IDENTITY_NAME', + 'type' => 'text', + 'rules' => 'nullable|max:255', + 'name' => 'Name', + 'docs_url' => null, + 'help' => 'What your IXP is generally known as. E.g. INEX, LONAP, etc.', + ], + 'sitename' => [ + 'config_key' => 'identity.sitename', + 'dotenv_key' => 'IDENTITY_SITENAME', + 'type' => 'text', + 'rules' => 'nullable|max:255', + 'name' => 'Site Name', + 'docs_url' => null, + 'help' => 'The name of this website. E.g. INEX IXP Manager', + ], + 'titlename' => [ + 'config_key' => 'identity.titlename', + 'dotenv_key' => 'IDENTITY_TITLENAME', + 'type' => 'text', + 'rules' => 'nullable|max:255', + 'name' => 'Site Title', + 'docs_url' => null, + 'help' => 'Your site/IXP name in the top left menu of IXP Manager.', + ], + 'legalname' => [ + 'config_key' => 'identity.legalname', + 'dotenv_key' => 'IDENTITY_LEGALNAME', + 'type' => 'text', + 'rules' => 'nullable|max:255', + 'name' => 'Legal Name', + 'docs_url' => null, + 'help' => 'The full legal name of the IXP.', + ], + 'location_city' => [ + 'config_key' => 'identity.location.city', + 'dotenv_key' => 'IDENTITY_CITY', + 'type' => 'text', + 'rules' => 'nullable|max:255', + 'name' => 'City', + 'docs_url' => null, + 'help' => 'This is a fall back location, typically only used if it cannot be inferred from your facility settings.', + ], + 'location_country' => [ + 'config_key' => 'identity.location.country', + 'dotenv_key' => 'IDENTITY_COUNTRY', + 'type' => 'select', + 'options' => [ 'type' => 'countries' ], // special option list for countries + 'rules' => 'nullable|size:2', + 'name' => 'Country', + 'docs_url' => null, + 'help' => 'This is a fall back location, typically only used if it cannot be inferred from your facility settings.', + ], + 'email' => [ + 'config_key' => 'identity.email', + 'dotenv_key' => 'IDENTITY_EMAIL', + 'type' => 'text', + 'rules' => 'nullable|max:255|email', + 'name' => 'Email Address', + 'docs_url' => null, + 'help' => 'Typically your support or other generic contact email address.', + ], + + + 'corporate_url' => [ + 'config_key' => 'identity.corporate_url', + 'dotenv_key' => 'IDENTITY_CORPORATE_URL', + 'type' => 'text', + 'rules' => 'nullable|max:255|url', + 'name' => 'Corporate URL', + 'docs_url' => null, + 'help' => 'E.g. https://www.example.com/', + ], + 'url' => [ + 'config_key' => 'identity.url', + 'dotenv_key' => 'APP_URL', + 'type' => 'text', + 'rules' => 'nullable|max:255|url', + 'name' => 'IXP Manager URL', + 'docs_url' => null, + 'help' => 'E.g. https://www.example.com/portal/', + ], + + + 'alerts_recipient_name' => [ + 'config_key' => 'mail.alerts_recipient.name', + 'dotenv_key' => 'IDENTITY_ALERTS_NAME', + 'type' => 'text', + 'rules' => 'nullable|max:255', + 'name' => 'Alert Recipient Name', + 'docs_url' => null, + 'help' => 'IXP Manager will need to send alert emails. This is the recipient name for these alerts. + E.g. MyIXP Ops Team', + ], + + 'alerts_recipient_address' => [ + 'config_key' => 'mail.alerts_recipient.address', + 'dotenv_key' => 'IDENTITY_ALERTS_EMAIL', + 'type' => 'nullable|text', + 'rules' => 'nullable|max:255|email', + 'name' => 'Alert Recipient Email Address', + 'docs_url' => null, + 'help' => 'IXP Manager will need to send alert emails. This is the recipient email for these alerts. + E.g. ops@example.com', + ], + +// 'testemail' => [ +// 'config_key' => 'identity.testemail', +// 'dotenv_key' => 'IDENTITY_TESTEMAIL', +// 'type' => 'text', +// 'rules' => 'nullable|max:255|email', +// 'name' => 'Test Email Address', +// 'docs_url' => null, +// 'help' => 'Used by the peering manager for testing when `ixp.peering_manager.testemail` is set to true.', +// ], + 'watermark' => [ + 'config_key' => 'identity.watermark', + 'dotenv_key' => 'IDENTITY_WATERMARK', + 'type' => 'text', + 'rules' => 'nullable|max:100', + 'name' => 'Watermark', + 'docs_url' => null, + 'help' => 'Printed on some graphes, etc. E.g., "INEX, Ireland"', + ], + 'support_email' => [ + 'config_key' => 'identity.support_email', + 'dotenv_key' => 'IDENTITY_SUPPORT_EMAIL', + 'type' => 'text', + 'rules' => 'nullable|max:255|email', + 'name' => 'Support Email Address', + 'docs_url' => null, + 'help' => 'Your support/operations email address.', + ], + 'support_phone' => [ + 'config_key' => 'identity.support_phone', + 'dotenv_key' => 'IDENTITY_SUPPORT_PHONE', + 'type' => 'text', + 'rules' => 'nullable|max:100', + 'name' => 'Support Phone Number', + 'docs_url' => null, + 'help' => 'Your support/operations phone number.', + ], + 'support_hours' => [ + 'config_key' => 'identity.support_hours', + 'dotenv_key' => 'IDENTITY_SUPPORT_HOURS', + 'type' => 'text', + 'rules' => 'nullable|max:100', + 'name' => 'Support Hours', + 'docs_url' => null, + 'help' => 'Hours that support is normally available. Standard industry nomenclature includes: 24/7, 8x5, 8x7, 12x5, 12x7', + ], + 'billing_email' => [ + 'config_key' => 'identity.billing_email', + 'dotenv_key' => 'IDENTITY_BILLING_EMAIL', + 'type' => 'text', + 'rules' => 'nullable|max:255|email', + 'name' => 'Billing Email Address', + 'docs_url' => null, + 'help' => 'Your billing/accounting contact email address.', + ], + 'billing_phone' => [ + 'config_key' => 'identity.billing_phone', + 'dotenv_key' => 'IDENTITY_BILLING_PHONE', + 'type' => 'text', + 'rules' => 'nullable|max:100', + 'name' => 'Billing Phone Number', + 'docs_url' => null, + 'help' => 'Your billing/accounting contact phone number.', + ], + 'billing_hours' => [ + 'config_key' => 'identity.billing_hours', + 'dotenv_key' => 'IDENTITY_BILLING_HOURS', + 'type' => 'text', + 'rules' => 'nullable|max:100', + 'name' => 'Billing Hours', + 'docs_url' => null, + 'help' => 'Hours that billing support is normally available. Standard industry nomenclature includes: 24/7, 8x5, 8x7, 12x5, 12x7', + ], + + 'biglogo' => [ + 'config_key' => 'identity.biglogo', + 'dotenv_key' => 'IDENTITY_BIGLOGO', + 'type' => 'text', + 'rules' => 'nullable', + 'name' => 'Big Logo', + 'docs_url' => null, + 'help' => 'URL of the logo to use on the login page. Can be https://... or file:///home/...', + ], + ], + + ], + + 'auth' => [ + + 'title' => 'Authentication', + 'description' => "Authentication related options.", + + 'fields' => [ + + '2fa_enabled' => [ + 'config_key' => 'google2fa.enabled', + 'dotenv_key' => '2FA_ENABLED', + 'type' => 'radio', + 'rules' => 'boolean', + 'name' => 'Two-factor Authentication (2fa) Enabled', + 'docs_url' => 'https://docs.ixpmanager.org/usage/authentication/#two-factor-authentication-2fa', + 'help' => 'If you disable this, 2fa will not be an option for securing users login, and any users with existing 2fa will not be required to confirm it on login.', + ], + + 'ixpm_2fa_enforce_for_users' => [ + + 'config_key' => 'google2fa.ixpm_2fa_enforce_for_users', + 'dotenv_key' => '2FA_IXPM_ENFORCE_FOR_USERS', + 'type' => 'select', + 'options' => [ 'type' => 'array', 'list' => \IXP\Models\User::$PRIVILEGES_TEXT + [ 4 => 'Do not enforce for any user' ] ], + 'name' => "Enforce 2FA for Users >=", + 'rules' => 'required|in:1,2,3,4', + 'docs_url' => 'https://docs.ixpmanager.org/usage/authentication/#enforcing-2fa-for-users', + 'help' => 'Chose between allowing users to opt into 2fa, or requiring all users with minimum selected previlege.', + ], + + 'secure_session_cookie' => [ + 'config_key' => 'session.secure', + 'dotenv_key' => 'SESSION_SECURE_COOKIE', + 'type' => 'radio', + 'rules' => 'boolean', + 'name' => 'Enforce secure session cookies', + 'help' => 'By setting this option, 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. Note that all cookies are also encrypted.', + ], + + 'peeringdb_oauth_enabled' => [ + 'config_key' => 'auth.peeringdb.enabled', + 'dotenv_key' => 'AUTH_PEERINGDB_ENABLED', + 'type' => 'radio', + 'rules' => 'boolean', + 'name' => 'PeeringDB OAuth Enabled', + 'docs_url' => 'https://docs.ixpmanager.org/latest/features/peeringdb-oauth/', + 'help' => 'Enable OAuth with PeeringDB. Note a number of additional settings below are required.', + ], + + 'peeringdb_oauth_client_id' => [ + 'config_key' => 'services.peeringdb_oauth_client_id', + 'dotenv_key' => 'PEERINGDB_OAUTH_CLIENT_ID', + 'type' => 'text', + 'rules' => 'nullable|max:255', + 'name' => 'PeeringDB OAuth Client ID', + 'docs_url' => 'https://docs.ixpmanager.org/latest/features/peeringdb-oauth/', + 'help' => 'PeeringDB OAuth client ID - you will receive this from PeeringDB', + ], + + 'peeringdb_oauth_client_secret' => [ + 'config_key' => 'services.peeringdb_oauth_client_secret', + 'dotenv_key' => 'PEERINGDB_OAUTH_CLIENT_SECRET', + 'type' => 'text', + 'rules' => 'nullable|max:255', + 'name' => 'PeeringDB OAuth Client Secret', + 'docs_url' => 'https://docs.ixpmanager.org/latest/features/peeringdb-oauth/', + 'help' => 'PeeringDB OAuth client secret - you will receive this from PeeringDB', + ], + + 'peeringdb_oauth_redirect' => [ + 'config_key' => 'services.peeringdb_oauth_redirect', + 'dotenv_key' => 'PEERINGDB_OAUTH_REDIRECT', + 'type' => 'text', + 'rules' => 'nullable|max:255|url', + 'name' => 'PeeringDB OAuth Redirect', + 'docs_url' => 'https://docs.ixpmanager.org/latest/features/peeringdb-oauth/', + 'help' => 'PeeringDB OAuth - post authentication redirect target on IXP Manager. Assuming your ' + . 'APP_URL is correct, then: ' . config('app.url') . '/auth/login/peeringdb/callback' + ], + + ], + + ], + + 'third_party' => [ + + 'title' => '3rd Parties', + 'description' => "Configuration options for third party services.", + + 'fields' => [ + + 'peeringdb_api_key' => [ + + 'config_key' => 'ixp_api.peeringdb.api_key', + 'dotenv_key' => 'IXP_API_PEERING_DB_API_KEY', + 'type' => 'text', + 'rules' => 'nullable|max:255', + 'name' => "PeeringDB API Key", + 'docs_url' => 'https://docs.peeringdb.com/howto/api_keys/', + 'help' => "IXP Manager uses information from PeeringDB in a number of places. Setting an API + key is highly recommended so additional information can be accessed and so that + rate limited can be avoided.", + ], + + ], + ], + + 'ixf_export' => [ + + 'title' => 'IX-F Export', + 'description' => "Configuration options for the IX-F export.", + + 'fields' => [ + + 'ixf_public' => [ + 'config_key' => 'ixp_api.json_export_schema.public', + 'dotenv_key' => 'IXP_API_JSONEXPORTSCHEMA_PUBLIC', + 'type' => 'radio', + 'rules' => 'boolean', + 'name' => 'IX-F Export is Public', + 'docs_url' => 'https://docs.ixpmanager.org/latest/features/ixf-export/', + 'help' => 'We strongly advise you not to disable public access if you are a standard IXP. Remember, the ' + . 'public version is essentially the same list as you would provide on your standard website\'s list of members. ' + . 'If you disable this then only logged in users can access the IX-F export.', + ], + + 'ixf_access_key' => [ + 'config_key' => 'ixp_api.json_export_schema.access_key', + 'dotenv_key' => 'IXP_API_JSONEXPORTSCHEMA_ACCESS_KEY', + 'type' => 'text', + 'rules' => 'nullable|max:255', + 'name' => 'IX-F Export Access Key', + 'docs_url' => 'https://docs.ixpmanager.org/7.0/features/ixf-export/#configuration-options', + 'help' => 'If you disable public access, then you can set an access key (password) here to allow remote non-logged in access.', + ], + + 'ixf_excludes_rfc5398' => [ + 'config_key' => 'ixp_api.json_export_schema.excludes.rfc5398', + 'dotenv_key' => 'IXP_API_JSONEXPORTSCHEMA_EXCLUDE_RFC5398', + 'type' => 'radio', + 'rules' => 'boolean', + 'name' => 'IX-F Export - Exclude rfc5398 ASNs', + 'docs_url' => 'https://docs.ixpmanager.org/7.0/features/ixf-export/#excluding-members', + 'help' => 'Excludes members with documentation ASNs (64496 - 64511, 65536 - 65551)', + ], + + 'ixf_excludes_rfc6996' => [ + 'config_key' => 'ixp_api.json_export_schema.excludes.rfc6996', + 'dotenv_key' => 'IXP_API_JSONEXPORTSCHEMA_EXCLUDE_RFC6996', + 'type' => 'radio', + 'rules' => 'boolean', + 'name' => 'IX-F Export - Exclude rfc6996 ASNs', + 'docs_url' => 'https://docs.ixpmanager.org/7.0/features/ixf-export/#excluding-members', + 'help' => 'Excludes members with private ASNs (64512 - 65534, 4200000000 - 4294967294)', + ], + + 'ixf_excludes_tags' => [ + 'config_key' => 'ixp_api.json_export_schema.excludes.tags', + 'dotenv_key' => 'IXP_API_JSONEXPORTSCHEMA_EXCLUDE_TAGS', + 'type' => 'text', + 'rules' => 'nullable|max:1024', + 'name' => 'IX-F Export - Excludes Tags', + 'docs_url' => 'https://docs.ixpmanager.org/7.0/features/ixf-export/#excluding-members', + 'help' => 'You can exclude members by tag by setting this option. E.g. tag1|tag2', + ], + + 'ixf_excludes_asnum' => [ + 'config_key' => 'ixp_api.json_export_schema.excludes.asnum', + 'dotenv_key' => 'IXP_API_JSONEXPORTSCHEMA_EXCLUDE_ASNUM', + 'type' => 'text', + 'rules' => 'nullable|max:1024', + 'name' => 'IX-F Export - Excludes ASNs', + 'docs_url' => 'https://docs.ixpmanager.org/7.0/features/ixf-export/#excluding-members', + 'help' => 'You can exclude members by ASN by setting this option. E.g. 64496|64497|...', + ], + + 'ixf_excludes_switch' => [ + 'config_key' => 'ixp_api.json_export_schema.excludes.switch', + 'dotenv_key' => 'IXP_API_JSONEXPORTSCHEMA_EXCLUDE_SWITCH', + 'type' => 'text', + 'rules' => 'nullable|max:1024', + 'name' => 'IX-F Export - Exclude Switch Info', + 'docs_url' => 'https://docs.ixpmanager.org/7.0/features/ixf-export/#excluding-some-data', + 'help' => 'If you need, e.g., to exclude the model and software version from switch information, you can set the following: model|software', + ], + + 'ixf_excludes_ixp' => [ + 'config_key' => 'ixp_api.json_export_schema.excludes.ixp', + 'dotenv_key' => 'IXP_API_JSONEXPORTSCHEMA_EXCLUDE_IXP', + 'type' => 'text', + 'rules' => 'nullable|max:1024', + 'name' => 'IX-F Export - Exclude IXP Info', + 'docs_url' => 'https://docs.ixpmanager.org/7.0/features/ixf-export/#excluding-some-data', + 'help' => 'If you need, e.g., to exclude some of your IXP information, you can set the following: name|url', + ], + + 'ixf_excludes_member' => [ + 'config_key' => 'ixp_api.json_export_schema.excludes.member', + 'dotenv_key' => 'IXP_API_JSONEXPORTSCHEMA_EXCLUDE_MEMBER', + 'type' => 'text', + 'rules' => 'nullable|max:1024', + 'name' => 'IX-F Export - Exclude Member Info', + 'docs_url' => 'https://docs.ixpmanager.org/7.0/features/ixf-export/#excluding-some-data', + 'help' => 'If you need, e.g., to exclude some member information, you can set the following: peering_policy|member_type', + ], + + 'ixf_excludes_intinfo' => [ + 'config_key' => 'ixp_api.json_export_schema.excludes.intinfo', + 'dotenv_key' => 'IXP_API_JSONEXPORTSCHEMA_EXCLUDE_INTINFO', + 'type' => 'text', + 'rules' => 'nullable|max:1024', + 'name' => 'IX-F Export - Exclude Interface Info', + 'docs_url' => 'https://docs.ixpmanager.org/7.0/features/ixf-export/#excluding-some-data', + 'help' => 'If you need, e.g., to exclude some of your members\' interface information, you can set the following: mac_addresses|routeserver', + ], + + ], + + ], + + 'admin_options' => [ + + 'title' => 'Admin', + 'description' => "Various administrator related options.", + + 'fields' => [ + + 'default_graph_period' => [ + + 'config_key' => 'ixp_fe.admin.default_graph_period', + 'dotenv_key' => 'IXP_FE_ADMIN_DASHBOARD_DEFAULT_GRAPH_PERIOD', + 'type' => 'select', + 'options' => [ 'type' => 'array', 'list' => array_filter( IXP\Services\Grapher\Graph::PERIODS, function( $k ) { + return $k !== IXP\Services\Grapher\Graph::PERIOD_CUSTOM; + }, ARRAY_FILTER_USE_KEY ) ], + 'rules' => "nullable|in:" . implode( ',', array_keys( IXP\Services\Grapher\Graph::PERIODS ) ), + 'name' => "Default Graph Period", + 'help' => 'Default graph period on the admin dashboard.', + ], + + + 'billing-updates-notification' => [ + + 'config_key' => 'ixp_fe.frontend.billing-updates.notification', + 'dotenv_key' => 'IXP_FE_BILLING_UPDATES', + 'type' => 'text', + 'rules' => 'nullable|max:255|email', + 'name' => 'Billing Updates Notification', + 'docs_url' => 'https://docs.ixpmanager.org/latest/usage/customers/#notification-of-billing-details-changed', + 'help' => "If a member edits their billing details in their portal, the changes can be emailed to + this address. If left blank, then no emails will be sent.", + ], + + // do we need more Grapher attributes here? + ], + ], + + 'route_servers' => [ + + 'title' => 'Route Servers', + 'description' => "Options for route servers. Changing any of these will affect production route servers! Proceed with caution. ", + + 'fields' => [ + + 'rs_min_v4_subnet_size' => [ + 'config_key' => 'ixp.irrdb.min_v4_subnet_size', + 'dotenv_key' => 'IXP_IRRDB_MIN_V4_SUBNET_SIZE', + 'type' => 'text', + 'rules' => 'integer|between:1,32', + 'name' => 'Minimum IPv4 Subnet Size', + 'docs_url' => 'https://docs.ixpmanager.org/7.0/features/route-servers/', + 'help' => 'Minimum ipv4 subnet size accepted by route servers. Should be no more specific than a /24.', + ], + + 'rs_min_v6_subnet_size' => [ + 'config_key' => 'ixp.irrdb.min_v6_subnet_size', + 'dotenv_key' => 'IXP_IRRDB_MIN_V6_SUBNET_SIZE', + 'type' => 'text', + 'rules' => 'integer|between:1,128', + 'name' => 'Minimum IPv6 Subnet Size', + 'docs_url' => 'https://docs.ixpmanager.org/7.0/features/route-servers/', + 'help' => 'Minimum ipv6 subnet size accepted by route servers. Should be no more specific than a /48.', + ], + + + 'rs-filters-ttl' => [ + // this via config() will give default value + 'config_key' => 'ixp_fe.frontend.rs-filters.ttl', + 'dotenv_key' => 'IXP_FE_RS_FILTERS_TIME_TO_LIVE', + 'type' => 'textarea', + 'rules' => 'nullable|max:1024', + 'name' => 'Route Server Update Period', + //'docs_url' => '', + 'help' => "If you have enabled the route server community filtering via UI option, then your members will + need to know how often you update their configurations. The text you enter here will be + displayed on the route server filters page.", + ], + + 'rs_rpki_rtr1_host' => [ + 'config_key' => 'ixp.rpki.rtr1.host', + 'dotenv_key' => 'IXP_RPKI_RTR1_HOST', + 'type' => 'text', + 'rules' => 'nullable|max:255', + 'name' => 'RPKI Validator #1 Host', + 'docs_url' => 'https://docs.ixpmanager.org/latest/features/rpki/', + 'help' => 'IP address of the first RPKI local cache / validator. This will be inserted into the generated route server configuration if RPKI is enabled.', + ], + + 'rs_rpki_rtr1_port' => [ + 'config_key' => 'ixp.rpki.rtr1.port', + 'dotenv_key' => 'IXP_RPKI_RTR1_PORT', + 'type' => 'text', + 'rules' => 'nullable|integer|between:1,65535', + 'name' => 'RPKI Validator #1 Port', + 'docs_url' => 'https://docs.ixpmanager.org/latest/features/rpki/', + 'help' => 'Port to connect to on the first RPKI local cache / validator. This will be inserted into the generated route server configuration if RPKI is enabled.', + ], + + 'rs_rpki_rtr2_host' => [ + 'config_key' => 'ixp.rpki.rtr2.host', + 'dotenv_key' => 'IXP_RPKI_RTR2_HOST', + 'type' => 'text', + 'rules' => 'nullable|max:255', + 'name' => 'RPKI Validator #2 Host', + 'docs_url' => 'https://docs.ixpmanager.org/latest/features/rpki/', + 'help' => 'IP address of the second RPKI local cache / validator. This will be inserted into the generated route server configuration if RPKI is enabled.', + ], + + 'rs_rpki_rtr2_port' => [ + 'config_key' => 'ixp.rpki.rtr2.port', + 'dotenv_key' => 'IXP_RPKI_RTR2_PORT', + 'type' => 'text', + 'rules' => 'nullable|integer|between:1,65535', + 'name' => 'RPKI Validator #2 Port', + 'docs_url' => 'https://docs.ixpmanager.org/latest/features/rpki/', + 'help' => 'Port to connect to on the second RPKI local cache / validator. This will be inserted into the generated route server configuration if RPKI is enabled.', + ], + + ], + ], + + + 'utilities' => [ + + 'title' => 'System Utilities', + 'description' => "Paths and options for system utilities.", + + 'fields' => [ + + 'bgpq3' => [ + 'config_key' => 'ixp.irrdb.bgpq3.path', + 'dotenv_key' => 'IXP_IRRDB_BGPQ3_PATH', + 'type' => 'text', + 'rules' => 'nullable|max:255', + 'name' => 'Path to bgpq3 utility', + 'docs_url' => 'https://docs.ixpmanager.org/latest/features/irrdb/', + 'help' => 'Full path to the bgpq3 utility.', + ], + +// 'ping4' => [ +// 'config_key' => 'ixp.exec.ping4', +// 'dotenv_key' => 'IXP_EXEC_PING4', +// 'type' => 'text', +// 'rules' => '', +// 'name' => 'Diagnostics - ipv4 ping', +// 'docs_url' => null, +// 'help' => 'ping command for ipv4 diagnostics. %s is the placeholder for the target IP.', +// ], +// +// 'ping6' => [ +// 'config_key' => 'ixp.exec.ping6', +// 'dotenv_key' => 'IXP_EXEC_PING6', +// 'type' => 'text', +// 'rules' => '', +// 'name' => 'Diagnostics - ipv6 ping', +// 'docs_url' => null, +// 'help' => 'ping command for ipv6 diagnostics. %s is the placeholder for the target IP.', +// ], + + + ], + + ], + + ], + + +]; + diff --git a/config/ixp_tools.php.dist b/config/ixp_tools.php.dist new file mode 100644 index 000000000..d6ab72870 --- /dev/null +++ b/config/ixp_tools.php.dist @@ -0,0 +1,95 @@ + [ + 0 => [ + 'name' => "Public Peering LAN #1", + // the vlan tag as entered in the 'number' column in the vlan table + 'number' => 100, + ], + 1 => [ + 'name' => "Public Peering LAN #2", + 'number' => 120 + ] + ], + + // the primary / only / main peering LAN (the 'number' column in the vlan table) + 'primary_peering_lan_vlan_tag' => 1, + + //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + // Billing updates notifications + // + // Send email with updated billing details to the following address when billing details + // are updated by an admin or a user. + // + 'billing_updates_notify' => "accounts1@example.com", + + //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + // + // Generated RIR objects + // + // See: https://github.com/inex/IXP-Manager/wiki/RIR-Objects + 'rir_ripe_password' => 'supersecret', + + //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + // + // We use PHP-Weathermap which is publicly available. Our true settings + // are commented out, please replace with your own + 'weathermap' => [ + 1 => [ + 'name' => "Weathermap for Peering LAN 1", + 'menu' => "Weathermap - LAN 1", + 'url' => "https://www.example.com/weathermap-lan1-plain.html", + 'width' => 900, + 'height' => 1200 + ], + // 2 => [ ... ] + ], + + //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + // + // Email alerts of exceptions can be particular useful for giving you a heads + // up of any issues rather than waiting for a customer to contact you. + // + // However, if your email system is out of band (i.e. not internal to your + // own network), beware that full exception details and traces are included + // and in some cases can leak sensitive information. + 'logger' => [ + 'email' => [ + 'from' => 'ixp-logger@example.com', + 'to' => 'ixp-notify-list@example.com', + 'prefix' => 'IXP_ERROR', + 'level' => 3 + ], + ], + +]; diff --git a/config/logging.php b/config/logging.php new file mode 100644 index 000000000..cc86f8e03 --- /dev/null +++ b/config/logging.php @@ -0,0 +1,157 @@ + env( 'LOG_CHANNEL', env( 'APP_LOG', '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' => env('LOG_DEPRECATIONS_TRACE', false), + ], + + /* + |-------------------------------------------------------------------------- + | Log Channels + |-------------------------------------------------------------------------- + | + | Here you may configure the log channels for your application. Out of + | the box, Laravel uses the Monolog PHP logging library. This gives + | you a variety of powerful log handlers / formatters to utilize. + | + | Available Drivers: "single", "daily", "slack", "syslog", + | "errorlog", "monolog", + | "custom", "stack" + | + */ + + 'channels' => [ + + + 'stack' => [ + 'driver' => 'stack', + 'channels' => explode(',', (string) env('LOG_STACK', 'single')), + 'ignore_exceptions' => false, + ], + + 'single' => [ + 'driver' => 'single', + 'path' => storage_path('logs/laravel.log'), + 'level' => env('LOG_LEVEL', 'debug'), + 'replace_placeholders' => true, + ], + + 'daily' => [ + 'driver' => 'daily', + 'path' => storage_path('logs/laravel.log'), + 'level' => env('LOG_LEVEL', 'debug'), + 'days' => env('LOG_DAILY_DAYS', 28), + 'replace_placeholders' => true, + ], + + 'slack' => [ + 'driver' => 'slack', + 'url' => env('LOG_SLACK_WEBHOOK_URL'), + 'username' => env('LOG_SLACK_USERNAME', 'Laravel Log'), + 'emoji' => env('LOG_SLACK_EMOJI', ':boom:'), + 'level' => env('LOG_LEVEL', 'critical'), + 'replace_placeholders' => true, + ], + + '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'), + ], + 'processors' => [PsrLogMessageProcessor::class], + ], + + 'stderr' => [ + 'driver' => 'monolog', + 'level' => env('LOG_LEVEL', 'debug'), + 'handler' => StreamHandler::class, + 'handler_with' => [ + 'stream' => 'php://stderr', + ], + 'formatter' => env('LOG_STDERR_FORMATTER'), + 'processors' => [PsrLogMessageProcessor::class], + ], + + 'syslog' => [ + 'driver' => 'syslog', + 'level' => env('LOG_LEVEL', 'debug'), + 'facility' => env('LOG_SYSLOG_FACILITY', LOG_USER), + 'replace_placeholders' => true, + ], + + 'errorlog' => [ + 'driver' => 'errorlog', + 'level' => env('LOG_LEVEL', 'debug'), + 'replace_placeholders' => true, + ], + + 'null' => [ + 'driver' => 'monolog', + 'handler' => NullHandler::class, + ], + + 'emergency' => [ + 'path' => storage_path('logs/laravel.log'), + ], + + ], + +]; diff --git a/config/mail.php b/config/mail.php new file mode 100644 index 000000000..40bc8b6be --- /dev/null +++ b/config/mail.php @@ -0,0 +1,146 @@ + env('MAIL_MAILER', 'smtp'), + + /* + |-------------------------------------------------------------------------- + | Mailer Configurations + |-------------------------------------------------------------------------- + | + | 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", + | "postmark", "log", "array", "failover" + | + */ + + 'mailers' => [ + 'smtp' => [ + 'transport' => 'smtp', + 'host' => env('MAIL_HOST', 'localhost'), + 'port' => env('MAIL_PORT', 25), + 'encryption' => env('MAIL_ENCRYPTION', false), + 'username' => env('MAIL_USERNAME'), + 'password' => env('MAIL_PASSWORD'), + 'timeout' => null, + 'local_domain' => env('MAIL_EHLO_DOMAIN', parse_url((string) env('APP_URL', 'http://localhost'), PHP_URL_HOST)), + ], + + 'ses' => [ + 'transport' => 'ses', + ], + + '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', + ], + ], + ], + + /* + |-------------------------------------------------------------------------- + | Global "From" Address + |-------------------------------------------------------------------------- + | + | You may wish for all e-mails sent by your application to be sent from + | the same address. Here, you may specify a name and address that is + | used globally for all e-mails that are sent by your application. + | + */ + + 'from' => [ + 'address' => env('IDENTITY_EMAIL', 'ixp-manager@example.com'), + 'name' => env('IDENTITY_NAME', 'Newly Installed IXP Manager'), + ], + + // IXP Manager will need to send alert emails. This is the recipient email for these alerts. + // E.g. ops@example.com + 'alerts_recipient' => [ + 'address' => env('IDENTITY_ALERTS_EMAIL',false ), + 'name' => env('IDENTITY_ALERTS_NAME','IXP Manager Alerts'), + ], + + /* + |-------------------------------------------------------------------------- + | Markdown Mail Settings + |-------------------------------------------------------------------------- + | + | If you are using Markdown based email rendering, you may configure your + | theme and component paths here, allowing you to customize the design + | of the emails. Or, you may simply stick with the Laravel defaults! + | + */ + + 'markdown' => [ + 'theme' => 'default', + + 'paths' => [ + resource_path('views/vendor/mail'), + ], + ], + +]; diff --git a/config/mailinglists.php.dist b/config/mailinglists.php.dist new file mode 100644 index 000000000..4d42898e1 --- /dev/null +++ b/config/mailinglists.php.dist @@ -0,0 +1,51 @@ + false, + + 'lists' => [ + 'xxx' => [ + 'name' => "xxx", + 'desc' => "xxx xxx xxx", + 'email' => "list@example.com", + 'archive' => "https://www.example.com/mailman/private/xxx/" + ], + ], + + 'mailman' => [ + 'cmds' => [ + 'list_members' => "/usr/local/mailman/bin/list_members", + 'add_members' => "/usr/local/mailman/bin/add_members -r - -w n -a n", + 'remove_members' => "/usr/local/mailman/bin/remove_members -f - -n -N", + 'changepw' => "/usr/local/mailman/bin/withlist -q -l -r changepw" + ] + ] +]; diff --git a/config/migrations.php b/config/migrations.php new file mode 100644 index 000000000..1c4d1ab9c --- /dev/null +++ b/config/migrations.php @@ -0,0 +1,68 @@ + 'migrations', + /* + |-------------------------------------------------------------------------- + | Migration Directory + |-------------------------------------------------------------------------- + | + | This directory is where all migrations will be stored + | + */ + 'directory' => database_path('migrations'), + /* + |-------------------------------------------------------------------------- + | Migration Namespace + |-------------------------------------------------------------------------- + | + | This namespace will be used on all migrations + | + */ + 'namespace' => 'Database\\Migrations', + /* + |-------------------------------------------------------------------------- + | Migration Repository Table + |-------------------------------------------------------------------------- + | + | Tables which are filtered by Regular Expression. You optionally + | exclude or limit to certain tables. The default will + | filter all tables. + | + */ + 'schema' => [ + 'filter' => '/^(?).*$/' + ] +]; diff --git a/config/purifier.php b/config/purifier.php new file mode 100644 index 000000000..b0888d25a --- /dev/null +++ b/config/purifier.php @@ -0,0 +1,127 @@ +set('Core.Encoding', $this->config->get('purifier.encoding')); + * $config->set('Cache.SerializerPath', $this->config->get('purifier.cachePath')); + * if ( ! $this->config->get('purifier.finalize')) { + * $config->autoFinalize = false; + * } + * $config->loadArray($this->getConfig()); + * + * You must NOT delete the default settings + * anything in settings should be compacted with params that needed to instance HTMLPurifier_Config. + * + * @link http://htmlpurifier.org/live/configdoc/plain.html + */ + +return [ + 'encoding' => 'UTF-8', + 'finalize' => true, + 'cachePath' => storage_path('app/purifier'), + 'cacheFileMode' => 0755, + 'settings' => [ + 'default' => [ + 'HTML.Doctype' => 'HTML 4.01 Transitional', + 'HTML.Allowed' => 'div,b,strong,i,em,u,a[href|title],ul,ol,li,p[style],br,span[style],img[width|height|alt|src],hr,pre,h1,h2,h3,h4,table[class],td,tr,th', + 'CSS.AllowedProperties' => 'font,font-size,font-weight,font-style,font-family,text-decoration,padding-left,color,background-color,text-align', + 'AutoFormat.AutoParagraph' => false, + 'AutoFormat.RemoveEmpty' => true, + ], + 'test' => [ + 'Attr.EnableID' => 'true', + ], + "youtube" => [ + "HTML.SafeIframe" => 'true', + "URI.SafeIframeRegexp" => "%^(http://|https://|//)(www.youtube.com/embed/|player.vimeo.com/video/)%", + ], + 'custom_definition' => [ + 'id' => 'html5-definitions', + 'rev' => 1, + 'debug' => false, + 'elements' => [ + // http://developers.whatwg.org/sections.html + ['section', 'Block', 'Flow', 'Common'], + ['nav', 'Block', 'Flow', 'Common'], + ['article', 'Block', 'Flow', 'Common'], + ['aside', 'Block', 'Flow', 'Common'], + ['header', 'Block', 'Flow', 'Common'], + ['footer', 'Block', 'Flow', 'Common'], + + // Content model actually excludes several tags, not modelled here + ['address', 'Block', 'Flow', 'Common'], + ['hgroup', 'Block', 'Required: h1 | h2 | h3 | h4 | h5 | h6', 'Common'], + + // http://developers.whatwg.org/grouping-content.html + ['figure', 'Block', 'Optional: (figcaption, Flow) | (Flow, figcaption) | Flow', 'Common'], + ['figcaption', 'Inline', 'Flow', 'Common'], + + // http://developers.whatwg.org/the-video-element.html#the-video-element + ['video', 'Block', 'Optional: (source, Flow) | (Flow, source) | Flow', 'Common', [ + 'src' => 'URI', + 'type' => 'Text', + 'width' => 'Length', + 'height' => 'Length', + 'poster' => 'URI', + 'preload' => 'Enum#auto,metadata,none', + 'controls' => 'Bool', + ]], + ['source', 'Block', 'Flow', 'Common', [ + 'src' => 'URI', + 'type' => 'Text', + ]], + + // http://developers.whatwg.org/text-level-semantics.html + ['s', 'Inline', 'Inline', 'Common'], + ['var', 'Inline', 'Inline', 'Common'], + ['sub', 'Inline', 'Inline', 'Common'], + ['sup', 'Inline', 'Inline', 'Common'], + ['mark', 'Inline', 'Inline', 'Common'], + ['wbr', 'Inline', 'Empty', 'Core'], + + // http://developers.whatwg.org/edits.html + ['ins', 'Block', 'Flow', 'Common', ['cite' => 'URI', 'datetime' => 'CDATA']], + ['del', 'Block', 'Flow', 'Common', ['cite' => 'URI', 'datetime' => 'CDATA']], + ], + 'attributes' => [ + ['iframe', 'allowfullscreen', 'Bool'], + ['table', 'height', 'Text'], + ['td', 'border', 'Text'], + ['th', 'border', 'Text'], + ['tr', 'width', 'Text'], + ['tr', 'height', 'Text'], + ['tr', 'border', 'Text'], + ], + ], + 'custom_attributes' => [ + ['a', 'target', 'Enum#_blank,_self,_target,_top'], + ], + 'custom_elements' => [ + ['u', 'Inline', 'Inline', 'Common'], + ], + ], + +]; diff --git a/config/queue.php b/config/queue.php new file mode 100644 index 000000000..1cfe85658 --- /dev/null +++ b/config/queue.php @@ -0,0 +1,109 @@ + env('QUEUE_CONNECTION', 'sync'), + + /* + |-------------------------------------------------------------------------- + | Queue Connections + |-------------------------------------------------------------------------- + | + | Here you may configure the connection information for each server that + | is used by your application. A default configuration has been added + | for each back-end shipped with Laravel. You are free to add more. + | + | Drivers: "sync", "database", "beanstalkd", "sqs", "redis", "null" + | + */ + + 'connections' => [ + + 'sync' => [ + 'driver' => 'sync', + ], + + 'database' => [ + 'driver' => 'database', + 'table' => 'jobs', + 'queue' => 'default', + 'retry_after' => 90, + ], + + 'beanstalkd' => [ + 'driver' => 'beanstalkd', + 'host' => 'localhost', + 'queue' => 'default', + 'retry_after' => 90, + 'block_for' => 0, + ], + + 'sqs' => [ + 'driver' => 'sqs', + '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', 'your-queue-name'), + 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), + ], + + 'redis' => [ + 'driver' => 'redis', + 'connection' => 'default', + 'queue' => env('REDIS_QUEUE', 'default'), + 'retry_after' => 90, + 'block_for' => null, + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Failed Queue Jobs + |-------------------------------------------------------------------------- + | + | These options configure the behavior of failed queue job logging so you + | can control which database and table are used to store the jobs that + | have failed. You may change them to any database / table you wish. + | + */ + + 'failed' => [ + 'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'), + 'database' => env('DB_CONNECTION', 'mysql'), + 'table' => 'failed_jobs', + ], + +]; diff --git a/config/services.php b/config/services.php new file mode 100644 index 000000000..cff67fc75 --- /dev/null +++ b/config/services.php @@ -0,0 +1,75 @@ + [ + 'domain' => env('MAILGUN_DOMAIN'), + 'secret' => env('MAILGUN_SECRET'), + 'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'), + 'scheme' => 'https', + ], + + 'postmark' => [ + 'token' => env('POSTMARK_TOKEN'), + ], + + 'ses' => [ + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), + ], + + 'stripe' => [ + 'model' => App\User::class, + 'key' => env('STRIPE_KEY'), + 'secret' => env('STRIPE_SECRET'), + 'webhook' => [ + 'secret' => env('STRIPE_WEBHOOK_SECRET'), + 'tolerance' => env('STRIPE_WEBHOOK_TOLERANCE', 300), + ], + ], + + + + // PeeringDB Socialite + 'peeringdb' => [ + 'client_id' => env('PEERINGDB_OAUTH_CLIENT_ID'), + 'client_secret' => env('PEERINGDB_OAUTH_CLIENT_SECRET'), + 'redirect' => env('PEERINGDB_OAUTH_REDIRECT'), + ], + + +]; diff --git a/config/session.php b/config/session.php new file mode 100644 index 000000000..635dc86f2 --- /dev/null +++ b/config/session.php @@ -0,0 +1,220 @@ + env('SESSION_DRIVER', 'file'), + + /* + |-------------------------------------------------------------------------- + | Session Lifetime + |-------------------------------------------------------------------------- + | + | Here you may specify the number of minutes that you wish the session + | to be allowed to remain idle before it expires. If you want them + | to immediately expire on the browser closing, set that option. + | + */ + + 'lifetime' => env('SESSION_LIFETIME', 120), + + 'expire_on_close' => false, + + /* + |-------------------------------------------------------------------------- + | Session Encryption + |-------------------------------------------------------------------------- + | + | This option allows you to easily specify that all of your session data + | should be encrypted before it is stored. All encryption will be run + | automatically by Laravel and you can use the Session like normal. + | + */ + + 'encrypt' => true, + + /* + |-------------------------------------------------------------------------- + | Session File Location + |-------------------------------------------------------------------------- + | + | When using the native session driver, we need a location where session + | files may be stored. A default has been set for you but a different + | location may be specified. This is only needed for file sessions. + | + */ + + 'files' => storage_path('framework/sessions'), + + /* + |-------------------------------------------------------------------------- + | Session Database Connection + |-------------------------------------------------------------------------- + | + | When using the "database" or "redis" session drivers, you may specify a + | connection that should be used to manage these sessions. This should + | correspond to a connection in your database configuration options. + | + */ + + 'connection' => env('SESSION_CONNECTION', 'mysql'), + + /* + |-------------------------------------------------------------------------- + | Session Database Table + |-------------------------------------------------------------------------- + | + | When using the "database" session driver, you may specify the table we + | should use to manage the sessions. Of course, a sensible default is + | provided for you; however, you are free to change this as needed. + | + */ + + 'table' => 'sessions', + + /* + |-------------------------------------------------------------------------- + | Session Cache Store + |-------------------------------------------------------------------------- + | + | 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". + | + */ + + 'store' => env('SESSION_STORE', false), + + /* + |-------------------------------------------------------------------------- + | Session Sweeping Lottery + |-------------------------------------------------------------------------- + | + | Some session drivers must manually sweep their storage location to get + | rid of old sessions from storage. Here are the chances that it will + | happen on a given request. By default, the odds are 2 out of 100. + | + */ + + 'lottery' => [2, 100], + + /* + |-------------------------------------------------------------------------- + | Session Cookie Name + |-------------------------------------------------------------------------- + | + | Here you may change the name of the cookie used to identify a session + | instance by ID. The name specified here will get used every time a + | new session cookie is created by the framework for every driver. + | + */ + + 'cookie' => env( + 'SESSION_COOKIE', + Str::slug(env('APP_NAME', 'laravel'), '_').'_session' + ), + + /* + |-------------------------------------------------------------------------- + | Session Cookie Path + |-------------------------------------------------------------------------- + | + | The session cookie path determines the path for which the cookie will + | be regarded as available. Typically, this will be the root path of + | your application but you are free to change this when necessary. + | + */ + + 'path' => '/', + + /* + |-------------------------------------------------------------------------- + | Session Cookie Domain + |-------------------------------------------------------------------------- + | + | Here you may change the domain of the cookie used to identify a session + | in your application. This will determine which domains the cookie is + | available to in your application. A sensible default has been set. + | + */ + + 'domain' => env('SESSION_DOMAIN' ), + + /* + |-------------------------------------------------------------------------- + | HTTPS Only Cookies + |-------------------------------------------------------------------------- + | + | 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. + | + */ + + 'secure' => env('SESSION_SECURE_COOKIE', false), + + /* + |-------------------------------------------------------------------------- + | HTTP Access Only + |-------------------------------------------------------------------------- + | + | Setting this value to true will prevent JavaScript from accessing the + | value of the cookie and the cookie will only be accessible through + | the HTTP protocol. You are free to modify this option if needed. + | + */ + + 'http_only' => true, + + /* + |-------------------------------------------------------------------------- + | Same-Site Cookies + |-------------------------------------------------------------------------- + | + | This option determines how your cookies behave when cross-site requests + | take place, and can be used to mitigate CSRF attacks. By default, we + | will set this value to "lax" since this is a secure default value. + | + | Supported: "lax", "strict", null + | + */ + + 'same_site' => 'lax', + +]; diff --git a/config/telescope.php b/config/telescope.php new file mode 100644 index 000000000..67784992d --- /dev/null +++ b/config/telescope.php @@ -0,0 +1,156 @@ + null, + + /* + |-------------------------------------------------------------------------- + | Telescope Path + |-------------------------------------------------------------------------- + | + | This is the URI path where Telescope will be accessible from. Feel free + | to change this path to anything you like. Note that the URI will not + | affect the paths of its internal API that aren't exposed to users. + | + */ + + 'path' => env( 'TELESCOPE_PATH', 'telescope' ), + 'url_path' => env( 'TELESCOPE_URL_PATH', 'telescope' ), + + /* + |-------------------------------------------------------------------------- + | Telescope Storage Driver + |-------------------------------------------------------------------------- + | + | This configuration options determines the storage driver that will + | be used to store Telescope's data. In addition, you may set any + | custom options as needed by the particular driver you choose. + | + */ + + 'driver' => env('TELESCOPE_DRIVER', 'database'), + + 'storage' => [ + 'database' => [ + 'connection' => env('DB_CONNECTION', 'mysql'), + ], + ], + + /* + |-------------------------------------------------------------------------- + | Telescope Master Switch + |-------------------------------------------------------------------------- + | + | This option may be used to disable all Telescope watchers regardless + | of their individual configuration, which simply provides a single + | and convenient way to enable or disable Telescope data storage. + | + */ + + 'enabled' => env('TELESCOPE_ENABLED', false), + + /* + |-------------------------------------------------------------------------- + | Telescope Route Middleware + |-------------------------------------------------------------------------- + | + | These middleware will be assigned to every Telescope route, giving you + | the chance to add your own middleware to this list or change any of + | the existing middleware. Or, you can simply stick with this list. + | + */ + + 'middleware' => [ + 'web', + Authorize::class, + ], + + /* + |-------------------------------------------------------------------------- + | Ignored Paths & Commands + |-------------------------------------------------------------------------- + | + | The following array lists the URI paths and Artisan commands that will + | not be watched by Telescope. In addition to this list, some Laravel + | commands, like migrations and queue commands, are always ignored. + | + */ + + 'ignore_paths' => [ + // + ], + + 'ignore_commands' => [ + // + ], + + /* + |-------------------------------------------------------------------------- + | Telescope Watchers + |-------------------------------------------------------------------------- + | + | The following array lists the "watchers" that will be registered with + | Telescope. The watchers gather the application's profile data when + | a request or task is executed. Feel free to customize this list. + | + */ + + 'watchers' => [ + Watchers\CacheWatcher::class => env('TELESCOPE_CACHE_WATCHER', true), + + Watchers\CommandWatcher::class => [ + 'enabled' => env('TELESCOPE_COMMAND_WATCHER', true), + 'ignore' => [], + ], + + Watchers\DumpWatcher::class => env('TELESCOPE_DUMP_WATCHER', true), + Watchers\EventWatcher::class => env('TELESCOPE_EVENT_WATCHER', true), + Watchers\ExceptionWatcher::class => env('TELESCOPE_EXCEPTION_WATCHER', true), + Watchers\JobWatcher::class => env('TELESCOPE_JOB_WATCHER', true), + Watchers\LogWatcher::class => env('TELESCOPE_LOG_WATCHER', true), + Watchers\MailWatcher::class => env('TELESCOPE_MAIL_WATCHER', true), + + Watchers\ModelWatcher::class => [ + 'enabled' => env('TELESCOPE_MODEL_WATCHER', true), + 'events' => ['eloquent.*'], + ], + + Watchers\NotificationWatcher::class => env('TELESCOPE_NOTIFICATION_WATCHER', true), + + Watchers\QueryWatcher::class => [ + 'enabled' => env('TELESCOPE_QUERY_WATCHER', true), + 'ignore_packages' => true, + 'slow' => 100, + ], + + Watchers\RedisWatcher::class => env('TELESCOPE_REDIS_WATCHER', true), + + Watchers\RequestWatcher::class => [ + 'enabled' => env('TELESCOPE_REQUEST_WATCHER', true), + 'size_limit' => env('TELESCOPE_RESPONSE_SIZE_LIMIT', 64), + ], + + Watchers\GateWatcher::class => [ + 'enabled' => env('TELESCOPE_GATE_WATCHER', true), + 'ignore_abilities' => [], + 'ignore_packages' => true, + ], + + Watchers\ScheduleWatcher::class => env('TELESCOPE_SCHEDULE_WATCHER', true), + ], +]; diff --git a/config/view.php b/config/view.php new file mode 100644 index 000000000..2d98748c2 --- /dev/null +++ b/config/view.php @@ -0,0 +1,61 @@ + call_user_func( function() { + if( env( 'VIEW_SKIN', 'placeholder' ) ) { + $paths[] = realpath(base_path('resources/skins/'.env('VIEW_SKIN', 'placeholder'))); + } + + $paths[] = realpath(base_path('resources/views')); + + return $paths; + }), + + /* + |-------------------------------------------------------------------------- + | Compiled View Path + |-------------------------------------------------------------------------- + | + | This option determines where all the compiled Blade templates will be + | stored for your application. Typically, this is within the storage + | directory. However, as usual, you are free to change this value. + | + */ + + 'compiled' => realpath(storage_path().'/framework/views'), + +]; diff --git a/data/SocialiteProviders/PeeringDB b/data/SocialiteProviders/PeeringDB new file mode 120000 index 000000000..14798b6da --- /dev/null +++ b/data/SocialiteProviders/PeeringDB @@ -0,0 +1 @@ +src/PeeringDB \ No newline at end of file diff --git a/data/SocialiteProviders/src/PeeringDB/PeeringDBExtendSocialite.php b/data/SocialiteProviders/src/PeeringDB/PeeringDBExtendSocialite.php new file mode 100644 index 000000000..b03e679d2 --- /dev/null +++ b/data/SocialiteProviders/src/PeeringDB/PeeringDBExtendSocialite.php @@ -0,0 +1,16 @@ +extendSocialite('peeringdb', __NAMESPACE__.'\Provider'); + } +} diff --git a/data/SocialiteProviders/src/PeeringDB/Provider.php b/data/SocialiteProviders/src/PeeringDB/Provider.php new file mode 100644 index 000000000..c303adf59 --- /dev/null +++ b/data/SocialiteProviders/src/PeeringDB/Provider.php @@ -0,0 +1,76 @@ +buildAuthUrlFromBase('https://auth.peeringdb.com/oauth2/authorize', $state); + } + + /** + * {@inheritdoc} + */ + #[\Override] + protected function getTokenUrl() + { + return 'https://auth.peeringdb.com/oauth2/token/'; + } + + /** + * {@inheritdoc} + */ + #[\Override] + protected function getUserByToken($token) + { + $response = $this->getHttpClient()->get('https://auth.peeringdb.com/profile/v1', [ + 'headers' => [ + 'Authorization' => 'Bearer '.$token, + ], + ]); + + return json_decode($response->getBody(), true); + } + + /** + * {@inheritdoc} + */ + #[\Override] + protected function mapUserToObject(array $user) + { + return (new User())->setRaw($user)->map([ + //'id' => $user['id'], + 'name' => $user['name'], + 'email' => $user['email'], + ]); + } + + /** + * {@inheritdoc} + */ + #[\Override] + protected function getTokenFields($code) + { + return array_merge(parent::getTokenFields($code), [ + 'grant_type' => 'authorization_code' + ]); + } +} diff --git a/data/SocialiteProviders/src/PeeringDB/composer.json b/data/SocialiteProviders/src/PeeringDB/composer.json new file mode 100644 index 000000000..a896562c4 --- /dev/null +++ b/data/SocialiteProviders/src/PeeringDB/composer.json @@ -0,0 +1,18 @@ +{ + "name": "socialiteproviders/peeringdb", + "description": "PeeringDB OAuth2 Provider for Laravel Socialite", + "license": "MIT", + "authors": [{ + "name": "Barry O'Donovan", + "email": "barry@opensolutions.ie" + }], + "require": { + "php": ">=5.5.9", + "socialiteproviders/manager": "^2.0" + }, + "autoload": { + "psr-4": { + "SocialiteProviders\\PeeringDB\\": "" + } + } +} diff --git a/data/ci/ci_test_db.sql b/data/ci/ci_test_db.sql new file mode 100644 index 000000000..f5ce003d9 --- /dev/null +++ b/data/ci/ci_test_db.sql @@ -0,0 +1,3070 @@ +-- MySQL dump 10.13 Distrib 8.0.36, for macos14 (arm64) +-- +-- Host: 127.0.0.1 Database: ixp_ci +-- ------------------------------------------------------ +-- Server version 8.0.36 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!50503 SET NAMES utf8mb4 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `api_keys` +-- + +DROP TABLE IF EXISTS `api_keys`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `api_keys` ( + `id` bigint NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `apiKey` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL, + `expires` datetime DEFAULT NULL, + `allowedIPs` mediumtext CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci, + `lastseenAt` datetime DEFAULT NULL, + `lastseenFrom` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `description` longtext CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `UNIQ_9579321F800A1141` (`apiKey`), + KEY `IDX_9579321FA76ED395` (`user_id`), + CONSTRAINT `FK_9579321FA76ED395` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `api_keys` +-- + +LOCK TABLES `api_keys` WRITE; +/*!40000 ALTER TABLE `api_keys` DISABLE KEYS */; +INSERT INTO `api_keys` VALUES (1,1,'Syy4R8uXTquJNkSav4mmbk5eZWOgoc6FKUJPqOoGHhBjhsC9',NULL,'','2017-05-19 09:48:49','127.0.0.1',NULL,'2014-01-06 13:43:19',NULL),(3,2,'Syy4R8uXTquJNkSav4mmbk5eZWOgoc6FKUJPqOoGHhBjhsC8',NULL,'','2017-05-19 09:48:49','127.0.0.1',NULL,'2014-01-06 13:43:19',NULL),(4,3,'Syy4R8uXTquJNkSav4mmbk5eZWOgoc6FKUJPqOoGHhBjhsC7',NULL,'','2017-05-19 09:48:49','127.0.0.1',NULL,'2014-01-06 13:43:19',NULL); +/*!40000 ALTER TABLE `api_keys` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `atlas_measurements` +-- + +DROP TABLE IF EXISTS `atlas_measurements`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `atlas_measurements` ( + `id` int NOT NULL AUTO_INCREMENT, + `run_id` int NOT NULL, + `cust_source` int DEFAULT NULL, + `cust_dest` int DEFAULT NULL, + `atlas_id` int DEFAULT NULL, + `atlas_create` datetime DEFAULT NULL, + `atlas_start` datetime DEFAULT NULL, + `atlas_stop` datetime DEFAULT NULL, + `atlas_data` json DEFAULT NULL, + `atlas_request` json DEFAULT NULL, + `atlas_state` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `atlas_measurements_cust_source_foreign` (`cust_source`), + KEY `atlas_measurements_cust_dest_foreign` (`cust_dest`), + KEY `atlas_measurements_run_id_foreign` (`run_id`), + CONSTRAINT `atlas_measurements_cust_dest_foreign` FOREIGN KEY (`cust_dest`) REFERENCES `cust` (`id`), + CONSTRAINT `atlas_measurements_cust_source_foreign` FOREIGN KEY (`cust_source`) REFERENCES `cust` (`id`), + CONSTRAINT `atlas_measurements_run_id_foreign` FOREIGN KEY (`run_id`) REFERENCES `atlas_runs` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `atlas_measurements` +-- + +LOCK TABLES `atlas_measurements` WRITE; +/*!40000 ALTER TABLE `atlas_measurements` DISABLE KEYS */; +/*!40000 ALTER TABLE `atlas_measurements` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `atlas_probes` +-- + +DROP TABLE IF EXISTS `atlas_probes`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `atlas_probes` ( + `id` bigint NOT NULL AUTO_INCREMENT, + `cust_id` int NOT NULL, + `address_v4` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `address_v6` varchar(39) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `v4_enabled` tinyint DEFAULT NULL, + `v6_enabled` tinyint DEFAULT NULL, + `asn` int DEFAULT NULL, + `atlas_id` int NOT NULL, + `is_anchor` tinyint NOT NULL, + `is_public` tinyint NOT NULL, + `last_connected` datetime DEFAULT NULL, + `status` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `api_data` json DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `atlas_probes_cust_id_foreign` (`cust_id`), + CONSTRAINT `atlas_probes_cust_id_foreign` FOREIGN KEY (`cust_id`) REFERENCES `cust` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `atlas_probes` +-- + +LOCK TABLES `atlas_probes` WRITE; +/*!40000 ALTER TABLE `atlas_probes` DISABLE KEYS */; +/*!40000 ALTER TABLE `atlas_probes` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `atlas_results` +-- + +DROP TABLE IF EXISTS `atlas_results`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `atlas_results` ( + `id` int NOT NULL AUTO_INCREMENT, + `measurement_id` int DEFAULT NULL, + `routing` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `path` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `atlas_results_measurement_id_unique` (`measurement_id`), + CONSTRAINT `atlas_results_measurement_id_foreign` FOREIGN KEY (`measurement_id`) REFERENCES `atlas_measurements` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `atlas_results` +-- + +LOCK TABLES `atlas_results` WRITE; +/*!40000 ALTER TABLE `atlas_results` DISABLE KEYS */; +/*!40000 ALTER TABLE `atlas_results` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `atlas_runs` +-- + +DROP TABLE IF EXISTS `atlas_runs`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `atlas_runs` ( + `id` int NOT NULL AUTO_INCREMENT, + `vlan_id` int DEFAULT NULL, + `protocol` int DEFAULT NULL, + `scheduled_at` datetime DEFAULT NULL, + `started_at` datetime DEFAULT NULL, + `completed_at` datetime DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `atlas_runs_vlan_id_foreign` (`vlan_id`), + CONSTRAINT `atlas_runs_vlan_id_foreign` FOREIGN KEY (`vlan_id`) REFERENCES `vlan` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `atlas_runs` +-- + +LOCK TABLES `atlas_runs` WRITE; +/*!40000 ALTER TABLE `atlas_runs` DISABLE KEYS */; +/*!40000 ALTER TABLE `atlas_runs` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `bgp_sessions` +-- + +DROP TABLE IF EXISTS `bgp_sessions`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `bgp_sessions` ( + `id` int NOT NULL AUTO_INCREMENT, + `srcipaddressid` int NOT NULL, + `protocol` int NOT NULL, + `dstipaddressid` int NOT NULL, + `packetcount` int NOT NULL DEFAULT '0', + `last_seen` datetime NOT NULL, + `source` varchar(40) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `src_protocol_dst` (`srcipaddressid`,`protocol`,`dstipaddressid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `bgp_sessions` +-- + +LOCK TABLES `bgp_sessions` WRITE; +/*!40000 ALTER TABLE `bgp_sessions` DISABLE KEYS */; +/*!40000 ALTER TABLE `bgp_sessions` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `bgpsessiondata` +-- + +DROP TABLE IF EXISTS `bgpsessiondata`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `bgpsessiondata` ( + `id` int NOT NULL AUTO_INCREMENT, + `srcipaddressid` int DEFAULT NULL, + `dstipaddressid` int DEFAULT NULL, + `protocol` int DEFAULT NULL, + `vlan` int DEFAULT NULL, + `packetcount` int DEFAULT '0', + `timestamp` datetime DEFAULT NULL, + `source` varchar(40) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `idx_timestamp` (`timestamp`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `bgpsessiondata` +-- + +LOCK TABLES `bgpsessiondata` WRITE; +/*!40000 ALTER TABLE `bgpsessiondata` DISABLE KEYS */; +/*!40000 ALTER TABLE `bgpsessiondata` ENABLE KEYS */; +UNLOCK TABLES; +/*!50003 SET @saved_cs_client = @@character_set_client */ ; +/*!50003 SET @saved_cs_results = @@character_set_results */ ; +/*!50003 SET @saved_col_connection = @@collation_connection */ ; +/*!50003 SET character_set_client = utf8mb4 */ ; +/*!50003 SET character_set_results = utf8mb4 */ ; +/*!50003 SET collation_connection = utf8mb4_unicode_ci */ ; +/*!50003 SET @saved_sql_mode = @@sql_mode */ ; +/*!50003 SET sql_mode = 'NO_ENGINE_SUBSTITUTION' */ ; +DELIMITER ;; +/*!50003 CREATE*/ /*!50017 DEFINER=`root`@`localhost`*/ /*!50003 TRIGGER `bgp_sessions_update` AFTER INSERT ON `bgpsessiondata` FOR EACH ROW BEGIN + + IF NOT EXISTS ( SELECT 1 FROM bgp_sessions WHERE srcipaddressid = NEW.srcipaddressid AND protocol = NEW.protocol AND dstipaddressid = NEW.dstipaddressid ) THEN + INSERT INTO bgp_sessions + ( srcipaddressid, protocol, dstipaddressid, packetcount, last_seen, source ) + VALUES + ( NEW.srcipaddressid, NEW.protocol, NEW.dstipaddressid, NEW.packetcount, NOW(), NEW.source ); + ELSE + UPDATE bgp_sessions SET + last_seen = NOW(), + packetcount = packetcount + NEW.packetcount + WHERE + srcipaddressid = NEW.srcipaddressid AND protocol = NEW.protocol AND dstipaddressid = NEW.dstipaddressid; + END IF; + + IF NOT EXISTS ( SELECT 1 FROM bgp_sessions WHERE dstipaddressid = NEW.srcipaddressid AND protocol = NEW.protocol AND srcipaddressid = NEW.dstipaddressid ) THEN + INSERT INTO bgp_sessions + ( srcipaddressid, protocol, dstipaddressid, packetcount, last_seen, source ) + VALUES + ( NEW.dstipaddressid, NEW.protocol, NEW.srcipaddressid, NEW.packetcount, NOW(), NEW.source ); + ELSE + UPDATE bgp_sessions SET + last_seen = NOW(), + packetcount = packetcount + NEW.packetcount + WHERE + dstipaddressid = NEW.srcipaddressid AND protocol = NEW.protocol AND srcipaddressid = NEW.dstipaddressid; + END IF; + +END */;; +DELIMITER ; +/*!50003 SET sql_mode = @saved_sql_mode */ ; +/*!50003 SET character_set_client = @saved_cs_client */ ; +/*!50003 SET character_set_results = @saved_cs_results */ ; +/*!50003 SET collation_connection = @saved_col_connection */ ; + +-- +-- Table structure for table `cabinet` +-- + +DROP TABLE IF EXISTS `cabinet`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `cabinet` ( + `id` int NOT NULL AUTO_INCREMENT, + `locationid` int DEFAULT NULL, + `name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `colocation` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `height` int DEFAULT NULL, + `type` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `notes` longtext CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci, + `u_counts_from` smallint DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `UNIQ_4CED05B05E237E06` (`name`), + KEY `IDX_4CED05B03530CCF` (`locationid`), + CONSTRAINT `FK_4CED05B03530CCF` FOREIGN KEY (`locationid`) REFERENCES `location` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `cabinet` +-- + +LOCK TABLES `cabinet` WRITE; +/*!40000 ALTER TABLE `cabinet` DISABLE KEYS */; +INSERT INTO `cabinet` VALUES (1,1,'Cabinet 1','c1',0,'','',NULL,NULL,NULL); +/*!40000 ALTER TABLE `cabinet` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `company_billing_detail` +-- + +DROP TABLE IF EXISTS `company_billing_detail`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `company_billing_detail` ( + `id` int NOT NULL AUTO_INCREMENT, + `billingContactName` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `billingAddress1` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `billingAddress2` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `billingAddress3` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `billingTownCity` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `billingPostcode` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `billingCountry` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `billingEmail` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `billingTelephone` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `vatNumber` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `vatRate` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `purchaseOrderRequired` tinyint(1) NOT NULL DEFAULT '0', + `purchaseOrderNumber` varchar(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `invoiceMethod` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `invoiceEmail` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `billingFrequency` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `notes` longtext CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `company_billing_detail` +-- + +LOCK TABLES `company_billing_detail` WRITE; +/*!40000 ALTER TABLE `company_billing_detail` DISABLE KEYS */; +INSERT INTO `company_billing_detail` VALUES (1,NULL,'c/o The Bill Payers','Money House, Moneybags Street',NULL,'Dublin','D4','IE',NULL,NULL,NULL,NULL,0,NULL,'EMAIL',NULL,'NOBILLING',NULL,NULL,NULL),(2,'','','','','','','','','','','',0,NULL,'EMAIL','','NOBILLING',NULL,NULL,NULL),(3,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0,NULL,'EMAIL',NULL,'NOBILLING',NULL,NULL,NULL),(4,'','','','','','','','','','','',0,NULL,'EMAIL','','NOBILLING',NULL,NULL,NULL),(5,'','','','','','','','','','','',0,NULL,'EMAIL','','NOBILLING',NULL,NULL,NULL); +/*!40000 ALTER TABLE `company_billing_detail` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `company_registration_detail` +-- + +DROP TABLE IF EXISTS `company_registration_detail`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `company_registration_detail` ( + `id` int NOT NULL AUTO_INCREMENT, + `registeredName` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `companyNumber` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `jurisdiction` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `address1` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `address2` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `address3` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `townCity` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `postcode` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `country` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `notes` longtext CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `company_registration_detail` +-- + +LOCK TABLES `company_registration_detail` WRITE; +/*!40000 ALTER TABLE `company_registration_detail` DISABLE KEYS */; +INSERT INTO `company_registration_detail` VALUES (1,'INEX','123456','Ireland','5 Somewhere',NULL,NULL,'Dublin','D4','IE',NULL,NULL,NULL),(2,'','','','','','','','','',NULL,NULL,NULL),(3,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(4,'','','','','','','','','',NULL,NULL,NULL),(5,'','','','','','','','','',NULL,NULL,NULL); +/*!40000 ALTER TABLE `company_registration_detail` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `console_server` +-- + +DROP TABLE IF EXISTS `console_server`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `console_server` ( + `id` int NOT NULL AUTO_INCREMENT, + `vendor_id` int DEFAULT NULL, + `cabinet_id` int DEFAULT NULL, + `name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `hostname` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `model` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `serialNumber` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `active` tinyint(1) DEFAULT '1', + `notes` longtext CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `UNIQ_92A539235E237E06` (`name`), + KEY `IDX_92A53923F603EE73` (`vendor_id`), + KEY `IDX_92A53923D351EC` (`cabinet_id`), + CONSTRAINT `FK_92A53923D351EC` FOREIGN KEY (`cabinet_id`) REFERENCES `cabinet` (`id`), + CONSTRAINT `FK_92A53923F603EE73` FOREIGN KEY (`vendor_id`) REFERENCES `vendor` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `console_server` +-- + +LOCK TABLES `console_server` WRITE; +/*!40000 ALTER TABLE `console_server` DISABLE KEYS */; +/*!40000 ALTER TABLE `console_server` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `consoleserverconnection` +-- + +DROP TABLE IF EXISTS `consoleserverconnection`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `consoleserverconnection` ( + `id` int NOT NULL AUTO_INCREMENT, + `custid` int DEFAULT NULL, + `description` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `port` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `speed` int DEFAULT NULL, + `parity` int DEFAULT NULL, + `stopbits` int DEFAULT NULL, + `flowcontrol` int DEFAULT NULL, + `autobaud` tinyint(1) DEFAULT NULL, + `notes` longtext CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci, + `console_server_id` int DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `console_server_port_uniq` (`console_server_id`,`port`), + KEY `IDX_530316DCDA0209B9` (`custid`), + KEY `IDX_530316DCF472E7C6` (`console_server_id`), + CONSTRAINT `FK_530316DCDA0209B9` FOREIGN KEY (`custid`) REFERENCES `cust` (`id`) ON DELETE CASCADE, + CONSTRAINT `FK_530316DCF472E7C6` FOREIGN KEY (`console_server_id`) REFERENCES `console_server` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `consoleserverconnection` +-- + +LOCK TABLES `consoleserverconnection` WRITE; +/*!40000 ALTER TABLE `consoleserverconnection` DISABLE KEYS */; +/*!40000 ALTER TABLE `consoleserverconnection` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `contact` +-- + +DROP TABLE IF EXISTS `contact`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `contact` ( + `id` int NOT NULL AUTO_INCREMENT, + `custid` int DEFAULT NULL, + `name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL, + `position` varchar(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `email` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `phone` varchar(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `mobile` varchar(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `facilityaccess` tinyint(1) NOT NULL DEFAULT '0', + `mayauthorize` tinyint(1) NOT NULL DEFAULT '0', + `notes` longtext CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci, + `updated_at` datetime DEFAULT NULL, + `lastupdatedby` int DEFAULT NULL, + `creator` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `created_at` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `IDX_4C62E638DA0209B9` (`custid`), + CONSTRAINT `FK_4C62E638DA0209B9` FOREIGN KEY (`custid`) REFERENCES `cust` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `contact` +-- + +LOCK TABLES `contact` WRITE; +/*!40000 ALTER TABLE `contact` DISABLE KEYS */; +INSERT INTO `contact` VALUES (1,1,'Travis CI','Master of the Universe','joe@siep.com','+353 86 123 4567','+353 1 123 4567',1,1,'','2014-01-06 13:54:22',1,'1','2014-01-06 13:54:22'),(2,5,'Imagine CustAdmin','Imagine CustAdmin','imagine-custadmin@example.com','','',1,1,'','2018-05-15 15:36:12',1,'travis','2018-05-15 15:36:12'),(3,5,'Imagine CustUser','Imagine CustUser','imagine-custuser@example.com','','',1,1,'','2018-05-15 15:36:54',1,'travis','2018-05-15 15:36:54'),(4,2,'HEAnet CustUser','HEANet CustUser','heanet-custuser@example.com','','',1,1,'','2018-05-22 12:00:00',1,'travis',NULL),(5,2,'HEAnet CustAdmin','HEANet CustAdmin','heanet-custadmin@example.com','','',1,1,'','2018-05-22 12:00:00',1,'travis',NULL); +/*!40000 ALTER TABLE `contact` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `contact_group` +-- + +DROP TABLE IF EXISTS `contact_group`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `contact_group` ( + `id` bigint NOT NULL AUTO_INCREMENT, + `name` varchar(20) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL, + `description` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL, + `type` varchar(20) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL, + `active` tinyint(1) NOT NULL DEFAULT '1', + `limited_to` int NOT NULL DEFAULT '0', + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `UNIQ_40EA54CA5E237E06` (`name`) +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `contact_group` +-- + +LOCK TABLES `contact_group` WRITE; +/*!40000 ALTER TABLE `contact_group` DISABLE KEYS */; +INSERT INTO `contact_group` VALUES (1,'Billing','Contact role for billing matters','ROLE',1,0,'2014-01-06 12:54:22',NULL),(2,'Technical','Contact role for technical matters','ROLE',1,0,'2014-01-06 12:54:22',NULL),(3,'Admin','Contact role for admin matters','ROLE',1,0,'2014-01-06 12:54:22',NULL),(4,'Marketing','Contact role for marketing matters','ROLE',1,0,'2014-01-06 12:54:22',NULL); +/*!40000 ALTER TABLE `contact_group` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `contact_to_group` +-- + +DROP TABLE IF EXISTS `contact_to_group`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `contact_to_group` ( + `contact_id` int NOT NULL, + `contact_group_id` bigint NOT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`contact_id`,`contact_group_id`), + KEY `IDX_FCD9E962E7A1254A` (`contact_id`), + KEY `IDX_FCD9E962647145D0` (`contact_group_id`), + CONSTRAINT `FK_FCD9E962647145D0` FOREIGN KEY (`contact_group_id`) REFERENCES `contact_group` (`id`), + CONSTRAINT `FK_FCD9E962E7A1254A` FOREIGN KEY (`contact_id`) REFERENCES `contact` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `contact_to_group` +-- + +LOCK TABLES `contact_to_group` WRITE; +/*!40000 ALTER TABLE `contact_to_group` DISABLE KEYS */; +/*!40000 ALTER TABLE `contact_to_group` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `corebundles` +-- + +DROP TABLE IF EXISTS `corebundles`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `corebundles` ( + `id` int NOT NULL AUTO_INCREMENT, + `description` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL, + `type` int NOT NULL, + `graph_title` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL, + `bfd` tinyint(1) NOT NULL DEFAULT '0', + `ipv4_subnet` varchar(18) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `ipv6_subnet` varchar(43) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `stp` tinyint(1) NOT NULL DEFAULT '0', + `cost` int unsigned DEFAULT NULL, + `preference` int unsigned DEFAULT NULL, + `enabled` tinyint(1) NOT NULL DEFAULT '0', + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `corebundles` +-- + +LOCK TABLES `corebundles` WRITE; +/*!40000 ALTER TABLE `corebundles` DISABLE KEYS */; +INSERT INTO `corebundles` VALUES (1,'Test Core Bundle',1,'Test Core Bundle',0,NULL,NULL,0,10,10,1,NULL,NULL); +/*!40000 ALTER TABLE `corebundles` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `coreinterfaces` +-- + +DROP TABLE IF EXISTS `coreinterfaces`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `coreinterfaces` ( + `id` int NOT NULL AUTO_INCREMENT, + `physical_interface_id` int DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `UNIQ_E1A404B7FF664B20` (`physical_interface_id`), + CONSTRAINT `FK_E1A404B7FF664B20` FOREIGN KEY (`physical_interface_id`) REFERENCES `physicalinterface` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `coreinterfaces` +-- + +LOCK TABLES `coreinterfaces` WRITE; +/*!40000 ALTER TABLE `coreinterfaces` DISABLE KEYS */; +INSERT INTO `coreinterfaces` VALUES (1,9,NULL,NULL),(2,10,NULL,NULL),(3,11,NULL,NULL),(4,12,NULL,NULL); +/*!40000 ALTER TABLE `coreinterfaces` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `corelinks` +-- + +DROP TABLE IF EXISTS `corelinks`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `corelinks` ( + `id` int NOT NULL AUTO_INCREMENT, + `core_interface_sidea_id` int NOT NULL, + `core_interface_sideb_id` int NOT NULL, + `core_bundle_id` int NOT NULL, + `bfd` tinyint(1) NOT NULL DEFAULT '0', + `ipv4_subnet` varchar(18) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `ipv6_subnet` varchar(43) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `enabled` tinyint(1) NOT NULL DEFAULT '0', + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `UNIQ_BE421236BEBB85C6` (`core_interface_sidea_id`), + UNIQUE KEY `UNIQ_BE421236AC0E2A28` (`core_interface_sideb_id`), + KEY `IDX_BE421236BE9AE9F7` (`core_bundle_id`), + CONSTRAINT `FK_BE421236AC0E2A28` FOREIGN KEY (`core_interface_sideb_id`) REFERENCES `coreinterfaces` (`id`), + CONSTRAINT `FK_BE421236BE9AE9F7` FOREIGN KEY (`core_bundle_id`) REFERENCES `corebundles` (`id`), + CONSTRAINT `FK_BE421236BEBB85C6` FOREIGN KEY (`core_interface_sidea_id`) REFERENCES `coreinterfaces` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `corelinks` +-- + +LOCK TABLES `corelinks` WRITE; +/*!40000 ALTER TABLE `corelinks` DISABLE KEYS */; +INSERT INTO `corelinks` VALUES (1,1,2,1,1,'10.0.0.0/31',NULL,1,NULL,NULL),(2,3,4,1,1,'10.0.0.2/31',NULL,1,NULL,NULL); +/*!40000 ALTER TABLE `corelinks` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `cust` +-- + +DROP TABLE IF EXISTS `cust`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `cust` ( + `id` int NOT NULL AUTO_INCREMENT, + `irrdb` int DEFAULT NULL, + `company_registered_detail_id` int DEFAULT NULL, + `company_billing_details_id` int DEFAULT NULL, + `reseller` int DEFAULT NULL, + `name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `type` int DEFAULT NULL, + `shortname` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `abbreviatedName` varchar(30) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `autsys` int DEFAULT NULL, + `maxprefixes` int DEFAULT NULL, + `peeringemail` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `nocphone` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `noc24hphone` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `nocfax` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `nocemail` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `nochours` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `nocwww` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `peeringmacro` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `peeringmacrov6` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `peeringpolicy` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `corpwww` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `datejoin` date DEFAULT NULL, + `dateleave` date DEFAULT NULL, + `status` smallint DEFAULT NULL, + `activepeeringmatrix` tinyint(1) DEFAULT NULL, + `lastupdatedby` int DEFAULT NULL, + `creator` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `MD5Support` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT 'UNKNOWN', + `isReseller` tinyint(1) NOT NULL DEFAULT '0', + `in_manrs` tinyint(1) NOT NULL DEFAULT '0', + `in_peeringdb` tinyint(1) NOT NULL DEFAULT '0', + `peeringdb_oauth` tinyint(1) NOT NULL DEFAULT '1', + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `UNIQ_997B25A64082763` (`shortname`), + UNIQUE KEY `UNIQ_997B25A98386213` (`company_registered_detail_id`), + UNIQUE KEY `UNIQ_997B25A84478F0C` (`company_billing_details_id`), + KEY `IDX_997B25A666E98DF` (`irrdb`), + KEY `IDX_997B25A18015899` (`reseller`), + CONSTRAINT `FK_997B25A18015899` FOREIGN KEY (`reseller`) REFERENCES `cust` (`id`), + CONSTRAINT `FK_997B25A666E98DF` FOREIGN KEY (`irrdb`) REFERENCES `irrdbconfig` (`id`), + CONSTRAINT `FK_997B25A84478F0C` FOREIGN KEY (`company_billing_details_id`) REFERENCES `company_billing_detail` (`id`), + CONSTRAINT `FK_997B25A98386213` FOREIGN KEY (`company_registered_detail_id`) REFERENCES `company_registration_detail` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `cust` +-- + +LOCK TABLES `cust` WRITE; +/*!40000 ALTER TABLE `cust` DISABLE KEYS */; +INSERT INTO `cust` VALUES (1,NULL,1,1,NULL,'INEX',3,'inex','INEX',2128,1000,'peering@siep.net','+353 1 123 4567','+353 1 123 4567','+353 1 123 4568','noc@siep.com','24x7','http://www.siep.com/noc/','AS-INEX','AS-INEX','mandatory','http://www.siep.com/','2014-01-06',NULL,1,1,NULL,'travis','YES',0,0,0,1,'2014-01-05 23:00:00',NULL),(2,1,2,2,NULL,'HEAnet',1,'heanet','HEAnet',1213,1000,'peering@example.com','','','','','0','','AS-HEANET',NULL,'open','http://www.example.com/','2014-01-06',NULL,1,1,NULL,'travis','UNKNOWN',0,0,0,1,'2014-01-05 23:00:00',NULL),(3,13,3,3,NULL,'PCH DNS',1,'pchdns','PCH DNS',42,2000,'peering@example.com','','','','','0','','AS-PCH',NULL,'open','http://www.example.com/','2014-01-06',NULL,1,1,1,'travis','YES',0,0,0,1,'2014-01-05 23:00:00','2014-01-05 23:00:00'),(4,2,4,4,NULL,'AS112',4,'as112','AS112',112,20,'peering@example.com','','','','','0','','',NULL,'open','http://www.example.com/','2014-01-06',NULL,1,1,NULL,'travis','NO',0,0,0,1,'2014-01-05 23:00:00',NULL),(5,1,5,5,NULL,'Imagine',1,'imagine','Imagine',25441,1000,'peering@example.com','','','','','0','','AS-IBIS',NULL,'open','http://www.example.com/','2014-01-06',NULL,1,1,NULL,'travis','YES',0,0,0,1,'2014-01-05 23:00:00',NULL); +/*!40000 ALTER TABLE `cust` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `cust_notes` +-- + +DROP TABLE IF EXISTS `cust_notes`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `cust_notes` ( + `id` bigint NOT NULL AUTO_INCREMENT, + `customer_id` int NOT NULL, + `private` tinyint(1) NOT NULL DEFAULT '1', + `title` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL, + `note` longtext CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `IDX_6377D8679395C3F3` (`customer_id`), + CONSTRAINT `FK_6377D8679395C3F3` FOREIGN KEY (`customer_id`) REFERENCES `cust` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `cust_notes` +-- + +LOCK TABLES `cust_notes` WRITE; +/*!40000 ALTER TABLE `cust_notes` DISABLE KEYS */; +/*!40000 ALTER TABLE `cust_notes` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `cust_tag` +-- + +DROP TABLE IF EXISTS `cust_tag`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `cust_tag` ( + `id` int NOT NULL AUTO_INCREMENT, + `tag` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL, + `display_as` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL, + `description` longtext CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci, + `internal_only` tinyint(1) NOT NULL DEFAULT '0', + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `UNIQ_6B54CFB8389B783` (`tag`) +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `cust_tag` +-- + +LOCK TABLES `cust_tag` WRITE; +/*!40000 ALTER TABLE `cust_tag` DISABLE KEYS */; +INSERT INTO `cust_tag` VALUES (1,'test-tag1','Test Tag1','Yeah!',0,'2018-06-19 11:38:28','2018-06-19 11:38:28'),(2,'test-tag2','Test Tag2','Yeah!',1,'2018-06-19 11:38:44','2018-06-19 11:38:44'); +/*!40000 ALTER TABLE `cust_tag` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `cust_to_cust_tag` +-- + +DROP TABLE IF EXISTS `cust_to_cust_tag`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `cust_to_cust_tag` ( + `customer_tag_id` int NOT NULL, + `customer_id` int NOT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`customer_tag_id`,`customer_id`), + KEY `IDX_A6CFB30CB17BF40` (`customer_tag_id`), + KEY `IDX_A6CFB30C9395C3F3` (`customer_id`), + CONSTRAINT `FK_A6CFB30C9395C3F3` FOREIGN KEY (`customer_id`) REFERENCES `cust` (`id`), + CONSTRAINT `FK_A6CFB30CB17BF40` FOREIGN KEY (`customer_tag_id`) REFERENCES `cust_tag` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `cust_to_cust_tag` +-- + +LOCK TABLES `cust_to_cust_tag` WRITE; +/*!40000 ALTER TABLE `cust_to_cust_tag` DISABLE KEYS */; +INSERT INTO `cust_to_cust_tag` VALUES (1,4,NULL,NULL),(1,5,NULL,NULL),(2,1,NULL,NULL),(2,4,NULL,NULL); +/*!40000 ALTER TABLE `cust_to_cust_tag` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `custkit` +-- + +DROP TABLE IF EXISTS `custkit`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `custkit` ( + `id` int NOT NULL AUTO_INCREMENT, + `custid` int DEFAULT NULL, + `cabinetid` int DEFAULT NULL, + `name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `descr` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `IDX_8127F9AADA0209B9` (`custid`), + KEY `IDX_8127F9AA2B96718A` (`cabinetid`), + CONSTRAINT `FK_8127F9AA2B96718A` FOREIGN KEY (`cabinetid`) REFERENCES `cabinet` (`id`), + CONSTRAINT `FK_8127F9AADA0209B9` FOREIGN KEY (`custid`) REFERENCES `cust` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `custkit` +-- + +LOCK TABLES `custkit` WRITE; +/*!40000 ALTER TABLE `custkit` DISABLE KEYS */; +/*!40000 ALTER TABLE `custkit` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `customer_to_users` +-- + +DROP TABLE IF EXISTS `customer_to_users`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `customer_to_users` ( + `id` int NOT NULL AUTO_INCREMENT, + `customer_id` int NOT NULL, + `user_id` int NOT NULL, + `privs` int NOT NULL, + `last_login_date` datetime DEFAULT NULL, + `last_login_from` tinytext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, + `extra_attributes` json DEFAULT NULL COMMENT '(DC2Type:json)', + `last_login_via` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `customer_user` (`customer_id`,`user_id`), + KEY `IDX_337AD7F69395C3F3` (`customer_id`), + KEY `IDX_337AD7F6A76ED395` (`user_id`), + CONSTRAINT `FK_337AD7F69395C3F3` FOREIGN KEY (`customer_id`) REFERENCES `cust` (`id`), + CONSTRAINT `FK_337AD7F6A76ED395` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `customer_to_users` +-- + +LOCK TABLES `customer_to_users` WRITE; +/*!40000 ALTER TABLE `customer_to_users` DISABLE KEYS */; +INSERT INTO `customer_to_users` VALUES (1,1,1,3,'2022-02-28 15:16:03','127.0.0.1','{\"created_by\": {\"type\": \"migration-script\"}}','Login','2019-05-10 11:40:45','2022-02-28 15:16:03'),(2,5,2,2,'2018-06-20 10:23:22','127.0.0.1','{\"created_by\": {\"type\": \"migration-script\"}}',NULL,'2019-05-10 11:40:45',NULL),(3,5,3,1,'2018-06-20 10:23:58','127.0.0.1','{\"created_by\": {\"type\": \"migration-script\"}}',NULL,'2019-05-10 11:40:45',NULL),(4,2,4,1,'1970-01-01 00:00:00','','{\"created_by\": {\"type\": \"migration-script\"}}',NULL,'2019-05-10 11:40:45',NULL),(5,2,5,2,'2018-06-20 10:24:24','127.0.0.1','{\"created_by\": {\"type\": \"migration-script\"}}',NULL,'2019-05-10 11:40:45',NULL); +/*!40000 ALTER TABLE `customer_to_users` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `docstore_customer_directories` +-- + +DROP TABLE IF EXISTS `docstore_customer_directories`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `docstore_customer_directories` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `cust_id` int NOT NULL, + `parent_dir_id` bigint unsigned DEFAULT NULL, + `name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `docstore_customer_directories_cust_id_foreign` (`cust_id`), + KEY `docstore_customer_directories_parent_dir_id_index` (`parent_dir_id`), + CONSTRAINT `docstore_customer_directories_cust_id_foreign` FOREIGN KEY (`cust_id`) REFERENCES `cust` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `docstore_customer_directories` +-- + +LOCK TABLES `docstore_customer_directories` WRITE; +/*!40000 ALTER TABLE `docstore_customer_directories` DISABLE KEYS */; +INSERT INTO `docstore_customer_directories` VALUES (1,5,NULL,'Folder 1','This is the folder 1','2020-04-28 08:00:00','2020-04-28 08:00:00'),(2,5,1,'Sub Folder 1','This is sub folder 1','2020-04-28 08:00:00','2020-04-28 08:00:00'),(3,5,NULL,'Folder 2','This is folder 2','2020-04-28 08:00:00','2020-04-28 08:00:00'); +/*!40000 ALTER TABLE `docstore_customer_directories` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `docstore_customer_files` +-- + +DROP TABLE IF EXISTS `docstore_customer_files`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `docstore_customer_files` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `cust_id` int NOT NULL, + `docstore_customer_directory_id` bigint unsigned DEFAULT NULL, + `name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `disk` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'docstore_customers', + `path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '', + `sha256` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, + `min_privs` smallint NOT NULL, + `file_last_updated` datetime NOT NULL, + `created_by` int DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `docstore_customer_files_cust_id_foreign` (`cust_id`), + KEY `docstore_customer_files_docstore_customer_directory_id_foreign` (`docstore_customer_directory_id`), + CONSTRAINT `docstore_customer_files_cust_id_foreign` FOREIGN KEY (`cust_id`) REFERENCES `cust` (`id`), + CONSTRAINT `docstore_customer_files_docstore_customer_directory_id_foreign` FOREIGN KEY (`docstore_customer_directory_id`) REFERENCES `docstore_customer_directories` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `docstore_customer_files` +-- + +LOCK TABLES `docstore_customer_files` WRITE; +/*!40000 ALTER TABLE `docstore_customer_files` DISABLE KEYS */; +INSERT INTO `docstore_customer_files` VALUES (1,5,1,'File.pdf','docstore_customers','5/7s5yYBsebKN64SHtFkM16pY2OBvkdURPXzW7abmb.pdf','76ca2a6f2acda3c8ff39df2695885a2dbf05565dedaed6912a2b4cf439a19228',NULL,3,'2020-04-28 09:04:46',1,'2020-04-28 08:04:46','2020-04-28 08:04:46'); +/*!40000 ALTER TABLE `docstore_customer_files` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `docstore_directories` +-- + +DROP TABLE IF EXISTS `docstore_directories`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `docstore_directories` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `parent_dir_id` bigint unsigned DEFAULT NULL, + `name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `docstore_directories_parent_dir_id_index` (`parent_dir_id`) +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `docstore_directories` +-- + +LOCK TABLES `docstore_directories` WRITE; +/*!40000 ALTER TABLE `docstore_directories` DISABLE KEYS */; +INSERT INTO `docstore_directories` VALUES (1,NULL,'Folder 1','I am the folder 1','2020-02-27 10:35:18','2020-02-27 10:35:18'),(2,1,'Sub Folder 1','I am the sub folder 1','2020-02-27 10:35:48','2020-02-27 10:35:48'),(3,NULL,'Folder 2','I am the folder 2','2020-02-27 10:36:11','2020-02-27 10:36:11'); +/*!40000 ALTER TABLE `docstore_directories` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `docstore_files` +-- + +DROP TABLE IF EXISTS `docstore_files`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `docstore_files` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `docstore_directory_id` bigint unsigned DEFAULT NULL, + `name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `disk` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'docstore', + `path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '', + `sha256` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, + `min_privs` smallint NOT NULL, + `file_last_updated` datetime NOT NULL, + `created_by` int DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `docstore_files_docstore_directory_id_foreign` (`docstore_directory_id`), + CONSTRAINT `docstore_files_docstore_directory_id_foreign` FOREIGN KEY (`docstore_directory_id`) REFERENCES `docstore_directories` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `docstore_files` +-- + +LOCK TABLES `docstore_files` WRITE; +/*!40000 ALTER TABLE `docstore_files` DISABLE KEYS */; +INSERT INTO `docstore_files` VALUES (1,1,'test.txt','docstore','BaLvISQV7Cn48p8LPaOoPSyJ5hzaKC7rHlqMu5Hd.txt','64cdd02f0ef14bf6b8e0a51915396a002afed410459935b1209ba2d654842f10',NULL,0,'2021-05-28 14:03:38',1,'2021-05-28 12:03:38','2021-05-28 12:03:38'); +/*!40000 ALTER TABLE `docstore_files` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `docstore_logs` +-- + +DROP TABLE IF EXISTS `docstore_logs`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `docstore_logs` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `docstore_file_id` bigint unsigned NOT NULL, + `downloaded_by` int DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `docstore_logs_docstore_file_id_foreign` (`docstore_file_id`), + CONSTRAINT `docstore_logs_docstore_file_id_foreign` FOREIGN KEY (`docstore_file_id`) REFERENCES `docstore_files` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `docstore_logs` +-- + +LOCK TABLES `docstore_logs` WRITE; +/*!40000 ALTER TABLE `docstore_logs` DISABLE KEYS */; +/*!40000 ALTER TABLE `docstore_logs` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `failed_jobs` +-- + +DROP TABLE IF EXISTS `failed_jobs`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `failed_jobs` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `connection` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `queue` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `payload` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `exception` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `failed_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `failed_jobs` +-- + +LOCK TABLES `failed_jobs` WRITE; +/*!40000 ALTER TABLE `failed_jobs` DISABLE KEYS */; +/*!40000 ALTER TABLE `failed_jobs` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `infrastructure` +-- + +DROP TABLE IF EXISTS `infrastructure`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `infrastructure` ( + `id` int NOT NULL AUTO_INCREMENT, + `name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `shortname` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `isPrimary` tinyint(1) NOT NULL DEFAULT '0', + `peeringdb_ix_id` bigint DEFAULT NULL, + `ixf_ix_id` bigint DEFAULT NULL, + `country` varchar(2) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `notes` longtext CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `unique_shortname` (`shortname`) +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `infrastructure` +-- + +LOCK TABLES `infrastructure` WRITE; +/*!40000 ALTER TABLE `infrastructure` DISABLE KEYS */; +INSERT INTO `infrastructure` VALUES (1,'Infrastructure #1','#1',1,48,20,NULL,NULL,NULL,NULL),(2,'Infrastructure #2','#2',0,387,645,NULL,NULL,NULL,NULL); +/*!40000 ALTER TABLE `infrastructure` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `ipv4address` +-- + +DROP TABLE IF EXISTS `ipv4address`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `ipv4address` ( + `id` int NOT NULL AUTO_INCREMENT, + `vlanid` int DEFAULT NULL, + `address` varchar(16) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `vlan_address` (`vlanid`,`address`), + KEY `IDX_A44BCBEEF48D6D0` (`vlanid`), + CONSTRAINT `FK_A44BCBEEF48D6D0` FOREIGN KEY (`vlanid`) REFERENCES `vlan` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=253 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `ipv4address` +-- + +LOCK TABLES `ipv4address` WRITE; +/*!40000 ALTER TABLE `ipv4address` DISABLE KEYS */; +INSERT INTO `ipv4address` VALUES (1,1,'10.1.0.1',NULL,NULL),(2,1,'10.1.0.2',NULL,NULL),(3,1,'10.1.0.3',NULL,NULL),(4,1,'10.1.0.4',NULL,NULL),(5,1,'10.1.0.5',NULL,NULL),(6,1,'10.1.0.6',NULL,NULL),(7,1,'10.1.0.7',NULL,NULL),(8,1,'10.1.0.8',NULL,NULL),(9,1,'10.1.0.9',NULL,NULL),(10,1,'10.1.0.10',NULL,NULL),(11,1,'10.1.0.11',NULL,NULL),(12,1,'10.1.0.12',NULL,NULL),(13,1,'10.1.0.13',NULL,NULL),(14,1,'10.1.0.14',NULL,NULL),(15,1,'10.1.0.15',NULL,NULL),(16,1,'10.1.0.16',NULL,NULL),(17,1,'10.1.0.17',NULL,NULL),(18,1,'10.1.0.18',NULL,NULL),(19,1,'10.1.0.19',NULL,NULL),(20,1,'10.1.0.20',NULL,NULL),(21,1,'10.1.0.21',NULL,NULL),(22,1,'10.1.0.22',NULL,NULL),(23,1,'10.1.0.23',NULL,NULL),(24,1,'10.1.0.24',NULL,NULL),(25,1,'10.1.0.25',NULL,NULL),(26,1,'10.1.0.26',NULL,NULL),(27,1,'10.1.0.27',NULL,NULL),(28,1,'10.1.0.28',NULL,NULL),(29,1,'10.1.0.29',NULL,NULL),(30,1,'10.1.0.30',NULL,NULL),(31,1,'10.1.0.31',NULL,NULL),(32,1,'10.1.0.32',NULL,NULL),(33,1,'10.1.0.33',NULL,NULL),(34,1,'10.1.0.34',NULL,NULL),(35,1,'10.1.0.35',NULL,NULL),(36,1,'10.1.0.36',NULL,NULL),(37,1,'10.1.0.37',NULL,NULL),(38,1,'10.1.0.38',NULL,NULL),(39,1,'10.1.0.39',NULL,NULL),(40,1,'10.1.0.40',NULL,NULL),(41,1,'10.1.0.41',NULL,NULL),(42,1,'10.1.0.42',NULL,NULL),(43,1,'10.1.0.43',NULL,NULL),(44,1,'10.1.0.44',NULL,NULL),(45,1,'10.1.0.45',NULL,NULL),(46,1,'10.1.0.46',NULL,NULL),(47,1,'10.1.0.47',NULL,NULL),(48,1,'10.1.0.48',NULL,NULL),(49,1,'10.1.0.49',NULL,NULL),(50,1,'10.1.0.50',NULL,NULL),(51,1,'10.1.0.51',NULL,NULL),(52,1,'10.1.0.52',NULL,NULL),(53,1,'10.1.0.53',NULL,NULL),(54,1,'10.1.0.54',NULL,NULL),(55,1,'10.1.0.55',NULL,NULL),(56,1,'10.1.0.56',NULL,NULL),(57,1,'10.1.0.57',NULL,NULL),(58,1,'10.1.0.58',NULL,NULL),(59,1,'10.1.0.59',NULL,NULL),(60,1,'10.1.0.60',NULL,NULL),(61,1,'10.1.0.61',NULL,NULL),(62,1,'10.1.0.62',NULL,NULL),(63,1,'10.1.0.63',NULL,NULL),(64,1,'10.1.0.64',NULL,NULL),(65,1,'10.1.0.65',NULL,NULL),(66,1,'10.1.0.66',NULL,NULL),(67,1,'10.1.0.67',NULL,NULL),(68,1,'10.1.0.68',NULL,NULL),(69,1,'10.1.0.69',NULL,NULL),(70,1,'10.1.0.70',NULL,NULL),(71,1,'10.1.0.71',NULL,NULL),(72,1,'10.1.0.72',NULL,NULL),(73,1,'10.1.0.73',NULL,NULL),(74,1,'10.1.0.74',NULL,NULL),(75,1,'10.1.0.75',NULL,NULL),(76,1,'10.1.0.76',NULL,NULL),(77,1,'10.1.0.77',NULL,NULL),(78,1,'10.1.0.78',NULL,NULL),(79,1,'10.1.0.79',NULL,NULL),(80,1,'10.1.0.80',NULL,NULL),(81,1,'10.1.0.81',NULL,NULL),(82,1,'10.1.0.82',NULL,NULL),(83,1,'10.1.0.83',NULL,NULL),(84,1,'10.1.0.84',NULL,NULL),(85,1,'10.1.0.85',NULL,NULL),(86,1,'10.1.0.86',NULL,NULL),(87,1,'10.1.0.87',NULL,NULL),(88,1,'10.1.0.88',NULL,NULL),(89,1,'10.1.0.89',NULL,NULL),(90,1,'10.1.0.90',NULL,NULL),(91,1,'10.1.0.91',NULL,NULL),(92,1,'10.1.0.92',NULL,NULL),(93,1,'10.1.0.93',NULL,NULL),(94,1,'10.1.0.94',NULL,NULL),(95,1,'10.1.0.95',NULL,NULL),(96,1,'10.1.0.96',NULL,NULL),(97,1,'10.1.0.97',NULL,NULL),(98,1,'10.1.0.98',NULL,NULL),(99,1,'10.1.0.99',NULL,NULL),(100,1,'10.1.0.100',NULL,NULL),(101,1,'10.1.0.101',NULL,NULL),(102,1,'10.1.0.102',NULL,NULL),(103,1,'10.1.0.103',NULL,NULL),(104,1,'10.1.0.104',NULL,NULL),(105,1,'10.1.0.105',NULL,NULL),(106,1,'10.1.0.106',NULL,NULL),(107,1,'10.1.0.107',NULL,NULL),(108,1,'10.1.0.108',NULL,NULL),(109,1,'10.1.0.109',NULL,NULL),(110,1,'10.1.0.110',NULL,NULL),(111,1,'10.1.0.111',NULL,NULL),(112,1,'10.1.0.112',NULL,NULL),(113,1,'10.1.0.113',NULL,NULL),(114,1,'10.1.0.114',NULL,NULL),(115,1,'10.1.0.115',NULL,NULL),(116,1,'10.1.0.116',NULL,NULL),(117,1,'10.1.0.117',NULL,NULL),(118,1,'10.1.0.118',NULL,NULL),(119,1,'10.1.0.119',NULL,NULL),(120,1,'10.1.0.120',NULL,NULL),(121,1,'10.1.0.121',NULL,NULL),(122,1,'10.1.0.122',NULL,NULL),(123,1,'10.1.0.123',NULL,NULL),(124,1,'10.1.0.124',NULL,NULL),(125,1,'10.1.0.125',NULL,NULL),(126,1,'10.1.0.126',NULL,NULL),(127,2,'10.2.0.1',NULL,NULL),(128,2,'10.2.0.2',NULL,NULL),(129,2,'10.2.0.3',NULL,NULL),(130,2,'10.2.0.4',NULL,NULL),(131,2,'10.2.0.5',NULL,NULL),(132,2,'10.2.0.6',NULL,NULL),(133,2,'10.2.0.7',NULL,NULL),(134,2,'10.2.0.8',NULL,NULL),(135,2,'10.2.0.9',NULL,NULL),(136,2,'10.2.0.10',NULL,NULL),(137,2,'10.2.0.11',NULL,NULL),(138,2,'10.2.0.12',NULL,NULL),(139,2,'10.2.0.13',NULL,NULL),(140,2,'10.2.0.14',NULL,NULL),(141,2,'10.2.0.15',NULL,NULL),(142,2,'10.2.0.16',NULL,NULL),(143,2,'10.2.0.17',NULL,NULL),(144,2,'10.2.0.18',NULL,NULL),(145,2,'10.2.0.19',NULL,NULL),(146,2,'10.2.0.20',NULL,NULL),(147,2,'10.2.0.21',NULL,NULL),(148,2,'10.2.0.22',NULL,NULL),(149,2,'10.2.0.23',NULL,NULL),(150,2,'10.2.0.24',NULL,NULL),(151,2,'10.2.0.25',NULL,NULL),(152,2,'10.2.0.26',NULL,NULL),(153,2,'10.2.0.27',NULL,NULL),(154,2,'10.2.0.28',NULL,NULL),(155,2,'10.2.0.29',NULL,NULL),(156,2,'10.2.0.30',NULL,NULL),(157,2,'10.2.0.31',NULL,NULL),(158,2,'10.2.0.32',NULL,NULL),(159,2,'10.2.0.33',NULL,NULL),(160,2,'10.2.0.34',NULL,NULL),(161,2,'10.2.0.35',NULL,NULL),(162,2,'10.2.0.36',NULL,NULL),(163,2,'10.2.0.37',NULL,NULL),(164,2,'10.2.0.38',NULL,NULL),(165,2,'10.2.0.39',NULL,NULL),(166,2,'10.2.0.40',NULL,NULL),(167,2,'10.2.0.41',NULL,NULL),(168,2,'10.2.0.42',NULL,NULL),(169,2,'10.2.0.43',NULL,NULL),(170,2,'10.2.0.44',NULL,NULL),(171,2,'10.2.0.45',NULL,NULL),(172,2,'10.2.0.46',NULL,NULL),(173,2,'10.2.0.47',NULL,NULL),(174,2,'10.2.0.48',NULL,NULL),(175,2,'10.2.0.49',NULL,NULL),(176,2,'10.2.0.50',NULL,NULL),(177,2,'10.2.0.51',NULL,NULL),(178,2,'10.2.0.52',NULL,NULL),(179,2,'10.2.0.53',NULL,NULL),(180,2,'10.2.0.54',NULL,NULL),(181,2,'10.2.0.55',NULL,NULL),(182,2,'10.2.0.56',NULL,NULL),(183,2,'10.2.0.57',NULL,NULL),(184,2,'10.2.0.58',NULL,NULL),(185,2,'10.2.0.59',NULL,NULL),(186,2,'10.2.0.60',NULL,NULL),(187,2,'10.2.0.61',NULL,NULL),(188,2,'10.2.0.62',NULL,NULL),(189,2,'10.2.0.63',NULL,NULL),(190,2,'10.2.0.64',NULL,NULL),(191,2,'10.2.0.65',NULL,NULL),(192,2,'10.2.0.66',NULL,NULL),(193,2,'10.2.0.67',NULL,NULL),(194,2,'10.2.0.68',NULL,NULL),(195,2,'10.2.0.69',NULL,NULL),(196,2,'10.2.0.70',NULL,NULL),(197,2,'10.2.0.71',NULL,NULL),(198,2,'10.2.0.72',NULL,NULL),(199,2,'10.2.0.73',NULL,NULL),(200,2,'10.2.0.74',NULL,NULL),(201,2,'10.2.0.75',NULL,NULL),(202,2,'10.2.0.76',NULL,NULL),(203,2,'10.2.0.77',NULL,NULL),(204,2,'10.2.0.78',NULL,NULL),(205,2,'10.2.0.79',NULL,NULL),(206,2,'10.2.0.80',NULL,NULL),(207,2,'10.2.0.81',NULL,NULL),(208,2,'10.2.0.82',NULL,NULL),(209,2,'10.2.0.83',NULL,NULL),(210,2,'10.2.0.84',NULL,NULL),(211,2,'10.2.0.85',NULL,NULL),(212,2,'10.2.0.86',NULL,NULL),(213,2,'10.2.0.87',NULL,NULL),(214,2,'10.2.0.88',NULL,NULL),(215,2,'10.2.0.89',NULL,NULL),(216,2,'10.2.0.90',NULL,NULL),(217,2,'10.2.0.91',NULL,NULL),(218,2,'10.2.0.92',NULL,NULL),(219,2,'10.2.0.93',NULL,NULL),(220,2,'10.2.0.94',NULL,NULL),(221,2,'10.2.0.95',NULL,NULL),(222,2,'10.2.0.96',NULL,NULL),(223,2,'10.2.0.97',NULL,NULL),(224,2,'10.2.0.98',NULL,NULL),(225,2,'10.2.0.99',NULL,NULL),(226,2,'10.2.0.100',NULL,NULL),(227,2,'10.2.0.101',NULL,NULL),(228,2,'10.2.0.102',NULL,NULL),(229,2,'10.2.0.103',NULL,NULL),(230,2,'10.2.0.104',NULL,NULL),(231,2,'10.2.0.105',NULL,NULL),(232,2,'10.2.0.106',NULL,NULL),(233,2,'10.2.0.107',NULL,NULL),(234,2,'10.2.0.108',NULL,NULL),(235,2,'10.2.0.109',NULL,NULL),(236,2,'10.2.0.110',NULL,NULL),(237,2,'10.2.0.111',NULL,NULL),(238,2,'10.2.0.112',NULL,NULL),(239,2,'10.2.0.113',NULL,NULL),(240,2,'10.2.0.114',NULL,NULL),(241,2,'10.2.0.115',NULL,NULL),(242,2,'10.2.0.116',NULL,NULL),(243,2,'10.2.0.117',NULL,NULL),(244,2,'10.2.0.118',NULL,NULL),(245,2,'10.2.0.119',NULL,NULL),(246,2,'10.2.0.120',NULL,NULL),(247,2,'10.2.0.121',NULL,NULL),(248,2,'10.2.0.122',NULL,NULL),(249,2,'10.2.0.123',NULL,NULL),(250,2,'10.2.0.124',NULL,NULL),(251,2,'10.2.0.125',NULL,NULL),(252,2,'10.2.0.126',NULL,NULL); +/*!40000 ALTER TABLE `ipv4address` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `ipv6address` +-- + +DROP TABLE IF EXISTS `ipv6address`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `ipv6address` ( + `id` int NOT NULL AUTO_INCREMENT, + `vlanid` int DEFAULT NULL, + `address` varchar(40) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `vlan_address` (`vlanid`,`address`), + KEY `IDX_E66ECC93F48D6D0` (`vlanid`), + CONSTRAINT `FK_E66ECC93F48D6D0` FOREIGN KEY (`vlanid`) REFERENCES `vlan` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=801 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `ipv6address` +-- + +LOCK TABLES `ipv6address` WRITE; +/*!40000 ALTER TABLE `ipv6address` DISABLE KEYS */; +INSERT INTO `ipv6address` VALUES (1,1,'2001:db8:1::1',NULL,NULL),(2,1,'2001:db8:1::2',NULL,NULL),(3,1,'2001:db8:1::3',NULL,NULL),(4,1,'2001:db8:1::4',NULL,NULL),(5,1,'2001:db8:1::5',NULL,NULL),(6,1,'2001:db8:1::6',NULL,NULL),(7,1,'2001:db8:1::7',NULL,NULL),(8,1,'2001:db8:1::8',NULL,NULL),(9,1,'2001:db8:1::9',NULL,NULL),(10,1,'2001:db8:1::a',NULL,NULL),(11,1,'2001:db8:1::b',NULL,NULL),(12,1,'2001:db8:1::c',NULL,NULL),(13,1,'2001:db8:1::d',NULL,NULL),(14,1,'2001:db8:1::e',NULL,NULL),(15,1,'2001:db8:1::f',NULL,NULL),(16,1,'2001:db8:1::10',NULL,NULL),(17,1,'2001:db8:1::11',NULL,NULL),(18,1,'2001:db8:1::12',NULL,NULL),(19,1,'2001:db8:1::13',NULL,NULL),(20,1,'2001:db8:1::14',NULL,NULL),(21,1,'2001:db8:1::15',NULL,NULL),(22,1,'2001:db8:1::16',NULL,NULL),(23,1,'2001:db8:1::17',NULL,NULL),(24,1,'2001:db8:1::18',NULL,NULL),(25,1,'2001:db8:1::19',NULL,NULL),(26,1,'2001:db8:1::1a',NULL,NULL),(27,1,'2001:db8:1::1b',NULL,NULL),(28,1,'2001:db8:1::1c',NULL,NULL),(29,1,'2001:db8:1::1d',NULL,NULL),(30,1,'2001:db8:1::1e',NULL,NULL),(31,1,'2001:db8:1::1f',NULL,NULL),(32,1,'2001:db8:1::20',NULL,NULL),(33,1,'2001:db8:1::21',NULL,NULL),(34,1,'2001:db8:1::22',NULL,NULL),(35,1,'2001:db8:1::23',NULL,NULL),(36,1,'2001:db8:1::24',NULL,NULL),(37,1,'2001:db8:1::25',NULL,NULL),(38,1,'2001:db8:1::26',NULL,NULL),(39,1,'2001:db8:1::27',NULL,NULL),(40,1,'2001:db8:1::28',NULL,NULL),(41,1,'2001:db8:1::29',NULL,NULL),(42,1,'2001:db8:1::2a',NULL,NULL),(43,1,'2001:db8:1::2b',NULL,NULL),(44,1,'2001:db8:1::2c',NULL,NULL),(45,1,'2001:db8:1::2d',NULL,NULL),(46,1,'2001:db8:1::2e',NULL,NULL),(47,1,'2001:db8:1::2f',NULL,NULL),(48,1,'2001:db8:1::30',NULL,NULL),(49,1,'2001:db8:1::31',NULL,NULL),(50,1,'2001:db8:1::32',NULL,NULL),(51,1,'2001:db8:1::33',NULL,NULL),(52,1,'2001:db8:1::34',NULL,NULL),(53,1,'2001:db8:1::35',NULL,NULL),(54,1,'2001:db8:1::36',NULL,NULL),(55,1,'2001:db8:1::37',NULL,NULL),(56,1,'2001:db8:1::38',NULL,NULL),(57,1,'2001:db8:1::39',NULL,NULL),(58,1,'2001:db8:1::3a',NULL,NULL),(59,1,'2001:db8:1::3b',NULL,NULL),(60,1,'2001:db8:1::3c',NULL,NULL),(61,1,'2001:db8:1::3d',NULL,NULL),(62,1,'2001:db8:1::3e',NULL,NULL),(63,1,'2001:db8:1::3f',NULL,NULL),(64,1,'2001:db8:1::40',NULL,NULL),(65,1,'2001:db8:1::41',NULL,NULL),(66,1,'2001:db8:1::42',NULL,NULL),(67,1,'2001:db8:1::43',NULL,NULL),(68,1,'2001:db8:1::44',NULL,NULL),(69,1,'2001:db8:1::45',NULL,NULL),(70,1,'2001:db8:1::46',NULL,NULL),(71,1,'2001:db8:1::47',NULL,NULL),(72,1,'2001:db8:1::48',NULL,NULL),(73,1,'2001:db8:1::49',NULL,NULL),(74,1,'2001:db8:1::4a',NULL,NULL),(75,1,'2001:db8:1::4b',NULL,NULL),(76,1,'2001:db8:1::4c',NULL,NULL),(77,1,'2001:db8:1::4d',NULL,NULL),(78,1,'2001:db8:1::4e',NULL,NULL),(79,1,'2001:db8:1::4f',NULL,NULL),(80,1,'2001:db8:1::50',NULL,NULL),(81,1,'2001:db8:1::51',NULL,NULL),(82,1,'2001:db8:1::52',NULL,NULL),(83,1,'2001:db8:1::53',NULL,NULL),(84,1,'2001:db8:1::54',NULL,NULL),(85,1,'2001:db8:1::55',NULL,NULL),(86,1,'2001:db8:1::56',NULL,NULL),(87,1,'2001:db8:1::57',NULL,NULL),(88,1,'2001:db8:1::58',NULL,NULL),(89,1,'2001:db8:1::59',NULL,NULL),(90,1,'2001:db8:1::5a',NULL,NULL),(91,1,'2001:db8:1::5b',NULL,NULL),(92,1,'2001:db8:1::5c',NULL,NULL),(93,1,'2001:db8:1::5d',NULL,NULL),(94,1,'2001:db8:1::5e',NULL,NULL),(95,1,'2001:db8:1::5f',NULL,NULL),(96,1,'2001:db8:1::60',NULL,NULL),(97,1,'2001:db8:1::61',NULL,NULL),(98,1,'2001:db8:1::62',NULL,NULL),(99,1,'2001:db8:1::63',NULL,NULL),(100,1,'2001:db8:1::64',NULL,NULL),(101,1,'2001:db8:1::65',NULL,NULL),(102,1,'2001:db8:1::66',NULL,NULL),(103,1,'2001:db8:1::67',NULL,NULL),(104,1,'2001:db8:1::68',NULL,NULL),(105,1,'2001:db8:1::69',NULL,NULL),(106,1,'2001:db8:1::6a',NULL,NULL),(107,1,'2001:db8:1::6b',NULL,NULL),(108,1,'2001:db8:1::6c',NULL,NULL),(109,1,'2001:db8:1::6d',NULL,NULL),(110,1,'2001:db8:1::6e',NULL,NULL),(111,1,'2001:db8:1::6f',NULL,NULL),(112,1,'2001:db8:1::70',NULL,NULL),(113,1,'2001:db8:1::71',NULL,NULL),(114,1,'2001:db8:1::72',NULL,NULL),(115,1,'2001:db8:1::73',NULL,NULL),(116,1,'2001:db8:1::74',NULL,NULL),(117,1,'2001:db8:1::75',NULL,NULL),(118,1,'2001:db8:1::76',NULL,NULL),(119,1,'2001:db8:1::77',NULL,NULL),(120,1,'2001:db8:1::78',NULL,NULL),(121,1,'2001:db8:1::79',NULL,NULL),(122,1,'2001:db8:1::7a',NULL,NULL),(123,1,'2001:db8:1::7b',NULL,NULL),(124,1,'2001:db8:1::7c',NULL,NULL),(125,1,'2001:db8:1::7d',NULL,NULL),(126,1,'2001:db8:1::7e',NULL,NULL),(127,1,'2001:db8:1::7f',NULL,NULL),(128,1,'2001:db8:1::80',NULL,NULL),(129,1,'2001:db8:1::81',NULL,NULL),(130,1,'2001:db8:1::82',NULL,NULL),(131,1,'2001:db8:1::83',NULL,NULL),(132,1,'2001:db8:1::84',NULL,NULL),(133,1,'2001:db8:1::85',NULL,NULL),(134,1,'2001:db8:1::86',NULL,NULL),(135,1,'2001:db8:1::87',NULL,NULL),(136,1,'2001:db8:1::88',NULL,NULL),(137,1,'2001:db8:1::89',NULL,NULL),(138,1,'2001:db8:1::8a',NULL,NULL),(139,1,'2001:db8:1::8b',NULL,NULL),(140,1,'2001:db8:1::8c',NULL,NULL),(141,1,'2001:db8:1::8d',NULL,NULL),(142,1,'2001:db8:1::8e',NULL,NULL),(143,1,'2001:db8:1::8f',NULL,NULL),(144,1,'2001:db8:1::90',NULL,NULL),(145,1,'2001:db8:1::91',NULL,NULL),(146,1,'2001:db8:1::92',NULL,NULL),(147,1,'2001:db8:1::93',NULL,NULL),(148,1,'2001:db8:1::94',NULL,NULL),(149,1,'2001:db8:1::95',NULL,NULL),(150,1,'2001:db8:1::96',NULL,NULL),(151,1,'2001:db8:1::97',NULL,NULL),(152,1,'2001:db8:1::98',NULL,NULL),(153,1,'2001:db8:1::99',NULL,NULL),(154,1,'2001:db8:1::9a',NULL,NULL),(155,1,'2001:db8:1::9b',NULL,NULL),(156,1,'2001:db8:1::9c',NULL,NULL),(157,1,'2001:db8:1::9d',NULL,NULL),(158,1,'2001:db8:1::9e',NULL,NULL),(159,1,'2001:db8:1::9f',NULL,NULL),(160,1,'2001:db8:1::a0',NULL,NULL),(161,1,'2001:db8:1::a1',NULL,NULL),(162,1,'2001:db8:1::a2',NULL,NULL),(163,1,'2001:db8:1::a3',NULL,NULL),(164,1,'2001:db8:1::a4',NULL,NULL),(165,1,'2001:db8:1::a5',NULL,NULL),(166,1,'2001:db8:1::a6',NULL,NULL),(167,1,'2001:db8:1::a7',NULL,NULL),(168,1,'2001:db8:1::a8',NULL,NULL),(169,1,'2001:db8:1::a9',NULL,NULL),(170,1,'2001:db8:1::aa',NULL,NULL),(171,1,'2001:db8:1::ab',NULL,NULL),(172,1,'2001:db8:1::ac',NULL,NULL),(173,1,'2001:db8:1::ad',NULL,NULL),(174,1,'2001:db8:1::ae',NULL,NULL),(175,1,'2001:db8:1::af',NULL,NULL),(176,1,'2001:db8:1::b0',NULL,NULL),(177,1,'2001:db8:1::b1',NULL,NULL),(178,1,'2001:db8:1::b2',NULL,NULL),(179,1,'2001:db8:1::b3',NULL,NULL),(180,1,'2001:db8:1::b4',NULL,NULL),(181,1,'2001:db8:1::b5',NULL,NULL),(182,1,'2001:db8:1::b6',NULL,NULL),(183,1,'2001:db8:1::b7',NULL,NULL),(184,1,'2001:db8:1::b8',NULL,NULL),(185,1,'2001:db8:1::b9',NULL,NULL),(186,1,'2001:db8:1::ba',NULL,NULL),(187,1,'2001:db8:1::bb',NULL,NULL),(188,1,'2001:db8:1::bc',NULL,NULL),(189,1,'2001:db8:1::bd',NULL,NULL),(190,1,'2001:db8:1::be',NULL,NULL),(191,1,'2001:db8:1::bf',NULL,NULL),(192,1,'2001:db8:1::c0',NULL,NULL),(193,1,'2001:db8:1::c1',NULL,NULL),(194,1,'2001:db8:1::c2',NULL,NULL),(195,1,'2001:db8:1::c3',NULL,NULL),(196,1,'2001:db8:1::c4',NULL,NULL),(197,1,'2001:db8:1::c5',NULL,NULL),(198,1,'2001:db8:1::c6',NULL,NULL),(199,1,'2001:db8:1::c7',NULL,NULL),(200,1,'2001:db8:1::c8',NULL,NULL),(201,1,'2001:db8:1::c9',NULL,NULL),(202,1,'2001:db8:1::ca',NULL,NULL),(203,1,'2001:db8:1::cb',NULL,NULL),(204,1,'2001:db8:1::cc',NULL,NULL),(205,1,'2001:db8:1::cd',NULL,NULL),(206,1,'2001:db8:1::ce',NULL,NULL),(207,1,'2001:db8:1::cf',NULL,NULL),(208,1,'2001:db8:1::d0',NULL,NULL),(209,1,'2001:db8:1::d1',NULL,NULL),(210,1,'2001:db8:1::d2',NULL,NULL),(211,1,'2001:db8:1::d3',NULL,NULL),(212,1,'2001:db8:1::d4',NULL,NULL),(213,1,'2001:db8:1::d5',NULL,NULL),(214,1,'2001:db8:1::d6',NULL,NULL),(215,1,'2001:db8:1::d7',NULL,NULL),(216,1,'2001:db8:1::d8',NULL,NULL),(217,1,'2001:db8:1::d9',NULL,NULL),(218,1,'2001:db8:1::da',NULL,NULL),(219,1,'2001:db8:1::db',NULL,NULL),(220,1,'2001:db8:1::dc',NULL,NULL),(221,1,'2001:db8:1::dd',NULL,NULL),(222,1,'2001:db8:1::de',NULL,NULL),(223,1,'2001:db8:1::df',NULL,NULL),(224,1,'2001:db8:1::e0',NULL,NULL),(225,1,'2001:db8:1::e1',NULL,NULL),(226,1,'2001:db8:1::e2',NULL,NULL),(227,1,'2001:db8:1::e3',NULL,NULL),(228,1,'2001:db8:1::e4',NULL,NULL),(229,1,'2001:db8:1::e5',NULL,NULL),(230,1,'2001:db8:1::e6',NULL,NULL),(231,1,'2001:db8:1::e7',NULL,NULL),(232,1,'2001:db8:1::e8',NULL,NULL),(233,1,'2001:db8:1::e9',NULL,NULL),(234,1,'2001:db8:1::ea',NULL,NULL),(235,1,'2001:db8:1::eb',NULL,NULL),(236,1,'2001:db8:1::ec',NULL,NULL),(237,1,'2001:db8:1::ed',NULL,NULL),(238,1,'2001:db8:1::ee',NULL,NULL),(239,1,'2001:db8:1::ef',NULL,NULL),(240,1,'2001:db8:1::f0',NULL,NULL),(241,1,'2001:db8:1::f1',NULL,NULL),(242,1,'2001:db8:1::f2',NULL,NULL),(243,1,'2001:db8:1::f3',NULL,NULL),(244,1,'2001:db8:1::f4',NULL,NULL),(245,1,'2001:db8:1::f5',NULL,NULL),(246,1,'2001:db8:1::f6',NULL,NULL),(247,1,'2001:db8:1::f7',NULL,NULL),(248,1,'2001:db8:1::f8',NULL,NULL),(249,1,'2001:db8:1::f9',NULL,NULL),(250,1,'2001:db8:1::fa',NULL,NULL),(251,1,'2001:db8:1::fb',NULL,NULL),(252,1,'2001:db8:1::fc',NULL,NULL),(253,1,'2001:db8:1::fd',NULL,NULL),(254,1,'2001:db8:1::fe',NULL,NULL),(255,1,'2001:db8:1::ff',NULL,NULL),(256,1,'2001:db8:1::100',NULL,NULL),(257,1,'2001:db8:1::101',NULL,NULL),(258,1,'2001:db8:1::102',NULL,NULL),(259,1,'2001:db8:1::103',NULL,NULL),(260,1,'2001:db8:1::104',NULL,NULL),(261,1,'2001:db8:1::105',NULL,NULL),(262,1,'2001:db8:1::106',NULL,NULL),(263,1,'2001:db8:1::107',NULL,NULL),(264,1,'2001:db8:1::108',NULL,NULL),(265,1,'2001:db8:1::109',NULL,NULL),(266,1,'2001:db8:1::10a',NULL,NULL),(267,1,'2001:db8:1::10b',NULL,NULL),(268,1,'2001:db8:1::10c',NULL,NULL),(269,1,'2001:db8:1::10d',NULL,NULL),(270,1,'2001:db8:1::10e',NULL,NULL),(271,1,'2001:db8:1::10f',NULL,NULL),(272,1,'2001:db8:1::110',NULL,NULL),(273,1,'2001:db8:1::111',NULL,NULL),(274,1,'2001:db8:1::112',NULL,NULL),(275,1,'2001:db8:1::113',NULL,NULL),(276,1,'2001:db8:1::114',NULL,NULL),(277,1,'2001:db8:1::115',NULL,NULL),(278,1,'2001:db8:1::116',NULL,NULL),(279,1,'2001:db8:1::117',NULL,NULL),(280,1,'2001:db8:1::118',NULL,NULL),(281,1,'2001:db8:1::119',NULL,NULL),(282,1,'2001:db8:1::11a',NULL,NULL),(283,1,'2001:db8:1::11b',NULL,NULL),(284,1,'2001:db8:1::11c',NULL,NULL),(285,1,'2001:db8:1::11d',NULL,NULL),(286,1,'2001:db8:1::11e',NULL,NULL),(287,1,'2001:db8:1::11f',NULL,NULL),(288,1,'2001:db8:1::120',NULL,NULL),(289,1,'2001:db8:1::121',NULL,NULL),(290,1,'2001:db8:1::122',NULL,NULL),(291,1,'2001:db8:1::123',NULL,NULL),(292,1,'2001:db8:1::124',NULL,NULL),(293,1,'2001:db8:1::125',NULL,NULL),(294,1,'2001:db8:1::126',NULL,NULL),(295,1,'2001:db8:1::127',NULL,NULL),(296,1,'2001:db8:1::128',NULL,NULL),(297,1,'2001:db8:1::129',NULL,NULL),(298,1,'2001:db8:1::12a',NULL,NULL),(299,1,'2001:db8:1::12b',NULL,NULL),(300,1,'2001:db8:1::12c',NULL,NULL),(301,1,'2001:db8:1::12d',NULL,NULL),(302,1,'2001:db8:1::12e',NULL,NULL),(303,1,'2001:db8:1::12f',NULL,NULL),(304,1,'2001:db8:1::130',NULL,NULL),(305,1,'2001:db8:1::131',NULL,NULL),(306,1,'2001:db8:1::132',NULL,NULL),(307,1,'2001:db8:1::133',NULL,NULL),(308,1,'2001:db8:1::134',NULL,NULL),(309,1,'2001:db8:1::135',NULL,NULL),(310,1,'2001:db8:1::136',NULL,NULL),(311,1,'2001:db8:1::137',NULL,NULL),(312,1,'2001:db8:1::138',NULL,NULL),(313,1,'2001:db8:1::139',NULL,NULL),(314,1,'2001:db8:1::13a',NULL,NULL),(315,1,'2001:db8:1::13b',NULL,NULL),(316,1,'2001:db8:1::13c',NULL,NULL),(317,1,'2001:db8:1::13d',NULL,NULL),(318,1,'2001:db8:1::13e',NULL,NULL),(319,1,'2001:db8:1::13f',NULL,NULL),(320,1,'2001:db8:1::140',NULL,NULL),(321,1,'2001:db8:1::141',NULL,NULL),(322,1,'2001:db8:1::142',NULL,NULL),(323,1,'2001:db8:1::143',NULL,NULL),(324,1,'2001:db8:1::144',NULL,NULL),(325,1,'2001:db8:1::145',NULL,NULL),(326,1,'2001:db8:1::146',NULL,NULL),(327,1,'2001:db8:1::147',NULL,NULL),(328,1,'2001:db8:1::148',NULL,NULL),(329,1,'2001:db8:1::149',NULL,NULL),(330,1,'2001:db8:1::14a',NULL,NULL),(331,1,'2001:db8:1::14b',NULL,NULL),(332,1,'2001:db8:1::14c',NULL,NULL),(333,1,'2001:db8:1::14d',NULL,NULL),(334,1,'2001:db8:1::14e',NULL,NULL),(335,1,'2001:db8:1::14f',NULL,NULL),(336,1,'2001:db8:1::150',NULL,NULL),(337,1,'2001:db8:1::151',NULL,NULL),(338,1,'2001:db8:1::152',NULL,NULL),(339,1,'2001:db8:1::153',NULL,NULL),(340,1,'2001:db8:1::154',NULL,NULL),(341,1,'2001:db8:1::155',NULL,NULL),(342,1,'2001:db8:1::156',NULL,NULL),(343,1,'2001:db8:1::157',NULL,NULL),(344,1,'2001:db8:1::158',NULL,NULL),(345,1,'2001:db8:1::159',NULL,NULL),(346,1,'2001:db8:1::15a',NULL,NULL),(347,1,'2001:db8:1::15b',NULL,NULL),(348,1,'2001:db8:1::15c',NULL,NULL),(349,1,'2001:db8:1::15d',NULL,NULL),(350,1,'2001:db8:1::15e',NULL,NULL),(351,1,'2001:db8:1::15f',NULL,NULL),(352,1,'2001:db8:1::160',NULL,NULL),(353,1,'2001:db8:1::161',NULL,NULL),(354,1,'2001:db8:1::162',NULL,NULL),(355,1,'2001:db8:1::163',NULL,NULL),(356,1,'2001:db8:1::164',NULL,NULL),(357,1,'2001:db8:1::165',NULL,NULL),(358,1,'2001:db8:1::166',NULL,NULL),(359,1,'2001:db8:1::167',NULL,NULL),(360,1,'2001:db8:1::168',NULL,NULL),(361,1,'2001:db8:1::169',NULL,NULL),(362,1,'2001:db8:1::16a',NULL,NULL),(363,1,'2001:db8:1::16b',NULL,NULL),(364,1,'2001:db8:1::16c',NULL,NULL),(365,1,'2001:db8:1::16d',NULL,NULL),(366,1,'2001:db8:1::16e',NULL,NULL),(367,1,'2001:db8:1::16f',NULL,NULL),(368,1,'2001:db8:1::170',NULL,NULL),(369,1,'2001:db8:1::171',NULL,NULL),(370,1,'2001:db8:1::172',NULL,NULL),(371,1,'2001:db8:1::173',NULL,NULL),(372,1,'2001:db8:1::174',NULL,NULL),(373,1,'2001:db8:1::175',NULL,NULL),(374,1,'2001:db8:1::176',NULL,NULL),(375,1,'2001:db8:1::177',NULL,NULL),(376,1,'2001:db8:1::178',NULL,NULL),(377,1,'2001:db8:1::179',NULL,NULL),(378,1,'2001:db8:1::17a',NULL,NULL),(379,1,'2001:db8:1::17b',NULL,NULL),(380,1,'2001:db8:1::17c',NULL,NULL),(381,1,'2001:db8:1::17d',NULL,NULL),(382,1,'2001:db8:1::17e',NULL,NULL),(383,1,'2001:db8:1::17f',NULL,NULL),(384,1,'2001:db8:1::180',NULL,NULL),(385,1,'2001:db8:1::181',NULL,NULL),(386,1,'2001:db8:1::182',NULL,NULL),(387,1,'2001:db8:1::183',NULL,NULL),(388,1,'2001:db8:1::184',NULL,NULL),(389,1,'2001:db8:1::185',NULL,NULL),(390,1,'2001:db8:1::186',NULL,NULL),(391,1,'2001:db8:1::187',NULL,NULL),(392,1,'2001:db8:1::188',NULL,NULL),(393,1,'2001:db8:1::189',NULL,NULL),(394,1,'2001:db8:1::18a',NULL,NULL),(395,1,'2001:db8:1::18b',NULL,NULL),(396,1,'2001:db8:1::18c',NULL,NULL),(397,1,'2001:db8:1::18d',NULL,NULL),(398,1,'2001:db8:1::18e',NULL,NULL),(399,1,'2001:db8:1::18f',NULL,NULL),(400,1,'2001:db8:1::190',NULL,NULL),(401,2,'2001:db8:2::1',NULL,NULL),(402,2,'2001:db8:2::2',NULL,NULL),(403,2,'2001:db8:2::3',NULL,NULL),(404,2,'2001:db8:2::4',NULL,NULL),(405,2,'2001:db8:2::5',NULL,NULL),(406,2,'2001:db8:2::6',NULL,NULL),(407,2,'2001:db8:2::7',NULL,NULL),(408,2,'2001:db8:2::8',NULL,NULL),(409,2,'2001:db8:2::9',NULL,NULL),(410,2,'2001:db8:2::a',NULL,NULL),(411,2,'2001:db8:2::b',NULL,NULL),(412,2,'2001:db8:2::c',NULL,NULL),(413,2,'2001:db8:2::d',NULL,NULL),(414,2,'2001:db8:2::e',NULL,NULL),(415,2,'2001:db8:2::f',NULL,NULL),(416,2,'2001:db8:2::10',NULL,NULL),(417,2,'2001:db8:2::11',NULL,NULL),(418,2,'2001:db8:2::12',NULL,NULL),(419,2,'2001:db8:2::13',NULL,NULL),(420,2,'2001:db8:2::14',NULL,NULL),(421,2,'2001:db8:2::15',NULL,NULL),(422,2,'2001:db8:2::16',NULL,NULL),(423,2,'2001:db8:2::17',NULL,NULL),(424,2,'2001:db8:2::18',NULL,NULL),(425,2,'2001:db8:2::19',NULL,NULL),(426,2,'2001:db8:2::1a',NULL,NULL),(427,2,'2001:db8:2::1b',NULL,NULL),(428,2,'2001:db8:2::1c',NULL,NULL),(429,2,'2001:db8:2::1d',NULL,NULL),(430,2,'2001:db8:2::1e',NULL,NULL),(431,2,'2001:db8:2::1f',NULL,NULL),(432,2,'2001:db8:2::20',NULL,NULL),(433,2,'2001:db8:2::21',NULL,NULL),(434,2,'2001:db8:2::22',NULL,NULL),(435,2,'2001:db8:2::23',NULL,NULL),(436,2,'2001:db8:2::24',NULL,NULL),(437,2,'2001:db8:2::25',NULL,NULL),(438,2,'2001:db8:2::26',NULL,NULL),(439,2,'2001:db8:2::27',NULL,NULL),(440,2,'2001:db8:2::28',NULL,NULL),(441,2,'2001:db8:2::29',NULL,NULL),(442,2,'2001:db8:2::2a',NULL,NULL),(443,2,'2001:db8:2::2b',NULL,NULL),(444,2,'2001:db8:2::2c',NULL,NULL),(445,2,'2001:db8:2::2d',NULL,NULL),(446,2,'2001:db8:2::2e',NULL,NULL),(447,2,'2001:db8:2::2f',NULL,NULL),(448,2,'2001:db8:2::30',NULL,NULL),(449,2,'2001:db8:2::31',NULL,NULL),(450,2,'2001:db8:2::32',NULL,NULL),(451,2,'2001:db8:2::33',NULL,NULL),(452,2,'2001:db8:2::34',NULL,NULL),(453,2,'2001:db8:2::35',NULL,NULL),(454,2,'2001:db8:2::36',NULL,NULL),(455,2,'2001:db8:2::37',NULL,NULL),(456,2,'2001:db8:2::38',NULL,NULL),(457,2,'2001:db8:2::39',NULL,NULL),(458,2,'2001:db8:2::3a',NULL,NULL),(459,2,'2001:db8:2::3b',NULL,NULL),(460,2,'2001:db8:2::3c',NULL,NULL),(461,2,'2001:db8:2::3d',NULL,NULL),(462,2,'2001:db8:2::3e',NULL,NULL),(463,2,'2001:db8:2::3f',NULL,NULL),(464,2,'2001:db8:2::40',NULL,NULL),(465,2,'2001:db8:2::41',NULL,NULL),(466,2,'2001:db8:2::42',NULL,NULL),(467,2,'2001:db8:2::43',NULL,NULL),(468,2,'2001:db8:2::44',NULL,NULL),(469,2,'2001:db8:2::45',NULL,NULL),(470,2,'2001:db8:2::46',NULL,NULL),(471,2,'2001:db8:2::47',NULL,NULL),(472,2,'2001:db8:2::48',NULL,NULL),(473,2,'2001:db8:2::49',NULL,NULL),(474,2,'2001:db8:2::4a',NULL,NULL),(475,2,'2001:db8:2::4b',NULL,NULL),(476,2,'2001:db8:2::4c',NULL,NULL),(477,2,'2001:db8:2::4d',NULL,NULL),(478,2,'2001:db8:2::4e',NULL,NULL),(479,2,'2001:db8:2::4f',NULL,NULL),(480,2,'2001:db8:2::50',NULL,NULL),(481,2,'2001:db8:2::51',NULL,NULL),(482,2,'2001:db8:2::52',NULL,NULL),(483,2,'2001:db8:2::53',NULL,NULL),(484,2,'2001:db8:2::54',NULL,NULL),(485,2,'2001:db8:2::55',NULL,NULL),(486,2,'2001:db8:2::56',NULL,NULL),(487,2,'2001:db8:2::57',NULL,NULL),(488,2,'2001:db8:2::58',NULL,NULL),(489,2,'2001:db8:2::59',NULL,NULL),(490,2,'2001:db8:2::5a',NULL,NULL),(491,2,'2001:db8:2::5b',NULL,NULL),(492,2,'2001:db8:2::5c',NULL,NULL),(493,2,'2001:db8:2::5d',NULL,NULL),(494,2,'2001:db8:2::5e',NULL,NULL),(495,2,'2001:db8:2::5f',NULL,NULL),(496,2,'2001:db8:2::60',NULL,NULL),(497,2,'2001:db8:2::61',NULL,NULL),(498,2,'2001:db8:2::62',NULL,NULL),(499,2,'2001:db8:2::63',NULL,NULL),(500,2,'2001:db8:2::64',NULL,NULL),(501,2,'2001:db8:2::65',NULL,NULL),(502,2,'2001:db8:2::66',NULL,NULL),(503,2,'2001:db8:2::67',NULL,NULL),(504,2,'2001:db8:2::68',NULL,NULL),(505,2,'2001:db8:2::69',NULL,NULL),(506,2,'2001:db8:2::6a',NULL,NULL),(507,2,'2001:db8:2::6b',NULL,NULL),(508,2,'2001:db8:2::6c',NULL,NULL),(509,2,'2001:db8:2::6d',NULL,NULL),(510,2,'2001:db8:2::6e',NULL,NULL),(511,2,'2001:db8:2::6f',NULL,NULL),(512,2,'2001:db8:2::70',NULL,NULL),(513,2,'2001:db8:2::71',NULL,NULL),(514,2,'2001:db8:2::72',NULL,NULL),(515,2,'2001:db8:2::73',NULL,NULL),(516,2,'2001:db8:2::74',NULL,NULL),(517,2,'2001:db8:2::75',NULL,NULL),(518,2,'2001:db8:2::76',NULL,NULL),(519,2,'2001:db8:2::77',NULL,NULL),(520,2,'2001:db8:2::78',NULL,NULL),(521,2,'2001:db8:2::79',NULL,NULL),(522,2,'2001:db8:2::7a',NULL,NULL),(523,2,'2001:db8:2::7b',NULL,NULL),(524,2,'2001:db8:2::7c',NULL,NULL),(525,2,'2001:db8:2::7d',NULL,NULL),(526,2,'2001:db8:2::7e',NULL,NULL),(527,2,'2001:db8:2::7f',NULL,NULL),(528,2,'2001:db8:2::80',NULL,NULL),(529,2,'2001:db8:2::81',NULL,NULL),(530,2,'2001:db8:2::82',NULL,NULL),(531,2,'2001:db8:2::83',NULL,NULL),(532,2,'2001:db8:2::84',NULL,NULL),(533,2,'2001:db8:2::85',NULL,NULL),(534,2,'2001:db8:2::86',NULL,NULL),(535,2,'2001:db8:2::87',NULL,NULL),(536,2,'2001:db8:2::88',NULL,NULL),(537,2,'2001:db8:2::89',NULL,NULL),(538,2,'2001:db8:2::8a',NULL,NULL),(539,2,'2001:db8:2::8b',NULL,NULL),(540,2,'2001:db8:2::8c',NULL,NULL),(541,2,'2001:db8:2::8d',NULL,NULL),(542,2,'2001:db8:2::8e',NULL,NULL),(543,2,'2001:db8:2::8f',NULL,NULL),(544,2,'2001:db8:2::90',NULL,NULL),(545,2,'2001:db8:2::91',NULL,NULL),(546,2,'2001:db8:2::92',NULL,NULL),(547,2,'2001:db8:2::93',NULL,NULL),(548,2,'2001:db8:2::94',NULL,NULL),(549,2,'2001:db8:2::95',NULL,NULL),(550,2,'2001:db8:2::96',NULL,NULL),(551,2,'2001:db8:2::97',NULL,NULL),(552,2,'2001:db8:2::98',NULL,NULL),(553,2,'2001:db8:2::99',NULL,NULL),(554,2,'2001:db8:2::9a',NULL,NULL),(555,2,'2001:db8:2::9b',NULL,NULL),(556,2,'2001:db8:2::9c',NULL,NULL),(557,2,'2001:db8:2::9d',NULL,NULL),(558,2,'2001:db8:2::9e',NULL,NULL),(559,2,'2001:db8:2::9f',NULL,NULL),(560,2,'2001:db8:2::a0',NULL,NULL),(561,2,'2001:db8:2::a1',NULL,NULL),(562,2,'2001:db8:2::a2',NULL,NULL),(563,2,'2001:db8:2::a3',NULL,NULL),(564,2,'2001:db8:2::a4',NULL,NULL),(565,2,'2001:db8:2::a5',NULL,NULL),(566,2,'2001:db8:2::a6',NULL,NULL),(567,2,'2001:db8:2::a7',NULL,NULL),(568,2,'2001:db8:2::a8',NULL,NULL),(569,2,'2001:db8:2::a9',NULL,NULL),(570,2,'2001:db8:2::aa',NULL,NULL),(571,2,'2001:db8:2::ab',NULL,NULL),(572,2,'2001:db8:2::ac',NULL,NULL),(573,2,'2001:db8:2::ad',NULL,NULL),(574,2,'2001:db8:2::ae',NULL,NULL),(575,2,'2001:db8:2::af',NULL,NULL),(576,2,'2001:db8:2::b0',NULL,NULL),(577,2,'2001:db8:2::b1',NULL,NULL),(578,2,'2001:db8:2::b2',NULL,NULL),(579,2,'2001:db8:2::b3',NULL,NULL),(580,2,'2001:db8:2::b4',NULL,NULL),(581,2,'2001:db8:2::b5',NULL,NULL),(582,2,'2001:db8:2::b6',NULL,NULL),(583,2,'2001:db8:2::b7',NULL,NULL),(584,2,'2001:db8:2::b8',NULL,NULL),(585,2,'2001:db8:2::b9',NULL,NULL),(586,2,'2001:db8:2::ba',NULL,NULL),(587,2,'2001:db8:2::bb',NULL,NULL),(588,2,'2001:db8:2::bc',NULL,NULL),(589,2,'2001:db8:2::bd',NULL,NULL),(590,2,'2001:db8:2::be',NULL,NULL),(591,2,'2001:db8:2::bf',NULL,NULL),(592,2,'2001:db8:2::c0',NULL,NULL),(593,2,'2001:db8:2::c1',NULL,NULL),(594,2,'2001:db8:2::c2',NULL,NULL),(595,2,'2001:db8:2::c3',NULL,NULL),(596,2,'2001:db8:2::c4',NULL,NULL),(597,2,'2001:db8:2::c5',NULL,NULL),(598,2,'2001:db8:2::c6',NULL,NULL),(599,2,'2001:db8:2::c7',NULL,NULL),(600,2,'2001:db8:2::c8',NULL,NULL),(601,2,'2001:db8:2::c9',NULL,NULL),(602,2,'2001:db8:2::ca',NULL,NULL),(603,2,'2001:db8:2::cb',NULL,NULL),(604,2,'2001:db8:2::cc',NULL,NULL),(605,2,'2001:db8:2::cd',NULL,NULL),(606,2,'2001:db8:2::ce',NULL,NULL),(607,2,'2001:db8:2::cf',NULL,NULL),(608,2,'2001:db8:2::d0',NULL,NULL),(609,2,'2001:db8:2::d1',NULL,NULL),(610,2,'2001:db8:2::d2',NULL,NULL),(611,2,'2001:db8:2::d3',NULL,NULL),(612,2,'2001:db8:2::d4',NULL,NULL),(613,2,'2001:db8:2::d5',NULL,NULL),(614,2,'2001:db8:2::d6',NULL,NULL),(615,2,'2001:db8:2::d7',NULL,NULL),(616,2,'2001:db8:2::d8',NULL,NULL),(617,2,'2001:db8:2::d9',NULL,NULL),(618,2,'2001:db8:2::da',NULL,NULL),(619,2,'2001:db8:2::db',NULL,NULL),(620,2,'2001:db8:2::dc',NULL,NULL),(621,2,'2001:db8:2::dd',NULL,NULL),(622,2,'2001:db8:2::de',NULL,NULL),(623,2,'2001:db8:2::df',NULL,NULL),(624,2,'2001:db8:2::e0',NULL,NULL),(625,2,'2001:db8:2::e1',NULL,NULL),(626,2,'2001:db8:2::e2',NULL,NULL),(627,2,'2001:db8:2::e3',NULL,NULL),(628,2,'2001:db8:2::e4',NULL,NULL),(629,2,'2001:db8:2::e5',NULL,NULL),(630,2,'2001:db8:2::e6',NULL,NULL),(631,2,'2001:db8:2::e7',NULL,NULL),(632,2,'2001:db8:2::e8',NULL,NULL),(633,2,'2001:db8:2::e9',NULL,NULL),(634,2,'2001:db8:2::ea',NULL,NULL),(635,2,'2001:db8:2::eb',NULL,NULL),(636,2,'2001:db8:2::ec',NULL,NULL),(637,2,'2001:db8:2::ed',NULL,NULL),(638,2,'2001:db8:2::ee',NULL,NULL),(639,2,'2001:db8:2::ef',NULL,NULL),(640,2,'2001:db8:2::f0',NULL,NULL),(641,2,'2001:db8:2::f1',NULL,NULL),(642,2,'2001:db8:2::f2',NULL,NULL),(643,2,'2001:db8:2::f3',NULL,NULL),(644,2,'2001:db8:2::f4',NULL,NULL),(645,2,'2001:db8:2::f5',NULL,NULL),(646,2,'2001:db8:2::f6',NULL,NULL),(647,2,'2001:db8:2::f7',NULL,NULL),(648,2,'2001:db8:2::f8',NULL,NULL),(649,2,'2001:db8:2::f9',NULL,NULL),(650,2,'2001:db8:2::fa',NULL,NULL),(651,2,'2001:db8:2::fb',NULL,NULL),(652,2,'2001:db8:2::fc',NULL,NULL),(653,2,'2001:db8:2::fd',NULL,NULL),(654,2,'2001:db8:2::fe',NULL,NULL),(655,2,'2001:db8:2::ff',NULL,NULL),(656,2,'2001:db8:2::100',NULL,NULL),(657,2,'2001:db8:2::101',NULL,NULL),(658,2,'2001:db8:2::102',NULL,NULL),(659,2,'2001:db8:2::103',NULL,NULL),(660,2,'2001:db8:2::104',NULL,NULL),(661,2,'2001:db8:2::105',NULL,NULL),(662,2,'2001:db8:2::106',NULL,NULL),(663,2,'2001:db8:2::107',NULL,NULL),(664,2,'2001:db8:2::108',NULL,NULL),(665,2,'2001:db8:2::109',NULL,NULL),(666,2,'2001:db8:2::10a',NULL,NULL),(667,2,'2001:db8:2::10b',NULL,NULL),(668,2,'2001:db8:2::10c',NULL,NULL),(669,2,'2001:db8:2::10d',NULL,NULL),(670,2,'2001:db8:2::10e',NULL,NULL),(671,2,'2001:db8:2::10f',NULL,NULL),(672,2,'2001:db8:2::110',NULL,NULL),(673,2,'2001:db8:2::111',NULL,NULL),(674,2,'2001:db8:2::112',NULL,NULL),(675,2,'2001:db8:2::113',NULL,NULL),(676,2,'2001:db8:2::114',NULL,NULL),(677,2,'2001:db8:2::115',NULL,NULL),(678,2,'2001:db8:2::116',NULL,NULL),(679,2,'2001:db8:2::117',NULL,NULL),(680,2,'2001:db8:2::118',NULL,NULL),(681,2,'2001:db8:2::119',NULL,NULL),(682,2,'2001:db8:2::11a',NULL,NULL),(683,2,'2001:db8:2::11b',NULL,NULL),(684,2,'2001:db8:2::11c',NULL,NULL),(685,2,'2001:db8:2::11d',NULL,NULL),(686,2,'2001:db8:2::11e',NULL,NULL),(687,2,'2001:db8:2::11f',NULL,NULL),(688,2,'2001:db8:2::120',NULL,NULL),(689,2,'2001:db8:2::121',NULL,NULL),(690,2,'2001:db8:2::122',NULL,NULL),(691,2,'2001:db8:2::123',NULL,NULL),(692,2,'2001:db8:2::124',NULL,NULL),(693,2,'2001:db8:2::125',NULL,NULL),(694,2,'2001:db8:2::126',NULL,NULL),(695,2,'2001:db8:2::127',NULL,NULL),(696,2,'2001:db8:2::128',NULL,NULL),(697,2,'2001:db8:2::129',NULL,NULL),(698,2,'2001:db8:2::12a',NULL,NULL),(699,2,'2001:db8:2::12b',NULL,NULL),(700,2,'2001:db8:2::12c',NULL,NULL),(701,2,'2001:db8:2::12d',NULL,NULL),(702,2,'2001:db8:2::12e',NULL,NULL),(703,2,'2001:db8:2::12f',NULL,NULL),(704,2,'2001:db8:2::130',NULL,NULL),(705,2,'2001:db8:2::131',NULL,NULL),(706,2,'2001:db8:2::132',NULL,NULL),(707,2,'2001:db8:2::133',NULL,NULL),(708,2,'2001:db8:2::134',NULL,NULL),(709,2,'2001:db8:2::135',NULL,NULL),(710,2,'2001:db8:2::136',NULL,NULL),(711,2,'2001:db8:2::137',NULL,NULL),(712,2,'2001:db8:2::138',NULL,NULL),(713,2,'2001:db8:2::139',NULL,NULL),(714,2,'2001:db8:2::13a',NULL,NULL),(715,2,'2001:db8:2::13b',NULL,NULL),(716,2,'2001:db8:2::13c',NULL,NULL),(717,2,'2001:db8:2::13d',NULL,NULL),(718,2,'2001:db8:2::13e',NULL,NULL),(719,2,'2001:db8:2::13f',NULL,NULL),(720,2,'2001:db8:2::140',NULL,NULL),(721,2,'2001:db8:2::141',NULL,NULL),(722,2,'2001:db8:2::142',NULL,NULL),(723,2,'2001:db8:2::143',NULL,NULL),(724,2,'2001:db8:2::144',NULL,NULL),(725,2,'2001:db8:2::145',NULL,NULL),(726,2,'2001:db8:2::146',NULL,NULL),(727,2,'2001:db8:2::147',NULL,NULL),(728,2,'2001:db8:2::148',NULL,NULL),(729,2,'2001:db8:2::149',NULL,NULL),(730,2,'2001:db8:2::14a',NULL,NULL),(731,2,'2001:db8:2::14b',NULL,NULL),(732,2,'2001:db8:2::14c',NULL,NULL),(733,2,'2001:db8:2::14d',NULL,NULL),(734,2,'2001:db8:2::14e',NULL,NULL),(735,2,'2001:db8:2::14f',NULL,NULL),(736,2,'2001:db8:2::150',NULL,NULL),(737,2,'2001:db8:2::151',NULL,NULL),(738,2,'2001:db8:2::152',NULL,NULL),(739,2,'2001:db8:2::153',NULL,NULL),(740,2,'2001:db8:2::154',NULL,NULL),(741,2,'2001:db8:2::155',NULL,NULL),(742,2,'2001:db8:2::156',NULL,NULL),(743,2,'2001:db8:2::157',NULL,NULL),(744,2,'2001:db8:2::158',NULL,NULL),(745,2,'2001:db8:2::159',NULL,NULL),(746,2,'2001:db8:2::15a',NULL,NULL),(747,2,'2001:db8:2::15b',NULL,NULL),(748,2,'2001:db8:2::15c',NULL,NULL),(749,2,'2001:db8:2::15d',NULL,NULL),(750,2,'2001:db8:2::15e',NULL,NULL),(751,2,'2001:db8:2::15f',NULL,NULL),(752,2,'2001:db8:2::160',NULL,NULL),(753,2,'2001:db8:2::161',NULL,NULL),(754,2,'2001:db8:2::162',NULL,NULL),(755,2,'2001:db8:2::163',NULL,NULL),(756,2,'2001:db8:2::164',NULL,NULL),(757,2,'2001:db8:2::165',NULL,NULL),(758,2,'2001:db8:2::166',NULL,NULL),(759,2,'2001:db8:2::167',NULL,NULL),(760,2,'2001:db8:2::168',NULL,NULL),(761,2,'2001:db8:2::169',NULL,NULL),(762,2,'2001:db8:2::16a',NULL,NULL),(763,2,'2001:db8:2::16b',NULL,NULL),(764,2,'2001:db8:2::16c',NULL,NULL),(765,2,'2001:db8:2::16d',NULL,NULL),(766,2,'2001:db8:2::16e',NULL,NULL),(767,2,'2001:db8:2::16f',NULL,NULL),(768,2,'2001:db8:2::170',NULL,NULL),(769,2,'2001:db8:2::171',NULL,NULL),(770,2,'2001:db8:2::172',NULL,NULL),(771,2,'2001:db8:2::173',NULL,NULL),(772,2,'2001:db8:2::174',NULL,NULL),(773,2,'2001:db8:2::175',NULL,NULL),(774,2,'2001:db8:2::176',NULL,NULL),(775,2,'2001:db8:2::177',NULL,NULL),(776,2,'2001:db8:2::178',NULL,NULL),(777,2,'2001:db8:2::179',NULL,NULL),(778,2,'2001:db8:2::17a',NULL,NULL),(779,2,'2001:db8:2::17b',NULL,NULL),(780,2,'2001:db8:2::17c',NULL,NULL),(781,2,'2001:db8:2::17d',NULL,NULL),(782,2,'2001:db8:2::17e',NULL,NULL),(783,2,'2001:db8:2::17f',NULL,NULL),(784,2,'2001:db8:2::180',NULL,NULL),(785,2,'2001:db8:2::181',NULL,NULL),(786,2,'2001:db8:2::182',NULL,NULL),(787,2,'2001:db8:2::183',NULL,NULL),(788,2,'2001:db8:2::184',NULL,NULL),(789,2,'2001:db8:2::185',NULL,NULL),(790,2,'2001:db8:2::186',NULL,NULL),(791,2,'2001:db8:2::187',NULL,NULL),(792,2,'2001:db8:2::188',NULL,NULL),(793,2,'2001:db8:2::189',NULL,NULL),(794,2,'2001:db8:2::18a',NULL,NULL),(795,2,'2001:db8:2::18b',NULL,NULL),(796,2,'2001:db8:2::18c',NULL,NULL),(797,2,'2001:db8:2::18d',NULL,NULL),(798,2,'2001:db8:2::18e',NULL,NULL),(799,2,'2001:db8:2::18f',NULL,NULL),(800,2,'2001:db8:2::190',NULL,NULL); +/*!40000 ALTER TABLE `ipv6address` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `irrdb_asn` +-- + +DROP TABLE IF EXISTS `irrdb_asn`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `irrdb_asn` ( + `id` bigint NOT NULL AUTO_INCREMENT, + `customer_id` int NOT NULL, + `asn` int unsigned NOT NULL, + `protocol` int NOT NULL, + `first_seen` datetime DEFAULT NULL, + `last_seen` datetime DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `custasn` (`asn`,`protocol`,`customer_id`), + KEY `IDX_87BFC5569395C3F3` (`customer_id`), + CONSTRAINT `FK_87BFC5569395C3F3` FOREIGN KEY (`customer_id`) REFERENCES `cust` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=105 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `irrdb_asn` +-- + +LOCK TABLES `irrdb_asn` WRITE; +/*!40000 ALTER TABLE `irrdb_asn` DISABLE KEYS */; +INSERT INTO `irrdb_asn` VALUES (1,4,112,4,'2014-01-06 14:42:49',NULL,NULL,NULL),(2,4,112,6,'2014-01-06 14:42:50',NULL,NULL,NULL),(3,2,112,4,'2014-01-06 14:42:50',NULL,NULL,NULL),(4,2,1213,4,'2014-01-06 14:42:50',NULL,NULL,NULL),(5,2,1921,4,'2014-01-06 14:42:50',NULL,NULL,NULL),(6,2,2128,4,'2014-01-06 14:42:50',NULL,NULL,NULL),(7,2,2850,4,'2014-01-06 14:42:50',NULL,NULL,NULL),(8,2,42310,4,'2014-01-06 14:42:50',NULL,NULL,NULL),(9,2,112,6,'2014-01-06 14:42:51',NULL,NULL,NULL),(10,2,1213,6,'2014-01-06 14:42:51',NULL,NULL,NULL),(11,2,1921,6,'2014-01-06 14:42:51',NULL,NULL,NULL),(12,2,2128,6,'2014-01-06 14:42:51',NULL,NULL,NULL),(13,2,2850,6,'2014-01-06 14:42:51',NULL,NULL,NULL),(14,2,42310,6,'2014-01-06 14:42:51',NULL,NULL,NULL),(15,5,11521,4,'2014-01-06 14:42:51',NULL,NULL,NULL),(16,5,25441,4,'2014-01-06 14:42:51',NULL,NULL,NULL),(17,5,34317,4,'2014-01-06 14:42:51',NULL,NULL,NULL),(18,5,35272,4,'2014-01-06 14:42:51',NULL,NULL,NULL),(19,5,39064,4,'2014-01-06 14:42:51',NULL,NULL,NULL),(20,5,43178,4,'2014-01-06 14:42:51',NULL,NULL,NULL),(21,5,43610,4,'2014-01-06 14:42:51',NULL,NULL,NULL),(22,5,47615,4,'2014-01-06 14:42:51',NULL,NULL,NULL),(23,5,48342,4,'2014-01-06 14:42:51',NULL,NULL,NULL),(24,5,49573,4,'2014-01-06 14:42:51',NULL,NULL,NULL),(25,5,197853,4,'2014-01-06 14:42:51',NULL,NULL,NULL),(26,5,197904,4,'2014-01-06 14:42:51',NULL,NULL,NULL),(27,5,11521,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(28,5,25441,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(29,5,34317,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(30,5,35272,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(31,5,39064,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(32,5,43178,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(33,5,43610,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(34,5,47615,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(35,5,48342,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(36,5,49573,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(37,5,197853,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(38,5,197904,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(39,3,27,4,'2014-01-06 14:42:52',NULL,NULL,NULL),(40,3,42,4,'2014-01-06 14:42:52',NULL,NULL,NULL),(41,3,187,4,'2014-01-06 14:42:52',NULL,NULL,NULL),(42,3,297,4,'2014-01-06 14:42:52',NULL,NULL,NULL),(43,3,715,4,'2014-01-06 14:42:52',NULL,NULL,NULL),(44,3,3856,4,'2014-01-06 14:42:52',NULL,NULL,NULL),(45,3,7251,4,'2014-01-06 14:42:52',NULL,NULL,NULL),(46,3,13202,4,'2014-01-06 14:42:52',NULL,NULL,NULL),(47,3,16327,4,'2014-01-06 14:42:52',NULL,NULL,NULL),(48,3,16668,4,'2014-01-06 14:42:52',NULL,NULL,NULL),(49,3,16686,4,'2014-01-06 14:42:52',NULL,NULL,NULL),(50,3,20144,4,'2014-01-06 14:42:52',NULL,NULL,NULL),(51,3,20539,4,'2014-01-06 14:42:52',NULL,NULL,NULL),(52,3,21312,4,'2014-01-06 14:42:52',NULL,NULL,NULL),(53,3,24999,4,'2014-01-06 14:42:52',NULL,NULL,NULL),(54,3,27678,4,'2014-01-06 14:42:52',NULL,NULL,NULL),(55,3,32978,4,'2014-01-06 14:42:52',NULL,NULL,NULL),(56,3,32979,4,'2014-01-06 14:42:52',NULL,NULL,NULL),(57,3,35160,4,'2014-01-06 14:42:52',NULL,NULL,NULL),(58,3,38052,4,'2014-01-06 14:42:52',NULL,NULL,NULL),(59,3,44876,4,'2014-01-06 14:42:52',NULL,NULL,NULL),(60,3,45170,4,'2014-01-06 14:42:52',NULL,NULL,NULL),(61,3,45494,4,'2014-01-06 14:42:52',NULL,NULL,NULL),(62,3,48582,4,'2014-01-06 14:42:52',NULL,NULL,NULL),(63,3,48892,4,'2014-01-06 14:42:52',NULL,NULL,NULL),(64,3,50843,4,'2014-01-06 14:42:52',NULL,NULL,NULL),(65,3,51874,4,'2014-01-06 14:42:52',NULL,NULL,NULL),(66,3,52234,4,'2014-01-06 14:42:52',NULL,NULL,NULL),(67,3,52306,4,'2014-01-06 14:42:52',NULL,NULL,NULL),(68,3,54145,4,'2014-01-06 14:42:52',NULL,NULL,NULL),(69,3,59464,4,'2014-01-06 14:42:52',NULL,NULL,NULL),(70,3,60313,4,'2014-01-06 14:42:52',NULL,NULL,NULL),(71,3,197058,4,'2014-01-06 14:42:52',NULL,NULL,NULL),(72,3,27,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(73,3,42,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(74,3,187,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(75,3,297,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(76,3,715,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(77,3,3856,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(78,3,7251,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(79,3,13202,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(80,3,16327,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(81,3,16668,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(82,3,16686,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(83,3,20144,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(84,3,20539,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(85,3,21312,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(86,3,24999,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(87,3,27678,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(88,3,32978,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(89,3,32979,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(90,3,35160,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(91,3,38052,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(92,3,44876,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(93,3,45170,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(94,3,45494,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(95,3,48582,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(96,3,48892,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(97,3,50843,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(98,3,51874,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(99,3,52234,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(100,3,52306,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(101,3,54145,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(102,3,59464,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(103,3,60313,6,'2014-01-06 14:42:52',NULL,NULL,NULL),(104,3,197058,6,'2014-01-06 14:42:52',NULL,NULL,NULL); +/*!40000 ALTER TABLE `irrdb_asn` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `irrdb_prefix` +-- + +DROP TABLE IF EXISTS `irrdb_prefix`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `irrdb_prefix` ( + `id` bigint NOT NULL AUTO_INCREMENT, + `customer_id` int NOT NULL, + `prefix` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL, + `protocol` int NOT NULL, + `first_seen` datetime DEFAULT NULL, + `last_seen` datetime DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `custprefix` (`prefix`,`protocol`,`customer_id`), + KEY `IDX_FE73E77C9395C3F3` (`customer_id`), + CONSTRAINT `FK_FE73E77C9395C3F3` FOREIGN KEY (`customer_id`) REFERENCES `cust` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=649 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `irrdb_prefix` +-- + +LOCK TABLES `irrdb_prefix` WRITE; +/*!40000 ALTER TABLE `irrdb_prefix` DISABLE KEYS */; +INSERT INTO `irrdb_prefix` VALUES (1,4,'192.175.48.0/24',4,'2014-01-06 14:42:30','2014-01-06 14:42:30',NULL,NULL),(2,2,'4.53.84.128/26',4,'2014-01-06 14:42:31','2014-01-06 14:42:31',NULL,NULL),(3,2,'4.53.146.192/26',4,'2014-01-06 14:42:31','2014-01-06 14:42:31',NULL,NULL),(4,2,'77.72.72.0/21',4,'2014-01-06 14:42:31','2014-01-06 14:42:31',NULL,NULL),(5,2,'87.32.0.0/12',4,'2014-01-06 14:42:31','2014-01-06 14:42:31',NULL,NULL),(6,2,'91.123.224.0/20',4,'2014-01-06 14:42:31','2014-01-06 14:42:31',NULL,NULL),(7,2,'134.226.0.0/16',4,'2014-01-06 14:42:31','2014-01-06 14:42:31',NULL,NULL),(8,2,'136.201.0.0/16',4,'2014-01-06 14:42:31','2014-01-06 14:42:31',NULL,NULL),(9,2,'136.206.0.0/16',4,'2014-01-06 14:42:31','2014-01-06 14:42:31',NULL,NULL),(10,2,'137.43.0.0/16',4,'2014-01-06 14:42:31','2014-01-06 14:42:31',NULL,NULL),(11,2,'140.203.0.0/16',4,'2014-01-06 14:42:31','2014-01-06 14:42:31',NULL,NULL),(12,2,'143.239.0.0/16',4,'2014-01-06 14:42:31','2014-01-06 14:42:31',NULL,NULL),(13,2,'147.252.0.0/16',4,'2014-01-06 14:42:31','2014-01-06 14:42:31',NULL,NULL),(14,2,'149.153.0.0/16',4,'2014-01-06 14:42:31','2014-01-06 14:42:31',NULL,NULL),(15,2,'149.157.0.0/16',4,'2014-01-06 14:42:31','2014-01-06 14:42:31',NULL,NULL),(16,2,'157.190.0.0/16',4,'2014-01-06 14:42:31','2014-01-06 14:42:31',NULL,NULL),(17,2,'160.6.0.0/16',4,'2014-01-06 14:42:31','2014-01-06 14:42:31',NULL,NULL),(18,2,'176.97.158.0/24',4,'2014-01-06 14:42:31','2014-01-06 14:42:31',NULL,NULL),(19,2,'192.174.68.0/24',4,'2014-01-06 14:42:31','2014-01-06 14:42:31',NULL,NULL),(20,2,'192.175.48.0/24',4,'2014-01-06 14:42:31','2014-01-06 14:42:31',NULL,NULL),(21,2,'193.1.0.0/16',4,'2014-01-06 14:42:31','2014-01-06 14:42:31',NULL,NULL),(22,2,'193.242.111.0/24',4,'2014-01-06 14:42:31','2014-01-06 14:42:31',NULL,NULL),(23,2,'194.0.24.0/24',4,'2014-01-06 14:42:31','2014-01-06 14:42:31',NULL,NULL),(24,2,'194.0.25.0/24',4,'2014-01-06 14:42:31','2014-01-06 14:42:31',NULL,NULL),(25,2,'194.0.26.0/24',4,'2014-01-06 14:42:31','2014-01-06 14:42:31',NULL,NULL),(26,2,'194.88.240.0/23',4,'2014-01-06 14:42:31','2014-01-06 14:42:31',NULL,NULL),(27,2,'212.3.242.128/26',4,'2014-01-06 14:42:31','2014-01-06 14:42:31',NULL,NULL),(28,2,'2001:678:20::/48',6,'2014-01-06 14:42:32','2014-01-06 14:42:32',NULL,NULL),(29,2,'2001:678:24::/48',6,'2014-01-06 14:42:32','2014-01-06 14:42:32',NULL,NULL),(30,2,'2001:67c:1bc::/48',6,'2014-01-06 14:42:32','2014-01-06 14:42:32',NULL,NULL),(31,2,'2001:67c:10b8::/48',6,'2014-01-06 14:42:32','2014-01-06 14:42:32',NULL,NULL),(32,2,'2001:67c:10e0::/48',6,'2014-01-06 14:42:32','2014-01-06 14:42:32',NULL,NULL),(33,2,'2001:770::/32',6,'2014-01-06 14:42:32','2014-01-06 14:42:32',NULL,NULL),(34,2,'2001:7f8:18::/48',6,'2014-01-06 14:42:32','2014-01-06 14:42:32',NULL,NULL),(35,2,'2001:1900:2205::/48',6,'2014-01-06 14:42:32','2014-01-06 14:42:32',NULL,NULL),(36,2,'2001:1900:2206::/48',6,'2014-01-06 14:42:32','2014-01-06 14:42:32',NULL,NULL),(37,2,'2620:4f:8000::/48',6,'2014-01-06 14:42:32','2014-01-06 14:42:32',NULL,NULL),(38,2,'2a01:4b0::/32',6,'2014-01-06 14:42:32','2014-01-06 14:42:32',NULL,NULL),(39,5,'31.169.96.0/21',4,'2014-01-06 14:42:33','2014-01-06 14:42:33',NULL,NULL),(40,5,'62.231.32.0/19',4,'2014-01-06 14:42:33','2014-01-06 14:42:33',NULL,NULL),(41,5,'78.135.128.0/17',4,'2014-01-06 14:42:33','2014-01-06 14:42:33',NULL,NULL),(42,5,'83.141.64.0/18',4,'2014-01-06 14:42:33','2014-01-06 14:42:33',NULL,NULL),(43,5,'85.134.128.0/17',4,'2014-01-06 14:42:33','2014-01-06 14:42:33',NULL,NULL),(44,5,'87.192.0.0/16',4,'2014-01-06 14:42:33','2014-01-06 14:42:33',NULL,NULL),(45,5,'87.232.0.0/16',4,'2014-01-06 14:42:33','2014-01-06 14:42:33',NULL,NULL),(46,5,'89.28.176.0/21',4,'2014-01-06 14:42:33','2014-01-06 14:42:33',NULL,NULL),(47,5,'89.124.0.0/14',4,'2014-01-06 14:42:33','2014-01-06 14:42:33',NULL,NULL),(48,5,'89.124.0.0/15',4,'2014-01-06 14:42:33','2014-01-06 14:42:33',NULL,NULL),(49,5,'89.125.0.0/16',4,'2014-01-06 14:42:33','2014-01-06 14:42:33',NULL,NULL),(50,5,'89.126.0.0/16',4,'2014-01-06 14:42:33','2014-01-06 14:42:33',NULL,NULL),(51,5,'89.126.0.0/19',4,'2014-01-06 14:42:33','2014-01-06 14:42:33',NULL,NULL),(52,5,'89.126.0.0/20',4,'2014-01-06 14:42:33','2014-01-06 14:42:33',NULL,NULL),(53,5,'89.126.32.0/19',4,'2014-01-06 14:42:33','2014-01-06 14:42:33',NULL,NULL),(54,5,'89.126.64.0/19',4,'2014-01-06 14:42:33','2014-01-06 14:42:33',NULL,NULL),(55,5,'89.126.96.0/19',4,'2014-01-06 14:42:33','2014-01-06 14:42:33',NULL,NULL),(56,5,'91.194.126.0/23',4,'2014-01-06 14:42:33','2014-01-06 14:42:33',NULL,NULL),(57,5,'91.194.126.0/24',4,'2014-01-06 14:42:33','2014-01-06 14:42:33',NULL,NULL),(58,5,'91.194.127.0/24',4,'2014-01-06 14:42:33','2014-01-06 14:42:33',NULL,NULL),(59,5,'91.209.106.0/24',4,'2014-01-06 14:42:33','2014-01-06 14:42:33',NULL,NULL),(60,5,'91.209.106.0/25',4,'2014-01-06 14:42:33','2014-01-06 14:42:33',NULL,NULL),(61,5,'91.209.106.128/25',4,'2014-01-06 14:42:33','2014-01-06 14:42:33',NULL,NULL),(62,5,'91.213.49.0/24',4,'2014-01-06 14:42:33','2014-01-06 14:42:33',NULL,NULL),(63,5,'91.220.224.0/24',4,'2014-01-06 14:42:33','2014-01-06 14:42:33',NULL,NULL),(64,5,'141.105.112.0/21',4,'2014-01-06 14:42:33','2014-01-06 14:42:33',NULL,NULL),(65,5,'176.52.216.0/21',4,'2014-01-06 14:42:33','2014-01-06 14:42:33',NULL,NULL),(66,5,'195.5.172.0/24',4,'2014-01-06 14:42:33','2014-01-06 14:42:33',NULL,NULL),(67,5,'195.60.166.0/23',4,'2014-01-06 14:42:33','2014-01-06 14:42:33',NULL,NULL),(68,5,'216.245.44.0/24',4,'2014-01-06 14:42:33','2014-01-06 14:42:33',NULL,NULL),(69,5,'2001:67c:20::/64',6,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(70,5,'2001:67c:338::/48',6,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(71,5,'2001:4d68::/32',6,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(72,5,'2a01:268::/32',6,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(73,5,'2a01:8f80::/32',6,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(74,3,'31.135.128.0/19',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(75,3,'31.135.128.0/21',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(76,3,'31.135.136.0/21',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(77,3,'31.135.144.0/22',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(78,3,'31.135.148.0/22',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(79,3,'31.135.152.0/22',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(80,3,'31.135.152.0/23',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(81,3,'31.135.154.0/23',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(82,3,'36.0.4.0/22',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(83,3,'63.246.32.0/20',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(84,3,'64.68.192.0/20',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(85,3,'64.68.192.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(86,3,'64.68.193.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(87,3,'64.68.194.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(88,3,'64.68.195.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(89,3,'64.68.196.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(90,3,'64.78.200.0/21',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(91,3,'64.185.240.0/20',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(92,3,'65.22.4.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(93,3,'65.22.5.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(94,3,'65.22.19.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(95,3,'65.22.23.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(96,3,'65.22.27.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(97,3,'65.22.31.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(98,3,'65.22.35.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(99,3,'65.22.39.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(100,3,'65.22.47.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(101,3,'65.22.51.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(102,3,'65.22.55.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(103,3,'65.22.59.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(104,3,'65.22.63.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(105,3,'65.22.67.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(106,3,'65.22.71.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(107,3,'65.22.79.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(108,3,'65.22.83.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(109,3,'65.22.87.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(110,3,'65.22.91.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(111,3,'65.22.95.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(112,3,'65.22.99.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(113,3,'65.22.103.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(114,3,'65.22.107.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(115,3,'65.22.111.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(116,3,'65.22.115.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(117,3,'65.22.119.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(118,3,'65.22.123.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(119,3,'65.22.127.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(120,3,'65.22.131.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(121,3,'65.22.135.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(122,3,'65.22.139.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(123,3,'65.22.143.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(124,3,'65.22.147.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(125,3,'65.22.151.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(126,3,'65.22.155.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(127,3,'65.22.159.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(128,3,'65.22.163.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(129,3,'65.22.171.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(130,3,'65.22.175.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(131,3,'65.22.179.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(132,3,'65.22.183.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(133,3,'65.22.187.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(134,3,'65.22.191.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(135,3,'65.22.195.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(136,3,'65.22.199.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(137,3,'65.22.203.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(138,3,'65.22.207.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(139,3,'65.22.211.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(140,3,'65.22.215.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(141,3,'65.22.219.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(142,3,'65.22.223.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(143,3,'65.22.227.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(144,3,'65.22.231.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(145,3,'65.22.235.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(146,3,'65.22.239.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(147,3,'65.22.243.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(148,3,'65.22.247.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(149,3,'66.96.112.0/20',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(150,3,'66.102.32.0/20',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(151,3,'66.175.104.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(152,3,'66.185.112.0/20',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(153,3,'66.225.199.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(154,3,'66.225.200.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(155,3,'66.225.201.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(156,3,'67.21.37.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(157,3,'67.22.112.0/21',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(158,3,'67.158.48.0/20',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(159,3,'68.65.112.0/21',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(160,3,'68.65.126.0/23',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(161,3,'68.65.126.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(162,3,'68.65.127.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(163,3,'69.166.10.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(164,3,'69.166.12.0/22',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(165,3,'70.40.0.0/21',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(166,3,'70.40.8.0/21',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(167,3,'72.0.48.0/20',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(168,3,'72.0.48.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(169,3,'72.0.49.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(170,3,'72.0.50.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(171,3,'72.0.51.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(172,3,'72.0.52.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(173,3,'72.0.53.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(174,3,'72.0.54.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(175,3,'72.0.55.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(176,3,'72.0.56.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(177,3,'72.0.57.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(178,3,'72.0.58.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(179,3,'72.0.59.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(180,3,'72.0.60.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(181,3,'72.0.61.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(182,3,'72.0.62.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(183,3,'72.0.63.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(184,3,'72.42.112.0/20',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(185,3,'72.42.112.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(186,3,'72.42.113.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(187,3,'72.42.114.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(188,3,'72.42.115.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(189,3,'72.42.116.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(190,3,'72.42.117.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(191,3,'72.42.118.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(192,3,'72.42.119.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(193,3,'72.42.120.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(194,3,'72.42.121.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(195,3,'72.42.122.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(196,3,'72.42.123.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(197,3,'72.42.124.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(198,3,'72.42.125.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(199,3,'72.42.126.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(200,3,'72.42.127.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(201,3,'74.63.16.0/20',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(202,3,'74.63.16.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(203,3,'74.63.17.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(204,3,'74.63.18.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(205,3,'74.63.19.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(206,3,'74.63.20.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(207,3,'74.63.21.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(208,3,'74.63.22.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(209,3,'74.63.23.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(210,3,'74.63.24.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(211,3,'74.63.25.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(212,3,'74.63.26.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(213,3,'74.63.27.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(214,3,'74.80.64.0/18',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(215,3,'74.80.64.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(216,3,'74.80.65.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(217,3,'74.80.66.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(218,3,'74.80.67.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(219,3,'74.80.68.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(220,3,'74.80.69.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(221,3,'74.80.70.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(222,3,'74.80.71.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(223,3,'74.80.72.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(224,3,'74.80.73.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(225,3,'74.80.74.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(226,3,'74.80.75.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(227,3,'74.80.76.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(228,3,'74.80.77.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(229,3,'74.80.78.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(230,3,'74.80.79.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(231,3,'74.80.80.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(232,3,'74.80.81.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(233,3,'74.80.82.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(234,3,'74.80.83.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(235,3,'74.80.84.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(236,3,'74.80.85.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(237,3,'74.80.86.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(238,3,'74.80.87.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(239,3,'74.80.88.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(240,3,'74.80.89.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(241,3,'74.80.90.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(242,3,'74.80.91.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(243,3,'74.80.92.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(244,3,'74.80.93.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(245,3,'74.80.94.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(246,3,'74.80.95.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(247,3,'74.80.96.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(248,3,'74.80.97.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(249,3,'74.80.98.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(250,3,'74.80.99.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(251,3,'74.80.100.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(252,3,'74.80.101.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(253,3,'74.80.102.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(254,3,'74.80.103.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(255,3,'74.80.104.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(256,3,'74.80.105.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(257,3,'74.80.106.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(258,3,'74.80.107.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(259,3,'74.80.108.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(260,3,'74.80.109.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(261,3,'74.80.110.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(262,3,'74.80.111.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(263,3,'74.80.112.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(264,3,'74.80.113.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(265,3,'74.80.114.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(266,3,'74.80.115.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(267,3,'74.80.116.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(268,3,'74.80.117.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(269,3,'74.80.118.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(270,3,'74.80.119.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(271,3,'74.80.120.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(272,3,'74.80.121.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(273,3,'74.80.122.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(274,3,'74.80.123.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(275,3,'74.80.124.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(276,3,'74.80.125.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(277,3,'74.80.126.0/23',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(278,3,'74.80.126.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(279,3,'74.80.127.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(280,3,'74.118.212.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(281,3,'74.118.213.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(282,3,'74.118.214.0/23',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(283,3,'75.127.16.0/20',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(284,3,'76.191.16.0/20',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(285,3,'89.19.120.0/21',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(286,3,'89.19.120.0/22',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(287,3,'89.19.124.0/23',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(288,3,'89.19.126.0/23',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(289,3,'91.201.224.0/22',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(290,3,'91.201.224.0/23',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(291,3,'91.201.224.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(292,3,'91.201.225.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(293,3,'91.201.226.0/23',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(294,3,'91.201.226.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(295,3,'91.201.227.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(296,3,'91.209.1.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(297,3,'91.209.193.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(298,3,'91.222.16.0/22',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(299,3,'91.222.40.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(300,3,'91.222.41.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(301,3,'91.222.42.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(302,3,'91.222.43.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(303,3,'91.241.93.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(304,3,'93.95.24.0/21',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(305,3,'93.95.24.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(306,3,'93.95.25.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(307,3,'93.95.26.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(308,3,'93.171.128.0/22',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(309,3,'95.47.163.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(310,3,'101.251.4.0/22',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(311,3,'114.69.222.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(312,3,'128.8.0.0/16',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(313,3,'128.161.0.0/16',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(314,3,'129.2.0.0/16',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(315,3,'130.135.0.0/16',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(316,3,'130.167.0.0/16',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(317,3,'131.161.128.0/18',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(318,3,'131.182.0.0/16',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(319,3,'139.229.0.0/16',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(320,3,'140.169.0.0/16',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(321,3,'146.5.0.0/16',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(322,3,'146.58.0.0/16',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(323,3,'150.144.0.0/16',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(324,3,'156.154.43.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(325,3,'156.154.50.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(326,3,'156.154.59.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(327,3,'156.154.96.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(328,3,'156.154.99.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(329,3,'158.154.0.0/16',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(330,3,'169.222.0.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(331,3,'183.91.132.0/22',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(332,3,'192.5.41.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(333,3,'192.12.123.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(334,3,'192.42.70.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(335,3,'192.58.36.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(336,3,'192.67.83.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(337,3,'192.67.107.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(338,3,'192.67.108.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(339,3,'192.68.52.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(340,3,'192.68.148.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(341,3,'192.68.162.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(342,3,'192.70.244.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(343,3,'192.70.249.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(344,3,'192.77.80.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(345,3,'192.84.8.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(346,3,'192.88.124.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(347,3,'192.92.65.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(348,3,'192.92.90.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(349,3,'192.100.9.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(350,3,'192.100.10.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(351,3,'192.100.15.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(352,3,'192.101.148.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(353,3,'192.102.15.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(354,3,'192.102.219.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(355,3,'192.102.233.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(356,3,'192.102.234.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(357,3,'192.112.18.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(358,3,'192.112.223.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(359,3,'192.112.224.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(360,3,'192.124.20.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(361,3,'192.138.101.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(362,3,'192.138.172.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(363,3,'192.149.89.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(364,3,'192.149.104.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(365,3,'192.149.107.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(366,3,'192.149.133.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(367,3,'192.150.32.0/21',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(368,3,'192.153.157.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(369,3,'192.188.4.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(370,3,'192.203.230.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(371,3,'192.225.64.0/19',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(372,3,'192.243.0.0/20',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(373,3,'192.243.16.0/21',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(374,3,'193.29.206.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(375,3,'193.110.16.0/22',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(376,3,'193.110.16.0/23',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(377,3,'193.110.18.0/23',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(378,3,'193.111.240.0/22',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(379,3,'193.178.228.0/23',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(380,3,'193.178.228.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(381,3,'193.178.229.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(382,3,'194.0.12.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(383,3,'194.0.13.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(384,3,'194.0.14.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(385,3,'194.0.17.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(386,3,'194.0.27.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(387,3,'194.0.36.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(388,3,'194.0.42.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(389,3,'194.0.47.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(390,3,'194.28.144.0/22',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(391,3,'194.117.58.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(392,3,'194.117.60.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(393,3,'194.117.61.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(394,3,'194.117.62.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(395,3,'194.117.63.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(396,3,'194.146.180.0/22',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(397,3,'194.146.180.0/23',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(398,3,'194.146.180.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(399,3,'194.146.181.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(400,3,'194.146.182.0/23',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(401,3,'194.146.182.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(402,3,'194.146.183.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(403,3,'194.146.228.0/22',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(404,3,'194.146.228.0/23',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(405,3,'194.146.228.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(406,3,'194.146.229.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(407,3,'194.146.230.0/23',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(408,3,'194.146.230.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(409,3,'194.146.231.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(410,3,'194.153.148.0/23',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(411,3,'195.64.162.0/23',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(412,3,'195.64.162.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(413,3,'195.64.163.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(414,3,'195.82.138.0/23',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(415,3,'198.9.0.0/16',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(416,3,'198.49.1.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(417,3,'198.116.0.0/14',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(418,3,'198.120.0.0/14',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(419,3,'198.182.28.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(420,3,'198.182.31.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(421,3,'198.182.167.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(422,3,'199.4.137.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(423,3,'199.7.64.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(424,3,'199.7.77.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(425,3,'199.7.83.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(426,3,'199.7.86.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(427,3,'199.7.91.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(428,3,'199.7.94.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(429,3,'199.7.95.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(430,3,'199.43.132.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(431,3,'199.115.156.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(432,3,'199.115.157.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(433,3,'199.120.141.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(434,3,'199.120.142.0/23',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(435,3,'199.120.144.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(436,3,'199.182.32.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(437,3,'199.182.40.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(438,3,'199.184.181.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(439,3,'199.184.182.0/23',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(440,3,'199.184.184.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(441,3,'199.249.112.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(442,3,'199.249.113.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(443,3,'199.249.114.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(444,3,'199.249.115.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(445,3,'199.249.116.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(446,3,'199.249.117.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(447,3,'199.249.118.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(448,3,'199.249.119.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(449,3,'199.249.120.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(450,3,'199.249.121.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(451,3,'199.249.122.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(452,3,'199.249.123.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(453,3,'199.249.124.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(454,3,'199.249.125.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(455,3,'199.249.126.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(456,3,'199.249.127.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(457,3,'199.254.171.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(458,3,'200.1.121.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(459,3,'200.1.131.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(460,3,'200.7.4.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(461,3,'200.16.98.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(462,3,'202.6.102.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(463,3,'202.7.4.0/22',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(464,3,'202.52.0.0/23',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(465,3,'202.53.186.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(466,3,'202.53.191.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(467,3,'203.119.88.0/23',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(468,3,'204.14.112.0/21',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(469,3,'204.19.119.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(470,3,'204.26.57.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(471,3,'204.61.208.0/21',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(472,3,'204.61.208.0/22',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(473,3,'204.61.208.0/23',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(474,3,'204.61.210.0/23',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(475,3,'204.61.210.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(476,3,'204.61.212.0/23',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(477,3,'204.61.216.0/23',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(478,3,'204.194.22.0/23',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(479,3,'204.194.22.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(480,3,'204.194.23.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(481,3,'205.132.46.0/23',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(482,3,'205.207.155.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(483,3,'206.51.254.0/23',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(484,3,'206.108.113.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(485,3,'206.196.160.0/19',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(486,3,'206.220.228.0/22',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(487,3,'206.220.228.0/23',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(488,3,'206.220.230.0/23',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(489,3,'206.223.122.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(490,3,'207.34.5.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(491,3,'207.34.6.0/23',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(492,3,'208.15.19.0/24',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(493,3,'208.49.115.64/27',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(494,3,'208.67.88.0/22',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(495,3,'216.21.2.0/23',4,'2014-01-06 14:42:34','2014-01-06 14:42:34',NULL,NULL),(496,3,'2001:500:3::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(497,3,'2001:500:14::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(498,3,'2001:500:15::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(499,3,'2001:500:40::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(500,3,'2001:500:41::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(501,3,'2001:500:42::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(502,3,'2001:500:43::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(503,3,'2001:500:44::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(504,3,'2001:500:45::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(505,3,'2001:500:46::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(506,3,'2001:500:47::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(507,3,'2001:500:48::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(508,3,'2001:500:49::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(509,3,'2001:500:4a::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(510,3,'2001:500:4b::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(511,3,'2001:500:4c::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(512,3,'2001:500:4d::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(513,3,'2001:500:4e::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(514,3,'2001:500:4f::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(515,3,'2001:500:50::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(516,3,'2001:500:51::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(517,3,'2001:500:52::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(518,3,'2001:500:53::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(519,3,'2001:500:54::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(520,3,'2001:500:55::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(521,3,'2001:500:56::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(522,3,'2001:500:7d::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(523,3,'2001:500:83::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(524,3,'2001:500:8c::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(525,3,'2001:500:9c::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(526,3,'2001:500:9d::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(527,3,'2001:500:a4::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(528,3,'2001:500:a5::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(529,3,'2001:500:e0::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(530,3,'2001:500:e1::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(531,3,'2001:678:3::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(532,3,'2001:678:28::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(533,3,'2001:678:4c::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(534,3,'2001:678:60::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(535,3,'2001:678:78::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(536,3,'2001:678:94::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(537,3,'2001:dd8:7::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(538,3,'2001:1398:121::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(539,3,'2404:2c00::/32',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(540,3,'2620:0:870::/45',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(541,3,'2620:0:876::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(542,3,'2620:49::/44',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(543,3,'2620:49::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(544,3,'2620:49:a::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(545,3,'2620:49:b::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(546,3,'2620:95:8000::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(547,3,'2620:171::/40',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(548,3,'2620:171:f0::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(549,3,'2620:171:f1::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(550,3,'2620:171:f2::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(551,3,'2620:171:f3::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(552,3,'2620:171:f4::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(553,3,'2620:171:f5::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(554,3,'2620:171:f6::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(555,3,'2620:171:f7::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(556,3,'2620:171:f8::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(557,3,'2620:171:f9::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(558,3,'2620:171:a00::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(559,3,'2620:171:a01::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(560,3,'2620:171:a02::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(561,3,'2620:171:a03::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(562,3,'2620:171:a04::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(563,3,'2620:171:a05::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(564,3,'2620:171:a06::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(565,3,'2620:171:a07::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(566,3,'2620:171:a08::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(567,3,'2620:171:a09::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(568,3,'2620:171:a0a::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(569,3,'2620:171:a0b::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(570,3,'2620:171:a0c::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(571,3,'2620:171:a0d::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(572,3,'2620:171:a0e::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(573,3,'2620:171:a0f::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(574,3,'2620:171:ad0::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(575,3,'2620:171:d00::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(576,3,'2620:171:d01::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(577,3,'2620:171:d02::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(578,3,'2620:171:d03::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(579,3,'2620:171:d04::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(580,3,'2620:171:d05::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(581,3,'2620:171:d06::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(582,3,'2620:171:d07::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(583,3,'2620:171:d08::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(584,3,'2620:171:d09::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(585,3,'2620:171:d0a::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(586,3,'2620:171:d0b::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(587,3,'2620:171:d0c::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(588,3,'2620:171:d0d::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(589,3,'2620:171:d0e::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(590,3,'2620:171:d0f::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(591,3,'2620:171:dd0::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(592,3,'2a01:8840:4::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(593,3,'2a01:8840:5::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(594,3,'2a01:8840:15::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(595,3,'2a01:8840:19::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(596,3,'2a01:8840:1d::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(597,3,'2a01:8840:21::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(598,3,'2a01:8840:25::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(599,3,'2a01:8840:29::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(600,3,'2a01:8840:2d::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(601,3,'2a01:8840:31::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(602,3,'2a01:8840:35::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(603,3,'2a01:8840:39::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(604,3,'2a01:8840:3d::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(605,3,'2a01:8840:41::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(606,3,'2a01:8840:45::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(607,3,'2a01:8840:4d::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(608,3,'2a01:8840:51::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(609,3,'2a01:8840:55::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(610,3,'2a01:8840:59::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(611,3,'2a01:8840:5d::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(612,3,'2a01:8840:61::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(613,3,'2a01:8840:65::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(614,3,'2a01:8840:69::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(615,3,'2a01:8840:6d::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(616,3,'2a01:8840:71::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(617,3,'2a01:8840:75::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(618,3,'2a01:8840:79::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(619,3,'2a01:8840:7d::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(620,3,'2a01:8840:81::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(621,3,'2a01:8840:85::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(622,3,'2a01:8840:89::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(623,3,'2a01:8840:8d::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(624,3,'2a01:8840:91::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(625,3,'2a01:8840:95::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(626,3,'2a01:8840:99::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(627,3,'2a01:8840:9d::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(628,3,'2a01:8840:a1::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(629,3,'2a01:8840:a5::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(630,3,'2a01:8840:a9::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(631,3,'2a01:8840:ad::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(632,3,'2a01:8840:b1::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(633,3,'2a01:8840:b5::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(634,3,'2a01:8840:b9::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(635,3,'2a01:8840:bd::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(636,3,'2a01:8840:c1::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(637,3,'2a01:8840:c5::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(638,3,'2a01:8840:c9::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(639,3,'2a01:8840:cd::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(640,3,'2a01:8840:d1::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(641,3,'2a01:8840:d5::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(642,3,'2a01:8840:d9::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(643,3,'2a01:8840:dd::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(644,3,'2a01:8840:e1::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(645,3,'2a01:8840:e5::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(646,3,'2a01:8840:e9::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(647,3,'2a01:8840:ed::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL),(648,3,'2a01:8840:f1::/48',6,'2014-01-06 14:42:36','2014-01-06 14:42:36',NULL,NULL); +/*!40000 ALTER TABLE `irrdb_prefix` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `irrdb_update_logs` +-- + +DROP TABLE IF EXISTS `irrdb_update_logs`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `irrdb_update_logs` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `cust_id` int NOT NULL, + `prefix_v4` datetime DEFAULT NULL, + `prefix_v6` datetime DEFAULT NULL, + `asn_v4` datetime DEFAULT NULL, + `asn_v6` datetime DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `irrdb_update_logs_cust_id_unique` (`cust_id`), + CONSTRAINT `irrdb_update_logs_cust_id_foreign` FOREIGN KEY (`cust_id`) REFERENCES `cust` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `irrdb_update_logs` +-- + +LOCK TABLES `irrdb_update_logs` WRITE; +/*!40000 ALTER TABLE `irrdb_update_logs` DISABLE KEYS */; +/*!40000 ALTER TABLE `irrdb_update_logs` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `irrdbconfig` +-- + +DROP TABLE IF EXISTS `irrdbconfig`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `irrdbconfig` ( + `id` int NOT NULL AUTO_INCREMENT, + `host` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `source` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `notes` longtext CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `irrdbconfig` +-- + +LOCK TABLES `irrdbconfig` WRITE; +/*!40000 ALTER TABLE `irrdbconfig` DISABLE KEYS */; +INSERT INTO `irrdbconfig` VALUES (1,'whois.radb.net','RIPE','RIPE Query from RIPE Database',NULL,NULL),(2,'whois.radb.net','RADB','RADB Query from RADB Database',NULL,NULL),(3,'whois.radb.net','LACNIC','LACNIC Query from LACNIC Database',NULL,NULL),(4,'whois.radb.net','AFRINIC','AFRINIC Query from AFRINIC Database',NULL,NULL),(5,'whois.radb.net','APNIC','APNIC Query from APNIC Database',NULL,NULL),(6,'whois.radb.net','LEVEL3','Level3 Query from Level3 Database',NULL,NULL),(7,'whois.radb.net','ARIN','ARIN Query from RADB Database',NULL,NULL),(8,'whois.radb.net','RADB,ARIN','RADB+ARIN Query from RADB Database',NULL,NULL),(9,'whois.radb.net','ALTDB','ALTDB Query from RADB Database',NULL,NULL),(10,'whois.radb.net','RADB,RIPE','RADB+RIPE Query from RADB Database',NULL,NULL),(11,'whois.radb.net','RADB,APNIC,ARIN','RADB+APNIC+ARIN Query from RADB Database',NULL,NULL),(12,'whois.radb.net','RIPE,ARIN','RIPE+ARIN Query from RADB Database',NULL,NULL),(13,'whois.radb.net','RADB,RIPE,APNIC,ARIN','',NULL,NULL); +/*!40000 ALTER TABLE `irrdbconfig` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `l2address` +-- + +DROP TABLE IF EXISTS `l2address`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `l2address` ( + `id` int NOT NULL AUTO_INCREMENT, + `vlan_interface_id` int NOT NULL, + `mac` varchar(12) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `firstseen` datetime DEFAULT NULL, + `lastseen` datetime DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `mac_vlanint` (`mac`,`vlan_interface_id`), + KEY `IDX_B9482E1D6AB5F82` (`vlan_interface_id`), + CONSTRAINT `FK_B9482E1D6AB5F82` FOREIGN KEY (`vlan_interface_id`) REFERENCES `vlaninterface` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `l2address` +-- + +LOCK TABLES `l2address` WRITE; +/*!40000 ALTER TABLE `l2address` DISABLE KEYS */; +/*!40000 ALTER TABLE `l2address` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `location` +-- + +DROP TABLE IF EXISTS `location`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `location` ( + `id` int NOT NULL AUTO_INCREMENT, + `name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `shortname` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `tag` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `address` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `nocphone` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `nocfax` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `nocemail` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `officephone` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `officefax` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `officeemail` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `notes` longtext CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci, + `pdb_facility_id` bigint DEFAULT NULL, + `city` varchar(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `country` varchar(2) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `UNIQ_5E9E89CB64082763` (`shortname`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `location` +-- + +LOCK TABLES `location` WRITE; +/*!40000 ALTER TABLE `location` DISABLE KEYS */; +INSERT INTO `location` VALUES (1,'Location 1','l1',NULL,'','','','','','','','',NULL,NULL,NULL,NULL,NULL); +/*!40000 ALTER TABLE `location` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `log` +-- + +DROP TABLE IF EXISTS `log`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `log` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `user_id` int DEFAULT NULL, + `model` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `model_id` bigint unsigned DEFAULT NULL, + `action` varchar(7) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `message` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `models` json NOT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `log_user_id_foreign` (`user_id`), + KEY `log_action_index` (`action`), + KEY `log_model_model_id_index` (`model`,`model_id`), + CONSTRAINT `log_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=66 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `log` +-- + +LOCK TABLES `log` WRITE; +/*!40000 ALTER TABLE `log` DISABLE KEYS */; +INSERT INTO `log` VALUES (1,1,'DocstoreFile',1,'CREATED','Docstore File [id:1] \'test.txt\'','{\"new\": {\"id\": 1, \"name\": \"test.txt\", \"path\": \"BaLvISQV7Cn48p8LPaOoPSyJ5hzaKC7rHlqMu5Hd.txt\", \"sha256\": \"64cdd02f0ef14bf6b8e0a51915396a002afed410459935b1209ba2d654842f10\", \"min_privs\": \"0\", \"created_at\": \"2021-05-28 14:03:38\", \"created_by\": 1, \"updated_at\": \"2021-05-28 14:03:38\", \"description\": null, \"file_last_updated\": \"2021-05-28 14:03:38\", \"docstore_directory_id\": \"1\"}, \"old\": null, \"changed\": null}','2021-05-28 12:03:38','2021-05-28 12:03:38'),(2,1,'RouteServerFilter',1,'CREATED','Route Server Filter [id:1] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 1, \"enabled\": true, \"peer_id\": null, \"vlan_id\": null, \"order_by\": 1, \"protocol\": null, \"created_at\": \"2022-02-28 15:36:04\", \"updated_at\": \"2022-02-28 15:36:04\", \"customer_id\": 2, \"action_receive\": \"AS_IS\", \"action_advertise\": \"AS_IS\"}, \"old\": null, \"changed\": null}','2022-02-28 15:36:04','2022-02-28 15:36:04'),(3,1,'RouteServerFilterProd',1,'CREATED','Route Server Filter (Production) [id:1] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 1, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": null, \"order_by\": 1, \"protocol\": null, \"created_at\": \"2022-02-28 15:36:04\", \"updated_at\": \"2022-02-28 15:36:04\", \"customer_id\": 2, \"action_receive\": \"AS_IS\", \"received_prefix\": null, \"action_advertise\": \"AS_IS\", \"advertised_prefix\": null}, \"old\": null, \"changed\": null}','2022-02-28 15:36:08','2022-02-28 15:36:08'),(4,1,'RouteServerFilter',2,'CREATED','Route Server Filter [id:2] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 2, \"enabled\": true, \"peer_id\": null, \"vlan_id\": \"1\", \"order_by\": 2, \"protocol\": \"4\", \"created_at\": \"2022-02-28 15:49:09\", \"updated_at\": \"2022-02-28 15:49:09\", \"customer_id\": 2, \"action_receive\": \"NO_ADVERTISE\", \"action_advertise\": \"NO_ADVERTISE\", \"advertised_prefix\": \"*\"}, \"old\": null, \"changed\": null}','2022-02-28 15:49:09','2022-02-28 15:49:09'),(5,1,'RouteServerFilterProd',2,'CREATED','Route Server Filter (Production) [id:2] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 2, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": null, \"order_by\": 1, \"protocol\": null, \"created_at\": \"2022-02-28 15:36:04\", \"updated_at\": \"2022-02-28 15:36:04\", \"customer_id\": 2, \"action_receive\": \"AS_IS\", \"received_prefix\": null, \"action_advertise\": \"AS_IS\", \"advertised_prefix\": null}, \"old\": null, \"changed\": null}','2022-02-28 15:49:22','2022-02-28 15:49:22'),(6,1,'RouteServerFilterProd',3,'CREATED','Route Server Filter (Production) [id:3] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 3, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": 1, \"order_by\": 2, \"protocol\": 4, \"created_at\": \"2022-02-28 15:49:09\", \"updated_at\": \"2022-02-28 15:49:09\", \"customer_id\": 2, \"action_receive\": \"NO_ADVERTISE\", \"received_prefix\": null, \"action_advertise\": \"NO_ADVERTISE\", \"advertised_prefix\": \"*\"}, \"old\": null, \"changed\": null}','2022-02-28 15:49:22','2022-02-28 15:49:22'),(7,1,'RouteServerFilter',1,'UPDATED','Route Server Filter [id:1] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 1, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": null, \"order_by\": 0, \"protocol\": null, \"created_at\": \"2022-02-28 15:36:04\", \"updated_at\": \"2022-02-28 15:49:25\", \"customer_id\": 2, \"action_receive\": \"AS_IS\", \"received_prefix\": null, \"action_advertise\": \"AS_IS\", \"advertised_prefix\": null}, \"old\": {\"id\": 1, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": null, \"order_by\": 1, \"protocol\": null, \"created_at\": \"2022-02-28T15:36:04.000000Z\", \"updated_at\": \"2022-02-28T15:36:04.000000Z\", \"customer_id\": 2, \"action_receive\": \"AS_IS\", \"received_prefix\": null, \"action_advertise\": \"AS_IS\", \"advertised_prefix\": null}, \"changed\": {\"order_by\": 0, \"updated_at\": \"2022-02-28 15:49:25\"}}','2022-02-28 15:49:25','2022-02-28 15:49:25'),(8,1,'RouteServerFilter',2,'UPDATED','Route Server Filter [id:2] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 2, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": 1, \"order_by\": 1, \"protocol\": 4, \"created_at\": \"2022-02-28 15:49:09\", \"updated_at\": \"2022-02-28 15:49:25\", \"customer_id\": 2, \"action_receive\": \"NO_ADVERTISE\", \"received_prefix\": null, \"action_advertise\": \"NO_ADVERTISE\", \"advertised_prefix\": \"*\"}, \"old\": {\"id\": 2, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": 1, \"order_by\": 2, \"protocol\": 4, \"created_at\": \"2022-02-28T15:49:09.000000Z\", \"updated_at\": \"2022-02-28T15:49:09.000000Z\", \"customer_id\": 2, \"action_receive\": \"NO_ADVERTISE\", \"received_prefix\": null, \"action_advertise\": \"NO_ADVERTISE\", \"advertised_prefix\": \"*\"}, \"changed\": {\"order_by\": 1, \"updated_at\": \"2022-02-28 15:49:25\"}}','2022-02-28 15:49:25','2022-02-28 15:49:25'),(9,1,'RouteServerFilter',1,'UPDATED','Route Server Filter [id:1] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 1, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": null, \"order_by\": 2, \"protocol\": null, \"created_at\": \"2022-02-28 15:36:04\", \"updated_at\": \"2022-02-28 15:49:25\", \"customer_id\": 2, \"action_receive\": \"AS_IS\", \"received_prefix\": null, \"action_advertise\": \"AS_IS\", \"advertised_prefix\": null}, \"old\": {\"id\": 1, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": null, \"order_by\": 0, \"protocol\": null, \"created_at\": \"2022-02-28T15:36:04.000000Z\", \"updated_at\": \"2022-02-28T15:49:25.000000Z\", \"customer_id\": 2, \"action_receive\": \"AS_IS\", \"received_prefix\": null, \"action_advertise\": \"AS_IS\", \"advertised_prefix\": null}, \"changed\": {\"order_by\": 2}}','2022-02-28 15:49:25','2022-02-28 15:49:25'),(10,1,'RouteServerFilterProd',4,'CREATED','Route Server Filter (Production) [id:4] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 4, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": 1, \"order_by\": 1, \"protocol\": 4, \"created_at\": \"2022-02-28 15:49:09\", \"updated_at\": \"2022-02-28 15:49:25\", \"customer_id\": 2, \"action_receive\": \"NO_ADVERTISE\", \"received_prefix\": null, \"action_advertise\": \"NO_ADVERTISE\", \"advertised_prefix\": \"*\"}, \"old\": null, \"changed\": null}','2022-02-28 15:49:30','2022-02-28 15:49:30'),(11,1,'RouteServerFilterProd',5,'CREATED','Route Server Filter (Production) [id:5] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 5, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": null, \"order_by\": 2, \"protocol\": null, \"created_at\": \"2022-02-28 15:36:04\", \"updated_at\": \"2022-02-28 15:49:25\", \"customer_id\": 2, \"action_receive\": \"AS_IS\", \"received_prefix\": null, \"action_advertise\": \"AS_IS\", \"advertised_prefix\": null}, \"old\": null, \"changed\": null}','2022-02-28 15:49:30','2022-02-28 15:49:30'),(12,1,'RouteServerFilter',3,'CREATED','Route Server Filter [id:3] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 3, \"enabled\": true, \"peer_id\": null, \"vlan_id\": \"1\", \"order_by\": 3, \"protocol\": \"4\", \"created_at\": \"2022-02-28 15:57:47\", \"updated_at\": \"2022-02-28 15:57:47\", \"customer_id\": 2, \"action_receive\": \"NO_ADVERTISE\", \"action_advertise\": \"NO_ADVERTISE\", \"advertised_prefix\": \"*\"}, \"old\": null, \"changed\": null}','2022-02-28 15:57:47','2022-02-28 15:57:47'),(13,1,'RouteServerFilter',2,'DELETED','Route Server Filter [id:2] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": null, \"old\": {\"id\": 2, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": 1, \"order_by\": 1, \"protocol\": 4, \"created_at\": \"2022-02-28T15:49:09.000000Z\", \"updated_at\": \"2022-02-28T15:49:25.000000Z\", \"customer_id\": 2, \"action_receive\": \"NO_ADVERTISE\", \"received_prefix\": null, \"action_advertise\": \"NO_ADVERTISE\", \"advertised_prefix\": \"*\"}, \"changed\": null}','2022-02-28 15:57:56','2022-02-28 15:57:56'),(14,1,'RouteServerFilter',1,'UPDATED','Route Server Filter [id:1] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 1, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": null, \"order_by\": 0, \"protocol\": null, \"created_at\": \"2022-02-28 15:36:04\", \"updated_at\": \"2022-02-28 15:57:58\", \"customer_id\": 2, \"action_receive\": \"AS_IS\", \"received_prefix\": null, \"action_advertise\": \"AS_IS\", \"advertised_prefix\": null}, \"old\": {\"id\": 1, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": null, \"order_by\": 2, \"protocol\": null, \"created_at\": \"2022-02-28T15:36:04.000000Z\", \"updated_at\": \"2022-02-28T15:49:25.000000Z\", \"customer_id\": 2, \"action_receive\": \"AS_IS\", \"received_prefix\": null, \"action_advertise\": \"AS_IS\", \"advertised_prefix\": null}, \"changed\": {\"order_by\": 0, \"updated_at\": \"2022-02-28 15:57:58\"}}','2022-02-28 15:57:58','2022-02-28 15:57:58'),(15,1,'RouteServerFilter',3,'UPDATED','Route Server Filter [id:3] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 3, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": 1, \"order_by\": 2, \"protocol\": 4, \"created_at\": \"2022-02-28 15:57:47\", \"updated_at\": \"2022-02-28 15:57:58\", \"customer_id\": 2, \"action_receive\": \"NO_ADVERTISE\", \"received_prefix\": null, \"action_advertise\": \"NO_ADVERTISE\", \"advertised_prefix\": \"*\"}, \"old\": {\"id\": 3, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": 1, \"order_by\": 3, \"protocol\": 4, \"created_at\": \"2022-02-28T15:57:47.000000Z\", \"updated_at\": \"2022-02-28T15:57:47.000000Z\", \"customer_id\": 2, \"action_receive\": \"NO_ADVERTISE\", \"received_prefix\": null, \"action_advertise\": \"NO_ADVERTISE\", \"advertised_prefix\": \"*\"}, \"changed\": {\"order_by\": 2, \"updated_at\": \"2022-02-28 15:57:58\"}}','2022-02-28 15:57:58','2022-02-28 15:57:58'),(16,1,'RouteServerFilter',1,'UPDATED','Route Server Filter [id:1] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 1, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": null, \"order_by\": 3, \"protocol\": null, \"created_at\": \"2022-02-28 15:36:04\", \"updated_at\": \"2022-02-28 15:57:58\", \"customer_id\": 2, \"action_receive\": \"AS_IS\", \"received_prefix\": null, \"action_advertise\": \"AS_IS\", \"advertised_prefix\": null}, \"old\": {\"id\": 1, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": null, \"order_by\": 0, \"protocol\": null, \"created_at\": \"2022-02-28T15:36:04.000000Z\", \"updated_at\": \"2022-02-28T15:57:58.000000Z\", \"customer_id\": 2, \"action_receive\": \"AS_IS\", \"received_prefix\": null, \"action_advertise\": \"AS_IS\", \"advertised_prefix\": null}, \"changed\": {\"order_by\": 3}}','2022-02-28 15:57:58','2022-02-28 15:57:58'),(17,1,'RouteServerFilterProd',6,'CREATED','Route Server Filter (Production) [id:6] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 6, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": 1, \"order_by\": 2, \"protocol\": 4, \"created_at\": \"2022-02-28 15:57:47\", \"updated_at\": \"2022-02-28 15:57:58\", \"customer_id\": 2, \"action_receive\": \"NO_ADVERTISE\", \"received_prefix\": null, \"action_advertise\": \"NO_ADVERTISE\", \"advertised_prefix\": \"*\"}, \"old\": null, \"changed\": null}','2022-02-28 15:58:02','2022-02-28 15:58:02'),(18,1,'RouteServerFilterProd',7,'CREATED','Route Server Filter (Production) [id:7] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 7, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": null, \"order_by\": 3, \"protocol\": null, \"created_at\": \"2022-02-28 15:36:04\", \"updated_at\": \"2022-02-28 15:57:58\", \"customer_id\": 2, \"action_receive\": \"AS_IS\", \"received_prefix\": null, \"action_advertise\": \"AS_IS\", \"advertised_prefix\": null}, \"old\": null, \"changed\": null}','2022-02-28 15:58:02','2022-02-28 15:58:02'),(19,1,'RouteServerFilter',3,'UPDATED','Route Server Filter [id:3] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 3, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": \"1\", \"order_by\": 2, \"protocol\": \"4\", \"created_at\": \"2022-02-28 15:57:47\", \"updated_at\": \"2022-02-28 16:00:01\", \"customer_id\": 2, \"action_receive\": \"NO_ADVERTISE\", \"received_prefix\": null, \"action_advertise\": \"NO_ADVERTISE\", \"advertised_prefix\": \"77.72.72.0/21\"}, \"old\": {\"id\": 3, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": 1, \"order_by\": 2, \"protocol\": 4, \"created_at\": \"2022-02-28T15:57:47.000000Z\", \"updated_at\": \"2022-02-28T15:57:58.000000Z\", \"customer_id\": 2, \"action_receive\": \"NO_ADVERTISE\", \"received_prefix\": null, \"action_advertise\": \"NO_ADVERTISE\", \"advertised_prefix\": \"*\"}, \"changed\": {\"updated_at\": \"2022-02-28 16:00:01\", \"advertised_prefix\": \"77.72.72.0/21\"}}','2022-02-28 16:00:01','2022-02-28 16:00:01'),(20,1,'RouteServerFilter',3,'UPDATED','Route Server Filter [id:3] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 3, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": \"1\", \"order_by\": 2, \"protocol\": \"4\", \"created_at\": \"2022-02-28 15:57:47\", \"updated_at\": \"2022-02-28 16:00:08\", \"customer_id\": 2, \"action_receive\": \"NO_ADVERTISE\", \"received_prefix\": null, \"action_advertise\": \"NO_ADVERTISE\", \"advertised_prefix\": \"*\"}, \"old\": {\"id\": 3, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": 1, \"order_by\": 2, \"protocol\": 4, \"created_at\": \"2022-02-28T15:57:47.000000Z\", \"updated_at\": \"2022-02-28T16:00:01.000000Z\", \"customer_id\": 2, \"action_receive\": \"NO_ADVERTISE\", \"received_prefix\": null, \"action_advertise\": \"NO_ADVERTISE\", \"advertised_prefix\": \"77.72.72.0/21\"}, \"changed\": {\"updated_at\": \"2022-02-28 16:00:08\", \"advertised_prefix\": \"*\"}}','2022-02-28 16:00:08','2022-02-28 16:00:08'),(21,1,'RouteServerFilter',4,'CREATED','Route Server Filter [id:4] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 4, \"enabled\": true, \"peer_id\": null, \"vlan_id\": \"1\", \"order_by\": 4, \"protocol\": \"4\", \"created_at\": \"2022-02-28 16:00:49\", \"updated_at\": \"2022-02-28 16:00:49\", \"customer_id\": 2, \"action_receive\": \"NO_ADVERTISE\", \"action_advertise\": \"NO_ADVERTISE\", \"advertised_prefix\": \"*\"}, \"old\": null, \"changed\": null}','2022-02-28 16:00:49','2022-02-28 16:00:49'),(22,1,'RouteServerFilterProd',8,'CREATED','Route Server Filter (Production) [id:8] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 8, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": 1, \"order_by\": 2, \"protocol\": 4, \"created_at\": \"2022-02-28 15:57:47\", \"updated_at\": \"2022-02-28 16:00:08\", \"customer_id\": 2, \"action_receive\": \"NO_ADVERTISE\", \"received_prefix\": null, \"action_advertise\": \"NO_ADVERTISE\", \"advertised_prefix\": \"*\"}, \"old\": null, \"changed\": null}','2022-02-28 16:00:52','2022-02-28 16:00:52'),(23,1,'RouteServerFilterProd',9,'CREATED','Route Server Filter (Production) [id:9] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 9, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": null, \"order_by\": 3, \"protocol\": null, \"created_at\": \"2022-02-28 15:36:04\", \"updated_at\": \"2022-02-28 15:57:58\", \"customer_id\": 2, \"action_receive\": \"AS_IS\", \"received_prefix\": null, \"action_advertise\": \"AS_IS\", \"advertised_prefix\": null}, \"old\": null, \"changed\": null}','2022-02-28 16:00:52','2022-02-28 16:00:52'),(24,1,'RouteServerFilterProd',10,'CREATED','Route Server Filter (Production) [id:10] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 10, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": 1, \"order_by\": 4, \"protocol\": 4, \"created_at\": \"2022-02-28 16:00:49\", \"updated_at\": \"2022-02-28 16:00:49\", \"customer_id\": 2, \"action_receive\": \"NO_ADVERTISE\", \"received_prefix\": null, \"action_advertise\": \"NO_ADVERTISE\", \"advertised_prefix\": \"*\"}, \"old\": null, \"changed\": null}','2022-02-28 16:00:52','2022-02-28 16:00:52'),(25,1,'RouteServerFilter',3,'UPDATED','Route Server Filter [id:3] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 3, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": \"1\", \"order_by\": 2, \"protocol\": \"4\", \"created_at\": \"2022-02-28 15:57:47\", \"updated_at\": \"2022-02-28 16:02:28\", \"customer_id\": 2, \"action_receive\": \"NO_ADVERTISE\", \"received_prefix\": null, \"action_advertise\": \"NO_ADVERTISE\", \"advertised_prefix\": null}, \"old\": {\"id\": 3, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": 1, \"order_by\": 2, \"protocol\": 4, \"created_at\": \"2022-02-28T15:57:47.000000Z\", \"updated_at\": \"2022-02-28T16:00:08.000000Z\", \"customer_id\": 2, \"action_receive\": \"NO_ADVERTISE\", \"received_prefix\": null, \"action_advertise\": \"NO_ADVERTISE\", \"advertised_prefix\": \"*\"}, \"changed\": {\"updated_at\": \"2022-02-28 16:02:28\", \"advertised_prefix\": null}}','2022-02-28 16:02:28','2022-02-28 16:02:28'),(26,1,'RouteServerFilterProd',11,'CREATED','Route Server Filter (Production) [id:11] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 11, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": 1, \"order_by\": 2, \"protocol\": 4, \"created_at\": \"2022-02-28 15:57:47\", \"updated_at\": \"2022-02-28 16:02:28\", \"customer_id\": 2, \"action_receive\": \"NO_ADVERTISE\", \"received_prefix\": null, \"action_advertise\": \"NO_ADVERTISE\", \"advertised_prefix\": null}, \"old\": null, \"changed\": null}','2022-02-28 16:02:33','2022-02-28 16:02:33'),(27,1,'RouteServerFilterProd',12,'CREATED','Route Server Filter (Production) [id:12] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 12, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": null, \"order_by\": 3, \"protocol\": null, \"created_at\": \"2022-02-28 15:36:04\", \"updated_at\": \"2022-02-28 15:57:58\", \"customer_id\": 2, \"action_receive\": \"AS_IS\", \"received_prefix\": null, \"action_advertise\": \"AS_IS\", \"advertised_prefix\": null}, \"old\": null, \"changed\": null}','2022-02-28 16:02:33','2022-02-28 16:02:33'),(28,1,'RouteServerFilterProd',13,'CREATED','Route Server Filter (Production) [id:13] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 13, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": 1, \"order_by\": 4, \"protocol\": 4, \"created_at\": \"2022-02-28 16:00:49\", \"updated_at\": \"2022-02-28 16:00:49\", \"customer_id\": 2, \"action_receive\": \"NO_ADVERTISE\", \"received_prefix\": null, \"action_advertise\": \"NO_ADVERTISE\", \"advertised_prefix\": \"*\"}, \"old\": null, \"changed\": null}','2022-02-28 16:02:33','2022-02-28 16:02:33'),(29,1,'RouteServerFilter',4,'DELETED','Route Server Filter [id:4] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": null, \"old\": {\"id\": 4, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": 1, \"order_by\": 4, \"protocol\": 4, \"created_at\": \"2022-02-28T16:00:49.000000Z\", \"updated_at\": \"2022-02-28T16:00:49.000000Z\", \"customer_id\": 2, \"action_receive\": \"NO_ADVERTISE\", \"received_prefix\": null, \"action_advertise\": \"NO_ADVERTISE\", \"advertised_prefix\": \"*\"}, \"changed\": null}','2022-02-28 16:02:40','2022-02-28 16:02:40'),(30,1,'RouteServerFilterProd',14,'CREATED','Route Server Filter (Production) [id:14] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 14, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": 1, \"order_by\": 2, \"protocol\": 4, \"created_at\": \"2022-02-28 15:57:47\", \"updated_at\": \"2022-02-28 16:02:28\", \"customer_id\": 2, \"action_receive\": \"NO_ADVERTISE\", \"received_prefix\": null, \"action_advertise\": \"NO_ADVERTISE\", \"advertised_prefix\": null}, \"old\": null, \"changed\": null}','2022-02-28 16:02:44','2022-02-28 16:02:44'),(31,1,'RouteServerFilterProd',15,'CREATED','Route Server Filter (Production) [id:15] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 15, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": null, \"order_by\": 3, \"protocol\": null, \"created_at\": \"2022-02-28 15:36:04\", \"updated_at\": \"2022-02-28 15:57:58\", \"customer_id\": 2, \"action_receive\": \"AS_IS\", \"received_prefix\": null, \"action_advertise\": \"AS_IS\", \"advertised_prefix\": null}, \"old\": null, \"changed\": null}','2022-02-28 16:02:44','2022-02-28 16:02:44'),(32,1,'RouteServerFilter',5,'CREATED','Route Server Filter [id:5] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 5, \"enabled\": true, \"peer_id\": null, \"vlan_id\": \"1\", \"order_by\": 4, \"protocol\": \"6\", \"created_at\": \"2022-02-28 16:06:50\", \"updated_at\": \"2022-02-28 16:06:50\", \"customer_id\": 2, \"action_receive\": \"PREPEND_ONCE\", \"action_advertise\": \"PREPEND_ONCE\", \"advertised_prefix\": null}, \"old\": null, \"changed\": null}','2022-02-28 16:06:50','2022-02-28 16:06:50'),(33,1,'RouteServerFilterProd',16,'CREATED','Route Server Filter (Production) [id:16] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 16, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": 1, \"order_by\": 2, \"protocol\": 4, \"created_at\": \"2022-02-28 15:57:47\", \"updated_at\": \"2022-02-28 16:02:28\", \"customer_id\": 2, \"action_receive\": \"NO_ADVERTISE\", \"received_prefix\": null, \"action_advertise\": \"NO_ADVERTISE\", \"advertised_prefix\": null}, \"old\": null, \"changed\": null}','2022-02-28 16:06:54','2022-02-28 16:06:54'),(34,1,'RouteServerFilterProd',17,'CREATED','Route Server Filter (Production) [id:17] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 17, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": null, \"order_by\": 3, \"protocol\": null, \"created_at\": \"2022-02-28 15:36:04\", \"updated_at\": \"2022-02-28 15:57:58\", \"customer_id\": 2, \"action_receive\": \"AS_IS\", \"received_prefix\": null, \"action_advertise\": \"AS_IS\", \"advertised_prefix\": null}, \"old\": null, \"changed\": null}','2022-02-28 16:06:54','2022-02-28 16:06:54'),(35,1,'RouteServerFilterProd',18,'CREATED','Route Server Filter (Production) [id:18] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 18, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": 1, \"order_by\": 4, \"protocol\": 6, \"created_at\": \"2022-02-28 16:06:50\", \"updated_at\": \"2022-02-28 16:06:50\", \"customer_id\": 2, \"action_receive\": \"PREPEND_ONCE\", \"received_prefix\": null, \"action_advertise\": \"PREPEND_ONCE\", \"advertised_prefix\": null}, \"old\": null, \"changed\": null}','2022-02-28 16:06:54','2022-02-28 16:06:54'),(36,1,'RouteServerFilter',1,'UPDATED','Route Server Filter [id:1] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 1, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": null, \"order_by\": 0, \"protocol\": null, \"created_at\": \"2022-02-28 15:36:04\", \"updated_at\": \"2022-02-28 16:06:56\", \"customer_id\": 2, \"action_receive\": \"AS_IS\", \"received_prefix\": null, \"action_advertise\": \"AS_IS\", \"advertised_prefix\": null}, \"old\": {\"id\": 1, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": null, \"order_by\": 3, \"protocol\": null, \"created_at\": \"2022-02-28T15:36:04.000000Z\", \"updated_at\": \"2022-02-28T15:57:58.000000Z\", \"customer_id\": 2, \"action_receive\": \"AS_IS\", \"received_prefix\": null, \"action_advertise\": \"AS_IS\", \"advertised_prefix\": null}, \"changed\": {\"order_by\": 0, \"updated_at\": \"2022-02-28 16:06:56\"}}','2022-02-28 16:06:56','2022-02-28 16:06:56'),(37,1,'RouteServerFilter',5,'UPDATED','Route Server Filter [id:5] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 5, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": 1, \"order_by\": 3, \"protocol\": 6, \"created_at\": \"2022-02-28 16:06:50\", \"updated_at\": \"2022-02-28 16:06:56\", \"customer_id\": 2, \"action_receive\": \"PREPEND_ONCE\", \"received_prefix\": null, \"action_advertise\": \"PREPEND_ONCE\", \"advertised_prefix\": null}, \"old\": {\"id\": 5, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": 1, \"order_by\": 4, \"protocol\": 6, \"created_at\": \"2022-02-28T16:06:50.000000Z\", \"updated_at\": \"2022-02-28T16:06:50.000000Z\", \"customer_id\": 2, \"action_receive\": \"PREPEND_ONCE\", \"received_prefix\": null, \"action_advertise\": \"PREPEND_ONCE\", \"advertised_prefix\": null}, \"changed\": {\"order_by\": 3, \"updated_at\": \"2022-02-28 16:06:56\"}}','2022-02-28 16:06:56','2022-02-28 16:06:56'),(38,1,'RouteServerFilter',1,'UPDATED','Route Server Filter [id:1] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 1, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": null, \"order_by\": 4, \"protocol\": null, \"created_at\": \"2022-02-28 15:36:04\", \"updated_at\": \"2022-02-28 16:06:56\", \"customer_id\": 2, \"action_receive\": \"AS_IS\", \"received_prefix\": null, \"action_advertise\": \"AS_IS\", \"advertised_prefix\": null}, \"old\": {\"id\": 1, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": null, \"order_by\": 0, \"protocol\": null, \"created_at\": \"2022-02-28T15:36:04.000000Z\", \"updated_at\": \"2022-02-28T16:06:56.000000Z\", \"customer_id\": 2, \"action_receive\": \"AS_IS\", \"received_prefix\": null, \"action_advertise\": \"AS_IS\", \"advertised_prefix\": null}, \"changed\": {\"order_by\": 4}}','2022-02-28 16:06:56','2022-02-28 16:06:56'),(39,1,'RouteServerFilterProd',19,'CREATED','Route Server Filter (Production) [id:19] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 19, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": 1, \"order_by\": 2, \"protocol\": 4, \"created_at\": \"2022-02-28 15:57:47\", \"updated_at\": \"2022-02-28 16:02:28\", \"customer_id\": 2, \"action_receive\": \"NO_ADVERTISE\", \"received_prefix\": null, \"action_advertise\": \"NO_ADVERTISE\", \"advertised_prefix\": null}, \"old\": null, \"changed\": null}','2022-02-28 16:07:00','2022-02-28 16:07:00'),(40,1,'RouteServerFilterProd',20,'CREATED','Route Server Filter (Production) [id:20] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 20, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": 1, \"order_by\": 3, \"protocol\": 6, \"created_at\": \"2022-02-28 16:06:50\", \"updated_at\": \"2022-02-28 16:06:56\", \"customer_id\": 2, \"action_receive\": \"PREPEND_ONCE\", \"received_prefix\": null, \"action_advertise\": \"PREPEND_ONCE\", \"advertised_prefix\": null}, \"old\": null, \"changed\": null}','2022-02-28 16:07:00','2022-02-28 16:07:00'),(41,1,'RouteServerFilterProd',21,'CREATED','Route Server Filter (Production) [id:21] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 21, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": null, \"order_by\": 4, \"protocol\": null, \"created_at\": \"2022-02-28 15:36:04\", \"updated_at\": \"2022-02-28 16:06:56\", \"customer_id\": 2, \"action_receive\": \"AS_IS\", \"received_prefix\": null, \"action_advertise\": \"AS_IS\", \"advertised_prefix\": null}, \"old\": null, \"changed\": null}','2022-02-28 16:07:00','2022-02-28 16:07:00'),(42,1,'RouteServerFilter',6,'CREATED','Route Server Filter [id:6] belonging to Customer [id:2] \'HEAnet\' and Peer [id:4] \'AS112\'','{\"new\": {\"id\": 6, \"enabled\": true, \"peer_id\": \"4\", \"vlan_id\": \"1\", \"order_by\": 5, \"protocol\": \"4\", \"created_at\": \"2022-02-28 16:14:58\", \"updated_at\": \"2022-02-28 16:14:58\", \"customer_id\": 2, \"action_receive\": \"PREPEND_TWICE\", \"received_prefix\": \"192.175.48.0/24\", \"action_advertise\": \"PREPEND_TWICE\", \"advertised_prefix\": \"77.72.72.0/21\"}, \"old\": null, \"changed\": null}','2022-02-28 16:14:58','2022-02-28 16:14:58'),(43,1,'RouteServerFilter',6,'UPDATED','Route Server Filter [id:6] belonging to Customer [id:2] \'HEAnet\' and Peer [id:4] \'AS112\'','{\"new\": {\"id\": 6, \"live\": \"\", \"enabled\": 1, \"peer_id\": 4, \"vlan_id\": 1, \"order_by\": 0, \"protocol\": 4, \"created_at\": \"2022-02-28 16:14:58\", \"updated_at\": \"2022-02-28 16:15:03\", \"customer_id\": 2, \"action_receive\": \"PREPEND_TWICE\", \"received_prefix\": \"192.175.48.0/24\", \"action_advertise\": \"PREPEND_TWICE\", \"advertised_prefix\": \"77.72.72.0/21\"}, \"old\": {\"id\": 6, \"live\": \"\", \"enabled\": 1, \"peer_id\": 4, \"vlan_id\": 1, \"order_by\": 5, \"protocol\": 4, \"created_at\": \"2022-02-28T16:14:58.000000Z\", \"updated_at\": \"2022-02-28T16:14:58.000000Z\", \"customer_id\": 2, \"action_receive\": \"PREPEND_TWICE\", \"received_prefix\": \"192.175.48.0/24\", \"action_advertise\": \"PREPEND_TWICE\", \"advertised_prefix\": \"77.72.72.0/21\"}, \"changed\": {\"order_by\": 0, \"updated_at\": \"2022-02-28 16:15:03\"}}','2022-02-28 16:15:03','2022-02-28 16:15:03'),(44,1,'RouteServerFilter',1,'UPDATED','Route Server Filter [id:1] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 1, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": null, \"order_by\": 5, \"protocol\": null, \"created_at\": \"2022-02-28 15:36:04\", \"updated_at\": \"2022-02-28 16:15:03\", \"customer_id\": 2, \"action_receive\": \"AS_IS\", \"received_prefix\": null, \"action_advertise\": \"AS_IS\", \"advertised_prefix\": null}, \"old\": {\"id\": 1, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": null, \"order_by\": 4, \"protocol\": null, \"created_at\": \"2022-02-28T15:36:04.000000Z\", \"updated_at\": \"2022-02-28T16:06:56.000000Z\", \"customer_id\": 2, \"action_receive\": \"AS_IS\", \"received_prefix\": null, \"action_advertise\": \"AS_IS\", \"advertised_prefix\": null}, \"changed\": {\"order_by\": 5, \"updated_at\": \"2022-02-28 16:15:03\"}}','2022-02-28 16:15:03','2022-02-28 16:15:03'),(45,1,'RouteServerFilter',6,'UPDATED','Route Server Filter [id:6] belonging to Customer [id:2] \'HEAnet\' and Peer [id:4] \'AS112\'','{\"new\": {\"id\": 6, \"live\": \"\", \"enabled\": 1, \"peer_id\": 4, \"vlan_id\": 1, \"order_by\": 4, \"protocol\": 4, \"created_at\": \"2022-02-28 16:14:58\", \"updated_at\": \"2022-02-28 16:15:03\", \"customer_id\": 2, \"action_receive\": \"PREPEND_TWICE\", \"received_prefix\": \"192.175.48.0/24\", \"action_advertise\": \"PREPEND_TWICE\", \"advertised_prefix\": \"77.72.72.0/21\"}, \"old\": {\"id\": 6, \"live\": \"\", \"enabled\": 1, \"peer_id\": 4, \"vlan_id\": 1, \"order_by\": 0, \"protocol\": 4, \"created_at\": \"2022-02-28T16:14:58.000000Z\", \"updated_at\": \"2022-02-28T16:15:03.000000Z\", \"customer_id\": 2, \"action_receive\": \"PREPEND_TWICE\", \"received_prefix\": \"192.175.48.0/24\", \"action_advertise\": \"PREPEND_TWICE\", \"advertised_prefix\": \"77.72.72.0/21\"}, \"changed\": {\"order_by\": 4}}','2022-02-28 16:15:03','2022-02-28 16:15:03'),(46,1,'RouteServerFilterProd',22,'CREATED','Route Server Filter (Production) [id:22] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 22, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": 1, \"order_by\": 2, \"protocol\": 4, \"created_at\": \"2022-02-28 15:57:47\", \"updated_at\": \"2022-02-28 16:02:28\", \"customer_id\": 2, \"action_receive\": \"NO_ADVERTISE\", \"received_prefix\": null, \"action_advertise\": \"NO_ADVERTISE\", \"advertised_prefix\": null}, \"old\": null, \"changed\": null}','2022-02-28 16:15:06','2022-02-28 16:15:06'),(47,1,'RouteServerFilterProd',23,'CREATED','Route Server Filter (Production) [id:23] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 23, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": 1, \"order_by\": 3, \"protocol\": 6, \"created_at\": \"2022-02-28 16:06:50\", \"updated_at\": \"2022-02-28 16:06:56\", \"customer_id\": 2, \"action_receive\": \"PREPEND_ONCE\", \"received_prefix\": null, \"action_advertise\": \"PREPEND_ONCE\", \"advertised_prefix\": null}, \"old\": null, \"changed\": null}','2022-02-28 16:15:06','2022-02-28 16:15:06'),(48,1,'RouteServerFilterProd',24,'CREATED','Route Server Filter (Production) [id:24] belonging to Customer [id:2] \'HEAnet\' and Peer [id:4] \'AS112\'','{\"new\": {\"id\": 24, \"live\": \"\", \"enabled\": 1, \"peer_id\": 4, \"vlan_id\": 1, \"order_by\": 4, \"protocol\": 4, \"created_at\": \"2022-02-28 16:14:58\", \"updated_at\": \"2022-02-28 16:15:03\", \"customer_id\": 2, \"action_receive\": \"PREPEND_TWICE\", \"received_prefix\": \"192.175.48.0/24\", \"action_advertise\": \"PREPEND_TWICE\", \"advertised_prefix\": \"77.72.72.0/21\"}, \"old\": null, \"changed\": null}','2022-02-28 16:15:06','2022-02-28 16:15:06'),(49,1,'RouteServerFilterProd',25,'CREATED','Route Server Filter (Production) [id:25] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 25, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": null, \"order_by\": 5, \"protocol\": null, \"created_at\": \"2022-02-28 15:36:04\", \"updated_at\": \"2022-02-28 16:15:03\", \"customer_id\": 2, \"action_receive\": \"AS_IS\", \"received_prefix\": null, \"action_advertise\": \"AS_IS\", \"advertised_prefix\": null}, \"old\": null, \"changed\": null}','2022-02-28 16:15:06','2022-02-28 16:15:06'),(50,1,'RouteServerFilter',7,'CREATED','Route Server Filter [id:7] belonging to Customer [id:2] \'HEAnet\' and Peer [id:5] \'Imagine\'','{\"new\": {\"id\": 7, \"enabled\": true, \"peer_id\": \"5\", \"vlan_id\": \"1\", \"order_by\": 6, \"protocol\": \"6\", \"created_at\": \"2022-02-28 16:20:53\", \"updated_at\": \"2022-02-28 16:20:53\", \"customer_id\": 2, \"action_receive\": \"PREPEND_THRICE\", \"received_prefix\": \"2001:4d68::/32\", \"action_advertise\": \"PREPEND_THRICE\", \"advertised_prefix\": \"2001:678:24::/48\"}, \"old\": null, \"changed\": null}','2022-02-28 16:20:53','2022-02-28 16:20:53'),(51,1,'RouteServerFilter',7,'UPDATED','Route Server Filter [id:7] belonging to Customer [id:2] \'HEAnet\' and Peer [id:5] \'Imagine\'','{\"new\": {\"id\": 7, \"live\": \"\", \"enabled\": 1, \"peer_id\": 5, \"vlan_id\": 1, \"order_by\": 0, \"protocol\": 6, \"created_at\": \"2022-02-28 16:20:53\", \"updated_at\": \"2022-02-28 16:20:58\", \"customer_id\": 2, \"action_receive\": \"PREPEND_THRICE\", \"received_prefix\": \"2001:4d68::/32\", \"action_advertise\": \"PREPEND_THRICE\", \"advertised_prefix\": \"2001:678:24::/48\"}, \"old\": {\"id\": 7, \"live\": \"\", \"enabled\": 1, \"peer_id\": 5, \"vlan_id\": 1, \"order_by\": 6, \"protocol\": 6, \"created_at\": \"2022-02-28T16:20:53.000000Z\", \"updated_at\": \"2022-02-28T16:20:53.000000Z\", \"customer_id\": 2, \"action_receive\": \"PREPEND_THRICE\", \"received_prefix\": \"2001:4d68::/32\", \"action_advertise\": \"PREPEND_THRICE\", \"advertised_prefix\": \"2001:678:24::/48\"}, \"changed\": {\"order_by\": 0, \"updated_at\": \"2022-02-28 16:20:58\"}}','2022-02-28 16:20:58','2022-02-28 16:20:58'),(52,1,'RouteServerFilter',1,'UPDATED','Route Server Filter [id:1] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 1, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": null, \"order_by\": 6, \"protocol\": null, \"created_at\": \"2022-02-28 15:36:04\", \"updated_at\": \"2022-02-28 16:20:58\", \"customer_id\": 2, \"action_receive\": \"AS_IS\", \"received_prefix\": null, \"action_advertise\": \"AS_IS\", \"advertised_prefix\": null}, \"old\": {\"id\": 1, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": null, \"order_by\": 5, \"protocol\": null, \"created_at\": \"2022-02-28T15:36:04.000000Z\", \"updated_at\": \"2022-02-28T16:15:03.000000Z\", \"customer_id\": 2, \"action_receive\": \"AS_IS\", \"received_prefix\": null, \"action_advertise\": \"AS_IS\", \"advertised_prefix\": null}, \"changed\": {\"order_by\": 6, \"updated_at\": \"2022-02-28 16:20:58\"}}','2022-02-28 16:20:58','2022-02-28 16:20:58'),(53,1,'RouteServerFilter',7,'UPDATED','Route Server Filter [id:7] belonging to Customer [id:2] \'HEAnet\' and Peer [id:5] \'Imagine\'','{\"new\": {\"id\": 7, \"live\": \"\", \"enabled\": 1, \"peer_id\": 5, \"vlan_id\": 1, \"order_by\": 5, \"protocol\": 6, \"created_at\": \"2022-02-28 16:20:53\", \"updated_at\": \"2022-02-28 16:20:58\", \"customer_id\": 2, \"action_receive\": \"PREPEND_THRICE\", \"received_prefix\": \"2001:4d68::/32\", \"action_advertise\": \"PREPEND_THRICE\", \"advertised_prefix\": \"2001:678:24::/48\"}, \"old\": {\"id\": 7, \"live\": \"\", \"enabled\": 1, \"peer_id\": 5, \"vlan_id\": 1, \"order_by\": 0, \"protocol\": 6, \"created_at\": \"2022-02-28T16:20:53.000000Z\", \"updated_at\": \"2022-02-28T16:20:58.000000Z\", \"customer_id\": 2, \"action_receive\": \"PREPEND_THRICE\", \"received_prefix\": \"2001:4d68::/32\", \"action_advertise\": \"PREPEND_THRICE\", \"advertised_prefix\": \"2001:678:24::/48\"}, \"changed\": {\"order_by\": 5}}','2022-02-28 16:20:58','2022-02-28 16:20:58'),(54,1,'RouteServerFilterProd',26,'CREATED','Route Server Filter (Production) [id:26] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 26, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": 1, \"order_by\": 2, \"protocol\": 4, \"created_at\": \"2022-02-28 15:57:47\", \"updated_at\": \"2022-02-28 16:02:28\", \"customer_id\": 2, \"action_receive\": \"NO_ADVERTISE\", \"received_prefix\": null, \"action_advertise\": \"NO_ADVERTISE\", \"advertised_prefix\": null}, \"old\": null, \"changed\": null}','2022-02-28 16:21:02','2022-02-28 16:21:02'),(55,1,'RouteServerFilterProd',27,'CREATED','Route Server Filter (Production) [id:27] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 27, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": 1, \"order_by\": 3, \"protocol\": 6, \"created_at\": \"2022-02-28 16:06:50\", \"updated_at\": \"2022-02-28 16:06:56\", \"customer_id\": 2, \"action_receive\": \"PREPEND_ONCE\", \"received_prefix\": null, \"action_advertise\": \"PREPEND_ONCE\", \"advertised_prefix\": null}, \"old\": null, \"changed\": null}','2022-02-28 16:21:02','2022-02-28 16:21:02'),(56,1,'RouteServerFilterProd',28,'CREATED','Route Server Filter (Production) [id:28] belonging to Customer [id:2] \'HEAnet\' and Peer [id:4] \'AS112\'','{\"new\": {\"id\": 28, \"live\": \"\", \"enabled\": 1, \"peer_id\": 4, \"vlan_id\": 1, \"order_by\": 4, \"protocol\": 4, \"created_at\": \"2022-02-28 16:14:58\", \"updated_at\": \"2022-02-28 16:15:03\", \"customer_id\": 2, \"action_receive\": \"PREPEND_TWICE\", \"received_prefix\": \"192.175.48.0/24\", \"action_advertise\": \"PREPEND_TWICE\", \"advertised_prefix\": \"77.72.72.0/21\"}, \"old\": null, \"changed\": null}','2022-02-28 16:21:02','2022-02-28 16:21:02'),(57,1,'RouteServerFilterProd',29,'CREATED','Route Server Filter (Production) [id:29] belonging to Customer [id:2] \'HEAnet\' and Peer [id:5] \'Imagine\'','{\"new\": {\"id\": 29, \"live\": \"\", \"enabled\": 1, \"peer_id\": 5, \"vlan_id\": 1, \"order_by\": 5, \"protocol\": 6, \"created_at\": \"2022-02-28 16:20:53\", \"updated_at\": \"2022-02-28 16:20:58\", \"customer_id\": 2, \"action_receive\": \"PREPEND_THRICE\", \"received_prefix\": \"2001:4d68::/32\", \"action_advertise\": \"PREPEND_THRICE\", \"advertised_prefix\": \"2001:678:24::/48\"}, \"old\": null, \"changed\": null}','2022-02-28 16:21:02','2022-02-28 16:21:02'),(58,1,'RouteServerFilterProd',30,'CREATED','Route Server Filter (Production) [id:30] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 30, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": null, \"order_by\": 6, \"protocol\": null, \"created_at\": \"2022-02-28 15:36:04\", \"updated_at\": \"2022-02-28 16:20:58\", \"customer_id\": 2, \"action_receive\": \"AS_IS\", \"received_prefix\": null, \"action_advertise\": \"AS_IS\", \"advertised_prefix\": null}, \"old\": null, \"changed\": null}','2022-02-28 16:21:02','2022-02-28 16:21:02'),(59,1,'RouteServerFilter',8,'CREATED','Route Server Filter [id:8] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 8, \"enabled\": true, \"peer_id\": null, \"vlan_id\": \"2\", \"order_by\": 7, \"protocol\": null, \"created_at\": \"2022-02-28 16:30:08\", \"updated_at\": \"2022-02-28 16:30:08\", \"customer_id\": 2, \"action_receive\": \"PREPEND_THRICE\", \"action_advertise\": \"PREPEND_THRICE\"}, \"old\": null, \"changed\": null}','2022-02-28 16:30:08','2022-02-28 16:30:08'),(60,1,'RouteServerFilterProd',31,'CREATED','Route Server Filter (Production) [id:31] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 31, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": 1, \"order_by\": 2, \"protocol\": 4, \"created_at\": \"2022-02-28 15:57:47\", \"updated_at\": \"2022-02-28 16:02:28\", \"customer_id\": 2, \"action_receive\": \"NO_ADVERTISE\", \"received_prefix\": null, \"action_advertise\": \"NO_ADVERTISE\", \"advertised_prefix\": null}, \"old\": null, \"changed\": null}','2022-02-28 16:30:13','2022-02-28 16:30:13'),(61,1,'RouteServerFilterProd',32,'CREATED','Route Server Filter (Production) [id:32] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 32, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": 1, \"order_by\": 3, \"protocol\": 6, \"created_at\": \"2022-02-28 16:06:50\", \"updated_at\": \"2022-02-28 16:06:56\", \"customer_id\": 2, \"action_receive\": \"PREPEND_ONCE\", \"received_prefix\": null, \"action_advertise\": \"PREPEND_ONCE\", \"advertised_prefix\": null}, \"old\": null, \"changed\": null}','2022-02-28 16:30:13','2022-02-28 16:30:13'),(62,1,'RouteServerFilterProd',33,'CREATED','Route Server Filter (Production) [id:33] belonging to Customer [id:2] \'HEAnet\' and Peer [id:4] \'AS112\'','{\"new\": {\"id\": 33, \"live\": \"\", \"enabled\": 1, \"peer_id\": 4, \"vlan_id\": 1, \"order_by\": 4, \"protocol\": 4, \"created_at\": \"2022-02-28 16:14:58\", \"updated_at\": \"2022-02-28 16:15:03\", \"customer_id\": 2, \"action_receive\": \"PREPEND_TWICE\", \"received_prefix\": \"192.175.48.0/24\", \"action_advertise\": \"PREPEND_TWICE\", \"advertised_prefix\": \"77.72.72.0/21\"}, \"old\": null, \"changed\": null}','2022-02-28 16:30:13','2022-02-28 16:30:13'),(63,1,'RouteServerFilterProd',34,'CREATED','Route Server Filter (Production) [id:34] belonging to Customer [id:2] \'HEAnet\' and Peer [id:5] \'Imagine\'','{\"new\": {\"id\": 34, \"live\": \"\", \"enabled\": 1, \"peer_id\": 5, \"vlan_id\": 1, \"order_by\": 5, \"protocol\": 6, \"created_at\": \"2022-02-28 16:20:53\", \"updated_at\": \"2022-02-28 16:20:58\", \"customer_id\": 2, \"action_receive\": \"PREPEND_THRICE\", \"received_prefix\": \"2001:4d68::/32\", \"action_advertise\": \"PREPEND_THRICE\", \"advertised_prefix\": \"2001:678:24::/48\"}, \"old\": null, \"changed\": null}','2022-02-28 16:30:13','2022-02-28 16:30:13'),(64,1,'RouteServerFilterProd',35,'CREATED','Route Server Filter (Production) [id:35] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 35, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": null, \"order_by\": 6, \"protocol\": null, \"created_at\": \"2022-02-28 15:36:04\", \"updated_at\": \"2022-02-28 16:20:58\", \"customer_id\": 2, \"action_receive\": \"AS_IS\", \"received_prefix\": null, \"action_advertise\": \"AS_IS\", \"advertised_prefix\": null}, \"old\": null, \"changed\": null}','2022-02-28 16:30:13','2022-02-28 16:30:13'),(65,1,'RouteServerFilterProd',36,'CREATED','Route Server Filter (Production) [id:36] belonging to Customer [id:2] \'HEAnet\' and Peer [id:0] \'\'','{\"new\": {\"id\": 36, \"live\": \"\", \"enabled\": 1, \"peer_id\": null, \"vlan_id\": 2, \"order_by\": 7, \"protocol\": null, \"created_at\": \"2022-02-28 16:30:08\", \"updated_at\": \"2022-02-28 16:30:08\", \"customer_id\": 2, \"action_receive\": \"PREPEND_THRICE\", \"received_prefix\": null, \"action_advertise\": \"PREPEND_THRICE\", \"advertised_prefix\": null}, \"old\": null, \"changed\": null}','2022-02-28 16:30:13','2022-02-28 16:30:13'); +/*!40000 ALTER TABLE `log` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `logos` +-- + +DROP TABLE IF EXISTS `logos`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `logos` ( + `id` int NOT NULL AUTO_INCREMENT, + `customer_id` int DEFAULT NULL, + `original_name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL, + `stored_name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL, + `uploaded_by` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL, + `width` int NOT NULL, + `height` int NOT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `UNIQ_9F54004F9395C3F3` (`customer_id`), + CONSTRAINT `FK_9F54004F9395C3F3` FOREIGN KEY (`customer_id`) REFERENCES `cust` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `logos` +-- + +LOCK TABLES `logos` WRITE; +/*!40000 ALTER TABLE `logos` DISABLE KEYS */; +/*!40000 ALTER TABLE `logos` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `macaddress` +-- + +DROP TABLE IF EXISTS `macaddress`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `macaddress` ( + `id` int NOT NULL AUTO_INCREMENT, + `virtualinterfaceid` int DEFAULT NULL, + `firstseen` datetime DEFAULT NULL, + `lastseen` datetime DEFAULT NULL, + `mac` varchar(12) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `IDX_42CD65F6BFDF15D5` (`virtualinterfaceid`), + CONSTRAINT `FK_42CD65F6BFDF15D5` FOREIGN KEY (`virtualinterfaceid`) REFERENCES `virtualinterface` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `macaddress` +-- + +LOCK TABLES `macaddress` WRITE; +/*!40000 ALTER TABLE `macaddress` DISABLE KEYS */; +/*!40000 ALTER TABLE `macaddress` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `migrations` +-- + +DROP TABLE IF EXISTS `migrations`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `migrations` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `migration` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `batch` int NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=37 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `migrations` +-- + +LOCK TABLES `migrations` WRITE; +/*!40000 ALTER TABLE `migrations` DISABLE KEYS */; +INSERT INTO `migrations` VALUES (1,'2014_10_12_100000_create_password_resets_table',1),(2,'2018_08_08_100000_create_telescope_entries_table',1),(3,'2019_03_25_211956_create_failed_jobs_table',1),(4,'2020_02_06_204556_create_docstore_directories',2),(5,'2020_02_06_204608_create_docstore_files',2),(6,'2020_02_06_204911_create_docstore_logs',2),(7,'2020_03_09_110945_create_docstore_customer_directories',3),(8,'2020_03_09_111505_create_docstore_customer_files',3),(9,'2020_07_21_094354_create_route_server_filters',4),(12,'2020_09_03_153723_add_timestamps',5),(13,'2020_09_18_095136_delete_ixp_table',6),(14,'2020_11_16_102415_database_fixes',7),(15,'2021_03_12_150418_create_log_table',8),(16,'2021_04_14_125742_user_pref',9),(17,'2021_04_14_101948_update_timestamps',10),(18,'2021_05_18_085721_add_note_infrastructure',11),(19,'2021_05_18_114206_update_pp_prefix_size',12),(20,'2020_06_01_143931_database_schema_at_end_v5',13),(21,'2021_03_30_124916_create_atlas_probes',13),(22,'2021_03_30_125238_create_atlas_runs',13),(23,'2021_03_30_125422_create_atlas_measurements',13),(24,'2021_03_30_125723_create_atlas_results',13),(25,'2021_06_11_141137_update_db_doctrine2eloquent',13),(26,'2021_07_20_134716_fix_last_updated_and_timestamps',13),(27,'2021_09_16_195333_add_rate_limit_col_to_physint',13),(28,'2021_09_17_144421_modernise_irrdb_conf_table',13),(29,'2021_09_21_100354_create_route_server_filters_prod',14),(30,'2021_09_21_162700_rs_pairing',15),(31,'2022_02_12_183121_add_colo_pp_type_patch_panel',15),(32,'2023_09_26_191150_add_registration_details',15),(33,'2024_03_18_191322_add_export_to_ixf_vlan',15),(34,'2024_08_10_125003_create_irrdb_update_logs',16),(35,'2024_09_05_111855_create_p2p_daily_stats_table',17),(36,'2024_05_29_102028_reset-views',18); +/*!40000 ALTER TABLE `migrations` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `netinfo` +-- + +DROP TABLE IF EXISTS `netinfo`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `netinfo` ( + `id` int NOT NULL AUTO_INCREMENT, + `vlan_id` int NOT NULL, + `protocol` int NOT NULL, + `property` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL, + `ix` int NOT NULL DEFAULT '0', + `value` longtext CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL, + PRIMARY KEY (`id`), + KEY `IDX_F843DE6B8B4937A1` (`vlan_id`), + KEY `VlanProtoProp` (`protocol`,`property`,`vlan_id`), + CONSTRAINT `FK_F843DE6B8B4937A1` FOREIGN KEY (`vlan_id`) REFERENCES `vlan` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `netinfo` +-- + +LOCK TABLES `netinfo` WRITE; +/*!40000 ALTER TABLE `netinfo` DISABLE KEYS */; +/*!40000 ALTER TABLE `netinfo` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `networkinfo` +-- + +DROP TABLE IF EXISTS `networkinfo`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `networkinfo` ( + `id` int NOT NULL AUTO_INCREMENT, + `vlanid` int DEFAULT NULL, + `protocol` int DEFAULT NULL, + `network` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `masklen` int DEFAULT NULL, + `rs1address` varchar(40) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `rs2address` varchar(40) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `dnsfile` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `IDX_6A0AF167F48D6D0` (`vlanid`), + CONSTRAINT `FK_6A0AF167F48D6D0` FOREIGN KEY (`vlanid`) REFERENCES `vlan` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `networkinfo` +-- + +LOCK TABLES `networkinfo` WRITE; +/*!40000 ALTER TABLE `networkinfo` DISABLE KEYS */; +INSERT INTO `networkinfo` VALUES (1,1,4,'192.0.2.0',25,'192.0.2.8','192.0.2.9',NULL,NULL,NULL),(2,1,6,'2001:db8::',64,'2001:db8::8','2001:db8::9',NULL,NULL,NULL),(3,2,4,'192.0.2.128',25,'192.0.2.136','192.0.2.137',NULL,NULL,NULL),(4,2,6,'2001:db8:0:2::',64,'2001:db8:0:2::8','2001:db8:0:2::9',NULL,NULL,NULL),(5,3,4,'192.0.2.0',25,'192.0.2.8','192.0.2.9',NULL,NULL,NULL),(6,3,6,'2001:db8::',64,'2001:db8::8','2001:db8::9',NULL,NULL,NULL); +/*!40000 ALTER TABLE `networkinfo` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `oui` +-- + +DROP TABLE IF EXISTS `oui`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `oui` ( + `id` int NOT NULL AUTO_INCREMENT, + `oui` varchar(6) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL, + `organisation` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `UNIQ_DAEC0140DAEC0140` (`oui`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `oui` +-- + +LOCK TABLES `oui` WRITE; +/*!40000 ALTER TABLE `oui` DISABLE KEYS */; +/*!40000 ALTER TABLE `oui` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `p2p_daily_stats` +-- + +DROP TABLE IF EXISTS `p2p_daily_stats`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `p2p_daily_stats` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `day` date NOT NULL, + `cust_id` int NOT NULL, + `peer_id` int NOT NULL, + `ipv4_total_in` double DEFAULT NULL, + `ipv4_total_out` double DEFAULT NULL, + `ipv6_total_in` double DEFAULT NULL, + `ipv6_total_out` double DEFAULT NULL, + `ipv4_max_in` double DEFAULT NULL, + `ipv4_max_out` double DEFAULT NULL, + `ipv6_max_in` double DEFAULT NULL, + `ipv6_max_out` double DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `p2p_daily_stats_day_cust_id_peer_id_unique` (`day`,`cust_id`,`peer_id`), + KEY `p2p_daily_stats_cust_id_foreign` (`cust_id`), + CONSTRAINT `p2p_daily_stats_cust_id_foreign` FOREIGN KEY (`cust_id`) REFERENCES `cust` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `p2p_daily_stats` +-- + +LOCK TABLES `p2p_daily_stats` WRITE; +/*!40000 ALTER TABLE `p2p_daily_stats` DISABLE KEYS */; +/*!40000 ALTER TABLE `p2p_daily_stats` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `password_resets` +-- + +DROP TABLE IF EXISTS `password_resets`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `password_resets` ( + `email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `token` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `created_at` timestamp NULL DEFAULT NULL, + KEY `password_resets_email_index` (`email`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `password_resets` +-- + +LOCK TABLES `password_resets` WRITE; +/*!40000 ALTER TABLE `password_resets` DISABLE KEYS */; +/*!40000 ALTER TABLE `password_resets` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `patch_panel` +-- + +DROP TABLE IF EXISTS `patch_panel`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `patch_panel` ( + `id` int NOT NULL AUTO_INCREMENT, + `cabinet_id` int DEFAULT NULL, + `name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL, + `colo_reference` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL, + `cable_type` int NOT NULL, + `connector_type` int NOT NULL, + `installation_date` datetime DEFAULT NULL, + `port_prefix` varchar(20) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL, + `chargeable` int NOT NULL DEFAULT '0', + `location_notes` longtext CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL, + `active` tinyint(1) NOT NULL DEFAULT '1', + `colo_pp_type` tinyint NOT NULL DEFAULT '1', + `u_position` int DEFAULT NULL, + `mounted_at` smallint DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `IDX_79A52562D351EC` (`cabinet_id`), + CONSTRAINT `FK_79A52562D351EC` FOREIGN KEY (`cabinet_id`) REFERENCES `cabinet` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `patch_panel` +-- + +LOCK TABLES `patch_panel` WRITE; +/*!40000 ALTER TABLE `patch_panel` DISABLE KEYS */; +/*!40000 ALTER TABLE `patch_panel` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `patch_panel_port` +-- + +DROP TABLE IF EXISTS `patch_panel_port`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `patch_panel_port` ( + `id` int NOT NULL AUTO_INCREMENT, + `switch_port_id` int DEFAULT NULL, + `patch_panel_id` int DEFAULT NULL, + `customer_id` int DEFAULT NULL, + `state` int NOT NULL, + `notes` longtext CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci, + `assigned_at` date DEFAULT NULL, + `connected_at` date DEFAULT NULL, + `cease_requested_at` date DEFAULT NULL, + `ceased_at` date DEFAULT NULL, + `last_state_change` date DEFAULT NULL, + `internal_use` tinyint(1) NOT NULL DEFAULT '0', + `chargeable` int NOT NULL DEFAULT '2', + `duplex_master_id` int DEFAULT NULL, + `number` smallint NOT NULL, + `colo_circuit_ref` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `ticket_ref` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `private_notes` longtext CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci, + `owned_by` int NOT NULL DEFAULT '0', + `loa_code` varchar(25) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `description` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `colo_billing_ref` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `UNIQ_4BE40BC2C1DA6A2A` (`switch_port_id`), + KEY `IDX_4BE40BC2635D5D87` (`patch_panel_id`), + KEY `IDX_4BE40BC29395C3F3` (`customer_id`), + KEY `IDX_4BE40BC23838446` (`duplex_master_id`), + CONSTRAINT `FK_4BE40BC23838446` FOREIGN KEY (`duplex_master_id`) REFERENCES `patch_panel_port` (`id`), + CONSTRAINT `FK_4BE40BC2635D5D87` FOREIGN KEY (`patch_panel_id`) REFERENCES `patch_panel` (`id`), + CONSTRAINT `FK_4BE40BC29395C3F3` FOREIGN KEY (`customer_id`) REFERENCES `cust` (`id`), + CONSTRAINT `FK_4BE40BC2C1DA6A2A` FOREIGN KEY (`switch_port_id`) REFERENCES `switchport` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `patch_panel_port` +-- + +LOCK TABLES `patch_panel_port` WRITE; +/*!40000 ALTER TABLE `patch_panel_port` DISABLE KEYS */; +/*!40000 ALTER TABLE `patch_panel_port` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `patch_panel_port_file` +-- + +DROP TABLE IF EXISTS `patch_panel_port_file`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `patch_panel_port_file` ( + `id` int NOT NULL AUTO_INCREMENT, + `patch_panel_port_id` int DEFAULT NULL, + `name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL, + `type` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL, + `uploaded_at` datetime NOT NULL, + `uploaded_by` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL, + `size` int NOT NULL, + `is_private` tinyint(1) NOT NULL DEFAULT '0', + `storage_location` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `IDX_28089403B0F978FF` (`patch_panel_port_id`), + CONSTRAINT `FK_28089403B0F978FF` FOREIGN KEY (`patch_panel_port_id`) REFERENCES `patch_panel_port` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `patch_panel_port_file` +-- + +LOCK TABLES `patch_panel_port_file` WRITE; +/*!40000 ALTER TABLE `patch_panel_port_file` DISABLE KEYS */; +/*!40000 ALTER TABLE `patch_panel_port_file` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `patch_panel_port_history` +-- + +DROP TABLE IF EXISTS `patch_panel_port_history`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `patch_panel_port_history` ( + `id` int NOT NULL AUTO_INCREMENT, + `patch_panel_port_id` int DEFAULT NULL, + `state` int NOT NULL, + `notes` longtext CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci, + `assigned_at` date DEFAULT NULL, + `connected_at` date DEFAULT NULL, + `cease_requested_at` date DEFAULT NULL, + `ceased_at` date DEFAULT NULL, + `internal_use` tinyint(1) NOT NULL DEFAULT '0', + `chargeable` int NOT NULL DEFAULT '0', + `customer` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `switchport` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `duplex_master_id` int DEFAULT NULL, + `number` smallint NOT NULL, + `colo_circuit_ref` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `ticket_ref` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `private_notes` longtext CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci, + `owned_by` int NOT NULL DEFAULT '0', + `description` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `colo_billing_ref` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `cust_id` int DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `IDX_CB80B54AB0F978FF` (`patch_panel_port_id`), + KEY `IDX_CB80B54A3838446` (`duplex_master_id`), + CONSTRAINT `FK_CB80B54A3838446` FOREIGN KEY (`duplex_master_id`) REFERENCES `patch_panel_port_history` (`id`), + CONSTRAINT `FK_CB80B54AB0F978FF` FOREIGN KEY (`patch_panel_port_id`) REFERENCES `patch_panel_port` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `patch_panel_port_history` +-- + +LOCK TABLES `patch_panel_port_history` WRITE; +/*!40000 ALTER TABLE `patch_panel_port_history` DISABLE KEYS */; +/*!40000 ALTER TABLE `patch_panel_port_history` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `patch_panel_port_history_file` +-- + +DROP TABLE IF EXISTS `patch_panel_port_history_file`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `patch_panel_port_history_file` ( + `id` int NOT NULL AUTO_INCREMENT, + `patch_panel_port_history_id` int DEFAULT NULL, + `name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL, + `type` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL, + `uploaded_at` datetime NOT NULL, + `uploaded_by` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL, + `size` int NOT NULL, + `is_private` tinyint(1) NOT NULL DEFAULT '0', + `storage_location` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `IDX_206EAD4E6F461430` (`patch_panel_port_history_id`), + CONSTRAINT `FK_206EAD4E6F461430` FOREIGN KEY (`patch_panel_port_history_id`) REFERENCES `patch_panel_port_history` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `patch_panel_port_history_file` +-- + +LOCK TABLES `patch_panel_port_history_file` WRITE; +/*!40000 ALTER TABLE `patch_panel_port_history_file` DISABLE KEYS */; +/*!40000 ALTER TABLE `patch_panel_port_history_file` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `peering_manager` +-- + +DROP TABLE IF EXISTS `peering_manager`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `peering_manager` ( + `id` int NOT NULL AUTO_INCREMENT, + `custid` int DEFAULT NULL, + `peerid` int DEFAULT NULL, + `email_last_sent` datetime DEFAULT NULL, + `emails_sent` int DEFAULT NULL, + `peered` tinyint(1) DEFAULT NULL, + `rejected` tinyint(1) DEFAULT NULL, + `notes` longtext CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `IDX_35A72597DA0209B9` (`custid`), + KEY `IDX_35A725974E5F9AFF` (`peerid`), + CONSTRAINT `FK_35A725974E5F9AFF` FOREIGN KEY (`peerid`) REFERENCES `cust` (`id`) ON DELETE CASCADE, + CONSTRAINT `FK_35A72597DA0209B9` FOREIGN KEY (`custid`) REFERENCES `cust` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `peering_manager` +-- + +LOCK TABLES `peering_manager` WRITE; +/*!40000 ALTER TABLE `peering_manager` DISABLE KEYS */; +/*!40000 ALTER TABLE `peering_manager` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `peering_matrix` +-- + +DROP TABLE IF EXISTS `peering_matrix`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `peering_matrix` ( + `id` int NOT NULL AUTO_INCREMENT, + `x_custid` int DEFAULT NULL, + `y_custid` int DEFAULT NULL, + `vlan` int DEFAULT NULL, + `x_as` int DEFAULT NULL, + `y_as` int DEFAULT NULL, + `peering_status` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `updated` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `IDX_C1A6F6F9A4CA6408` (`x_custid`), + KEY `IDX_C1A6F6F968606496` (`y_custid`), + CONSTRAINT `FK_C1A6F6F968606496` FOREIGN KEY (`y_custid`) REFERENCES `cust` (`id`) ON DELETE CASCADE, + CONSTRAINT `FK_C1A6F6F9A4CA6408` FOREIGN KEY (`x_custid`) REFERENCES `cust` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `peering_matrix` +-- + +LOCK TABLES `peering_matrix` WRITE; +/*!40000 ALTER TABLE `peering_matrix` DISABLE KEYS */; +/*!40000 ALTER TABLE `peering_matrix` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `physicalinterface` +-- + +DROP TABLE IF EXISTS `physicalinterface`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `physicalinterface` ( + `id` int NOT NULL AUTO_INCREMENT, + `switchportid` int DEFAULT NULL, + `fanout_physical_interface_id` int DEFAULT NULL, + `virtualinterfaceid` int DEFAULT NULL, + `status` int DEFAULT NULL, + `speed` int DEFAULT NULL, + `duplex` varchar(16) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `rate_limit` int unsigned DEFAULT NULL, + `notes` longtext CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci, + `autoneg` tinyint(1) NOT NULL DEFAULT '1', + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `UNIQ_5FFF4D60E5F6FACB` (`switchportid`), + UNIQUE KEY `UNIQ_5FFF4D602E68AB8C` (`fanout_physical_interface_id`), + KEY `IDX_5FFF4D60BFDF15D5` (`virtualinterfaceid`), + CONSTRAINT `FK_5FFF4D602E68AB8C` FOREIGN KEY (`fanout_physical_interface_id`) REFERENCES `physicalinterface` (`id`), + CONSTRAINT `FK_5FFF4D60BFDF15D5` FOREIGN KEY (`virtualinterfaceid`) REFERENCES `virtualinterface` (`id`) ON DELETE CASCADE, + CONSTRAINT `FK_5FFF4D60E5F6FACB` FOREIGN KEY (`switchportid`) REFERENCES `switchport` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `physicalinterface` +-- + +LOCK TABLES `physicalinterface` WRITE; +/*!40000 ALTER TABLE `physicalinterface` DISABLE KEYS */; +INSERT INTO `physicalinterface` VALUES (1,3,NULL,1,1,1000,'full',NULL,'',1,NULL,NULL),(2,4,NULL,1,1,1000,'full',NULL,'',1,NULL,NULL),(3,25,NULL,2,1,1000,'full',NULL,NULL,1,NULL,NULL),(4,8,NULL,3,1,100,'full',NULL,NULL,1,NULL,NULL),(5,6,NULL,4,1,10,'full',NULL,NULL,1,NULL,NULL),(6,30,NULL,5,1,10,'full',NULL,NULL,1,NULL,NULL),(7,9,NULL,6,1,1000,'full',NULL,NULL,1,NULL,NULL),(8,32,NULL,7,1,10000,'full',NULL,NULL,1,NULL,NULL),(9,18,NULL,8,1,1000,'full',NULL,NULL,1,NULL,NULL),(10,42,NULL,9,1,1000,'full',NULL,NULL,1,NULL,NULL),(11,19,NULL,8,1,1000,'full',NULL,NULL,1,NULL,NULL),(12,43,NULL,9,1,1000,'full',NULL,NULL,1,NULL,NULL),(13,27,NULL,10,4,1000,'full',NULL,NULL,1,NULL,NULL); +/*!40000 ALTER TABLE `physicalinterface` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `route_server_filters` +-- + +DROP TABLE IF EXISTS `route_server_filters`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `route_server_filters` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `customer_id` int DEFAULT NULL, + `peer_id` int DEFAULT NULL, + `vlan_id` int DEFAULT NULL, + `received_prefix` varchar(43) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `advertised_prefix` varchar(43) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `protocol` smallint DEFAULT NULL, + `action_advertise` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `action_receive` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `enabled` tinyint(1) NOT NULL DEFAULT '1', + `order_by` int NOT NULL, + `live` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `route_server_filters_customer_id_order_by_unique` (`customer_id`,`order_by`), + KEY `route_server_filters_peer_id_foreign` (`peer_id`), + KEY `route_server_filters_vlan_id_foreign` (`vlan_id`), + CONSTRAINT `route_server_filters_customer_id_foreign` FOREIGN KEY (`customer_id`) REFERENCES `cust` (`id`), + CONSTRAINT `route_server_filters_peer_id_foreign` FOREIGN KEY (`peer_id`) REFERENCES `cust` (`id`), + CONSTRAINT `route_server_filters_vlan_id_foreign` FOREIGN KEY (`vlan_id`) REFERENCES `vlan` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `route_server_filters` +-- + +LOCK TABLES `route_server_filters` WRITE; +/*!40000 ALTER TABLE `route_server_filters` DISABLE KEYS */; +INSERT INTO `route_server_filters` VALUES (1,2,NULL,NULL,NULL,NULL,NULL,'AS_IS','AS_IS',1,6,'','2022-02-28 15:36:04','2022-02-28 16:20:58'),(3,2,NULL,1,NULL,NULL,4,'NO_ADVERTISE','NO_ADVERTISE',1,2,'','2022-02-28 15:57:47','2022-02-28 16:02:28'),(5,2,NULL,1,NULL,NULL,6,'PREPEND_ONCE','PREPEND_ONCE',1,3,'','2022-02-28 16:06:50','2022-02-28 16:06:56'),(6,2,4,1,'192.175.48.0/24','77.72.72.0/21',4,'PREPEND_TWICE','PREPEND_TWICE',1,4,'','2022-02-28 16:14:58','2022-02-28 16:15:03'),(7,2,5,1,'2001:4d68::/32','2001:678:24::/48',6,'PREPEND_THRICE','PREPEND_THRICE',1,5,'','2022-02-28 16:20:53','2022-02-28 16:20:58'),(8,2,NULL,2,NULL,NULL,NULL,'PREPEND_THRICE','PREPEND_THRICE',1,7,'','2022-02-28 16:30:08','2022-02-28 16:30:08'); +/*!40000 ALTER TABLE `route_server_filters` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `route_server_filters_prod` +-- + +DROP TABLE IF EXISTS `route_server_filters_prod`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `route_server_filters_prod` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `customer_id` int DEFAULT NULL, + `peer_id` int DEFAULT NULL, + `vlan_id` int DEFAULT NULL, + `received_prefix` varchar(43) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `advertised_prefix` varchar(43) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `protocol` smallint DEFAULT NULL, + `action_advertise` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `action_receive` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `enabled` tinyint(1) NOT NULL DEFAULT '1', + `order_by` int NOT NULL, + `live` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `route_server_filters_prod_customer_id_order_by_unique` (`customer_id`,`order_by`), + KEY `route_server_filters_prod_peer_id_foreign` (`peer_id`), + KEY `route_server_filters_prod_vlan_id_foreign` (`vlan_id`), + CONSTRAINT `route_server_filters_prod_customer_id_foreign` FOREIGN KEY (`customer_id`) REFERENCES `cust` (`id`), + CONSTRAINT `route_server_filters_prod_peer_id_foreign` FOREIGN KEY (`peer_id`) REFERENCES `cust` (`id`), + CONSTRAINT `route_server_filters_prod_vlan_id_foreign` FOREIGN KEY (`vlan_id`) REFERENCES `vlan` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=37 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `route_server_filters_prod` +-- + +LOCK TABLES `route_server_filters_prod` WRITE; +/*!40000 ALTER TABLE `route_server_filters_prod` DISABLE KEYS */; +INSERT INTO `route_server_filters_prod` VALUES (31,2,NULL,1,NULL,NULL,4,'NO_ADVERTISE','NO_ADVERTISE',1,2,'','2022-02-28 15:57:47','2022-02-28 16:02:28'),(32,2,NULL,1,NULL,NULL,6,'PREPEND_ONCE','PREPEND_ONCE',1,3,'','2022-02-28 16:06:50','2022-02-28 16:06:56'),(33,2,4,1,'192.175.48.0/24','77.72.72.0/21',4,'PREPEND_TWICE','PREPEND_TWICE',1,4,'','2022-02-28 16:14:58','2022-02-28 16:15:03'),(34,2,5,1,'2001:4d68::/32','2001:678:24::/48',6,'PREPEND_THRICE','PREPEND_THRICE',1,5,'','2022-02-28 16:20:53','2022-02-28 16:20:58'),(35,2,NULL,NULL,NULL,NULL,NULL,'AS_IS','AS_IS',1,6,'','2022-02-28 15:36:04','2022-02-28 16:20:58'),(36,2,NULL,2,NULL,NULL,NULL,'PREPEND_THRICE','PREPEND_THRICE',1,7,'','2022-02-28 16:30:08','2022-02-28 16:30:08'); +/*!40000 ALTER TABLE `route_server_filters_prod` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `routers` +-- + +DROP TABLE IF EXISTS `routers`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `routers` ( + `id` int NOT NULL AUTO_INCREMENT, + `pair_id` int DEFAULT NULL, + `vlan_id` int NOT NULL, + `handle` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL, + `protocol` smallint unsigned NOT NULL, + `type` smallint unsigned NOT NULL, + `name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL, + `shortname` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL, + `router_id` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL, + `peering_ip` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL, + `asn` int unsigned NOT NULL, + `software` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL, + `mgmt_host` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL, + `api` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `api_type` smallint unsigned NOT NULL, + `lg_access` smallint unsigned DEFAULT NULL, + `quarantine` tinyint(1) NOT NULL, + `bgp_lc` tinyint(1) NOT NULL, + `template` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL, + `skip_md5` tinyint(1) NOT NULL, + `last_update_started` datetime DEFAULT NULL, + `rpki` tinyint(1) NOT NULL DEFAULT '0', + `software_version` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `operating_system` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `operating_system_version` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `rfc1997_passthru` tinyint(1) NOT NULL DEFAULT '0', + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + `last_updated` datetime DEFAULT NULL, + `pause_updates` tinyint(1) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + UNIQUE KEY `UNIQ_504FC9BE918020D9` (`handle`), + KEY `IDX_504FC9BE8B4937A1` (`vlan_id`), + KEY `routers_pair_id_foreign` (`pair_id`), + CONSTRAINT `FK_504FC9BE8B4937A1` FOREIGN KEY (`vlan_id`) REFERENCES `vlan` (`id`), + CONSTRAINT `routers_pair_id_foreign` FOREIGN KEY (`pair_id`) REFERENCES `routers` (`id`) ON DELETE SET NULL +) ENGINE=InnoDB AUTO_INCREMENT=33 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `routers` +-- + +LOCK TABLES `routers` WRITE; +/*!40000 ALTER TABLE `routers` DISABLE KEYS */; +INSERT INTO `routers` VALUES (1,NULL,1,'rc1-lan1-ipv4',4,2,'INEX LAN1 - Route Collector - IPv4','RC1 - LAN1 - IPv4','192.0.2.8','192.0.2.8',65500,'1','203.0.113.8','http://rc1-lan1-ipv4.mgmt.example.com/api',1,0,0,0,'api/v4/router/collector/bird/standard',0,NULL,0,NULL,NULL,NULL,0,NULL,NULL,NULL,0),(2,NULL,1,'rc1-lan1-ipv6',6,2,'INEX LAN1 - Route Collector - IPv6','RC1 - LAN1 - IPv6','192.0.2.8','2001:db8::8',65500,'1','2001:db8:0:0:2::8','http://rc1-lan1-ipv6.mgmt.example.com/api',1,0,0,0,'api/v4/router/collector/bird/standard',0,NULL,0,NULL,NULL,NULL,0,NULL,NULL,NULL,0),(3,NULL,2,'rc1-lan2-ipv4',4,2,'INEX LAN2 - Route Collector - IPv4','RC1 - LAN2 - IPv4','192.0.2.9','192.0.2.9',65500,'1','203.0.113.9','http://rc1-lan2-ipv4.mgmt.example.com/api',1,0,0,0,'api/v4/router/collector/bird/standard',0,NULL,0,NULL,NULL,NULL,0,NULL,NULL,NULL,0),(4,NULL,2,'rc1-lan2-ipv6',6,2,'INEX LAN2 - Route Collector - IPv6','RC1 - LAN2 - IPv6','192.0.2.9','2001:db8::9',65500,'1','2001:db8:0:0:2::9','http://rc1-lan2-ipv6.mgmt.example.com/api',1,0,0,0,'api/v4/router/collector/bird/standard',0,NULL,0,NULL,NULL,NULL,0,NULL,NULL,NULL,0),(5,NULL,1,'rs1-lan1-ipv4',4,1,'INEX LAN1 - Route Server - IPv4','RS1 - LAN1 - IPv4','192.0.2.18','192.0.2.18',65501,'1','203.0.113.18','http://rs1-lan1-ipv4.mgmt.example.com/api',0,0,0,0,'api/v4/router/server/bird/standard',0,NULL,0,NULL,NULL,NULL,0,NULL,NULL,NULL,0),(6,NULL,1,'rs1-lan1-ipv6',6,1,'INEX LAN1 - Route Server - IPv6','RS1 - LAN1 - IPv6','192.0.2.18','2001:db8::18',65501,'1','2001:db8:0:0:2::18','http://rs1-lan1-ipv6.mgmt.example.com/api',1,0,0,0,'api/v4/router/server/bird/standard',0,NULL,0,NULL,NULL,NULL,0,NULL,NULL,NULL,0),(7,NULL,2,'rs1-lan2-ipv4',4,1,'INEX LAN2 - Route Server - IPv4','RS1 - LAN2 - IPv4','192.0.2.19','192.0.2.19',65501,'1','203.0.113.19','http://rs1-lan2-ipv4.mgmt.example.com/api',1,0,0,1,'api/v4/router/server/bird/standard',0,NULL,0,NULL,NULL,NULL,0,NULL,NULL,NULL,0),(8,NULL,2,'rs1-lan2-ipv6',6,1,'INEX LAN2 - Route Server - IPv6','RS1 - LAN2 - IPv6','192.0.2.19','2001:db8::19',65501,'1','2001:db8:0:0:2::19','http://rs1-lan2-ipv6.mgmt.example.com/api',1,0,0,0,'api/v4/router/server/bird/standard',0,NULL,0,NULL,NULL,NULL,0,NULL,NULL,NULL,0),(9,NULL,1,'as112-lan1-ipv4',4,3,'INEX LAN1 - AS112 Service - IPv4','AS112 - LAN1 - IPv4','192.0.2.6','192.0.2.6',112,'1','203.0.113.6','http://as112-lan1-ipv4.mgmt.example.com/api',1,0,0,0,'api/v4/router/as112/bird/standard',1,NULL,0,NULL,NULL,NULL,0,NULL,NULL,NULL,0),(10,NULL,1,'as112-lan1-ipv6',6,3,'INEX LAN1 - AS112 Service - IPv6','AS112 - LAN1 - IPv6','192.0.2.6','2001:db8:0:0:2::6',112,'1','203.0.113.6','http://as112-lan1-ipv6.mgmt.example.com/api',1,0,0,0,'api/v4/router/as112/bird/standard',1,NULL,0,NULL,NULL,NULL,0,NULL,NULL,NULL,0),(11,NULL,2,'as112-lan2-ipv4',4,3,'INEX LAN2 - AS112 Service - IPv4','AS112 - LAN2 - IPv4','192.0.2.16','192.0.2.16',112,'1','203.0.113.16','http://as112-lan2-ipv4.mgmt.example.com/api',1,0,0,0,'api/v4/router/as112/bird/standard',0,NULL,0,NULL,NULL,NULL,0,NULL,NULL,NULL,0),(12,NULL,2,'as112-lan2-ipv6',6,3,'INEX LAN2 - AS112 Service - IPv6','AS112 - LAN2 - IPv6','192.0.2.16','2001:db8:0:0:2::16',112,'1','203.0.113.16','http://as112-lan2-ipv6.mgmt.example.com/api',1,0,0,0,'api/v4/router/as112/bird/standard',0,NULL,0,NULL,NULL,NULL,0,NULL,NULL,NULL,0),(13,NULL,1,'unknown-template',6,2,'INEX LAN2 - Route Collector - IPv6','RC1 - LAN2 - IPv6','192.0.2.9','2001:db8::9',65500,'1','2001:db8:0:0:2::9','http://rc1-lan2-ipv6.mgmt.example.com/api',1,0,0,0,'api/v4/router/does-not-exist',0,NULL,0,NULL,NULL,NULL,0,NULL,NULL,NULL,0),(29,NULL,1,'b2-rs1-lan1-ipv4',4,1,'Bird2 - INEX LAN1 - Route Server - IPv4','B2 RS1 - LAN1 - IPv4','192.0.2.18','192.0.2.18',65501,'6','203.0.113.18',NULL,0,0,0,1,'api/v4/router/server/bird2/standard',0,NULL,1,NULL,NULL,NULL,0,NULL,NULL,NULL,0),(30,NULL,1,'b2-rs1-lan1-ipv6',6,1,'Bird2 - INEX LAN1 - Route Server - IPv6','B2 RS1 - LAN1 - IPv6','192.0.2.18','2001:db8::8',65501,'6','203.0.113.18',NULL,0,0,0,1,'api/v4/router/server/bird2/standard',0,NULL,1,NULL,NULL,NULL,1,NULL,NULL,NULL,0),(31,NULL,1,'b2-rc1-lan1-ipv4',4,2,'Bird2 - INEX LAN1 - Route Collector - IPv4','B2 RC1 - LAN1 - IPv4','192.0.2.8','192.0.2.8',65500,'1','203.0.113.8','http://rc1-lan1-ipv4.mgmt.example.com/api',1,0,0,1,'api/v4/router/collector/bird2/standard',0,NULL,1,NULL,NULL,NULL,0,NULL,NULL,NULL,0),(32,NULL,1,'b2-rc1-lan1-ipv6',6,2,'Bird2 - INEX LAN1 - Route Collector - IPv6','B2 RC1 - LAN1 - IPv6','192.0.2.8','2001:db8::8',65500,'1','2001:db8:0:0:2::8','http://rc1-lan1-ipv6.mgmt.example.com/api',1,0,0,1,'api/v4/router/collector/bird2/standard',0,NULL,1,NULL,NULL,NULL,0,NULL,NULL,NULL,0); +/*!40000 ALTER TABLE `routers` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `rs_prefixes` +-- + +DROP TABLE IF EXISTS `rs_prefixes`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `rs_prefixes` ( + `id` int NOT NULL AUTO_INCREMENT, + `custid` int DEFAULT NULL, + `timestamp` datetime DEFAULT NULL, + `prefix` varchar(64) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `protocol` int DEFAULT NULL, + `irrdb` int DEFAULT NULL, + `rs_origin` int DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `IDX_29FA9871DA0209B9` (`custid`), + CONSTRAINT `FK_29FA9871DA0209B9` FOREIGN KEY (`custid`) REFERENCES `cust` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `rs_prefixes` +-- + +LOCK TABLES `rs_prefixes` WRITE; +/*!40000 ALTER TABLE `rs_prefixes` DISABLE KEYS */; +/*!40000 ALTER TABLE `rs_prefixes` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `sessions` +-- + +DROP TABLE IF EXISTS `sessions`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `sessions` ( + `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `user_id` bigint DEFAULT NULL, + `ip_address` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `user_agent` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, + `payload` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `last_activity` int NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `sessions_id_unique` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `sessions` +-- + +LOCK TABLES `sessions` WRITE; +/*!40000 ALTER TABLE `sessions` DISABLE KEYS */; +INSERT INTO `sessions` VALUES ('j3MLjT008zdFSJhApwe7dwiY8BXvpdR7KFRGSzv7',1,'127.0.0.1','Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36','ZXlKcGRpSTZJakZMVGlzelpGVXdkamhCYVhJNVkyaG9WM1oxVUhjOVBTSXNJblpoYkhWbElqb2lWVmhpZDBwSUswSjJSREZ6Y3paaFIwaHdSRFJUTVdseFkyNHJNSHBtUW1WTlpFUmhkVzUyU3poNlZWRmNMMnh6ZWtKQ05EZzBSMEpSVVhaQ1NtUk1aWGxVTWtOU1FuQkRVbGMzVmtsSVdrazJibXhPYW1wb09XZzNRM1ZaZUd0elJGUXlkbTUwVjFsbVdVSjVUekEyVVd0cWVGWnRkMWw2Ym1aSlJEWm5UblpOYlRVd1ZrZFFkVGhVZHpOTFRYRkNORmxpUjNSSmQyYzBiV2RDUkZoeFpWVkphVkJJUlRGcmJ6VlhZVnd2ZVV4UWFYQmxkelJzV2xGeVUzaEJhM05TYTJZck1rcENVR041VWx3dlhDOWhiM3BUUzBKV2JDdHFVVkI1ZFdGY0wyOVZVVE5yT1Zsd2JWZ3JSbTh6YjFwc1JVaHBRaXR5UVdvNFJuazBZV2xvWVZkSlJsZHhjWFJ6TlVoelMwMVVUVWs1Y1dGeFJsaHNiMnhwUjNsbVRXWlNLMlo1VkVGY0wxTm1kRXRHTUdGQ2NrdzNkMGxHTm5oQmVrbEpPRUY2ZFVWdE1WQk5WbTFMTVVaUFpqWjFTbTFLU0RSNVRra3hXVWxhVFZsWlNubFVlVmxqU0dSVFEzbzVORTk2WVZBd1R6VnpQU0lzSW0xaFl5STZJbUl5TXpNeE1URTFZemd5TlRJMk1HVTFPRFkxT0RaaE1EVTNORFU0WTJJNE5URmxOakkxTnpnMk1tUTFPVE00TlRobVpHVmhOVE5oWmpabU9HRmhOaklpZlE9PQ==',1580126664); +/*!40000 ALTER TABLE `sessions` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `sflow_receiver` +-- + +DROP TABLE IF EXISTS `sflow_receiver`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `sflow_receiver` ( + `id` int NOT NULL AUTO_INCREMENT, + `virtual_interface_id` int DEFAULT NULL, + `dst_ip` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL, + `dst_port` int NOT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `IDX_E633EA142C0D6F5F` (`virtual_interface_id`), + CONSTRAINT `FK_E633EA142C0D6F5F` FOREIGN KEY (`virtual_interface_id`) REFERENCES `virtualinterface` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `sflow_receiver` +-- + +LOCK TABLES `sflow_receiver` WRITE; +/*!40000 ALTER TABLE `sflow_receiver` DISABLE KEYS */; +/*!40000 ALTER TABLE `sflow_receiver` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `switch` +-- + +DROP TABLE IF EXISTS `switch`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `switch` ( + `id` int NOT NULL AUTO_INCREMENT, + `infrastructure` int DEFAULT NULL, + `cabinetid` int DEFAULT NULL, + `vendorid` int DEFAULT NULL, + `name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `hostname` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `ipv4addr` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `ipv6addr` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `snmppasswd` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `model` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `active` tinyint(1) DEFAULT '1', + `os` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `osDate` datetime DEFAULT NULL, + `osVersion` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `lastPolled` datetime DEFAULT NULL, + `notes` longtext CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci, + `serialNumber` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `mauSupported` tinyint(1) DEFAULT NULL, + `asn` int unsigned DEFAULT NULL, + `loopback_ip` varchar(39) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `loopback_name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `mgmt_mac_address` varchar(12) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `snmp_engine_time` bigint DEFAULT NULL, + `snmp_system_uptime` bigint DEFAULT NULL, + `snmp_engine_boots` bigint DEFAULT NULL, + `poll` tinyint(1) NOT NULL DEFAULT '1', + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `UNIQ_6FE94B185E237E06` (`name`), + UNIQUE KEY `UNIQ_6FE94B1850C101F8` (`loopback_ip`), + KEY `IDX_6FE94B18D129B190` (`infrastructure`), + KEY `IDX_6FE94B182B96718A` (`cabinetid`), + KEY `IDX_6FE94B18420FB55F` (`vendorid`), + CONSTRAINT `FK_6FE94B182B96718A` FOREIGN KEY (`cabinetid`) REFERENCES `cabinet` (`id`), + CONSTRAINT `FK_6FE94B18420FB55F` FOREIGN KEY (`vendorid`) REFERENCES `vendor` (`id`), + CONSTRAINT `FK_6FE94B18D129B190` FOREIGN KEY (`infrastructure`) REFERENCES `infrastructure` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `switch` +-- + +LOCK TABLES `switch` WRITE; +/*!40000 ALTER TABLE `switch` DISABLE KEYS */; +INSERT INTO `switch` VALUES (1,1,1,12,'switch1','s1','10.0.0.1','','public','FESX624',1,NULL,NULL,NULL,NULL,'',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1,NULL,NULL),(2,2,1,12,'switch2','s2','10.0.0.2','','public','FESX624',1,NULL,NULL,NULL,NULL,'',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1,NULL,NULL); +/*!40000 ALTER TABLE `switch` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `switchport` +-- + +DROP TABLE IF EXISTS `switchport`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `switchport` ( + `id` int NOT NULL AUTO_INCREMENT, + `switchid` int DEFAULT NULL, + `type` int DEFAULT NULL, + `name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `active` tinyint(1) NOT NULL DEFAULT '1', + `ifIndex` int DEFAULT NULL, + `ifName` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `ifAlias` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `ifHighSpeed` int DEFAULT NULL, + `ifMtu` int DEFAULT NULL, + `ifPhysAddress` varchar(17) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `ifAdminStatus` int DEFAULT NULL, + `ifOperStatus` int DEFAULT NULL, + `ifLastChange` int DEFAULT NULL, + `lastSnmpPoll` datetime DEFAULT NULL, + `lagIfIndex` int DEFAULT NULL, + `mauType` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `mauState` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `mauAvailability` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `mauJacktype` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `mauAutoNegSupported` tinyint(1) DEFAULT NULL, + `mauAutoNegAdminState` tinyint(1) DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `IDX_F84274F1DC2C08F8` (`switchid`), + CONSTRAINT `FK_F84274F1DC2C08F8` FOREIGN KEY (`switchid`) REFERENCES `switch` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=57 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `switchport` +-- + +LOCK TABLES `switchport` WRITE; +/*!40000 ALTER TABLE `switchport` DISABLE KEYS */; +INSERT INTO `switchport` VALUES (1,1,1,'GigabitEthernet1',1,1,'GigabitEthernet1','GigabitEthernet1',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(2,1,1,'GigabitEthernet2',1,2,'GigabitEthernet2','GigabitEthernet2',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(3,1,1,'GigabitEthernet3',1,3,'GigabitEthernet3','GigabitEthernet3',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(4,1,1,'GigabitEthernet4',1,4,'GigabitEthernet4','GigabitEthernet4',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(5,1,1,'GigabitEthernet5',1,5,'GigabitEthernet5','GigabitEthernet5',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(6,1,1,'GigabitEthernet6',1,6,'GigabitEthernet6','GigabitEthernet6',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(7,1,1,'GigabitEthernet7',1,7,'GigabitEthernet7','GigabitEthernet7',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(8,1,1,'GigabitEthernet8',1,8,'GigabitEthernet8','GigabitEthernet8',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(9,1,1,'GigabitEthernet9',1,9,'GigabitEthernet9','GigabitEthernet9',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(10,1,1,'GigabitEthernet10',1,10,'GigabitEthernet10','GigabitEthernet10',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(11,1,1,'GigabitEthernet11',1,11,'GigabitEthernet11','GigabitEthernet11',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(12,1,1,'GigabitEthernet12',1,12,'GigabitEthernet12','GigabitEthernet12',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(13,1,1,'GigabitEthernet13',1,13,'GigabitEthernet13','GigabitEthernet13',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(14,1,1,'GigabitEthernet14',1,14,'GigabitEthernet14','GigabitEthernet14',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(15,1,1,'GigabitEthernet15',1,15,'GigabitEthernet15','GigabitEthernet15',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(16,1,1,'GigabitEthernet16',1,16,'GigabitEthernet16','GigabitEthernet16',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(17,1,1,'GigabitEthernet17',1,17,'GigabitEthernet17','GigabitEthernet17',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(18,1,3,'GigabitEthernet18',1,18,'GigabitEthernet18','GigabitEthernet18',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(19,1,3,'GigabitEthernet19',1,19,'GigabitEthernet19','GigabitEthernet19',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(20,1,1,'GigabitEthernet20',1,20,'GigabitEthernet20','GigabitEthernet20',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(21,1,1,'GigabitEthernet21',1,21,'GigabitEthernet21','GigabitEthernet21',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(22,1,1,'GigabitEthernet22',1,22,'GigabitEthernet22','GigabitEthernet22',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(23,1,1,'GigabitEthernet23',1,23,'GigabitEthernet23','GigabitEthernet23',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(24,1,1,'GigabitEthernet24',1,24,'GigabitEthernet24','GigabitEthernet24',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(25,2,1,'GigabitEthernet1',1,25,'GigabitEthernet1','GigabitEthernet1',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(26,2,1,'GigabitEthernet2',1,26,'GigabitEthernet2','GigabitEthernet2',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(27,2,1,'GigabitEthernet3',1,27,'GigabitEthernet3','GigabitEthernet3',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(28,2,1,'GigabitEthernet4',1,28,'GigabitEthernet4','GigabitEthernet4',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(29,2,1,'GigabitEthernet5',1,29,'GigabitEthernet5','GigabitEthernet5',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(30,2,1,'GigabitEthernet6',1,30,'GigabitEthernet6','GigabitEthernet6',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(31,2,1,'GigabitEthernet7',1,31,'GigabitEthernet7','GigabitEthernet7',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(32,2,1,'GigabitEthernet8',1,32,'GigabitEthernet8','GigabitEthernet8',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(33,2,1,'GigabitEthernet9',1,33,'GigabitEthernet9','GigabitEthernet9',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(34,2,1,'GigabitEthernet10',1,34,'GigabitEthernet10','GigabitEthernet10',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(35,2,1,'GigabitEthernet11',1,35,'GigabitEthernet11','GigabitEthernet11',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(36,2,1,'GigabitEthernet12',1,36,'GigabitEthernet12','GigabitEthernet12',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(37,2,1,'GigabitEthernet13',1,37,'GigabitEthernet13','GigabitEthernet13',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(38,2,1,'GigabitEthernet14',1,38,'GigabitEthernet14','GigabitEthernet14',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(39,2,1,'GigabitEthernet15',1,39,'GigabitEthernet15','GigabitEthernet15',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(40,2,1,'GigabitEthernet16',1,40,'GigabitEthernet16','GigabitEthernet16',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(41,2,1,'GigabitEthernet17',1,41,'GigabitEthernet17','GigabitEthernet17',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(42,2,3,'GigabitEthernet18',1,42,'GigabitEthernet18','GigabitEthernet18',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(43,2,3,'GigabitEthernet19',1,43,'GigabitEthernet19','GigabitEthernet19',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(44,2,1,'GigabitEthernet20',1,44,'GigabitEthernet20','GigabitEthernet20',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(45,2,1,'GigabitEthernet21',1,45,'GigabitEthernet21','GigabitEthernet21',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(46,2,1,'GigabitEthernet22',1,46,'GigabitEthernet22','GigabitEthernet22',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(47,2,1,'GigabitEthernet23',1,47,'GigabitEthernet23','GigabitEthernet23',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(48,2,1,'GigabitEthernet24',1,48,'GigabitEthernet24','GigabitEthernet24',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(49,1,3,'GigabitEthernet25',1,49,'GigabitEthernet25','GigabitEthernet25',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(50,1,3,'GigabitEthernet26',1,50,'GigabitEthernet26','GigabitEthernet26',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(51,1,3,'GigabitEthernet27',1,51,'GigabitEthernet27','GigabitEthernet27',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(52,1,3,'GigabitEthernet28',1,52,'GigabitEthernet28','GigabitEthernet28',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(53,2,3,'GigabitEthernet29',1,53,'GigabitEthernet29','GigabitEthernet29',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(54,2,3,'GigabitEthernet30',1,54,'GigabitEthernet30','GigabitEthernet30',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(55,2,3,'GigabitEthernet31',1,55,'GigabitEthernet31','GigabitEthernet31',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(56,2,3,'GigabitEthernet32',1,56,'GigabitEthernet32','GigabitEthernet32',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); +/*!40000 ALTER TABLE `switchport` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `telescope_entries` +-- + +DROP TABLE IF EXISTS `telescope_entries`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `telescope_entries` ( + `sequence` bigint unsigned NOT NULL AUTO_INCREMENT, + `uuid` char(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `batch_id` char(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `family_hash` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `should_display_on_index` tinyint(1) NOT NULL DEFAULT '1', + `type` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `content` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `created_at` datetime DEFAULT NULL, + PRIMARY KEY (`sequence`), + UNIQUE KEY `telescope_entries_uuid_unique` (`uuid`), + KEY `telescope_entries_batch_id_index` (`batch_id`), + KEY `telescope_entries_type_should_display_on_index_index` (`type`,`should_display_on_index`), + KEY `telescope_entries_family_hash_index` (`family_hash`) +) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `telescope_entries` +-- + +LOCK TABLES `telescope_entries` WRITE; +/*!40000 ALTER TABLE `telescope_entries` DISABLE KEYS */; +INSERT INTO `telescope_entries` VALUES (1,'8ff2744d-3f5e-4d3f-8136-6d1b43cb27ea','8ff2744d-b7d2-45ba-a780-89265a29a336','7fbfaf0b63e202da3dffb66c93082246',1,'exception','{\"class\":\"Illuminate\\\\Database\\\\QueryException\",\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Database\\/Connection.php\",\"line\":669,\"message\":\"SQLSTATE[42S02]: Base table or view not found: 1146 Table \'ixp_ci.docstore_directories\' doesn\'t exist (SQL: select * from `docstore_directories` where `parent_dir_id` is null order by `name` asc)\",\"trace\":[{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Database\\/Connection.php\",\"line\":629},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Database\\/Connection.php\",\"line\":338},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Database\\/Query\\/Builder.php\",\"line\":2132},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Database\\/Query\\/Builder.php\",\"line\":2120},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Database\\/Query\\/Builder.php\",\"line\":2592},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Database\\/Query\\/Builder.php\",\"line\":2121},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Database\\/Eloquent\\/Builder.php\",\"line\":537},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Database\\/Eloquent\\/Builder.php\",\"line\":521},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/app\\/Models\\/DocstoreDirectory.php\",\"line\":129},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/app\\/Http\\/Controllers\\/Docstore\\/DirectoryController.php\",\"line\":72},[],{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Routing\\/Controller.php\",\"line\":54},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Routing\\/ControllerDispatcher.php\",\"line\":45},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Routing\\/Route.php\",\"line\":219},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Routing\\/Route.php\",\"line\":176},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Routing\\/Router.php\",\"line\":681},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Pipeline\\/Pipeline.php\",\"line\":130},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/app\\/Http\\/Middleware\\/ControllerEnabled.php\",\"line\":96},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Pipeline\\/Pipeline.php\",\"line\":171},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Routing\\/Middleware\\/SubstituteBindings.php\",\"line\":41},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Pipeline\\/Pipeline.php\",\"line\":171},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Foundation\\/Http\\/Middleware\\/VerifyCsrfToken.php\",\"line\":76},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Pipeline\\/Pipeline.php\",\"line\":171},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/View\\/Middleware\\/ShareErrorsFromSession.php\",\"line\":49},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Pipeline\\/Pipeline.php\",\"line\":171},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Session\\/Middleware\\/StartSession.php\",\"line\":56},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Pipeline\\/Pipeline.php\",\"line\":171},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Cookie\\/Middleware\\/AddQueuedCookiesToResponse.php\",\"line\":37},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Pipeline\\/Pipeline.php\",\"line\":171},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Cookie\\/Middleware\\/EncryptCookies.php\",\"line\":66},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Pipeline\\/Pipeline.php\",\"line\":171},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Pipeline\\/Pipeline.php\",\"line\":105},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Routing\\/Router.php\",\"line\":683},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Routing\\/Router.php\",\"line\":658},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Routing\\/Router.php\",\"line\":624},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Routing\\/Router.php\",\"line\":613},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Foundation\\/Http\\/Kernel.php\",\"line\":170},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Pipeline\\/Pipeline.php\",\"line\":130},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/fideloper\\/proxy\\/src\\/TrustProxies.php\",\"line\":57},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Pipeline\\/Pipeline.php\",\"line\":171},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Foundation\\/Http\\/Middleware\\/TransformsRequest.php\",\"line\":21},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Pipeline\\/Pipeline.php\",\"line\":171},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Foundation\\/Http\\/Middleware\\/TransformsRequest.php\",\"line\":21},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Pipeline\\/Pipeline.php\",\"line\":171},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Foundation\\/Http\\/Middleware\\/ValidatePostSize.php\",\"line\":27},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Pipeline\\/Pipeline.php\",\"line\":171},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Foundation\\/Http\\/Middleware\\/CheckForMaintenanceMode.php\",\"line\":63},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Pipeline\\/Pipeline.php\",\"line\":171},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Pipeline\\/Pipeline.php\",\"line\":105},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Foundation\\/Http\\/Kernel.php\",\"line\":145},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Foundation\\/Http\\/Kernel.php\",\"line\":110},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/public\\/index.php\",\"line\":85},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/server.php\",\"line\":21}],\"line_preview\":{\"660\":\" \\/\\/ took to execute and log the query SQL, bindings and time in our memory.\",\"661\":\" try {\",\"662\":\" $result = $callback($query, $bindings);\",\"663\":\" }\",\"664\":\"\",\"665\":\" \\/\\/ If an exception occurs when attempting to run a query, we\'ll format the error\",\"666\":\" \\/\\/ message to include the bindings with SQL, which will make this exception a\",\"667\":\" \\/\\/ lot more helpful to the developer instead of just the database\'s errors.\",\"668\":\" catch (Exception $e) {\",\"669\":\" throw new QueryException(\",\"670\":\" $query, $this->prepareBindings($bindings), $e\",\"671\":\" );\",\"672\":\" }\",\"673\":\"\",\"674\":\" return $result;\",\"675\":\" }\",\"676\":\"\",\"677\":\" \\/**\",\"678\":\" * Log a query in the connection\'s query log.\",\"679\":\" *\"},\"hostname\":\"Yanns-MacBook-Pro.local\",\"user\":{\"id\":1,\"name\":null,\"email\":\"joe@siep.com\"},\"occurrences\":1}','2020-02-26 11:02:40'),(2,'9050da12-67a6-4d87-955a-ce770eee65f6','9050da12-6833-468f-be4b-fdffc215e5d8','4acf6fd3bd1ba79c05989b7b18db9175',1,'exception','{\"class\":\"Symfony\\\\Component\\\\Console\\\\Exception\\\\CommandNotFoundException\",\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-inex\\/vendor\\/symfony\\/console\\/Application.php\",\"line\":669,\"message\":\"Command \\\"doctrine:schema:migrate\\\" is not defined.\\n\\nDid you mean one of these?\\n doctrine:schema:create\\n doctrine:schema:drop\\n doctrine:schema:update\\n doctrine:schema:validate\\n utils:json-schema-post\",\"trace\":[{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-inex\\/vendor\\/symfony\\/console\\/Application.php\",\"line\":235},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-inex\\/vendor\\/symfony\\/console\\/Application.php\",\"line\":147},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-inex\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Console\\/Application.php\",\"line\":93},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-inex\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Foundation\\/Console\\/Kernel.php\",\"line\":131},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-inex\\/artisan\",\"line\":37}],\"line_preview\":{\"660\":\"\",\"661\":\" if (1 == \\\\count($alternatives)) {\",\"662\":\" $message .= \\\"\\\\n\\\\nDid you mean this?\\\\n \\\";\",\"663\":\" } else {\",\"664\":\" $message .= \\\"\\\\n\\\\nDid you mean one of these?\\\\n \\\";\",\"665\":\" }\",\"666\":\" $message .= implode(\\\"\\\\n \\\", $alternatives);\",\"667\":\" }\",\"668\":\"\",\"669\":\" throw new CommandNotFoundException($message, array_values($alternatives));\",\"670\":\" }\",\"671\":\"\",\"672\":\" \\/\\/ filter out aliases for commands which are already on the list\",\"673\":\" if (\\\\count($commands) > 1) {\",\"674\":\" $commandList = $this->commandLoader ? array_merge(array_flip($this->commandLoader->getNames()), $this->commands) : $this->commands;\",\"675\":\" $commands = array_unique(array_filter($commands, function ($nameOrAlias) use (&$commandList, $commands, &$aliases) {\",\"676\":\" if (!$commandList[$nameOrAlias] instanceof Command) {\",\"677\":\" $commandList[$nameOrAlias] = $this->commandLoader->get($nameOrAlias);\",\"678\":\" }\",\"679\":\"\"},\"hostname\":\"Barrys-MacBook-Pro.local\",\"occurrences\":1}','2020-04-13 10:15:04'),(3,'9101c3ca-7580-4574-b3d5-7f2008dd4637','9101c3cb-1198-4a7f-816d-9cc395e8df2b','04edfba26839d239e6f420a64cbad297',1,'exception','{\"class\":\"Doctrine\\\\DBAL\\\\Exception\\\\InvalidFieldNameException\",\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/doctrine\\/dbal\\/lib\\/Doctrine\\/DBAL\\/Driver\\/AbstractMySQLDriver.php\",\"line\":60,\"message\":\"An exception occurred while executing \'SELECT t0.name AS name_1, t0.colocation AS colocation_2, t0.height AS height_3, t0.u_counts_from AS u_counts_from_4, t0.type AS type_5, t0.notes AS notes_6, t0.id AS id_7, t0.locationid AS locationid_8 FROM cabinet t0 WHERE t0.id = ?\' with params [1]:\\n\\nSQLSTATE[42S22]: Column not found: 1054 Unknown column \'t0.colocation\' in \'field list\'\",\"trace\":[{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/doctrine\\/dbal\\/lib\\/Doctrine\\/DBAL\\/DBALException.php\",\"line\":169},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/doctrine\\/dbal\\/lib\\/Doctrine\\/DBAL\\/DBALException.php\",\"line\":149},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/doctrine\\/dbal\\/lib\\/Doctrine\\/DBAL\\/Connection.php\",\"line\":914},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/doctrine\\/orm\\/lib\\/Doctrine\\/ORM\\/Persisters\\/Entity\\/BasicEntityPersister.php\",\"line\":718},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/doctrine\\/orm\\/lib\\/Doctrine\\/ORM\\/Persisters\\/Entity\\/BasicEntityPersister.php\",\"line\":736},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/doctrine\\/orm\\/lib\\/Doctrine\\/ORM\\/Proxy\\/ProxyFactory.php\",\"line\":159},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/database\\/Proxies\\/__CG__EntitiesCabinet.php\",\"line\":453},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/database\\/Proxies\\/__CG__EntitiesCabinet.php\",\"line\":453},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/resources\\/views\\/customer\\/overview-tabs\\/ports\\/port.foil.php\",\"line\":134},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/foil\\/foil\\/src\\/Template\\/Template.php\",\"line\":287},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/foil\\/foil\\/src\\/Template\\/Template.php\",\"line\":231},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/foil\\/foil\\/src\\/Engine.php\",\"line\":307},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/foil\\/foil\\/src\\/Engine.php\",\"line\":211},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/foil\\/foil\\/src\\/Template\\/Template.php\",\"line\":188},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/resources\\/views\\/customer\\/overview-tabs\\/ports.foil.php\",\"line\":16},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/foil\\/foil\\/src\\/Template\\/Template.php\",\"line\":287},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/foil\\/foil\\/src\\/Template\\/Template.php\",\"line\":231},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/foil\\/foil\\/src\\/Engine.php\",\"line\":307},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/foil\\/foil\\/src\\/Engine.php\",\"line\":211},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/foil\\/foil\\/src\\/Template\\/Template.php\",\"line\":188},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/resources\\/views\\/customer\\/overview.foil.php\",\"line\":375},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/foil\\/foil\\/src\\/Template\\/Template.php\",\"line\":287},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/foil\\/foil\\/src\\/Template\\/Template.php\",\"line\":231},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/foil\\/foil\\/src\\/Engine.php\",\"line\":307},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/foil\\/foil\\/src\\/Engine.php\",\"line\":231},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/foil\\/foil\\/src\\/Engine.php\",\"line\":204},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/app\\/Services\\/FoilEngine.php\",\"line\":51},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/View\\/View.php\",\"line\":143},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/View\\/View.php\",\"line\":126},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/View\\/View.php\",\"line\":91},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Http\\/Response.php\",\"line\":42},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/symfony\\/http-foundation\\/Response.php\",\"line\":205},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Routing\\/Router.php\",\"line\":749},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Routing\\/Router.php\",\"line\":721},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Routing\\/Router.php\",\"line\":681},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Pipeline\\/Pipeline.php\",\"line\":130},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/app\\/Http\\/Middleware\\/AssertUserPrivilege.php\",\"line\":58},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Pipeline\\/Pipeline.php\",\"line\":171},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/app\\/Http\\/Middleware\\/Google2FA.php\",\"line\":74},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Pipeline\\/Pipeline.php\",\"line\":171},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/app\\/Http\\/Middleware\\/ControllerEnabled.php\",\"line\":96},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Pipeline\\/Pipeline.php\",\"line\":171},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Routing\\/Middleware\\/SubstituteBindings.php\",\"line\":41},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Pipeline\\/Pipeline.php\",\"line\":171},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/app\\/Http\\/Middleware\\/Authenticate.php\",\"line\":80},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Pipeline\\/Pipeline.php\",\"line\":171},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Foundation\\/Http\\/Middleware\\/VerifyCsrfToken.php\",\"line\":76},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Pipeline\\/Pipeline.php\",\"line\":171},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/View\\/Middleware\\/ShareErrorsFromSession.php\",\"line\":49},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Pipeline\\/Pipeline.php\",\"line\":171},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Session\\/Middleware\\/StartSession.php\",\"line\":56},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Pipeline\\/Pipeline.php\",\"line\":171},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Cookie\\/Middleware\\/AddQueuedCookiesToResponse.php\",\"line\":37},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Pipeline\\/Pipeline.php\",\"line\":171},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Cookie\\/Middleware\\/EncryptCookies.php\",\"line\":66},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Pipeline\\/Pipeline.php\",\"line\":171},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Pipeline\\/Pipeline.php\",\"line\":105},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Routing\\/Router.php\",\"line\":683},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Routing\\/Router.php\",\"line\":658},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Routing\\/Router.php\",\"line\":624},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Routing\\/Router.php\",\"line\":613},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Foundation\\/Http\\/Kernel.php\",\"line\":170},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Pipeline\\/Pipeline.php\",\"line\":130},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/fideloper\\/proxy\\/src\\/TrustProxies.php\",\"line\":57},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Pipeline\\/Pipeline.php\",\"line\":171},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Foundation\\/Http\\/Middleware\\/TransformsRequest.php\",\"line\":21},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Pipeline\\/Pipeline.php\",\"line\":171},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Foundation\\/Http\\/Middleware\\/TransformsRequest.php\",\"line\":21},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Pipeline\\/Pipeline.php\",\"line\":171},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Foundation\\/Http\\/Middleware\\/ValidatePostSize.php\",\"line\":27},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Pipeline\\/Pipeline.php\",\"line\":171},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Foundation\\/Http\\/Middleware\\/CheckForMaintenanceMode.php\",\"line\":63},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Pipeline\\/Pipeline.php\",\"line\":171},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Pipeline\\/Pipeline.php\",\"line\":105},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Foundation\\/Http\\/Kernel.php\",\"line\":145},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Foundation\\/Http\\/Kernel.php\",\"line\":110},{\"file\":\"\\/Users\\/yannrobin\\/Documents\\/development\\/ixp\\/IXP-Manager\\/public\\/index.php\",\"line\":85},{\"file\":\"\\/Users\\/yannrobin\\/.composer\\/vendor\\/laravel\\/valet\\/server.php\",\"line\":158}],\"line_preview\":{\"51\":\" case \'1062\':\",\"52\":\" case \'1557\':\",\"53\":\" case \'1569\':\",\"54\":\" case \'1586\':\",\"55\":\" return new Exception\\\\UniqueConstraintViolationException($message, $exception);\",\"56\":\"\",\"57\":\" case \'1054\':\",\"58\":\" case \'1166\':\",\"59\":\" case \'1611\':\",\"60\":\" return new Exception\\\\InvalidFieldNameException($message, $exception);\",\"61\":\"\",\"62\":\" case \'1052\':\",\"63\":\" case \'1060\':\",\"64\":\" case \'1110\':\",\"65\":\" return new Exception\\\\NonUniqueFieldNameException($message, $exception);\",\"66\":\"\",\"67\":\" case \'1064\':\",\"68\":\" case \'1149\':\",\"69\":\" case \'1287\':\",\"70\":\" case \'1341\':\"},\"hostname\":\"Yanns-MacBook-Pro.local\",\"user\":{\"id\":1,\"name\":null,\"email\":\"joe@siep.com\"},\"occurrences\":1}','2020-07-10 07:54:18'),(4,'9170f381-84a3-4846-8fae-95d3b6c2df10','9170f381-919e-4d66-8a2d-17f571676d7b','fe14f6e8415a436cf310f61ef353127e',0,'exception','{\"class\":\"Illuminate\\\\Database\\\\QueryException\",\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Database\\/Connection.php\",\"line\":669,\"message\":\"SQLSTATE[42000]: Syntax error or access violation: 1227 Access denied; you need (at least one of) the SYSTEM_USER privilege(s) for this operation (SQL: -- Views and triggers used on the IXP Manager database\\n\\n-- view: view_cust_current_active\\n--\\n-- This is used to pick up all currently active members. This can further \\n-- be refined by checking for customer type.\\n\\nDROP VIEW IF EXISTS view_cust_current_active;\\nCREATE VIEW view_cust_current_active AS\\n\\tSELECT * FROM cust cu\\n\\tWHERE\\n\\t\\tcu.datejoin <= CURDATE()\\n\\tAND\\t(\\n\\t\\t\\t( cu.dateleave IS NULL )\\n\\t\\tOR\\t( cu.dateleave < \'1970-01-01\' )\\n\\t\\tOR\\t( cu.dateleave >= CURDATE() )\\n\\t\\t)\\n\\tAND\\t(cu.status = 1 OR cu.status = 2);\\n\\n-- view: view_vlaninterface_details_by_custid\\n--\\n-- This is used to pick up all interesting details from virtualinterfaces.\\n\\nDROP VIEW IF EXISTS view_vlaninterface_details_by_custid;\\nCREATE VIEW view_vlaninterface_details_by_custid AS\\n\\tSELECT\\n \\t`pi`.`id` AS `id`,\\n\\t\\tvi.custid,\\n\\t\\tpi.virtualinterfaceid,\\n\\t\\tpi.status,\\n\\t\\tCONCAT(vi.name,vi.channelgroup) AS virtualinterfacename,\\n\\t\\tvlan.number AS vlan,\\n\\t\\tvlan.name AS vlanname,\\n\\t\\tvlan.id AS vlanid,\\n\\t\\tvli.id AS vlaninterfaceid,\\n\\t\\tvli.ipv4enabled,\\n\\t\\tvli.ipv4hostname,\\n\\t\\tvli.ipv4canping,\\n\\t\\tvli.ipv4monitorrcbgp,\\n\\t\\tvli.ipv6enabled,\\n\\t\\tvli.ipv6hostname,\\n\\t\\tvli.ipv6canping,\\n\\t\\tvli.ipv6monitorrcbgp,\\n\\t\\tvli.as112client,\\n\\t\\tvli.mcastenabled,\\n\\t\\tvli.ipv4bgpmd5secret,\\n\\t\\tvli.ipv6bgpmd5secret,\\n\\t\\tvli.rsclient,\\n\\t\\tvli.irrdbfilter,\\n\\t\\tvli.busyhost,\\n\\t\\tvli.notes,\\n\\t\\tv4.address AS ipv4address,\\n\\t\\tv6.address AS ipv6address\\n\\tFROM\\n\\t\\tphysicalinterface pi,\\n\\t\\tvirtualinterface vi,\\n\\t\\tvlaninterface vli\\n\\tLEFT JOIN (ipv4address v4) ON vli.ipv4addressid = v4.id\\n\\tLEFT JOIN (ipv6address v6) ON vli.ipv6addressid = v6.id\\n\\tLEFT JOIN vlan ON vli.vlanid = vlan.id\\n\\tWHERE\\n\\t\\tpi.virtualinterfaceid = vi.id\\n\\tAND\\tvli.virtualinterfaceid = vi.id;\\n\\n-- view: view_switch_details_by_custid\\n--\\n-- This is used to pick up all interesting details from switches.\\n\\nDROP VIEW IF EXISTS view_switch_details_by_custid;\\nCREATE VIEW view_switch_details_by_custid AS\\n\\tSELECT\\n\\t\\tvi.id AS id,\\n\\t\\tvi.custid,\\n\\t\\tCONCAT(vi.name,vi.channelgroup) AS virtualinterfacename,\\n\\t\\tpi.virtualinterfaceid,\\n\\t\\tpi.status,\\n\\t\\tpi.speed,\\n\\t\\tpi.duplex,\\n\\t\\tpi.notes,\\n\\t\\tsp.name AS switchport,\\n\\t\\tsp.id AS switchportid,\\n\\t\\tsp.ifName AS spifname,\\n\\t\\tsw.name AS switch,\\n\\t\\tsw.hostname AS switchhostname,\\n\\t\\tsw.id AS switchid,\\n\\t\\tsw.vendorid,\\n\\t\\tsw.snmppasswd,\\n\\t\\tsw.infrastructure,\\n\\t\\tca.name AS cabinet,\\n\\t\\tca.cololocation AS colocabinet,\\n\\t\\tlo.name AS locationname,\\n\\t\\tlo.shortname AS locationshortname\\n\\tFROM\\n\\t\\tvirtualinterface vi,\\n\\t\\tphysicalinterface pi,\\n\\t\\tswitchport sp,\\n\\t\\tswitch sw,\\n\\t\\tcabinet ca,\\n\\t\\tlocation lo\\n\\tWHERE\\n\\t\\tpi.virtualinterfaceid = vi.id\\n\\tAND\\tpi.switchportid = sp.id\\n\\tAND\\tsp.switchid = sw.id\\n\\tAND\\tsw.cabinetid = ca.id\\n\\tAND\\tca.locationid = lo.id;\\n\\n\\n\\n-- trigger: bgp_sessions_update\\n--\\n-- This is used to update a n^2 table showing who peers with whom\\n\\n\\nDROP TRIGGER IF EXISTS `bgp_sessions_update`;\\n\\nDELIMITER ;;\\n\\nCREATE TRIGGER bgp_sessions_update AFTER INSERT ON `bgpsessiondata` FOR EACH ROW\\n\\n\\tBEGIN\\n\\n\\t\\tIF NOT EXISTS ( SELECT 1 FROM bgp_sessions WHERE srcipaddressid = NEW.srcipaddressid AND protocol = NEW.protocol AND dstipaddressid = NEW.dstipaddressid ) THEN\\n\\t\\t\\tINSERT INTO bgp_sessions\\n\\t\\t\\t\\t( srcipaddressid, protocol, dstipaddressid, packetcount, last_seen, source )\\n\\t\\t\\tVALUES\\n\\t\\t\\t\\t( NEW.srcipaddressid, NEW.protocol, NEW.dstipaddressid, NEW.packetcount, NOW(), NEW.source );\\n\\t\\tELSE\\n\\t\\t\\tUPDATE bgp_sessions SET\\n\\t\\t\\t\\tlast_seen = NOW(),\\n\\t\\t\\t\\tpacketcount = packetcount + NEW.packetcount\\n\\t\\t\\tWHERE\\n\\t\\t\\t\\tsrcipaddressid = NEW.srcipaddressid AND protocol = NEW.protocol AND dstipaddressid = NEW.dstipaddressid;\\n\\t\\tEND IF;\\n\\n\\t\\tIF NOT EXISTS ( SELECT 1 FROM bgp_sessions WHERE dstipaddressid = NEW.srcipaddressid AND protocol = NEW.protocol AND srcipaddressid = NEW.dstipaddressid ) THEN\\n\\t\\t\\tINSERT INTO bgp_sessions\\n\\t\\t\\t\\t( srcipaddressid, protocol, dstipaddressid, packetcount, last_seen, source )\\n\\t\\t\\tVALUES\\n\\t\\t\\t\\t( NEW.dstipaddressid, NEW.protocol, NEW.srcipaddressid, NEW.packetcount, NOW(), NEW.source );\\n\\t\\tELSE\\n\\t\\t\\tUPDATE bgp_sessions SET\\n\\t\\t\\t\\tlast_seen = NOW(),\\n\\t\\t\\t\\tpacketcount = packetcount + NEW.packetcount\\n\\t\\t\\tWHERE\\n\\t\\t\\t\\tdstipaddressid = NEW.srcipaddressid AND protocol = NEW.protocol AND srcipaddressid = NEW.dstipaddressid;\\n\\t\\tEND IF;\\n\\n\\tEND ;;\\n\\nDELIMITER ;\\n)\",\"trace\":[{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Database\\/Connection.php\",\"line\":629},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Database\\/Connection.php\",\"line\":516},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Database\\/DatabaseManager.php\",\"line\":349},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Support\\/Facades\\/Facade.php\",\"line\":261},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/app\\/Console\\/Commands\\/Upgrade\\/ResetMysqlViews.php\",\"line\":67},[],{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Container\\/BoundMethod.php\",\"line\":32},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Container\\/Util.php\",\"line\":36},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Container\\/BoundMethod.php\",\"line\":90},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Container\\/BoundMethod.php\",\"line\":34},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Container\\/Container.php\",\"line\":590},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Console\\/Command.php\",\"line\":134},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/symfony\\/console\\/Command\\/Command.php\",\"line\":255},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Console\\/Command.php\",\"line\":121},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/symfony\\/console\\/Application.php\",\"line\":1001},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/symfony\\/console\\/Application.php\",\"line\":271},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/symfony\\/console\\/Application.php\",\"line\":147},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Console\\/Application.php\",\"line\":93},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Foundation\\/Console\\/Kernel.php\",\"line\":131},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/artisan\",\"line\":37}],\"line_preview\":{\"660\":\" \\/\\/ took to execute and log the query SQL, bindings and time in our memory.\",\"661\":\" try {\",\"662\":\" $result = $callback($query, $bindings);\",\"663\":\" }\",\"664\":\"\",\"665\":\" \\/\\/ If an exception occurs when attempting to run a query, we\'ll format the error\",\"666\":\" \\/\\/ message to include the bindings with SQL, which will make this exception a\",\"667\":\" \\/\\/ lot more helpful to the developer instead of just the database\'s errors.\",\"668\":\" catch (Exception $e) {\",\"669\":\" throw new QueryException(\",\"670\":\" $query, $this->prepareBindings($bindings), $e\",\"671\":\" );\",\"672\":\" }\",\"673\":\"\",\"674\":\" return $result;\",\"675\":\" }\",\"676\":\"\",\"677\":\" \\/**\",\"678\":\" * Log a query in the connection\'s query log.\",\"679\":\" *\"},\"hostname\":\"Barrys-MacBook-Pro.local\",\"occurrences\":1}','2020-09-03 15:24:37'),(5,'9170f401-79d3-4043-bb31-c7d41f88875f','9170f401-85e8-440a-9603-a86b7e6509dd','fe14f6e8415a436cf310f61ef353127e',0,'exception','{\"class\":\"Illuminate\\\\Database\\\\QueryException\",\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Database\\/Connection.php\",\"line\":669,\"message\":\"SQLSTATE[42000]: Syntax error or access violation: 1227 Access denied; you need (at least one of) the SYSTEM_USER privilege(s) for this operation (SQL: -- Views and triggers used on the IXP Manager database\\n\\n-- view: view_cust_current_active\\n--\\n-- This is used to pick up all currently active members. This can further \\n-- be refined by checking for customer type.\\n\\nDROP VIEW IF EXISTS view_cust_current_active;\\nCREATE VIEW view_cust_current_active AS\\n\\tSELECT * FROM cust cu\\n\\tWHERE\\n\\t\\tcu.datejoin <= CURDATE()\\n\\tAND\\t(\\n\\t\\t\\t( cu.dateleave IS NULL )\\n\\t\\tOR\\t( cu.dateleave < \'1970-01-01\' )\\n\\t\\tOR\\t( cu.dateleave >= CURDATE() )\\n\\t\\t)\\n\\tAND\\t(cu.status = 1 OR cu.status = 2);\\n\\n-- view: view_vlaninterface_details_by_custid\\n--\\n-- This is used to pick up all interesting details from virtualinterfaces.\\n\\nDROP VIEW IF EXISTS view_vlaninterface_details_by_custid;\\nCREATE VIEW view_vlaninterface_details_by_custid AS\\n\\tSELECT\\n \\t`pi`.`id` AS `id`,\\n\\t\\tvi.custid,\\n\\t\\tpi.virtualinterfaceid,\\n\\t\\tpi.status,\\n\\t\\tCONCAT(vi.name,vi.channelgroup) AS virtualinterfacename,\\n\\t\\tvlan.number AS vlan,\\n\\t\\tvlan.name AS vlanname,\\n\\t\\tvlan.id AS vlanid,\\n\\t\\tvli.id AS vlaninterfaceid,\\n\\t\\tvli.ipv4enabled,\\n\\t\\tvli.ipv4hostname,\\n\\t\\tvli.ipv4canping,\\n\\t\\tvli.ipv4monitorrcbgp,\\n\\t\\tvli.ipv6enabled,\\n\\t\\tvli.ipv6hostname,\\n\\t\\tvli.ipv6canping,\\n\\t\\tvli.ipv6monitorrcbgp,\\n\\t\\tvli.as112client,\\n\\t\\tvli.mcastenabled,\\n\\t\\tvli.ipv4bgpmd5secret,\\n\\t\\tvli.ipv6bgpmd5secret,\\n\\t\\tvli.rsclient,\\n\\t\\tvli.irrdbfilter,\\n\\t\\tvli.busyhost,\\n\\t\\tvli.notes,\\n\\t\\tv4.address AS ipv4address,\\n\\t\\tv6.address AS ipv6address\\n\\tFROM\\n\\t\\tphysicalinterface pi,\\n\\t\\tvirtualinterface vi,\\n\\t\\tvlaninterface vli\\n\\tLEFT JOIN (ipv4address v4) ON vli.ipv4addressid = v4.id\\n\\tLEFT JOIN (ipv6address v6) ON vli.ipv6addressid = v6.id\\n\\tLEFT JOIN vlan ON vli.vlanid = vlan.id\\n\\tWHERE\\n\\t\\tpi.virtualinterfaceid = vi.id\\n\\tAND\\tvli.virtualinterfaceid = vi.id;\\n\\n-- view: view_switch_details_by_custid\\n--\\n-- This is used to pick up all interesting details from switches.\\n\\nDROP VIEW IF EXISTS view_switch_details_by_custid;\\nCREATE VIEW view_switch_details_by_custid AS\\n\\tSELECT\\n\\t\\tvi.id AS id,\\n\\t\\tvi.custid,\\n\\t\\tCONCAT(vi.name,vi.channelgroup) AS virtualinterfacename,\\n\\t\\tpi.virtualinterfaceid,\\n\\t\\tpi.status,\\n\\t\\tpi.speed,\\n\\t\\tpi.duplex,\\n\\t\\tpi.notes,\\n\\t\\tsp.name AS switchport,\\n\\t\\tsp.id AS switchportid,\\n\\t\\tsp.ifName AS spifname,\\n\\t\\tsw.name AS switch,\\n\\t\\tsw.hostname AS switchhostname,\\n\\t\\tsw.id AS switchid,\\n\\t\\tsw.vendorid,\\n\\t\\tsw.snmppasswd,\\n\\t\\tsw.infrastructure,\\n\\t\\tca.name AS cabinet,\\n\\t\\tca.cololocation AS colocabinet,\\n\\t\\tlo.name AS locationname,\\n\\t\\tlo.shortname AS locationshortname\\n\\tFROM\\n\\t\\tvirtualinterface vi,\\n\\t\\tphysicalinterface pi,\\n\\t\\tswitchport sp,\\n\\t\\tswitch sw,\\n\\t\\tcabinet ca,\\n\\t\\tlocation lo\\n\\tWHERE\\n\\t\\tpi.virtualinterfaceid = vi.id\\n\\tAND\\tpi.switchportid = sp.id\\n\\tAND\\tsp.switchid = sw.id\\n\\tAND\\tsw.cabinetid = ca.id\\n\\tAND\\tca.locationid = lo.id;\\n\\n\\n\\n-- trigger: bgp_sessions_update\\n--\\n-- This is used to update a n^2 table showing who peers with whom\\n\\n\\nDROP TRIGGER IF EXISTS `bgp_sessions_update`;\\n\\nDELIMITER ;;\\n\\nCREATE TRIGGER bgp_sessions_update AFTER INSERT ON `bgpsessiondata` FOR EACH ROW\\n\\n\\tBEGIN\\n\\n\\t\\tIF NOT EXISTS ( SELECT 1 FROM bgp_sessions WHERE srcipaddressid = NEW.srcipaddressid AND protocol = NEW.protocol AND dstipaddressid = NEW.dstipaddressid ) THEN\\n\\t\\t\\tINSERT INTO bgp_sessions\\n\\t\\t\\t\\t( srcipaddressid, protocol, dstipaddressid, packetcount, last_seen, source )\\n\\t\\t\\tVALUES\\n\\t\\t\\t\\t( NEW.srcipaddressid, NEW.protocol, NEW.dstipaddressid, NEW.packetcount, NOW(), NEW.source );\\n\\t\\tELSE\\n\\t\\t\\tUPDATE bgp_sessions SET\\n\\t\\t\\t\\tlast_seen = NOW(),\\n\\t\\t\\t\\tpacketcount = packetcount + NEW.packetcount\\n\\t\\t\\tWHERE\\n\\t\\t\\t\\tsrcipaddressid = NEW.srcipaddressid AND protocol = NEW.protocol AND dstipaddressid = NEW.dstipaddressid;\\n\\t\\tEND IF;\\n\\n\\t\\tIF NOT EXISTS ( SELECT 1 FROM bgp_sessions WHERE dstipaddressid = NEW.srcipaddressid AND protocol = NEW.protocol AND srcipaddressid = NEW.dstipaddressid ) THEN\\n\\t\\t\\tINSERT INTO bgp_sessions\\n\\t\\t\\t\\t( srcipaddressid, protocol, dstipaddressid, packetcount, last_seen, source )\\n\\t\\t\\tVALUES\\n\\t\\t\\t\\t( NEW.dstipaddressid, NEW.protocol, NEW.srcipaddressid, NEW.packetcount, NOW(), NEW.source );\\n\\t\\tELSE\\n\\t\\t\\tUPDATE bgp_sessions SET\\n\\t\\t\\t\\tlast_seen = NOW(),\\n\\t\\t\\t\\tpacketcount = packetcount + NEW.packetcount\\n\\t\\t\\tWHERE\\n\\t\\t\\t\\tdstipaddressid = NEW.srcipaddressid AND protocol = NEW.protocol AND srcipaddressid = NEW.dstipaddressid;\\n\\t\\tEND IF;\\n\\n\\tEND ;;\\n\\nDELIMITER ;\\n)\",\"trace\":[{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Database\\/Connection.php\",\"line\":629},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Database\\/Connection.php\",\"line\":516},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Database\\/DatabaseManager.php\",\"line\":349},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Support\\/Facades\\/Facade.php\",\"line\":261},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/app\\/Console\\/Commands\\/Upgrade\\/ResetMysqlViews.php\",\"line\":67},[],{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Container\\/BoundMethod.php\",\"line\":32},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Container\\/Util.php\",\"line\":36},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Container\\/BoundMethod.php\",\"line\":90},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Container\\/BoundMethod.php\",\"line\":34},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Container\\/Container.php\",\"line\":590},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Console\\/Command.php\",\"line\":134},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/symfony\\/console\\/Command\\/Command.php\",\"line\":255},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Console\\/Command.php\",\"line\":121},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/symfony\\/console\\/Application.php\",\"line\":1001},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/symfony\\/console\\/Application.php\",\"line\":271},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/symfony\\/console\\/Application.php\",\"line\":147},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Console\\/Application.php\",\"line\":93},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Foundation\\/Console\\/Kernel.php\",\"line\":131},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/artisan\",\"line\":37}],\"line_preview\":{\"660\":\" \\/\\/ took to execute and log the query SQL, bindings and time in our memory.\",\"661\":\" try {\",\"662\":\" $result = $callback($query, $bindings);\",\"663\":\" }\",\"664\":\"\",\"665\":\" \\/\\/ If an exception occurs when attempting to run a query, we\'ll format the error\",\"666\":\" \\/\\/ message to include the bindings with SQL, which will make this exception a\",\"667\":\" \\/\\/ lot more helpful to the developer instead of just the database\'s errors.\",\"668\":\" catch (Exception $e) {\",\"669\":\" throw new QueryException(\",\"670\":\" $query, $this->prepareBindings($bindings), $e\",\"671\":\" );\",\"672\":\" }\",\"673\":\"\",\"674\":\" return $result;\",\"675\":\" }\",\"676\":\"\",\"677\":\" \\/**\",\"678\":\" * Log a query in the connection\'s query log.\",\"679\":\" *\"},\"hostname\":\"Barrys-MacBook-Pro.local\",\"occurrences\":2}','2020-09-03 15:26:01'),(6,'9170f424-a432-4324-8b5e-375dc233029b','9170f424-ae1f-4af9-bb87-27aa90175327','fe14f6e8415a436cf310f61ef353127e',0,'exception','{\"class\":\"Illuminate\\\\Database\\\\QueryException\",\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Database\\/Connection.php\",\"line\":669,\"message\":\"SQLSTATE[42000]: Syntax error or access violation: 1227 Access denied; you need (at least one of) the SYSTEM_USER privilege(s) for this operation (SQL: -- Views and triggers used on the IXP Manager database\\n\\n-- view: view_cust_current_active\\n--\\n-- This is used to pick up all currently active members. This can further \\n-- be refined by checking for customer type.\\n\\nDROP VIEW IF EXISTS view_cust_current_active;\\nCREATE VIEW view_cust_current_active AS\\n\\tSELECT * FROM cust cu\\n\\tWHERE\\n\\t\\tcu.datejoin <= CURDATE()\\n\\tAND\\t(\\n\\t\\t\\t( cu.dateleave IS NULL )\\n\\t\\tOR\\t( cu.dateleave < \'1970-01-01\' )\\n\\t\\tOR\\t( cu.dateleave >= CURDATE() )\\n\\t\\t)\\n\\tAND\\t(cu.status = 1 OR cu.status = 2);\\n\\n-- view: view_vlaninterface_details_by_custid\\n--\\n-- This is used to pick up all interesting details from virtualinterfaces.\\n\\nDROP VIEW IF EXISTS view_vlaninterface_details_by_custid;\\nCREATE VIEW view_vlaninterface_details_by_custid AS\\n\\tSELECT\\n \\t`pi`.`id` AS `id`,\\n\\t\\tvi.custid,\\n\\t\\tpi.virtualinterfaceid,\\n\\t\\tpi.status,\\n\\t\\tCONCAT(vi.name,vi.channelgroup) AS virtualinterfacename,\\n\\t\\tvlan.number AS vlan,\\n\\t\\tvlan.name AS vlanname,\\n\\t\\tvlan.id AS vlanid,\\n\\t\\tvli.id AS vlaninterfaceid,\\n\\t\\tvli.ipv4enabled,\\n\\t\\tvli.ipv4hostname,\\n\\t\\tvli.ipv4canping,\\n\\t\\tvli.ipv4monitorrcbgp,\\n\\t\\tvli.ipv6enabled,\\n\\t\\tvli.ipv6hostname,\\n\\t\\tvli.ipv6canping,\\n\\t\\tvli.ipv6monitorrcbgp,\\n\\t\\tvli.as112client,\\n\\t\\tvli.mcastenabled,\\n\\t\\tvli.ipv4bgpmd5secret,\\n\\t\\tvli.ipv6bgpmd5secret,\\n\\t\\tvli.rsclient,\\n\\t\\tvli.irrdbfilter,\\n\\t\\tvli.busyhost,\\n\\t\\tvli.notes,\\n\\t\\tv4.address AS ipv4address,\\n\\t\\tv6.address AS ipv6address\\n\\tFROM\\n\\t\\tphysicalinterface pi,\\n\\t\\tvirtualinterface vi,\\n\\t\\tvlaninterface vli\\n\\tLEFT JOIN (ipv4address v4) ON vli.ipv4addressid = v4.id\\n\\tLEFT JOIN (ipv6address v6) ON vli.ipv6addressid = v6.id\\n\\tLEFT JOIN vlan ON vli.vlanid = vlan.id\\n\\tWHERE\\n\\t\\tpi.virtualinterfaceid = vi.id\\n\\tAND\\tvli.virtualinterfaceid = vi.id;\\n\\n-- view: view_switch_details_by_custid\\n--\\n-- This is used to pick up all interesting details from switches.\\n\\nDROP VIEW IF EXISTS view_switch_details_by_custid;\\nCREATE VIEW view_switch_details_by_custid AS\\n\\tSELECT\\n\\t\\tvi.id AS id,\\n\\t\\tvi.custid,\\n\\t\\tCONCAT(vi.name,vi.channelgroup) AS virtualinterfacename,\\n\\t\\tpi.virtualinterfaceid,\\n\\t\\tpi.status,\\n\\t\\tpi.speed,\\n\\t\\tpi.duplex,\\n\\t\\tpi.notes,\\n\\t\\tsp.name AS switchport,\\n\\t\\tsp.id AS switchportid,\\n\\t\\tsp.ifName AS spifname,\\n\\t\\tsw.name AS switch,\\n\\t\\tsw.hostname AS switchhostname,\\n\\t\\tsw.id AS switchid,\\n\\t\\tsw.vendorid,\\n\\t\\tsw.snmppasswd,\\n\\t\\tsw.infrastructure,\\n\\t\\tca.name AS cabinet,\\n\\t\\tca.cololocation AS colocabinet,\\n\\t\\tlo.name AS locationname,\\n\\t\\tlo.shortname AS locationshortname\\n\\tFROM\\n\\t\\tvirtualinterface vi,\\n\\t\\tphysicalinterface pi,\\n\\t\\tswitchport sp,\\n\\t\\tswitch sw,\\n\\t\\tcabinet ca,\\n\\t\\tlocation lo\\n\\tWHERE\\n\\t\\tpi.virtualinterfaceid = vi.id\\n\\tAND\\tpi.switchportid = sp.id\\n\\tAND\\tsp.switchid = sw.id\\n\\tAND\\tsw.cabinetid = ca.id\\n\\tAND\\tca.locationid = lo.id;\\n\\n\\n\\n-- trigger: bgp_sessions_update\\n--\\n-- This is used to update a n^2 table showing who peers with whom\\n\\n\\nDROP TRIGGER IF EXISTS `bgp_sessions_update`;\\n\\nDELIMITER ;;\\n\\nCREATE TRIGGER bgp_sessions_update AFTER INSERT ON `bgpsessiondata` FOR EACH ROW\\n\\n\\tBEGIN\\n\\n\\t\\tIF NOT EXISTS ( SELECT 1 FROM bgp_sessions WHERE srcipaddressid = NEW.srcipaddressid AND protocol = NEW.protocol AND dstipaddressid = NEW.dstipaddressid ) THEN\\n\\t\\t\\tINSERT INTO bgp_sessions\\n\\t\\t\\t\\t( srcipaddressid, protocol, dstipaddressid, packetcount, last_seen, source )\\n\\t\\t\\tVALUES\\n\\t\\t\\t\\t( NEW.srcipaddressid, NEW.protocol, NEW.dstipaddressid, NEW.packetcount, NOW(), NEW.source );\\n\\t\\tELSE\\n\\t\\t\\tUPDATE bgp_sessions SET\\n\\t\\t\\t\\tlast_seen = NOW(),\\n\\t\\t\\t\\tpacketcount = packetcount + NEW.packetcount\\n\\t\\t\\tWHERE\\n\\t\\t\\t\\tsrcipaddressid = NEW.srcipaddressid AND protocol = NEW.protocol AND dstipaddressid = NEW.dstipaddressid;\\n\\t\\tEND IF;\\n\\n\\t\\tIF NOT EXISTS ( SELECT 1 FROM bgp_sessions WHERE dstipaddressid = NEW.srcipaddressid AND protocol = NEW.protocol AND srcipaddressid = NEW.dstipaddressid ) THEN\\n\\t\\t\\tINSERT INTO bgp_sessions\\n\\t\\t\\t\\t( srcipaddressid, protocol, dstipaddressid, packetcount, last_seen, source )\\n\\t\\t\\tVALUES\\n\\t\\t\\t\\t( NEW.dstipaddressid, NEW.protocol, NEW.srcipaddressid, NEW.packetcount, NOW(), NEW.source );\\n\\t\\tELSE\\n\\t\\t\\tUPDATE bgp_sessions SET\\n\\t\\t\\t\\tlast_seen = NOW(),\\n\\t\\t\\t\\tpacketcount = packetcount + NEW.packetcount\\n\\t\\t\\tWHERE\\n\\t\\t\\t\\tdstipaddressid = NEW.srcipaddressid AND protocol = NEW.protocol AND srcipaddressid = NEW.dstipaddressid;\\n\\t\\tEND IF;\\n\\n\\tEND ;;\\n\\nDELIMITER ;\\n)\",\"trace\":[{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Database\\/Connection.php\",\"line\":629},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Database\\/Connection.php\",\"line\":516},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Database\\/DatabaseManager.php\",\"line\":349},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Support\\/Facades\\/Facade.php\",\"line\":261},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/app\\/Console\\/Commands\\/Upgrade\\/ResetMysqlViews.php\",\"line\":67},[],{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Container\\/BoundMethod.php\",\"line\":32},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Container\\/Util.php\",\"line\":36},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Container\\/BoundMethod.php\",\"line\":90},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Container\\/BoundMethod.php\",\"line\":34},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Container\\/Container.php\",\"line\":590},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Console\\/Command.php\",\"line\":134},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/symfony\\/console\\/Command\\/Command.php\",\"line\":255},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Console\\/Command.php\",\"line\":121},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/symfony\\/console\\/Application.php\",\"line\":1001},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/symfony\\/console\\/Application.php\",\"line\":271},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/symfony\\/console\\/Application.php\",\"line\":147},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Console\\/Application.php\",\"line\":93},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Foundation\\/Console\\/Kernel.php\",\"line\":131},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/artisan\",\"line\":37}],\"line_preview\":{\"660\":\" \\/\\/ took to execute and log the query SQL, bindings and time in our memory.\",\"661\":\" try {\",\"662\":\" $result = $callback($query, $bindings);\",\"663\":\" }\",\"664\":\"\",\"665\":\" \\/\\/ If an exception occurs when attempting to run a query, we\'ll format the error\",\"666\":\" \\/\\/ message to include the bindings with SQL, which will make this exception a\",\"667\":\" \\/\\/ lot more helpful to the developer instead of just the database\'s errors.\",\"668\":\" catch (Exception $e) {\",\"669\":\" throw new QueryException(\",\"670\":\" $query, $this->prepareBindings($bindings), $e\",\"671\":\" );\",\"672\":\" }\",\"673\":\"\",\"674\":\" return $result;\",\"675\":\" }\",\"676\":\"\",\"677\":\" \\/**\",\"678\":\" * Log a query in the connection\'s query log.\",\"679\":\" *\"},\"hostname\":\"Barrys-MacBook-Pro.local\",\"occurrences\":3}','2020-09-03 15:26:24'),(7,'9170f484-e2f4-4b89-9eb8-61091d0df78a','9170f484-ec7a-43cb-b5a3-1adda015d112','fe14f6e8415a436cf310f61ef353127e',0,'exception','{\"class\":\"Illuminate\\\\Database\\\\QueryException\",\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Database\\/Connection.php\",\"line\":669,\"message\":\"SQLSTATE[42000]: Syntax error or access violation: 1227 Access denied; you need (at least one of) the SYSTEM_USER privilege(s) for this operation (SQL: -- Views and triggers used on the IXP Manager database\\n\\n-- view: view_cust_current_active\\n--\\n-- This is used to pick up all currently active members. This can further \\n-- be refined by checking for customer type.\\n\\nDROP VIEW IF EXISTS view_cust_current_active;\\nCREATE VIEW view_cust_current_active AS\\n\\tSELECT * FROM cust cu\\n\\tWHERE\\n\\t\\tcu.datejoin <= CURDATE()\\n\\tAND\\t(\\n\\t\\t\\t( cu.dateleave IS NULL )\\n\\t\\tOR\\t( cu.dateleave < \'1970-01-01\' )\\n\\t\\tOR\\t( cu.dateleave >= CURDATE() )\\n\\t\\t)\\n\\tAND\\t(cu.status = 1 OR cu.status = 2);\\n\\n-- view: view_vlaninterface_details_by_custid\\n--\\n-- This is used to pick up all interesting details from virtualinterfaces.\\n\\nDROP VIEW IF EXISTS view_vlaninterface_details_by_custid;\\nCREATE VIEW view_vlaninterface_details_by_custid AS\\n\\tSELECT\\n \\t`pi`.`id` AS `id`,\\n\\t\\tvi.custid,\\n\\t\\tpi.virtualinterfaceid,\\n\\t\\tpi.status,\\n\\t\\tCONCAT(vi.name,vi.channelgroup) AS virtualinterfacename,\\n\\t\\tvlan.number AS vlan,\\n\\t\\tvlan.name AS vlanname,\\n\\t\\tvlan.id AS vlanid,\\n\\t\\tvli.id AS vlaninterfaceid,\\n\\t\\tvli.ipv4enabled,\\n\\t\\tvli.ipv4hostname,\\n\\t\\tvli.ipv4canping,\\n\\t\\tvli.ipv4monitorrcbgp,\\n\\t\\tvli.ipv6enabled,\\n\\t\\tvli.ipv6hostname,\\n\\t\\tvli.ipv6canping,\\n\\t\\tvli.ipv6monitorrcbgp,\\n\\t\\tvli.as112client,\\n\\t\\tvli.mcastenabled,\\n\\t\\tvli.ipv4bgpmd5secret,\\n\\t\\tvli.ipv6bgpmd5secret,\\n\\t\\tvli.rsclient,\\n\\t\\tvli.irrdbfilter,\\n\\t\\tvli.busyhost,\\n\\t\\tvli.notes,\\n\\t\\tv4.address AS ipv4address,\\n\\t\\tv6.address AS ipv6address\\n\\tFROM\\n\\t\\tphysicalinterface pi,\\n\\t\\tvirtualinterface vi,\\n\\t\\tvlaninterface vli\\n\\tLEFT JOIN (ipv4address v4) ON vli.ipv4addressid = v4.id\\n\\tLEFT JOIN (ipv6address v6) ON vli.ipv6addressid = v6.id\\n\\tLEFT JOIN vlan ON vli.vlanid = vlan.id\\n\\tWHERE\\n\\t\\tpi.virtualinterfaceid = vi.id\\n\\tAND\\tvli.virtualinterfaceid = vi.id;\\n\\n-- view: view_switch_details_by_custid\\n--\\n-- This is used to pick up all interesting details from switches.\\n\\nDROP VIEW IF EXISTS view_switch_details_by_custid;\\nCREATE VIEW view_switch_details_by_custid AS\\n\\tSELECT\\n\\t\\tvi.id AS id,\\n\\t\\tvi.custid,\\n\\t\\tCONCAT(vi.name,vi.channelgroup) AS virtualinterfacename,\\n\\t\\tpi.virtualinterfaceid,\\n\\t\\tpi.status,\\n\\t\\tpi.speed,\\n\\t\\tpi.duplex,\\n\\t\\tpi.notes,\\n\\t\\tsp.name AS switchport,\\n\\t\\tsp.id AS switchportid,\\n\\t\\tsp.ifName AS spifname,\\n\\t\\tsw.name AS switch,\\n\\t\\tsw.hostname AS switchhostname,\\n\\t\\tsw.id AS switchid,\\n\\t\\tsw.vendorid,\\n\\t\\tsw.snmppasswd,\\n\\t\\tsw.infrastructure,\\n\\t\\tca.name AS cabinet,\\n\\t\\tca.colocation AS colocabinet,\\n\\t\\tlo.name AS locationname,\\n\\t\\tlo.shortname AS locationshortname\\n\\tFROM\\n\\t\\tvirtualinterface vi,\\n\\t\\tphysicalinterface pi,\\n\\t\\tswitchport sp,\\n\\t\\tswitch sw,\\n\\t\\tcabinet ca,\\n\\t\\tlocation lo\\n\\tWHERE\\n\\t\\tpi.virtualinterfaceid = vi.id\\n\\tAND\\tpi.switchportid = sp.id\\n\\tAND\\tsp.switchid = sw.id\\n\\tAND\\tsw.cabinetid = ca.id\\n\\tAND\\tca.locationid = lo.id;\\n\\n\\n\\n-- trigger: bgp_sessions_update\\n--\\n-- This is used to update a n^2 table showing who peers with whom\\n\\n\\nDROP TRIGGER IF EXISTS `bgp_sessions_update`;\\n\\nDELIMITER ;;\\n\\nCREATE TRIGGER bgp_sessions_update AFTER INSERT ON `bgpsessiondata` FOR EACH ROW\\n\\n\\tBEGIN\\n\\n\\t\\tIF NOT EXISTS ( SELECT 1 FROM bgp_sessions WHERE srcipaddressid = NEW.srcipaddressid AND protocol = NEW.protocol AND dstipaddressid = NEW.dstipaddressid ) THEN\\n\\t\\t\\tINSERT INTO bgp_sessions\\n\\t\\t\\t\\t( srcipaddressid, protocol, dstipaddressid, packetcount, last_seen, source )\\n\\t\\t\\tVALUES\\n\\t\\t\\t\\t( NEW.srcipaddressid, NEW.protocol, NEW.dstipaddressid, NEW.packetcount, NOW(), NEW.source );\\n\\t\\tELSE\\n\\t\\t\\tUPDATE bgp_sessions SET\\n\\t\\t\\t\\tlast_seen = NOW(),\\n\\t\\t\\t\\tpacketcount = packetcount + NEW.packetcount\\n\\t\\t\\tWHERE\\n\\t\\t\\t\\tsrcipaddressid = NEW.srcipaddressid AND protocol = NEW.protocol AND dstipaddressid = NEW.dstipaddressid;\\n\\t\\tEND IF;\\n\\n\\t\\tIF NOT EXISTS ( SELECT 1 FROM bgp_sessions WHERE dstipaddressid = NEW.srcipaddressid AND protocol = NEW.protocol AND srcipaddressid = NEW.dstipaddressid ) THEN\\n\\t\\t\\tINSERT INTO bgp_sessions\\n\\t\\t\\t\\t( srcipaddressid, protocol, dstipaddressid, packetcount, last_seen, source )\\n\\t\\t\\tVALUES\\n\\t\\t\\t\\t( NEW.dstipaddressid, NEW.protocol, NEW.srcipaddressid, NEW.packetcount, NOW(), NEW.source );\\n\\t\\tELSE\\n\\t\\t\\tUPDATE bgp_sessions SET\\n\\t\\t\\t\\tlast_seen = NOW(),\\n\\t\\t\\t\\tpacketcount = packetcount + NEW.packetcount\\n\\t\\t\\tWHERE\\n\\t\\t\\t\\tdstipaddressid = NEW.srcipaddressid AND protocol = NEW.protocol AND srcipaddressid = NEW.dstipaddressid;\\n\\t\\tEND IF;\\n\\n\\tEND ;;\\n\\nDELIMITER ;\\n)\",\"trace\":[{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Database\\/Connection.php\",\"line\":629},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Database\\/Connection.php\",\"line\":516},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Database\\/DatabaseManager.php\",\"line\":349},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Support\\/Facades\\/Facade.php\",\"line\":261},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/app\\/Console\\/Commands\\/Upgrade\\/ResetMysqlViews.php\",\"line\":67},[],{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Container\\/BoundMethod.php\",\"line\":32},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Container\\/Util.php\",\"line\":36},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Container\\/BoundMethod.php\",\"line\":90},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Container\\/BoundMethod.php\",\"line\":34},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Container\\/Container.php\",\"line\":590},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Console\\/Command.php\",\"line\":134},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/symfony\\/console\\/Command\\/Command.php\",\"line\":255},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Console\\/Command.php\",\"line\":121},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/symfony\\/console\\/Application.php\",\"line\":1001},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/symfony\\/console\\/Application.php\",\"line\":271},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/symfony\\/console\\/Application.php\",\"line\":147},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Console\\/Application.php\",\"line\":93},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Foundation\\/Console\\/Kernel.php\",\"line\":131},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/artisan\",\"line\":37}],\"line_preview\":{\"660\":\" \\/\\/ took to execute and log the query SQL, bindings and time in our memory.\",\"661\":\" try {\",\"662\":\" $result = $callback($query, $bindings);\",\"663\":\" }\",\"664\":\"\",\"665\":\" \\/\\/ If an exception occurs when attempting to run a query, we\'ll format the error\",\"666\":\" \\/\\/ message to include the bindings with SQL, which will make this exception a\",\"667\":\" \\/\\/ lot more helpful to the developer instead of just the database\'s errors.\",\"668\":\" catch (Exception $e) {\",\"669\":\" throw new QueryException(\",\"670\":\" $query, $this->prepareBindings($bindings), $e\",\"671\":\" );\",\"672\":\" }\",\"673\":\"\",\"674\":\" return $result;\",\"675\":\" }\",\"676\":\"\",\"677\":\" \\/**\",\"678\":\" * Log a query in the connection\'s query log.\",\"679\":\" *\"},\"hostname\":\"Barrys-MacBook-Pro.local\",\"occurrences\":4}','2020-09-03 15:27:27'),(8,'9170f4d3-2f56-4797-94d6-eb02dd5a4b01','9170f4d3-3b1d-486c-8e0c-2ef663e9357f','fe14f6e8415a436cf310f61ef353127e',1,'exception','{\"class\":\"Illuminate\\\\Database\\\\QueryException\",\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Database\\/Connection.php\",\"line\":669,\"message\":\"SQLSTATE[42000]: Syntax error or access violation: 1227 Access denied; you need (at least one of) the SYSTEM_USER privilege(s) for this operation (SQL: -- Views and triggers used on the IXP Manager database\\n\\n-- view: view_cust_current_active\\n--\\n-- This is used to pick up all currently active members. This can further \\n-- be refined by checking for customer type.\\n\\nDROP VIEW IF EXISTS view_cust_current_active;\\nCREATE VIEW view_cust_current_active AS\\n\\tSELECT * FROM cust cu\\n\\tWHERE\\n\\t\\tcu.datejoin <= CURDATE()\\n\\tAND\\t(\\n\\t\\t\\t( cu.dateleave IS NULL )\\n\\t\\tOR\\t( cu.dateleave < \'1970-01-01\' )\\n\\t\\tOR\\t( cu.dateleave >= CURDATE() )\\n\\t\\t)\\n\\tAND\\t(cu.status = 1 OR cu.status = 2);\\n\\n-- view: view_vlaninterface_details_by_custid\\n--\\n-- This is used to pick up all interesting details from virtualinterfaces.\\n\\nDROP VIEW IF EXISTS view_vlaninterface_details_by_custid;\\nCREATE VIEW view_vlaninterface_details_by_custid AS\\n\\tSELECT\\n \\t`pi`.`id` AS `id`,\\n\\t\\tvi.custid,\\n\\t\\tpi.virtualinterfaceid,\\n\\t\\tpi.status,\\n\\t\\tCONCAT(vi.name,vi.channelgroup) AS virtualinterfacename,\\n\\t\\tvlan.number AS vlan,\\n\\t\\tvlan.name AS vlanname,\\n\\t\\tvlan.id AS vlanid,\\n\\t\\tvli.id AS vlaninterfaceid,\\n\\t\\tvli.ipv4enabled,\\n\\t\\tvli.ipv4hostname,\\n\\t\\tvli.ipv4canping,\\n\\t\\tvli.ipv4monitorrcbgp,\\n\\t\\tvli.ipv6enabled,\\n\\t\\tvli.ipv6hostname,\\n\\t\\tvli.ipv6canping,\\n\\t\\tvli.ipv6monitorrcbgp,\\n\\t\\tvli.as112client,\\n\\t\\tvli.mcastenabled,\\n\\t\\tvli.ipv4bgpmd5secret,\\n\\t\\tvli.ipv6bgpmd5secret,\\n\\t\\tvli.rsclient,\\n\\t\\tvli.irrdbfilter,\\n\\t\\tvli.busyhost,\\n\\t\\tvli.notes,\\n\\t\\tv4.address AS ipv4address,\\n\\t\\tv6.address AS ipv6address\\n\\tFROM\\n\\t\\tphysicalinterface pi,\\n\\t\\tvirtualinterface vi,\\n\\t\\tvlaninterface vli\\n\\tLEFT JOIN (ipv4address v4) ON vli.ipv4addressid = v4.id\\n\\tLEFT JOIN (ipv6address v6) ON vli.ipv6addressid = v6.id\\n\\tLEFT JOIN vlan ON vli.vlanid = vlan.id\\n\\tWHERE\\n\\t\\tpi.virtualinterfaceid = vi.id\\n\\tAND\\tvli.virtualinterfaceid = vi.id;\\n\\n-- view: view_switch_details_by_custid\\n--\\n-- This is used to pick up all interesting details from switches.\\n\\nDROP VIEW IF EXISTS view_switch_details_by_custid;\\nCREATE VIEW view_switch_details_by_custid AS\\n\\tSELECT\\n\\t\\tvi.id AS id,\\n\\t\\tvi.custid,\\n\\t\\tCONCAT(vi.name,vi.channelgroup) AS virtualinterfacename,\\n\\t\\tpi.virtualinterfaceid,\\n\\t\\tpi.status,\\n\\t\\tpi.speed,\\n\\t\\tpi.duplex,\\n\\t\\tpi.notes,\\n\\t\\tsp.name AS switchport,\\n\\t\\tsp.id AS switchportid,\\n\\t\\tsp.ifName AS spifname,\\n\\t\\tsw.name AS switch,\\n\\t\\tsw.hostname AS switchhostname,\\n\\t\\tsw.id AS switchid,\\n\\t\\tsw.vendorid,\\n\\t\\tsw.snmppasswd,\\n\\t\\tsw.infrastructure,\\n\\t\\tca.name AS cabinet,\\n\\t\\tca.colocation AS colocabinet,\\n\\t\\tlo.name AS locationname,\\n\\t\\tlo.shortname AS locationshortname\\n\\tFROM\\n\\t\\tvirtualinterface vi,\\n\\t\\tphysicalinterface pi,\\n\\t\\tswitchport sp,\\n\\t\\tswitch sw,\\n\\t\\tcabinet ca,\\n\\t\\tlocation lo\\n\\tWHERE\\n\\t\\tpi.virtualinterfaceid = vi.id\\n\\tAND\\tpi.switchportid = sp.id\\n\\tAND\\tsp.switchid = sw.id\\n\\tAND\\tsw.cabinetid = ca.id\\n\\tAND\\tca.locationid = lo.id;\\n\\n\\n\\n-- trigger: bgp_sessions_update\\n--\\n-- This is used to update a n^2 table showing who peers with whom\\n\\n\\nDROP TRIGGER IF EXISTS `bgp_sessions_update`;\\n\\nDELIMITER ;;\\n\\nCREATE TRIGGER bgp_sessions_update AFTER INSERT ON `bgpsessiondata` FOR EACH ROW\\n\\n\\tBEGIN\\n\\n\\t\\tIF NOT EXISTS ( SELECT 1 FROM bgp_sessions WHERE srcipaddressid = NEW.srcipaddressid AND protocol = NEW.protocol AND dstipaddressid = NEW.dstipaddressid ) THEN\\n\\t\\t\\tINSERT INTO bgp_sessions\\n\\t\\t\\t\\t( srcipaddressid, protocol, dstipaddressid, packetcount, last_seen, source )\\n\\t\\t\\tVALUES\\n\\t\\t\\t\\t( NEW.srcipaddressid, NEW.protocol, NEW.dstipaddressid, NEW.packetcount, NOW(), NEW.source );\\n\\t\\tELSE\\n\\t\\t\\tUPDATE bgp_sessions SET\\n\\t\\t\\t\\tlast_seen = NOW(),\\n\\t\\t\\t\\tpacketcount = packetcount + NEW.packetcount\\n\\t\\t\\tWHERE\\n\\t\\t\\t\\tsrcipaddressid = NEW.srcipaddressid AND protocol = NEW.protocol AND dstipaddressid = NEW.dstipaddressid;\\n\\t\\tEND IF;\\n\\n\\t\\tIF NOT EXISTS ( SELECT 1 FROM bgp_sessions WHERE dstipaddressid = NEW.srcipaddressid AND protocol = NEW.protocol AND srcipaddressid = NEW.dstipaddressid ) THEN\\n\\t\\t\\tINSERT INTO bgp_sessions\\n\\t\\t\\t\\t( srcipaddressid, protocol, dstipaddressid, packetcount, last_seen, source )\\n\\t\\t\\tVALUES\\n\\t\\t\\t\\t( NEW.dstipaddressid, NEW.protocol, NEW.srcipaddressid, NEW.packetcount, NOW(), NEW.source );\\n\\t\\tELSE\\n\\t\\t\\tUPDATE bgp_sessions SET\\n\\t\\t\\t\\tlast_seen = NOW(),\\n\\t\\t\\t\\tpacketcount = packetcount + NEW.packetcount\\n\\t\\t\\tWHERE\\n\\t\\t\\t\\tdstipaddressid = NEW.srcipaddressid AND protocol = NEW.protocol AND srcipaddressid = NEW.dstipaddressid;\\n\\t\\tEND IF;\\n\\n\\tEND ;;\\n\\nDELIMITER ;\\n)\",\"trace\":[{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Database\\/Connection.php\",\"line\":629},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Database\\/Connection.php\",\"line\":516},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Database\\/DatabaseManager.php\",\"line\":349},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Support\\/Facades\\/Facade.php\",\"line\":261},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/app\\/Console\\/Commands\\/Upgrade\\/ResetMysqlViews.php\",\"line\":67},[],{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Container\\/BoundMethod.php\",\"line\":32},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Container\\/Util.php\",\"line\":36},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Container\\/BoundMethod.php\",\"line\":90},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Container\\/BoundMethod.php\",\"line\":34},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Container\\/Container.php\",\"line\":590},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Console\\/Command.php\",\"line\":134},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/symfony\\/console\\/Command\\/Command.php\",\"line\":255},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Console\\/Command.php\",\"line\":121},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/symfony\\/console\\/Application.php\",\"line\":1001},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/symfony\\/console\\/Application.php\",\"line\":271},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/symfony\\/console\\/Application.php\",\"line\":147},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Console\\/Application.php\",\"line\":93},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/vendor\\/laravel\\/framework\\/src\\/Illuminate\\/Foundation\\/Console\\/Kernel.php\",\"line\":131},{\"file\":\"\\/Users\\/barryo\\/dev\\/ixp-ibn\\/artisan\",\"line\":37}],\"line_preview\":{\"660\":\" \\/\\/ took to execute and log the query SQL, bindings and time in our memory.\",\"661\":\" try {\",\"662\":\" $result = $callback($query, $bindings);\",\"663\":\" }\",\"664\":\"\",\"665\":\" \\/\\/ If an exception occurs when attempting to run a query, we\'ll format the error\",\"666\":\" \\/\\/ message to include the bindings with SQL, which will make this exception a\",\"667\":\" \\/\\/ lot more helpful to the developer instead of just the database\'s errors.\",\"668\":\" catch (Exception $e) {\",\"669\":\" throw new QueryException(\",\"670\":\" $query, $this->prepareBindings($bindings), $e\",\"671\":\" );\",\"672\":\" }\",\"673\":\"\",\"674\":\" return $result;\",\"675\":\" }\",\"676\":\"\",\"677\":\" \\/**\",\"678\":\" * Log a query in the connection\'s query log.\",\"679\":\" *\"},\"hostname\":\"Barrys-MacBook-Pro.local\",\"occurrences\":5}','2020-09-03 15:28:18'); +/*!40000 ALTER TABLE `telescope_entries` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `telescope_entries_tags` +-- + +DROP TABLE IF EXISTS `telescope_entries_tags`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `telescope_entries_tags` ( + `entry_uuid` char(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `tag` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + KEY `telescope_entries_tags_entry_uuid_tag_index` (`entry_uuid`,`tag`), + KEY `telescope_entries_tags_tag_index` (`tag`), + CONSTRAINT `telescope_entries_tags_entry_uuid_foreign` FOREIGN KEY (`entry_uuid`) REFERENCES `telescope_entries` (`uuid`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `telescope_entries_tags` +-- + +LOCK TABLES `telescope_entries_tags` WRITE; +/*!40000 ALTER TABLE `telescope_entries_tags` DISABLE KEYS */; +INSERT INTO `telescope_entries_tags` VALUES ('8ff2744d-3f5e-4d3f-8136-6d1b43cb27ea','Auth:1'),('9101c3ca-7580-4574-b3d5-7f2008dd4637','Auth:1'); +/*!40000 ALTER TABLE `telescope_entries_tags` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `telescope_monitoring` +-- + +DROP TABLE IF EXISTS `telescope_monitoring`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `telescope_monitoring` ( + `tag` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `telescope_monitoring` +-- + +LOCK TABLES `telescope_monitoring` WRITE; +/*!40000 ALTER TABLE `telescope_monitoring` DISABLE KEYS */; +/*!40000 ALTER TABLE `telescope_monitoring` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `traffic_95th` +-- + +DROP TABLE IF EXISTS `traffic_95th`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `traffic_95th` ( + `id` bigint NOT NULL AUTO_INCREMENT, + `cust_id` int DEFAULT NULL, + `datetime` datetime DEFAULT NULL, + `average` bigint DEFAULT NULL, + `max` bigint DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `IDX_70BB409ABFF2A482` (`cust_id`), + CONSTRAINT `FK_70BB409ABFF2A482` FOREIGN KEY (`cust_id`) REFERENCES `cust` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `traffic_95th` +-- + +LOCK TABLES `traffic_95th` WRITE; +/*!40000 ALTER TABLE `traffic_95th` DISABLE KEYS */; +/*!40000 ALTER TABLE `traffic_95th` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `traffic_95th_monthly` +-- + +DROP TABLE IF EXISTS `traffic_95th_monthly`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `traffic_95th_monthly` ( + `id` bigint NOT NULL AUTO_INCREMENT, + `cust_id` int DEFAULT NULL, + `month` date DEFAULT NULL, + `max_95th` bigint DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `IDX_ED79F9DCBFF2A482` (`cust_id`), + CONSTRAINT `FK_ED79F9DCBFF2A482` FOREIGN KEY (`cust_id`) REFERENCES `cust` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `traffic_95th_monthly` +-- + +LOCK TABLES `traffic_95th_monthly` WRITE; +/*!40000 ALTER TABLE `traffic_95th_monthly` DISABLE KEYS */; +/*!40000 ALTER TABLE `traffic_95th_monthly` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `traffic_daily` +-- + +DROP TABLE IF EXISTS `traffic_daily`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `traffic_daily` ( + `id` bigint NOT NULL AUTO_INCREMENT, + `cust_id` int NOT NULL, + `day` date DEFAULT NULL, + `category` varchar(10) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `day_avg_in` bigint DEFAULT NULL, + `day_avg_out` bigint DEFAULT NULL, + `day_max_in` bigint DEFAULT NULL, + `day_max_out` bigint DEFAULT NULL, + `day_tot_in` bigint DEFAULT NULL, + `day_tot_out` bigint DEFAULT NULL, + `week_avg_in` bigint DEFAULT NULL, + `week_avg_out` bigint DEFAULT NULL, + `week_max_in` bigint DEFAULT NULL, + `week_max_out` bigint DEFAULT NULL, + `week_tot_in` bigint DEFAULT NULL, + `week_tot_out` bigint DEFAULT NULL, + `month_avg_in` bigint DEFAULT NULL, + `month_avg_out` bigint DEFAULT NULL, + `month_max_in` bigint DEFAULT NULL, + `month_max_out` bigint DEFAULT NULL, + `month_tot_in` bigint DEFAULT NULL, + `month_tot_out` bigint DEFAULT NULL, + `year_avg_in` bigint DEFAULT NULL, + `year_avg_out` bigint DEFAULT NULL, + `year_max_in` bigint DEFAULT NULL, + `year_max_out` bigint DEFAULT NULL, + `year_tot_in` bigint DEFAULT NULL, + `year_tot_out` bigint DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `IDX_1F0F81A7BFF2A482` (`cust_id`), + CONSTRAINT `FK_1F0F81A7BFF2A482` FOREIGN KEY (`cust_id`) REFERENCES `cust` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `traffic_daily` +-- + +LOCK TABLES `traffic_daily` WRITE; +/*!40000 ALTER TABLE `traffic_daily` DISABLE KEYS */; +/*!40000 ALTER TABLE `traffic_daily` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `traffic_daily_phys_ints` +-- + +DROP TABLE IF EXISTS `traffic_daily_phys_ints`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `traffic_daily_phys_ints` ( + `id` bigint NOT NULL AUTO_INCREMENT, + `physicalinterface_id` int NOT NULL, + `day` date DEFAULT NULL, + `category` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `day_avg_in` bigint DEFAULT NULL, + `day_avg_out` bigint DEFAULT NULL, + `day_max_in` bigint DEFAULT NULL, + `day_max_out` bigint DEFAULT NULL, + `day_max_in_at` datetime DEFAULT NULL, + `day_max_out_at` datetime DEFAULT NULL, + `day_tot_in` bigint DEFAULT NULL, + `day_tot_out` bigint DEFAULT NULL, + `week_avg_in` bigint DEFAULT NULL, + `week_avg_out` bigint DEFAULT NULL, + `week_max_in` bigint DEFAULT NULL, + `week_max_out` bigint DEFAULT NULL, + `week_max_in_at` datetime DEFAULT NULL, + `week_max_out_at` datetime DEFAULT NULL, + `week_tot_in` bigint DEFAULT NULL, + `week_tot_out` bigint DEFAULT NULL, + `month_avg_in` bigint DEFAULT NULL, + `month_avg_out` bigint DEFAULT NULL, + `month_max_in` bigint DEFAULT NULL, + `month_max_out` bigint DEFAULT NULL, + `month_max_in_at` datetime DEFAULT NULL, + `month_max_out_at` datetime DEFAULT NULL, + `month_tot_in` bigint DEFAULT NULL, + `month_tot_out` bigint DEFAULT NULL, + `year_avg_in` bigint DEFAULT NULL, + `year_avg_out` bigint DEFAULT NULL, + `year_max_in` bigint DEFAULT NULL, + `year_max_out` bigint DEFAULT NULL, + `year_max_in_at` datetime DEFAULT NULL, + `year_max_out_at` datetime DEFAULT NULL, + `year_tot_in` bigint DEFAULT NULL, + `year_tot_out` bigint DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `IDX_E219461D4643D08A` (`physicalinterface_id`), + CONSTRAINT `FK_E219461D4643D08A` FOREIGN KEY (`physicalinterface_id`) REFERENCES `physicalinterface` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `traffic_daily_phys_ints` +-- + +LOCK TABLES `traffic_daily_phys_ints` WRITE; +/*!40000 ALTER TABLE `traffic_daily_phys_ints` DISABLE KEYS */; +/*!40000 ALTER TABLE `traffic_daily_phys_ints` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `user` +-- + +DROP TABLE IF EXISTS `user`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `user` ( + `id` int NOT NULL AUTO_INCREMENT, + `custid` int DEFAULT NULL, + `username` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `password` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `email` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `authorisedMobile` varchar(30) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `uid` int DEFAULT NULL, + `privs` int DEFAULT NULL, + `disabled` tinyint(1) DEFAULT NULL, + `lastupdatedby` int DEFAULT NULL, + `creator` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `peeringdb_id` bigint DEFAULT NULL, + `extra_attributes` json DEFAULT NULL COMMENT '(DC2Type:json)', + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + `prefs` json DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `UNIQ_8D93D649F85E0677` (`username`), + UNIQUE KEY `UNIQ_8D93D649F2C6186B` (`peeringdb_id`), + KEY `IDX_8D93D649DA0209B9` (`custid`), + CONSTRAINT `FK_8D93D649DA0209B9` FOREIGN KEY (`custid`) REFERENCES `cust` (`id`) ON DELETE SET NULL +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `user` +-- + +LOCK TABLES `user` WRITE; +/*!40000 ALTER TABLE `user` DISABLE KEYS */; +INSERT INTO `user` VALUES (1,1,'travis','$2y$10$FNzPyTKm64oSKeUUCwm1buLQp7h80nBj2suqdjsWH2aajVS1xz/ce','joe@siep.com',NULL,NULL,3,0,1,'travis',NULL,NULL,NULL,'2014-01-06 12:54:22','2014-01-06 12:54:22',NULL),(2,5,'imcustadmin','$2y$10$VlJG/42TCK7VQz1Wwy7yreP73Eq/1VKn55B4vJfXy4U7fIGK/9YWC','imagine-custadmin@example.com',NULL,NULL,2,0,2,'travis','Test Test',NULL,NULL,'2018-05-15 13:36:12','2019-01-16 14:37:24',NULL),(3,5,'imcustuser','$2y$10$sIUXAklQmQwalBF0nGgCLenCYYUMXWdqSESRjw6faXfiyymfmpk3y','imagine-custuser@example.com',NULL,NULL,1,0,3,'travis','Joe Bloggs',NULL,NULL,'2018-05-15 13:36:54','2019-01-16 14:44:30',NULL),(4,2,'hecustuser','$2y$10$sIUXAklQmQwalBF0nGgCLenCYYUMXWdqSESRjw6faXfiyymfmpk3y','heanet-custuser@example.com',NULL,NULL,1,0,1,'travis',NULL,NULL,NULL,'2018-05-15 13:36:54','2018-05-15 13:36:54',NULL),(5,2,'hecustadmin','$2y$10$sIUXAklQmQwalBF0nGgCLenCYYUMXWdqSESRjw6faXfiyymfmpk3y','heanet-custadmin@example.com',NULL,NULL,2,0,1,'travis',NULL,NULL,NULL,'2018-05-15 13:36:54','2018-05-15 13:36:54',NULL); +/*!40000 ALTER TABLE `user` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `user_2fa` +-- + +DROP TABLE IF EXISTS `user_2fa`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `user_2fa` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `enabled` tinyint(1) NOT NULL DEFAULT '0', + `secret` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `UNIQ_3AAA1488A76ED395` (`user_id`), + CONSTRAINT `FK_3AAA1488A76ED395` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `user_2fa` +-- + +LOCK TABLES `user_2fa` WRITE; +/*!40000 ALTER TABLE `user_2fa` DISABLE KEYS */; +/*!40000 ALTER TABLE `user_2fa` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `user_logins` +-- + +DROP TABLE IF EXISTS `user_logins`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `user_logins` ( + `id` bigint NOT NULL AUTO_INCREMENT, + `user_id` int DEFAULT NULL, + `ip` varchar(39) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL, + `at` datetime NOT NULL, + `customer_to_user_id` int DEFAULT NULL, + `via` varchar(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `user_id_idx` (`user_id`), + KEY `IDX_6341CC99D43FEAE2` (`customer_to_user_id`), + KEY `at_idx` (`at`), + CONSTRAINT `FK_6341CC99D43FEAE2` FOREIGN KEY (`customer_to_user_id`) REFERENCES `customer_to_users` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=60 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `user_logins` +-- + +LOCK TABLES `user_logins` WRITE; +/*!40000 ALTER TABLE `user_logins` DISABLE KEYS */; +INSERT INTO `user_logins` VALUES (1,1,'10.37.129.2','2014-01-06 13:54:52',1,NULL,NULL,NULL),(2,1,'10.37.129.2','2014-01-13 10:38:11',1,NULL,NULL,NULL),(3,1,'::1','2016-11-07 19:30:35',1,NULL,NULL,NULL),(4,1,'127.0.0.1','2017-10-09 13:19:59',1,NULL,NULL,NULL),(5,1,'127.0.0.1','2018-05-15 15:34:35',1,NULL,NULL,NULL),(6,1,'127.0.0.1','2018-06-18 08:30:06',1,NULL,NULL,NULL),(7,1,'127.0.0.1','2018-06-18 08:30:08',1,NULL,NULL,NULL),(8,1,'127.0.0.1','2018-06-18 08:31:04',1,NULL,NULL,NULL),(9,1,'127.0.0.1','2018-06-18 08:31:06',1,NULL,NULL,NULL),(10,1,'127.0.0.1','2018-06-18 08:36:56',1,NULL,NULL,NULL),(11,1,'127.0.0.1','2018-06-18 08:36:58',1,NULL,NULL,NULL),(12,1,'127.0.0.1','2018-06-18 08:43:14',1,NULL,NULL,NULL),(13,1,'127.0.0.1','2018-06-18 08:43:16',1,NULL,NULL,NULL),(14,1,'127.0.0.1','2018-06-18 08:43:27',1,NULL,NULL,NULL),(15,1,'127.0.0.1','2018-06-18 08:43:29',1,NULL,NULL,NULL),(16,1,'127.0.0.1','2018-06-18 11:29:20',1,NULL,NULL,NULL),(17,1,'127.0.0.1','2018-06-18 11:29:22',1,NULL,NULL,NULL),(18,1,'127.0.0.1','2018-06-19 13:15:32',1,NULL,NULL,NULL),(19,1,'127.0.0.1','2018-06-19 14:16:24',1,NULL,NULL,NULL),(20,1,'127.0.0.1','2018-06-19 14:16:26',1,NULL,NULL,NULL),(21,1,'127.0.0.1','2018-06-19 14:17:07',1,NULL,NULL,NULL),(22,1,'127.0.0.1','2018-06-19 14:17:09',1,NULL,NULL,NULL),(23,1,'127.0.0.1','2018-06-19 14:19:14',1,NULL,NULL,NULL),(24,1,'127.0.0.1','2018-06-19 14:19:16',1,NULL,NULL,NULL),(25,1,'127.0.0.1','2018-06-19 14:22:14',1,NULL,NULL,NULL),(26,1,'127.0.0.1','2018-06-19 14:22:17',1,NULL,NULL,NULL),(27,2,'127.0.0.1','2018-06-20 10:23:22',2,NULL,NULL,NULL),(28,3,'127.0.0.1','2018-06-20 10:23:58',3,NULL,NULL,NULL),(29,5,'127.0.0.1','2018-06-20 10:24:14',5,NULL,NULL,NULL),(30,5,'127.0.0.1','2018-06-20 10:24:24',5,NULL,NULL,NULL),(31,1,'127.0.0.1','2018-06-20 10:25:55',1,NULL,NULL,NULL),(32,1,'127.0.0.1','2018-06-20 10:25:57',1,NULL,NULL,NULL),(33,1,'127.0.0.1','2018-06-20 10:26:49',1,NULL,NULL,NULL),(34,1,'127.0.0.1','2018-06-20 10:26:51',1,NULL,NULL,NULL),(35,1,'127.0.0.1','2018-06-20 10:27:05',1,NULL,NULL,NULL),(36,1,'127.0.0.1','2018-06-20 10:27:07',1,NULL,NULL,NULL),(37,1,'127.0.0.1','2018-06-20 10:27:22',1,NULL,NULL,NULL),(38,1,'127.0.0.1','2018-06-20 10:27:24',1,NULL,NULL,NULL),(39,1,'127.0.0.1','2018-06-20 10:28:25',1,NULL,NULL,NULL),(40,1,'127.0.0.1','2018-06-20 10:28:27',1,NULL,NULL,NULL),(41,1,'127.0.0.1','2018-06-20 10:28:57',1,NULL,NULL,NULL),(42,1,'127.0.0.1','2018-06-20 10:28:59',1,NULL,NULL,NULL),(43,1,'127.0.0.1','2018-06-20 10:32:11',1,NULL,NULL,NULL),(44,1,'127.0.0.1','2018-06-20 10:32:13',1,NULL,NULL,NULL),(45,1,'127.0.0.1','2018-06-20 10:36:34',1,NULL,NULL,NULL),(46,1,'127.0.0.1','2018-06-20 10:36:36',1,NULL,NULL,NULL),(47,1,'127.0.0.1','2018-06-20 10:37:19',1,NULL,NULL,NULL),(48,1,'127.0.0.1','2018-06-20 10:37:21',1,NULL,NULL,NULL),(49,1,'127.0.0.1','2018-06-20 10:37:44',1,NULL,NULL,NULL),(50,1,'127.0.0.1','2018-06-20 10:37:46',1,NULL,NULL,NULL),(51,1,'127.0.0.1','2018-06-20 10:38:41',1,NULL,NULL,NULL),(52,1,'127.0.0.1','2018-06-20 10:38:42',1,NULL,NULL,NULL),(53,2,'127.0.0.1','2019-01-16 15:37:08',2,NULL,NULL,NULL),(54,3,'127.0.0.1','2019-01-16 15:38:05',3,NULL,NULL,NULL),(55,1,'127.0.0.1','2019-03-09 15:38:09',1,NULL,NULL,NULL),(56,NULL,'127.0.0.1','2020-01-27 12:04:24',1,NULL,NULL,NULL),(57,NULL,'127.0.0.1','2020-07-10 07:54:18',1,'Login',NULL,NULL),(58,NULL,'127.0.0.1','2022-02-28 11:36:25',1,'Login','2022-02-28 11:36:25','2022-02-28 11:36:25'),(59,NULL,'127.0.0.1','2022-02-28 15:16:03',1,'Login','2022-02-28 15:16:03','2022-02-28 15:16:03'); +/*!40000 ALTER TABLE `user_logins` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `user_pref` +-- + +DROP TABLE IF EXISTS `user_pref`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `user_pref` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int DEFAULT NULL, + `attribute` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `ix` int NOT NULL DEFAULT '0', + `op` varchar(2) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `value` longtext CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci, + `expire` bigint NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + UNIQUE KEY `IX_UserPreference_1` (`user_id`,`attribute`,`op`,`ix`), + KEY `IDX_DBD4D4F8A76ED395` (`user_id`), + CONSTRAINT `FK_DBD4D4F8A76ED395` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `user_pref` +-- + +LOCK TABLES `user_pref` WRITE; +/*!40000 ALTER TABLE `user_pref` DISABLE KEYS */; +INSERT INTO `user_pref` VALUES (1,1,'auth.last_login_from',0,'=','127.0.0.1',0),(2,1,'auth.last_login_at',0,'=','1529491122',0),(3,2,'auth.last_login_from',0,'=','127.0.0.1',0),(4,2,'auth.last_login_at',0,'=','1529490202',0),(5,3,'auth.last_login_from',0,'=','127.0.0.1',0),(6,3,'auth.last_login_at',0,'=','1529490238',0),(7,5,'auth.last_login_from',0,'=','127.0.0.1',0),(8,5,'auth.last_login_at',0,'=','1529490264',0); +/*!40000 ALTER TABLE `user_pref` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `user_remember_tokens` +-- + +DROP TABLE IF EXISTS `user_remember_tokens`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `user_remember_tokens` ( + `id` bigint NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `token` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `device` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `ip` varchar(39) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `expires` datetime NOT NULL, + `is_2fa_complete` tinyint(1) NOT NULL DEFAULT '0', + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `user_token` (`user_id`,`token`), + KEY `IDX_E253302EA76ED395` (`user_id`), + CONSTRAINT `FK_E253302EA76ED395` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `user_remember_tokens` +-- + +LOCK TABLES `user_remember_tokens` WRITE; +/*!40000 ALTER TABLE `user_remember_tokens` DISABLE KEYS */; +/*!40000 ALTER TABLE `user_remember_tokens` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `vendor` +-- + +DROP TABLE IF EXISTS `vendor`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `vendor` ( + `id` int NOT NULL AUTO_INCREMENT, + `name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `shortname` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `nagios_name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `bundle_name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `vendor` +-- + +LOCK TABLES `vendor` WRITE; +/*!40000 ALTER TABLE `vendor` DISABLE KEYS */; +INSERT INTO `vendor` VALUES (1,'Cisco Systems','Cisco','cisco',NULL,NULL,NULL),(2,'Foundry Networks','Brocade','brocade',NULL,NULL,NULL),(3,'Extreme Networks','Extreme','extreme',NULL,NULL,NULL),(4,'Force10 Networks','Force10','force10',NULL,NULL,NULL),(5,'Glimmerglass','Glimmerglass','glimmerglass',NULL,NULL,NULL),(6,'Allied Telesyn','AlliedTel','alliedtel',NULL,NULL,NULL),(7,'Enterasys','Enterasys','enterasys',NULL,NULL,NULL),(8,'Dell','Dell','dell',NULL,NULL,NULL),(9,'Hitachi Cable','Hitachi','hitachi',NULL,NULL,NULL),(10,'MRV','MRV','mrv',NULL,NULL,NULL),(11,'Transmode','Transmode','transmode',NULL,NULL,NULL),(12,'Brocade','Brocade','brocade',NULL,NULL,NULL),(13,'Juniper Networks','Juniper','juniper',NULL,NULL,NULL); +/*!40000 ALTER TABLE `vendor` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Temporary view structure for view `view_cust_current_active` +-- + +DROP TABLE IF EXISTS `view_cust_current_active`; +/*!50001 DROP VIEW IF EXISTS `view_cust_current_active`*/; +SET @saved_cs_client = @@character_set_client; +/*!50503 SET character_set_client = utf8mb4 */; +/*!50001 CREATE VIEW `view_cust_current_active` AS SELECT + 1 AS `id`, + 1 AS `irrdb`, + 1 AS `company_registered_detail_id`, + 1 AS `company_billing_details_id`, + 1 AS `reseller`, + 1 AS `name`, + 1 AS `type`, + 1 AS `shortname`, + 1 AS `abbreviatedName`, + 1 AS `autsys`, + 1 AS `maxprefixes`, + 1 AS `peeringemail`, + 1 AS `nocphone`, + 1 AS `noc24hphone`, + 1 AS `nocfax`, + 1 AS `nocemail`, + 1 AS `nochours`, + 1 AS `nocwww`, + 1 AS `peeringmacro`, + 1 AS `peeringmacrov6`, + 1 AS `peeringpolicy`, + 1 AS `corpwww`, + 1 AS `datejoin`, + 1 AS `dateleave`, + 1 AS `status`, + 1 AS `activepeeringmatrix`, + 1 AS `lastupdatedby`, + 1 AS `creator`, + 1 AS `MD5Support`, + 1 AS `isReseller`, + 1 AS `in_manrs`, + 1 AS `in_peeringdb`, + 1 AS `peeringdb_oauth`, + 1 AS `created_at`, + 1 AS `updated_at`*/; +SET character_set_client = @saved_cs_client; + +-- +-- Temporary view structure for view `view_switch_details_by_custid` +-- + +DROP TABLE IF EXISTS `view_switch_details_by_custid`; +/*!50001 DROP VIEW IF EXISTS `view_switch_details_by_custid`*/; +SET @saved_cs_client = @@character_set_client; +/*!50503 SET character_set_client = utf8mb4 */; +/*!50001 CREATE VIEW `view_switch_details_by_custid` AS SELECT + 1 AS `id`, + 1 AS `custid`, + 1 AS `virtualinterfacename`, + 1 AS `virtualinterfaceid`, + 1 AS `status`, + 1 AS `speed`, + 1 AS `duplex`, + 1 AS `notes`, + 1 AS `switchport`, + 1 AS `switchportid`, + 1 AS `spifname`, + 1 AS `switch`, + 1 AS `switchhostname`, + 1 AS `switchid`, + 1 AS `vendorid`, + 1 AS `snmppasswd`, + 1 AS `infrastructure`, + 1 AS `cabinet`, + 1 AS `colocabinet`, + 1 AS `locationname`, + 1 AS `locationshortname`*/; +SET character_set_client = @saved_cs_client; + +-- +-- Temporary view structure for view `view_vlaninterface_details_by_custid` +-- + +DROP TABLE IF EXISTS `view_vlaninterface_details_by_custid`; +/*!50001 DROP VIEW IF EXISTS `view_vlaninterface_details_by_custid`*/; +SET @saved_cs_client = @@character_set_client; +/*!50503 SET character_set_client = utf8mb4 */; +/*!50001 CREATE VIEW `view_vlaninterface_details_by_custid` AS SELECT + 1 AS `id`, + 1 AS `custid`, + 1 AS `virtualinterfaceid`, + 1 AS `status`, + 1 AS `virtualinterfacename`, + 1 AS `vlan`, + 1 AS `vlanname`, + 1 AS `vlanid`, + 1 AS `vlaninterfaceid`, + 1 AS `ipv4enabled`, + 1 AS `ipv4hostname`, + 1 AS `ipv4canping`, + 1 AS `ipv4monitorrcbgp`, + 1 AS `ipv6enabled`, + 1 AS `ipv6hostname`, + 1 AS `ipv6canping`, + 1 AS `ipv6monitorrcbgp`, + 1 AS `as112client`, + 1 AS `mcastenabled`, + 1 AS `ipv4bgpmd5secret`, + 1 AS `ipv6bgpmd5secret`, + 1 AS `rsclient`, + 1 AS `irrdbfilter`, + 1 AS `busyhost`, + 1 AS `notes`, + 1 AS `ipv4address`, + 1 AS `ipv6address`*/; +SET character_set_client = @saved_cs_client; + +-- +-- Table structure for table `virtualinterface` +-- + +DROP TABLE IF EXISTS `virtualinterface`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `virtualinterface` ( + `id` int NOT NULL AUTO_INCREMENT, + `custid` int DEFAULT NULL, + `name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `description` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `mtu` int DEFAULT NULL, + `trunk` tinyint(1) DEFAULT NULL, + `channelgroup` int DEFAULT NULL, + `lag_framing` tinyint(1) NOT NULL DEFAULT '0', + `fastlacp` tinyint(1) NOT NULL DEFAULT '0', + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `IDX_11D9014FDA0209B9` (`custid`), + CONSTRAINT `FK_11D9014FDA0209B9` FOREIGN KEY (`custid`) REFERENCES `cust` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `virtualinterface` +-- + +LOCK TABLES `virtualinterface` WRITE; +/*!40000 ALTER TABLE `virtualinterface` DISABLE KEYS */; +INSERT INTO `virtualinterface` VALUES (1,2,'Port-Channel','',NULL,0,1,1,1,NULL,NULL),(2,2,'Port-Channel','',NULL,1,2,1,0,NULL,NULL),(3,3,'','',NULL,0,NULL,0,0,NULL,NULL),(4,4,'','',NULL,0,NULL,0,0,NULL,NULL),(5,4,'','',NULL,0,NULL,0,0,NULL,NULL),(6,5,'','',NULL,0,NULL,0,0,NULL,NULL),(7,5,'','',NULL,0,NULL,0,0,NULL,NULL),(8,1,NULL,NULL,9000,1,NULL,0,0,NULL,NULL),(9,1,NULL,NULL,9000,1,NULL,0,0,NULL,NULL),(10,5,'',NULL,NULL,0,NULL,0,0,NULL,NULL); +/*!40000 ALTER TABLE `virtualinterface` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `vlan` +-- + +DROP TABLE IF EXISTS `vlan`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `vlan` ( + `id` int NOT NULL AUTO_INCREMENT, + `infrastructureid` int NOT NULL, + `name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `number` int DEFAULT NULL, + `private` tinyint(1) NOT NULL DEFAULT '0', + `notes` longtext CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci, + `peering_matrix` tinyint(1) NOT NULL DEFAULT '0', + `peering_manager` tinyint(1) NOT NULL DEFAULT '0', + `export_to_ixf` tinyint NOT NULL DEFAULT '1', + `config_name` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `infra_config_name` (`infrastructureid`,`config_name`), + KEY `IDX_F83104A1721EBF79` (`infrastructureid`), + CONSTRAINT `FK_F83104A1721EBF79` FOREIGN KEY (`infrastructureid`) REFERENCES `infrastructure` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `vlan` +-- + +LOCK TABLES `vlan` WRITE; +/*!40000 ALTER TABLE `vlan` DISABLE KEYS */; +INSERT INTO `vlan` VALUES (1,1,'Peering LAN 1',1,0,'',1,1,1,NULL,NULL,NULL),(2,2,'Peering LAN 2',2,0,'',1,1,1,NULL,NULL,NULL),(3,1,'Quarantine LAN 1',3,0,'',1,1,0,NULL,NULL,NULL); +/*!40000 ALTER TABLE `vlan` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `vlaninterface` +-- + +DROP TABLE IF EXISTS `vlaninterface`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `vlaninterface` ( + `id` int NOT NULL AUTO_INCREMENT, + `ipv4addressid` int DEFAULT NULL, + `ipv6addressid` int DEFAULT NULL, + `virtualinterfaceid` int DEFAULT NULL, + `vlanid` int DEFAULT NULL, + `ipv4enabled` tinyint(1) DEFAULT '0', + `ipv4hostname` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `ipv6enabled` tinyint(1) DEFAULT '0', + `ipv6hostname` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `mcastenabled` tinyint(1) DEFAULT '0', + `irrdbfilter` tinyint(1) DEFAULT '1', + `bgpmd5secret` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `ipv4bgpmd5secret` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `ipv6bgpmd5secret` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `maxbgpprefix` int DEFAULT NULL, + `rsclient` tinyint(1) DEFAULT NULL, + `ipv4canping` tinyint(1) DEFAULT NULL, + `ipv6canping` tinyint(1) DEFAULT NULL, + `ipv4monitorrcbgp` tinyint(1) DEFAULT NULL, + `ipv6monitorrcbgp` tinyint(1) DEFAULT NULL, + `as112client` tinyint(1) DEFAULT NULL, + `busyhost` tinyint(1) DEFAULT NULL, + `notes` longtext CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci, + `rsmorespecifics` tinyint(1) NOT NULL DEFAULT '0', + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `UNIQ_B4B4411A73720641` (`ipv4addressid`), + UNIQUE KEY `UNIQ_B4B4411A7787D67C` (`ipv6addressid`), + KEY `IDX_B4B4411ABFDF15D5` (`virtualinterfaceid`), + KEY `IDX_B4B4411AF48D6D0` (`vlanid`), + CONSTRAINT `FK_B4B4411A73720641` FOREIGN KEY (`ipv4addressid`) REFERENCES `ipv4address` (`id`), + CONSTRAINT `FK_B4B4411A7787D67C` FOREIGN KEY (`ipv6addressid`) REFERENCES `ipv6address` (`id`), + CONSTRAINT `FK_B4B4411ABFDF15D5` FOREIGN KEY (`virtualinterfaceid`) REFERENCES `virtualinterface` (`id`) ON DELETE CASCADE, + CONSTRAINT `FK_B4B4411AF48D6D0` FOREIGN KEY (`vlanid`) REFERENCES `vlan` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `vlaninterface` +-- + +LOCK TABLES `vlaninterface` WRITE; +/*!40000 ALTER TABLE `vlaninterface` DISABLE KEYS */; +INSERT INTO `vlaninterface` VALUES (1,10,16,1,1,1,'a.heanet.ie',1,'a.heanet.ie',0,1,NULL,'N7rX2SdfbRsyBLTm','N7rX2SdfbRsyBLTm',1000,1,1,1,1,1,1,0,NULL,0,NULL,NULL),(2,137,417,2,2,1,'b.heanet.ie',1,'b.heanet.ie',0,1,NULL,'u5zSNJLAVT87RGXQ','u5zSNJLAVT87RGXQ',1000,1,1,1,1,1,0,0,NULL,0,NULL,NULL),(3,36,NULL,3,1,1,'a.pch.ie',0,'',0,1,NULL,'mcWsqMdzGwTKt67g','mcWsqMdzGwTKt67g',2000,1,1,0,1,0,1,0,NULL,0,NULL,NULL),(4,6,NULL,4,1,1,'a.as112.net',0,'',0,1,NULL,'w83fmGpRDtaKomQo','w83fmGpRDtaKomQo',20,1,1,0,1,0,0,0,NULL,0,NULL,NULL),(5,132,NULL,5,2,1,'b.as112.net',0,'',0,1,NULL,'Pz8VYMNwEdCjKz68','Pz8VYMNwEdCjKz68',20,1,1,0,1,0,0,0,NULL,0,NULL,NULL),(6,NULL,8,6,1,0,'',1,'a.imagine.ie',0,1,NULL,'X8Ks9QnbER9cyzU3','X8Ks9QnbER9cyzU3',1000,1,0,1,0,1,0,0,NULL,1,NULL,NULL),(7,172,470,7,2,1,'b.imagine.ie',1,'b.imagine.ie',0,1,NULL,'LyJND4eoKuQz5j49','LyJND4eoKuQz5j49',1000,0,1,1,1,1,0,0,'',1,NULL,NULL),(8,142,422,10,2,1,'v4.example.com',1,'v6.example.com',0,1,NULL,'soopersecret','soopersecret',100,1,1,1,1,1,1,0,NULL,0,NULL,NULL); +/*!40000 ALTER TABLE `vlaninterface` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Final view structure for view `view_cust_current_active` +-- + +/*!50001 DROP VIEW IF EXISTS `view_cust_current_active`*/; +/*!50001 SET @saved_cs_client = @@character_set_client */; +/*!50001 SET @saved_cs_results = @@character_set_results */; +/*!50001 SET @saved_col_connection = @@collation_connection */; +/*!50001 SET character_set_client = utf8mb4 */; +/*!50001 SET character_set_results = utf8mb4 */; +/*!50001 SET collation_connection = utf8mb4_unicode_ci */; +/*!50001 CREATE ALGORITHM=UNDEFINED */ +/*!50013 DEFINER=`root`@`localhost` SQL SECURITY DEFINER */ +/*!50001 VIEW `view_cust_current_active` AS select `cu`.`id` AS `id`,`cu`.`irrdb` AS `irrdb`,`cu`.`company_registered_detail_id` AS `company_registered_detail_id`,`cu`.`company_billing_details_id` AS `company_billing_details_id`,`cu`.`reseller` AS `reseller`,`cu`.`name` AS `name`,`cu`.`type` AS `type`,`cu`.`shortname` AS `shortname`,`cu`.`abbreviatedName` AS `abbreviatedName`,`cu`.`autsys` AS `autsys`,`cu`.`maxprefixes` AS `maxprefixes`,`cu`.`peeringemail` AS `peeringemail`,`cu`.`nocphone` AS `nocphone`,`cu`.`noc24hphone` AS `noc24hphone`,`cu`.`nocfax` AS `nocfax`,`cu`.`nocemail` AS `nocemail`,`cu`.`nochours` AS `nochours`,`cu`.`nocwww` AS `nocwww`,`cu`.`peeringmacro` AS `peeringmacro`,`cu`.`peeringmacrov6` AS `peeringmacrov6`,`cu`.`peeringpolicy` AS `peeringpolicy`,`cu`.`corpwww` AS `corpwww`,`cu`.`datejoin` AS `datejoin`,`cu`.`dateleave` AS `dateleave`,`cu`.`status` AS `status`,`cu`.`activepeeringmatrix` AS `activepeeringmatrix`,`cu`.`lastupdatedby` AS `lastupdatedby`,`cu`.`creator` AS `creator`,`cu`.`MD5Support` AS `MD5Support`,`cu`.`isReseller` AS `isReseller`,`cu`.`in_manrs` AS `in_manrs`,`cu`.`in_peeringdb` AS `in_peeringdb`,`cu`.`peeringdb_oauth` AS `peeringdb_oauth`,`cu`.`created_at` AS `created_at`,`cu`.`updated_at` AS `updated_at` from `cust` `cu` where ((`cu`.`datejoin` <= curdate()) and ((`cu`.`dateleave` is null) or (`cu`.`dateleave` < '1970-01-01') or (`cu`.`dateleave` >= curdate())) and ((`cu`.`status` = 1) or (`cu`.`status` = 2))) */; +/*!50001 SET character_set_client = @saved_cs_client */; +/*!50001 SET character_set_results = @saved_cs_results */; +/*!50001 SET collation_connection = @saved_col_connection */; + +-- +-- Final view structure for view `view_switch_details_by_custid` +-- + +/*!50001 DROP VIEW IF EXISTS `view_switch_details_by_custid`*/; +/*!50001 SET @saved_cs_client = @@character_set_client */; +/*!50001 SET @saved_cs_results = @@character_set_results */; +/*!50001 SET @saved_col_connection = @@collation_connection */; +/*!50001 SET character_set_client = utf8mb4 */; +/*!50001 SET character_set_results = utf8mb4 */; +/*!50001 SET collation_connection = utf8mb4_unicode_ci */; +/*!50001 CREATE ALGORITHM=UNDEFINED */ +/*!50013 DEFINER=`root`@`localhost` SQL SECURITY DEFINER */ +/*!50001 VIEW `view_switch_details_by_custid` AS select `vi`.`id` AS `id`,`vi`.`custid` AS `custid`,concat(`vi`.`name`,`vi`.`channelgroup`) AS `virtualinterfacename`,`pi`.`virtualinterfaceid` AS `virtualinterfaceid`,`pi`.`status` AS `status`,`pi`.`speed` AS `speed`,`pi`.`duplex` AS `duplex`,`pi`.`notes` AS `notes`,`sp`.`name` AS `switchport`,`sp`.`id` AS `switchportid`,`sp`.`ifName` AS `spifname`,`sw`.`name` AS `switch`,`sw`.`hostname` AS `switchhostname`,`sw`.`id` AS `switchid`,`sw`.`vendorid` AS `vendorid`,`sw`.`snmppasswd` AS `snmppasswd`,`sw`.`infrastructure` AS `infrastructure`,`ca`.`name` AS `cabinet`,`ca`.`colocation` AS `colocabinet`,`lo`.`name` AS `locationname`,`lo`.`shortname` AS `locationshortname` from (((((`virtualinterface` `vi` join `physicalinterface` `pi`) join `switchport` `sp`) join `switch` `sw`) join `cabinet` `ca`) join `location` `lo`) where ((`pi`.`virtualinterfaceid` = `vi`.`id`) and (`pi`.`switchportid` = `sp`.`id`) and (`sp`.`switchid` = `sw`.`id`) and (`sw`.`cabinetid` = `ca`.`id`) and (`ca`.`locationid` = `lo`.`id`)) */; +/*!50001 SET character_set_client = @saved_cs_client */; +/*!50001 SET character_set_results = @saved_cs_results */; +/*!50001 SET collation_connection = @saved_col_connection */; + +-- +-- Final view structure for view `view_vlaninterface_details_by_custid` +-- + +/*!50001 DROP VIEW IF EXISTS `view_vlaninterface_details_by_custid`*/; +/*!50001 SET @saved_cs_client = @@character_set_client */; +/*!50001 SET @saved_cs_results = @@character_set_results */; +/*!50001 SET @saved_col_connection = @@collation_connection */; +/*!50001 SET character_set_client = utf8mb4 */; +/*!50001 SET character_set_results = utf8mb4 */; +/*!50001 SET collation_connection = utf8mb4_unicode_ci */; +/*!50001 CREATE ALGORITHM=UNDEFINED */ +/*!50013 DEFINER=`root`@`localhost` SQL SECURITY DEFINER */ +/*!50001 VIEW `view_vlaninterface_details_by_custid` AS select `pi`.`id` AS `id`,`vi`.`custid` AS `custid`,`pi`.`virtualinterfaceid` AS `virtualinterfaceid`,`pi`.`status` AS `status`,concat(`vi`.`name`,`vi`.`channelgroup`) AS `virtualinterfacename`,`vlan`.`number` AS `vlan`,`vlan`.`name` AS `vlanname`,`vlan`.`id` AS `vlanid`,`vli`.`id` AS `vlaninterfaceid`,`vli`.`ipv4enabled` AS `ipv4enabled`,`vli`.`ipv4hostname` AS `ipv4hostname`,`vli`.`ipv4canping` AS `ipv4canping`,`vli`.`ipv4monitorrcbgp` AS `ipv4monitorrcbgp`,`vli`.`ipv6enabled` AS `ipv6enabled`,`vli`.`ipv6hostname` AS `ipv6hostname`,`vli`.`ipv6canping` AS `ipv6canping`,`vli`.`ipv6monitorrcbgp` AS `ipv6monitorrcbgp`,`vli`.`as112client` AS `as112client`,`vli`.`mcastenabled` AS `mcastenabled`,`vli`.`ipv4bgpmd5secret` AS `ipv4bgpmd5secret`,`vli`.`ipv6bgpmd5secret` AS `ipv6bgpmd5secret`,`vli`.`rsclient` AS `rsclient`,`vli`.`irrdbfilter` AS `irrdbfilter`,`vli`.`busyhost` AS `busyhost`,`vli`.`notes` AS `notes`,`v4`.`address` AS `ipv4address`,`v6`.`address` AS `ipv6address` from ((`physicalinterface` `pi` join `virtualinterface` `vi`) join (((`vlaninterface` `vli` left join `ipv4address` `v4` on((`vli`.`ipv4addressid` = `v4`.`id`))) left join `ipv6address` `v6` on((`vli`.`ipv6addressid` = `v6`.`id`))) left join `vlan` on((`vli`.`vlanid` = `vlan`.`id`)))) where ((`pi`.`virtualinterfaceid` = `vi`.`id`) and (`vli`.`virtualinterfaceid` = `vi`.`id`)) */; +/*!50001 SET character_set_client = @saved_cs_client */; +/*!50001 SET character_set_results = @saved_cs_results */; +/*!50001 SET collation_connection = @saved_col_connection */; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2025-08-11 15:10:32 diff --git a/data/ci/known-good/README b/data/ci/known-good/README new file mode 100644 index 000000000..af470b276 --- /dev/null +++ b/data/ci/known-good/README @@ -0,0 +1,6 @@ + +This directory contains "known good" generated configurations from the +Travis database which Travis can test against to ensure no bugs / +regressions have been introduced into the code. + +See the regenerate.sh script to build new versions of these. diff --git a/data/ci/known-good/api-v4-member-export-0.6-auth.json b/data/ci/known-good/api-v4-member-export-0.6-auth.json new file mode 100644 index 000000000..3a4ed4165 --- /dev/null +++ b/data/ci/known-good/api-v4-member-export-0.6-auth.json @@ -0,0 +1,395 @@ +{ + "version": "0.6", + "generator": "IXP Manager v999.9.9", + "timestamp": "2025-08-18T18:27:39Z", + "ixp_list": [ + { + "shortname": "Infrastructure #1", + "name": "IXP", + "country": "IE", + "url": "https:\/\/www.example.com\/", + "peeringdb_id": 48, + "ixf_id": 20, + "ixp_id": 1, + "support_email": "ixp@example.com", + "support_phone": "+353 20 912 2000", + "support_contact_hours": "24x7", + "emergency_email": "ixp@example.com", + "emergency_phone": "+353 20 912 2000", + "emergency_contact_hours": "24x7", + "billing_contact_hours": "8x5", + "billing_email": "ixp@example.com", + "billing_phone": "+353 20 912 2000", + "peering_policy_list": [ + "open", + "selective", + "mandatory", + "closed" + ], + "vlan": [ + { + "id": 1, + "name": "Peering LAN 1", + "ipv4": { + "prefix": "192.0.2.0", + "mask_length": 25 + }, + "ipv6": { + "prefix": "2001:db8::", + "mask_length": 64 + } + } + ], + "switch": [ + { + "id": 1, + "name": "switch1", + "colo": "Location 1", + "city": "Cork", + "country": "IE" + } + ] + }, + { + "shortname": "Infrastructure #2", + "name": "IXP", + "country": "IE", + "url": "https:\/\/www.example.com\/", + "peeringdb_id": 387, + "ixf_id": 645, + "ixp_id": 2, + "support_email": "ixp@example.com", + "support_phone": "+353 20 912 2000", + "support_contact_hours": "24x7", + "emergency_email": "ixp@example.com", + "emergency_phone": "+353 20 912 2000", + "emergency_contact_hours": "24x7", + "billing_contact_hours": "8x5", + "billing_email": "ixp@example.com", + "billing_phone": "+353 20 912 2000", + "peering_policy_list": [ + "open", + "selective", + "mandatory", + "closed" + ], + "vlan": [ + { + "id": 2, + "name": "Peering LAN 2", + "ipv4": { + "prefix": "192.0.2.128", + "mask_length": 25 + }, + "ipv6": { + "prefix": "2001:db8:0:2::", + "mask_length": 64 + } + } + ], + "switch": [ + { + "id": 2, + "name": "switch2", + "colo": "Location 1", + "city": "Cork", + "country": "IE" + } + ] + } + ], + "member_list": [ + { + "asnum": 42, + "member_since": "2014-01-06T00:00:00Z", + "url": "http:\/\/www.example.com\/", + "name": "PCH DNS", + "peering_policy": "open", + "member_type": "peering", + "contact_email": [ + "peering@example.com" + ], + "contact_phone": [ + "" + ], + "ixp_manager": { + "tags": [], + "in_manrs": false, + "is_reseller": false, + "is_resold": false + }, + "connection_list": [ + { + "ixp_id": 1, + "state": "active", + "if_list": [ + { + "switch_id": 1, + "if_speed": 100 + } + ], + "vlan_list": [ + { + "vlan_id": 1, + "ipv4": { + "address": "10.1.0.36", + "as_macro": "AS-PCH", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 2000 + } + } + ] + } + ] + }, + { + "asnum": 112, + "member_since": "2014-01-06T00:00:00Z", + "url": "http:\/\/www.example.com\/", + "name": "AS112", + "peering_policy": "open", + "member_type": "peering", + "contact_email": [ + "peering@example.com" + ], + "contact_phone": [ + "" + ], + "ixp_manager": { + "tags": { + "test-tag1": "Test Tag1" + }, + "in_manrs": false, + "is_reseller": false, + "is_resold": false + }, + "connection_list": [ + { + "ixp_id": 1, + "state": "active", + "if_list": [ + { + "switch_id": 1, + "if_speed": 10 + } + ], + "vlan_list": [ + { + "vlan_id": 1, + "ipv4": { + "address": "10.1.0.6", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 20 + } + } + ] + }, + { + "ixp_id": 2, + "state": "active", + "if_list": [ + { + "switch_id": 2, + "if_speed": 10 + } + ], + "vlan_list": [ + { + "vlan_id": 2, + "ipv4": { + "address": "10.2.0.6", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 20 + } + } + ] + } + ] + }, + { + "asnum": 1213, + "member_since": "2014-01-06T00:00:00Z", + "url": "http:\/\/www.example.com\/", + "name": "HEAnet", + "peering_policy": "open", + "member_type": "peering", + "contact_email": [ + "peering@example.com" + ], + "contact_phone": [ + "" + ], + "ixp_manager": { + "tags": [], + "in_manrs": false, + "is_reseller": false, + "is_resold": false + }, + "connection_list": [ + { + "ixp_id": 1, + "state": "active", + "if_list": [ + { + "switch_id": 1, + "if_speed": 1000 + }, + { + "switch_id": 1, + "if_speed": 1000 + } + ], + "vlan_list": [ + { + "vlan_id": 1, + "ipv4": { + "address": "10.1.0.10", + "as_macro": "AS-HEANET", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 1000 + }, + "ipv6": { + "address": "2001:db8:1::10", + "as_macro": "AS-HEANET", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 1000 + } + } + ] + }, + { + "ixp_id": 2, + "state": "active", + "if_list": [ + { + "switch_id": 2, + "if_speed": 1000 + } + ], + "vlan_list": [ + { + "vlan_id": 2, + "ipv4": { + "address": "10.2.0.11", + "as_macro": "AS-HEANET", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 1000 + }, + "ipv6": { + "address": "2001:db8:2::11", + "as_macro": "AS-HEANET", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 1000 + } + } + ] + } + ] + }, + { + "asnum": 2128, + "member_since": "2014-01-06T00:00:00Z", + "url": "http:\/\/www.siep.com\/", + "name": "INEX", + "peering_policy": "mandatory", + "member_type": "ixp", + "contact_email": [ + "peering@siep.net" + ], + "contact_phone": [ + "+353 1 123 4567" + ], + "peering_policy_url": "http:\/\/www.siep.com\/noc\/", + "contact_hours": "24x7", + "ixp_manager": { + "tags": [], + "in_manrs": false, + "is_reseller": false, + "is_resold": false + }, + "connection_list": [] + }, + { + "asnum": 25441, + "member_since": "2014-01-06T00:00:00Z", + "url": "http:\/\/www.example.com\/", + "name": "Imagine", + "peering_policy": "open", + "member_type": "peering", + "contact_email": [ + "peering@example.com" + ], + "contact_phone": [ + "" + ], + "ixp_manager": { + "tags": { + "test-tag1": "Test Tag1" + }, + "in_manrs": false, + "is_reseller": false, + "is_resold": false + }, + "connection_list": [ + { + "ixp_id": 1, + "state": "active", + "if_list": [ + { + "switch_id": 1, + "if_speed": 1000 + } + ], + "vlan_list": [ + { + "vlan_id": 1, + "ipv6": { + "address": "2001:db8:1::8", + "as_macro": "AS-IBIS", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 1000 + } + } + ] + }, + { + "ixp_id": 2, + "state": "active", + "if_list": [ + { + "switch_id": 2, + "if_speed": 10000 + } + ], + "vlan_list": [ + { + "vlan_id": 2, + "ipv4": { + "address": "10.2.0.46", + "as_macro": "AS-IBIS", + "routeserver": false, + "mac_addresses": [], + "max_prefix": 1000 + }, + "ipv6": { + "address": "2001:db8:2::46", + "as_macro": "AS-IBIS", + "routeserver": false, + "mac_addresses": [], + "max_prefix": 1000 + } + } + ] + } + ] + } + ] +} diff --git a/data/ci/known-good/api-v4-member-export-0.6-unauth.json b/data/ci/known-good/api-v4-member-export-0.6-unauth.json new file mode 100644 index 000000000..86be6c9c7 --- /dev/null +++ b/data/ci/known-good/api-v4-member-export-0.6-unauth.json @@ -0,0 +1,363 @@ +{ + "version": "0.6", + "generator": "IXP Manager v999.9.9", + "timestamp": "2025-08-18T18:27:03Z", + "ixp_list": [ + { + "shortname": "Infrastructure #1", + "name": "IXP", + "country": "IE", + "url": "https:\/\/www.example.com\/", + "peeringdb_id": 48, + "ixf_id": 20, + "ixp_id": 1, + "support_email": "ixp@example.com", + "support_phone": "+353 20 912 2000", + "support_contact_hours": "24x7", + "emergency_email": "ixp@example.com", + "emergency_phone": "+353 20 912 2000", + "emergency_contact_hours": "24x7", + "billing_contact_hours": "8x5", + "billing_email": "ixp@example.com", + "billing_phone": "+353 20 912 2000", + "peering_policy_list": [ + "open", + "selective", + "mandatory", + "closed" + ], + "vlan": [ + { + "id": 1, + "name": "Peering LAN 1", + "ipv4": { + "prefix": "192.0.2.0", + "mask_length": 25 + }, + "ipv6": { + "prefix": "2001:db8::", + "mask_length": 64 + } + } + ], + "switch": [ + { + "id": 1, + "name": "switch1", + "colo": "Location 1", + "city": "Cork", + "country": "IE" + } + ] + }, + { + "shortname": "Infrastructure #2", + "name": "IXP", + "country": "IE", + "url": "https:\/\/www.example.com\/", + "peeringdb_id": 387, + "ixf_id": 645, + "ixp_id": 2, + "support_email": "ixp@example.com", + "support_phone": "+353 20 912 2000", + "support_contact_hours": "24x7", + "emergency_email": "ixp@example.com", + "emergency_phone": "+353 20 912 2000", + "emergency_contact_hours": "24x7", + "billing_contact_hours": "8x5", + "billing_email": "ixp@example.com", + "billing_phone": "+353 20 912 2000", + "peering_policy_list": [ + "open", + "selective", + "mandatory", + "closed" + ], + "vlan": [ + { + "id": 2, + "name": "Peering LAN 2", + "ipv4": { + "prefix": "192.0.2.128", + "mask_length": 25 + }, + "ipv6": { + "prefix": "2001:db8:0:2::", + "mask_length": 64 + } + } + ], + "switch": [ + { + "id": 2, + "name": "switch2", + "colo": "Location 1", + "city": "Cork", + "country": "IE" + } + ] + } + ], + "member_list": [ + { + "asnum": 42, + "member_since": "2014-01-06T00:00:00Z", + "url": "http:\/\/www.example.com\/", + "name": "PCH DNS", + "peering_policy": "open", + "member_type": "peering", + "ixp_manager": { + "tags": [], + "in_manrs": false, + "is_reseller": false, + "is_resold": false + }, + "connection_list": [ + { + "ixp_id": 1, + "state": "active", + "if_list": [ + { + "switch_id": 1, + "if_speed": 100 + } + ], + "vlan_list": [ + { + "vlan_id": 1, + "ipv4": { + "address": "10.1.0.36", + "as_macro": "AS-PCH", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 2000 + } + } + ] + } + ] + }, + { + "asnum": 112, + "member_since": "2014-01-06T00:00:00Z", + "url": "http:\/\/www.example.com\/", + "name": "AS112", + "peering_policy": "open", + "member_type": "peering", + "ixp_manager": { + "tags": { + "test-tag1": "Test Tag1" + }, + "in_manrs": false, + "is_reseller": false, + "is_resold": false + }, + "connection_list": [ + { + "ixp_id": 1, + "state": "active", + "if_list": [ + { + "switch_id": 1, + "if_speed": 10 + } + ], + "vlan_list": [ + { + "vlan_id": 1, + "ipv4": { + "address": "10.1.0.6", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 20 + } + } + ] + }, + { + "ixp_id": 2, + "state": "active", + "if_list": [ + { + "switch_id": 2, + "if_speed": 10 + } + ], + "vlan_list": [ + { + "vlan_id": 2, + "ipv4": { + "address": "10.2.0.6", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 20 + } + } + ] + } + ] + }, + { + "asnum": 1213, + "member_since": "2014-01-06T00:00:00Z", + "url": "http:\/\/www.example.com\/", + "name": "HEAnet", + "peering_policy": "open", + "member_type": "peering", + "ixp_manager": { + "tags": [], + "in_manrs": false, + "is_reseller": false, + "is_resold": false + }, + "connection_list": [ + { + "ixp_id": 1, + "state": "active", + "if_list": [ + { + "switch_id": 1, + "if_speed": 1000 + }, + { + "switch_id": 1, + "if_speed": 1000 + } + ], + "vlan_list": [ + { + "vlan_id": 1, + "ipv4": { + "address": "10.1.0.10", + "as_macro": "AS-HEANET", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 1000 + }, + "ipv6": { + "address": "2001:db8:1::10", + "as_macro": "AS-HEANET", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 1000 + } + } + ] + }, + { + "ixp_id": 2, + "state": "active", + "if_list": [ + { + "switch_id": 2, + "if_speed": 1000 + } + ], + "vlan_list": [ + { + "vlan_id": 2, + "ipv4": { + "address": "10.2.0.11", + "as_macro": "AS-HEANET", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 1000 + }, + "ipv6": { + "address": "2001:db8:2::11", + "as_macro": "AS-HEANET", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 1000 + } + } + ] + } + ] + }, + { + "asnum": 2128, + "member_since": "2014-01-06T00:00:00Z", + "url": "http:\/\/www.siep.com\/", + "name": "INEX", + "peering_policy": "mandatory", + "member_type": "ixp", + "ixp_manager": { + "tags": [], + "in_manrs": false, + "is_reseller": false, + "is_resold": false + }, + "connection_list": [] + }, + { + "asnum": 25441, + "member_since": "2014-01-06T00:00:00Z", + "url": "http:\/\/www.example.com\/", + "name": "Imagine", + "peering_policy": "open", + "member_type": "peering", + "ixp_manager": { + "tags": { + "test-tag1": "Test Tag1" + }, + "in_manrs": false, + "is_reseller": false, + "is_resold": false + }, + "connection_list": [ + { + "ixp_id": 1, + "state": "active", + "if_list": [ + { + "switch_id": 1, + "if_speed": 1000 + } + ], + "vlan_list": [ + { + "vlan_id": 1, + "ipv6": { + "address": "2001:db8:1::8", + "as_macro": "AS-IBIS", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 1000 + } + } + ] + }, + { + "ixp_id": 2, + "state": "active", + "if_list": [ + { + "switch_id": 2, + "if_speed": 10000 + } + ], + "vlan_list": [ + { + "vlan_id": 2, + "ipv4": { + "address": "10.2.0.46", + "as_macro": "AS-IBIS", + "routeserver": false, + "mac_addresses": [], + "max_prefix": 1000 + }, + "ipv6": { + "address": "2001:db8:2::46", + "as_macro": "AS-IBIS", + "routeserver": false, + "mac_addresses": [], + "max_prefix": 1000 + } + } + ] + } + ] + } + ] +} diff --git a/data/ci/known-good/api-v4-member-export-0.7-auth.json b/data/ci/known-good/api-v4-member-export-0.7-auth.json new file mode 100644 index 000000000..776bcb52e --- /dev/null +++ b/data/ci/known-good/api-v4-member-export-0.7-auth.json @@ -0,0 +1,399 @@ +{ + "version": "0.7", + "generator": "IXP Manager v999.9.9", + "timestamp": "2025-08-18T18:24:08Z", + "ixp_list": [ + { + "shortname": "Infrastructure #1", + "name": "IXP", + "country": "IE", + "url": "https:\/\/www.example.com\/", + "peeringdb_id": 48, + "ixf_id": 20, + "ixp_id": 1, + "support_email": "ixp@example.com", + "support_phone": "+353 20 912 2000", + "support_contact_hours": "24x7", + "emergency_email": "ixp@example.com", + "emergency_phone": "+353 20 912 2000", + "emergency_contact_hours": "24x7", + "billing_contact_hours": "8x5", + "billing_email": "ixp@example.com", + "billing_phone": "+353 20 912 2000", + "peering_policy_list": [ + "open", + "selective", + "mandatory", + "closed" + ], + "vlan": [ + { + "id": 1, + "name": "Peering LAN 1", + "ipv4": { + "prefix": "192.0.2.0", + "mask_length": 25 + }, + "ipv6": { + "prefix": "2001:db8::", + "mask_length": 64 + } + } + ], + "switch": [ + { + "id": 1, + "name": "switch1", + "colo": "Location 1", + "city": "Cork", + "country": "IE", + "manufacturer": "Brocade", + "model": "FESX624" + } + ] + }, + { + "shortname": "Infrastructure #2", + "name": "IXP", + "country": "IE", + "url": "https:\/\/www.example.com\/", + "peeringdb_id": 387, + "ixf_id": 645, + "ixp_id": 2, + "support_email": "ixp@example.com", + "support_phone": "+353 20 912 2000", + "support_contact_hours": "24x7", + "emergency_email": "ixp@example.com", + "emergency_phone": "+353 20 912 2000", + "emergency_contact_hours": "24x7", + "billing_contact_hours": "8x5", + "billing_email": "ixp@example.com", + "billing_phone": "+353 20 912 2000", + "peering_policy_list": [ + "open", + "selective", + "mandatory", + "closed" + ], + "vlan": [ + { + "id": 2, + "name": "Peering LAN 2", + "ipv4": { + "prefix": "192.0.2.128", + "mask_length": 25 + }, + "ipv6": { + "prefix": "2001:db8:0:2::", + "mask_length": 64 + } + } + ], + "switch": [ + { + "id": 2, + "name": "switch2", + "colo": "Location 1", + "city": "Cork", + "country": "IE", + "manufacturer": "Brocade", + "model": "FESX624" + } + ] + } + ], + "member_list": [ + { + "asnum": 42, + "member_since": "2014-01-06T00:00:00Z", + "url": "http:\/\/www.example.com\/", + "name": "PCH DNS", + "peering_policy": "open", + "member_type": "peering", + "contact_email": [ + "peering@example.com" + ], + "contact_phone": [ + "" + ], + "ixp_manager": { + "tags": [], + "in_manrs": false, + "is_reseller": false, + "is_resold": false + }, + "connection_list": [ + { + "ixp_id": 1, + "state": "active", + "if_list": [ + { + "switch_id": 1, + "if_speed": 100 + } + ], + "vlan_list": [ + { + "vlan_id": 1, + "ipv4": { + "address": "10.1.0.36", + "as_macro": "AS-PCH", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 2000 + } + } + ] + } + ] + }, + { + "asnum": 112, + "member_since": "2014-01-06T00:00:00Z", + "url": "http:\/\/www.example.com\/", + "name": "AS112", + "peering_policy": "open", + "member_type": "peering", + "contact_email": [ + "peering@example.com" + ], + "contact_phone": [ + "" + ], + "ixp_manager": { + "tags": { + "test-tag1": "Test Tag1" + }, + "in_manrs": false, + "is_reseller": false, + "is_resold": false + }, + "connection_list": [ + { + "ixp_id": 1, + "state": "active", + "if_list": [ + { + "switch_id": 1, + "if_speed": 10 + } + ], + "vlan_list": [ + { + "vlan_id": 1, + "ipv4": { + "address": "10.1.0.6", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 20 + } + } + ] + }, + { + "ixp_id": 2, + "state": "active", + "if_list": [ + { + "switch_id": 2, + "if_speed": 10 + } + ], + "vlan_list": [ + { + "vlan_id": 2, + "ipv4": { + "address": "10.2.0.6", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 20 + } + } + ] + } + ] + }, + { + "asnum": 1213, + "member_since": "2014-01-06T00:00:00Z", + "url": "http:\/\/www.example.com\/", + "name": "HEAnet", + "peering_policy": "open", + "member_type": "peering", + "contact_email": [ + "peering@example.com" + ], + "contact_phone": [ + "" + ], + "ixp_manager": { + "tags": [], + "in_manrs": false, + "is_reseller": false, + "is_resold": false + }, + "connection_list": [ + { + "ixp_id": 1, + "state": "active", + "if_list": [ + { + "switch_id": 1, + "if_speed": 1000 + }, + { + "switch_id": 1, + "if_speed": 1000 + } + ], + "vlan_list": [ + { + "vlan_id": 1, + "ipv4": { + "address": "10.1.0.10", + "as_macro": "AS-HEANET", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 1000 + }, + "ipv6": { + "address": "2001:db8:1::10", + "as_macro": "AS-HEANET", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 1000 + } + } + ] + }, + { + "ixp_id": 2, + "state": "active", + "if_list": [ + { + "switch_id": 2, + "if_speed": 1000 + } + ], + "vlan_list": [ + { + "vlan_id": 2, + "ipv4": { + "address": "10.2.0.11", + "as_macro": "AS-HEANET", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 1000 + }, + "ipv6": { + "address": "2001:db8:2::11", + "as_macro": "AS-HEANET", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 1000 + } + } + ] + } + ] + }, + { + "asnum": 2128, + "member_since": "2014-01-06T00:00:00Z", + "url": "http:\/\/www.siep.com\/", + "name": "INEX", + "peering_policy": "mandatory", + "member_type": "ixp", + "contact_email": [ + "peering@siep.net" + ], + "contact_phone": [ + "+353 1 123 4567" + ], + "peering_policy_url": "http:\/\/www.siep.com\/noc\/", + "contact_hours": "24x7", + "ixp_manager": { + "tags": [], + "in_manrs": false, + "is_reseller": false, + "is_resold": false + }, + "connection_list": [] + }, + { + "asnum": 25441, + "member_since": "2014-01-06T00:00:00Z", + "url": "http:\/\/www.example.com\/", + "name": "Imagine", + "peering_policy": "open", + "member_type": "peering", + "contact_email": [ + "peering@example.com" + ], + "contact_phone": [ + "" + ], + "ixp_manager": { + "tags": { + "test-tag1": "Test Tag1" + }, + "in_manrs": false, + "is_reseller": false, + "is_resold": false + }, + "connection_list": [ + { + "ixp_id": 1, + "state": "active", + "if_list": [ + { + "switch_id": 1, + "if_speed": 1000 + } + ], + "vlan_list": [ + { + "vlan_id": 1, + "ipv6": { + "address": "2001:db8:1::8", + "as_macro": "AS-IBIS", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 1000 + } + } + ] + }, + { + "ixp_id": 2, + "state": "active", + "if_list": [ + { + "switch_id": 2, + "if_speed": 10000 + } + ], + "vlan_list": [ + { + "vlan_id": 2, + "ipv4": { + "address": "10.2.0.46", + "as_macro": "AS-IBIS", + "routeserver": false, + "mac_addresses": [], + "max_prefix": 1000 + }, + "ipv6": { + "address": "2001:db8:2::46", + "as_macro": "AS-IBIS", + "routeserver": false, + "mac_addresses": [], + "max_prefix": 1000 + } + } + ] + } + ] + } + ] +} diff --git a/data/ci/known-good/api-v4-member-export-0.7-unauth.json b/data/ci/known-good/api-v4-member-export-0.7-unauth.json new file mode 100644 index 000000000..1be1e140c --- /dev/null +++ b/data/ci/known-good/api-v4-member-export-0.7-unauth.json @@ -0,0 +1,367 @@ +{ + "version": "0.7", + "generator": "IXP Manager v999.9.9", + "timestamp": "2025-08-18T18:22:14Z", + "ixp_list": [ + { + "shortname": "Infrastructure #1", + "name": "IXP", + "country": "IE", + "url": "https:\/\/www.example.com\/", + "peeringdb_id": 48, + "ixf_id": 20, + "ixp_id": 1, + "support_email": "ixp@example.com", + "support_phone": "+353 20 912 2000", + "support_contact_hours": "24x7", + "emergency_email": "ixp@example.com", + "emergency_phone": "+353 20 912 2000", + "emergency_contact_hours": "24x7", + "billing_contact_hours": "8x5", + "billing_email": "ixp@example.com", + "billing_phone": "+353 20 912 2000", + "peering_policy_list": [ + "open", + "selective", + "mandatory", + "closed" + ], + "vlan": [ + { + "id": 1, + "name": "Peering LAN 1", + "ipv4": { + "prefix": "192.0.2.0", + "mask_length": 25 + }, + "ipv6": { + "prefix": "2001:db8::", + "mask_length": 64 + } + } + ], + "switch": [ + { + "id": 1, + "name": "switch1", + "colo": "Location 1", + "city": "Cork", + "country": "IE", + "manufacturer": "Brocade", + "model": "FESX624" + } + ] + }, + { + "shortname": "Infrastructure #2", + "name": "IXP", + "country": "IE", + "url": "https:\/\/www.example.com\/", + "peeringdb_id": 387, + "ixf_id": 645, + "ixp_id": 2, + "support_email": "ixp@example.com", + "support_phone": "+353 20 912 2000", + "support_contact_hours": "24x7", + "emergency_email": "ixp@example.com", + "emergency_phone": "+353 20 912 2000", + "emergency_contact_hours": "24x7", + "billing_contact_hours": "8x5", + "billing_email": "ixp@example.com", + "billing_phone": "+353 20 912 2000", + "peering_policy_list": [ + "open", + "selective", + "mandatory", + "closed" + ], + "vlan": [ + { + "id": 2, + "name": "Peering LAN 2", + "ipv4": { + "prefix": "192.0.2.128", + "mask_length": 25 + }, + "ipv6": { + "prefix": "2001:db8:0:2::", + "mask_length": 64 + } + } + ], + "switch": [ + { + "id": 2, + "name": "switch2", + "colo": "Location 1", + "city": "Cork", + "country": "IE", + "manufacturer": "Brocade", + "model": "FESX624" + } + ] + } + ], + "member_list": [ + { + "asnum": 42, + "member_since": "2014-01-06T00:00:00Z", + "url": "http:\/\/www.example.com\/", + "name": "PCH DNS", + "peering_policy": "open", + "member_type": "peering", + "ixp_manager": { + "tags": [], + "in_manrs": false, + "is_reseller": false, + "is_resold": false + }, + "connection_list": [ + { + "ixp_id": 1, + "state": "active", + "if_list": [ + { + "switch_id": 1, + "if_speed": 100 + } + ], + "vlan_list": [ + { + "vlan_id": 1, + "ipv4": { + "address": "10.1.0.36", + "as_macro": "AS-PCH", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 2000 + } + } + ] + } + ] + }, + { + "asnum": 112, + "member_since": "2014-01-06T00:00:00Z", + "url": "http:\/\/www.example.com\/", + "name": "AS112", + "peering_policy": "open", + "member_type": "peering", + "ixp_manager": { + "tags": { + "test-tag1": "Test Tag1" + }, + "in_manrs": false, + "is_reseller": false, + "is_resold": false + }, + "connection_list": [ + { + "ixp_id": 1, + "state": "active", + "if_list": [ + { + "switch_id": 1, + "if_speed": 10 + } + ], + "vlan_list": [ + { + "vlan_id": 1, + "ipv4": { + "address": "10.1.0.6", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 20 + } + } + ] + }, + { + "ixp_id": 2, + "state": "active", + "if_list": [ + { + "switch_id": 2, + "if_speed": 10 + } + ], + "vlan_list": [ + { + "vlan_id": 2, + "ipv4": { + "address": "10.2.0.6", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 20 + } + } + ] + } + ] + }, + { + "asnum": 1213, + "member_since": "2014-01-06T00:00:00Z", + "url": "http:\/\/www.example.com\/", + "name": "HEAnet", + "peering_policy": "open", + "member_type": "peering", + "ixp_manager": { + "tags": [], + "in_manrs": false, + "is_reseller": false, + "is_resold": false + }, + "connection_list": [ + { + "ixp_id": 1, + "state": "active", + "if_list": [ + { + "switch_id": 1, + "if_speed": 1000 + }, + { + "switch_id": 1, + "if_speed": 1000 + } + ], + "vlan_list": [ + { + "vlan_id": 1, + "ipv4": { + "address": "10.1.0.10", + "as_macro": "AS-HEANET", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 1000 + }, + "ipv6": { + "address": "2001:db8:1::10", + "as_macro": "AS-HEANET", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 1000 + } + } + ] + }, + { + "ixp_id": 2, + "state": "active", + "if_list": [ + { + "switch_id": 2, + "if_speed": 1000 + } + ], + "vlan_list": [ + { + "vlan_id": 2, + "ipv4": { + "address": "10.2.0.11", + "as_macro": "AS-HEANET", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 1000 + }, + "ipv6": { + "address": "2001:db8:2::11", + "as_macro": "AS-HEANET", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 1000 + } + } + ] + } + ] + }, + { + "asnum": 2128, + "member_since": "2014-01-06T00:00:00Z", + "url": "http:\/\/www.siep.com\/", + "name": "INEX", + "peering_policy": "mandatory", + "member_type": "ixp", + "ixp_manager": { + "tags": [], + "in_manrs": false, + "is_reseller": false, + "is_resold": false + }, + "connection_list": [] + }, + { + "asnum": 25441, + "member_since": "2014-01-06T00:00:00Z", + "url": "http:\/\/www.example.com\/", + "name": "Imagine", + "peering_policy": "open", + "member_type": "peering", + "ixp_manager": { + "tags": { + "test-tag1": "Test Tag1" + }, + "in_manrs": false, + "is_reseller": false, + "is_resold": false + }, + "connection_list": [ + { + "ixp_id": 1, + "state": "active", + "if_list": [ + { + "switch_id": 1, + "if_speed": 1000 + } + ], + "vlan_list": [ + { + "vlan_id": 1, + "ipv6": { + "address": "2001:db8:1::8", + "as_macro": "AS-IBIS", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 1000 + } + } + ] + }, + { + "ixp_id": 2, + "state": "active", + "if_list": [ + { + "switch_id": 2, + "if_speed": 10000 + } + ], + "vlan_list": [ + { + "vlan_id": 2, + "ipv4": { + "address": "10.2.0.46", + "as_macro": "AS-IBIS", + "routeserver": false, + "mac_addresses": [], + "max_prefix": 1000 + }, + "ipv6": { + "address": "2001:db8:2::46", + "as_macro": "AS-IBIS", + "routeserver": false, + "mac_addresses": [], + "max_prefix": 1000 + } + } + ] + } + ] + } + ] +} diff --git a/data/ci/known-good/api-v4-member-export-1.0-auth.json b/data/ci/known-good/api-v4-member-export-1.0-auth.json new file mode 100644 index 000000000..44af5f36a --- /dev/null +++ b/data/ci/known-good/api-v4-member-export-1.0-auth.json @@ -0,0 +1,401 @@ +{ + "version": "1.0", + "generator": "IXP Manager v999.9.9", + "timestamp": "2020-05-23T08:34:42Z", + "ixp_list": [ + { + "shortname": "Infrastructure #1", + "name": "IXP", + "country": "IE", + "url": "https:\/\/www.example.com\/", + "peeringdb_id": 48, + "ixf_id": 20, + "ixp_id": 1, + "support_email": "ixp@example.com", + "support_phone": "+353 20 912 2000", + "support_contact_hours": "24x7", + "emergency_email": "ixp@example.com", + "emergency_phone": "+353 20 912 2000", + "emergency_contact_hours": "24x7", + "billing_contact_hours": "8x5", + "billing_email": "ixp@example.com", + "billing_phone": "+353 20 912 2000", + "peering_policy_list": [ + "open", + "selective", + "mandatory", + "closed" + ], + "vlan": [ + { + "id": 1, + "name": "Peering LAN 1", + "ipv4": { + "prefix": "192.0.2.0", + "mask_length": 25 + }, + "ipv6": { + "prefix": "2001:db8::", + "mask_length": 64 + } + } + ], + "switch": [ + { + "id": 1, + "name": "switch1", + "colo": "Location 1", + "city": "Cork", + "country": "IE", + "manufacturer": "Brocade", + "model": "FESX624", + "software": "" + } + ] + }, + { + "shortname": "Infrastructure #2", + "name": "IXP", + "country": "IE", + "url": "https:\/\/www.example.com\/", + "peeringdb_id": 387, + "ixf_id": 645, + "ixp_id": 2, + "support_email": "ixp@example.com", + "support_phone": "+353 20 912 2000", + "support_contact_hours": "24x7", + "emergency_email": "ixp@example.com", + "emergency_phone": "+353 20 912 2000", + "emergency_contact_hours": "24x7", + "billing_contact_hours": "8x5", + "billing_email": "ixp@example.com", + "billing_phone": "+353 20 912 2000", + "peering_policy_list": [ + "open", + "selective", + "mandatory", + "closed" + ], + "vlan": [ + { + "id": 2, + "name": "Peering LAN 2", + "ipv4": { + "prefix": "192.0.2.128", + "mask_length": 25 + }, + "ipv6": { + "prefix": "2001:db8:0:2::", + "mask_length": 64 + } + } + ], + "switch": [ + { + "id": 2, + "name": "switch2", + "colo": "Location 1", + "city": "Cork", + "country": "IE", + "manufacturer": "Brocade", + "model": "FESX624", + "software": "" + } + ] + } + ], + "member_list": [ + { + "asnum": 42, + "member_since": "2014-01-06T00:00:00Z", + "url": "http:\/\/www.example.com\/", + "name": "PCH DNS", + "peering_policy": "open", + "member_type": "peering", + "contact_email": [ + "peering@example.com" + ], + "contact_phone": [ + "" + ], + "ixp_manager": { + "tags": [], + "in_manrs": false, + "is_reseller": false, + "is_resold": false + }, + "connection_list": [ + { + "ixp_id": 1, + "state": "active", + "if_list": [ + { + "switch_id": 1, + "if_speed": 100 + } + ], + "vlan_list": [ + { + "vlan_id": 1, + "ipv4": { + "address": "10.1.0.36", + "as_macro": "AS-PCH", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 2000 + } + } + ] + } + ] + }, + { + "asnum": 112, + "member_since": "2014-01-06T00:00:00Z", + "url": "http:\/\/www.example.com\/", + "name": "AS112", + "peering_policy": "open", + "member_type": "peering", + "contact_email": [ + "peering@example.com" + ], + "contact_phone": [ + "" + ], + "ixp_manager": { + "tags": { + "test-tag1": "Test Tag1" + }, + "in_manrs": false, + "is_reseller": false, + "is_resold": false + }, + "connection_list": [ + { + "ixp_id": 1, + "state": "active", + "if_list": [ + { + "switch_id": 1, + "if_speed": 10 + } + ], + "vlan_list": [ + { + "vlan_id": 1, + "ipv4": { + "address": "10.1.0.6", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 20 + } + } + ] + }, + { + "ixp_id": 2, + "state": "active", + "if_list": [ + { + "switch_id": 2, + "if_speed": 10 + } + ], + "vlan_list": [ + { + "vlan_id": 2, + "ipv4": { + "address": "10.2.0.6", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 20 + } + } + ] + } + ] + }, + { + "asnum": 1213, + "member_since": "2014-01-06T00:00:00Z", + "url": "http:\/\/www.example.com\/", + "name": "HEAnet", + "peering_policy": "open", + "member_type": "peering", + "contact_email": [ + "peering@example.com" + ], + "contact_phone": [ + "" + ], + "ixp_manager": { + "tags": [], + "in_manrs": false, + "is_reseller": false, + "is_resold": false + }, + "connection_list": [ + { + "ixp_id": 1, + "state": "active", + "if_list": [ + { + "switch_id": 1, + "if_speed": 1000 + }, + { + "switch_id": 1, + "if_speed": 1000 + } + ], + "vlan_list": [ + { + "vlan_id": 1, + "ipv4": { + "address": "10.1.0.10", + "as_macro": "AS-HEANET", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 1000 + }, + "ipv6": { + "address": "2001:db8:1::10", + "as_macro": "AS-HEANET", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 1000 + } + } + ] + }, + { + "ixp_id": 2, + "state": "active", + "if_list": [ + { + "switch_id": 2, + "if_speed": 1000 + } + ], + "vlan_list": [ + { + "vlan_id": 2, + "ipv4": { + "address": "10.2.0.11", + "as_macro": "AS-HEANET", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 1000 + }, + "ipv6": { + "address": "2001:db8:2::11", + "as_macro": "AS-HEANET", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 1000 + } + } + ] + } + ] + }, + { + "asnum": 2128, + "member_since": "2014-01-06T00:00:00Z", + "url": "http:\/\/www.siep.com\/", + "name": "INEX", + "peering_policy": "mandatory", + "member_type": "ixp", + "contact_email": [ + "peering@siep.net" + ], + "contact_phone": [ + "+353 1 123 4567" + ], + "peering_policy_url": "http:\/\/www.siep.com\/noc\/", + "contact_hours": "24x7", + "ixp_manager": { + "tags": [], + "in_manrs": false, + "is_reseller": false, + "is_resold": false + }, + "connection_list": [] + }, + { + "asnum": 25441, + "member_since": "2014-01-06T00:00:00Z", + "url": "http:\/\/www.example.com\/", + "name": "Imagine", + "peering_policy": "open", + "member_type": "peering", + "contact_email": [ + "peering@example.com" + ], + "contact_phone": [ + "" + ], + "ixp_manager": { + "tags": { + "test-tag1": "Test Tag1" + }, + "in_manrs": false, + "is_reseller": false, + "is_resold": false + }, + "connection_list": [ + { + "ixp_id": 1, + "state": "active", + "if_list": [ + { + "switch_id": 1, + "if_speed": 1000 + } + ], + "vlan_list": [ + { + "vlan_id": 1, + "ipv6": { + "address": "2001:db8:1::8", + "as_macro": "AS-IBIS", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 1000 + } + } + ] + }, + { + "ixp_id": 2, + "state": "active", + "if_list": [ + { + "switch_id": 2, + "if_speed": 10000 + } + ], + "vlan_list": [ + { + "vlan_id": 2, + "ipv4": { + "address": "10.2.0.46", + "as_macro": "AS-IBIS", + "routeserver": false, + "mac_addresses": [], + "max_prefix": 1000 + }, + "ipv6": { + "address": "2001:db8:2::46", + "as_macro": "AS-IBIS", + "routeserver": false, + "mac_addresses": [], + "max_prefix": 1000 + } + } + ] + } + ] + } + ] +} diff --git a/data/ci/known-good/api-v4-member-export-1.0-unauth.json b/data/ci/known-good/api-v4-member-export-1.0-unauth.json new file mode 100644 index 000000000..587b09153 --- /dev/null +++ b/data/ci/known-good/api-v4-member-export-1.0-unauth.json @@ -0,0 +1,369 @@ +{ + "version": "1.0", + "generator": "IXP Manager v999.9.9", + "timestamp": "2020-05-23T08:36:27Z", + "ixp_list": [ + { + "shortname": "Infrastructure #1", + "name": "IXP", + "country": "IE", + "url": "https:\/\/www.example.com\/", + "peeringdb_id": 48, + "ixf_id": 20, + "ixp_id": 1, + "support_email": "ixp@example.com", + "support_phone": "+353 20 912 2000", + "support_contact_hours": "24x7", + "emergency_email": "ixp@example.com", + "emergency_phone": "+353 20 912 2000", + "emergency_contact_hours": "24x7", + "billing_contact_hours": "8x5", + "billing_email": "ixp@example.com", + "billing_phone": "+353 20 912 2000", + "peering_policy_list": [ + "open", + "selective", + "mandatory", + "closed" + ], + "vlan": [ + { + "id": 1, + "name": "Peering LAN 1", + "ipv4": { + "prefix": "192.0.2.0", + "mask_length": 25 + }, + "ipv6": { + "prefix": "2001:db8::", + "mask_length": 64 + } + } + ], + "switch": [ + { + "id": 1, + "name": "switch1", + "colo": "Location 1", + "city": "Cork", + "country": "IE", + "manufacturer": "Brocade", + "model": "FESX624", + "software": "" + } + ] + }, + { + "shortname": "Infrastructure #2", + "name": "IXP", + "country": "IE", + "url": "https:\/\/www.example.com\/", + "peeringdb_id": 387, + "ixf_id": 645, + "ixp_id": 2, + "support_email": "ixp@example.com", + "support_phone": "+353 20 912 2000", + "support_contact_hours": "24x7", + "emergency_email": "ixp@example.com", + "emergency_phone": "+353 20 912 2000", + "emergency_contact_hours": "24x7", + "billing_contact_hours": "8x5", + "billing_email": "ixp@example.com", + "billing_phone": "+353 20 912 2000", + "peering_policy_list": [ + "open", + "selective", + "mandatory", + "closed" + ], + "vlan": [ + { + "id": 2, + "name": "Peering LAN 2", + "ipv4": { + "prefix": "192.0.2.128", + "mask_length": 25 + }, + "ipv6": { + "prefix": "2001:db8:0:2::", + "mask_length": 64 + } + } + ], + "switch": [ + { + "id": 2, + "name": "switch2", + "colo": "Location 1", + "city": "Cork", + "country": "IE", + "manufacturer": "Brocade", + "model": "FESX624", + "software": "" + } + ] + } + ], + "member_list": [ + { + "asnum": 42, + "member_since": "2014-01-06T00:00:00Z", + "url": "http:\/\/www.example.com\/", + "name": "PCH DNS", + "peering_policy": "open", + "member_type": "peering", + "ixp_manager": { + "tags": [], + "in_manrs": false, + "is_reseller": false, + "is_resold": false + }, + "connection_list": [ + { + "ixp_id": 1, + "state": "active", + "if_list": [ + { + "switch_id": 1, + "if_speed": 100 + } + ], + "vlan_list": [ + { + "vlan_id": 1, + "ipv4": { + "address": "10.1.0.36", + "as_macro": "AS-PCH", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 2000 + } + } + ] + } + ] + }, + { + "asnum": 112, + "member_since": "2014-01-06T00:00:00Z", + "url": "http:\/\/www.example.com\/", + "name": "AS112", + "peering_policy": "open", + "member_type": "peering", + "ixp_manager": { + "tags": { + "test-tag1": "Test Tag1" + }, + "in_manrs": false, + "is_reseller": false, + "is_resold": false + }, + "connection_list": [ + { + "ixp_id": 1, + "state": "active", + "if_list": [ + { + "switch_id": 1, + "if_speed": 10 + } + ], + "vlan_list": [ + { + "vlan_id": 1, + "ipv4": { + "address": "10.1.0.6", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 20 + } + } + ] + }, + { + "ixp_id": 2, + "state": "active", + "if_list": [ + { + "switch_id": 2, + "if_speed": 10 + } + ], + "vlan_list": [ + { + "vlan_id": 2, + "ipv4": { + "address": "10.2.0.6", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 20 + } + } + ] + } + ] + }, + { + "asnum": 1213, + "member_since": "2014-01-06T00:00:00Z", + "url": "http:\/\/www.example.com\/", + "name": "HEAnet", + "peering_policy": "open", + "member_type": "peering", + "ixp_manager": { + "tags": [], + "in_manrs": false, + "is_reseller": false, + "is_resold": false + }, + "connection_list": [ + { + "ixp_id": 1, + "state": "active", + "if_list": [ + { + "switch_id": 1, + "if_speed": 1000 + }, + { + "switch_id": 1, + "if_speed": 1000 + } + ], + "vlan_list": [ + { + "vlan_id": 1, + "ipv4": { + "address": "10.1.0.10", + "as_macro": "AS-HEANET", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 1000 + }, + "ipv6": { + "address": "2001:db8:1::10", + "as_macro": "AS-HEANET", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 1000 + } + } + ] + }, + { + "ixp_id": 2, + "state": "active", + "if_list": [ + { + "switch_id": 2, + "if_speed": 1000 + } + ], + "vlan_list": [ + { + "vlan_id": 2, + "ipv4": { + "address": "10.2.0.11", + "as_macro": "AS-HEANET", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 1000 + }, + "ipv6": { + "address": "2001:db8:2::11", + "as_macro": "AS-HEANET", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 1000 + } + } + ] + } + ] + }, + { + "asnum": 2128, + "member_since": "2014-01-06T00:00:00Z", + "url": "http:\/\/www.siep.com\/", + "name": "INEX", + "peering_policy": "mandatory", + "member_type": "ixp", + "ixp_manager": { + "tags": [], + "in_manrs": false, + "is_reseller": false, + "is_resold": false + }, + "connection_list": [] + }, + { + "asnum": 25441, + "member_since": "2014-01-06T00:00:00Z", + "url": "http:\/\/www.example.com\/", + "name": "Imagine", + "peering_policy": "open", + "member_type": "peering", + "ixp_manager": { + "tags": { + "test-tag1": "Test Tag1" + }, + "in_manrs": false, + "is_reseller": false, + "is_resold": false + }, + "connection_list": [ + { + "ixp_id": 1, + "state": "active", + "if_list": [ + { + "switch_id": 1, + "if_speed": 1000 + } + ], + "vlan_list": [ + { + "vlan_id": 1, + "ipv6": { + "address": "2001:db8:1::8", + "as_macro": "AS-IBIS", + "routeserver": true, + "mac_addresses": [], + "max_prefix": 1000 + } + } + ] + }, + { + "ixp_id": 2, + "state": "active", + "if_list": [ + { + "switch_id": 2, + "if_speed": 10000 + } + ], + "vlan_list": [ + { + "vlan_id": 2, + "ipv4": { + "address": "10.2.0.46", + "as_macro": "AS-IBIS", + "routeserver": false, + "mac_addresses": [], + "max_prefix": 1000 + }, + "ipv6": { + "address": "2001:db8:2::46", + "as_macro": "AS-IBIS", + "routeserver": false, + "mac_addresses": [], + "max_prefix": 1000 + } + } + ] + } + ] + } + ] +} diff --git a/data/ci/known-good/ci-apiv4-as112-lan1-ipv4.conf b/data/ci/known-good/ci-apiv4-as112-lan1-ipv4.conf new file mode 100644 index 000000000..649673656 --- /dev/null +++ b/data/ci/known-good/ci-apiv4-as112-lan1-ipv4.conf @@ -0,0 +1,124 @@ +# +# Bird AS112 configuration generated by IXP Manager +# +# Do not edit this file, it will be overwritten. +# +# Generated: 2016-11-08 18:51:34 +# + +# For VLAN: Peering LAN 1 (Tag: 1, Database ID: 1) + +# standardise time formats: +timeformat base iso long; +timeformat log iso long; +timeformat protocol iso long; +timeformat route iso long; + + +log "/var/log/bird/as112-lan1-ipv4.log" all; +log syslog all; + +define routerasn = 112; +define routeraddress = 192.0.2.6; + +router id 192.0.2.6; +listen bgp address routeraddress; + +protocol kernel { + export all; + scan time 120; +} + +protocol device { + scan time 10; +} + +# These function excludes weird networks +# rfc1918, class D, class E, too long and too short prefixes + + +function avoid_martians_v4() +prefix set martiansv4; +{ + # This list of martians is obtained from the IANA IPv4 + # Special-Purpose Address Registry: + # http://www.iana.org/assignments/iana-ipv4-special-registry + martiansv4 = [ + 10.0.0.0/8+, # RFC1918 - Private use + 100.64.0.0/10+, # RFC6598 - Shared address space + 127.0.0.0/8+, # RFC1122 - Loopback + 169.254.0.0/16+, # RFC3927 - Link-local + 172.16.0.0/12+, # RFC1918 - Private use + 192.0.0.0/24+, # multiple RFCs + 192.0.2.0/24+, # RFC5737 - Documentation - TEST-NET-1 + 192.168.0.0/16+, # RFC1918 - Private use + 198.18.0.0/15+, # RFC2544 - Benchmarking + 198.51.100.0/24+, # RFC5737 - Documentation - TEST-NET-2 + 203.0.113.0/24+, # RFC5737 - Documentation - TEST-NET-3 + 224.0.0.0/4+, # RFC3171 - Multicast + 240.0.0.0/4+, # RFC1112 - Reserved + 0.0.0.0/32-, + 0.0.0.0/0{25,32}, + 0.0.0.0/0{0,7} + ]; + + # Avoid RFC1918 and similar networks + if net ~ martiansv4 then + return false; + + return true; +} + +filter f_import_policy +{ + if !(avoid_martians_v4()) then + reject; + + accept; +} + + + + +# This protocol defines the routes we want to export for AS112 + +protocol static static_as112 { + + + route 192.175.48.0/24 blackhole; + route 192.31.196.0/24 blackhole; + + +} + +## +## AS112 client configuration +## + + + + +protocol bgp pb_as42_vli3_ipv4 { + description "AS42 - PCH DNS"; + local as routerasn; + source address routeraddress; + neighbor 10.1.0.36 as 42; + import filter f_import_policy; + export where proto = "static_as112"; + import limit 2000 action restart; + +} + + +protocol bgp pb_as1213_vli1_ipv4 { + description "AS1213 - HEAnet"; + local as routerasn; + source address routeraddress; + neighbor 10.1.0.10 as 1213; + import filter f_import_policy; + export where proto = "static_as112"; + import limit 1000 action restart; + +} + + diff --git a/data/ci/known-good/ci-apiv4-as112-lan1-ipv6.conf b/data/ci/known-good/ci-apiv4-as112-lan1-ipv6.conf new file mode 100644 index 000000000..f8a583c25 --- /dev/null +++ b/data/ci/known-good/ci-apiv4-as112-lan1-ipv6.conf @@ -0,0 +1,123 @@ +# +# Bird AS112 configuration generated by IXP Manager +# +# Do not edit this file, it will be overwritten. +# +# Generated: 2016-11-08 18:51:42 +# + +# For VLAN: Peering LAN 1 (Tag: 1, Database ID: 1) + +# standardise time formats: +timeformat base iso long; +timeformat log iso long; +timeformat protocol iso long; +timeformat route iso long; + + +log "/var/log/bird/as112-lan1-ipv6.log" all; +log syslog all; + +define routerasn = 112; +define routeraddress = 2001:db8:0:0:2::6; + +router id 192.0.2.6; +listen bgp address routeraddress; + +protocol kernel { + export all; + scan time 120; +} + +protocol device { + scan time 10; +} + +# These function excludes weird networks +# rfc1918, class D, class E, too long and too short prefixes + + +function avoid_martians_v6() +prefix set martiansv6; +{ + # This list of martians is obtained from the IANA IPv6 + # Special-Purpose Address Registry: + # http://www.iana.org/assignments/iana-ipv6-special-registry + martiansv6 = [ + ::/0, # Default (can be advertised as a route in BGP to peers if desired) + ::/96, # IPv4-compatible IPv6 address - deprecated by RFC4291 + ::/128, # Unspecified address + ::1/128, # Local host loopback address + ::ffff:0.0.0.0/96+, # IPv4-mapped addresses + ::224.0.0.0/100+, # Compatible address (IPv4 format) + ::127.0.0.0/104+, # Compatible address (IPv4 format) + ::0.0.0.0/104+, # Compatible address (IPv4 format) + ::255.0.0.0/104+, # Compatible address (IPv4 format) + 0000::/8+, # Pool used for unspecified, loopback and embedded IPv4 addresses + 0100::/64+, # RFC6666 - discard-only address block + 0200::/7+, # RFC4548 - OSI NSAP-mapped prefix set, deprecated by RFC4048 + 3ffe::/16+, # Former 6bone, now decommissioned + 2001:2::/48+, # RFC5180 - Benchmarking + 2001:10::/28+, # RFC4843 - ORCHID + 2001:db8::/32+, # RFC3849 - Reserved by IANA for special purposes and documentation + 2002:e000::/20+, # Invalid 6to4 packets (IPv4 multicast) + 2002:7f00::/24+, # Invalid 6to4 packets (IPv4 loopback) + 2002:0000::/24+, # Invalid 6to4 packets (IPv4 default) + 2002:ff00::/24+, # Invalid 6to4 packets + 2002:0a00::/24+, # Invalid 6to4 packets (IPv4 private 10.0.0.0/8 network) + 2002:ac10::/28+, # Invalid 6to4 packets (IPv4 private 172.16.0.0/12 network) + 2002:c0a8::/32+, # Invalid 6to4 packets (IPv4 private 192.168.0.0/16 network) + fc00::/7+, # RFC4193 - Unicast Unique Local Addresses (ULA) + fe80::/10+, # Link-local Unicast + fec0::/10+, # Site-local Unicast - deprecated by RFC 3879 (replaced by ULA) + ff00::/8+, # Multicast + ::/0{49,128} # Filter small prefixes + ]; + + # Avoid RFC1918 and similar networks + if net ~ martiansv6 then + return false; + + return true; +} + +filter f_import_policy +{ + if !(avoid_martians_v6()) then + reject; + + accept; +} + + + +# This protocol defines the routes we want to export for AS112 + +protocol static static_as112 { + + + route 2620:4f:8000::/48 blackhole; + route 2001:4:112::/48 blackhole; + + +} + +## +## AS112 client configuration +## + + + + +protocol bgp pb_as1213_vli1_ipv6 { + description "AS1213 - HEAnet"; + local as routerasn; + source address routeraddress; + neighbor 2001:db8:1::10 as 1213; + import filter f_import_policy; + export where proto = "static_as112"; + import limit 1000 action restart; + +} + + diff --git a/data/ci/known-good/ci-apiv4-as112-lan2-ipv4.conf b/data/ci/known-good/ci-apiv4-as112-lan2-ipv4.conf new file mode 100644 index 000000000..b24589138 --- /dev/null +++ b/data/ci/known-good/ci-apiv4-as112-lan2-ipv4.conf @@ -0,0 +1,100 @@ +# +# Bird AS112 configuration generated by IXP Manager +# +# Do not edit this file, it will be overwritten. +# +# Generated: 2016-11-08 18:51:59 +# + +# For VLAN: Peering LAN 2 (Tag: 2, Database ID: 2) + +# standardise time formats: +timeformat base iso long; +timeformat log iso long; +timeformat protocol iso long; +timeformat route iso long; + + +log "/var/log/bird/as112-lan2-ipv4.log" all; +log syslog all; + +define routerasn = 112; +define routeraddress = 192.0.2.16; + +router id 192.0.2.16; +listen bgp address routeraddress; + +protocol kernel { + export all; + scan time 120; +} + +protocol device { + scan time 10; +} + +# These function excludes weird networks +# rfc1918, class D, class E, too long and too short prefixes + + +function avoid_martians_v4() +prefix set martiansv4; +{ + # This list of martians is obtained from the IANA IPv4 + # Special-Purpose Address Registry: + # http://www.iana.org/assignments/iana-ipv4-special-registry + martiansv4 = [ + 10.0.0.0/8+, # RFC1918 - Private use + 100.64.0.0/10+, # RFC6598 - Shared address space + 127.0.0.0/8+, # RFC1122 - Loopback + 169.254.0.0/16+, # RFC3927 - Link-local + 172.16.0.0/12+, # RFC1918 - Private use + 192.0.0.0/24+, # multiple RFCs + 192.0.2.0/24+, # RFC5737 - Documentation - TEST-NET-1 + 192.168.0.0/16+, # RFC1918 - Private use + 198.18.0.0/15+, # RFC2544 - Benchmarking + 198.51.100.0/24+, # RFC5737 - Documentation - TEST-NET-2 + 203.0.113.0/24+, # RFC5737 - Documentation - TEST-NET-3 + 224.0.0.0/4+, # RFC3171 - Multicast + 240.0.0.0/4+, # RFC1112 - Reserved + 0.0.0.0/32-, + 0.0.0.0/0{25,32}, + 0.0.0.0/0{0,7} + ]; + + # Avoid RFC1918 and similar networks + if net ~ martiansv4 then + return false; + + return true; +} + +filter f_import_policy +{ + if !(avoid_martians_v4()) then + reject; + + accept; +} + + + + +# This protocol defines the routes we want to export for AS112 + +protocol static static_as112 { + + + route 192.175.48.0/24 blackhole; + route 192.31.196.0/24 blackhole; + + +} + +## +## AS112 client configuration +## + + + + diff --git a/data/ci/known-good/ci-apiv4-as112-lan2-ipv6.conf b/data/ci/known-good/ci-apiv4-as112-lan2-ipv6.conf new file mode 100644 index 000000000..fe0260770 --- /dev/null +++ b/data/ci/known-good/ci-apiv4-as112-lan2-ipv6.conf @@ -0,0 +1,111 @@ +# +# Bird AS112 configuration generated by IXP Manager +# +# Do not edit this file, it will be overwritten. +# +# Generated: 2016-11-08 18:51:51 +# + +# For VLAN: Peering LAN 2 (Tag: 2, Database ID: 2) + +# standardise time formats: +timeformat base iso long; +timeformat log iso long; +timeformat protocol iso long; +timeformat route iso long; + + +log "/var/log/bird/as112-lan2-ipv6.log" all; +log syslog all; + +define routerasn = 112; +define routeraddress = 2001:db8:0:0:2::16; + +router id 192.0.2.16; +listen bgp address routeraddress; + +protocol kernel { + export all; + scan time 120; +} + +protocol device { + scan time 10; +} + +# These function excludes weird networks +# rfc1918, class D, class E, too long and too short prefixes + + +function avoid_martians_v6() +prefix set martiansv6; +{ + # This list of martians is obtained from the IANA IPv6 + # Special-Purpose Address Registry: + # http://www.iana.org/assignments/iana-ipv6-special-registry + martiansv6 = [ + ::/0, # Default (can be advertised as a route in BGP to peers if desired) + ::/96, # IPv4-compatible IPv6 address - deprecated by RFC4291 + ::/128, # Unspecified address + ::1/128, # Local host loopback address + ::ffff:0.0.0.0/96+, # IPv4-mapped addresses + ::224.0.0.0/100+, # Compatible address (IPv4 format) + ::127.0.0.0/104+, # Compatible address (IPv4 format) + ::0.0.0.0/104+, # Compatible address (IPv4 format) + ::255.0.0.0/104+, # Compatible address (IPv4 format) + 0000::/8+, # Pool used for unspecified, loopback and embedded IPv4 addresses + 0100::/64+, # RFC6666 - discard-only address block + 0200::/7+, # RFC4548 - OSI NSAP-mapped prefix set, deprecated by RFC4048 + 3ffe::/16+, # Former 6bone, now decommissioned + 2001:2::/48+, # RFC5180 - Benchmarking + 2001:10::/28+, # RFC4843 - ORCHID + 2001:db8::/32+, # RFC3849 - Reserved by IANA for special purposes and documentation + 2002:e000::/20+, # Invalid 6to4 packets (IPv4 multicast) + 2002:7f00::/24+, # Invalid 6to4 packets (IPv4 loopback) + 2002:0000::/24+, # Invalid 6to4 packets (IPv4 default) + 2002:ff00::/24+, # Invalid 6to4 packets + 2002:0a00::/24+, # Invalid 6to4 packets (IPv4 private 10.0.0.0/8 network) + 2002:ac10::/28+, # Invalid 6to4 packets (IPv4 private 172.16.0.0/12 network) + 2002:c0a8::/32+, # Invalid 6to4 packets (IPv4 private 192.168.0.0/16 network) + fc00::/7+, # RFC4193 - Unicast Unique Local Addresses (ULA) + fe80::/10+, # Link-local Unicast + fec0::/10+, # Site-local Unicast - deprecated by RFC 3879 (replaced by ULA) + ff00::/8+, # Multicast + ::/0{49,128} # Filter small prefixes + ]; + + # Avoid RFC1918 and similar networks + if net ~ martiansv6 then + return false; + + return true; +} + +filter f_import_policy +{ + if !(avoid_martians_v6()) then + reject; + + accept; +} + + + +# This protocol defines the routes we want to export for AS112 + +protocol static static_as112 { + + + route 2620:4f:8000::/48 blackhole; + route 2001:4:112::/48 blackhole; + + +} + +## +## AS112 client configuration +## + + + + diff --git a/data/ci/known-good/ci-apiv4-b2-rc1-lan1-ipv4.conf b/data/ci/known-good/ci-apiv4-b2-rc1-lan1-ipv4.conf new file mode 100644 index 000000000..9c6263f05 --- /dev/null +++ b/data/ci/known-good/ci-apiv4-b2-rc1-lan1-ipv4.conf @@ -0,0 +1,736 @@ +# +# Bird v2 Route Collector configuration generated by IXP Manager +# +# Do not edit this file, it will be overwritten. +# +# Generated: 2019-03-09 15:47:37 +# + +# For VLAN: Peering LAN 1 (Tag: 1, Database ID: 1) + + +# standardise time formats: +timeformat base iso long; +timeformat log iso long; +timeformat protocol iso long; +timeformat route iso long; + + +log "/var/log/bird/b2-rc1-lan1-ipv4.log" all; +log syslog name "bird-b2-rc1-lan1-ipv4" all; + +define routerasn = 65500; +define routeraddress = 192.0.2.8; + +router id 192.0.2.8; + +# ignore interface up/down events +protocol device { } + + +# This function excludes weird networks +# rfc1918, class D, class E, too long and too short prefixes +function avoid_martians() -> bool +prefix set martians; +{ + + martians = [ + 0.0.0.0/32-, # rfc5735 Special Use IPv4 Addresses + 0.0.0.0/0{0,7}, # rfc1122 Requirements for Internet Hosts -- Communication Layers 3.2.1.3 + 10.0.0.0/8+, # rfc1918 Address Allocation for Private Internets + 100.64.0.0/10+, # rfc6598 IANA-Reserved IPv4 Prefix for Shared Address Space + 127.0.0.0/8+, # rfc1122 Requirements for Internet Hosts -- Communication Layers 3.2.1.3 + 169.254.0.0/16+, # rfc3927 Dynamic Configuration of IPv4 Link-Local Addresses + 172.16.0.0/12+, # rfc1918 Address Allocation for Private Internets + 192.0.0.0/24+, # rfc6890 Special-Purpose Address Registries + 192.0.2.0/24+, # rfc5737 IPv4 Address Blocks Reserved for Documentation + 192.168.0.0/16+, # rfc1918 Address Allocation for Private Internets + 198.18.0.0/15+, # rfc2544 Benchmarking Methodology for Network Interconnect Devices + 198.51.100.0/24+, # rfc5737 IPv4 Address Blocks Reserved for Documentation + 203.0.113.0/24+, # rfc5737 IPv4 Address Blocks Reserved for Documentation + 224.0.0.0/4+, # rfc1112 Host Extensions for IP Multicasting + 240.0.0.0/4+ # rfc6890 Special-Purpose Address Registries + ]; + + + # Avoid RFC1918 and similar networks + if net ~ martians then + return false; + + return true; +} + + + + +## +## Route collector client configuration +## + + +######################################################################################## +######################################################################################## +# +# Community filtering definitions for use with looking glasses +# +# Current implementation based on: +# +# https://github.com/euro-ix/rs-workshop-july-2017/wiki/Route-Server-BGP-Community-usage +# +######################################################################################## +######################################################################################## + + + +# These will all be filtered and not piped to the master table: + +define IXP_LC_FILTERED_PREFIX_LEN_TOO_LONG = ( routerasn, 1101, 1 ); +define IXP_LC_FILTERED_PREFIX_LEN_TOO_SHORT = ( routerasn, 1101, 2 ); +define IXP_LC_FILTERED_BOGON = ( routerasn, 1101, 3 ); +define IXP_LC_FILTERED_BOGON_ASN = ( routerasn, 1101, 4 ); +define IXP_LC_FILTERED_AS_PATH_TOO_LONG = ( routerasn, 1101, 5 ); +define IXP_LC_FILTERED_AS_PATH_TOO_SHORT = ( routerasn, 1101, 6 ); +define IXP_LC_FILTERED_FIRST_AS_NOT_PEER_AS = ( routerasn, 1101, 7 ); +define IXP_LC_FILTERED_NEXT_HOP_NOT_PEER_IP = ( routerasn, 1101, 8 ); +define IXP_LC_FILTERED_IRRDB_PREFIX_FILTERED = ( routerasn, 1101, 9 ); +define IXP_LC_FILTERED_IRRDB_ORIGIN_AS_FILTERED = ( routerasn, 1101, 10 ); +define IXP_LC_FILTERED_PREFIX_NOT_IN_ORIGIN_AS = ( routerasn, 1101, 11 ); + +define IXP_LC_FILTERED_RPKI_UNKNOWN = ( routerasn, 1101, 12 ); +define IXP_LC_FILTERED_RPKI_INVALID = ( routerasn, 1101, 13 ); +define IXP_LC_FILTERED_TRANSIT_FREE_ASN = ( routerasn, 1101, 14 ); +define IXP_LC_FILTERED_TOO_MANY_COMMUNITIES = ( routerasn, 1101, 15 ); + + + + +# Informational prefixes + +define IXP_LC_INFO_RPKI_VALID = ( routerasn, 1000, 1 ); +define IXP_LC_INFO_RPKI_UNKNOWN = ( routerasn, 1000, 2 ); +define IXP_LC_INFO_RPKI_NOT_CHECKED = ( routerasn, 1000, 3 ); + +define IXP_LC_INFO_IRRDB_INVALID = ( routerasn, 1001, 0 ); +define IXP_LC_INFO_IRRDB_VALID = ( routerasn, 1001, 1 ); +define IXP_LC_INFO_IRRDB_NOT_CHECKED = ( routerasn, 1001, 2 ); +define IXP_LC_INFO_IRRDB_MORE_SPECIFIC = ( routerasn, 1001, 3 ); + +define IXP_LC_INFO_IRRDB_FILTERED_LOOSE = ( routerasn, 1001, 1000 ); +define IXP_LC_INFO_IRRDB_FILTERED_STRICT = ( routerasn, 1001, 1001 ); +define IXP_LC_INFO_IRRDB_PREFIX_EMPTY = ( routerasn, 1001, 1002 ); + +define IXP_LC_INFO_FROM_IXROUTESERVER = ( routerasn, 1001, 1100 ); + +define IXP_LC_INFO_SAME_AS_NEXT_HOP = ( routerasn, 1001, 1200 ); + + + + +######################################################################################## +######################################################################################## +# +# RPKI protocol configuration +# +######################################################################################## +######################################################################################## + + +roa4 table t_roa; + +protocol rpki rpki1 { + + roa4 { table t_roa; }; + + remote "10.39.5.123" port 3323; + + retry keep 90; + refresh keep 900; + expire keep 172800; +} + + +protocol rpki rpki2 { + + roa4 { table t_roa; }; + + remote "10.39.5.124" port 3323; + + retry keep 90; + refresh keep 900; + expire keep 172800; +} + + + + + +######################################################################################## +######################################################################################## +# +# Filter known transit networks +# +# Inspired by: http://bgpfilterguide.nlnog.net/guides/no_transit_leaks/ +# +######################################################################################## +######################################################################################## + + +define TRANSIT_ASNS = [ 174, 701, 1299, 2914, 3257, 3320, 3356, 3491, 4134, 5511, 6453, 6461, 6762, 6830, 7018 ]; + +function filter_has_transit_path() -> bool +int set transit_asns; +{ + transit_asns = TRANSIT_ASNS; + if (bgp_path ~ transit_asns) then { + bgp_large_community.add( IXP_LC_FILTERED_TRANSIT_FREE_ASN ); + return true; + } + + return false; +} + + + +######################################################################################## +######################################################################################## +# +# Route collector clients +# +######################################################################################## +######################################################################################## + + +######################################################################################## +######################################################################################## +### +### AS42 - PCH DNS - VLAN Interface #3 + + + +function f_import_as42() -> bool + +prefix set allnet; +ip set allips; +int set allas; +{ + + + # From: api/v4/router/collector/bird2/import-pre-extra) + # This file can be skinned to add custom config to + # the fn_import function here. For example, INEX uses + # it to tag routes learnt from our own route servers + # with an information community and accept them as is. + + + # Filter small prefixes + if ( net ~ [ 0.0.0.0/0{25,32} ] ) then { + bgp_large_community.add( IXP_LC_FILTERED_PREFIX_LEN_TOO_LONG ); + } + + + if !(avoid_martians()) then { + bgp_large_community.add( IXP_LC_FILTERED_BOGON ); + } + + # Belt and braces: must have at least one ASN in the path + if( bgp_path.len < 1 ) then { + bgp_large_community.add( IXP_LC_FILTERED_AS_PATH_TOO_SHORT ); + # we won't continue in this case: zero path asn is broken + return true; + } + + # Peer ASN == route's first ASN? + if (bgp_path.first != 42 ) then { + bgp_large_community.add( IXP_LC_FILTERED_FIRST_AS_NOT_PEER_AS ); + } + + # set of all IPs this ASN uses to peer with on this VLAN + allips = [ 10.1.0.36 ]; + + # Prevent BGP NEXT_HOP Hijacking + if !( from = bgp_next_hop ) then { + + # need to differentiate between same ASN next hop or actual next hop hijacking + if( bgp_next_hop ~ allips ) then { + bgp_large_community.add( IXP_LC_INFO_SAME_AS_NEXT_HOP ); + } else { + # looks like hijacking (intentional or not) + bgp_large_community.add( IXP_LC_FILTERED_NEXT_HOP_NOT_PEER_IP ); + } + } + + # Filter Known Transit Networks + filter_has_transit_path(); + + # Belt and braces: no one needs an ASN path with > 64 hops, that's just broken + if( bgp_path.len > 64 ) then { + bgp_large_community.add( IXP_LC_FILTERED_AS_PATH_TOO_LONG ); + } + + + + allas = [ 27, 42, 187, 297, 715, 3856, 7251, 13202, 16327, 16668, + 16686, 20144, 20539, 21312, 24999, 27678, 32978, 32979, 35160, 38052, + 44876, 45170, 45494, 48582, 48892, 50843, 51874, 52234, 52306, 54145, + 59464, 60313, 197058 + ]; + + + # Ensure origin ASN is in the neighbors AS-SET + if !(bgp_path.last_nonaggregated ~ allas) then { + bgp_large_community.add( IXP_LC_FILTERED_IRRDB_ORIGIN_AS_FILTERED ); + } + + + + + + # RPKI check + if( roa_check( t_roa, net, bgp_path.last_nonaggregated ) = ROA_INVALID ) then { + bgp_large_community.add( IXP_LC_FILTERED_RPKI_INVALID ); + } else if( roa_check( t_roa, net, bgp_path.last_nonaggregated ) = ROA_VALID ) then { + bgp_large_community.add( IXP_LC_INFO_RPKI_VALID ); + } else { + # RPKI unknown, keep checking and mark as unknown for info + bgp_large_community.add( IXP_LC_INFO_RPKI_UNKNOWN ); + } + + + + + + + allnet = [ 31.135.128.0/19, 31.135.128.0/21, 31.135.136.0/21, 31.135.144.0/22, + 31.135.148.0/22, 31.135.152.0/22, 31.135.152.0/23, 31.135.154.0/23, + 36.0.4.0/22, 63.246.32.0/20, 64.68.192.0/20, 64.68.192.0/24, + 64.68.193.0/24, 64.68.194.0/24, 64.68.195.0/24, 64.68.196.0/24, + 64.78.200.0/21, 64.185.240.0/20, 65.22.4.0/24, 65.22.5.0/24, + 65.22.19.0/24, 65.22.23.0/24, 65.22.27.0/24, 65.22.31.0/24, + 65.22.35.0/24, 65.22.39.0/24, 65.22.47.0/24, 65.22.51.0/24, + 65.22.55.0/24, 65.22.59.0/24, 65.22.63.0/24, 65.22.67.0/24, + 65.22.71.0/24, 65.22.79.0/24, 65.22.83.0/24, 65.22.87.0/24, + 65.22.91.0/24, 65.22.95.0/24, 65.22.99.0/24, 65.22.103.0/24, + 65.22.107.0/24, 65.22.111.0/24, 65.22.115.0/24, 65.22.119.0/24, + 65.22.123.0/24, 65.22.127.0/24, 65.22.131.0/24, 65.22.135.0/24, + 65.22.139.0/24, 65.22.143.0/24, 65.22.147.0/24, 65.22.151.0/24, + 65.22.155.0/24, 65.22.159.0/24, 65.22.163.0/24, 65.22.171.0/24, + 65.22.175.0/24, 65.22.179.0/24, 65.22.183.0/24, 65.22.187.0/24, + 65.22.191.0/24, 65.22.195.0/24, 65.22.199.0/24, 65.22.203.0/24, + 65.22.207.0/24, 65.22.211.0/24, 65.22.215.0/24, 65.22.219.0/24, + 65.22.223.0/24, 65.22.227.0/24, 65.22.231.0/24, 65.22.235.0/24, + 65.22.239.0/24, 65.22.243.0/24, 65.22.247.0/24, 66.96.112.0/20, + 66.102.32.0/20, 66.175.104.0/24, 66.185.112.0/20, 66.225.199.0/24, + 66.225.200.0/24, 66.225.201.0/24, 67.21.37.0/24, 67.22.112.0/21, + 67.158.48.0/20, 68.65.112.0/21, 68.65.126.0/23, 68.65.126.0/24, + 68.65.127.0/24, 69.166.10.0/24, 69.166.12.0/22, 70.40.0.0/21, + 70.40.8.0/21, 72.0.48.0/20, 72.0.48.0/24, 72.0.49.0/24, + 72.0.50.0/24, 72.0.51.0/24, 72.0.52.0/24, 72.0.53.0/24, + 72.0.54.0/24, 72.0.55.0/24, 72.0.56.0/24, 72.0.57.0/24, + 72.0.58.0/24, 72.0.59.0/24, 72.0.60.0/24, 72.0.61.0/24, + 72.0.62.0/24, 72.0.63.0/24, 72.42.112.0/20, 72.42.112.0/24, + 72.42.113.0/24, 72.42.114.0/24, 72.42.115.0/24, 72.42.116.0/24, + 72.42.117.0/24, 72.42.118.0/24, 72.42.119.0/24, 72.42.120.0/24, + 72.42.121.0/24, 72.42.122.0/24, 72.42.123.0/24, 72.42.124.0/24, + 72.42.125.0/24, 72.42.126.0/24, 72.42.127.0/24, 74.63.16.0/20, + 74.63.16.0/24, 74.63.17.0/24, 74.63.18.0/24, 74.63.19.0/24, + 74.63.20.0/24, 74.63.21.0/24, 74.63.22.0/24, 74.63.23.0/24, + 74.63.24.0/24, 74.63.25.0/24, 74.63.26.0/24, 74.63.27.0/24, + 74.80.64.0/18, 74.80.64.0/24, 74.80.65.0/24, 74.80.66.0/24, + 74.80.67.0/24, 74.80.68.0/24, 74.80.69.0/24, 74.80.70.0/24, + 74.80.71.0/24, 74.80.72.0/24, 74.80.73.0/24, 74.80.74.0/24, + 74.80.75.0/24, 74.80.76.0/24, 74.80.77.0/24, 74.80.78.0/24, + 74.80.79.0/24, 74.80.80.0/24, 74.80.81.0/24, 74.80.82.0/24, + 74.80.83.0/24, 74.80.84.0/24, 74.80.85.0/24, 74.80.86.0/24, + 74.80.87.0/24, 74.80.88.0/24, 74.80.89.0/24, 74.80.90.0/24, + 74.80.91.0/24, 74.80.92.0/24, 74.80.93.0/24, 74.80.94.0/24, + 74.80.95.0/24, 74.80.96.0/24, 74.80.97.0/24, 74.80.98.0/24, + 74.80.99.0/24, 74.80.100.0/24, 74.80.101.0/24, 74.80.102.0/24, + 74.80.103.0/24, 74.80.104.0/24, 74.80.105.0/24, 74.80.106.0/24, + 74.80.107.0/24, 74.80.108.0/24, 74.80.109.0/24, 74.80.110.0/24, + 74.80.111.0/24, 74.80.112.0/24, 74.80.113.0/24, 74.80.114.0/24, + 74.80.115.0/24, 74.80.116.0/24, 74.80.117.0/24, 74.80.118.0/24, + 74.80.119.0/24, 74.80.120.0/24, 74.80.121.0/24, 74.80.122.0/24, + 74.80.123.0/24, 74.80.124.0/24, 74.80.125.0/24, 74.80.126.0/23, + 74.80.126.0/24, 74.80.127.0/24, 74.118.212.0/24, 74.118.213.0/24, + 74.118.214.0/23, 75.127.16.0/20, 76.191.16.0/20, 89.19.120.0/21, + 89.19.120.0/22, 89.19.124.0/23, 89.19.126.0/23, 91.201.224.0/22, + 91.201.224.0/23, 91.201.224.0/24, 91.201.225.0/24, 91.201.226.0/23, + 91.201.226.0/24, 91.201.227.0/24, 91.209.1.0/24, 91.209.193.0/24, + 91.222.16.0/22, 91.222.40.0/24, 91.222.41.0/24, 91.222.42.0/24, + 91.222.43.0/24, 91.241.93.0/24, 93.95.24.0/21, 93.95.24.0/24, + 93.95.25.0/24, 93.95.26.0/24, 93.171.128.0/22, 95.47.163.0/24, + 101.251.4.0/22, 114.69.222.0/24, 128.8.0.0/16, 128.161.0.0/16, + 129.2.0.0/16, 130.135.0.0/16, 130.167.0.0/16, 131.161.128.0/18, + 131.182.0.0/16, 139.229.0.0/16, 140.169.0.0/16, 146.5.0.0/16, + 146.58.0.0/16, 150.144.0.0/16, 156.154.43.0/24, 156.154.50.0/24, + 156.154.59.0/24, 156.154.96.0/24, 156.154.99.0/24, 158.154.0.0/16, + 169.222.0.0/24, 183.91.132.0/22, 192.5.41.0/24, 192.12.123.0/24, + 192.42.70.0/24, 192.58.36.0/24, 192.67.83.0/24, 192.67.107.0/24, + 192.67.108.0/24, 192.68.52.0/24, 192.68.148.0/24, 192.68.162.0/24, + 192.70.244.0/24, 192.70.249.0/24, 192.77.80.0/24, 192.84.8.0/24, + 192.88.124.0/24, 192.92.65.0/24, 192.92.90.0/24, 192.100.9.0/24, + 192.100.10.0/24, 192.100.15.0/24, 192.101.148.0/24, 192.102.15.0/24, + 192.102.219.0/24, 192.102.233.0/24, 192.102.234.0/24, 192.112.18.0/24, + 192.112.223.0/24, 192.112.224.0/24, 192.124.20.0/24, 192.138.101.0/24, + 192.138.172.0/24, 192.149.89.0/24, 192.149.104.0/24, 192.149.107.0/24, + 192.149.133.0/24, 192.150.32.0/21, 192.153.157.0/24, 192.188.4.0/24, + 192.203.230.0/24, 192.225.64.0/19, 192.243.0.0/20, 192.243.16.0/21, + 193.29.206.0/24, 193.110.16.0/22, 193.110.16.0/23, 193.110.18.0/23, + 193.111.240.0/22, 193.178.228.0/23, 193.178.228.0/24, 193.178.229.0/24, + 194.0.12.0/24, 194.0.13.0/24, 194.0.14.0/24, 194.0.17.0/24, + 194.0.27.0/24, 194.0.36.0/24, 194.0.42.0/24, 194.0.47.0/24, + 194.28.144.0/22, 194.117.58.0/24, 194.117.60.0/24, 194.117.61.0/24, + 194.117.62.0/24, 194.117.63.0/24, 194.146.180.0/22, 194.146.180.0/23, + 194.146.180.0/24, 194.146.181.0/24, 194.146.182.0/23, 194.146.182.0/24, + 194.146.183.0/24, 194.146.228.0/22, 194.146.228.0/23, 194.146.228.0/24, + 194.146.229.0/24, 194.146.230.0/23, 194.146.230.0/24, 194.146.231.0/24, + 194.153.148.0/23, 195.64.162.0/23, 195.64.162.0/24, 195.64.163.0/24, + 195.82.138.0/23, 198.9.0.0/16, 198.49.1.0/24, 198.116.0.0/14, + 198.120.0.0/14, 198.182.28.0/24, 198.182.31.0/24, 198.182.167.0/24, + 199.4.137.0/24, 199.7.64.0/24, 199.7.77.0/24, 199.7.83.0/24, + 199.7.86.0/24, 199.7.91.0/24, 199.7.94.0/24, 199.7.95.0/24, + 199.43.132.0/24, 199.115.156.0/24, 199.115.157.0/24, 199.120.141.0/24, + 199.120.142.0/23, 199.120.144.0/24, 199.182.32.0/24, 199.182.40.0/24, + 199.184.181.0/24, 199.184.182.0/23, 199.184.184.0/24, 199.249.112.0/24, + 199.249.113.0/24, 199.249.114.0/24, 199.249.115.0/24, 199.249.116.0/24, + 199.249.117.0/24, 199.249.118.0/24, 199.249.119.0/24, 199.249.120.0/24, + 199.249.121.0/24, 199.249.122.0/24, 199.249.123.0/24, 199.249.124.0/24, + 199.249.125.0/24, 199.249.126.0/24, 199.249.127.0/24, 199.254.171.0/24, + 200.1.121.0/24, 200.1.131.0/24, 200.7.4.0/24, 200.16.98.0/24, + 202.6.102.0/24, 202.7.4.0/22, 202.52.0.0/23, 202.53.186.0/24, + 202.53.191.0/24, 203.119.88.0/23, 204.14.112.0/21, 204.19.119.0/24, + 204.26.57.0/24, 204.61.208.0/21, 204.61.208.0/22, 204.61.208.0/23, + 204.61.210.0/23, 204.61.210.0/24, 204.61.212.0/23, 204.61.216.0/23, + 204.194.22.0/23, 204.194.22.0/24, 204.194.23.0/24, 205.132.46.0/23, + 205.207.155.0/24, 206.51.254.0/23, 206.108.113.0/24, 206.196.160.0/19, + 206.220.228.0/22, 206.220.228.0/23, 206.220.230.0/23, 206.223.122.0/24, + 207.34.5.0/24, 207.34.6.0/23, 208.15.19.0/24, 208.49.115.64/27, + 208.67.88.0/22, 216.21.2.0/23 + ]; + + if ! (net ~ allnet) then { + if bgp_large_community ~ [IXP_LC_INFO_RPKI_VALID] then { + bgp_large_community.add( IXP_LC_INFO_IRRDB_INVALID ); + } else { + bgp_large_community.add( IXP_LC_FILTERED_IRRDB_PREFIX_FILTERED ); + } + + bgp_large_community.add( IXP_LC_INFO_IRRDB_FILTERED_STRICT ); + } else { + bgp_large_community.add( IXP_LC_INFO_IRRDB_VALID ); + } + + + + + + return true; +} + + + + +protocol bgp pb_as42_vli3_ipv4 { + description "AS42 - PCH DNS"; + local as routerasn; + source address routeraddress; + strict bind yes; + neighbor 10.1.0.36 as 42; + + ipv4 { + # As a route collector, we want to import everything and export nothing. + # The import filter listed here just accepts everything but adds tags. + import where f_import_as42(); + export none; + }; + + password "mcWsqMdzGwTKt67g"; +} + + +######################################################################################## +######################################################################################## +### +### AS112 - AS112 - VLAN Interface #4 + + + +function f_import_as112() -> bool + +prefix set allnet; +ip set allips; +int set allas; +{ + + + # From: api/v4/router/collector/bird2/import-pre-extra) + # This file can be skinned to add custom config to + # the fn_import function here. For example, INEX uses + # it to tag routes learnt from our own route servers + # with an information community and accept them as is. + + + # Filter small prefixes + if ( net ~ [ 0.0.0.0/0{25,32} ] ) then { + bgp_large_community.add( IXP_LC_FILTERED_PREFIX_LEN_TOO_LONG ); + } + + + if !(avoid_martians()) then { + bgp_large_community.add( IXP_LC_FILTERED_BOGON ); + } + + # Belt and braces: must have at least one ASN in the path + if( bgp_path.len < 1 ) then { + bgp_large_community.add( IXP_LC_FILTERED_AS_PATH_TOO_SHORT ); + # we won't continue in this case: zero path asn is broken + return true; + } + + # Peer ASN == route's first ASN? + if (bgp_path.first != 112 ) then { + bgp_large_community.add( IXP_LC_FILTERED_FIRST_AS_NOT_PEER_AS ); + } + + # set of all IPs this ASN uses to peer with on this VLAN + allips = [ 10.1.0.6 ]; + + # Prevent BGP NEXT_HOP Hijacking + if !( from = bgp_next_hop ) then { + + # need to differentiate between same ASN next hop or actual next hop hijacking + if( bgp_next_hop ~ allips ) then { + bgp_large_community.add( IXP_LC_INFO_SAME_AS_NEXT_HOP ); + } else { + # looks like hijacking (intentional or not) + bgp_large_community.add( IXP_LC_FILTERED_NEXT_HOP_NOT_PEER_IP ); + } + } + + # Filter Known Transit Networks + filter_has_transit_path(); + + # Belt and braces: no one needs an ASN path with > 64 hops, that's just broken + if( bgp_path.len > 64 ) then { + bgp_large_community.add( IXP_LC_FILTERED_AS_PATH_TOO_LONG ); + } + + + + allas = [ 112 + ]; + + + # Ensure origin ASN is in the neighbors AS-SET + if !(bgp_path.last_nonaggregated ~ allas) then { + bgp_large_community.add( IXP_LC_FILTERED_IRRDB_ORIGIN_AS_FILTERED ); + } + + + + + + # RPKI check + if( roa_check( t_roa, net, bgp_path.last_nonaggregated ) = ROA_INVALID ) then { + bgp_large_community.add( IXP_LC_FILTERED_RPKI_INVALID ); + } else if( roa_check( t_roa, net, bgp_path.last_nonaggregated ) = ROA_VALID ) then { + bgp_large_community.add( IXP_LC_INFO_RPKI_VALID ); + } else { + # RPKI unknown, keep checking and mark as unknown for info + bgp_large_community.add( IXP_LC_INFO_RPKI_UNKNOWN ); + } + + + + + + + allnet = [ 192.175.48.0/24 + ]; + + if ! (net ~ allnet) then { + if bgp_large_community ~ [IXP_LC_INFO_RPKI_VALID] then { + bgp_large_community.add( IXP_LC_INFO_IRRDB_INVALID ); + } else { + bgp_large_community.add( IXP_LC_FILTERED_IRRDB_PREFIX_FILTERED ); + } + + bgp_large_community.add( IXP_LC_INFO_IRRDB_FILTERED_STRICT ); + } else { + bgp_large_community.add( IXP_LC_INFO_IRRDB_VALID ); + } + + + + + + return true; +} + + + + +protocol bgp pb_as112_vli4_ipv4 { + description "AS112 - AS112"; + local as routerasn; + source address routeraddress; + strict bind yes; + neighbor 10.1.0.6 as 112; + + ipv4 { + # As a route collector, we want to import everything and export nothing. + # The import filter listed here just accepts everything but adds tags. + import where f_import_as112(); + export none; + }; + + password "w83fmGpRDtaKomQo"; +} + + +######################################################################################## +######################################################################################## +### +### AS1213 - HEAnet - VLAN Interface #1 + + + +function f_import_as1213() -> bool + +prefix set allnet; +ip set allips; +int set allas; +{ + + + # From: api/v4/router/collector/bird2/import-pre-extra) + # This file can be skinned to add custom config to + # the fn_import function here. For example, INEX uses + # it to tag routes learnt from our own route servers + # with an information community and accept them as is. + + + # Filter small prefixes + if ( net ~ [ 0.0.0.0/0{25,32} ] ) then { + bgp_large_community.add( IXP_LC_FILTERED_PREFIX_LEN_TOO_LONG ); + } + + + if !(avoid_martians()) then { + bgp_large_community.add( IXP_LC_FILTERED_BOGON ); + } + + # Belt and braces: must have at least one ASN in the path + if( bgp_path.len < 1 ) then { + bgp_large_community.add( IXP_LC_FILTERED_AS_PATH_TOO_SHORT ); + # we won't continue in this case: zero path asn is broken + return true; + } + + # Peer ASN == route's first ASN? + if (bgp_path.first != 1213 ) then { + bgp_large_community.add( IXP_LC_FILTERED_FIRST_AS_NOT_PEER_AS ); + } + + # set of all IPs this ASN uses to peer with on this VLAN + allips = [ 10.1.0.10 ]; + + # Prevent BGP NEXT_HOP Hijacking + if !( from = bgp_next_hop ) then { + + # need to differentiate between same ASN next hop or actual next hop hijacking + if( bgp_next_hop ~ allips ) then { + bgp_large_community.add( IXP_LC_INFO_SAME_AS_NEXT_HOP ); + } else { + # looks like hijacking (intentional or not) + bgp_large_community.add( IXP_LC_FILTERED_NEXT_HOP_NOT_PEER_IP ); + } + } + + # Filter Known Transit Networks + filter_has_transit_path(); + + # Belt and braces: no one needs an ASN path with > 64 hops, that's just broken + if( bgp_path.len > 64 ) then { + bgp_large_community.add( IXP_LC_FILTERED_AS_PATH_TOO_LONG ); + } + + + + allas = [ 112, 1213, 1921, 2128, 2850, 42310 + ]; + + + # Ensure origin ASN is in the neighbors AS-SET + if !(bgp_path.last_nonaggregated ~ allas) then { + bgp_large_community.add( IXP_LC_FILTERED_IRRDB_ORIGIN_AS_FILTERED ); + } + + + + + + # RPKI check + if( roa_check( t_roa, net, bgp_path.last_nonaggregated ) = ROA_INVALID ) then { + bgp_large_community.add( IXP_LC_FILTERED_RPKI_INVALID ); + } else if( roa_check( t_roa, net, bgp_path.last_nonaggregated ) = ROA_VALID ) then { + bgp_large_community.add( IXP_LC_INFO_RPKI_VALID ); + } else { + # RPKI unknown, keep checking and mark as unknown for info + bgp_large_community.add( IXP_LC_INFO_RPKI_UNKNOWN ); + } + + + + + + + allnet = [ 4.53.84.128/26, 4.53.146.192/26, 77.72.72.0/21, 87.32.0.0/12, + 91.123.224.0/20, 134.226.0.0/16, 136.201.0.0/16, 136.206.0.0/16, + 137.43.0.0/16, 140.203.0.0/16, 143.239.0.0/16, 147.252.0.0/16, + 149.153.0.0/16, 149.157.0.0/16, 157.190.0.0/16, 160.6.0.0/16, + 176.97.158.0/24, 192.174.68.0/24, 192.175.48.0/24, 193.1.0.0/16, + 193.242.111.0/24, 194.0.24.0/24, 194.0.25.0/24, 194.0.26.0/24, + 194.88.240.0/23, 212.3.242.128/26 + ]; + + if ! (net ~ allnet) then { + if bgp_large_community ~ [IXP_LC_INFO_RPKI_VALID] then { + bgp_large_community.add( IXP_LC_INFO_IRRDB_INVALID ); + } else { + bgp_large_community.add( IXP_LC_FILTERED_IRRDB_PREFIX_FILTERED ); + } + + bgp_large_community.add( IXP_LC_INFO_IRRDB_FILTERED_STRICT ); + } else { + bgp_large_community.add( IXP_LC_INFO_IRRDB_VALID ); + } + + + + + + return true; +} + + + + +protocol bgp pb_as1213_vli1_ipv4 { + description "AS1213 - HEAnet"; + local as routerasn; + source address routeraddress; + strict bind yes; + neighbor 10.1.0.10 as 1213; + + ipv4 { + # As a route collector, we want to import everything and export nothing. + # The import filter listed here just accepts everything but adds tags. + import where f_import_as1213(); + export none; + }; + + password "N7rX2SdfbRsyBLTm"; +} + + diff --git a/data/ci/known-good/ci-apiv4-b2-rc1-lan1-ipv6.conf b/data/ci/known-good/ci-apiv4-b2-rc1-lan1-ipv6.conf new file mode 100644 index 000000000..fddb63af0 --- /dev/null +++ b/data/ci/known-good/ci-apiv4-b2-rc1-lan1-ipv6.conf @@ -0,0 +1,495 @@ +# +# Bird v2 Route Collector configuration generated by IXP Manager +# +# Do not edit this file, it will be overwritten. +# +# Generated: 2019-03-09 15:47:45 +# + +# For VLAN: Peering LAN 1 (Tag: 1, Database ID: 1) + + +# standardise time formats: +timeformat base iso long; +timeformat log iso long; +timeformat protocol iso long; +timeformat route iso long; + + +log "/var/log/bird/b2-rc1-lan1-ipv6.log" all; +log syslog name "bird-b2-rc1-lan1-ipv6" all; + +define routerasn = 65500; +define routeraddress = 2001:db8::8; + +router id 192.0.2.8; + +# ignore interface up/down events +protocol device { } + + +# This function excludes weird networks +# rfc1918, class D, class E, too long and too short prefixes +function avoid_martians() -> bool +prefix set martians; +{ + + martians = [ + ::/0, # Default (can be advertised as a route in BGP to peers if desired) + ::/96, # IPv4-compatible IPv6 address - deprecated by RFC4291 + ::/128, # Unspecified address + ::1/128, # Local host loopback address + ::ffff:0.0.0.0/96+, # IPv4-mapped addresses + ::224.0.0.0/100+, # Compatible address (IPv4 format) + ::127.0.0.0/104+, # Compatible address (IPv4 format) + ::0.0.0.0/104+, # Compatible address (IPv4 format) + ::255.0.0.0/104+, # Compatible address (IPv4 format) + 0000::/8+, # Pool used for unspecified, loopback and embedded IPv4 addresses + 0200::/7+, # OSI NSAP-mapped prefix set (RFC4548) - deprecated by RFC4048 + 3ffe::/16+, # Former 6bone, now decommissioned + 2001:db8::/32+, # Reserved by IANA for special purposes and documentation + 2002:e000::/20+, # Invalid 6to4 packets (IPv4 multicast) + 2002:7f00::/24+, # Invalid 6to4 packets (IPv4 loopback) + 2002:0000::/24+, # Invalid 6to4 packets (IPv4 default) + 2002:ff00::/24+, # Invalid 6to4 packets + 2002:0a00::/24+, # Invalid 6to4 packets (IPv4 private 10.0.0.0/8 network) + 2002:ac10::/28+, # Invalid 6to4 packets (IPv4 private 172.16.0.0/12 network) + 2002:c0a8::/32+, # Invalid 6to4 packets (IPv4 private 192.168.0.0/16 network) + fc00::/7+, # Unicast Unique Local Addresses (ULA) - RFC 4193 + fe80::/10+, # Link-local Unicast + fec0::/10+, # Site-local Unicast - deprecated by RFC 3879 (replaced by ULA) + ff00::/8+ # Multicast + ]; + + + # Avoid RFC1918 and similar networks + if net ~ martians then + return false; + + return true; +} + + + + +## +## Route collector client configuration +## + + +######################################################################################## +######################################################################################## +# +# Community filtering definitions for use with looking glasses +# +# Current implementation based on: +# +# https://github.com/euro-ix/rs-workshop-july-2017/wiki/Route-Server-BGP-Community-usage +# +######################################################################################## +######################################################################################## + + + +# These will all be filtered and not piped to the master table: + +define IXP_LC_FILTERED_PREFIX_LEN_TOO_LONG = ( routerasn, 1101, 1 ); +define IXP_LC_FILTERED_PREFIX_LEN_TOO_SHORT = ( routerasn, 1101, 2 ); +define IXP_LC_FILTERED_BOGON = ( routerasn, 1101, 3 ); +define IXP_LC_FILTERED_BOGON_ASN = ( routerasn, 1101, 4 ); +define IXP_LC_FILTERED_AS_PATH_TOO_LONG = ( routerasn, 1101, 5 ); +define IXP_LC_FILTERED_AS_PATH_TOO_SHORT = ( routerasn, 1101, 6 ); +define IXP_LC_FILTERED_FIRST_AS_NOT_PEER_AS = ( routerasn, 1101, 7 ); +define IXP_LC_FILTERED_NEXT_HOP_NOT_PEER_IP = ( routerasn, 1101, 8 ); +define IXP_LC_FILTERED_IRRDB_PREFIX_FILTERED = ( routerasn, 1101, 9 ); +define IXP_LC_FILTERED_IRRDB_ORIGIN_AS_FILTERED = ( routerasn, 1101, 10 ); +define IXP_LC_FILTERED_PREFIX_NOT_IN_ORIGIN_AS = ( routerasn, 1101, 11 ); + +define IXP_LC_FILTERED_RPKI_UNKNOWN = ( routerasn, 1101, 12 ); +define IXP_LC_FILTERED_RPKI_INVALID = ( routerasn, 1101, 13 ); +define IXP_LC_FILTERED_TRANSIT_FREE_ASN = ( routerasn, 1101, 14 ); +define IXP_LC_FILTERED_TOO_MANY_COMMUNITIES = ( routerasn, 1101, 15 ); + + + + +# Informational prefixes + +define IXP_LC_INFO_RPKI_VALID = ( routerasn, 1000, 1 ); +define IXP_LC_INFO_RPKI_UNKNOWN = ( routerasn, 1000, 2 ); +define IXP_LC_INFO_RPKI_NOT_CHECKED = ( routerasn, 1000, 3 ); + +define IXP_LC_INFO_IRRDB_INVALID = ( routerasn, 1001, 0 ); +define IXP_LC_INFO_IRRDB_VALID = ( routerasn, 1001, 1 ); +define IXP_LC_INFO_IRRDB_NOT_CHECKED = ( routerasn, 1001, 2 ); +define IXP_LC_INFO_IRRDB_MORE_SPECIFIC = ( routerasn, 1001, 3 ); + +define IXP_LC_INFO_IRRDB_FILTERED_LOOSE = ( routerasn, 1001, 1000 ); +define IXP_LC_INFO_IRRDB_FILTERED_STRICT = ( routerasn, 1001, 1001 ); +define IXP_LC_INFO_IRRDB_PREFIX_EMPTY = ( routerasn, 1001, 1002 ); + +define IXP_LC_INFO_FROM_IXROUTESERVER = ( routerasn, 1001, 1100 ); + +define IXP_LC_INFO_SAME_AS_NEXT_HOP = ( routerasn, 1001, 1200 ); + + + + +######################################################################################## +######################################################################################## +# +# RPKI protocol configuration +# +######################################################################################## +######################################################################################## + + +roa6 table t_roa; + +protocol rpki rpki1 { + + roa6 { table t_roa; }; + + remote "10.39.5.123" port 3323; + + retry keep 90; + refresh keep 900; + expire keep 172800; +} + + +protocol rpki rpki2 { + + roa6 { table t_roa; }; + + remote "10.39.5.124" port 3323; + + retry keep 90; + refresh keep 900; + expire keep 172800; +} + + + + + +######################################################################################## +######################################################################################## +# +# Filter known transit networks +# +# Inspired by: http://bgpfilterguide.nlnog.net/guides/no_transit_leaks/ +# +######################################################################################## +######################################################################################## + + +define TRANSIT_ASNS = [ 174, 701, 1299, 2914, 3257, 3320, 3356, 3491, 4134, 5511, 6453, 6461, 6762, 6830, 7018 ]; + +function filter_has_transit_path() -> bool +int set transit_asns; +{ + transit_asns = TRANSIT_ASNS; + if (bgp_path ~ transit_asns) then { + bgp_large_community.add( IXP_LC_FILTERED_TRANSIT_FREE_ASN ); + return true; + } + + return false; +} + + + + +######################################################################################## +######################################################################################## +# +# Route collector clients +# +######################################################################################## +######################################################################################## + + +######################################################################################## +######################################################################################## +### +### AS1213 - HEAnet - VLAN Interface #1 + + + +function f_import_as1213() -> bool + +prefix set allnet; +ip set allips; +int set allas; +{ + + + # From: api/v4/router/collector/bird2/import-pre-extra) + # This file can be skinned to add custom config to + # the fn_import function here. For example, INEX uses + # it to tag routes learnt from our own route servers + # with an information community and accept them as is. + + + # Filter small prefixes + if ( net ~ [ ::/0{49,128} ] ) then { + bgp_large_community.add( IXP_LC_FILTERED_PREFIX_LEN_TOO_LONG ); + } + + + if !(avoid_martians()) then { + bgp_large_community.add( IXP_LC_FILTERED_BOGON ); + } + + # Belt and braces: must have at least one ASN in the path + if( bgp_path.len < 1 ) then { + bgp_large_community.add( IXP_LC_FILTERED_AS_PATH_TOO_SHORT ); + # we won't continue in this case: zero path asn is broken + return true; + } + + # Peer ASN == route's first ASN? + if (bgp_path.first != 1213 ) then { + bgp_large_community.add( IXP_LC_FILTERED_FIRST_AS_NOT_PEER_AS ); + } + + # set of all IPs this ASN uses to peer with on this VLAN + allips = [ 2001:db8:1::10 ]; + + # Prevent BGP NEXT_HOP Hijacking + if !( from = bgp_next_hop ) then { + + # need to differentiate between same ASN next hop or actual next hop hijacking + if( bgp_next_hop ~ allips ) then { + bgp_large_community.add( IXP_LC_INFO_SAME_AS_NEXT_HOP ); + } else { + # looks like hijacking (intentional or not) + bgp_large_community.add( IXP_LC_FILTERED_NEXT_HOP_NOT_PEER_IP ); + } + } + + # Filter Known Transit Networks + filter_has_transit_path(); + + # Belt and braces: no one needs an ASN path with > 64 hops, that's just broken + if( bgp_path.len > 64 ) then { + bgp_large_community.add( IXP_LC_FILTERED_AS_PATH_TOO_LONG ); + } + + + + allas = [ 112, 1213, 1921, 2128, 2850, 42310 + ]; + + + # Ensure origin ASN is in the neighbors AS-SET + if !(bgp_path.last_nonaggregated ~ allas) then { + bgp_large_community.add( IXP_LC_FILTERED_IRRDB_ORIGIN_AS_FILTERED ); + } + + + + + + # RPKI check + if( roa_check( t_roa, net, bgp_path.last_nonaggregated ) = ROA_INVALID ) then { + bgp_large_community.add( IXP_LC_FILTERED_RPKI_INVALID ); + } else if( roa_check( t_roa, net, bgp_path.last_nonaggregated ) = ROA_VALID ) then { + bgp_large_community.add( IXP_LC_INFO_RPKI_VALID ); + } else { + # RPKI unknown, keep checking and mark as unknown for info + bgp_large_community.add( IXP_LC_INFO_RPKI_UNKNOWN ); + } + + + + + + + allnet = [ 2001:678:20::/48, 2001:678:24::/48, 2001:67c:1bc::/48, 2001:67c:10b8::/48, + 2001:67c:10e0::/48, 2001:770::/32, 2001:7f8:18::/48, 2001:1900:2205::/48, + 2001:1900:2206::/48, 2620:4f:8000::/48, 2a01:4b0::/32 + ]; + + if ! (net ~ allnet) then { + if bgp_large_community ~ [IXP_LC_INFO_RPKI_VALID] then { + bgp_large_community.add( IXP_LC_INFO_IRRDB_INVALID ); + } else { + bgp_large_community.add( IXP_LC_FILTERED_IRRDB_PREFIX_FILTERED ); + } + + bgp_large_community.add( IXP_LC_INFO_IRRDB_FILTERED_STRICT ); + } else { + bgp_large_community.add( IXP_LC_INFO_IRRDB_VALID ); + } + + + + + + return true; +} + + + + +protocol bgp pb_as1213_vli1_ipv6 { + description "AS1213 - HEAnet"; + local as routerasn; + source address routeraddress; + strict bind yes; + neighbor 2001:db8:1::10 as 1213; + + ipv6 { + # As a route collector, we want to import everything and export nothing. + # The import filter listed here just accepts everything but adds tags. + import where f_import_as1213(); + export none; + }; + + password "N7rX2SdfbRsyBLTm"; +} + + +######################################################################################## +######################################################################################## +### +### AS25441 - Imagine - VLAN Interface #6 + + + +function f_import_as25441() -> bool + +prefix set allnet; +ip set allips; +int set allas; +{ + + + # From: api/v4/router/collector/bird2/import-pre-extra) + # This file can be skinned to add custom config to + # the fn_import function here. For example, INEX uses + # it to tag routes learnt from our own route servers + # with an information community and accept them as is. + + + # Filter small prefixes + if ( net ~ [ ::/0{49,128} ] ) then { + bgp_large_community.add( IXP_LC_FILTERED_PREFIX_LEN_TOO_LONG ); + } + + + if !(avoid_martians()) then { + bgp_large_community.add( IXP_LC_FILTERED_BOGON ); + } + + # Belt and braces: must have at least one ASN in the path + if( bgp_path.len < 1 ) then { + bgp_large_community.add( IXP_LC_FILTERED_AS_PATH_TOO_SHORT ); + # we won't continue in this case: zero path asn is broken + return true; + } + + # Peer ASN == route's first ASN? + if (bgp_path.first != 25441 ) then { + bgp_large_community.add( IXP_LC_FILTERED_FIRST_AS_NOT_PEER_AS ); + } + + # set of all IPs this ASN uses to peer with on this VLAN + allips = [ 2001:db8:1::8 ]; + + # Prevent BGP NEXT_HOP Hijacking + if !( from = bgp_next_hop ) then { + + # need to differentiate between same ASN next hop or actual next hop hijacking + if( bgp_next_hop ~ allips ) then { + bgp_large_community.add( IXP_LC_INFO_SAME_AS_NEXT_HOP ); + } else { + # looks like hijacking (intentional or not) + bgp_large_community.add( IXP_LC_FILTERED_NEXT_HOP_NOT_PEER_IP ); + } + } + + # Filter Known Transit Networks + filter_has_transit_path(); + + # Belt and braces: no one needs an ASN path with > 64 hops, that's just broken + if( bgp_path.len > 64 ) then { + bgp_large_community.add( IXP_LC_FILTERED_AS_PATH_TOO_LONG ); + } + + + + allas = [ 11521, 25441, 34317, 35272, 39064, 43178, 43610, 47615, 48342, 49573, + 197853, 197904 + ]; + + + # Ensure origin ASN is in the neighbors AS-SET + if !(bgp_path.last_nonaggregated ~ allas) then { + bgp_large_community.add( IXP_LC_FILTERED_IRRDB_ORIGIN_AS_FILTERED ); + } + + + + + + # RPKI check + if( roa_check( t_roa, net, bgp_path.last_nonaggregated ) = ROA_INVALID ) then { + bgp_large_community.add( IXP_LC_FILTERED_RPKI_INVALID ); + } else if( roa_check( t_roa, net, bgp_path.last_nonaggregated ) = ROA_VALID ) then { + bgp_large_community.add( IXP_LC_INFO_RPKI_VALID ); + } else { + # RPKI unknown, keep checking and mark as unknown for info + bgp_large_community.add( IXP_LC_INFO_RPKI_UNKNOWN ); + } + + + + + + + allnet = [ 2001:67c:338::/48, 2001:4d68::/32{32,48}, 2a01:268::/32{32,48}, 2a01:8f80::/32{32,48} + ]; + + if ! (net ~ allnet) then { + if bgp_large_community ~ [IXP_LC_INFO_RPKI_VALID] then { + bgp_large_community.add( IXP_LC_INFO_IRRDB_INVALID ); + } else { + bgp_large_community.add( IXP_LC_FILTERED_IRRDB_PREFIX_FILTERED ); + } + + bgp_large_community.add( IXP_LC_INFO_IRRDB_FILTERED_LOOSE ); + } else { + bgp_large_community.add( IXP_LC_INFO_IRRDB_VALID ); + } + + + + + + return true; +} + + + + +protocol bgp pb_as25441_vli6_ipv6 { + description "AS25441 - Imagine"; + local as routerasn; + source address routeraddress; + strict bind yes; + neighbor 2001:db8:1::8 as 25441; + + ipv6 { + # As a route collector, we want to import everything and export nothing. + # The import filter listed here just accepts everything but adds tags. + import where f_import_as25441(); + export none; + }; + + password "X8Ks9QnbER9cyzU3"; +} + + diff --git a/data/ci/known-good/ci-apiv4-b2-rs1-lan1-ipv4.conf b/data/ci/known-good/ci-apiv4-b2-rs1-lan1-ipv4.conf new file mode 100644 index 000000000..edd043e46 --- /dev/null +++ b/data/ci/known-good/ci-apiv4-b2-rs1-lan1-ipv4.conf @@ -0,0 +1,940 @@ + +# +# Bird v2 Route Server configuration generated by IXP Manager +# +# Do not edit this file, it will be overwritten. Please see: +# +# https://github.com/inex/IXP-Manager/wiki/Route-Server +# +# Generated: 2019-03-09 15:41:31 +# + +# For VLAN: Peering LAN 1 (Tag: 1, Database ID: 1) + +# standardise time formats: +timeformat base iso long; +timeformat log iso long; +timeformat protocol iso long; +timeformat route iso long; + + +log "/var/log/bird/b2-rs1-lan1-ipv4.log" all; +log syslog name "bird-b2-rs1-lan1-ipv4" all; + +define routeserverasn = 65501; +define routeserveraddress = 192.0.2.18; + + +router id 192.0.2.18; + +# ignore interface up/down events +protocol device { } + +# This function excludes weird networks +# rfc1918, class D, class E, too long and too short prefixes +function avoid_martians() +prefix set martians; +{ + + martians = [ + 0.0.0.0/32-, # rfc5735 Special Use IPv4 Addresses + 0.0.0.0/0{0,7}, # rfc1122 Requirements for Internet Hosts -- Communication Layers 3.2.1.3 + 10.0.0.0/8+, # rfc1918 Address Allocation for Private Internets + 100.64.0.0/10+, # rfc6598 IANA-Reserved IPv4 Prefix for Shared Address Space + 127.0.0.0/8+, # rfc1122 Requirements for Internet Hosts -- Communication Layers 3.2.1.3 + 169.254.0.0/16+, # rfc3927 Dynamic Configuration of IPv4 Link-Local Addresses + 172.16.0.0/12+, # rfc1918 Address Allocation for Private Internets + 192.0.0.0/24+, # rfc6890 Special-Purpose Address Registries + 192.0.2.0/24+, # rfc5737 IPv4 Address Blocks Reserved for Documentation + 192.168.0.0/16+, # rfc1918 Address Allocation for Private Internets + 198.18.0.0/15+, # rfc2544 Benchmarking Methodology for Network Interconnect Devices + 198.51.100.0/24+, # rfc5737 IPv4 Address Blocks Reserved for Documentation + 203.0.113.0/24+, # rfc5737 IPv4 Address Blocks Reserved for Documentation + 224.0.0.0/4+, # rfc1112 Host Extensions for IP Multicasting + 240.0.0.0/4+ # rfc6890 Special-Purpose Address Registries + ]; + + + # Avoid RFC1918 and similar networks + if net ~ martians then + return false; + + return true; +} + + +######################################################################################## +######################################################################################## +# +# Community filtering definitions for use with looking glasses +# +# Current implementation based on: +# +# https://github.com/euro-ix/rs-workshop-july-2017/wiki/Route-Server-BGP-Community-usage +# +######################################################################################## +######################################################################################## + + + +# These will all be filtered and not piped to the master table: + +define IXP_LC_FILTERED_PREFIX_LEN_TOO_LONG = ( routeserverasn, 1101, 1 ); +define IXP_LC_FILTERED_PREFIX_LEN_TOO_SHORT = ( routeserverasn, 1101, 2 ); +define IXP_LC_FILTERED_BOGON = ( routeserverasn, 1101, 3 ); +define IXP_LC_FILTERED_BOGON_ASN = ( routeserverasn, 1101, 4 ); +define IXP_LC_FILTERED_AS_PATH_TOO_LONG = ( routeserverasn, 1101, 5 ); +define IXP_LC_FILTERED_AS_PATH_TOO_SHORT = ( routeserverasn, 1101, 6 ); +define IXP_LC_FILTERED_FIRST_AS_NOT_PEER_AS = ( routeserverasn, 1101, 7 ); +define IXP_LC_FILTERED_NEXT_HOP_NOT_PEER_IP = ( routeserverasn, 1101, 8 ); +define IXP_LC_FILTERED_IRRDB_PREFIX_FILTERED = ( routeserverasn, 1101, 9 ); +define IXP_LC_FILTERED_IRRDB_ORIGIN_AS_FILTERED = ( routeserverasn, 1101, 10 ); +define IXP_LC_FILTERED_PREFIX_NOT_IN_ORIGIN_AS = ( routeserverasn, 1101, 11 ); + +define IXP_LC_FILTERED_RPKI_UNKNOWN = ( routeserverasn, 1101, 12 ); +define IXP_LC_FILTERED_RPKI_INVALID = ( routeserverasn, 1101, 13 ); +define IXP_LC_FILTERED_TRANSIT_FREE_ASN = ( routeserverasn, 1101, 14 ); +define IXP_LC_FILTERED_TOO_MANY_COMMUNITIES = ( routeserverasn, 1101, 15 ); + + + + +# Informational prefixes + +define IXP_LC_INFO_RPKI_VALID = ( routeserverasn, 1000, 1 ); +define IXP_LC_INFO_RPKI_UNKNOWN = ( routeserverasn, 1000, 2 ); +define IXP_LC_INFO_RPKI_NOT_CHECKED = ( routeserverasn, 1000, 3 ); + +define IXP_LC_INFO_IRRDB_VALID = ( routeserverasn, 1001, 1 ); +define IXP_LC_INFO_IRRDB_NOT_CHECKED = ( routeserverasn, 1001, 2 ); +define IXP_LC_INFO_IRRDB_MORE_SPECIFIC = ( routeserverasn, 1001, 3 ); + +define IXP_LC_INFO_IRRDB_FILTERED_LOOSE = ( routeserverasn, 1001, 1000 ); +define IXP_LC_INFO_IRRDB_FILTERED_STRICT = ( routeserverasn, 1001, 1001 ); +define IXP_LC_INFO_IRRDB_PREFIX_EMPTY = ( routeserverasn, 1001, 1002 ); + +define IXP_LC_INFO_SAME_AS_NEXT_HOP = ( routeserverasn, 1001, 1200 ); + +# ( routeserverasn, 1010, peerasn ) -> route learnt from peerasn via routeserverasn +# ( routeserverasn, 1011, originasn ) -> route origin asn via routeserverasn + + +# And the filter for examining routes in the peers import table being exported +# to the master table + +filter f_export_to_master +{ + + if bgp_large_community ~ [( routeserverasn, 1101, * )] then reject; + + accept; +} + + + + + + +######################################################################################## +######################################################################################## +# +# Standard IXP community filter +# +######################################################################################## +######################################################################################## + + +function ixp_community_filter(int peerasn) +{ + if !(source = RTS_BGP) then + return false; + + # AS path prepending + if (routeserverasn, 103, peerasn) ~ bgp_large_community then { + bgp_path.prepend( bgp_path.first ); + bgp_path.prepend( bgp_path.first ); + bgp_path.prepend( bgp_path.first ); + } else if (routeserverasn, 102, peerasn) ~ bgp_large_community then { + bgp_path.prepend( bgp_path.first ); + bgp_path.prepend( bgp_path.first ); + } else if (routeserverasn, 101, peerasn) ~ bgp_large_community then { + bgp_path.prepend( bgp_path.first ); + } else if (routeserverasn, 103, 0) ~ bgp_large_community then { + bgp_path.prepend( bgp_path.first ); + bgp_path.prepend( bgp_path.first ); + bgp_path.prepend( bgp_path.first ); + } else if (routeserverasn, 102, 0) ~ bgp_large_community then { + bgp_path.prepend( bgp_path.first ); + bgp_path.prepend( bgp_path.first ); + } else if (routeserverasn, 101, 0) ~ bgp_large_community then { + bgp_path.prepend( bgp_path.first ); + } + + + # support for BGP Large Communities + if (routeserverasn, 0, peerasn) ~ bgp_large_community then + return false; + if (routeserverasn, 1, peerasn) ~ bgp_large_community then + return true; + if (routeserverasn, 0, 0) ~ bgp_large_community then + return false; + if (routeserverasn, 1, 0) ~ bgp_large_community then + return true; + + # it's unwise to conduct a 32-bit check on a 16-bit value + if routeserverasn > 65535 || peerasn > 65535 then + return true; + + # Implement widely used community filtering schema. + if (0, peerasn) ~ bgp_community then + return false; + if (routeserverasn, peerasn) ~ bgp_community then + return true; + if (0, routeserverasn) ~ bgp_community then + return false; + + return true; +} + + + + +######################################################################################## +######################################################################################## +# +# RPKI protocol configuration +# +######################################################################################## +######################################################################################## + + +roa4 table t_roa; + +protocol rpki rpki1 { + + roa4 { table t_roa; }; + + remote "10.39.5.123" port 3323; + + retry keep 90; + refresh keep 900; + expire keep 172800; +} + + +protocol rpki rpki2 { + + roa4 { table t_roa; }; + + remote "10.39.5.124" port 3323; + + retry keep 90; + refresh keep 900; + expire keep 172800; +} + + +/* + * RPKI check for the path + * + * return: true means the filter should stop processing, false means keep processing + */ +function filter_rpki() +{ + # RPKI check + if( roa_check( t_roa, net, bgp_path.last_nonaggregated ) = ROA_INVALID ) then { + print "Tagging invalid ROA ", net, " for ASN ", bgp_path.last; + bgp_large_community.add( IXP_LC_FILTERED_RPKI_INVALID ); + return true; + } + + if( roa_check( t_roa, net, bgp_path.last_nonaggregated ) = ROA_VALID ) then { + bgp_large_community.add( IXP_LC_INFO_RPKI_VALID ); + return true; + } + + # RPKI unknown, keep checking and mark as unknown for info + bgp_large_community.add( IXP_LC_INFO_RPKI_UNKNOWN ); + + return false; +} + + + + + +######################################################################################## +######################################################################################## +# +# Filter known transit networks +# +# Inspired by: http://bgpfilterguide.nlnog.net/guides/no_transit_leaks/ +# +######################################################################################## +######################################################################################## + + +define TRANSIT_ASNS = [ 174, 701, 1299, 2914, 3257, 3320, 3356, 3491, 4134, 5511, 6453, 6461, 6762, 6830, 7018 ]; + +function filter_has_transit_path() +int set transit_asns; +{ + transit_asns = TRANSIT_ASNS; + if (bgp_path ~ transit_asns) then { + bgp_large_community.add( IXP_LC_FILTERED_TRANSIT_FREE_ASN ); + return true; + } + + return false; +} + + +######################################################################################## +######################################################################################## +# +# Route Server client configuration +# +######################################################################################## +######################################################################################## + + +template bgp tb_rsclient { + local as routeserverasn; + source address routeserveraddress; + strict bind yes; + + # give RPKI-RTR a chance to start and populate + # (RPKI is /really/ quick) + connect delay time 30; + + ipv4 { + export all; + }; + + rs client; +} + + + + +######################################################################################## +######################################################################################## +# +# Route server clients +# +######################################################################################## +######################################################################################## + + +######################################################################################## +######################################################################################## +### +### AS42 - PCH DNS - VLAN Interface #3 + +ipv4 table t_0003_as42; + + + +filter f_import_as42 +prefix set allnet; +ip set allips; +int set allas; +{ + + + # Filter small prefixes + if ( net ~ [ 0.0.0.0/0{25,32} ] ) then { + bgp_large_community.add( IXP_LC_FILTERED_PREFIX_LEN_TOO_LONG ); + accept; + } + + + if !(avoid_martians()) then { + bgp_large_community.add( IXP_LC_FILTERED_BOGON ); + accept; + } + + # Belt and braces: must have at least one ASN in the path + if( bgp_path.len < 1 ) then { + bgp_large_community.add( IXP_LC_FILTERED_AS_PATH_TOO_SHORT ); + accept; + } + + # Peer ASN == route's first ASN? + if (bgp_path.first != 42 ) then { + bgp_large_community.add( IXP_LC_FILTERED_FIRST_AS_NOT_PEER_AS ); + accept; + } + + # set of all IPs this ASN uses to peer with on this VLAN + allips = [ 10.1.0.36 ]; + + # Prevent BGP NEXT_HOP Hijacking + if !( from = bgp_next_hop ) then { + + # need to differentiate between same ASN next hop or actual next hop hijacking + if( bgp_next_hop ~ allips ) then { + bgp_large_community.add( IXP_LC_INFO_SAME_AS_NEXT_HOP ); + } else { + # looks like hijacking (intentional or not) + bgp_large_community.add( IXP_LC_FILTERED_NEXT_HOP_NOT_PEER_IP ); + accept; + } + } + + + # Filter Known Transit Networks + if filter_has_transit_path() then accept; + + # Belt and braces: no one needs an ASN path with > 64 hops, that's just broken + if( bgp_path.len > 64 ) then { + bgp_large_community.add( IXP_LC_FILTERED_AS_PATH_TOO_LONG ); + accept; + } + + + + allas = [ 27, 42, 187, 297, 715, 3856, 7251, 13202, 16327, 16668, + 16686, 20144, 20539, 21312, 24999, 27678, 32978, 32979, 35160, 38052, + 44876, 45170, 45494, 48582, 48892, 50843, 51874, 52234, 52306, 54145, + 59464, 60313, 197058 + ]; + + + # Ensure origin ASN is in the neighbors AS-SET + if !(bgp_path.last_nonaggregated ~ allas) then { + bgp_large_community.add( IXP_LC_FILTERED_IRRDB_ORIGIN_AS_FILTERED ); + accept; + } + + + + # RPKI test - if it's INVALID or VALID, we are done + if filter_rpki() then accept; + + + + + allnet = [ 31.135.128.0/19, 31.135.128.0/21, 31.135.136.0/21, 31.135.144.0/22, + 31.135.148.0/22, 31.135.152.0/22, 31.135.152.0/23, 31.135.154.0/23, + 36.0.4.0/22, 63.246.32.0/20, 64.68.192.0/20, 64.68.192.0/24, + 64.68.193.0/24, 64.68.194.0/24, 64.68.195.0/24, 64.68.196.0/24, + 64.78.200.0/21, 64.185.240.0/20, 65.22.4.0/24, 65.22.5.0/24, + 65.22.19.0/24, 65.22.23.0/24, 65.22.27.0/24, 65.22.31.0/24, + 65.22.35.0/24, 65.22.39.0/24, 65.22.47.0/24, 65.22.51.0/24, + 65.22.55.0/24, 65.22.59.0/24, 65.22.63.0/24, 65.22.67.0/24, + 65.22.71.0/24, 65.22.79.0/24, 65.22.83.0/24, 65.22.87.0/24, + 65.22.91.0/24, 65.22.95.0/24, 65.22.99.0/24, 65.22.103.0/24, + 65.22.107.0/24, 65.22.111.0/24, 65.22.115.0/24, 65.22.119.0/24, + 65.22.123.0/24, 65.22.127.0/24, 65.22.131.0/24, 65.22.135.0/24, + 65.22.139.0/24, 65.22.143.0/24, 65.22.147.0/24, 65.22.151.0/24, + 65.22.155.0/24, 65.22.159.0/24, 65.22.163.0/24, 65.22.171.0/24, + 65.22.175.0/24, 65.22.179.0/24, 65.22.183.0/24, 65.22.187.0/24, + 65.22.191.0/24, 65.22.195.0/24, 65.22.199.0/24, 65.22.203.0/24, + 65.22.207.0/24, 65.22.211.0/24, 65.22.215.0/24, 65.22.219.0/24, + 65.22.223.0/24, 65.22.227.0/24, 65.22.231.0/24, 65.22.235.0/24, + 65.22.239.0/24, 65.22.243.0/24, 65.22.247.0/24, 66.96.112.0/20, + 66.102.32.0/20, 66.175.104.0/24, 66.185.112.0/20, 66.225.199.0/24, + 66.225.200.0/24, 66.225.201.0/24, 67.21.37.0/24, 67.22.112.0/21, + 67.158.48.0/20, 68.65.112.0/21, 68.65.126.0/23, 68.65.126.0/24, + 68.65.127.0/24, 69.166.10.0/24, 69.166.12.0/22, 70.40.0.0/21, + 70.40.8.0/21, 72.0.48.0/20, 72.0.48.0/24, 72.0.49.0/24, + 72.0.50.0/24, 72.0.51.0/24, 72.0.52.0/24, 72.0.53.0/24, + 72.0.54.0/24, 72.0.55.0/24, 72.0.56.0/24, 72.0.57.0/24, + 72.0.58.0/24, 72.0.59.0/24, 72.0.60.0/24, 72.0.61.0/24, + 72.0.62.0/24, 72.0.63.0/24, 72.42.112.0/20, 72.42.112.0/24, + 72.42.113.0/24, 72.42.114.0/24, 72.42.115.0/24, 72.42.116.0/24, + 72.42.117.0/24, 72.42.118.0/24, 72.42.119.0/24, 72.42.120.0/24, + 72.42.121.0/24, 72.42.122.0/24, 72.42.123.0/24, 72.42.124.0/24, + 72.42.125.0/24, 72.42.126.0/24, 72.42.127.0/24, 74.63.16.0/20, + 74.63.16.0/24, 74.63.17.0/24, 74.63.18.0/24, 74.63.19.0/24, + 74.63.20.0/24, 74.63.21.0/24, 74.63.22.0/24, 74.63.23.0/24, + 74.63.24.0/24, 74.63.25.0/24, 74.63.26.0/24, 74.63.27.0/24, + 74.80.64.0/18, 74.80.64.0/24, 74.80.65.0/24, 74.80.66.0/24, + 74.80.67.0/24, 74.80.68.0/24, 74.80.69.0/24, 74.80.70.0/24, + 74.80.71.0/24, 74.80.72.0/24, 74.80.73.0/24, 74.80.74.0/24, + 74.80.75.0/24, 74.80.76.0/24, 74.80.77.0/24, 74.80.78.0/24, + 74.80.79.0/24, 74.80.80.0/24, 74.80.81.0/24, 74.80.82.0/24, + 74.80.83.0/24, 74.80.84.0/24, 74.80.85.0/24, 74.80.86.0/24, + 74.80.87.0/24, 74.80.88.0/24, 74.80.89.0/24, 74.80.90.0/24, + 74.80.91.0/24, 74.80.92.0/24, 74.80.93.0/24, 74.80.94.0/24, + 74.80.95.0/24, 74.80.96.0/24, 74.80.97.0/24, 74.80.98.0/24, + 74.80.99.0/24, 74.80.100.0/24, 74.80.101.0/24, 74.80.102.0/24, + 74.80.103.0/24, 74.80.104.0/24, 74.80.105.0/24, 74.80.106.0/24, + 74.80.107.0/24, 74.80.108.0/24, 74.80.109.0/24, 74.80.110.0/24, + 74.80.111.0/24, 74.80.112.0/24, 74.80.113.0/24, 74.80.114.0/24, + 74.80.115.0/24, 74.80.116.0/24, 74.80.117.0/24, 74.80.118.0/24, + 74.80.119.0/24, 74.80.120.0/24, 74.80.121.0/24, 74.80.122.0/24, + 74.80.123.0/24, 74.80.124.0/24, 74.80.125.0/24, 74.80.126.0/23, + 74.80.126.0/24, 74.80.127.0/24, 74.118.212.0/24, 74.118.213.0/24, + 74.118.214.0/23, 75.127.16.0/20, 76.191.16.0/20, 89.19.120.0/21, + 89.19.120.0/22, 89.19.124.0/23, 89.19.126.0/23, 91.201.224.0/22, + 91.201.224.0/23, 91.201.224.0/24, 91.201.225.0/24, 91.201.226.0/23, + 91.201.226.0/24, 91.201.227.0/24, 91.209.1.0/24, 91.209.193.0/24, + 91.222.16.0/22, 91.222.40.0/24, 91.222.41.0/24, 91.222.42.0/24, + 91.222.43.0/24, 91.241.93.0/24, 93.95.24.0/21, 93.95.24.0/24, + 93.95.25.0/24, 93.95.26.0/24, 93.171.128.0/22, 95.47.163.0/24, + 101.251.4.0/22, 114.69.222.0/24, 128.8.0.0/16, 128.161.0.0/16, + 129.2.0.0/16, 130.135.0.0/16, 130.167.0.0/16, 131.161.128.0/18, + 131.182.0.0/16, 139.229.0.0/16, 140.169.0.0/16, 146.5.0.0/16, + 146.58.0.0/16, 150.144.0.0/16, 156.154.43.0/24, 156.154.50.0/24, + 156.154.59.0/24, 156.154.96.0/24, 156.154.99.0/24, 158.154.0.0/16, + 169.222.0.0/24, 183.91.132.0/22, 192.5.41.0/24, 192.12.123.0/24, + 192.42.70.0/24, 192.58.36.0/24, 192.67.83.0/24, 192.67.107.0/24, + 192.67.108.0/24, 192.68.52.0/24, 192.68.148.0/24, 192.68.162.0/24, + 192.70.244.0/24, 192.70.249.0/24, 192.77.80.0/24, 192.84.8.0/24, + 192.88.124.0/24, 192.92.65.0/24, 192.92.90.0/24, 192.100.9.0/24, + 192.100.10.0/24, 192.100.15.0/24, 192.101.148.0/24, 192.102.15.0/24, + 192.102.219.0/24, 192.102.233.0/24, 192.102.234.0/24, 192.112.18.0/24, + 192.112.223.0/24, 192.112.224.0/24, 192.124.20.0/24, 192.138.101.0/24, + 192.138.172.0/24, 192.149.89.0/24, 192.149.104.0/24, 192.149.107.0/24, + 192.149.133.0/24, 192.150.32.0/21, 192.153.157.0/24, 192.188.4.0/24, + 192.203.230.0/24, 192.225.64.0/19, 192.243.0.0/20, 192.243.16.0/21, + 193.29.206.0/24, 193.110.16.0/22, 193.110.16.0/23, 193.110.18.0/23, + 193.111.240.0/22, 193.178.228.0/23, 193.178.228.0/24, 193.178.229.0/24, + 194.0.12.0/24, 194.0.13.0/24, 194.0.14.0/24, 194.0.17.0/24, + 194.0.27.0/24, 194.0.36.0/24, 194.0.42.0/24, 194.0.47.0/24, + 194.28.144.0/22, 194.117.58.0/24, 194.117.60.0/24, 194.117.61.0/24, + 194.117.62.0/24, 194.117.63.0/24, 194.146.180.0/22, 194.146.180.0/23, + 194.146.180.0/24, 194.146.181.0/24, 194.146.182.0/23, 194.146.182.0/24, + 194.146.183.0/24, 194.146.228.0/22, 194.146.228.0/23, 194.146.228.0/24, + 194.146.229.0/24, 194.146.230.0/23, 194.146.230.0/24, 194.146.231.0/24, + 194.153.148.0/23, 195.64.162.0/23, 195.64.162.0/24, 195.64.163.0/24, + 195.82.138.0/23, 198.9.0.0/16, 198.49.1.0/24, 198.116.0.0/14, + 198.120.0.0/14, 198.182.28.0/24, 198.182.31.0/24, 198.182.167.0/24, + 199.4.137.0/24, 199.7.64.0/24, 199.7.77.0/24, 199.7.83.0/24, + 199.7.86.0/24, 199.7.91.0/24, 199.7.94.0/24, 199.7.95.0/24, + 199.43.132.0/24, 199.115.156.0/24, 199.115.157.0/24, 199.120.141.0/24, + 199.120.142.0/23, 199.120.144.0/24, 199.182.32.0/24, 199.182.40.0/24, + 199.184.181.0/24, 199.184.182.0/23, 199.184.184.0/24, 199.249.112.0/24, + 199.249.113.0/24, 199.249.114.0/24, 199.249.115.0/24, 199.249.116.0/24, + 199.249.117.0/24, 199.249.118.0/24, 199.249.119.0/24, 199.249.120.0/24, + 199.249.121.0/24, 199.249.122.0/24, 199.249.123.0/24, 199.249.124.0/24, + 199.249.125.0/24, 199.249.126.0/24, 199.249.127.0/24, 199.254.171.0/24, + 200.1.121.0/24, 200.1.131.0/24, 200.7.4.0/24, 200.16.98.0/24, + 202.6.102.0/24, 202.7.4.0/22, 202.52.0.0/23, 202.53.186.0/24, + 202.53.191.0/24, 203.119.88.0/23, 204.14.112.0/21, 204.19.119.0/24, + 204.26.57.0/24, 204.61.208.0/21, 204.61.208.0/22, 204.61.208.0/23, + 204.61.210.0/23, 204.61.210.0/24, 204.61.212.0/23, 204.61.216.0/23, + 204.194.22.0/23, 204.194.22.0/24, 204.194.23.0/24, 205.132.46.0/23, + 205.207.155.0/24, 206.51.254.0/23, 206.108.113.0/24, 206.196.160.0/19, + 206.220.228.0/22, 206.220.228.0/23, 206.220.230.0/23, 206.223.122.0/24, + 207.34.5.0/24, 207.34.6.0/23, 208.15.19.0/24, 208.49.115.64/27, + 208.67.88.0/22, 216.21.2.0/23 + ]; + + if ! (net ~ allnet) then { + bgp_large_community.add( IXP_LC_FILTERED_IRRDB_PREFIX_FILTERED ); + bgp_large_community.add( IXP_LC_INFO_IRRDB_FILTERED_STRICT ); + accept; + } else { + bgp_large_community.add( IXP_LC_INFO_IRRDB_VALID ); + } + + + + accept; +} + + +# The route server export filter exists as the export gateway on the BGP protocol. +# +# Remember that standard IXP community filtering has already happened on the +# master -> bgp protocol pipe. + +filter f_export_as42{ + + + + # we should strip our own communities which we used for the looking glass and filtering + bgp_large_community.delete( [( routeserverasn, *, * )] ); + bgp_community.delete( [( routeserverasn, * )] ); + + # default position is to accept: + accept; + +} + + + + + + + +protocol pipe pp_0003_as42 { + description "Pipe for AS42 - PCH DNS - VLAN Interface 3"; + table master4; + peer table t_0003_as42; + import filter f_export_to_master; + export where ixp_community_filter(42); +} + +protocol bgp pb_0003_as42 from tb_rsclient { + description "AS42 - PCH DNS"; + neighbor 10.1.0.36 as 42; + ipv4 { + import limit 2000 action restart; + import filter f_import_as42; + table t_0003_as42; + export filter f_export_as42; + }; + password "mcWsqMdzGwTKt67g"; +} + + +######################################################################################## +######################################################################################## +### +### AS112 - AS112 - VLAN Interface #4 + +ipv4 table t_0004_as112; + + + +filter f_import_as112 +prefix set allnet; +ip set allips; +int set allas; +{ + + + # Filter small prefixes + if ( net ~ [ 0.0.0.0/0{25,32} ] ) then { + bgp_large_community.add( IXP_LC_FILTERED_PREFIX_LEN_TOO_LONG ); + accept; + } + + + if !(avoid_martians()) then { + bgp_large_community.add( IXP_LC_FILTERED_BOGON ); + accept; + } + + # Belt and braces: must have at least one ASN in the path + if( bgp_path.len < 1 ) then { + bgp_large_community.add( IXP_LC_FILTERED_AS_PATH_TOO_SHORT ); + accept; + } + + # Peer ASN == route's first ASN? + if (bgp_path.first != 112 ) then { + bgp_large_community.add( IXP_LC_FILTERED_FIRST_AS_NOT_PEER_AS ); + accept; + } + + # set of all IPs this ASN uses to peer with on this VLAN + allips = [ 10.1.0.6 ]; + + # Prevent BGP NEXT_HOP Hijacking + if !( from = bgp_next_hop ) then { + + # need to differentiate between same ASN next hop or actual next hop hijacking + if( bgp_next_hop ~ allips ) then { + bgp_large_community.add( IXP_LC_INFO_SAME_AS_NEXT_HOP ); + } else { + # looks like hijacking (intentional or not) + bgp_large_community.add( IXP_LC_FILTERED_NEXT_HOP_NOT_PEER_IP ); + accept; + } + } + + + # Filter Known Transit Networks + if filter_has_transit_path() then accept; + + # Belt and braces: no one needs an ASN path with > 64 hops, that's just broken + if( bgp_path.len > 64 ) then { + bgp_large_community.add( IXP_LC_FILTERED_AS_PATH_TOO_LONG ); + accept; + } + + + + allas = [ 112 + ]; + + + # Ensure origin ASN is in the neighbors AS-SET + if !(bgp_path.last_nonaggregated ~ allas) then { + bgp_large_community.add( IXP_LC_FILTERED_IRRDB_ORIGIN_AS_FILTERED ); + accept; + } + + + + # RPKI test - if it's INVALID or VALID, we are done + if filter_rpki() then accept; + + + + + allnet = [ 192.175.48.0/24 + ]; + + if ! (net ~ allnet) then { + bgp_large_community.add( IXP_LC_FILTERED_IRRDB_PREFIX_FILTERED ); + bgp_large_community.add( IXP_LC_INFO_IRRDB_FILTERED_STRICT ); + accept; + } else { + bgp_large_community.add( IXP_LC_INFO_IRRDB_VALID ); + } + + + + accept; +} + + +# The route server export filter exists as the export gateway on the BGP protocol. +# +# Remember that standard IXP community filtering has already happened on the +# master -> bgp protocol pipe. + +filter f_export_as112{ + + + + # we should strip our own communities which we used for the looking glass and filtering + bgp_large_community.delete( [( routeserverasn, *, * )] ); + bgp_community.delete( [( routeserverasn, * )] ); + + # default position is to accept: + accept; + +} + + + + + + + +protocol pipe pp_0004_as112 { + description "Pipe for AS112 - AS112 - VLAN Interface 4"; + table master4; + peer table t_0004_as112; + import filter f_export_to_master; + export where ixp_community_filter(112); +} + +protocol bgp pb_0004_as112 from tb_rsclient { + description "AS112 - AS112"; + neighbor 10.1.0.6 as 112; + ipv4 { + import limit 20 action restart; + import filter f_import_as112; + table t_0004_as112; + export filter f_export_as112; + }; + password "w83fmGpRDtaKomQo"; +} + + +######################################################################################## +######################################################################################## +### +### AS1213 - HEAnet - VLAN Interface #1 + +ipv4 table t_0001_as1213; + + + +filter f_import_as1213 +prefix set allnet; +ip set allips; +int set allas; +{ + + ######################################################################################## + ######################################################################################## + # + # UI Based Filtering (import rules) - **WHAT THE RS IS LEARNING FROM THE MEMBER** + # + ######################################################################################## + ######################################################################################## + + + # NO_ADVERTISE to all + bgp_large_community.add( (routeserverasn,0,0) ); + + + if ( net = 77.72.72.0/21 ) then { + # PREPEND_TWICE to AS112 + bgp_large_community.add( (routeserverasn,102,112) ); + } + + + ######################################################################################## + # End UI Based Filtering - import rules + ######################################################################################## + + + + # Filter small prefixes + if ( net ~ [ 0.0.0.0/0{25,32} ] ) then { + bgp_large_community.add( IXP_LC_FILTERED_PREFIX_LEN_TOO_LONG ); + accept; + } + + + if !(avoid_martians()) then { + bgp_large_community.add( IXP_LC_FILTERED_BOGON ); + accept; + } + + # Belt and braces: must have at least one ASN in the path + if( bgp_path.len < 1 ) then { + bgp_large_community.add( IXP_LC_FILTERED_AS_PATH_TOO_SHORT ); + accept; + } + + # Peer ASN == route's first ASN? + if (bgp_path.first != 1213 ) then { + bgp_large_community.add( IXP_LC_FILTERED_FIRST_AS_NOT_PEER_AS ); + accept; + } + + # set of all IPs this ASN uses to peer with on this VLAN + allips = [ 10.1.0.10 ]; + + # Prevent BGP NEXT_HOP Hijacking + if !( from = bgp_next_hop ) then { + + # need to differentiate between same ASN next hop or actual next hop hijacking + if( bgp_next_hop ~ allips ) then { + bgp_large_community.add( IXP_LC_INFO_SAME_AS_NEXT_HOP ); + } else { + # looks like hijacking (intentional or not) + bgp_large_community.add( IXP_LC_FILTERED_NEXT_HOP_NOT_PEER_IP ); + accept; + } + } + + + # Filter Known Transit Networks + if filter_has_transit_path() then accept; + + # Belt and braces: no one needs an ASN path with > 64 hops, that's just broken + if( bgp_path.len > 64 ) then { + bgp_large_community.add( IXP_LC_FILTERED_AS_PATH_TOO_LONG ); + accept; + } + + + + allas = [ 112, 1213, 1921, 2128, 2850, 42310 + ]; + + + # Ensure origin ASN is in the neighbors AS-SET + if !(bgp_path.last_nonaggregated ~ allas) then { + bgp_large_community.add( IXP_LC_FILTERED_IRRDB_ORIGIN_AS_FILTERED ); + accept; + } + + + + # RPKI test - if it's INVALID or VALID, we are done + if filter_rpki() then accept; + + + + + allnet = [ 4.53.84.128/26, 4.53.146.192/26, 77.72.72.0/21, 87.32.0.0/12, + 91.123.224.0/20, 134.226.0.0/16, 136.201.0.0/16, 136.206.0.0/16, + 137.43.0.0/16, 140.203.0.0/16, 143.239.0.0/16, 147.252.0.0/16, + 149.153.0.0/16, 149.157.0.0/16, 157.190.0.0/16, 160.6.0.0/16, + 176.97.158.0/24, 192.174.68.0/24, 192.175.48.0/24, 193.1.0.0/16, + 193.242.111.0/24, 194.0.24.0/24, 194.0.25.0/24, 194.0.26.0/24, + 194.88.240.0/23, 212.3.242.128/26 + ]; + + if ! (net ~ allnet) then { + bgp_large_community.add( IXP_LC_FILTERED_IRRDB_PREFIX_FILTERED ); + bgp_large_community.add( IXP_LC_INFO_IRRDB_FILTERED_STRICT ); + accept; + } else { + bgp_large_community.add( IXP_LC_INFO_IRRDB_VALID ); + } + + + + accept; +} + + +# The route server export filter exists as the export gateway on the BGP protocol. +# +# Remember that standard IXP community filtering has already happened on the +# master -> bgp protocol pipe. + +filter f_export_as1213{ + + + + # we should strip our own communities which we used for the looking glass and filtering + bgp_large_community.delete( [( routeserverasn, *, * )] ); + bgp_community.delete( [( routeserverasn, * )] ); + + ############################################################################################ + ############################################################################################ + # + # UI Based Filtering (export rules) - **THIRD PARTY ROUTES THE RS IS SENDING TO THE MEMBER** + # + ############################################################################################ + ############################################################################################ + + + # NO_ADVERTISE - do not advertise this to the route server client + reject; + + + if ( bgp_path.first = 112 ) then { + if ( net = 192.175.48.0/24 ) then { + # PREPEND_TWICE + bgp_path.prepend( bgp_path.first ); + bgp_path.prepend( bgp_path.first ); + } + } + + + # AS_IS - a no-op but we accept here as 'first rule matches' + accept; + + ######################################################################################## + # End UI Based Filtering - export rules + ######################################################################################## + + # default position is to accept: + accept; + +} + + + + + + + +protocol pipe pp_0001_as1213 { + description "Pipe for AS1213 - HEAnet - VLAN Interface 1"; + table master4; + peer table t_0001_as1213; + import filter f_export_to_master; + export where ixp_community_filter(1213); +} + +protocol bgp pb_0001_as1213 from tb_rsclient { + description "AS1213 - HEAnet"; + neighbor 10.1.0.10 as 1213; + ipv4 { + import limit 1000 action restart; + import filter f_import_as1213; + table t_0001_as1213; + export filter f_export_as1213; + }; + password "N7rX2SdfbRsyBLTm"; +} + + + diff --git a/data/ci/known-good/ci-apiv4-b2-rs1-lan1-ipv6.conf b/data/ci/known-good/ci-apiv4-b2-rs1-lan1-ipv6.conf new file mode 100644 index 000000000..224b01498 --- /dev/null +++ b/data/ci/known-good/ci-apiv4-b2-rs1-lan1-ipv6.conf @@ -0,0 +1,692 @@ + +# +# Bird v2 Route Server configuration generated by IXP Manager +# +# Do not edit this file, it will be overwritten. Please see: +# +# https://github.com/inex/IXP-Manager/wiki/Route-Server +# +# Generated: 2019-03-09 15:43:36 +# + +# For VLAN: Peering LAN 1 (Tag: 1, Database ID: 1) + +# standardise time formats: +timeformat base iso long; +timeformat log iso long; +timeformat protocol iso long; +timeformat route iso long; + + +log "/var/log/bird/b2-rs1-lan1-ipv6.log" all; +log syslog name "bird-b2-rs1-lan1-ipv6" all; + +define routeserverasn = 65501; +define routeserveraddress = 2001:db8::8; + + +router id 192.0.2.18; + +# ignore interface up/down events +protocol device { } + +# This function excludes weird networks +# rfc1918, class D, class E, too long and too short prefixes +function avoid_martians() +prefix set martians; +{ + + martians = [ + ::/0, # Default (can be advertised as a route in BGP to peers if desired) + ::/96, # IPv4-compatible IPv6 address - deprecated by RFC4291 + ::/128, # Unspecified address + ::1/128, # Local host loopback address + ::ffff:0.0.0.0/96+, # IPv4-mapped addresses + ::224.0.0.0/100+, # Compatible address (IPv4 format) + ::127.0.0.0/104+, # Compatible address (IPv4 format) + ::0.0.0.0/104+, # Compatible address (IPv4 format) + ::255.0.0.0/104+, # Compatible address (IPv4 format) + 0000::/8+, # Pool used for unspecified, loopback and embedded IPv4 addresses + 0200::/7+, # OSI NSAP-mapped prefix set (RFC4548) - deprecated by RFC4048 + 3ffe::/16+, # Former 6bone, now decommissioned + 2001:db8::/32+, # Reserved by IANA for special purposes and documentation + 2002:e000::/20+, # Invalid 6to4 packets (IPv4 multicast) + 2002:7f00::/24+, # Invalid 6to4 packets (IPv4 loopback) + 2002:0000::/24+, # Invalid 6to4 packets (IPv4 default) + 2002:ff00::/24+, # Invalid 6to4 packets + 2002:0a00::/24+, # Invalid 6to4 packets (IPv4 private 10.0.0.0/8 network) + 2002:ac10::/28+, # Invalid 6to4 packets (IPv4 private 172.16.0.0/12 network) + 2002:c0a8::/32+, # Invalid 6to4 packets (IPv4 private 192.168.0.0/16 network) + fc00::/7+, # Unicast Unique Local Addresses (ULA) - RFC 4193 + fe80::/10+, # Link-local Unicast + fec0::/10+, # Site-local Unicast - deprecated by RFC 3879 (replaced by ULA) + ff00::/8+ # Multicast + ]; + + + # Avoid RFC1918 and similar networks + if net ~ martians then + return false; + + return true; +} + + +######################################################################################## +######################################################################################## +# +# Community filtering definitions for use with looking glasses +# +# Current implementation based on: +# +# https://github.com/euro-ix/rs-workshop-july-2017/wiki/Route-Server-BGP-Community-usage +# +######################################################################################## +######################################################################################## + + + +# These will all be filtered and not piped to the master table: + +define IXP_LC_FILTERED_PREFIX_LEN_TOO_LONG = ( routeserverasn, 1101, 1 ); +define IXP_LC_FILTERED_PREFIX_LEN_TOO_SHORT = ( routeserverasn, 1101, 2 ); +define IXP_LC_FILTERED_BOGON = ( routeserverasn, 1101, 3 ); +define IXP_LC_FILTERED_BOGON_ASN = ( routeserverasn, 1101, 4 ); +define IXP_LC_FILTERED_AS_PATH_TOO_LONG = ( routeserverasn, 1101, 5 ); +define IXP_LC_FILTERED_AS_PATH_TOO_SHORT = ( routeserverasn, 1101, 6 ); +define IXP_LC_FILTERED_FIRST_AS_NOT_PEER_AS = ( routeserverasn, 1101, 7 ); +define IXP_LC_FILTERED_NEXT_HOP_NOT_PEER_IP = ( routeserverasn, 1101, 8 ); +define IXP_LC_FILTERED_IRRDB_PREFIX_FILTERED = ( routeserverasn, 1101, 9 ); +define IXP_LC_FILTERED_IRRDB_ORIGIN_AS_FILTERED = ( routeserverasn, 1101, 10 ); +define IXP_LC_FILTERED_PREFIX_NOT_IN_ORIGIN_AS = ( routeserverasn, 1101, 11 ); + +define IXP_LC_FILTERED_RPKI_UNKNOWN = ( routeserverasn, 1101, 12 ); +define IXP_LC_FILTERED_RPKI_INVALID = ( routeserverasn, 1101, 13 ); +define IXP_LC_FILTERED_TRANSIT_FREE_ASN = ( routeserverasn, 1101, 14 ); +define IXP_LC_FILTERED_TOO_MANY_COMMUNITIES = ( routeserverasn, 1101, 15 ); + + + + +# Informational prefixes + +define IXP_LC_INFO_RPKI_VALID = ( routeserverasn, 1000, 1 ); +define IXP_LC_INFO_RPKI_UNKNOWN = ( routeserverasn, 1000, 2 ); +define IXP_LC_INFO_RPKI_NOT_CHECKED = ( routeserverasn, 1000, 3 ); + +define IXP_LC_INFO_IRRDB_VALID = ( routeserverasn, 1001, 1 ); +define IXP_LC_INFO_IRRDB_NOT_CHECKED = ( routeserverasn, 1001, 2 ); +define IXP_LC_INFO_IRRDB_MORE_SPECIFIC = ( routeserverasn, 1001, 3 ); + +define IXP_LC_INFO_IRRDB_FILTERED_LOOSE = ( routeserverasn, 1001, 1000 ); +define IXP_LC_INFO_IRRDB_FILTERED_STRICT = ( routeserverasn, 1001, 1001 ); +define IXP_LC_INFO_IRRDB_PREFIX_EMPTY = ( routeserverasn, 1001, 1002 ); + +define IXP_LC_INFO_SAME_AS_NEXT_HOP = ( routeserverasn, 1001, 1200 ); + +# ( routeserverasn, 1010, peerasn ) -> route learnt from peerasn via routeserverasn +# ( routeserverasn, 1011, originasn ) -> route origin asn via routeserverasn + + +# And the filter for examining routes in the peers import table being exported +# to the master table + +filter f_export_to_master +{ + + if bgp_large_community ~ [( routeserverasn, 1101, * )] then reject; + + accept; +} + + + + + + +######################################################################################## +######################################################################################## +# +# Standard IXP community filter +# +######################################################################################## +######################################################################################## + + +function ixp_community_filter(int peerasn) +{ + if !(source = RTS_BGP) then + return false; + + # AS path prepending + if (routeserverasn, 103, peerasn) ~ bgp_large_community then { + bgp_path.prepend( bgp_path.first ); + bgp_path.prepend( bgp_path.first ); + bgp_path.prepend( bgp_path.first ); + } else if (routeserverasn, 102, peerasn) ~ bgp_large_community then { + bgp_path.prepend( bgp_path.first ); + bgp_path.prepend( bgp_path.first ); + } else if (routeserverasn, 101, peerasn) ~ bgp_large_community then { + bgp_path.prepend( bgp_path.first ); + } else if (routeserverasn, 103, 0) ~ bgp_large_community then { + bgp_path.prepend( bgp_path.first ); + bgp_path.prepend( bgp_path.first ); + bgp_path.prepend( bgp_path.first ); + } else if (routeserverasn, 102, 0) ~ bgp_large_community then { + bgp_path.prepend( bgp_path.first ); + bgp_path.prepend( bgp_path.first ); + } else if (routeserverasn, 101, 0) ~ bgp_large_community then { + bgp_path.prepend( bgp_path.first ); + } + + + # support for BGP Large Communities + if (routeserverasn, 0, peerasn) ~ bgp_large_community then + return false; + if (routeserverasn, 1, peerasn) ~ bgp_large_community then + return true; + if (routeserverasn, 0, 0) ~ bgp_large_community then + return false; + if (routeserverasn, 1, 0) ~ bgp_large_community then + return true; + + # it's unwise to conduct a 32-bit check on a 16-bit value + if routeserverasn > 65535 || peerasn > 65535 then + return true; + + # Implement widely used community filtering schema. + if (0, peerasn) ~ bgp_community then + return false; + if (routeserverasn, peerasn) ~ bgp_community then + return true; + if (0, routeserverasn) ~ bgp_community then + return false; + + return true; +} + + + + +######################################################################################## +######################################################################################## +# +# RPKI protocol configuration +# +######################################################################################## +######################################################################################## + + +roa6 table t_roa; + +protocol rpki rpki1 { + + roa6 { table t_roa; }; + + remote "10.39.5.123" port 3323; + + retry keep 90; + refresh keep 900; + expire keep 172800; +} + + +protocol rpki rpki2 { + + roa6 { table t_roa; }; + + remote "10.39.5.124" port 3323; + + retry keep 90; + refresh keep 900; + expire keep 172800; +} + + +/* + * RPKI check for the path + * + * return: true means the filter should stop processing, false means keep processing + */ +function filter_rpki() +{ + # RPKI check + if( roa_check( t_roa, net, bgp_path.last_nonaggregated ) = ROA_INVALID ) then { + print "Tagging invalid ROA ", net, " for ASN ", bgp_path.last; + bgp_large_community.add( IXP_LC_FILTERED_RPKI_INVALID ); + return true; + } + + if( roa_check( t_roa, net, bgp_path.last_nonaggregated ) = ROA_VALID ) then { + bgp_large_community.add( IXP_LC_INFO_RPKI_VALID ); + return true; + } + + # RPKI unknown, keep checking and mark as unknown for info + bgp_large_community.add( IXP_LC_INFO_RPKI_UNKNOWN ); + + return false; +} + + + + + +######################################################################################## +######################################################################################## +# +# Filter known transit networks +# +# Inspired by: http://bgpfilterguide.nlnog.net/guides/no_transit_leaks/ +# +######################################################################################## +######################################################################################## + + +define TRANSIT_ASNS = [ 174, 701, 1299, 2914, 3257, 3320, 3356, 3491, 4134, 5511, 6453, 6461, 6762, 6830, 7018 ]; + +function filter_has_transit_path() +int set transit_asns; +{ + transit_asns = TRANSIT_ASNS; + if (bgp_path ~ transit_asns) then { + bgp_large_community.add( IXP_LC_FILTERED_TRANSIT_FREE_ASN ); + return true; + } + + return false; +} + + +######################################################################################## +######################################################################################## +# +# Route Server client configuration +# +######################################################################################## +######################################################################################## + + +template bgp tb_rsclient { + local as routeserverasn; + source address routeserveraddress; + strict bind yes; + + # give RPKI-RTR a chance to start and populate + # (RPKI is /really/ quick) + connect delay time 30; + + interpret communities off; # enable rfc1997 well-known community pass through + + ipv6 { + export all; + }; + + rs client; +} + + + + +######################################################################################## +######################################################################################## +# +# Route server clients +# +######################################################################################## +######################################################################################## + + +######################################################################################## +######################################################################################## +### +### AS1213 - HEAnet - VLAN Interface #1 + +ipv6 table t_0001_as1213; + + + +filter f_import_as1213 +prefix set allnet; +ip set allips; +int set allas; +{ + + ######################################################################################## + ######################################################################################## + # + # UI Based Filtering (import rules) - **WHAT THE RS IS LEARNING FROM THE MEMBER** + # + ######################################################################################## + ######################################################################################## + + + # PREPEND_ONCE to all + bgp_large_community.add( (routeserverasn,101,0) ); + + + if ( net = 2001:678:24::/48 ) then { + # PREPEND_THRICE to Imagine + bgp_large_community.add( (routeserverasn,103,25441) ); + } + + + ######################################################################################## + # End UI Based Filtering - import rules + ######################################################################################## + + + # Filter small prefixes + if ( net ~ [ ::/0{49,128} ] ) then { + bgp_large_community.add( IXP_LC_FILTERED_PREFIX_LEN_TOO_LONG ); + accept; + } + + + if !(avoid_martians()) then { + bgp_large_community.add( IXP_LC_FILTERED_BOGON ); + accept; + } + + # Belt and braces: must have at least one ASN in the path + if( bgp_path.len < 1 ) then { + bgp_large_community.add( IXP_LC_FILTERED_AS_PATH_TOO_SHORT ); + accept; + } + + # Peer ASN == route's first ASN? + if (bgp_path.first != 1213 ) then { + bgp_large_community.add( IXP_LC_FILTERED_FIRST_AS_NOT_PEER_AS ); + accept; + } + + # set of all IPs this ASN uses to peer with on this VLAN + allips = [ 2001:db8:1::10 ]; + + # Prevent BGP NEXT_HOP Hijacking + if !( from = bgp_next_hop ) then { + + # need to differentiate between same ASN next hop or actual next hop hijacking + if( bgp_next_hop ~ allips ) then { + bgp_large_community.add( IXP_LC_INFO_SAME_AS_NEXT_HOP ); + } else { + # looks like hijacking (intentional or not) + bgp_large_community.add( IXP_LC_FILTERED_NEXT_HOP_NOT_PEER_IP ); + accept; + } + } + + + # Filter Known Transit Networks + if filter_has_transit_path() then accept; + + # Belt and braces: no one needs an ASN path with > 64 hops, that's just broken + if( bgp_path.len > 64 ) then { + bgp_large_community.add( IXP_LC_FILTERED_AS_PATH_TOO_LONG ); + accept; + } + + + + allas = [ 112, 1213, 1921, 2128, 2850, 42310 + ]; + + + # Ensure origin ASN is in the neighbors AS-SET + if !(bgp_path.last_nonaggregated ~ allas) then { + bgp_large_community.add( IXP_LC_FILTERED_IRRDB_ORIGIN_AS_FILTERED ); + accept; + } + + + + # RPKI test - if it's INVALID or VALID, we are done + if filter_rpki() then accept; + + + + + allnet = [ 2001:678:20::/48, 2001:678:24::/48, 2001:67c:1bc::/48, 2001:67c:10b8::/48, + 2001:67c:10e0::/48, 2001:770::/32, 2001:7f8:18::/48, 2001:1900:2205::/48, + 2001:1900:2206::/48, 2620:4f:8000::/48, 2a01:4b0::/32 + ]; + + if ! (net ~ allnet) then { + bgp_large_community.add( IXP_LC_FILTERED_IRRDB_PREFIX_FILTERED ); + bgp_large_community.add( IXP_LC_INFO_IRRDB_FILTERED_STRICT ); + accept; + } else { + bgp_large_community.add( IXP_LC_INFO_IRRDB_VALID ); + } + + + + accept; +} + + +# The route server export filter exists as the export gateway on the BGP protocol. +# +# Remember that standard IXP community filtering has already happened on the +# master -> bgp protocol pipe. + +filter f_export_as1213{ + + + + # we should strip our own communities which we used for the looking glass and filtering + bgp_large_community.delete( [( routeserverasn, *, * )] ); + bgp_community.delete( [( routeserverasn, * )] ); + + ############################################################################################ + ############################################################################################ + # + # UI Based Filtering (export rules) - **THIRD PARTY ROUTES THE RS IS SENDING TO THE MEMBER** + # + ############################################################################################ + ############################################################################################ + + + # PREPEND_ONCE + bgp_path.prepend( bgp_path.first ); + + + if ( bgp_path.first = 25441 ) then { + if ( net = 2001:4d68::/32 ) then { + # PREPEND_THRICE + bgp_path.prepend( bgp_path.first ); + bgp_path.prepend( bgp_path.first ); + bgp_path.prepend( bgp_path.first ); + } + } + + + # AS_IS - a no-op but we accept here as 'first rule matches' + accept; + + ######################################################################################## + # End UI Based Filtering - export rules + ######################################################################################## + + # default position is to accept: + accept; + +} + + + + + + + +protocol pipe pp_0001_as1213 { + description "Pipe for AS1213 - HEAnet - VLAN Interface 1"; + table master6; + peer table t_0001_as1213; + import filter f_export_to_master; + export where ixp_community_filter(1213); +} + +protocol bgp pb_0001_as1213 from tb_rsclient { + description "AS1213 - HEAnet"; + neighbor 2001:db8:1::10 as 1213; + ipv6 { + import limit 1000 action restart; + import filter f_import_as1213; + table t_0001_as1213; + export filter f_export_as1213; + }; + password "N7rX2SdfbRsyBLTm"; +} + + +######################################################################################## +######################################################################################## +### +### AS25441 - Imagine - VLAN Interface #6 + +ipv6 table t_0006_as25441; + + + +filter f_import_as25441 +prefix set allnet; +ip set allips; +int set allas; +{ + + + # Filter small prefixes + if ( net ~ [ ::/0{49,128} ] ) then { + bgp_large_community.add( IXP_LC_FILTERED_PREFIX_LEN_TOO_LONG ); + accept; + } + + + if !(avoid_martians()) then { + bgp_large_community.add( IXP_LC_FILTERED_BOGON ); + accept; + } + + # Belt and braces: must have at least one ASN in the path + if( bgp_path.len < 1 ) then { + bgp_large_community.add( IXP_LC_FILTERED_AS_PATH_TOO_SHORT ); + accept; + } + + # Peer ASN == route's first ASN? + if (bgp_path.first != 25441 ) then { + bgp_large_community.add( IXP_LC_FILTERED_FIRST_AS_NOT_PEER_AS ); + accept; + } + + # set of all IPs this ASN uses to peer with on this VLAN + allips = [ 2001:db8:1::8 ]; + + # Prevent BGP NEXT_HOP Hijacking + if !( from = bgp_next_hop ) then { + + # need to differentiate between same ASN next hop or actual next hop hijacking + if( bgp_next_hop ~ allips ) then { + bgp_large_community.add( IXP_LC_INFO_SAME_AS_NEXT_HOP ); + } else { + # looks like hijacking (intentional or not) + bgp_large_community.add( IXP_LC_FILTERED_NEXT_HOP_NOT_PEER_IP ); + accept; + } + } + + + # Filter Known Transit Networks + if filter_has_transit_path() then accept; + + # Belt and braces: no one needs an ASN path with > 64 hops, that's just broken + if( bgp_path.len > 64 ) then { + bgp_large_community.add( IXP_LC_FILTERED_AS_PATH_TOO_LONG ); + accept; + } + + + + allas = [ 11521, 25441, 34317, 35272, 39064, 43178, 43610, 47615, 48342, 49573, + 197853, 197904 + ]; + + + # Ensure origin ASN is in the neighbors AS-SET + if !(bgp_path.last_nonaggregated ~ allas) then { + bgp_large_community.add( IXP_LC_FILTERED_IRRDB_ORIGIN_AS_FILTERED ); + accept; + } + + + + # RPKI test - if it's INVALID or VALID, we are done + if filter_rpki() then accept; + + + + + allnet = [ 2001:67c:338::/48, 2001:4d68::/32{32,48}, 2a01:268::/32{32,48}, 2a01:8f80::/32{32,48} + ]; + + if ! (net ~ allnet) then { + bgp_large_community.add( IXP_LC_FILTERED_IRRDB_PREFIX_FILTERED ); + bgp_large_community.add( IXP_LC_INFO_IRRDB_FILTERED_LOOSE ); + accept; + } else { + bgp_large_community.add( IXP_LC_INFO_IRRDB_VALID ); + } + + + + accept; +} + + +# The route server export filter exists as the export gateway on the BGP protocol. +# +# Remember that standard IXP community filtering has already happened on the +# master -> bgp protocol pipe. + +filter f_export_as25441{ + + + + # we should strip our own communities which we used for the looking glass and filtering + bgp_large_community.delete( [( routeserverasn, *, * )] ); + bgp_community.delete( [( routeserverasn, * )] ); + + # default position is to accept: + accept; + +} + + + + + + + +protocol pipe pp_0006_as25441 { + description "Pipe for AS25441 - Imagine - VLAN Interface 6"; + table master6; + peer table t_0006_as25441; + import filter f_export_to_master; + export where ixp_community_filter(25441); +} + +protocol bgp pb_0006_as25441 from tb_rsclient { + description "AS25441 - Imagine"; + neighbor 2001:db8:1::8 as 25441; + ipv6 { + import limit 1000 action restart; + import filter f_import_as25441; + table t_0006_as25441; + export filter f_export_as25441; + }; + password "X8Ks9QnbER9cyzU3"; +} + + + diff --git a/data/ci/known-good/ci-apiv4-rc1-lan1-ipv4.conf b/data/ci/known-good/ci-apiv4-rc1-lan1-ipv4.conf new file mode 100644 index 000000000..aaacd55b8 --- /dev/null +++ b/data/ci/known-good/ci-apiv4-rc1-lan1-ipv4.conf @@ -0,0 +1,119 @@ +# +# Bird Route Collector configuration generated by IXP Manager +# +# Do not edit this file, it will be overwritten. +# +# Generated: 2016-11-04 00:11:35 +# + +# For VLAN: Peering LAN 1 (Tag: 1, Database ID: 1) + +# standardise time formats: +timeformat base iso long; +timeformat log iso long; +timeformat protocol iso long; +timeformat route iso long; + + +log "/var/log/bird/rc1-lan1-ipv4.log" all; +log syslog all; + +define routerasn = 65500; +define routeraddress = 192.0.2.8; + +router id 192.0.2.8; +listen bgp address routeraddress; + +# ignore interface up/down events +protocol device { } + +# These function excludes weird networks +# rfc1918, class D, class E, too long and too short prefixes + + +function avoid_martians_v4() +prefix set martiansv4; +{ + # This list of martians is obtained from the IANA IPv4 + # Special-Purpose Address Registry: + # http://www.iana.org/assignments/iana-ipv4-special-registry + martiansv4 = [ + 10.0.0.0/8+, # RFC1918 - Private use + 100.64.0.0/10+, # RFC6598 - Shared address space + 127.0.0.0/8+, # RFC1122 - Loopback + 169.254.0.0/16+, # RFC3927 - Link-local + 172.16.0.0/12+, # RFC1918 - Private use + 192.0.0.0/24+, # multiple RFCs + 192.0.2.0/24+, # RFC5737 - Documentation - TEST-NET-1 + 192.168.0.0/16+, # RFC1918 - Private use + 198.18.0.0/15+, # RFC2544 - Benchmarking + 198.51.100.0/24+, # RFC5737 - Documentation - TEST-NET-2 + 203.0.113.0/24+, # RFC5737 - Documentation - TEST-NET-3 + 224.0.0.0/4+, # RFC3171 - Multicast + 240.0.0.0/4+, # RFC1112 - Reserved + 0.0.0.0/32-, + 0.0.0.0/0{25,32}, + 0.0.0.0/0{0,7} + ]; + + # Avoid RFC1918 and similar networks + if net ~ martiansv4 then + return false; + + return true; +} + +filter f_import_policy_v4 +{ + if !(avoid_martians_v4()) then + reject; + + accept; +} + + +## +## Route collector client configuration +## + + + +protocol bgp pb_as42_vli3_ipv4 { + description "AS42 - PCH DNS"; + local as routerasn; + source address routeraddress; + neighbor 10.1.0.36 as 42; + # As a route collector, we want to import everything and export nothing: + import all; + export none; + import limit 2000 action restart; + password "mcWsqMdzGwTKt67g"; +} + + +protocol bgp pb_as112_vli4_ipv4 { + description "AS112 - AS112"; + local as routerasn; + source address routeraddress; + neighbor 10.1.0.6 as 112; + # As a route collector, we want to import everything and export nothing: + import all; + export none; + import limit 20 action restart; + password "w83fmGpRDtaKomQo"; +} + + +protocol bgp pb_as1213_vli1_ipv4 { + description "AS1213 - HEAnet"; + local as routerasn; + source address routeraddress; + neighbor 10.1.0.10 as 1213; + # As a route collector, we want to import everything and export nothing: + import all; + export none; + import limit 1000 action restart; + password "N7rX2SdfbRsyBLTm"; +} + + diff --git a/data/ci/known-good/ci-apiv4-rc1-lan1-ipv6.conf b/data/ci/known-good/ci-apiv4-rc1-lan1-ipv6.conf new file mode 100644 index 000000000..dc17fe8a2 --- /dev/null +++ b/data/ci/known-good/ci-apiv4-rc1-lan1-ipv6.conf @@ -0,0 +1,118 @@ +# +# Bird Route Collector configuration generated by IXP Manager +# +# Do not edit this file, it will be overwritten. +# +# Generated: 2016-11-04 00:45:36 +# + +# For VLAN: Peering LAN 1 (Tag: 1, Database ID: 1) + +# standardise time formats: +timeformat base iso long; +timeformat log iso long; +timeformat protocol iso long; +timeformat route iso long; + + +log "/var/log/bird/rc1-lan1-ipv6.log" all; +log syslog all; + +define routerasn = 65500; +define routeraddress = 2001:db8::8; + +router id 192.0.2.8; +listen bgp address routeraddress; + +# ignore interface up/down events +protocol device { } + +# These function excludes weird networks +# rfc1918, class D, class E, too long and too short prefixes + + +function avoid_martians_v6() +prefix set martiansv6; +{ + # This list of martians is obtained from the IANA IPv6 + # Special-Purpose Address Registry: + # http://www.iana.org/assignments/iana-ipv6-special-registry + martiansv6 = [ + ::/0, # Default (can be advertised as a route in BGP to peers if desired) + ::/96, # IPv4-compatible IPv6 address - deprecated by RFC4291 + ::/128, # Unspecified address + ::1/128, # Local host loopback address + ::ffff:0.0.0.0/96+, # IPv4-mapped addresses + ::224.0.0.0/100+, # Compatible address (IPv4 format) + ::127.0.0.0/104+, # Compatible address (IPv4 format) + ::0.0.0.0/104+, # Compatible address (IPv4 format) + ::255.0.0.0/104+, # Compatible address (IPv4 format) + 0000::/8+, # Pool used for unspecified, loopback and embedded IPv4 addresses + 0100::/64+, # RFC6666 - discard-only address block + 0200::/7+, # RFC4548 - OSI NSAP-mapped prefix set, deprecated by RFC4048 + 3ffe::/16+, # Former 6bone, now decommissioned + 2001:2::/48+, # RFC5180 - Benchmarking + 2001:10::/28+, # RFC4843 - ORCHID + 2001:db8::/32+, # RFC3849 - Reserved by IANA for special purposes and documentation + 2002:e000::/20+, # Invalid 6to4 packets (IPv4 multicast) + 2002:7f00::/24+, # Invalid 6to4 packets (IPv4 loopback) + 2002:0000::/24+, # Invalid 6to4 packets (IPv4 default) + 2002:ff00::/24+, # Invalid 6to4 packets + 2002:0a00::/24+, # Invalid 6to4 packets (IPv4 private 10.0.0.0/8 network) + 2002:ac10::/28+, # Invalid 6to4 packets (IPv4 private 172.16.0.0/12 network) + 2002:c0a8::/32+, # Invalid 6to4 packets (IPv4 private 192.168.0.0/16 network) + fc00::/7+, # RFC4193 - Unicast Unique Local Addresses (ULA) + fe80::/10+, # Link-local Unicast + fec0::/10+, # Site-local Unicast - deprecated by RFC 3879 (replaced by ULA) + ff00::/8+, # Multicast + ::/0{49,128} # Filter small prefixes + ]; + + # Avoid RFC1918 and similar networks + if net ~ martiansv6 then + return false; + + return true; +} + +filter f_import_policy_v6 +{ + if !(avoid_martians_v6()) then + reject; + + accept; +} + + +## +## Route collector client configuration +## + + + +protocol bgp pb_as1213_vli1_ipv6 { + description "AS1213 - HEAnet"; + local as routerasn; + source address routeraddress; + neighbor 2001:db8:1::10 as 1213; + # As a route collector, we want to import everything and export nothing: + import all; + export none; + import limit 1000 action restart; + password "N7rX2SdfbRsyBLTm"; +} + + +protocol bgp pb_as25441_vli6_ipv6 { + description "AS25441 - Imagine"; + local as routerasn; + source address routeraddress; + neighbor 2001:db8:1::8 as 25441; + # As a route collector, we want to import everything and export nothing: + import all; + export none; + import limit 1000 action restart; + password "X8Ks9QnbER9cyzU3"; +} + + diff --git a/data/ci/known-good/ci-apiv4-rc1-lan2-ipv4.conf b/data/ci/known-good/ci-apiv4-rc1-lan2-ipv4.conf new file mode 100644 index 000000000..4216431f1 --- /dev/null +++ b/data/ci/known-good/ci-apiv4-rc1-lan2-ipv4.conf @@ -0,0 +1,119 @@ +# +# Bird Route Collector configuration generated by IXP Manager +# +# Do not edit this file, it will be overwritten. +# +# Generated: 2016-11-04 00:11:47 +# + +# For VLAN: Peering LAN 2 (Tag: 2, Database ID: 2) + +# standardise time formats: +timeformat base iso long; +timeformat log iso long; +timeformat protocol iso long; +timeformat route iso long; + + +log "/var/log/bird/rc1-lan2-ipv4.log" all; +log syslog all; + +define routerasn = 65500; +define routeraddress = 192.0.2.9; + +router id 192.0.2.9; +listen bgp address routeraddress; + +# ignore interface up/down events +protocol device { } + +# These function excludes weird networks +# rfc1918, class D, class E, too long and too short prefixes + + +function avoid_martians_v4() +prefix set martiansv4; +{ + # This list of martians is obtained from the IANA IPv4 + # Special-Purpose Address Registry: + # http://www.iana.org/assignments/iana-ipv4-special-registry + martiansv4 = [ + 10.0.0.0/8+, # RFC1918 - Private use + 100.64.0.0/10+, # RFC6598 - Shared address space + 127.0.0.0/8+, # RFC1122 - Loopback + 169.254.0.0/16+, # RFC3927 - Link-local + 172.16.0.0/12+, # RFC1918 - Private use + 192.0.0.0/24+, # multiple RFCs + 192.0.2.0/24+, # RFC5737 - Documentation - TEST-NET-1 + 192.168.0.0/16+, # RFC1918 - Private use + 198.18.0.0/15+, # RFC2544 - Benchmarking + 198.51.100.0/24+, # RFC5737 - Documentation - TEST-NET-2 + 203.0.113.0/24+, # RFC5737 - Documentation - TEST-NET-3 + 224.0.0.0/4+, # RFC3171 - Multicast + 240.0.0.0/4+, # RFC1112 - Reserved + 0.0.0.0/32-, + 0.0.0.0/0{25,32}, + 0.0.0.0/0{0,7} + ]; + + # Avoid RFC1918 and similar networks + if net ~ martiansv4 then + return false; + + return true; +} + +filter f_import_policy_v4 +{ + if !(avoid_martians_v4()) then + reject; + + accept; +} + + +## +## Route collector client configuration +## + + + +protocol bgp pb_as112_vli5_ipv4 { + description "AS112 - AS112"; + local as routerasn; + source address routeraddress; + neighbor 10.2.0.6 as 112; + # As a route collector, we want to import everything and export nothing: + import all; + export none; + import limit 20 action restart; + password "Pz8VYMNwEdCjKz68"; +} + + +protocol bgp pb_as1213_vli2_ipv4 { + description "AS1213 - HEAnet"; + local as routerasn; + source address routeraddress; + neighbor 10.2.0.11 as 1213; + # As a route collector, we want to import everything and export nothing: + import all; + export none; + import limit 1000 action restart; + password "u5zSNJLAVT87RGXQ"; +} + + +protocol bgp pb_as25441_vli7_ipv4 { + description "AS25441 - Imagine"; + local as routerasn; + source address routeraddress; + neighbor 10.2.0.46 as 25441; + # As a route collector, we want to import everything and export nothing: + import all; + export none; + import limit 1000 action restart; + password "LyJND4eoKuQz5j49"; +} + + diff --git a/data/ci/known-good/ci-apiv4-rc1-lan2-ipv6.conf b/data/ci/known-good/ci-apiv4-rc1-lan2-ipv6.conf new file mode 100644 index 000000000..8d74f35d4 --- /dev/null +++ b/data/ci/known-good/ci-apiv4-rc1-lan2-ipv6.conf @@ -0,0 +1,118 @@ +# +# Bird Route Collector configuration generated by IXP Manager +# +# Do not edit this file, it will be overwritten. +# +# Generated: 2016-11-04 00:45:43 +# + +# For VLAN: Peering LAN 2 (Tag: 2, Database ID: 2) + +# standardise time formats: +timeformat base iso long; +timeformat log iso long; +timeformat protocol iso long; +timeformat route iso long; + + +log "/var/log/bird/rc1-lan2-ipv6.log" all; +log syslog all; + +define routerasn = 65500; +define routeraddress = 2001:db8::9; + +router id 192.0.2.9; +listen bgp address routeraddress; + +# ignore interface up/down events +protocol device { } + +# These function excludes weird networks +# rfc1918, class D, class E, too long and too short prefixes + + +function avoid_martians_v6() +prefix set martiansv6; +{ + # This list of martians is obtained from the IANA IPv6 + # Special-Purpose Address Registry: + # http://www.iana.org/assignments/iana-ipv6-special-registry + martiansv6 = [ + ::/0, # Default (can be advertised as a route in BGP to peers if desired) + ::/96, # IPv4-compatible IPv6 address - deprecated by RFC4291 + ::/128, # Unspecified address + ::1/128, # Local host loopback address + ::ffff:0.0.0.0/96+, # IPv4-mapped addresses + ::224.0.0.0/100+, # Compatible address (IPv4 format) + ::127.0.0.0/104+, # Compatible address (IPv4 format) + ::0.0.0.0/104+, # Compatible address (IPv4 format) + ::255.0.0.0/104+, # Compatible address (IPv4 format) + 0000::/8+, # Pool used for unspecified, loopback and embedded IPv4 addresses + 0100::/64+, # RFC6666 - discard-only address block + 0200::/7+, # RFC4548 - OSI NSAP-mapped prefix set, deprecated by RFC4048 + 3ffe::/16+, # Former 6bone, now decommissioned + 2001:2::/48+, # RFC5180 - Benchmarking + 2001:10::/28+, # RFC4843 - ORCHID + 2001:db8::/32+, # RFC3849 - Reserved by IANA for special purposes and documentation + 2002:e000::/20+, # Invalid 6to4 packets (IPv4 multicast) + 2002:7f00::/24+, # Invalid 6to4 packets (IPv4 loopback) + 2002:0000::/24+, # Invalid 6to4 packets (IPv4 default) + 2002:ff00::/24+, # Invalid 6to4 packets + 2002:0a00::/24+, # Invalid 6to4 packets (IPv4 private 10.0.0.0/8 network) + 2002:ac10::/28+, # Invalid 6to4 packets (IPv4 private 172.16.0.0/12 network) + 2002:c0a8::/32+, # Invalid 6to4 packets (IPv4 private 192.168.0.0/16 network) + fc00::/7+, # RFC4193 - Unicast Unique Local Addresses (ULA) + fe80::/10+, # Link-local Unicast + fec0::/10+, # Site-local Unicast - deprecated by RFC 3879 (replaced by ULA) + ff00::/8+, # Multicast + ::/0{49,128} # Filter small prefixes + ]; + + # Avoid RFC1918 and similar networks + if net ~ martiansv6 then + return false; + + return true; +} + +filter f_import_policy_v6 +{ + if !(avoid_martians_v6()) then + reject; + + accept; +} + + +## +## Route collector client configuration +## + + + +protocol bgp pb_as1213_vli2_ipv6 { + description "AS1213 - HEAnet"; + local as routerasn; + source address routeraddress; + neighbor 2001:db8:2::11 as 1213; + # As a route collector, we want to import everything and export nothing: + import all; + export none; + import limit 1000 action restart; + password "u5zSNJLAVT87RGXQ"; +} + + +protocol bgp pb_as25441_vli7_ipv6 { + description "AS25441 - Imagine"; + local as routerasn; + source address routeraddress; + neighbor 2001:db8:2::46 as 25441; + # As a route collector, we want to import everything and export nothing: + import all; + export none; + import limit 1000 action restart; + password "LyJND4eoKuQz5j49"; +} + + diff --git a/data/ci/known-good/ci-apiv4-rs1-lan1-ipv4.conf b/data/ci/known-good/ci-apiv4-rs1-lan1-ipv4.conf new file mode 100644 index 000000000..347a616c3 --- /dev/null +++ b/data/ci/known-good/ci-apiv4-rs1-lan1-ipv4.conf @@ -0,0 +1,270 @@ +# +# Bird Route Server configuration generated by IXP Manager +# +# Do not edit this file, it will be overwritten. Please see: +# +# https://github.com/inex/IXP-Manager/wiki/Route-Server +# +# Generated: 2019-03-09 15:28:46 +# + +# For VLAN: Peering LAN 1 (Tag: 1, Database ID: 1) + +# standardise time formats: +timeformat base iso long; +timeformat log iso long; +timeformat protocol iso long; +timeformat route iso long; + + +log "/var/log/bird/rs1-lan1-ipv4.log" all; +log syslog all; + +define routeserverasn = 65501; +define routeserveraddress = 192.0.2.18; + +router id 192.0.2.18; + +listen bgp address routeserveraddress; + +# ignore interface up/down events +protocol device { } + +# This function excludes weird networks +# rfc1918, class D, class E, too long and too short prefixes +function avoid_martians() +prefix set martians; +{ + + martians = [ + 0.0.0.0/32-, # rfc5735 Special Use IPv4 Addresses + 0.0.0.0/0{25,32}, # Filter small prefixes + 0.0.0.0/0{0,7}, # rfc1122 Requirements for Internet Hosts -- Communication Layers 3.2.1.3 + 10.0.0.0/8+, # rfc1918 Address Allocation for Private Internets + 100.64.0.0/10+, # rfc6598 IANA-Reserved IPv4 Prefix for Shared Address Space + 127.0.0.0/8+, # rfc1122 Requirements for Internet Hosts -- Communication Layers 3.2.1.3 + 169.254.0.0/16+, # rfc3927 Dynamic Configuration of IPv4 Link-Local Addresses + 172.16.0.0/12+, # rfc1918 Address Allocation for Private Internets + 192.0.0.0/24+, # rfc6890 Special-Purpose Address Registries + 192.0.2.0/24+, # rfc5737 IPv4 Address Blocks Reserved for Documentation + 192.168.0.0/16+, # rfc1918 Address Allocation for Private Internets + 198.18.0.0/15+, # rfc2544 Benchmarking Methodology for Network Interconnect Devices + 198.51.100.0/24+, # rfc5737 IPv4 Address Blocks Reserved for Documentation + 203.0.113.0/24+, # rfc5737 IPv4 Address Blocks Reserved for Documentation + 224.0.0.0/4+, # rfc1112 Host Extensions for IP Multicasting + 240.0.0.0/4+ # rfc6890 Special-Purpose Address Registries + ]; + + + # Avoid RFC1918 and similar networks + if net ~ martians then + return false; + + return true; +} + + +## +## Standard IXP community filter +## + +function ixp_community_filter(int peerasn) +{ + if !(source = RTS_BGP) then + return false; + + # it's unwise to conduct a 32-bit check on a 16-bit value + if peerasn > 65535 then + return true; + + # Implement widely used community filtering schema. + if (0, peerasn) ~ bgp_community then + return false; + if (routeserverasn, peerasn) ~ bgp_community then + return true; + if (0, routeserverasn) ~ bgp_community then + return false; + + return true; +} + + +## +## Route Server client configuration +## + +template bgp tb_rsclient { + local as routeserverasn; + source address routeserveraddress; + import filter { + ## Prevent BGP NEXT_HOP Hijacking + if !( from = bgp_next_hop ) then + reject "BGP neighbor address [", from, "] != next hop address [", bgp_next_hop, "]", ", net:[", net, "], path:[", bgp_path, "]"; + + accept; + }; + + export all; + rs client; + +} + + + + +### AS42 - PCH DNS - VLAN Interface #3 +table t_0003_as42; + + +filter f_import_0003_as42 +prefix set allnet; +int set allas; +{ + if !(avoid_martians()) then + reject; + + # Route servers peering with route servers will cause the universe + # to collapse. Recommend evasive manoeuvers. + if (bgp_path.first != 42 ) then + reject; + + allas = [ 27, 42, 187, 297, 715, 3856, 7251, 13202, 16327, 16668, + 16686, 20144, 20539, 21312, 24999, 27678, 32978, 32979, 35160, 38052, + 44876, 45170, 45494, 48582, 48892, 50843, 51874, 52234, 52306, 54145, + 59464, 60313, 197058 ]; + + + if !(bgp_path.last ~ allas) then + reject; + + + allnet = [ 31.135.128.0/19, 31.135.128.0/21, 31.135.136.0/21, 31.135.144.0/22, 31.135.148.0/22, 31.135.152.0/22, 31.135.152.0/23, 31.135.154.0/23, 36.0.4.0/22, 63.246.32.0/20, 64.68.192.0/20, 64.68.192.0/24, 64.68.193.0/24, 64.68.194.0/24, 64.68.195.0/24, 64.68.196.0/24, 64.78.200.0/21, 64.185.240.0/20, 65.22.4.0/24, 65.22.5.0/24, 65.22.19.0/24, 65.22.23.0/24, 65.22.27.0/24, 65.22.31.0/24, 65.22.35.0/24, 65.22.39.0/24, 65.22.47.0/24, 65.22.51.0/24, 65.22.55.0/24, 65.22.59.0/24, 65.22.63.0/24, 65.22.67.0/24, 65.22.71.0/24, 65.22.79.0/24, 65.22.83.0/24, 65.22.87.0/24, 65.22.91.0/24, 65.22.95.0/24, 65.22.99.0/24, 65.22.103.0/24, 65.22.107.0/24, 65.22.111.0/24, 65.22.115.0/24, 65.22.119.0/24, 65.22.123.0/24, 65.22.127.0/24, 65.22.131.0/24, 65.22.135.0/24, 65.22.139.0/24, 65.22.143.0/24, 65.22.147.0/24, 65.22.151.0/24, 65.22.155.0/24, 65.22.159.0/24, 65.22.163.0/24, 65.22.171.0/24, 65.22.175.0/24, 65.22.179.0/24, 65.22.183.0/24, 65.22.187.0/24, 65.22.191.0/24, 65.22.195.0/24, 65.22.199.0/24, 65.22.203.0/24, 65.22.207.0/24, 65.22.211.0/24, 65.22.215.0/24, 65.22.219.0/24, 65.22.223.0/24, 65.22.227.0/24, 65.22.231.0/24, 65.22.235.0/24, 65.22.239.0/24, 65.22.243.0/24, 65.22.247.0/24, 66.96.112.0/20, 66.102.32.0/20, 66.175.104.0/24, 66.185.112.0/20, 66.225.199.0/24, 66.225.200.0/24, 66.225.201.0/24, 67.21.37.0/24, 67.22.112.0/21, 67.158.48.0/20, 68.65.112.0/21, 68.65.126.0/23, 68.65.126.0/24, 68.65.127.0/24, 69.166.10.0/24, 69.166.12.0/22, 70.40.0.0/21, 70.40.8.0/21, 72.0.48.0/20, 72.0.48.0/24, 72.0.49.0/24, 72.0.50.0/24, 72.0.51.0/24, 72.0.52.0/24, 72.0.53.0/24, 72.0.54.0/24, 72.0.55.0/24, 72.0.56.0/24, 72.0.57.0/24, 72.0.58.0/24, 72.0.59.0/24, 72.0.60.0/24, 72.0.61.0/24, 72.0.62.0/24, 72.0.63.0/24, 72.42.112.0/20, 72.42.112.0/24, 72.42.113.0/24, 72.42.114.0/24, 72.42.115.0/24, 72.42.116.0/24, 72.42.117.0/24, 72.42.118.0/24, 72.42.119.0/24, 72.42.120.0/24, 72.42.121.0/24, 72.42.122.0/24, 72.42.123.0/24, 72.42.124.0/24, 72.42.125.0/24, 72.42.126.0/24, 72.42.127.0/24, 74.63.16.0/20, 74.63.16.0/24, 74.63.17.0/24, 74.63.18.0/24, 74.63.19.0/24, 74.63.20.0/24, 74.63.21.0/24, 74.63.22.0/24, 74.63.23.0/24, 74.63.24.0/24, 74.63.25.0/24, 74.63.26.0/24, 74.63.27.0/24, 74.80.64.0/18, 74.80.64.0/24, 74.80.65.0/24, 74.80.66.0/24, 74.80.67.0/24, 74.80.68.0/24, 74.80.69.0/24, 74.80.70.0/24, 74.80.71.0/24, 74.80.72.0/24, 74.80.73.0/24, 74.80.74.0/24, 74.80.75.0/24, 74.80.76.0/24, 74.80.77.0/24, 74.80.78.0/24, 74.80.79.0/24, 74.80.80.0/24, 74.80.81.0/24, 74.80.82.0/24, 74.80.83.0/24, 74.80.84.0/24, 74.80.85.0/24, 74.80.86.0/24, 74.80.87.0/24, 74.80.88.0/24, 74.80.89.0/24, 74.80.90.0/24, 74.80.91.0/24, 74.80.92.0/24, 74.80.93.0/24, 74.80.94.0/24, 74.80.95.0/24, 74.80.96.0/24, 74.80.97.0/24, 74.80.98.0/24, 74.80.99.0/24, 74.80.100.0/24, 74.80.101.0/24, 74.80.102.0/24, 74.80.103.0/24, 74.80.104.0/24, 74.80.105.0/24, 74.80.106.0/24, 74.80.107.0/24, 74.80.108.0/24, 74.80.109.0/24, 74.80.110.0/24, 74.80.111.0/24, 74.80.112.0/24, 74.80.113.0/24, 74.80.114.0/24, 74.80.115.0/24, 74.80.116.0/24, 74.80.117.0/24, 74.80.118.0/24, 74.80.119.0/24, 74.80.120.0/24, 74.80.121.0/24, 74.80.122.0/24, 74.80.123.0/24, 74.80.124.0/24, 74.80.125.0/24, 74.80.126.0/23, 74.80.126.0/24, 74.80.127.0/24, 74.118.212.0/24, 74.118.213.0/24, 74.118.214.0/23, 75.127.16.0/20, 76.191.16.0/20, 89.19.120.0/21, 89.19.120.0/22, 89.19.124.0/23, 89.19.126.0/23, 91.201.224.0/22, 91.201.224.0/23, 91.201.224.0/24, 91.201.225.0/24, 91.201.226.0/23, 91.201.226.0/24, 91.201.227.0/24, 91.209.1.0/24, 91.209.193.0/24, 91.222.16.0/22, 91.222.40.0/24, 91.222.41.0/24, 91.222.42.0/24, 91.222.43.0/24, 91.241.93.0/24, 93.95.24.0/21, 93.95.24.0/24, 93.95.25.0/24, 93.95.26.0/24, 93.171.128.0/22, 95.47.163.0/24, 101.251.4.0/22, 114.69.222.0/24, 128.8.0.0/16, 128.161.0.0/16, 129.2.0.0/16, 130.135.0.0/16, 130.167.0.0/16, 131.161.128.0/18, 131.182.0.0/16, 139.229.0.0/16, 140.169.0.0/16, 146.5.0.0/16, 146.58.0.0/16, 150.144.0.0/16, 156.154.43.0/24, 156.154.50.0/24, 156.154.59.0/24, 156.154.96.0/24, 156.154.99.0/24, 158.154.0.0/16, 169.222.0.0/24, 183.91.132.0/22, 192.5.41.0/24, 192.12.123.0/24, 192.42.70.0/24, 192.58.36.0/24, 192.67.83.0/24, 192.67.107.0/24, 192.67.108.0/24, 192.68.52.0/24, 192.68.148.0/24, 192.68.162.0/24, 192.70.244.0/24, 192.70.249.0/24, 192.77.80.0/24, 192.84.8.0/24, 192.88.124.0/24, 192.92.65.0/24, 192.92.90.0/24, 192.100.9.0/24, 192.100.10.0/24, 192.100.15.0/24, 192.101.148.0/24, 192.102.15.0/24, 192.102.219.0/24, 192.102.233.0/24, 192.102.234.0/24, 192.112.18.0/24, 192.112.223.0/24, 192.112.224.0/24, 192.124.20.0/24, 192.138.101.0/24, 192.138.172.0/24, 192.149.89.0/24, 192.149.104.0/24, 192.149.107.0/24, 192.149.133.0/24, 192.150.32.0/21, 192.153.157.0/24, 192.188.4.0/24, 192.203.230.0/24, 192.225.64.0/19, 192.243.0.0/20, 192.243.16.0/21, 193.29.206.0/24, 193.110.16.0/22, 193.110.16.0/23, 193.110.18.0/23, 193.111.240.0/22, 193.178.228.0/23, 193.178.228.0/24, 193.178.229.0/24, 194.0.12.0/24, 194.0.13.0/24, 194.0.14.0/24, 194.0.17.0/24, 194.0.27.0/24, 194.0.36.0/24, 194.0.42.0/24, 194.0.47.0/24, 194.28.144.0/22, 194.117.58.0/24, 194.117.60.0/24, 194.117.61.0/24, 194.117.62.0/24, 194.117.63.0/24, 194.146.180.0/22, 194.146.180.0/23, 194.146.180.0/24, 194.146.181.0/24, 194.146.182.0/23, 194.146.182.0/24, 194.146.183.0/24, 194.146.228.0/22, 194.146.228.0/23, 194.146.228.0/24, 194.146.229.0/24, 194.146.230.0/23, 194.146.230.0/24, 194.146.231.0/24, 194.153.148.0/23, 195.64.162.0/23, 195.64.162.0/24, 195.64.163.0/24, 195.82.138.0/23, 198.9.0.0/16, 198.49.1.0/24, 198.116.0.0/14, 198.120.0.0/14, 198.182.28.0/24, 198.182.31.0/24, 198.182.167.0/24, 199.4.137.0/24, 199.7.64.0/24, 199.7.77.0/24, 199.7.83.0/24, 199.7.86.0/24, 199.7.91.0/24, 199.7.94.0/24, 199.7.95.0/24, 199.43.132.0/24, 199.115.156.0/24, 199.115.157.0/24, 199.120.141.0/24, 199.120.142.0/23, 199.120.144.0/24, 199.182.32.0/24, 199.182.40.0/24, 199.184.181.0/24, 199.184.182.0/23, 199.184.184.0/24, 199.249.112.0/24, 199.249.113.0/24, 199.249.114.0/24, 199.249.115.0/24, 199.249.116.0/24, 199.249.117.0/24, 199.249.118.0/24, 199.249.119.0/24, 199.249.120.0/24, 199.249.121.0/24, 199.249.122.0/24, 199.249.123.0/24, 199.249.124.0/24, 199.249.125.0/24, 199.249.126.0/24, 199.249.127.0/24, 199.254.171.0/24, 200.1.121.0/24, 200.1.131.0/24, 200.7.4.0/24, 200.16.98.0/24, 202.6.102.0/24, 202.7.4.0/22, 202.52.0.0/23, 202.53.186.0/24, 202.53.191.0/24, 203.119.88.0/23, 204.14.112.0/21, 204.19.119.0/24, 204.26.57.0/24, 204.61.208.0/21, 204.61.208.0/22, 204.61.208.0/23, 204.61.210.0/23, 204.61.210.0/24, 204.61.212.0/23, 204.61.216.0/23, 204.194.22.0/23, 204.194.22.0/24, 204.194.23.0/24, 205.132.46.0/23, 205.207.155.0/24, 206.51.254.0/23, 206.108.113.0/24, 206.196.160.0/19, 206.220.228.0/22, 206.220.228.0/23, 206.220.230.0/23, 206.223.122.0/24, 207.34.5.0/24, 207.34.6.0/23, 208.15.19.0/24, 208.49.115.64/27, 208.67.88.0/22, 216.21.2.0/23 ]; + + if ! (net ~ allnet) then + reject; + + + + accept; +} + +protocol pipe pp_0003_as42 { + description "Pipe for AS42 - PCH DNS - VLAN Interface 3"; + table master; + mode transparent; + peer table t_0003_as42; + import filter f_import_0003_as42; + export where ixp_community_filter(42); +} + +protocol bgp pb_0003_as42 from tb_rsclient { + description "AS42 - PCH DNS"; + neighbor 10.1.0.36 as 42; + import limit 2000 action restart; + table t_0003_as42; + password "mcWsqMdzGwTKt67g"; +} + + +### AS112 - AS112 - VLAN Interface #4 +table t_0004_as112; + + +filter f_import_0004_as112 +prefix set allnet; +int set allas; +{ + if !(avoid_martians()) then + reject; + + # Route servers peering with route servers will cause the universe + # to collapse. Recommend evasive manoeuvers. + if (bgp_path.first != 112 ) then + reject; + + allas = [ 112 ]; + + + if !(bgp_path.last ~ allas) then + reject; + + + allnet = [ 192.175.48.0/24 ]; + + if ! (net ~ allnet) then + reject; + + + + accept; +} + +protocol pipe pp_0004_as112 { + description "Pipe for AS112 - AS112 - VLAN Interface 4"; + table master; + mode transparent; + peer table t_0004_as112; + import filter f_import_0004_as112; + export where ixp_community_filter(112); +} + +protocol bgp pb_0004_as112 from tb_rsclient { + description "AS112 - AS112"; + neighbor 10.1.0.6 as 112; + import limit 20 action restart; + table t_0004_as112; + password "w83fmGpRDtaKomQo"; +} + + +### AS1213 - HEAnet - VLAN Interface #1 +table t_0001_as1213; + + +filter f_import_0001_as1213 +prefix set allnet; +int set allas; +{ + if !(avoid_martians()) then + reject; + + # Route servers peering with route servers will cause the universe + # to collapse. Recommend evasive manoeuvers. + if (bgp_path.first != 1213 ) then + reject; + + allas = [ 112, 1213, 1921, 2128, 2850, 42310 ]; + + + if !(bgp_path.last ~ allas) then + reject; + + + allnet = [ 4.53.84.128/26, 4.53.146.192/26, 77.72.72.0/21, 87.32.0.0/12, 91.123.224.0/20, 134.226.0.0/16, 136.201.0.0/16, 136.206.0.0/16, 137.43.0.0/16, 140.203.0.0/16, 143.239.0.0/16, 147.252.0.0/16, 149.153.0.0/16, 149.157.0.0/16, 157.190.0.0/16, 160.6.0.0/16, 176.97.158.0/24, 192.174.68.0/24, 192.175.48.0/24, 193.1.0.0/16, 193.242.111.0/24, 194.0.24.0/24, 194.0.25.0/24, 194.0.26.0/24, 194.88.240.0/23, 212.3.242.128/26 ]; + + if ! (net ~ allnet) then + reject; + + + + accept; +} + +protocol pipe pp_0001_as1213 { + description "Pipe for AS1213 - HEAnet - VLAN Interface 1"; + table master; + mode transparent; + peer table t_0001_as1213; + import filter f_import_0001_as1213; + export where ixp_community_filter(1213); +} + +protocol bgp pb_0001_as1213 from tb_rsclient { + description "AS1213 - HEAnet"; + neighbor 10.1.0.10 as 1213; + import limit 1000 action restart; + table t_0001_as1213; + password "N7rX2SdfbRsyBLTm"; +} + + diff --git a/data/ci/known-good/ci-apiv4-rs1-lan1-ipv6.conf b/data/ci/known-good/ci-apiv4-rs1-lan1-ipv6.conf new file mode 100644 index 000000000..528bf6ea9 --- /dev/null +++ b/data/ci/known-good/ci-apiv4-rs1-lan1-ipv6.conf @@ -0,0 +1,227 @@ +# +# Bird Route Server configuration generated by IXP Manager +# +# Do not edit this file, it will be overwritten. Please see: +# +# https://github.com/inex/IXP-Manager/wiki/Route-Server +# +# Generated: 2019-03-09 15:29:41 +# + +# For VLAN: Peering LAN 1 (Tag: 1, Database ID: 1) + +# standardise time formats: +timeformat base iso long; +timeformat log iso long; +timeformat protocol iso long; +timeformat route iso long; + + +log "/var/log/bird/rs1-lan1-ipv6.log" all; +log syslog all; + +define routeserverasn = 65501; +define routeserveraddress = 2001:db8::18; + +router id 192.0.2.18; + +listen bgp address routeserveraddress; + +# ignore interface up/down events +protocol device { } + +# This function excludes weird networks +# rfc1918, class D, class E, too long and too short prefixes +function avoid_martians() +prefix set martians; +{ + + martians = [ + ::/0, # Default (can be advertised as a route in BGP to peers if desired) + ::/96, # IPv4-compatible IPv6 address - deprecated by RFC4291 + ::/128, # Unspecified address + ::1/128, # Local host loopback address + ::ffff:0.0.0.0/96+, # IPv4-mapped addresses + ::224.0.0.0/100+, # Compatible address (IPv4 format) + ::127.0.0.0/104+, # Compatible address (IPv4 format) + ::0.0.0.0/104+, # Compatible address (IPv4 format) + ::255.0.0.0/104+, # Compatible address (IPv4 format) + 0000::/8+, # Pool used for unspecified, loopback and embedded IPv4 addresses + 0200::/7+, # OSI NSAP-mapped prefix set (RFC4548) - deprecated by RFC4048 + 3ffe::/16+, # Former 6bone, now decommissioned + 2001:db8::/32+, # Reserved by IANA for special purposes and documentation + 2002:e000::/20+, # Invalid 6to4 packets (IPv4 multicast) + 2002:7f00::/24+, # Invalid 6to4 packets (IPv4 loopback) + 2002:0000::/24+, # Invalid 6to4 packets (IPv4 default) + 2002:ff00::/24+, # Invalid 6to4 packets + 2002:0a00::/24+, # Invalid 6to4 packets (IPv4 private 10.0.0.0/8 network) + 2002:ac10::/28+, # Invalid 6to4 packets (IPv4 private 172.16.0.0/12 network) + 2002:c0a8::/32+, # Invalid 6to4 packets (IPv4 private 192.168.0.0/16 network) + fc00::/7+, # Unicast Unique Local Addresses (ULA) - RFC 4193 + fe80::/10+, # Link-local Unicast + fec0::/10+, # Site-local Unicast - deprecated by RFC 3879 (replaced by ULA) + ff00::/8+, # Multicast + ::/0{49,128} # Filter small prefixes + ]; + + + # Avoid RFC1918 and similar networks + if net ~ martians then + return false; + + return true; +} + + +## +## Standard IXP community filter +## + +function ixp_community_filter(int peerasn) +{ + if !(source = RTS_BGP) then + return false; + + # it's unwise to conduct a 32-bit check on a 16-bit value + if peerasn > 65535 then + return true; + + # Implement widely used community filtering schema. + if (0, peerasn) ~ bgp_community then + return false; + if (routeserverasn, peerasn) ~ bgp_community then + return true; + if (0, routeserverasn) ~ bgp_community then + return false; + + return true; +} + + +## +## Route Server client configuration +## + +template bgp tb_rsclient { + local as routeserverasn; + source address routeserveraddress; + import filter { + ## Prevent BGP NEXT_HOP Hijacking + if !( from = bgp_next_hop ) then + reject "BGP neighbor address [", from, "] != next hop address [", bgp_next_hop, "]", ", net:[", net, "], path:[", bgp_path, "]"; + + accept; + }; + + export all; + rs client; + missing lladdr ignore; + +} + + + + +### AS1213 - HEAnet - VLAN Interface #1 +table t_0001_as1213; + + +filter f_import_0001_as1213 +prefix set allnet; +int set allas; +{ + if !(avoid_martians()) then + reject; + + # Route servers peering with route servers will cause the universe + # to collapse. Recommend evasive manoeuvers. + if (bgp_path.first != 1213 ) then + reject; + + allas = [ 112, 1213, 1921, 2128, 2850, 42310 ]; + + + if !(bgp_path.last ~ allas) then + reject; + + + allnet = [ 2001:678:20::/48, 2001:678:24::/48, 2001:67c:1bc::/48, 2001:67c:10b8::/48, 2001:67c:10e0::/48, 2001:770::/32, 2001:7f8:18::/48, 2001:1900:2205::/48, 2001:1900:2206::/48, 2620:4f:8000::/48, 2a01:4b0::/32 ]; + + if ! (net ~ allnet) then + reject; + + + + accept; +} + +protocol pipe pp_0001_as1213 { + description "Pipe for AS1213 - HEAnet - VLAN Interface 1"; + table master; + mode transparent; + peer table t_0001_as1213; + import filter f_import_0001_as1213; + export where ixp_community_filter(1213); +} + +protocol bgp pb_0001_as1213 from tb_rsclient { + description "AS1213 - HEAnet"; + neighbor 2001:db8:1::10 as 1213; + import limit 1000 action restart; + table t_0001_as1213; + password "N7rX2SdfbRsyBLTm"; +} + + +### AS25441 - Imagine - VLAN Interface #6 +table t_0006_as25441; + + +filter f_import_0006_as25441 +prefix set allnet; +int set allas; +{ + if !(avoid_martians()) then + reject; + + # Route servers peering with route servers will cause the universe + # to collapse. Recommend evasive manoeuvers. + if (bgp_path.first != 25441 ) then + reject; + + allas = [ 11521, 25441, 34317, 35272, 39064, 43178, 43610, 47615, 48342, 49573, + 197853, 197904 ]; + + + if !(bgp_path.last ~ allas) then + reject; + + + allnet = [ 2001:67c:338::/48, 2001:4d68::/32{32,48}, 2a01:268::/32{32,48}, 2a01:8f80::/32{32,48} ]; + + if ! (net ~ allnet) then + reject; + + + + accept; +} + +protocol pipe pp_0006_as25441 { + description "Pipe for AS25441 - Imagine - VLAN Interface 6"; + table master; + mode transparent; + peer table t_0006_as25441; + import filter f_import_0006_as25441; + export where ixp_community_filter(25441); +} + +protocol bgp pb_0006_as25441 from tb_rsclient { + description "AS25441 - Imagine"; + neighbor 2001:db8:1::8 as 25441; + import limit 1000 action restart; + table t_0006_as25441; + password "X8Ks9QnbER9cyzU3"; +} + + diff --git a/data/ci/known-good/ci-apiv4-rs1-lan2-ipv4.conf b/data/ci/known-good/ci-apiv4-rs1-lan2-ipv4.conf new file mode 100644 index 000000000..f43110d88 --- /dev/null +++ b/data/ci/known-good/ci-apiv4-rs1-lan2-ipv4.conf @@ -0,0 +1,226 @@ +# +# Bird Route Server configuration generated by IXP Manager +# +# Do not edit this file, it will be overwritten. Please see: +# +# https://github.com/inex/IXP-Manager/wiki/Route-Server +# +# Generated: 2019-03-09 15:29:56 +# + +# For VLAN: Peering LAN 2 (Tag: 2, Database ID: 2) + +# standardise time formats: +timeformat base iso long; +timeformat log iso long; +timeformat protocol iso long; +timeformat route iso long; + + +log "/var/log/bird/rs1-lan2-ipv4.log" all; +log syslog all; + +define routeserverasn = 65501; +define routeserveraddress = 192.0.2.19; + +router id 192.0.2.19; + +listen bgp address routeserveraddress; + +# ignore interface up/down events +protocol device { } + +# This function excludes weird networks +# rfc1918, class D, class E, too long and too short prefixes +function avoid_martians() +prefix set martians; +{ + + martians = [ + 0.0.0.0/32-, # rfc5735 Special Use IPv4 Addresses + 0.0.0.0/0{25,32}, # Filter small prefixes + 0.0.0.0/0{0,7}, # rfc1122 Requirements for Internet Hosts -- Communication Layers 3.2.1.3 + 10.0.0.0/8+, # rfc1918 Address Allocation for Private Internets + 100.64.0.0/10+, # rfc6598 IANA-Reserved IPv4 Prefix for Shared Address Space + 127.0.0.0/8+, # rfc1122 Requirements for Internet Hosts -- Communication Layers 3.2.1.3 + 169.254.0.0/16+, # rfc3927 Dynamic Configuration of IPv4 Link-Local Addresses + 172.16.0.0/12+, # rfc1918 Address Allocation for Private Internets + 192.0.0.0/24+, # rfc6890 Special-Purpose Address Registries + 192.0.2.0/24+, # rfc5737 IPv4 Address Blocks Reserved for Documentation + 192.168.0.0/16+, # rfc1918 Address Allocation for Private Internets + 198.18.0.0/15+, # rfc2544 Benchmarking Methodology for Network Interconnect Devices + 198.51.100.0/24+, # rfc5737 IPv4 Address Blocks Reserved for Documentation + 203.0.113.0/24+, # rfc5737 IPv4 Address Blocks Reserved for Documentation + 224.0.0.0/4+, # rfc1112 Host Extensions for IP Multicasting + 240.0.0.0/4+ # rfc6890 Special-Purpose Address Registries + ]; + + + # Avoid RFC1918 and similar networks + if net ~ martians then + return false; + + return true; +} + + +## +## Standard IXP community filter +## + +function ixp_community_filter(int peerasn) +{ + if !(source = RTS_BGP) then + return false; + + # support for BGP Large Communities + if (routeserverasn, 0, peerasn) ~ bgp_large_community then + return false; + if (routeserverasn, 1, peerasn) ~ bgp_large_community then + return true; + if (routeserverasn, 0, 0) ~ bgp_large_community then + return false; + if (routeserverasn, 1, 0) ~ bgp_large_community then + return true; + + # it's unwise to conduct a 32-bit check on a 16-bit value + if peerasn > 65535 then + return true; + + # Implement widely used community filtering schema. + if (0, peerasn) ~ bgp_community then + return false; + if (routeserverasn, peerasn) ~ bgp_community then + return true; + if (0, routeserverasn) ~ bgp_community then + return false; + + return true; +} + + +## +## Route Server client configuration +## + +template bgp tb_rsclient { + local as routeserverasn; + source address routeserveraddress; + import filter { + ## Prevent BGP NEXT_HOP Hijacking + if !( from = bgp_next_hop ) then + reject "BGP neighbor address [", from, "] != next hop address [", bgp_next_hop, "]", ", net:[", net, "], path:[", bgp_path, "]"; + + accept; + }; + + export all; + rs client; + +} + + + + +### AS112 - AS112 - VLAN Interface #5 +table t_0005_as112; + + +filter f_import_0005_as112 +prefix set allnet; +int set allas; +{ + if !(avoid_martians()) then + reject; + + # Route servers peering with route servers will cause the universe + # to collapse. Recommend evasive manoeuvers. + if (bgp_path.first != 112 ) then + reject; + + allas = [ 112 ]; + + + if !(bgp_path.last ~ allas) then + reject; + + + allnet = [ 192.175.48.0/24 ]; + + if ! (net ~ allnet) then + reject; + + + + accept; +} + +protocol pipe pp_0005_as112 { + description "Pipe for AS112 - AS112 - VLAN Interface 5"; + table master; + mode transparent; + peer table t_0005_as112; + import filter f_import_0005_as112; + export where ixp_community_filter(112); +} + +protocol bgp pb_0005_as112 from tb_rsclient { + description "AS112 - AS112"; + neighbor 10.2.0.6 as 112; + import limit 20 action restart; + table t_0005_as112; + password "Pz8VYMNwEdCjKz68"; +} + + +### AS1213 - HEAnet - VLAN Interface #2 +table t_0002_as1213; + + +filter f_import_0002_as1213 +prefix set allnet; +int set allas; +{ + if !(avoid_martians()) then + reject; + + # Route servers peering with route servers will cause the universe + # to collapse. Recommend evasive manoeuvers. + if (bgp_path.first != 1213 ) then + reject; + + allas = [ 112, 1213, 1921, 2128, 2850, 42310 ]; + + + if !(bgp_path.last ~ allas) then + reject; + + + allnet = [ 4.53.84.128/26, 4.53.146.192/26, 77.72.72.0/21, 87.32.0.0/12, 91.123.224.0/20, 134.226.0.0/16, 136.201.0.0/16, 136.206.0.0/16, 137.43.0.0/16, 140.203.0.0/16, 143.239.0.0/16, 147.252.0.0/16, 149.153.0.0/16, 149.157.0.0/16, 157.190.0.0/16, 160.6.0.0/16, 176.97.158.0/24, 192.174.68.0/24, 192.175.48.0/24, 193.1.0.0/16, 193.242.111.0/24, 194.0.24.0/24, 194.0.25.0/24, 194.0.26.0/24, 194.88.240.0/23, 212.3.242.128/26 ]; + + if ! (net ~ allnet) then + reject; + + + + accept; +} + +protocol pipe pp_0002_as1213 { + description "Pipe for AS1213 - HEAnet - VLAN Interface 2"; + table master; + mode transparent; + peer table t_0002_as1213; + import filter f_import_0002_as1213; + export where ixp_community_filter(1213); +} + +protocol bgp pb_0002_as1213 from tb_rsclient { + description "AS1213 - HEAnet"; + neighbor 10.2.0.11 as 1213; + import limit 1000 action restart; + table t_0002_as1213; + password "u5zSNJLAVT87RGXQ"; +} + + diff --git a/data/ci/known-good/ci-apiv4-rs1-lan2-ipv6.conf b/data/ci/known-good/ci-apiv4-rs1-lan2-ipv6.conf new file mode 100644 index 000000000..4911c6dbe --- /dev/null +++ b/data/ci/known-good/ci-apiv4-rs1-lan2-ipv6.conf @@ -0,0 +1,175 @@ +# +# Bird Route Server configuration generated by IXP Manager +# +# Do not edit this file, it will be overwritten. Please see: +# +# https://github.com/inex/IXP-Manager/wiki/Route-Server +# +# Generated: 2019-03-09 15:30:10 +# + +# For VLAN: Peering LAN 2 (Tag: 2, Database ID: 2) + +# standardise time formats: +timeformat base iso long; +timeformat log iso long; +timeformat protocol iso long; +timeformat route iso long; + + +log "/var/log/bird/rs1-lan2-ipv6.log" all; +log syslog all; + +define routeserverasn = 65501; +define routeserveraddress = 2001:db8::19; + +router id 192.0.2.19; + +listen bgp address routeserveraddress; + +# ignore interface up/down events +protocol device { } + +# This function excludes weird networks +# rfc1918, class D, class E, too long and too short prefixes +function avoid_martians() +prefix set martians; +{ + + martians = [ + ::/0, # Default (can be advertised as a route in BGP to peers if desired) + ::/96, # IPv4-compatible IPv6 address - deprecated by RFC4291 + ::/128, # Unspecified address + ::1/128, # Local host loopback address + ::ffff:0.0.0.0/96+, # IPv4-mapped addresses + ::224.0.0.0/100+, # Compatible address (IPv4 format) + ::127.0.0.0/104+, # Compatible address (IPv4 format) + ::0.0.0.0/104+, # Compatible address (IPv4 format) + ::255.0.0.0/104+, # Compatible address (IPv4 format) + 0000::/8+, # Pool used for unspecified, loopback and embedded IPv4 addresses + 0200::/7+, # OSI NSAP-mapped prefix set (RFC4548) - deprecated by RFC4048 + 3ffe::/16+, # Former 6bone, now decommissioned + 2001:db8::/32+, # Reserved by IANA for special purposes and documentation + 2002:e000::/20+, # Invalid 6to4 packets (IPv4 multicast) + 2002:7f00::/24+, # Invalid 6to4 packets (IPv4 loopback) + 2002:0000::/24+, # Invalid 6to4 packets (IPv4 default) + 2002:ff00::/24+, # Invalid 6to4 packets + 2002:0a00::/24+, # Invalid 6to4 packets (IPv4 private 10.0.0.0/8 network) + 2002:ac10::/28+, # Invalid 6to4 packets (IPv4 private 172.16.0.0/12 network) + 2002:c0a8::/32+, # Invalid 6to4 packets (IPv4 private 192.168.0.0/16 network) + fc00::/7+, # Unicast Unique Local Addresses (ULA) - RFC 4193 + fe80::/10+, # Link-local Unicast + fec0::/10+, # Site-local Unicast - deprecated by RFC 3879 (replaced by ULA) + ff00::/8+, # Multicast + ::/0{49,128} # Filter small prefixes + ]; + + + # Avoid RFC1918 and similar networks + if net ~ martians then + return false; + + return true; +} + + +## +## Standard IXP community filter +## + +function ixp_community_filter(int peerasn) +{ + if !(source = RTS_BGP) then + return false; + + # it's unwise to conduct a 32-bit check on a 16-bit value + if peerasn > 65535 then + return true; + + # Implement widely used community filtering schema. + if (0, peerasn) ~ bgp_community then + return false; + if (routeserverasn, peerasn) ~ bgp_community then + return true; + if (0, routeserverasn) ~ bgp_community then + return false; + + return true; +} + + +## +## Route Server client configuration +## + +template bgp tb_rsclient { + local as routeserverasn; + source address routeserveraddress; + import filter { + ## Prevent BGP NEXT_HOP Hijacking + if !( from = bgp_next_hop ) then + reject "BGP neighbor address [", from, "] != next hop address [", bgp_next_hop, "]", ", net:[", net, "], path:[", bgp_path, "]"; + + accept; + }; + + export all; + rs client; + missing lladdr ignore; + +} + + + + +### AS1213 - HEAnet - VLAN Interface #2 +table t_0002_as1213; + + +filter f_import_0002_as1213 +prefix set allnet; +int set allas; +{ + if !(avoid_martians()) then + reject; + + # Route servers peering with route servers will cause the universe + # to collapse. Recommend evasive manoeuvers. + if (bgp_path.first != 1213 ) then + reject; + + allas = [ 112, 1213, 1921, 2128, 2850, 42310 ]; + + + if !(bgp_path.last ~ allas) then + reject; + + + allnet = [ 2001:678:20::/48, 2001:678:24::/48, 2001:67c:1bc::/48, 2001:67c:10b8::/48, 2001:67c:10e0::/48, 2001:770::/32, 2001:7f8:18::/48, 2001:1900:2205::/48, 2001:1900:2206::/48, 2620:4f:8000::/48, 2a01:4b0::/32 ]; + + if ! (net ~ allnet) then + reject; + + + + accept; +} + +protocol pipe pp_0002_as1213 { + description "Pipe for AS1213 - HEAnet - VLAN Interface 2"; + table master; + mode transparent; + peer table t_0002_as1213; + import filter f_import_0002_as1213; + export where ixp_community_filter(1213); +} + +protocol bgp pb_0002_as1213 from tb_rsclient { + description "AS1213 - HEAnet"; + neighbor 2001:db8:2::11 as 1213; + import limit 1000 action restart; + table t_0002_as1213; + password "u5zSNJLAVT87RGXQ"; +} + + diff --git a/data/ci/known-good/ci-apiv4-yaml-1.yaml b/data/ci/known-good/ci-apiv4-yaml-1.yaml new file mode 100644 index 000000000..e2db793e5 --- /dev/null +++ b/data/ci/known-good/ci-apiv4-yaml-1.yaml @@ -0,0 +1,79 @@ +interfacescust: + + - name: Port-Channel1 + type: edge + description: "HEAnet" + dot1q: no + shutdown: no + lagindex: 1 + lagmaster: yes + lagmembers: + - "GigabitEthernet3" + - "GigabitEthernet4" + fastlacp: yes + virtualinterfaceid: 1 + vlans: + - number: 1 + + - name: GigabitEthernet3 + type: edge + description: "HEAnet" + dot1q: no + shutdown: no + autoneg: yes + speed: 1000 + lagindex: 1 + lagmaster: no + fastlacp: yes + virtualinterfaceid: 1 + vlans: + - number: 1 + + - name: GigabitEthernet4 + type: edge + description: "HEAnet" + dot1q: no + shutdown: no + autoneg: yes + speed: 1000 + lagindex: 1 + lagmaster: no + fastlacp: yes + virtualinterfaceid: 1 + vlans: + - number: 1 + + - name: GigabitEthernet6 + type: edge + description: "AS112" + dot1q: no + shutdown: no + autoneg: yes + speed: 10 + virtualinterfaceid: 4 + vlans: + - number: 1 + + - name: GigabitEthernet8 + type: edge + description: "PCH DNS" + dot1q: no + shutdown: no + autoneg: yes + speed: 100 + virtualinterfaceid: 3 + vlans: + - number: 1 + + - name: GigabitEthernet9 + type: edge + description: "Imagine" + dot1q: no + shutdown: no + autoneg: yes + speed: 1000 + virtualinterfaceid: 6 + vlans: + - number: 1 + + diff --git a/data/ci/known-good/ci-apiv4-yaml-2.yaml b/data/ci/known-good/ci-apiv4-yaml-2.yaml new file mode 100644 index 000000000..1c56f093a --- /dev/null +++ b/data/ci/known-good/ci-apiv4-yaml-2.yaml @@ -0,0 +1,53 @@ +interfacescust: + + - name: Port-Channel2 + type: edge + description: "HEAnet" + dot1q: yes + shutdown: no + lagindex: 2 + lagmaster: yes + lagmembers: + - "GigabitEthernet1" + fastlacp: no + virtualinterfaceid: 2 + vlans: + - number: 2 + + - name: GigabitEthernet1 + type: edge + description: "HEAnet" + dot1q: yes + shutdown: no + autoneg: yes + speed: 1000 + lagindex: 2 + lagmaster: no + fastlacp: no + virtualinterfaceid: 2 + vlans: + - number: 2 + + - name: GigabitEthernet6 + type: edge + description: "AS112" + dot1q: no + shutdown: no + autoneg: yes + speed: 10 + virtualinterfaceid: 5 + vlans: + - number: 2 + + - name: GigabitEthernet8 + type: edge + description: "Imagine" + dot1q: no + shutdown: no + autoneg: yes + speed: 10000 + virtualinterfaceid: 7 + vlans: + - number: 2 + + diff --git a/data/ci/known-good/ci-apiv4-yaml-switch1.yaml b/data/ci/known-good/ci-apiv4-yaml-switch1.yaml new file mode 100644 index 000000000..e2db793e5 --- /dev/null +++ b/data/ci/known-good/ci-apiv4-yaml-switch1.yaml @@ -0,0 +1,79 @@ +interfacescust: + + - name: Port-Channel1 + type: edge + description: "HEAnet" + dot1q: no + shutdown: no + lagindex: 1 + lagmaster: yes + lagmembers: + - "GigabitEthernet3" + - "GigabitEthernet4" + fastlacp: yes + virtualinterfaceid: 1 + vlans: + - number: 1 + + - name: GigabitEthernet3 + type: edge + description: "HEAnet" + dot1q: no + shutdown: no + autoneg: yes + speed: 1000 + lagindex: 1 + lagmaster: no + fastlacp: yes + virtualinterfaceid: 1 + vlans: + - number: 1 + + - name: GigabitEthernet4 + type: edge + description: "HEAnet" + dot1q: no + shutdown: no + autoneg: yes + speed: 1000 + lagindex: 1 + lagmaster: no + fastlacp: yes + virtualinterfaceid: 1 + vlans: + - number: 1 + + - name: GigabitEthernet6 + type: edge + description: "AS112" + dot1q: no + shutdown: no + autoneg: yes + speed: 10 + virtualinterfaceid: 4 + vlans: + - number: 1 + + - name: GigabitEthernet8 + type: edge + description: "PCH DNS" + dot1q: no + shutdown: no + autoneg: yes + speed: 100 + virtualinterfaceid: 3 + vlans: + - number: 1 + + - name: GigabitEthernet9 + type: edge + description: "Imagine" + dot1q: no + shutdown: no + autoneg: yes + speed: 1000 + virtualinterfaceid: 6 + vlans: + - number: 1 + + diff --git a/data/ci/known-good/ci-apiv4-yaml-switch2.yaml b/data/ci/known-good/ci-apiv4-yaml-switch2.yaml new file mode 100644 index 000000000..1c56f093a --- /dev/null +++ b/data/ci/known-good/ci-apiv4-yaml-switch2.yaml @@ -0,0 +1,53 @@ +interfacescust: + + - name: Port-Channel2 + type: edge + description: "HEAnet" + dot1q: yes + shutdown: no + lagindex: 2 + lagmaster: yes + lagmembers: + - "GigabitEthernet1" + fastlacp: no + virtualinterfaceid: 2 + vlans: + - number: 2 + + - name: GigabitEthernet1 + type: edge + description: "HEAnet" + dot1q: yes + shutdown: no + autoneg: yes + speed: 1000 + lagindex: 2 + lagmaster: no + fastlacp: no + virtualinterfaceid: 2 + vlans: + - number: 2 + + - name: GigabitEthernet6 + type: edge + description: "AS112" + dot1q: no + shutdown: no + autoneg: yes + speed: 10 + virtualinterfaceid: 5 + vlans: + - number: 2 + + - name: GigabitEthernet8 + type: edge + description: "Imagine" + dot1q: no + shutdown: no + autoneg: yes + speed: 10000 + virtualinterfaceid: 7 + vlans: + - number: 2 + + diff --git a/data/ci/known-good/ci-dotenv-complex.txt b/data/ci/known-good/ci-dotenv-complex.txt new file mode 100644 index 000000000..74e861875 --- /dev/null +++ b/data/ci/known-good/ci-dotenv-complex.txt @@ -0,0 +1,329 @@ +####################################################################################### +# +# IXP Manager V4+ configuration. +# +# + +# Run "artisan key:generate" to set the application key: +APP_KEY=base64:tu7P5D2vImFs5QMq0OIzXn7iqdR3ugqQqqsnviTGIho= + +# Set this to false in production (but change it to true if you have installation or +# other issues running IXP Manager). +APP_DEBUG=false + +# Web address where IXP Manager is accessed. This is a **required** setting. It is +# currently used for generating all URLs within IXP Manager (action, assets, etc.). +# It is also used / required for sending emails via CLI scripts. +# +APP_URL=https://ixp.example.com + +TELESCOPE_URL_PATH=ixp/telescope +TELESCOPE_ENABLED=false + + +# HTTPS Only Cookies +# 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. +# +SESSION_SECURE_COOKIE=false + + +# See http://php.net/manual/en/timezones.php for a list of timezones: +APP_TIMEZONE=Europe/Dublin + +# Laravel log format (storage/log). See config/logging.php and +# https://laravel.com/docs/11.x/logging +# LOG_CHANNEL="daily" + +# info by default, one of: debug, info, notice, warning, error, critical, alert, emergency. +LOG_LEVEL=debug + +# MySQL Connection Details +DB_HOST=127.0.0.1 +DB_DATABASE=ixp +DB_USERNAME=ixp +DB_PASSWORD=password + + +####################################################################################### +### Email Settings. +# +# We use Laravel's mail system - see: https://docs.ixpmanager.org/latest/usage/email/ +# +# The default setting are as follows: +# +# MAIL_MAILER=smtp +# MAIL_HOST=localhost +# MAIL_PORT=25 +# MAIL_ENCRYPTION=false + + + +####################################################################################### +### Identity +# +# Used throughout IXP Manager in various ways. +# + +# Used in various emails, etc. +IDENTITY_SITENAME="IXP Manager" +APP_NAME="IXP Manager" + +# Shown in title bar of web portal. +IDENTITY_TITLENAME="IXP Manager" + +IDENTITY_LEGALNAME="Some City IXP CLG" +IDENTITY_CITY=Cork +IDENTITY_COUNTRY=IE +IDENTITY_ORGNAME="Some City IXP" + +# As well as uses in other places, emails are sent from the following name/email: +IDENTITY_NAME="Some City IXP" +IDENTITY_EMAIL="ixp@example.com" + +IDENTITY_TESTEMAIL="ixp@example.com" + +# Used on some traffic graphs: +IDENTITY_WATERMARK="Some City IXP" + +IDENTITY_SUPPORT_EMAIL="ixp@example.com" +IDENTITY_SUPPORT_PHONE="+353 20 912 2000" +IDENTITY_SUPPORT_HOURS=8x5 + +# IXP Manager will need to send alert emails. This is the recipient email for these alerts. +IDENTITY_ALERTS_EMAIL="ixp@example.com" +IDENTITY_ALERTS_NAME="IXP Manager Alerts" + + +IDENTITY_BILLING_EMAIL="ixp@example.com" +IDENTITY_BILLING_PHONE="+1 111 555 5555" +IDENTITY_BILLING_HOURS=24x7 + +# Web address of your IXP's website. Used in IX-F Export schema, etc. +IDENTITY_CORPORATE_URL=https://www.example.com/ + +# The logo to show on the login page. Should be a URL. +# (the example here works - the leading '//' means the browser should match http/https based on the web page) +IDENTITY_BIGLOGO=https://www.ixpmanager.org/images/logos/ixp-manager.png + + + +######################################################################################### +### Member vs. Customer +### +### IXP Manager is an open source project and typically used in member-owned IXPs. +### As such, the language used mostly is 'member'. To change this to 'customer' just +### uncomment the following lines: + +IXP_FE_FRONTEND_CUSTOMER_ONE=customer +IXP_FE_FRONTEND_CUSTOMER_MANY=customers +IXP_FE_FRONTEND_CUSTOMER_OWNER="customer's" +IXP_FE_FRONTEND_CUSTOMER_OWNERS="customers'" + + + +####################################################################################### +### Features +# + +# See: https://docs.ixpmanager.org/latest/features/reseller/ +IXP_RESELLER_ENABLED=false + +# See: https://docs.ixpmanager.org/latest/features/as112/ +IXP_AS112_UI_ACTIVE=false + + +####################################################################################### +### Frontend controllers and controller configuration +# +# Some frontend controllers are disabled by default. This is for a variety of reasons +# including: additional configuration may be required, maintain backwards +# compatibility, etc. + +# Allow customers / admins to upload logos for members. Set to false to enabled. +# See: https://docs.ixpmanager.org/latest/usage/customers/#customer-logos +IXP_FE_FRONTEND_DISABLED_LOGO=false + + +# Send email notifications when a customer's billing details are updated. +# See: https://docs.ixpmanager.org/latest/usage/customers/#notification-of-billing-details-changed +IXP_FE_CUSTOMER_BILLING_UPDATES_NOTIFY="mail@example.com" + + +# Disable links to the peering matrix if you have not set it up (with sflow): +# IXP_FE_FRONTEND_DISABLED_PEERING_MATRIX=true + +# Enable the UI for route server community-based filtering by uncommenting this line: +# IXP_FE_FRONTEND_DISABLED_RS_FILTERS=false + +####################################################################################### +### Graphing - see https://docs.ixpmanager.org/latest/grapher/introduction + +# Enable the backends you have configured. E.g.: +# GRAPHER_BACKENDS="mrtg|sflow|smokeping" + +# On a new installation, we just use placeholders from the dummy backend: +GRAPHER_BACKENDS="mrtg|dummy" + +# With the cache enabled, IXP Manager does not have to regenerate / reload / reprocess +# log / rrd / image files if we have cached them and they are less than 5mins old. This +# is enabled by default which is the recommended setting. +GRAPHER_CACHE_ENABLED=true + +################################################################################# +## Grapher - Mrtg - see: https://docs.ixpmanager.org/latest/grapher/mrtg/ +## + +# For backwards compatibility, the default is 'log' but 'rrd' is more modern: +GRAPHER_BACKEND_MRTG_DBTYPE=rrd + +# The defaults for these are '/tmp' to require you to change them to something +# more sensible such as: +GRAPHER_BACKEND_MRTG_WORKDIR=/srv/mrtg +GRAPHER_BACKEND_MRTG_LOGDIR=/srv/mrtg + +################################################################################# +## Grapher - sflow - see: https://docs.ixpmanager.org/latest/grapher/sflow/ +## + +GRAPHER_BACKEND_SFLOW_ENABLED=true +GRAPHER_BACKEND_SFLOW_ROOT=http://sflow-server.example.com/grapher-sflow + + +################################################################################# +## Grapher - smokeping - see: https://docs.ixpmanager.org/latest/grapher/smokeping/ +## + +# Mark it as enabled (this just affects whether certain UI elements are shown): +GRAPHER_BACKEND_SMOKEPING_ENABLED=true + +# And set the default location to fetch the Smokeping graphs from: +GRAPHER_BACKEND_SMOKEPING_URL=http://www.example.com/smokeping + + +################################################################################# +## IX-F Member Export - see: https://docs.ixpmanager.org/latest/features/ixf-export/ + + +IXP_API_JSONEXPORTSCHEMA_PUBLIC=true + +# Some variables can be excluded as required. +# See: https://docs.ixpmanager.org/latest/features/ixf-export/ + +IXP_API_JSONEXPORTSCHEMA_EXCLUDE_SWITCH="model|software" +IXP_API_JSONEXPORTSCHEMA_EXCLUDE_IXP="name|url" +IXP_API_JSONEXPORTSCHEMA_EXCLUDE_MEMBER="asnum|name" +IXP_API_JSONEXPORTSCHEMA_EXCLUDE_INTINFO="mac_addresses|routeserver" + +# Exclude members with certain AS numbers +IXP_API_JSONEXPORTSCHEMA_EXCLUDE_ASNUM="65001|65002|65003" + +# Exclude members with certain tags +IXP_API_JSONEXPORTSCHEMA_EXCLUDE_TAGS="tag1|tag2" + +# Exclude documentation ASNs (64496 - 64511, 65536 - 65551) +IXP_API_JSONEXPORTSCHEMA_EXCLUDE_RFC5398=true + +# Exclude private ASNs (64512 - 65534, 4200000000 - 4294967294) + +# Exclude private ASNs: +IXP_API_JSONEXPORTSCHEMA_EXCLUDE_RFC6996=true + + +####################################################################################### +### Skinning +# +# See https://ixp-manager.readthedocs.io/en/latest/features/skinning.html + +VIEW_SKIN=myskin + + +####################################################################################### +# See config/cache.php +CACHE_DRIVER=array + +####################################################################################### +# Session Lifetimes - standard and remember me. +# +# See https://docs.ixpmanager.org/latest/usage/authentication/ + +SESSION_LIFETIME=120 +AUTH_TOKEN_EXPIRE=43200 + + +####################################################################################### +# PeeringDB Authentication +# +# PeeringDb's API is used, for example, to pre-populate new customer details. If you +# provide a working PeeringDb username/password then these will be used to get more +# complete information. + +IXP_API_PEERING_DB_USERNAME=username +IXP_API_PEERING_DB_PASSWORD=password + + + +####################################################################################### +# Options for updating RIR Objects - see https://docs.ixpmanager.org/latest/features/rir-objects/ + +# Your RIR password to allow the updating of a RIR object by email: +IXP_API_RIR_PASSWORD=soopersecret + +# Rather than specifiying the destination address on the command line, you can set it here +# (useful for cronjobs and required for use with artisan schedule:run in >=v5.0) +IXP_API_RIR_EMAIL_TO="test-dbm@ripe.net" + +# Rather than specifiying the from address on the command line, you can set it here +# (useful for cronjobs and required for use with artisan schedule:run in >=v5.0) +IXP_API_RIR_EMAIL_FROM="ixp@example.com" + + + +####################################################################################### +# Utility paths + +# See: https://docs.ixpmanager.org/latest/features/irrdb/ +IXP_IRRDB_BGPQ3_PATH=/usr/bin/bgpq3 + +# See: https://docs.ixpmanager.org/latest/features/rpki/ +IXP_RPKI_RTR1_HOST=192.0.2.11 +IXP_RPKI_RTR1_PORT=3323 +IXP_RPKI_RTR2_HOST=192.0.2.12 +IXP_RPKI_RTR2_PORT=3323 + + +######################################################################################### +### Development Helpers +### + +# Disable HTML5 validation to test PHP code based request validators +FORMER_LIVE_VALIDATION=false + + +######################################################################################### +### PeeringDB OAuth +### +### https://docs.ixpmanager.org/latest/features/peeringdb-oauth/ +### + +AUTH_PEERINGDB_ENABLED=true + +PEERINGDB_OAUTH_CLIENT_ID="xxx" +PEERINGDB_OAUTH_CLIENT_SECRET="xxx" +PEERINGDB_OAUTH_REDIRECT="https://www.example.com/auth/login/peeringdb/callback" + + +######################################################################################### +### See: https://docs.ixpmanager.org/latest/features/routers/#filtering-known-transit-networks +IXP_NO_TRANSIT_ASNS_EXCLUDE=65501,65502 +IXP_NO_TRANSIT_ASNS_OVERRIDE=65501,65502,65503 + +IXP_API_NAGIOS_INFRA_HOST=ixp-generic-host +IXP_API_NAGIOS_CUSTOMER_HOST=ixp-generic-host +IXP_API_NAGIOS_INFRA_SERVICE=ixp-core-service +IXP_API_NAGIOS_CUSTOMER_SERVICE=ixp-core-service + + +IXP_FE_FRONTEND_DISABLED_RS_FILTERS=false +IXP_FE_RS_FILTERS_TIME_TO_LIVE="Changes should be live within ten minutes." diff --git a/data/ci/known-good/ci-services-grapher-mrtg.conf b/data/ci/known-good/ci-services-grapher-mrtg.conf new file mode 100644 index 000000000..43ef3a7e3 --- /dev/null +++ b/data/ci/known-good/ci-services-grapher-mrtg.conf @@ -0,0 +1,2184 @@ +### DO NOT EDIT THIS FILE - IT IS AUTOMATICALLY GENERATED +### +### Source: /Users/barryo/dev/ixpm-inex/resources/views/services/grapher/mrtg/header.foil.php +### Generated: 2025-08-18 19:00:59 + +# +# IXP Manager MRTG configuration file +# +# This file is generated by IXP Manager. +# + +### Global Config Options +WorkDir: /tmp +RunAsDaemon:Yes +Interval:5 +IconDir: /images/ + + + + +### Global Defaults +# to get bits instead of bytes and graphs growing to the right +Options[_]: growright, bits +WithPeak[_]: ym +PageTop[_]:

    IXP Traffic Analysis

    +PageFoot[_]: + + + +
    + Based on configuration last generated by IXP Manager at 2025-08-18 19:00:59
    + + + +##################################################################################################################### +##################################################################################################################### +##################################################################################################################### +### +### +### +### AGGREGATE GRAPHS +### +### Source: /Users/barryo/dev/ixpm-inex/resources/views/services/grapher/mrtg/aggregates.foil.php +### +##################################################################################################################### +##################################################################################################################### +##################################################################################################################### + + +##################################################################################################################### +# +# ixp001-infra001-bits +# + +Target[ixp001-infra001-bits]: + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet3&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet3:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet4&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet4:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet8&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet8:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet6&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet6:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet9&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet9:public@s1:::::2 +Title[ixp001-infra001-bits]: IXP Bits / second on Infrastructure #1 +Options[ixp001-infra001-bits]: growright,bits , pngdate +YLegend[ixp001-infra001-bits]: Bits / Second +MaxBytes[ixp001-infra001-bits]: 388750000 +Directory[ixp001-infra001-bits]: infras/001 + + +##################################################################################################################### +# +# ixp001-infra001-pkts +# + +Target[ixp001-infra001-pkts]: + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet3&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet3:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet4&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet4:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet8&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet8:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet6&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet6:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet9&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet9:public@s1:::::2 +Title[ixp001-infra001-pkts]: IXP Packets / second on Infrastructure #1 +Options[ixp001-infra001-pkts]: growright , pngdate +YLegend[ixp001-infra001-pkts]: Packets / Second +MaxBytes[ixp001-infra001-pkts]: 6074219 +Directory[ixp001-infra001-pkts]: infras/001 + + +##################################################################################################################### +# +# ixp001-infra001-errs +# + +Target[ixp001-infra001-errs]: + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet3&1.3.6.1.2.1.2.2.1.20#GigabitEthernet3:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet4&1.3.6.1.2.1.2.2.1.20#GigabitEthernet4:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet8&1.3.6.1.2.1.2.2.1.20#GigabitEthernet8:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet6&1.3.6.1.2.1.2.2.1.20#GigabitEthernet6:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet9&1.3.6.1.2.1.2.2.1.20#GigabitEthernet9:public@s1:::::2 +Title[ixp001-infra001-errs]: IXP Errors / second on Infrastructure #1 +Options[ixp001-infra001-errs]: growright , pngdate +YLegend[ixp001-infra001-errs]: Errors / Second +MaxBytes[ixp001-infra001-errs]: 6074219 +Directory[ixp001-infra001-errs]: infras/001 + + +##################################################################################################################### +# +# ixp001-infra001-discs +# + +Target[ixp001-infra001-discs]: + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet3&1.3.6.1.2.1.2.2.1.19#GigabitEthernet3:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet4&1.3.6.1.2.1.2.2.1.19#GigabitEthernet4:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet8&1.3.6.1.2.1.2.2.1.19#GigabitEthernet8:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet6&1.3.6.1.2.1.2.2.1.19#GigabitEthernet6:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet9&1.3.6.1.2.1.2.2.1.19#GigabitEthernet9:public@s1:::::2 +Title[ixp001-infra001-discs]: IXP Discards / second on Infrastructure #1 +Options[ixp001-infra001-discs]: growright , pngdate +YLegend[ixp001-infra001-discs]: Discards / Second +MaxBytes[ixp001-infra001-discs]: 6074219 +Directory[ixp001-infra001-discs]: infras/001 + + +##################################################################################################################### +# +# ixp001-infra001-bcasts +# + +Target[ixp001-infra001-bcasts]: + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet3&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet3:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet4&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet4:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet8&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet8:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet6&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet6:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet9&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet9:public@s1:::::2 +Title[ixp001-infra001-bcasts]: IXP Broadcasts / second on Infrastructure #1 +Options[ixp001-infra001-bcasts]: growright , pngdate +YLegend[ixp001-infra001-bcasts]: Broadcasts / Second +MaxBytes[ixp001-infra001-bcasts]: 6074219 +Directory[ixp001-infra001-bcasts]: infras/001 + + +##################################################################################################################### +# +# ixp001-infra002-bits +# + +Target[ixp001-infra002-bits]: + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet1&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet1:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet6&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet6:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet8&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet8:public@s2:::::2 +Title[ixp001-infra002-bits]: IXP Bits / second on Infrastructure #2 +Options[ixp001-infra002-bits]: growright,bits , pngdate +YLegend[ixp001-infra002-bits]: Bits / Second +MaxBytes[ixp001-infra002-bits]: 1376250000 +Directory[ixp001-infra002-bits]: infras/002 + + +##################################################################################################################### +# +# ixp001-infra002-pkts +# + +Target[ixp001-infra002-pkts]: + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet1&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet1:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet6&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet6:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet8&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet8:public@s2:::::2 +Title[ixp001-infra002-pkts]: IXP Packets / second on Infrastructure #2 +Options[ixp001-infra002-pkts]: growright , pngdate +YLegend[ixp001-infra002-pkts]: Packets / Second +MaxBytes[ixp001-infra002-pkts]: 21503906 +Directory[ixp001-infra002-pkts]: infras/002 + + +##################################################################################################################### +# +# ixp001-infra002-errs +# + +Target[ixp001-infra002-errs]: + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet1&1.3.6.1.2.1.2.2.1.20#GigabitEthernet1:public@s2:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet6&1.3.6.1.2.1.2.2.1.20#GigabitEthernet6:public@s2:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet8&1.3.6.1.2.1.2.2.1.20#GigabitEthernet8:public@s2:::::2 +Title[ixp001-infra002-errs]: IXP Errors / second on Infrastructure #2 +Options[ixp001-infra002-errs]: growright , pngdate +YLegend[ixp001-infra002-errs]: Errors / Second +MaxBytes[ixp001-infra002-errs]: 21503906 +Directory[ixp001-infra002-errs]: infras/002 + + +##################################################################################################################### +# +# ixp001-infra002-discs +# + +Target[ixp001-infra002-discs]: + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet1&1.3.6.1.2.1.2.2.1.19#GigabitEthernet1:public@s2:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet6&1.3.6.1.2.1.2.2.1.19#GigabitEthernet6:public@s2:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet8&1.3.6.1.2.1.2.2.1.19#GigabitEthernet8:public@s2:::::2 +Title[ixp001-infra002-discs]: IXP Discards / second on Infrastructure #2 +Options[ixp001-infra002-discs]: growright , pngdate +YLegend[ixp001-infra002-discs]: Discards / Second +MaxBytes[ixp001-infra002-discs]: 21503906 +Directory[ixp001-infra002-discs]: infras/002 + + +##################################################################################################################### +# +# ixp001-infra002-bcasts +# + +Target[ixp001-infra002-bcasts]: + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet1&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet1:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet6&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet6:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet8&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet8:public@s2:::::2 +Title[ixp001-infra002-bcasts]: IXP Broadcasts / second on Infrastructure #2 +Options[ixp001-infra002-bcasts]: growright , pngdate +YLegend[ixp001-infra002-bcasts]: Broadcasts / Second +MaxBytes[ixp001-infra002-bcasts]: 21503906 +Directory[ixp001-infra002-bcasts]: infras/002 + + + + +##################################################################################################################### +# +# ixp001-bits +# + +Target[ixp001-bits]: + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet3&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet3:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet4&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet4:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet1&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet1:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet8&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet8:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet6&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet6:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet6&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet6:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet9&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet9:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet8&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet8:public@s2:::::2 +Title[ixp001-bits]: IXP - Bits / second +Options[ixp001-bits]: growright,bits , pngdate +YLegend[ixp001-bits]: Bits / Second +MaxBytes[ixp001-bits]: 1765000000 +Directory[ixp001-bits]: ixp + + +##################################################################################################################### +# +# ixp001-pkts +# + +Target[ixp001-pkts]: + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet3&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet3:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet4&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet4:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet1&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet1:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet8&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet8:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet6&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet6:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet6&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet6:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet9&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet9:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet8&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet8:public@s2:::::2 +Title[ixp001-pkts]: IXP - Packets / second +Options[ixp001-pkts]: growright , pngdate +YLegend[ixp001-pkts]: Packets / Second +MaxBytes[ixp001-pkts]: 27578125 +Directory[ixp001-pkts]: ixp + + +##################################################################################################################### +# +# ixp001-errs +# + +Target[ixp001-errs]: + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet3&1.3.6.1.2.1.2.2.1.20#GigabitEthernet3:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet4&1.3.6.1.2.1.2.2.1.20#GigabitEthernet4:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet1&1.3.6.1.2.1.2.2.1.20#GigabitEthernet1:public@s2:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet8&1.3.6.1.2.1.2.2.1.20#GigabitEthernet8:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet6&1.3.6.1.2.1.2.2.1.20#GigabitEthernet6:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet6&1.3.6.1.2.1.2.2.1.20#GigabitEthernet6:public@s2:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet9&1.3.6.1.2.1.2.2.1.20#GigabitEthernet9:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet8&1.3.6.1.2.1.2.2.1.20#GigabitEthernet8:public@s2:::::2 +Title[ixp001-errs]: IXP - Errors / second +Options[ixp001-errs]: growright , pngdate +YLegend[ixp001-errs]: Errors / Second +MaxBytes[ixp001-errs]: 27578125 +Directory[ixp001-errs]: ixp + + +##################################################################################################################### +# +# ixp001-discs +# + +Target[ixp001-discs]: + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet3&1.3.6.1.2.1.2.2.1.19#GigabitEthernet3:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet4&1.3.6.1.2.1.2.2.1.19#GigabitEthernet4:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet1&1.3.6.1.2.1.2.2.1.19#GigabitEthernet1:public@s2:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet8&1.3.6.1.2.1.2.2.1.19#GigabitEthernet8:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet6&1.3.6.1.2.1.2.2.1.19#GigabitEthernet6:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet6&1.3.6.1.2.1.2.2.1.19#GigabitEthernet6:public@s2:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet9&1.3.6.1.2.1.2.2.1.19#GigabitEthernet9:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet8&1.3.6.1.2.1.2.2.1.19#GigabitEthernet8:public@s2:::::2 +Title[ixp001-discs]: IXP - Discards / second +Options[ixp001-discs]: growright , pngdate +YLegend[ixp001-discs]: Discards / Second +MaxBytes[ixp001-discs]: 27578125 +Directory[ixp001-discs]: ixp + + +##################################################################################################################### +# +# ixp001-bcasts +# + +Target[ixp001-bcasts]: + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet3&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet3:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet4&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet4:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet1&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet1:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet8&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet8:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet6&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet6:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet6&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet6:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet9&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet9:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet8&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet8:public@s2:::::2 +Title[ixp001-bcasts]: IXP - Broadcasts / second +Options[ixp001-bcasts]: growright , pngdate +YLegend[ixp001-bcasts]: Broadcasts / Second +MaxBytes[ixp001-bcasts]: 27578125 +Directory[ixp001-bcasts]: ixp + + +##################################################################################################################### +##################################################################################################################### +##################################################################################################################### + + +##################################################################################################################### +##################################################################################################################### +##################################################################################################################### +### +### +### +### LOCATION AGGREGATE GRAPHS +### +### Source: /Users/barryo/dev/ixpm-inex/resources/views/services/grapher/mrtg/location-aggregates.foil.php +### +##################################################################################################################### +##################################################################################################################### +##################################################################################################################### + + +##################################################################################################################### +# +# location-aggregate-00001-bits +# + +Target[location-aggregate-00001-bits]: + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet3&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet3:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet4&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet4:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet1&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet1:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet8&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet8:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet6&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet6:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet6&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet6:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet9&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet9:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet8&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet8:public@s2:::::2 +Title[location-aggregate-00001-bits]: IXP - Peering Bits / second on Location 1 +Options[location-aggregate-00001-bits]: growright,bits , pngdate +YLegend[location-aggregate-00001-bits]: Bits / Second +MaxBytes[location-aggregate-00001-bits]: 1765000000 +Directory[location-aggregate-00001-bits]: locations/001 + + +##################################################################################################################### +# +# location-aggregate-00001-pkts +# + +Target[location-aggregate-00001-pkts]: + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet3&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet3:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet4&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet4:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet1&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet1:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet8&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet8:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet6&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet6:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet6&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet6:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet9&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet9:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet8&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet8:public@s2:::::2 +Title[location-aggregate-00001-pkts]: IXP - Peering Packets / second on Location 1 +Options[location-aggregate-00001-pkts]: growright , pngdate +YLegend[location-aggregate-00001-pkts]: Packets / Second +MaxBytes[location-aggregate-00001-pkts]: 27578125 +Directory[location-aggregate-00001-pkts]: locations/001 + + +##################################################################################################################### +# +# location-aggregate-00001-errs +# + +Target[location-aggregate-00001-errs]: + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet3&1.3.6.1.2.1.2.2.1.20#GigabitEthernet3:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet4&1.3.6.1.2.1.2.2.1.20#GigabitEthernet4:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet1&1.3.6.1.2.1.2.2.1.20#GigabitEthernet1:public@s2:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet8&1.3.6.1.2.1.2.2.1.20#GigabitEthernet8:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet6&1.3.6.1.2.1.2.2.1.20#GigabitEthernet6:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet6&1.3.6.1.2.1.2.2.1.20#GigabitEthernet6:public@s2:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet9&1.3.6.1.2.1.2.2.1.20#GigabitEthernet9:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet8&1.3.6.1.2.1.2.2.1.20#GigabitEthernet8:public@s2:::::2 +Title[location-aggregate-00001-errs]: IXP - Peering Errors / second on Location 1 +Options[location-aggregate-00001-errs]: growright , pngdate +YLegend[location-aggregate-00001-errs]: Errors / Second +MaxBytes[location-aggregate-00001-errs]: 27578125 +Directory[location-aggregate-00001-errs]: locations/001 + + +##################################################################################################################### +# +# location-aggregate-00001-discs +# + +Target[location-aggregate-00001-discs]: + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet3&1.3.6.1.2.1.2.2.1.19#GigabitEthernet3:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet4&1.3.6.1.2.1.2.2.1.19#GigabitEthernet4:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet1&1.3.6.1.2.1.2.2.1.19#GigabitEthernet1:public@s2:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet8&1.3.6.1.2.1.2.2.1.19#GigabitEthernet8:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet6&1.3.6.1.2.1.2.2.1.19#GigabitEthernet6:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet6&1.3.6.1.2.1.2.2.1.19#GigabitEthernet6:public@s2:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet9&1.3.6.1.2.1.2.2.1.19#GigabitEthernet9:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet8&1.3.6.1.2.1.2.2.1.19#GigabitEthernet8:public@s2:::::2 +Title[location-aggregate-00001-discs]: IXP - Peering Discards / second on Location 1 +Options[location-aggregate-00001-discs]: growright , pngdate +YLegend[location-aggregate-00001-discs]: Discards / Second +MaxBytes[location-aggregate-00001-discs]: 27578125 +Directory[location-aggregate-00001-discs]: locations/001 + + +##################################################################################################################### +# +# location-aggregate-00001-bcasts +# + +Target[location-aggregate-00001-bcasts]: + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet3&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet3:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet4&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet4:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet1&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet1:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet8&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet8:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet6&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet6:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet6&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet6:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet9&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet9:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet8&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet8:public@s2:::::2 +Title[location-aggregate-00001-bcasts]: IXP - Peering Broadcasts / second on Location 1 +Options[location-aggregate-00001-bcasts]: growright , pngdate +YLegend[location-aggregate-00001-bcasts]: Broadcasts / Second +MaxBytes[location-aggregate-00001-bcasts]: 27578125 +Directory[location-aggregate-00001-bcasts]: locations/001 + + + +##################################################################################################################### +##################################################################################################################### +##################################################################################################################### + + +##################################################################################################################### +##################################################################################################################### +##################################################################################################################### +### +### +### +### SWITCH AGGREGATE GRAPHS +### +### Source: /Users/barryo/dev/ixpm-inex/resources/views/services/grapher/mrtg/switch-aggregates.foil.php +### +##################################################################################################################### +##################################################################################################################### +##################################################################################################################### + + +##################################################################################################################### +# +# switch-aggregate-00001-bits +# + +Target[switch-aggregate-00001-bits]: + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet3&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet3:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet4&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet4:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet8&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet8:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet6&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet6:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet9&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet9:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet18&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet18:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet19&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet19:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet25&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet25:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet26&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet26:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet27&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet27:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet28&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet28:public@s1:::::2 +Title[switch-aggregate-00001-bits]: IXP - Peering Bits / second on switch1 +Options[switch-aggregate-00001-bits]: growright,bits , pngdate +YLegend[switch-aggregate-00001-bits]: Bits / Second +MaxBytes[switch-aggregate-00001-bits]: 639250000 +Directory[switch-aggregate-00001-bits]: switches/001 + + +##################################################################################################################### +# +# switch-aggregate-00001-pkts +# + +Target[switch-aggregate-00001-pkts]: + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet3&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet3:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet4&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet4:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet8&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet8:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet6&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet6:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet9&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet9:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet18&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet18:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet19&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet19:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet25&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet25:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet26&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet26:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet27&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet27:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet28&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet28:public@s1:::::2 +Title[switch-aggregate-00001-pkts]: IXP - Peering Packets / second on switch1 +Options[switch-aggregate-00001-pkts]: growright , pngdate +YLegend[switch-aggregate-00001-pkts]: Packets / Second +MaxBytes[switch-aggregate-00001-pkts]: 9988281 +Directory[switch-aggregate-00001-pkts]: switches/001 + + +##################################################################################################################### +# +# switch-aggregate-00001-errs +# + +Target[switch-aggregate-00001-errs]: + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet3&1.3.6.1.2.1.2.2.1.20#GigabitEthernet3:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet4&1.3.6.1.2.1.2.2.1.20#GigabitEthernet4:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet8&1.3.6.1.2.1.2.2.1.20#GigabitEthernet8:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet6&1.3.6.1.2.1.2.2.1.20#GigabitEthernet6:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet9&1.3.6.1.2.1.2.2.1.20#GigabitEthernet9:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet18&1.3.6.1.2.1.2.2.1.20#GigabitEthernet18:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet19&1.3.6.1.2.1.2.2.1.20#GigabitEthernet19:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet25&1.3.6.1.2.1.2.2.1.20#GigabitEthernet25:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet26&1.3.6.1.2.1.2.2.1.20#GigabitEthernet26:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet27&1.3.6.1.2.1.2.2.1.20#GigabitEthernet27:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet28&1.3.6.1.2.1.2.2.1.20#GigabitEthernet28:public@s1:::::2 +Title[switch-aggregate-00001-errs]: IXP - Peering Errors / second on switch1 +Options[switch-aggregate-00001-errs]: growright , pngdate +YLegend[switch-aggregate-00001-errs]: Errors / Second +MaxBytes[switch-aggregate-00001-errs]: 9988281 +Directory[switch-aggregate-00001-errs]: switches/001 + + +##################################################################################################################### +# +# switch-aggregate-00001-discs +# + +Target[switch-aggregate-00001-discs]: + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet3&1.3.6.1.2.1.2.2.1.19#GigabitEthernet3:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet4&1.3.6.1.2.1.2.2.1.19#GigabitEthernet4:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet8&1.3.6.1.2.1.2.2.1.19#GigabitEthernet8:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet6&1.3.6.1.2.1.2.2.1.19#GigabitEthernet6:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet9&1.3.6.1.2.1.2.2.1.19#GigabitEthernet9:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet18&1.3.6.1.2.1.2.2.1.19#GigabitEthernet18:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet19&1.3.6.1.2.1.2.2.1.19#GigabitEthernet19:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet25&1.3.6.1.2.1.2.2.1.19#GigabitEthernet25:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet26&1.3.6.1.2.1.2.2.1.19#GigabitEthernet26:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet27&1.3.6.1.2.1.2.2.1.19#GigabitEthernet27:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet28&1.3.6.1.2.1.2.2.1.19#GigabitEthernet28:public@s1:::::2 +Title[switch-aggregate-00001-discs]: IXP - Peering Discards / second on switch1 +Options[switch-aggregate-00001-discs]: growright , pngdate +YLegend[switch-aggregate-00001-discs]: Discards / Second +MaxBytes[switch-aggregate-00001-discs]: 9988281 +Directory[switch-aggregate-00001-discs]: switches/001 + + +##################################################################################################################### +# +# switch-aggregate-00001-bcasts +# + +Target[switch-aggregate-00001-bcasts]: + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet3&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet3:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet4&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet4:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet8&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet8:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet6&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet6:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet9&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet9:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet18&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet18:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet19&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet19:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet25&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet25:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet26&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet26:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet27&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet27:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet28&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet28:public@s1:::::2 +Title[switch-aggregate-00001-bcasts]: IXP - Peering Broadcasts / second on switch1 +Options[switch-aggregate-00001-bcasts]: growright , pngdate +YLegend[switch-aggregate-00001-bcasts]: Broadcasts / Second +MaxBytes[switch-aggregate-00001-bcasts]: 9988281 +Directory[switch-aggregate-00001-bcasts]: switches/001 + + +##################################################################################################################### +# +# switch-aggregate-00002-bits +# + +Target[switch-aggregate-00002-bits]: + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet1&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet1:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet6&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet6:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet8&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet8:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet18&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet18:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet19&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet19:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet29&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet29:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet30&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet30:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet31&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet31:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet32&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet32:public@s2:::::2 +Title[switch-aggregate-00002-bits]: IXP - Peering Bits / second on switch2 +Options[switch-aggregate-00002-bits]: growright,bits , pngdate +YLegend[switch-aggregate-00002-bits]: Bits / Second +MaxBytes[switch-aggregate-00002-bits]: 1626750000 +Directory[switch-aggregate-00002-bits]: switches/002 + + +##################################################################################################################### +# +# switch-aggregate-00002-pkts +# + +Target[switch-aggregate-00002-pkts]: + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet1&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet1:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet6&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet6:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet8&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet8:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet18&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet18:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet19&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet19:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet29&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet29:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet30&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet30:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet31&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet31:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet32&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet32:public@s2:::::2 +Title[switch-aggregate-00002-pkts]: IXP - Peering Packets / second on switch2 +Options[switch-aggregate-00002-pkts]: growright , pngdate +YLegend[switch-aggregate-00002-pkts]: Packets / Second +MaxBytes[switch-aggregate-00002-pkts]: 25417969 +Directory[switch-aggregate-00002-pkts]: switches/002 + + +##################################################################################################################### +# +# switch-aggregate-00002-errs +# + +Target[switch-aggregate-00002-errs]: + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet1&1.3.6.1.2.1.2.2.1.20#GigabitEthernet1:public@s2:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet6&1.3.6.1.2.1.2.2.1.20#GigabitEthernet6:public@s2:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet8&1.3.6.1.2.1.2.2.1.20#GigabitEthernet8:public@s2:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet18&1.3.6.1.2.1.2.2.1.20#GigabitEthernet18:public@s2:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet19&1.3.6.1.2.1.2.2.1.20#GigabitEthernet19:public@s2:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet29&1.3.6.1.2.1.2.2.1.20#GigabitEthernet29:public@s2:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet30&1.3.6.1.2.1.2.2.1.20#GigabitEthernet30:public@s2:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet31&1.3.6.1.2.1.2.2.1.20#GigabitEthernet31:public@s2:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet32&1.3.6.1.2.1.2.2.1.20#GigabitEthernet32:public@s2:::::2 +Title[switch-aggregate-00002-errs]: IXP - Peering Errors / second on switch2 +Options[switch-aggregate-00002-errs]: growright , pngdate +YLegend[switch-aggregate-00002-errs]: Errors / Second +MaxBytes[switch-aggregate-00002-errs]: 25417969 +Directory[switch-aggregate-00002-errs]: switches/002 + + +##################################################################################################################### +# +# switch-aggregate-00002-discs +# + +Target[switch-aggregate-00002-discs]: + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet1&1.3.6.1.2.1.2.2.1.19#GigabitEthernet1:public@s2:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet6&1.3.6.1.2.1.2.2.1.19#GigabitEthernet6:public@s2:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet8&1.3.6.1.2.1.2.2.1.19#GigabitEthernet8:public@s2:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet18&1.3.6.1.2.1.2.2.1.19#GigabitEthernet18:public@s2:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet19&1.3.6.1.2.1.2.2.1.19#GigabitEthernet19:public@s2:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet29&1.3.6.1.2.1.2.2.1.19#GigabitEthernet29:public@s2:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet30&1.3.6.1.2.1.2.2.1.19#GigabitEthernet30:public@s2:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet31&1.3.6.1.2.1.2.2.1.19#GigabitEthernet31:public@s2:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet32&1.3.6.1.2.1.2.2.1.19#GigabitEthernet32:public@s2:::::2 +Title[switch-aggregate-00002-discs]: IXP - Peering Discards / second on switch2 +Options[switch-aggregate-00002-discs]: growright , pngdate +YLegend[switch-aggregate-00002-discs]: Discards / Second +MaxBytes[switch-aggregate-00002-discs]: 25417969 +Directory[switch-aggregate-00002-discs]: switches/002 + + +##################################################################################################################### +# +# switch-aggregate-00002-bcasts +# + +Target[switch-aggregate-00002-bcasts]: + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet1&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet1:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet6&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet6:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet8&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet8:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet18&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet18:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet19&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet19:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet29&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet29:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet30&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet30:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet31&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet31:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet32&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet32:public@s2:::::2 +Title[switch-aggregate-00002-bcasts]: IXP - Peering Broadcasts / second on switch2 +Options[switch-aggregate-00002-bcasts]: growright , pngdate +YLegend[switch-aggregate-00002-bcasts]: Broadcasts / Second +MaxBytes[switch-aggregate-00002-bcasts]: 25417969 +Directory[switch-aggregate-00002-bcasts]: switches/002 + + + +##################################################################################################################### +##################################################################################################################### +##################################################################################################################### + + +# Manually insert a bunch of definitions here for your IXP's trunk links +# between switches. There is really no way to be able to create a sane +# definition of these in the IXP Manager database, so we chicken out and let +# each IXP do it manually. Simplicity r00lz. +# +# See: https://github.com/inex/IXP-Manager/wiki/MRTG---Traffic-Graphs#inter-switch-link-graphs +# +# Here are some examples. + +## degkcp-ixdub1 - LAN1 +#Target[core-degkcp-ixdub1-lan1]: #ethernet23:sillypassword@swi1-deg1-2:::::2 + #ethernet24:sillypassword@swi1-deg1-2:::::2 +#MaxBytes[core-degkcp-ixdub1-lan1]: 2500000000 +#Directory[core-degkcp-ixdub1-lan1]: trunks +#Title[core-degkcp-ixdub1-lan1]: Trunk Core - DEGKCP-IXDUB1 - LAN1 + +## tcydub1-ixdub1 - LAN1 +#Target[core-tcydub1-ixdub1-lan1]: #ethernet23:sillypassword@swi1-ix1-1:::::2 + #ethernet24:sillypassword@swi1-ix1-1:::::2 +#MaxBytes[core-tcydub1-ixdub1-lan1]: 2500000000 +#Directory[core-tcydub1-ixdub1-lan1]: trunks +#Title[core-tcydub1-ixdub1-lan1]: Trunk Core - TCYDUB1-IXDUB1 - LAN1 + + +## swi1-ix1-1 - swi1-ix1-2 +#Target[core-swi1-ix1-1_swi1-ix1-2-lan1]: #ethernet21:sillypassword@swi1-ix1-1:::::2 + #ethernet22:sillypassword@swi1-ix1-1:::::2 +#MaxBytes[core-swi1-ix1-1_swi1-ix1-2-lan1]: 2500000000 +#Directory[core-swi1-ix1-1_swi1-ix1-2-lan1]: trunks +#Title[core-swi1-ix1-1_swi1-ix1-2-lan1]: Inter-POP Trunk Core - IXDUB1 - swi1-ix1-1 swi1-ix1-2 - LAN1 + + +##################################################################################################################### +##################################################################################################################### +##################################################################################################################### +### +### +### +### MEMBER PORTS +### +### Source: /Users/barryo/dev/ixpm-inex/resources/views/services/grapher/mrtg/member-ports.foil.php +### +##################################################################################################################### +##################################################################################################################### +##################################################################################################################### + + +##################################################################################################################### +##################################################################################################################### +##################################################################################################################### +### +### MEMBER PORT: HEAnet [AS1213]### + + + +##################################################################################################################### +# +# pi00001-bits +# + +Target[pi00001-bits]: 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet3&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet3:public@s1:::::2 +Title[pi00001-bits]: HEAnet -- GigabitEthernet3 -- switch1 -- Bits / second +Options[pi00001-bits]: growright,bits , pngdate +YLegend[pi00001-bits]: Bits / Second +MaxBytes[pi00001-bits]: 125000000 +Directory[pi00001-bits]: members/2/00002/ints + + +##################################################################################################################### +# +# pi00001-pkts +# + +Target[pi00001-pkts]: 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet3&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet3:public@s1:::::2 +Title[pi00001-pkts]: HEAnet -- GigabitEthernet3 -- switch1 -- Packets / second +Options[pi00001-pkts]: growright , pngdate +YLegend[pi00001-pkts]: Packets / Second +MaxBytes[pi00001-pkts]: 1953125 +Directory[pi00001-pkts]: members/2/00002/ints + + +##################################################################################################################### +# +# pi00001-errs +# + +Target[pi00001-errs]: 1.3.6.1.2.1.2.2.1.14#GigabitEthernet3&1.3.6.1.2.1.2.2.1.20#GigabitEthernet3:public@s1:::::2 +Title[pi00001-errs]: HEAnet -- GigabitEthernet3 -- switch1 -- Errors / second +Options[pi00001-errs]: growright , pngdate +YLegend[pi00001-errs]: Errors / Second +MaxBytes[pi00001-errs]: 1953125 +Directory[pi00001-errs]: members/2/00002/ints + + +##################################################################################################################### +# +# pi00001-discs +# + +Target[pi00001-discs]: 1.3.6.1.2.1.2.2.1.13#GigabitEthernet3&1.3.6.1.2.1.2.2.1.19#GigabitEthernet3:public@s1:::::2 +Title[pi00001-discs]: HEAnet -- GigabitEthernet3 -- switch1 -- Discards / second +Options[pi00001-discs]: growright , pngdate +YLegend[pi00001-discs]: Discards / Second +MaxBytes[pi00001-discs]: 1953125 +Directory[pi00001-discs]: members/2/00002/ints + + +##################################################################################################################### +# +# pi00001-bcasts +# + +Target[pi00001-bcasts]: 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet3&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet3:public@s1:::::2 +Title[pi00001-bcasts]: HEAnet -- GigabitEthernet3 -- switch1 -- Broadcasts / second +Options[pi00001-bcasts]: growright , pngdate +YLegend[pi00001-bcasts]: Broadcasts / Second +MaxBytes[pi00001-bcasts]: 1953125 +Directory[pi00001-bcasts]: members/2/00002/ints + + + + + +##################################################################################################################### +# +# pi00002-bits +# + +Target[pi00002-bits]: 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet4&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet4:public@s1:::::2 +Title[pi00002-bits]: HEAnet -- GigabitEthernet4 -- switch1 -- Bits / second +Options[pi00002-bits]: growright,bits , pngdate +YLegend[pi00002-bits]: Bits / Second +MaxBytes[pi00002-bits]: 125000000 +Directory[pi00002-bits]: members/2/00002/ints + + +##################################################################################################################### +# +# pi00002-pkts +# + +Target[pi00002-pkts]: 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet4&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet4:public@s1:::::2 +Title[pi00002-pkts]: HEAnet -- GigabitEthernet4 -- switch1 -- Packets / second +Options[pi00002-pkts]: growright , pngdate +YLegend[pi00002-pkts]: Packets / Second +MaxBytes[pi00002-pkts]: 1953125 +Directory[pi00002-pkts]: members/2/00002/ints + + +##################################################################################################################### +# +# pi00002-errs +# + +Target[pi00002-errs]: 1.3.6.1.2.1.2.2.1.14#GigabitEthernet4&1.3.6.1.2.1.2.2.1.20#GigabitEthernet4:public@s1:::::2 +Title[pi00002-errs]: HEAnet -- GigabitEthernet4 -- switch1 -- Errors / second +Options[pi00002-errs]: growright , pngdate +YLegend[pi00002-errs]: Errors / Second +MaxBytes[pi00002-errs]: 1953125 +Directory[pi00002-errs]: members/2/00002/ints + + +##################################################################################################################### +# +# pi00002-discs +# + +Target[pi00002-discs]: 1.3.6.1.2.1.2.2.1.13#GigabitEthernet4&1.3.6.1.2.1.2.2.1.19#GigabitEthernet4:public@s1:::::2 +Title[pi00002-discs]: HEAnet -- GigabitEthernet4 -- switch1 -- Discards / second +Options[pi00002-discs]: growright , pngdate +YLegend[pi00002-discs]: Discards / Second +MaxBytes[pi00002-discs]: 1953125 +Directory[pi00002-discs]: members/2/00002/ints + + +##################################################################################################################### +# +# pi00002-bcasts +# + +Target[pi00002-bcasts]: 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet4&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet4:public@s1:::::2 +Title[pi00002-bcasts]: HEAnet -- GigabitEthernet4 -- switch1 -- Broadcasts / second +Options[pi00002-bcasts]: growright , pngdate +YLegend[pi00002-bcasts]: Broadcasts / Second +MaxBytes[pi00002-bcasts]: 1953125 +Directory[pi00002-bcasts]: members/2/00002/ints + + + + + +##################################################################################################################### +# +# pi00003-bits +# + +Target[pi00003-bits]: 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet1&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet1:public@s2:::::2 +Title[pi00003-bits]: HEAnet -- GigabitEthernet1 -- switch2 -- Bits / second +Options[pi00003-bits]: growright,bits , pngdate +YLegend[pi00003-bits]: Bits / Second +MaxBytes[pi00003-bits]: 125000000 +Directory[pi00003-bits]: members/2/00002/ints + + +##################################################################################################################### +# +# pi00003-pkts +# + +Target[pi00003-pkts]: 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet1&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet1:public@s2:::::2 +Title[pi00003-pkts]: HEAnet -- GigabitEthernet1 -- switch2 -- Packets / second +Options[pi00003-pkts]: growright , pngdate +YLegend[pi00003-pkts]: Packets / Second +MaxBytes[pi00003-pkts]: 1953125 +Directory[pi00003-pkts]: members/2/00002/ints + + +##################################################################################################################### +# +# pi00003-errs +# + +Target[pi00003-errs]: 1.3.6.1.2.1.2.2.1.14#GigabitEthernet1&1.3.6.1.2.1.2.2.1.20#GigabitEthernet1:public@s2:::::2 +Title[pi00003-errs]: HEAnet -- GigabitEthernet1 -- switch2 -- Errors / second +Options[pi00003-errs]: growright , pngdate +YLegend[pi00003-errs]: Errors / Second +MaxBytes[pi00003-errs]: 1953125 +Directory[pi00003-errs]: members/2/00002/ints + + +##################################################################################################################### +# +# pi00003-discs +# + +Target[pi00003-discs]: 1.3.6.1.2.1.2.2.1.13#GigabitEthernet1&1.3.6.1.2.1.2.2.1.19#GigabitEthernet1:public@s2:::::2 +Title[pi00003-discs]: HEAnet -- GigabitEthernet1 -- switch2 -- Discards / second +Options[pi00003-discs]: growright , pngdate +YLegend[pi00003-discs]: Discards / Second +MaxBytes[pi00003-discs]: 1953125 +Directory[pi00003-discs]: members/2/00002/ints + + +##################################################################################################################### +# +# pi00003-bcasts +# + +Target[pi00003-bcasts]: 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet1&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet1:public@s2:::::2 +Title[pi00003-bcasts]: HEAnet -- GigabitEthernet1 -- switch2 -- Broadcasts / second +Options[pi00003-bcasts]: growright , pngdate +YLegend[pi00003-bcasts]: Broadcasts / Second +MaxBytes[pi00003-bcasts]: 1953125 +Directory[pi00003-bcasts]: members/2/00002/ints + + + + + +##################################################################################################################### +# +# vi00001-bits +# + +Target[vi00001-bits]: + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet3&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet3:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet4&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet4:public@s1:::::2 +Title[vi00001-bits]: HEAnet -- LAG Aggregate Bits / second +Options[vi00001-bits]: growright,bits , pngdate +YLegend[vi00001-bits]: Bits / Second +MaxBytes[vi00001-bits]: 250000000 +Directory[vi00001-bits]: members/2/00002/lags + + +##################################################################################################################### +# +# vi00001-pkts +# + +Target[vi00001-pkts]: + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet3&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet3:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet4&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet4:public@s1:::::2 +Title[vi00001-pkts]: HEAnet -- LAG Aggregate Packets / second +Options[vi00001-pkts]: growright , pngdate +YLegend[vi00001-pkts]: Packets / Second +MaxBytes[vi00001-pkts]: 3906250 +Directory[vi00001-pkts]: members/2/00002/lags + + +##################################################################################################################### +# +# vi00001-errs +# + +Target[vi00001-errs]: + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet3&1.3.6.1.2.1.2.2.1.20#GigabitEthernet3:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet4&1.3.6.1.2.1.2.2.1.20#GigabitEthernet4:public@s1:::::2 +Title[vi00001-errs]: HEAnet -- LAG Aggregate Errors / second +Options[vi00001-errs]: growright , pngdate +YLegend[vi00001-errs]: Errors / Second +MaxBytes[vi00001-errs]: 3906250 +Directory[vi00001-errs]: members/2/00002/lags + + +##################################################################################################################### +# +# vi00001-discs +# + +Target[vi00001-discs]: + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet3&1.3.6.1.2.1.2.2.1.19#GigabitEthernet3:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet4&1.3.6.1.2.1.2.2.1.19#GigabitEthernet4:public@s1:::::2 +Title[vi00001-discs]: HEAnet -- LAG Aggregate Discards / second +Options[vi00001-discs]: growright , pngdate +YLegend[vi00001-discs]: Discards / Second +MaxBytes[vi00001-discs]: 3906250 +Directory[vi00001-discs]: members/2/00002/lags + + +##################################################################################################################### +# +# vi00001-bcasts +# + +Target[vi00001-bcasts]: + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet3&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet3:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet4&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet4:public@s1:::::2 +Title[vi00001-bcasts]: HEAnet -- LAG Aggregate Broadcasts / second +Options[vi00001-bcasts]: growright , pngdate +YLegend[vi00001-bcasts]: Broadcasts / Second +MaxBytes[vi00001-bcasts]: 3906250 +Directory[vi00001-bcasts]: members/2/00002/lags + + + + + +##################################################################################################################### +# +# aggregate-00002-bits +# + +Target[aggregate-00002-bits]: + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet3&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet3:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet4&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet4:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet1&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet1:public@s2:::::2 +Title[aggregate-00002-bits]: HEAnet -- IXP Total Aggregate -- Bits / second +Options[aggregate-00002-bits]: growright,bits , pngdate +YLegend[aggregate-00002-bits]: Bits / Second +MaxBytes[aggregate-00002-bits]: 375000000 +Directory[aggregate-00002-bits]: members/2/00002 + + +##################################################################################################################### +# +# aggregate-00002-pkts +# + +Target[aggregate-00002-pkts]: + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet3&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet3:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet4&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet4:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet1&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet1:public@s2:::::2 +Title[aggregate-00002-pkts]: HEAnet -- IXP Total Aggregate -- Packets / second +Options[aggregate-00002-pkts]: growright , pngdate +YLegend[aggregate-00002-pkts]: Packets / Second +MaxBytes[aggregate-00002-pkts]: 5859375 +Directory[aggregate-00002-pkts]: members/2/00002 + + +##################################################################################################################### +# +# aggregate-00002-errs +# + +Target[aggregate-00002-errs]: + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet3&1.3.6.1.2.1.2.2.1.20#GigabitEthernet3:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet4&1.3.6.1.2.1.2.2.1.20#GigabitEthernet4:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet1&1.3.6.1.2.1.2.2.1.20#GigabitEthernet1:public@s2:::::2 +Title[aggregate-00002-errs]: HEAnet -- IXP Total Aggregate -- Errors / second +Options[aggregate-00002-errs]: growright , pngdate +YLegend[aggregate-00002-errs]: Errors / Second +MaxBytes[aggregate-00002-errs]: 5859375 +Directory[aggregate-00002-errs]: members/2/00002 + + +##################################################################################################################### +# +# aggregate-00002-discs +# + +Target[aggregate-00002-discs]: + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet3&1.3.6.1.2.1.2.2.1.19#GigabitEthernet3:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet4&1.3.6.1.2.1.2.2.1.19#GigabitEthernet4:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet1&1.3.6.1.2.1.2.2.1.19#GigabitEthernet1:public@s2:::::2 +Title[aggregate-00002-discs]: HEAnet -- IXP Total Aggregate -- Discards / second +Options[aggregate-00002-discs]: growright , pngdate +YLegend[aggregate-00002-discs]: Discards / Second +MaxBytes[aggregate-00002-discs]: 5859375 +Directory[aggregate-00002-discs]: members/2/00002 + + +##################################################################################################################### +# +# aggregate-00002-bcasts +# + +Target[aggregate-00002-bcasts]: + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet3&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet3:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet4&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet4:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet1&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet1:public@s2:::::2 +Title[aggregate-00002-bcasts]: HEAnet -- IXP Total Aggregate -- Broadcasts / second +Options[aggregate-00002-bcasts]: growright , pngdate +YLegend[aggregate-00002-bcasts]: Broadcasts / Second +MaxBytes[aggregate-00002-bcasts]: 5859375 +Directory[aggregate-00002-bcasts]: members/2/00002 + + + + + +##################################################################################################################### +##################################################################################################################### +##################################################################################################################### +### +### MEMBER PORT: PCH DNS [AS42]### + + + +##################################################################################################################### +# +# pi00004-bits +# + +Target[pi00004-bits]: 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet8&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet8:public@s1:::::2 +Title[pi00004-bits]: PCH DNS -- GigabitEthernet8 -- switch1 -- Bits / second +Options[pi00004-bits]: growright,bits , pngdate +YLegend[pi00004-bits]: Bits / Second +MaxBytes[pi00004-bits]: 12500000 +Directory[pi00004-bits]: members/3/00003/ints + + +##################################################################################################################### +# +# pi00004-pkts +# + +Target[pi00004-pkts]: 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet8&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet8:public@s1:::::2 +Title[pi00004-pkts]: PCH DNS -- GigabitEthernet8 -- switch1 -- Packets / second +Options[pi00004-pkts]: growright , pngdate +YLegend[pi00004-pkts]: Packets / Second +MaxBytes[pi00004-pkts]: 195313 +Directory[pi00004-pkts]: members/3/00003/ints + + +##################################################################################################################### +# +# pi00004-errs +# + +Target[pi00004-errs]: 1.3.6.1.2.1.2.2.1.14#GigabitEthernet8&1.3.6.1.2.1.2.2.1.20#GigabitEthernet8:public@s1:::::2 +Title[pi00004-errs]: PCH DNS -- GigabitEthernet8 -- switch1 -- Errors / second +Options[pi00004-errs]: growright , pngdate +YLegend[pi00004-errs]: Errors / Second +MaxBytes[pi00004-errs]: 195313 +Directory[pi00004-errs]: members/3/00003/ints + + +##################################################################################################################### +# +# pi00004-discs +# + +Target[pi00004-discs]: 1.3.6.1.2.1.2.2.1.13#GigabitEthernet8&1.3.6.1.2.1.2.2.1.19#GigabitEthernet8:public@s1:::::2 +Title[pi00004-discs]: PCH DNS -- GigabitEthernet8 -- switch1 -- Discards / second +Options[pi00004-discs]: growright , pngdate +YLegend[pi00004-discs]: Discards / Second +MaxBytes[pi00004-discs]: 195313 +Directory[pi00004-discs]: members/3/00003/ints + + +##################################################################################################################### +# +# pi00004-bcasts +# + +Target[pi00004-bcasts]: 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet8&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet8:public@s1:::::2 +Title[pi00004-bcasts]: PCH DNS -- GigabitEthernet8 -- switch1 -- Broadcasts / second +Options[pi00004-bcasts]: growright , pngdate +YLegend[pi00004-bcasts]: Broadcasts / Second +MaxBytes[pi00004-bcasts]: 195313 +Directory[pi00004-bcasts]: members/3/00003/ints + + + + + +##################################################################################################################### +# +# aggregate-00003-bits +# + +Target[aggregate-00003-bits]: 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet8&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet8:public@s1:::::2 +Title[aggregate-00003-bits]: PCH DNS -- IXP Total Aggregate -- Bits / second +Options[aggregate-00003-bits]: growright,bits , pngdate +YLegend[aggregate-00003-bits]: Bits / Second +MaxBytes[aggregate-00003-bits]: 12500000 +Directory[aggregate-00003-bits]: members/3/00003 + + +##################################################################################################################### +# +# aggregate-00003-pkts +# + +Target[aggregate-00003-pkts]: 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet8&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet8:public@s1:::::2 +Title[aggregate-00003-pkts]: PCH DNS -- IXP Total Aggregate -- Packets / second +Options[aggregate-00003-pkts]: growright , pngdate +YLegend[aggregate-00003-pkts]: Packets / Second +MaxBytes[aggregate-00003-pkts]: 195313 +Directory[aggregate-00003-pkts]: members/3/00003 + + +##################################################################################################################### +# +# aggregate-00003-errs +# + +Target[aggregate-00003-errs]: 1.3.6.1.2.1.2.2.1.14#GigabitEthernet8&1.3.6.1.2.1.2.2.1.20#GigabitEthernet8:public@s1:::::2 +Title[aggregate-00003-errs]: PCH DNS -- IXP Total Aggregate -- Errors / second +Options[aggregate-00003-errs]: growright , pngdate +YLegend[aggregate-00003-errs]: Errors / Second +MaxBytes[aggregate-00003-errs]: 195313 +Directory[aggregate-00003-errs]: members/3/00003 + + +##################################################################################################################### +# +# aggregate-00003-discs +# + +Target[aggregate-00003-discs]: 1.3.6.1.2.1.2.2.1.13#GigabitEthernet8&1.3.6.1.2.1.2.2.1.19#GigabitEthernet8:public@s1:::::2 +Title[aggregate-00003-discs]: PCH DNS -- IXP Total Aggregate -- Discards / second +Options[aggregate-00003-discs]: growright , pngdate +YLegend[aggregate-00003-discs]: Discards / Second +MaxBytes[aggregate-00003-discs]: 195313 +Directory[aggregate-00003-discs]: members/3/00003 + + +##################################################################################################################### +# +# aggregate-00003-bcasts +# + +Target[aggregate-00003-bcasts]: 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet8&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet8:public@s1:::::2 +Title[aggregate-00003-bcasts]: PCH DNS -- IXP Total Aggregate -- Broadcasts / second +Options[aggregate-00003-bcasts]: growright , pngdate +YLegend[aggregate-00003-bcasts]: Broadcasts / Second +MaxBytes[aggregate-00003-bcasts]: 195313 +Directory[aggregate-00003-bcasts]: members/3/00003 + + + + + +##################################################################################################################### +##################################################################################################################### +##################################################################################################################### +### +### MEMBER PORT: AS112 [AS112]### + + + +##################################################################################################################### +# +# pi00005-bits +# + +Target[pi00005-bits]: 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet6&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet6:public@s1:::::2 +Title[pi00005-bits]: AS112 -- GigabitEthernet6 -- switch1 -- Bits / second +Options[pi00005-bits]: growright,bits , pngdate +YLegend[pi00005-bits]: Bits / Second +MaxBytes[pi00005-bits]: 1250000 +Directory[pi00005-bits]: members/4/00004/ints + + +##################################################################################################################### +# +# pi00005-pkts +# + +Target[pi00005-pkts]: 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet6&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet6:public@s1:::::2 +Title[pi00005-pkts]: AS112 -- GigabitEthernet6 -- switch1 -- Packets / second +Options[pi00005-pkts]: growright , pngdate +YLegend[pi00005-pkts]: Packets / Second +MaxBytes[pi00005-pkts]: 19531 +Directory[pi00005-pkts]: members/4/00004/ints + + +##################################################################################################################### +# +# pi00005-errs +# + +Target[pi00005-errs]: 1.3.6.1.2.1.2.2.1.14#GigabitEthernet6&1.3.6.1.2.1.2.2.1.20#GigabitEthernet6:public@s1:::::2 +Title[pi00005-errs]: AS112 -- GigabitEthernet6 -- switch1 -- Errors / second +Options[pi00005-errs]: growright , pngdate +YLegend[pi00005-errs]: Errors / Second +MaxBytes[pi00005-errs]: 19531 +Directory[pi00005-errs]: members/4/00004/ints + + +##################################################################################################################### +# +# pi00005-discs +# + +Target[pi00005-discs]: 1.3.6.1.2.1.2.2.1.13#GigabitEthernet6&1.3.6.1.2.1.2.2.1.19#GigabitEthernet6:public@s1:::::2 +Title[pi00005-discs]: AS112 -- GigabitEthernet6 -- switch1 -- Discards / second +Options[pi00005-discs]: growright , pngdate +YLegend[pi00005-discs]: Discards / Second +MaxBytes[pi00005-discs]: 19531 +Directory[pi00005-discs]: members/4/00004/ints + + +##################################################################################################################### +# +# pi00005-bcasts +# + +Target[pi00005-bcasts]: 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet6&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet6:public@s1:::::2 +Title[pi00005-bcasts]: AS112 -- GigabitEthernet6 -- switch1 -- Broadcasts / second +Options[pi00005-bcasts]: growright , pngdate +YLegend[pi00005-bcasts]: Broadcasts / Second +MaxBytes[pi00005-bcasts]: 19531 +Directory[pi00005-bcasts]: members/4/00004/ints + + + + + +##################################################################################################################### +# +# pi00006-bits +# + +Target[pi00006-bits]: 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet6&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet6:public@s2:::::2 +Title[pi00006-bits]: AS112 -- GigabitEthernet6 -- switch2 -- Bits / second +Options[pi00006-bits]: growright,bits , pngdate +YLegend[pi00006-bits]: Bits / Second +MaxBytes[pi00006-bits]: 1250000 +Directory[pi00006-bits]: members/4/00004/ints + + +##################################################################################################################### +# +# pi00006-pkts +# + +Target[pi00006-pkts]: 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet6&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet6:public@s2:::::2 +Title[pi00006-pkts]: AS112 -- GigabitEthernet6 -- switch2 -- Packets / second +Options[pi00006-pkts]: growright , pngdate +YLegend[pi00006-pkts]: Packets / Second +MaxBytes[pi00006-pkts]: 19531 +Directory[pi00006-pkts]: members/4/00004/ints + + +##################################################################################################################### +# +# pi00006-errs +# + +Target[pi00006-errs]: 1.3.6.1.2.1.2.2.1.14#GigabitEthernet6&1.3.6.1.2.1.2.2.1.20#GigabitEthernet6:public@s2:::::2 +Title[pi00006-errs]: AS112 -- GigabitEthernet6 -- switch2 -- Errors / second +Options[pi00006-errs]: growright , pngdate +YLegend[pi00006-errs]: Errors / Second +MaxBytes[pi00006-errs]: 19531 +Directory[pi00006-errs]: members/4/00004/ints + + +##################################################################################################################### +# +# pi00006-discs +# + +Target[pi00006-discs]: 1.3.6.1.2.1.2.2.1.13#GigabitEthernet6&1.3.6.1.2.1.2.2.1.19#GigabitEthernet6:public@s2:::::2 +Title[pi00006-discs]: AS112 -- GigabitEthernet6 -- switch2 -- Discards / second +Options[pi00006-discs]: growright , pngdate +YLegend[pi00006-discs]: Discards / Second +MaxBytes[pi00006-discs]: 19531 +Directory[pi00006-discs]: members/4/00004/ints + + +##################################################################################################################### +# +# pi00006-bcasts +# + +Target[pi00006-bcasts]: 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet6&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet6:public@s2:::::2 +Title[pi00006-bcasts]: AS112 -- GigabitEthernet6 -- switch2 -- Broadcasts / second +Options[pi00006-bcasts]: growright , pngdate +YLegend[pi00006-bcasts]: Broadcasts / Second +MaxBytes[pi00006-bcasts]: 19531 +Directory[pi00006-bcasts]: members/4/00004/ints + + + + + +##################################################################################################################### +# +# aggregate-00004-bits +# + +Target[aggregate-00004-bits]: + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet6&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet6:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet6&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet6:public@s2:::::2 +Title[aggregate-00004-bits]: AS112 -- IXP Total Aggregate -- Bits / second +Options[aggregate-00004-bits]: growright,bits , pngdate +YLegend[aggregate-00004-bits]: Bits / Second +MaxBytes[aggregate-00004-bits]: 2500000 +Directory[aggregate-00004-bits]: members/4/00004 + + +##################################################################################################################### +# +# aggregate-00004-pkts +# + +Target[aggregate-00004-pkts]: + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet6&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet6:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet6&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet6:public@s2:::::2 +Title[aggregate-00004-pkts]: AS112 -- IXP Total Aggregate -- Packets / second +Options[aggregate-00004-pkts]: growright , pngdate +YLegend[aggregate-00004-pkts]: Packets / Second +MaxBytes[aggregate-00004-pkts]: 39063 +Directory[aggregate-00004-pkts]: members/4/00004 + + +##################################################################################################################### +# +# aggregate-00004-errs +# + +Target[aggregate-00004-errs]: + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet6&1.3.6.1.2.1.2.2.1.20#GigabitEthernet6:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet6&1.3.6.1.2.1.2.2.1.20#GigabitEthernet6:public@s2:::::2 +Title[aggregate-00004-errs]: AS112 -- IXP Total Aggregate -- Errors / second +Options[aggregate-00004-errs]: growright , pngdate +YLegend[aggregate-00004-errs]: Errors / Second +MaxBytes[aggregate-00004-errs]: 39063 +Directory[aggregate-00004-errs]: members/4/00004 + + +##################################################################################################################### +# +# aggregate-00004-discs +# + +Target[aggregate-00004-discs]: + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet6&1.3.6.1.2.1.2.2.1.19#GigabitEthernet6:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet6&1.3.6.1.2.1.2.2.1.19#GigabitEthernet6:public@s2:::::2 +Title[aggregate-00004-discs]: AS112 -- IXP Total Aggregate -- Discards / second +Options[aggregate-00004-discs]: growright , pngdate +YLegend[aggregate-00004-discs]: Discards / Second +MaxBytes[aggregate-00004-discs]: 39063 +Directory[aggregate-00004-discs]: members/4/00004 + + +##################################################################################################################### +# +# aggregate-00004-bcasts +# + +Target[aggregate-00004-bcasts]: + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet6&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet6:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet6&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet6:public@s2:::::2 +Title[aggregate-00004-bcasts]: AS112 -- IXP Total Aggregate -- Broadcasts / second +Options[aggregate-00004-bcasts]: growright , pngdate +YLegend[aggregate-00004-bcasts]: Broadcasts / Second +MaxBytes[aggregate-00004-bcasts]: 39063 +Directory[aggregate-00004-bcasts]: members/4/00004 + + + + + +##################################################################################################################### +##################################################################################################################### +##################################################################################################################### +### +### MEMBER PORT: Imagine [AS25441]### + + + +##################################################################################################################### +# +# pi00007-bits +# + +Target[pi00007-bits]: 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet9&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet9:public@s1:::::2 +Title[pi00007-bits]: Imagine -- GigabitEthernet9 -- switch1 -- Bits / second +Options[pi00007-bits]: growright,bits , pngdate +YLegend[pi00007-bits]: Bits / Second +MaxBytes[pi00007-bits]: 125000000 +Directory[pi00007-bits]: members/5/00005/ints + + +##################################################################################################################### +# +# pi00007-pkts +# + +Target[pi00007-pkts]: 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet9&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet9:public@s1:::::2 +Title[pi00007-pkts]: Imagine -- GigabitEthernet9 -- switch1 -- Packets / second +Options[pi00007-pkts]: growright , pngdate +YLegend[pi00007-pkts]: Packets / Second +MaxBytes[pi00007-pkts]: 1953125 +Directory[pi00007-pkts]: members/5/00005/ints + + +##################################################################################################################### +# +# pi00007-errs +# + +Target[pi00007-errs]: 1.3.6.1.2.1.2.2.1.14#GigabitEthernet9&1.3.6.1.2.1.2.2.1.20#GigabitEthernet9:public@s1:::::2 +Title[pi00007-errs]: Imagine -- GigabitEthernet9 -- switch1 -- Errors / second +Options[pi00007-errs]: growright , pngdate +YLegend[pi00007-errs]: Errors / Second +MaxBytes[pi00007-errs]: 1953125 +Directory[pi00007-errs]: members/5/00005/ints + + +##################################################################################################################### +# +# pi00007-discs +# + +Target[pi00007-discs]: 1.3.6.1.2.1.2.2.1.13#GigabitEthernet9&1.3.6.1.2.1.2.2.1.19#GigabitEthernet9:public@s1:::::2 +Title[pi00007-discs]: Imagine -- GigabitEthernet9 -- switch1 -- Discards / second +Options[pi00007-discs]: growright , pngdate +YLegend[pi00007-discs]: Discards / Second +MaxBytes[pi00007-discs]: 1953125 +Directory[pi00007-discs]: members/5/00005/ints + + +##################################################################################################################### +# +# pi00007-bcasts +# + +Target[pi00007-bcasts]: 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet9&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet9:public@s1:::::2 +Title[pi00007-bcasts]: Imagine -- GigabitEthernet9 -- switch1 -- Broadcasts / second +Options[pi00007-bcasts]: growright , pngdate +YLegend[pi00007-bcasts]: Broadcasts / Second +MaxBytes[pi00007-bcasts]: 1953125 +Directory[pi00007-bcasts]: members/5/00005/ints + + + + + +##################################################################################################################### +# +# pi00008-bits +# + +Target[pi00008-bits]: 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet8&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet8:public@s2:::::2 +Title[pi00008-bits]: Imagine -- GigabitEthernet8 -- switch2 -- Bits / second +Options[pi00008-bits]: growright,bits , pngdate +YLegend[pi00008-bits]: Bits / Second +MaxBytes[pi00008-bits]: 1250000000 +Directory[pi00008-bits]: members/5/00005/ints + + +##################################################################################################################### +# +# pi00008-pkts +# + +Target[pi00008-pkts]: 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet8&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet8:public@s2:::::2 +Title[pi00008-pkts]: Imagine -- GigabitEthernet8 -- switch2 -- Packets / second +Options[pi00008-pkts]: growright , pngdate +YLegend[pi00008-pkts]: Packets / Second +MaxBytes[pi00008-pkts]: 19531250 +Directory[pi00008-pkts]: members/5/00005/ints + + +##################################################################################################################### +# +# pi00008-errs +# + +Target[pi00008-errs]: 1.3.6.1.2.1.2.2.1.14#GigabitEthernet8&1.3.6.1.2.1.2.2.1.20#GigabitEthernet8:public@s2:::::2 +Title[pi00008-errs]: Imagine -- GigabitEthernet8 -- switch2 -- Errors / second +Options[pi00008-errs]: growright , pngdate +YLegend[pi00008-errs]: Errors / Second +MaxBytes[pi00008-errs]: 19531250 +Directory[pi00008-errs]: members/5/00005/ints + + +##################################################################################################################### +# +# pi00008-discs +# + +Target[pi00008-discs]: 1.3.6.1.2.1.2.2.1.13#GigabitEthernet8&1.3.6.1.2.1.2.2.1.19#GigabitEthernet8:public@s2:::::2 +Title[pi00008-discs]: Imagine -- GigabitEthernet8 -- switch2 -- Discards / second +Options[pi00008-discs]: growright , pngdate +YLegend[pi00008-discs]: Discards / Second +MaxBytes[pi00008-discs]: 19531250 +Directory[pi00008-discs]: members/5/00005/ints + + +##################################################################################################################### +# +# pi00008-bcasts +# + +Target[pi00008-bcasts]: 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet8&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet8:public@s2:::::2 +Title[pi00008-bcasts]: Imagine -- GigabitEthernet8 -- switch2 -- Broadcasts / second +Options[pi00008-bcasts]: growright , pngdate +YLegend[pi00008-bcasts]: Broadcasts / Second +MaxBytes[pi00008-bcasts]: 19531250 +Directory[pi00008-bcasts]: members/5/00005/ints + + + + + +##################################################################################################################### +# +# aggregate-00005-bits +# + +Target[aggregate-00005-bits]: + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet9&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet9:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet8&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet8:public@s2:::::2 +Title[aggregate-00005-bits]: Imagine -- IXP Total Aggregate -- Bits / second +Options[aggregate-00005-bits]: growright,bits , pngdate +YLegend[aggregate-00005-bits]: Bits / Second +MaxBytes[aggregate-00005-bits]: 1375000000 +Directory[aggregate-00005-bits]: members/5/00005 + + +##################################################################################################################### +# +# aggregate-00005-pkts +# + +Target[aggregate-00005-pkts]: + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet9&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet9:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet8&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet8:public@s2:::::2 +Title[aggregate-00005-pkts]: Imagine -- IXP Total Aggregate -- Packets / second +Options[aggregate-00005-pkts]: growright , pngdate +YLegend[aggregate-00005-pkts]: Packets / Second +MaxBytes[aggregate-00005-pkts]: 21484375 +Directory[aggregate-00005-pkts]: members/5/00005 + + +##################################################################################################################### +# +# aggregate-00005-errs +# + +Target[aggregate-00005-errs]: + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet9&1.3.6.1.2.1.2.2.1.20#GigabitEthernet9:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet8&1.3.6.1.2.1.2.2.1.20#GigabitEthernet8:public@s2:::::2 +Title[aggregate-00005-errs]: Imagine -- IXP Total Aggregate -- Errors / second +Options[aggregate-00005-errs]: growright , pngdate +YLegend[aggregate-00005-errs]: Errors / Second +MaxBytes[aggregate-00005-errs]: 21484375 +Directory[aggregate-00005-errs]: members/5/00005 + + +##################################################################################################################### +# +# aggregate-00005-discs +# + +Target[aggregate-00005-discs]: + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet9&1.3.6.1.2.1.2.2.1.19#GigabitEthernet9:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet8&1.3.6.1.2.1.2.2.1.19#GigabitEthernet8:public@s2:::::2 +Title[aggregate-00005-discs]: Imagine -- IXP Total Aggregate -- Discards / second +Options[aggregate-00005-discs]: growright , pngdate +YLegend[aggregate-00005-discs]: Discards / Second +MaxBytes[aggregate-00005-discs]: 21484375 +Directory[aggregate-00005-discs]: members/5/00005 + + +##################################################################################################################### +# +# aggregate-00005-bcasts +# + +Target[aggregate-00005-bcasts]: + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet9&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet9:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet8&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet8:public@s2:::::2 +Title[aggregate-00005-bcasts]: Imagine -- IXP Total Aggregate -- Broadcasts / second +Options[aggregate-00005-bcasts]: growright , pngdate +YLegend[aggregate-00005-bcasts]: Broadcasts / Second +MaxBytes[aggregate-00005-bcasts]: 21484375 +Directory[aggregate-00005-bcasts]: members/5/00005 + + + + + + +##################################################################################################################### +##################################################################################################################### +##################################################################################################################### + + +##################################################################################################################### +##################################################################################################################### +##################################################################################################################### +### +### +### +### Core Bundle Ports +### +### Source: /Users/barryo/dev/ixpm-inex/resources/views/services/grapher/mrtg/core-bundles.foil.php +### +##################################################################################################################### +##################################################################################################################### +##################################################################################################################### + + +##################################################################################################################### +##################################################################################################################### +##################################################################################################################### +### +### Core Bundle: Test Core Bundle +### + + + +##################################################################################################################### +# +# cl00009-sidea-bits +# + +Target[cl00009-sidea-bits]: 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet18&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet18:public@s1:::::2 +Title[cl00009-sidea-bits]: Test Core Bundle -- Side A -- GigabitEthernet18 -- switch1 -- Bits / second +Options[cl00009-sidea-bits]: growright,bits , pngdate +YLegend[cl00009-sidea-bits]: Bits / Second +MaxBytes[cl00009-sidea-bits]: 125000000 +Directory[cl00009-sidea-bits]: corebundles/00001/ints + + +##################################################################################################################### +# +# cl00009-sidea-pkts +# + +Target[cl00009-sidea-pkts]: 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet18&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet18:public@s1:::::2 +Title[cl00009-sidea-pkts]: Test Core Bundle -- Side A -- GigabitEthernet18 -- switch1 -- Packets / second +Options[cl00009-sidea-pkts]: growright , pngdate +YLegend[cl00009-sidea-pkts]: Packets / Second +MaxBytes[cl00009-sidea-pkts]: 1953125 +Directory[cl00009-sidea-pkts]: corebundles/00001/ints + + +##################################################################################################################### +# +# cl00009-sidea-errs +# + +Target[cl00009-sidea-errs]: 1.3.6.1.2.1.2.2.1.14#GigabitEthernet18&1.3.6.1.2.1.2.2.1.20#GigabitEthernet18:public@s1:::::2 +Title[cl00009-sidea-errs]: Test Core Bundle -- Side A -- GigabitEthernet18 -- switch1 -- Errors / second +Options[cl00009-sidea-errs]: growright , pngdate +YLegend[cl00009-sidea-errs]: Errors / Second +MaxBytes[cl00009-sidea-errs]: 1953125 +Directory[cl00009-sidea-errs]: corebundles/00001/ints + + +##################################################################################################################### +# +# cl00009-sidea-discs +# + +Target[cl00009-sidea-discs]: 1.3.6.1.2.1.2.2.1.13#GigabitEthernet18&1.3.6.1.2.1.2.2.1.19#GigabitEthernet18:public@s1:::::2 +Title[cl00009-sidea-discs]: Test Core Bundle -- Side A -- GigabitEthernet18 -- switch1 -- Discards / second +Options[cl00009-sidea-discs]: growright , pngdate +YLegend[cl00009-sidea-discs]: Discards / Second +MaxBytes[cl00009-sidea-discs]: 1953125 +Directory[cl00009-sidea-discs]: corebundles/00001/ints + + +##################################################################################################################### +# +# cl00009-sidea-bcasts +# + +Target[cl00009-sidea-bcasts]: 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet18&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet18:public@s1:::::2 +Title[cl00009-sidea-bcasts]: Test Core Bundle -- Side A -- GigabitEthernet18 -- switch1 -- Broadcasts / second +Options[cl00009-sidea-bcasts]: growright , pngdate +YLegend[cl00009-sidea-bcasts]: Broadcasts / Second +MaxBytes[cl00009-sidea-bcasts]: 1953125 +Directory[cl00009-sidea-bcasts]: corebundles/00001/ints + + + + + +##################################################################################################################### +# +# cl00010-sideb-bits +# + +Target[cl00010-sideb-bits]: 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet18&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet18:public@s2:::::2 +Title[cl00010-sideb-bits]: Test Core Bundle -- Side B -- GigabitEthernet18 -- switch2 -- Bits / second +Options[cl00010-sideb-bits]: growright,bits , pngdate +YLegend[cl00010-sideb-bits]: Bits / Second +MaxBytes[cl00010-sideb-bits]: 125000000 +Directory[cl00010-sideb-bits]: corebundles/00001/ints + + +##################################################################################################################### +# +# cl00010-sideb-pkts +# + +Target[cl00010-sideb-pkts]: 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet18&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet18:public@s2:::::2 +Title[cl00010-sideb-pkts]: Test Core Bundle -- Side B -- GigabitEthernet18 -- switch2 -- Packets / second +Options[cl00010-sideb-pkts]: growright , pngdate +YLegend[cl00010-sideb-pkts]: Packets / Second +MaxBytes[cl00010-sideb-pkts]: 1953125 +Directory[cl00010-sideb-pkts]: corebundles/00001/ints + + +##################################################################################################################### +# +# cl00010-sideb-errs +# + +Target[cl00010-sideb-errs]: 1.3.6.1.2.1.2.2.1.14#GigabitEthernet18&1.3.6.1.2.1.2.2.1.20#GigabitEthernet18:public@s2:::::2 +Title[cl00010-sideb-errs]: Test Core Bundle -- Side B -- GigabitEthernet18 -- switch2 -- Errors / second +Options[cl00010-sideb-errs]: growright , pngdate +YLegend[cl00010-sideb-errs]: Errors / Second +MaxBytes[cl00010-sideb-errs]: 1953125 +Directory[cl00010-sideb-errs]: corebundles/00001/ints + + +##################################################################################################################### +# +# cl00010-sideb-discs +# + +Target[cl00010-sideb-discs]: 1.3.6.1.2.1.2.2.1.13#GigabitEthernet18&1.3.6.1.2.1.2.2.1.19#GigabitEthernet18:public@s2:::::2 +Title[cl00010-sideb-discs]: Test Core Bundle -- Side B -- GigabitEthernet18 -- switch2 -- Discards / second +Options[cl00010-sideb-discs]: growright , pngdate +YLegend[cl00010-sideb-discs]: Discards / Second +MaxBytes[cl00010-sideb-discs]: 1953125 +Directory[cl00010-sideb-discs]: corebundles/00001/ints + + +##################################################################################################################### +# +# cl00010-sideb-bcasts +# + +Target[cl00010-sideb-bcasts]: 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet18&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet18:public@s2:::::2 +Title[cl00010-sideb-bcasts]: Test Core Bundle -- Side B -- GigabitEthernet18 -- switch2 -- Broadcasts / second +Options[cl00010-sideb-bcasts]: growright , pngdate +YLegend[cl00010-sideb-bcasts]: Broadcasts / Second +MaxBytes[cl00010-sideb-bcasts]: 1953125 +Directory[cl00010-sideb-bcasts]: corebundles/00001/ints + + + + + +##################################################################################################################### +# +# cl00011-sidea-bits +# + +Target[cl00011-sidea-bits]: 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet19&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet19:public@s1:::::2 +Title[cl00011-sidea-bits]: Test Core Bundle -- Side A -- GigabitEthernet19 -- switch1 -- Bits / second +Options[cl00011-sidea-bits]: growright,bits , pngdate +YLegend[cl00011-sidea-bits]: Bits / Second +MaxBytes[cl00011-sidea-bits]: 125000000 +Directory[cl00011-sidea-bits]: corebundles/00001/ints + + +##################################################################################################################### +# +# cl00011-sidea-pkts +# + +Target[cl00011-sidea-pkts]: 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet19&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet19:public@s1:::::2 +Title[cl00011-sidea-pkts]: Test Core Bundle -- Side A -- GigabitEthernet19 -- switch1 -- Packets / second +Options[cl00011-sidea-pkts]: growright , pngdate +YLegend[cl00011-sidea-pkts]: Packets / Second +MaxBytes[cl00011-sidea-pkts]: 1953125 +Directory[cl00011-sidea-pkts]: corebundles/00001/ints + + +##################################################################################################################### +# +# cl00011-sidea-errs +# + +Target[cl00011-sidea-errs]: 1.3.6.1.2.1.2.2.1.14#GigabitEthernet19&1.3.6.1.2.1.2.2.1.20#GigabitEthernet19:public@s1:::::2 +Title[cl00011-sidea-errs]: Test Core Bundle -- Side A -- GigabitEthernet19 -- switch1 -- Errors / second +Options[cl00011-sidea-errs]: growright , pngdate +YLegend[cl00011-sidea-errs]: Errors / Second +MaxBytes[cl00011-sidea-errs]: 1953125 +Directory[cl00011-sidea-errs]: corebundles/00001/ints + + +##################################################################################################################### +# +# cl00011-sidea-discs +# + +Target[cl00011-sidea-discs]: 1.3.6.1.2.1.2.2.1.13#GigabitEthernet19&1.3.6.1.2.1.2.2.1.19#GigabitEthernet19:public@s1:::::2 +Title[cl00011-sidea-discs]: Test Core Bundle -- Side A -- GigabitEthernet19 -- switch1 -- Discards / second +Options[cl00011-sidea-discs]: growright , pngdate +YLegend[cl00011-sidea-discs]: Discards / Second +MaxBytes[cl00011-sidea-discs]: 1953125 +Directory[cl00011-sidea-discs]: corebundles/00001/ints + + +##################################################################################################################### +# +# cl00011-sidea-bcasts +# + +Target[cl00011-sidea-bcasts]: 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet19&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet19:public@s1:::::2 +Title[cl00011-sidea-bcasts]: Test Core Bundle -- Side A -- GigabitEthernet19 -- switch1 -- Broadcasts / second +Options[cl00011-sidea-bcasts]: growright , pngdate +YLegend[cl00011-sidea-bcasts]: Broadcasts / Second +MaxBytes[cl00011-sidea-bcasts]: 1953125 +Directory[cl00011-sidea-bcasts]: corebundles/00001/ints + + + + + +##################################################################################################################### +# +# cl00012-sideb-bits +# + +Target[cl00012-sideb-bits]: 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet19&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet19:public@s2:::::2 +Title[cl00012-sideb-bits]: Test Core Bundle -- Side B -- GigabitEthernet19 -- switch2 -- Bits / second +Options[cl00012-sideb-bits]: growright,bits , pngdate +YLegend[cl00012-sideb-bits]: Bits / Second +MaxBytes[cl00012-sideb-bits]: 125000000 +Directory[cl00012-sideb-bits]: corebundles/00001/ints + + +##################################################################################################################### +# +# cl00012-sideb-pkts +# + +Target[cl00012-sideb-pkts]: 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet19&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet19:public@s2:::::2 +Title[cl00012-sideb-pkts]: Test Core Bundle -- Side B -- GigabitEthernet19 -- switch2 -- Packets / second +Options[cl00012-sideb-pkts]: growright , pngdate +YLegend[cl00012-sideb-pkts]: Packets / Second +MaxBytes[cl00012-sideb-pkts]: 1953125 +Directory[cl00012-sideb-pkts]: corebundles/00001/ints + + +##################################################################################################################### +# +# cl00012-sideb-errs +# + +Target[cl00012-sideb-errs]: 1.3.6.1.2.1.2.2.1.14#GigabitEthernet19&1.3.6.1.2.1.2.2.1.20#GigabitEthernet19:public@s2:::::2 +Title[cl00012-sideb-errs]: Test Core Bundle -- Side B -- GigabitEthernet19 -- switch2 -- Errors / second +Options[cl00012-sideb-errs]: growright , pngdate +YLegend[cl00012-sideb-errs]: Errors / Second +MaxBytes[cl00012-sideb-errs]: 1953125 +Directory[cl00012-sideb-errs]: corebundles/00001/ints + + +##################################################################################################################### +# +# cl00012-sideb-discs +# + +Target[cl00012-sideb-discs]: 1.3.6.1.2.1.2.2.1.13#GigabitEthernet19&1.3.6.1.2.1.2.2.1.19#GigabitEthernet19:public@s2:::::2 +Title[cl00012-sideb-discs]: Test Core Bundle -- Side B -- GigabitEthernet19 -- switch2 -- Discards / second +Options[cl00012-sideb-discs]: growright , pngdate +YLegend[cl00012-sideb-discs]: Discards / Second +MaxBytes[cl00012-sideb-discs]: 1953125 +Directory[cl00012-sideb-discs]: corebundles/00001/ints + + +##################################################################################################################### +# +# cl00012-sideb-bcasts +# + +Target[cl00012-sideb-bcasts]: 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet19&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet19:public@s2:::::2 +Title[cl00012-sideb-bcasts]: Test Core Bundle -- Side B -- GigabitEthernet19 -- switch2 -- Broadcasts / second +Options[cl00012-sideb-bcasts]: growright , pngdate +YLegend[cl00012-sideb-bcasts]: Broadcasts / Second +MaxBytes[cl00012-sideb-bcasts]: 1953125 +Directory[cl00012-sideb-bcasts]: corebundles/00001/ints + + + + + +##################################################################################################################### +# +# cb-aggregate-00001-sidea-bits +# + +Target[cb-aggregate-00001-sidea-bits]: + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet18&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet18:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet19&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet19:public@s1:::::2 +Title[cb-aggregate-00001-sidea-bits]: Test Core Bundle -- Side A -- Bits / second +Options[cb-aggregate-00001-sidea-bits]: growright,bits , pngdate +YLegend[cb-aggregate-00001-sidea-bits]: Bits / Second +MaxBytes[cb-aggregate-00001-sidea-bits]: 500000000 +Directory[cb-aggregate-00001-sidea-bits]: corebundles/00001 + + +##################################################################################################################### +# +# cb-aggregate-00001-sidea-pkts +# + +Target[cb-aggregate-00001-sidea-pkts]: + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet18&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet18:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet19&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet19:public@s1:::::2 +Title[cb-aggregate-00001-sidea-pkts]: Test Core Bundle -- Side A -- Packets / second +Options[cb-aggregate-00001-sidea-pkts]: growright , pngdate +YLegend[cb-aggregate-00001-sidea-pkts]: Packets / Second +MaxBytes[cb-aggregate-00001-sidea-pkts]: 7812500 +Directory[cb-aggregate-00001-sidea-pkts]: corebundles/00001 + + +##################################################################################################################### +# +# cb-aggregate-00001-sidea-errs +# + +Target[cb-aggregate-00001-sidea-errs]: + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet18&1.3.6.1.2.1.2.2.1.20#GigabitEthernet18:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet19&1.3.6.1.2.1.2.2.1.20#GigabitEthernet19:public@s1:::::2 +Title[cb-aggregate-00001-sidea-errs]: Test Core Bundle -- Side A -- Errors / second +Options[cb-aggregate-00001-sidea-errs]: growright , pngdate +YLegend[cb-aggregate-00001-sidea-errs]: Errors / Second +MaxBytes[cb-aggregate-00001-sidea-errs]: 7812500 +Directory[cb-aggregate-00001-sidea-errs]: corebundles/00001 + + +##################################################################################################################### +# +# cb-aggregate-00001-sidea-discs +# + +Target[cb-aggregate-00001-sidea-discs]: + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet18&1.3.6.1.2.1.2.2.1.19#GigabitEthernet18:public@s1:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet19&1.3.6.1.2.1.2.2.1.19#GigabitEthernet19:public@s1:::::2 +Title[cb-aggregate-00001-sidea-discs]: Test Core Bundle -- Side A -- Discards / second +Options[cb-aggregate-00001-sidea-discs]: growright , pngdate +YLegend[cb-aggregate-00001-sidea-discs]: Discards / Second +MaxBytes[cb-aggregate-00001-sidea-discs]: 7812500 +Directory[cb-aggregate-00001-sidea-discs]: corebundles/00001 + + +##################################################################################################################### +# +# cb-aggregate-00001-sidea-bcasts +# + +Target[cb-aggregate-00001-sidea-bcasts]: + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet18&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet18:public@s1:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet19&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet19:public@s1:::::2 +Title[cb-aggregate-00001-sidea-bcasts]: Test Core Bundle -- Side A -- Broadcasts / second +Options[cb-aggregate-00001-sidea-bcasts]: growright , pngdate +YLegend[cb-aggregate-00001-sidea-bcasts]: Broadcasts / Second +MaxBytes[cb-aggregate-00001-sidea-bcasts]: 7812500 +Directory[cb-aggregate-00001-sidea-bcasts]: corebundles/00001 + + + + + +##################################################################################################################### +# +# cb-aggregate-00001-sideb-bits +# + +Target[cb-aggregate-00001-sideb-bits]: + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet18&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet18:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.6#GigabitEthernet19&1.3.6.1.2.1.31.1.1.1.10#GigabitEthernet19:public@s2:::::2 +Title[cb-aggregate-00001-sideb-bits]: Test Core Bundle -- Side B -- Bits / second +Options[cb-aggregate-00001-sideb-bits]: growright,bits , pngdate +YLegend[cb-aggregate-00001-sideb-bits]: Bits / Second +MaxBytes[cb-aggregate-00001-sideb-bits]: 500000000 +Directory[cb-aggregate-00001-sideb-bits]: corebundles/00001 + + +##################################################################################################################### +# +# cb-aggregate-00001-sideb-pkts +# + +Target[cb-aggregate-00001-sideb-pkts]: + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet18&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet18:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.7#GigabitEthernet19&1.3.6.1.2.1.31.1.1.1.11#GigabitEthernet19:public@s2:::::2 +Title[cb-aggregate-00001-sideb-pkts]: Test Core Bundle -- Side B -- Packets / second +Options[cb-aggregate-00001-sideb-pkts]: growright , pngdate +YLegend[cb-aggregate-00001-sideb-pkts]: Packets / Second +MaxBytes[cb-aggregate-00001-sideb-pkts]: 7812500 +Directory[cb-aggregate-00001-sideb-pkts]: corebundles/00001 + + +##################################################################################################################### +# +# cb-aggregate-00001-sideb-errs +# + +Target[cb-aggregate-00001-sideb-errs]: + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet18&1.3.6.1.2.1.2.2.1.20#GigabitEthernet18:public@s2:::::2 + + 1.3.6.1.2.1.2.2.1.14#GigabitEthernet19&1.3.6.1.2.1.2.2.1.20#GigabitEthernet19:public@s2:::::2 +Title[cb-aggregate-00001-sideb-errs]: Test Core Bundle -- Side B -- Errors / second +Options[cb-aggregate-00001-sideb-errs]: growright , pngdate +YLegend[cb-aggregate-00001-sideb-errs]: Errors / Second +MaxBytes[cb-aggregate-00001-sideb-errs]: 7812500 +Directory[cb-aggregate-00001-sideb-errs]: corebundles/00001 + + +##################################################################################################################### +# +# cb-aggregate-00001-sideb-discs +# + +Target[cb-aggregate-00001-sideb-discs]: + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet18&1.3.6.1.2.1.2.2.1.19#GigabitEthernet18:public@s2:::::2 + + 1.3.6.1.2.1.2.2.1.13#GigabitEthernet19&1.3.6.1.2.1.2.2.1.19#GigabitEthernet19:public@s2:::::2 +Title[cb-aggregate-00001-sideb-discs]: Test Core Bundle -- Side B -- Discards / second +Options[cb-aggregate-00001-sideb-discs]: growright , pngdate +YLegend[cb-aggregate-00001-sideb-discs]: Discards / Second +MaxBytes[cb-aggregate-00001-sideb-discs]: 7812500 +Directory[cb-aggregate-00001-sideb-discs]: corebundles/00001 + + +##################################################################################################################### +# +# cb-aggregate-00001-sideb-bcasts +# + +Target[cb-aggregate-00001-sideb-bcasts]: + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet18&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet18:public@s2:::::2 + + 1.3.6.1.2.1.31.1.1.1.9#GigabitEthernet19&1.3.6.1.2.1.31.1.1.1.13#GigabitEthernet19:public@s2:::::2 +Title[cb-aggregate-00001-sideb-bcasts]: Test Core Bundle -- Side B -- Broadcasts / second +Options[cb-aggregate-00001-sideb-bcasts]: growright , pngdate +YLegend[cb-aggregate-00001-sideb-bcasts]: Broadcasts / Second +MaxBytes[cb-aggregate-00001-sideb-bcasts]: 7812500 +Directory[cb-aggregate-00001-sideb-bcasts]: corebundles/00001 + + + + + +##################################################################################################################### +##################################################################################################################### +##################################################################################################################### + + + + + +### DO NOT EDIT THIS FILE - IT IS AUTOMATICALLY GENERATED +### +### Source: /Users/barryo/dev/ixpm-inex/resources/views/services/grapher/mrtg/footer.foil.php +### Generated: 2025-08-18 19:00:59 +### Generation time: 0.014029 secs diff --git a/data/ci/known-good/ci-services-grapher-smokeping-vlanid1-ipv4.conf b/data/ci/known-good/ci-services-grapher-smokeping-vlanid1-ipv4.conf new file mode 100644 index 000000000..c46961535 --- /dev/null +++ b/data/ci/known-good/ci-services-grapher-smokeping-vlanid1-ipv4.conf @@ -0,0 +1,36 @@ +# +# This file contains Smokeping configuration for customers VLAN interfaces +# +# WARNING: this file is automatically generated using the +# api/v4/grapher/config?backend=smokeping&vlanid=1&protocol=ipv4 API call to IXP Manager. +# Any local changes made to this script will be lost. +# +# VLAN id: 1; tag: 1; name: Peering LAN 1; protocol: ipv4. +# +# Generated: 2018-05-13 19:42:36 +# + + +# AS112 / 10.1.0.6 ++++ vlanint_4_ipv4 +menu = AS112 (IPv4) +title = Peering LAN 1 :: AS112 via 10.1.0.6 +probe = FPing +host = 10.1.0.6 + + +# HEAnet / 10.1.0.10 ++++ vlanint_1_ipv4 +menu = HEAnet (IPv4) +title = Peering LAN 1 :: HEAnet via 10.1.0.10 +probe = FPing +host = 10.1.0.10 + + +# PCH DNS / 10.1.0.36 ++++ vlanint_3_ipv4 +menu = PCH DNS (IPv4) +title = Peering LAN 1 :: PCH DNS via 10.1.0.36 +probe = FPing +host = 10.1.0.36 + diff --git a/data/ci/known-good/ci-services-grapher-smokeping-vlanid1-ipv6.conf b/data/ci/known-good/ci-services-grapher-smokeping-vlanid1-ipv6.conf new file mode 100644 index 000000000..dc1e531ab --- /dev/null +++ b/data/ci/known-good/ci-services-grapher-smokeping-vlanid1-ipv6.conf @@ -0,0 +1,28 @@ +# +# This file contains Smokeping configuration for customers VLAN interfaces +# +# WARNING: this file is automatically generated using the +# api/v4/grapher/config?backend=smokeping&vlanid=1&protocol=ipv6 API call to IXP Manager. +# Any local changes made to this script will be lost. +# +# VLAN id: 1; tag: 1; name: Peering LAN 1; protocol: ipv6. +# +# Generated: 2018-05-13 19:43:18 +# + + +# HEAnet / 2001:db8:1::10 ++++ vlanint_1_ipv6 +menu = HEAnet (IPv6) +title = Peering LAN 1 :: HEAnet via 2001:db8:1::10 +probe = FPing6 +host = 2001:db8:1::10 + + +# Imagine / 2001:db8:1::8 ++++ vlanint_6_ipv6 +menu = Imagine (IPv6) +title = Peering LAN 1 :: Imagine via 2001:db8:1::8 +probe = FPing6 +host = 2001:db8:1::8 + diff --git a/data/ci/known-good/ci-services-grapher-smokeping-vlanid2-ipv4.conf b/data/ci/known-good/ci-services-grapher-smokeping-vlanid2-ipv4.conf new file mode 100644 index 000000000..790af19fd --- /dev/null +++ b/data/ci/known-good/ci-services-grapher-smokeping-vlanid2-ipv4.conf @@ -0,0 +1,36 @@ +# +# This file contains Smokeping configuration for customers VLAN interfaces +# +# WARNING: this file is automatically generated using the +# api/v4/grapher/config?backend=smokeping&vlanid=2&protocol=ipv4 API call to IXP Manager. +# Any local changes made to this script will be lost. +# +# VLAN id: 2; tag: 2; name: Peering LAN 2; protocol: ipv4. +# +# Generated: 2018-05-13 19:43:32 +# + + +# AS112 / 10.2.0.6 ++++ vlanint_5_ipv4 +menu = AS112 (IPv4) +title = Peering LAN 2 :: AS112 via 10.2.0.6 +probe = FPing +host = 10.2.0.6 + + +# HEAnet / 10.2.0.11 ++++ vlanint_2_ipv4 +menu = HEAnet (IPv4) +title = Peering LAN 2 :: HEAnet via 10.2.0.11 +probe = FPing +host = 10.2.0.11 + + +# Imagine / 10.2.0.46 ++++ vlanint_7_ipv4 +menu = Imagine (IPv4) +title = Peering LAN 2 :: Imagine via 10.2.0.46 +probe = FPing +host = 10.2.0.46 + diff --git a/data/ci/known-good/ci-services-grapher-smokeping-vlanid2-ipv6.conf b/data/ci/known-good/ci-services-grapher-smokeping-vlanid2-ipv6.conf new file mode 100644 index 000000000..998d1ca37 --- /dev/null +++ b/data/ci/known-good/ci-services-grapher-smokeping-vlanid2-ipv6.conf @@ -0,0 +1,28 @@ +# +# This file contains Smokeping configuration for customers VLAN interfaces +# +# WARNING: this file is automatically generated using the +# api/v4/grapher/config?backend=smokeping&vlanid=2&protocol=ipv6 API call to IXP Manager. +# Any local changes made to this script will be lost. +# +# VLAN id: 2; tag: 2; name: Peering LAN 2; protocol: ipv6. +# +# Generated: 2018-05-13 19:43:44 +# + + +# HEAnet / 2001:db8:2::11 ++++ vlanint_2_ipv6 +menu = HEAnet (IPv6) +title = Peering LAN 2 :: HEAnet via 2001:db8:2::11 +probe = FPing6 +host = 2001:db8:2::11 + + +# Imagine / 2001:db8:2::46 ++++ vlanint_7_ipv6 +menu = Imagine (IPv6) +title = Peering LAN 2 :: Imagine via 2001:db8:2::46 +probe = FPing6 +host = 2001:db8:2::46 + diff --git a/data/ci/known-good/ix-f/provider.json b/data/ci/known-good/ix-f/provider.json new file mode 100644 index 000000000..37a38a93e --- /dev/null +++ b/data/ci/known-good/ix-f/provider.json @@ -0,0 +1 @@ +[{"id":1153,"pdb_id":3562,"name":"1-IX Kyiv (1-IX Internet Exchange)","city":"Kyiv","country":"UA","organization_id":82003,"apis":{"ixfexport":"","traffic":""},"participant_count":0,"location_count":0,"updated":"2022-10-18T15:08:19Z","manrs":false,"website":"http://1-ix.net","looking_glass":[""]},{"id":1152,"pdb_id":3958,"name":"1-IX Warsaw (1-IX Internet Exchange Warsaw)","city":"Warsaw","country":"PL","organization_id":82001,"apis":{"ixfexport":"","traffic":""},"participant_count":0,"location_count":0,"updated":"2022-10-18T15:11:35Z","manrs":false,"website":"https://1-ix.net","looking_glass":[""]},{"id":899,"pdb_id":3179,"name":"48 IX (48 IX)","city":"","country":"US","organization_id":70781,"apis":{"ixfexport":"https://ixf.48ix.net","traffic":""},"participant_count":4,"location_count":1,"updated":"2024-02-13T03:04:06Z","manrs":false,"website":"https://www.48ix.net/","looking_glass":[""]},{"id":2,"pdb_id":26,"name":"AMS-IX","city":"Amsterdam","country":"NL","organization_id":9,"apis":{"ixfexport":"https://my.ams-ix.net/api/v1/members.json","traffic":""},"participant_count":875,"location_count":68,"updated":"2024-04-25T08:16:30Z","manrs":true,"website":"https://ams-ix.net/ams/","looking_glass":[""]},{"id":1214,"pdb_id":4232,"name":"AMS-IX Bangkok","city":"Bangkok","country":"TH","organization_id":84043,"apis":{"ixfexport":"https://my.ams-ix.net/api/v1/links.json?exchange=BAN","traffic":""},"participant_count":0,"location_count":0,"updated":"2023-08-23T05:14:47Z","manrs":false,"website":"https://www.ams-ix.net/ban","looking_glass":[""]},{"id":1,"pdb_id":18,"name":"LINX LON1 (LINX LON1)","city":"London","country":"GB","organization_id":7,"apis":{"ixfexport":"https://portal.linx.net/members.json","traffic":""},"participant_count":829,"location_count":10,"updated":"2024-04-25T08:16:31Z","manrs":true,"website":"https://www.linx.net/","looking_glass":[""]},{"id":600,"pdb_id":321,"name":"LINX LON2 (LINX LON2)","city":"London","country":"GB","organization_id":572,"apis":{"ixfexport":"https://portal.linx.net/members.json","traffic":""},"participant_count":317,"location_count":10,"updated":"2024-04-25T08:16:31Z","manrs":true,"website":"https://www.linx.net/","looking_glass":[""]},{"id":646,"pdb_id":1262,"name":"INEX Cork (Internet Neutral Exchange Association Limited by Guarantee)","city":"Cork","country":"IE","organization_id":614,"apis":{"ixfexport":"https://www.inex.ie/ixp/api/v4/member-export/ixf/0.7","traffic":"https://www.inex.ie/ixp/grapher/infrastructure?id=3\u0026type=log\u0026period=day"},"participant_count":14,"location_count":1,"updated":"2024-04-10T00:01:51Z","manrs":false,"website":"https://www.inex.ie/","looking_glass":["https://www.inex.ie/ixp/lg"]},{"id":20,"pdb_id":48,"name":"INEX LAN1 (Internet Neutral Exchange Association Limited by Guarantee)","city":"Dublin","country":"IE","organization_id":28,"apis":{"ixfexport":"https://www.inex.ie/ixp/api/v4/member-export/ixf/1.0","traffic":"https://www.inex.ie/ixp/grapher/infrastructure?id=1\u0026type=log\u0026period=day"},"participant_count":111,"location_count":5,"updated":"2024-04-25T08:16:31Z","manrs":true,"website":"https://www.inex.ie/","looking_glass":["https://www.inex.ie/ixp/lg"]},{"id":645,"pdb_id":387,"name":"INEX LAN2 (Internet Neutral Exchange Association Limited by Guarantee)","city":"Dublin","country":"IE","organization_id":613,"apis":{"ixfexport":"https://www.inex.ie/ixp/api/v4/member-export/ixf/0.7","traffic":"https://www.inex.ie/ixp/grapher/infrastructure?id=2\u0026type=log\u0026period=day"},"participant_count":70,"location_count":5,"updated":"2024-04-10T00:01:46Z","manrs":false,"website":"https://www.inex.ie/","looking_glass":["https://www.inex.ie/ixp/lg"]},{"id":349,"pdb_id":583,"name":"LINX Manchester (LINX Manchester)","city":"Manchester","country":"GB","organization_id":377,"apis":{"ixfexport":"https://portal.linx.net/members.json","traffic":""},"participant_count":134,"location_count":6,"updated":"2024-04-25T08:16:31Z","manrs":true,"website":"https://www.linx.net/","looking_glass":[""]}] \ No newline at end of file diff --git a/data/ci/known-good/peeringdb/fac.json b/data/ci/known-good/peeringdb/fac.json new file mode 100644 index 000000000..2647073ed --- /dev/null +++ b/data/ci/known-good/peeringdb/fac.json @@ -0,0 +1 @@ +{"data": [{"id": 1, "org_id": 2, "org_name": "Equinix, Inc.", "campus_id": 14, "name": "Equinix DC1-DC15, DC21 - Ashburn", "aka": "", "name_long": "", "website": "http://www.equinix.com/", "social_media": [{"service": "website", "identifier": "http://www.equinix.com/"}], "clli": "ASBNVA", "rencode": "", "npanxx": "703-723", "notes": "", "net_count": 458, "ix_count": 6, "sales_email": "", "sales_phone": "", "tech_email": "", "tech_phone": "", "available_voltage_services": [], "diverse_serving_substations": null, "property": null, "region_continent": "North America", "status_dashboard": null, "created": "2010-07-29T00:00:00Z", "updated": "2023-02-28T21:27:14Z", "status": "ok", "address1": "21715 Filigree Ct", "address2": "Building F", "city": "Ashburn", "country": "US", "state": "VA", "zipcode": "20147-6205", "floor": "", "suite": "", "latitude": 39.016363, "longitude": -77.459023}, {"id": 2, "org_id": 8592, "org_name": "Digital Realty", "campus_id": null, "name": "Digital Realty SFO (200 Paul)", "aka": "", "name_long": "", "website": "https://www.digitalrealty.com", "social_media": [{"service": "website", "identifier": "https://www.digitalrealty.com"}], "clli": "SNFCCA", "rencode": "", "npanxx": "415-822", "notes": "", "net_count": 44, "ix_count": 2, "sales_email": "", "sales_phone": "", "tech_email": "", "tech_phone": "", "available_voltage_services": [], "diverse_serving_substations": null, "property": "", "region_continent": "North America", "status_dashboard": null, "created": "2010-07-29T00:00:00Z", "updated": "2021-11-01T23:10:23Z", "status": "ok", "address1": "200 Paul Ave", "address2": "", "city": "San Francisco", "country": "US", "state": "CA", "zipcode": "94110", "floor": "", "suite": "", "latitude": 37.723407, "longitude": -122.397923}, {"id": 4, "org_id": 2, "org_name": "Equinix, Inc.", "campus_id": null, "name": "Equinix DA1 - Dallas", "aka": "", "name_long": "", "website": "https://www.equinix.com/locations/americas-colocation/united-states-colocation/dallas-data-centers/da1/", "social_media": [{"service": "website", "identifier": "https://www.equinix.com/locations/americas-colocation/united-states-colocation/dallas-data-centers/da1/"}], "clli": "DLLSTX", "rencode": "", "npanxx": "214-782", "notes": "", "net_count": 147, "ix_count": 4, "sales_email": "", "sales_phone": "", "tech_email": "", "tech_phone": "", "available_voltage_services": [], "diverse_serving_substations": null, "property": null, "region_continent": "North America", "status_dashboard": null, "created": "2010-07-29T00:00:00Z", "updated": "2020-08-26T05:23:01Z", "status": "ok", "address1": "1950 N Stemmons Fwy", "address2": "Ste 1034", "city": "Dallas", "country": "US", "state": "TX", "zipcode": "75207-3137", "floor": "", "suite": "", "latitude": 32.800955, "longitude": -96.81955}, {"id": 5, "org_id": 2, "org_name": "Equinix, Inc.", "campus_id": null, "name": "Equinix SV8 - Silicon Valley, Palo Alto", "aka": "", "name_long": "", "website": "https://www.equinix.com/locations/americas-colocation/united-states-colocation/silicon-valley-data-centers/sv8/", "social_media": [{"service": "website", "identifier": "https://www.equinix.com/locations/americas-colocation/united-states-colocation/silicon-valley-data-centers/sv8/"}], "clli": "PLALCA", "rencode": "", "npanxx": "650-617", "notes": "", "net_count": 74, "ix_count": 4, "sales_email": "", "sales_phone": "", "tech_email": "", "tech_phone": "", "available_voltage_services": [], "diverse_serving_substations": null, "property": null, "region_continent": "North America", "status_dashboard": null, "created": "2010-07-29T00:00:00Z", "updated": "2020-08-26T05:23:01Z", "status": "ok", "address1": "529 Bryant St", "address2": "", "city": "Palo Alto", "country": "US", "state": "CA", "zipcode": "94301-1704", "floor": "", "suite": "", "latitude": 37.445896, "longitude": -122.160775}, {"id": 6, "org_id": 2, "org_name": "Equinix, Inc.", "campus_id": null, "name": "Equinix SV1/SV5/SV10 - Silicon Valley, San Jose", "aka": "", "name_long": "", "website": "https://www.equinix.com/locations/americas-colocation/united-states-colocation/silicon-valley-data-centers/", "social_media": [{"service": "website", "identifier": "https://www.equinix.com/locations/americas-colocation/united-states-colocation/silicon-valley-data-centers/"}], "clli": "SNJSCA", "rencode": "", "npanxx": "408-360", "notes": "", "net_count": 270, "ix_count": 7, "sales_email": "", "sales_phone": "", "tech_email": "", "tech_phone": "", "available_voltage_services": [], "diverse_serving_substations": null, "property": null, "region_continent": "North America", "status_dashboard": null, "created": "2010-07-29T00:00:00Z", "updated": "2020-08-26T05:23:01Z", "status": "ok", "address1": "11 Great Oaks Blvd", "address2": "", "city": "San Jose", "country": "US", "state": "CA", "zipcode": "95119-1242", "floor": "", "suite": "", "latitude": 37.241767, "longitude": -121.782967}, {"id": 7, "org_id": 2, "org_name": "Equinix, Inc.", "campus_id": null, "name": "Equinix CH1/CH2/CH4 - Chicago", "aka": "", "name_long": "", "website": "http://www.equinix.com/", "social_media": [{"service": "website", "identifier": "http://www.equinix.com/"}], "clli": "CHCGIL", "rencode": "", "npanxx": "312-596", "notes": "", "net_count": 312, "ix_count": 6, "sales_email": "", "sales_phone": "", "tech_email": "", "tech_phone": "", "available_voltage_services": [], "diverse_serving_substations": null, "property": null, "region_continent": "North America", "status_dashboard": null, "created": "2010-07-29T00:00:00Z", "updated": "2020-08-26T05:23:01Z", "status": "ok", "address1": "350 E Cermak Rd", "address2": "5th Floor / 6th Floor / 8th Floor", "city": "Chicago", "country": "US", "state": "IL", "zipcode": "60616-1568", "floor": "", "suite": "", "latitude": 41.85365, "longitude": -87.618342}, {"id": 8, "org_id": 2, "org_name": "Equinix, Inc.", "campus_id": null, "name": "Equinix LA1 - Los Angeles", "aka": "", "name_long": "", "website": "https://www.equinix.com/locations/americas-colocation/united-states-colocation/los-angeles-data-centers/la1/", "social_media": [{"service": "website", "identifier": "https://www.equinix.com/locations/americas-colocation/united-states-colocation/los-angeles-data-centers/la1/"}], "clli": "LSANCA", "rencode": "", "npanxx": "213-270", "notes": "", "net_count": 147, "ix_count": 6, "sales_email": "", "sales_phone": "", "tech_email": "", "tech_phone": "", "available_voltage_services": [], "diverse_serving_substations": null, "property": null, "region_continent": "North America", "status_dashboard": null, "created": "2010-07-29T00:00:00Z", "updated": "2020-08-26T05:23:01Z", "status": "ok", "address1": "600 W 7th St", "address2": "6th Floor", "city": "Los Angeles", "country": "US", "state": "CA", "zipcode": "90017-3859", "floor": "", "suite": "", "latitude": 34.047501, "longitude": -118.2573}, {"id": 9, "org_id": 2, "org_name": "Equinix, Inc.", "campus_id": null, "name": "Equinix NY1 - New York, Newark", "aka": "", "name_long": "", "website": "https://www.equinix.com/locations/americas-colocation/united-states-colocation/new-york-data-centers/ny1/", "social_media": [{"service": "website", "identifier": "https://www.equinix.com/locations/americas-colocation/united-states-colocation/new-york-data-centers/ny1/"}], "clli": "NWRKNJ", "rencode": "", "npanxx": "973-792", "notes": "", "net_count": 31, "ix_count": 2, "sales_email": "", "sales_phone": "", "tech_email": "", "tech_phone": "", "available_voltage_services": [], "diverse_serving_substations": null, "property": null, "region_continent": "North America", "status_dashboard": null, "created": "2010-07-29T00:00:00Z", "updated": "2020-08-26T05:23:01Z", "status": "ok", "address1": "165 Halsey St", "address2": "8th Floor", "city": "Newark", "country": "US", "state": "NJ", "zipcode": "07102-2834", "floor": "", "suite": "", "latitude": 40.736844, "longitude": -74.173402}, {"id": 10, "org_id": 8592, "org_name": "Digital Realty", "campus_id": null, "name": "Digital Realty NYC (60 Hudson)", "aka": "", "name_long": "", "website": "https://www.digitalrealty.com", "social_media": [{"service": "website", "identifier": "https://www.digitalrealty.com"}], "clli": "NYCMNY", "rencode": "", "npanxx": "212-233", "notes": "fka: Telx NYC1", "net_count": 150, "ix_count": 7, "sales_email": "", "sales_phone": "", "tech_email": "", "tech_phone": "", "available_voltage_services": [], "diverse_serving_substations": null, "property": "", "region_continent": "North America", "status_dashboard": null, "created": "2010-07-29T00:00:00Z", "updated": "2021-11-01T23:08:37Z", "status": "ok", "address1": "60 Hudson St", "address2": "", "city": "New York", "country": "US", "state": "NY", "zipcode": "10013-3315", "floor": "", "suite": "", "latitude": 40.717723, "longitude": -74.008299}, {"id": 11, "org_id": 2, "org_name": "Equinix, Inc.", "campus_id": null, "name": "Equinix AT2/AT3 - Atlanta", "aka": "", "name_long": "", "website": "http://www.equinix.com/", "social_media": [{"service": "website", "identifier": "http://www.equinix.com/"}], "clli": "ATLNGA", "rencode": "", "npanxx": "404-525", "notes": "", "net_count": 30, "ix_count": 2, "sales_email": "", "sales_phone": "", "tech_email": "", "tech_phone": "", "available_voltage_services": [], "diverse_serving_substations": null, "property": null, "region_continent": "North America", "status_dashboard": null, "created": "2010-07-29T00:00:00Z", "updated": "2020-08-26T05:23:01Z", "status": "ok", "address1": "56 Marietta St NW", "address2": "5th Floor", "city": "Atlanta", "country": "US", "state": "GA", "zipcode": "30303-2885", "floor": "", "suite": "", "latitude": 33.755456, "longitude": -84.39153}, {"id": 12, "org_id": 2, "org_name": "Equinix, Inc.", "campus_id": null, "name": "Equinix TR1 - Toronto", "aka": "", "name_long": "", "website": "https://www.equinix.com/locations/americas-colocation/canada-colocation/toronto-data-center/tr1/", "social_media": [{"service": "website", "identifier": "https://www.equinix.com/locations/americas-colocation/canada-colocation/toronto-data-center/tr1/"}], "clli": "TOROON", "rencode": "", "npanxx": "416-369", "notes": "Suite 706", "net_count": 41, "ix_count": 5, "sales_email": "", "sales_phone": "", "tech_email": "", "tech_phone": "", "available_voltage_services": [], "diverse_serving_substations": null, "property": null, "region_continent": "North America", "status_dashboard": null, "created": "2010-07-29T00:00:00Z", "updated": "2020-07-03T12:01:07Z", "status": "ok", "address1": "151 Front St", "address2": "", "city": "Toronto", "country": "CA", "state": "ON", "zipcode": "M5J 2N1", "floor": "", "suite": "", "latitude": 43.644672, "longitude": -79.384128}, {"id": 15, "org_id": 2, "org_name": "Equinix, Inc.", "campus_id": null, "name": "Equinix MI1 - Miami, NOTA", "aka": "", "name_long": "", "website": "https://www.equinix.com/locations/americas-colocation/united-states-colocation/miami-data-centers/mi1/", "social_media": [{"service": "website", "identifier": "https://www.equinix.com/locations/americas-colocation/united-states-colocation/miami-data-centers/mi1/"}], "clli": "MIAUFL", "rencode": "", "npanxx": "305-373", "notes": "Formerly: NAP of the Americas", "net_count": 286, "ix_count": 7, "sales_email": "", "sales_phone": "", "tech_email": "", "tech_phone": "", "available_voltage_services": [], "diverse_serving_substations": null, "property": null, "region_continent": "North America", "status_dashboard": null, "created": "2010-07-29T00:00:00Z", "updated": "2020-08-26T05:23:01Z", "status": "ok", "address1": "50 NE 9th St", "address2": "", "city": "Miami", "country": "US", "state": "FL", "zipcode": "33132-1709", "floor": "", "suite": "", "latitude": 25.782648, "longitude": -80.193157}, {"id": 16, "org_id": 8592, "org_name": "Digital Realty", "campus_id": null, "name": "Digital Realty NYC (111 8th Ave)", "aka": "", "name_long": "", "website": "https://www.digitalrealty.com", "social_media": [{"service": "website", "identifier": "https://www.digitalrealty.com"}], "clli": "NYCMNY", "rencode": "", "npanxx": "212-905", "notes": "", "net_count": 129, "ix_count": 5, "sales_email": "", "sales_phone": "", "tech_email": "", "tech_phone": "", "available_voltage_services": [], "diverse_serving_substations": null, "property": "", "region_continent": "North America", "status_dashboard": null, "created": "2010-07-29T00:00:00Z", "updated": "2021-11-01T23:07:33Z", "status": "ok", "address1": "111 8th Ave", "address2": "", "city": "New York", "country": "US", "state": "New York", "zipcode": "10011", "floor": "", "suite": "", "latitude": 40.741469, "longitude": -74.003387}, {"id": 18, "org_id": 31, "org_name": "Nikhef", "campus_id": null, "name": "NIKHEF Amsterdam", "aka": "", "name_long": "", "website": "http://www.nikhefhousing.nl", "social_media": [{"service": "website", "identifier": "http://www.nikhefhousing.nl"}], "clli": "AMSTNL", "rencode": "", "npanxx": "", "notes": "", "net_count": 351, "ix_count": 22, "sales_email": "", "sales_phone": "", "tech_email": "", "tech_phone": "", "available_voltage_services": [], "diverse_serving_substations": null, "property": null, "region_continent": "Europe", "status_dashboard": null, "created": "2010-07-29T00:00:00Z", "updated": "2019-10-03T13:40:48Z", "status": "ok", "address1": "Science Park 105", "address2": "", "city": "Amsterdam", "country": "NL", "state": "NH", "zipcode": "1098 XG", "floor": "", "suite": "", "latitude": 52.356394, "longitude": 4.950837}, {"id": 19, "org_id": 34, "org_name": "CoreSite", "campus_id": 7, "name": "CoreSite - Los Angeles (LA1) One Wilshire", "aka": "", "name_long": "", "website": "http://www.coresite.com", "social_media": [{"service": "website", "identifier": "http://www.coresite.com"}], "clli": "LSANCARC", "rencode": "", "npanxx": "213-489", "notes": "", "net_count": 317, "ix_count": 6, "sales_email": "info@coresite.com", "sales_phone": "+18667772673", "tech_email": "info@coresite.com", "tech_phone": "+18667772673", "available_voltage_services": [], "diverse_serving_substations": null, "property": null, "region_continent": "North America", "status_dashboard": null, "created": "2010-07-29T00:00:00Z", "updated": "2023-02-28T18:07:59Z", "status": "ok", "address1": "624 S. Grand Ave.", "address2": "Suite 110", "city": "Los Angeles", "country": "US", "state": "CA", "zipcode": "90017", "floor": "", "suite": "", "latitude": 34.047942, "longitude": -118.255564}, {"id": 20, "org_id": 34, "org_name": "CoreSite", "campus_id": 10, "name": "CoreSite - San Jose (SV1)", "aka": "", "name_long": "", "website": "http://www.coresite.com", "social_media": [{"service": "website", "identifier": "http://www.coresite.com"}], "clli": "SNJSCAJN", "rencode": "", "npanxx": "408-918", "notes": "", "net_count": 57, "ix_count": 2, "sales_email": "info@coresite.com", "sales_phone": "+18667772673", "tech_email": "info@coresite.com", "tech_phone": "+18667772673", "available_voltage_services": [], "diverse_serving_substations": null, "property": "Owner", "region_continent": "North America", "status_dashboard": "", "created": "2010-07-29T00:00:00Z", "updated": "2023-05-09T00:16:00Z", "status": "ok", "address1": "55 S. Market Street", "address2": "Suite 440", "city": "San Jose", "country": "US", "state": "CA", "zipcode": "95113", "floor": "", "suite": "", "latitude": 37.334147, "longitude": -121.891649}, {"id": 21, "org_id": 38, "org_name": "60 Fed LLC", "campus_id": null, "name": "FedConnect San Francisco", "aka": "", "name_long": "", "website": "", "social_media": [], "clli": "SNFCCA", "rencode": "", "npanxx": "415-495", "notes": "", "net_count": 0, "ix_count": 0, "sales_email": "", "sales_phone": "", "tech_email": "", "tech_phone": "", "available_voltage_services": [], "diverse_serving_substations": null, "property": null, "region_continent": "North America", "status_dashboard": null, "created": "2010-07-29T00:00:00Z", "updated": "2017-09-06T04:15:10Z", "status": "ok", "address1": "60 Federal Street", "address2": "", "city": "San Francisco", "country": "US", "state": "CA", "zipcode": "94103", "floor": "", "suite": "", "latitude": 37.783322, "longitude": -122.392048}, {"id": 22, "org_id": 41, "org_name": "JMA Wired", "campus_id": null, "name": "JMA NAC Los Angeles", "aka": "", "name_long": "", "website": "", "social_media": [], "clli": "LSANCA", "rencode": "", "npanxx": "213-955", "notes": "", "net_count": 1, "ix_count": 1, "sales_email": "", "sales_phone": "", "tech_email": "", "tech_phone": "", "available_voltage_services": [], "diverse_serving_substations": null, "property": null, "region_continent": "North America", "status_dashboard": null, "created": "2010-07-29T00:00:00Z", "updated": "2017-09-06T04:15:10Z", "status": "ok", "address1": "530 West 6th Street", "address2": "", "city": "Los Angeles", "country": "US", "state": "CA", "zipcode": "90014", "floor": "", "suite": "", "latitude": 34.048428, "longitude": -118.255186}, {"id": 23, "org_id": 8592, "org_name": "Digital Realty", "campus_id": null, "name": "Digital Realty SFO (365 Main)", "aka": "", "name_long": "", "website": "https://www.digitalrealty.com", "social_media": [{"service": "website", "identifier": "https://www.digitalrealty.com"}], "clli": "SNFCCA", "rencode": "", "npanxx": "415-546", "notes": "", "net_count": 29, "ix_count": 2, "sales_email": "", "sales_phone": "", "tech_email": "", "tech_phone": "", "available_voltage_services": [], "diverse_serving_substations": null, "property": "", "region_continent": "North America", "status_dashboard": null, "created": "2010-07-29T00:00:00Z", "updated": "2021-11-01T23:10:43Z", "status": "ok", "address1": "365 Main Street", "address2": "", "city": "San Francisco", "country": "US", "state": "CA", "zipcode": "94105", "floor": "", "suite": "", "latitude": 37.788669, "longitude": -122.390509}, {"id": 24, "org_id": 1090, "org_name": "UnitedLayer LLC", "campus_id": null, "name": "The Mission Street Center", "aka": "", "name_long": "", "website": "https://unitedlayer.com", "social_media": [], "clli": "SNFCCA", "rencode": "", "npanxx": "415-865", "notes": "", "net_count": 0, "ix_count": 0, "sales_email": "", "sales_phone": "", "tech_email": "", "tech_phone": "", "available_voltage_services": [], "diverse_serving_substations": null, "property": null, "region_continent": "North America", "status_dashboard": null, "created": "2010-07-29T00:00:00Z", "updated": "2018-06-28T20:37:30Z", "status": "ok", "address1": "1019 Mission Street", "address2": "", "city": "San Francisco", "country": "US", "state": "CA", "zipcode": "94107", "floor": "", "suite": "", "latitude": 37.780351, "longitude": -122.408954}], "meta": {}} diff --git a/data/ci/known-good/peeringdb/ix.json b/data/ci/known-good/peeringdb/ix.json new file mode 100644 index 000000000..f2818e9fe --- /dev/null +++ b/data/ci/known-good/peeringdb/ix.json @@ -0,0 +1 @@ +{"data": [{"id": 1, "org_id": 2, "name": "Equinix Ashburn", "aka": "", "name_long": "Equinix Internet Exchange Ashburn", "city": "Ashburn", "country": "US", "region_continent": "North America", "media": "Ethernet", "notes": "", "proto_unicast": true, "proto_multicast": false, "proto_ipv6": true, "website": "https://ix.equinix.com", "social_media": [{"service": "website", "identifier": "https://ix.equinix.com"}], "url_stats": "", "tech_email": "support@equinix.com", "tech_phone": "", "policy_email": "support@equinix.com", "policy_phone": "", "sales_phone": "", "sales_email": "", "net_count": 343, "fac_count": 2, "ixf_net_count": 0, "ixf_last_import": null, "ixf_import_request": null, "ixf_import_request_status": "queued", "service_level": "24/7 Support", "terms": "Recurring Fees", "status_dashboard": null, "created": "2010-07-29T00:00:00Z", "updated": "2021-10-16T21:48:58Z", "status": "ok"}, {"id": 2, "org_id": 2, "name": "Equinix Chicago", "aka": "", "name_long": "Equinix Internet Exchange Chicago", "city": "Chicago", "country": "US", "region_continent": "North America", "media": "Ethernet", "notes": "", "proto_unicast": true, "proto_multicast": false, "proto_ipv6": true, "website": "https://ix.equinix.com", "social_media": [{"service": "website", "identifier": "https://ix.equinix.com"}], "url_stats": "", "tech_email": "support@equinix.com", "tech_phone": "", "policy_email": "support@equinix.com", "policy_phone": "", "sales_phone": "", "sales_email": "", "net_count": 254, "fac_count": 3, "ixf_net_count": 0, "ixf_last_import": null, "ixf_import_request": null, "ixf_import_request_status": "queued", "service_level": "24/7 Support", "terms": "Recurring Fees", "status_dashboard": null, "created": "2010-07-29T00:00:00Z", "updated": "2021-10-16T21:49:09Z", "status": "ok"}, {"id": 3, "org_id": 2, "name": "Equinix Dallas", "aka": "", "name_long": "Equinix Internet Exchange Dallas", "city": "Dallas", "country": "US", "region_continent": "North America", "media": "Ethernet", "notes": "", "proto_unicast": true, "proto_multicast": false, "proto_ipv6": true, "website": "https://ix.equinix.com", "social_media": [{"service": "website", "identifier": "https://ix.equinix.com"}], "url_stats": "", "tech_email": "support@equinix.com", "tech_phone": "", "policy_email": "support@equinix.com", "policy_phone": "", "sales_phone": "", "sales_email": "", "net_count": 195, "fac_count": 6, "ixf_net_count": 0, "ixf_last_import": null, "ixf_import_request": null, "ixf_import_request_status": "queued", "service_level": "24/7 Support", "terms": "Recurring Fees", "status_dashboard": null, "created": "2010-07-29T00:00:00Z", "updated": "2021-10-16T21:49:14Z", "status": "ok"}, {"id": 4, "org_id": 2, "name": "Equinix Los Angeles", "aka": "", "name_long": "Equinix Internet Exchange Los Angeles", "city": "Los Angeles", "country": "US", "region_continent": "North America", "media": "Ethernet", "notes": "", "proto_unicast": true, "proto_multicast": false, "proto_ipv6": true, "website": "https://ix.equinix.com", "social_media": [{"service": "website", "identifier": "https://ix.equinix.com"}], "url_stats": "", "tech_email": "support@equinix.com", "tech_phone": "", "policy_email": "support@equinix.com", "policy_phone": "", "sales_phone": "", "sales_email": "", "net_count": 102, "fac_count": 3, "ixf_net_count": 0, "ixf_last_import": null, "ixf_import_request": null, "ixf_import_request_status": "queued", "service_level": "24/7 Support", "terms": "Recurring Fees", "status_dashboard": null, "created": "2010-07-29T00:00:00Z", "updated": "2021-10-16T21:49:15Z", "status": "ok"}, {"id": 5, "org_id": 2, "name": "Equinix San Jose", "aka": "", "name_long": "Equinix Internet Exchange San Jose (Bay Area)", "city": "San Jose", "country": "US", "region_continent": "North America", "media": "Ethernet", "notes": "", "proto_unicast": true, "proto_multicast": false, "proto_ipv6": true, "website": "https://ix.equinix.com", "social_media": [{"service": "website", "identifier": "https://ix.equinix.com"}], "url_stats": "", "tech_email": "support@equinix.com", "tech_phone": "", "policy_email": "support@equinix.com", "policy_phone": "", "sales_phone": "", "sales_email": "", "net_count": 205, "fac_count": 5, "ixf_net_count": 0, "ixf_last_import": null, "ixf_import_request": null, "ixf_import_request_status": "queued", "service_level": "24/7 Support", "terms": "Recurring Fees", "status_dashboard": null, "created": "2010-07-29T00:00:00Z", "updated": "2021-10-16T21:49:16Z", "status": "ok"}, {"id": 7, "org_id": 2, "name": "Equinix Palo Alto", "aka": "", "name_long": "Equinix Internet Exchange Palo Alto", "city": "Palo Alto", "country": "US", "region_continent": "North America", "media": "Ethernet", "notes": "Formerly PAIX", "proto_unicast": true, "proto_multicast": false, "proto_ipv6": true, "website": "https://ix.equinix.com", "social_media": [{"service": "website", "identifier": "https://ix.equinix.com"}], "url_stats": "", "tech_email": "support@equinix.com", "tech_phone": "", "policy_email": "support@equinix.com", "policy_phone": "", "sales_phone": "", "sales_email": "", "net_count": 81, "fac_count": 3, "ixf_net_count": 0, "ixf_last_import": null, "ixf_import_request": null, "ixf_import_request_status": "queued", "service_level": "24/7 Support", "terms": "Recurring Fees", "status_dashboard": "", "created": "2010-07-29T00:00:00Z", "updated": "2022-08-08T11:19:30Z", "status": "ok"}, {"id": 9, "org_id": 2, "name": "Equinix Atlanta", "aka": "", "name_long": "Equinix Internet Exchange Atlanta", "city": "Atlanta", "country": "US", "region_continent": "North America", "media": "Ethernet", "notes": "", "proto_unicast": true, "proto_multicast": false, "proto_ipv6": true, "website": "https://ix.equinix.com", "social_media": [{"service": "website", "identifier": "https://ix.equinix.com"}], "url_stats": "", "tech_email": "servicesupport@equinix.com", "tech_phone": "+18668118720", "policy_email": "servicesupport@equinix.com", "policy_phone": "", "sales_phone": "", "sales_email": "", "net_count": 30, "fac_count": 3, "ixf_net_count": 0, "ixf_last_import": null, "ixf_import_request": null, "ixf_import_request_status": "queued", "service_level": "24/7 Support", "terms": "Recurring Fees", "status_dashboard": null, "created": "2010-07-29T00:00:00Z", "updated": "2021-10-16T21:49:19Z", "status": "ok"}, {"id": 11, "org_id": 2, "name": "Equinix Seattle", "aka": "", "name_long": "Equinix Internet Exchange Seattle", "city": "Seattle", "country": "US", "region_continent": "North America", "media": "Ethernet", "notes": "", "proto_unicast": true, "proto_multicast": false, "proto_ipv6": true, "website": "https://ix.equinix.com", "social_media": [{"service": "website", "identifier": "https://ix.equinix.com"}], "url_stats": "", "tech_email": "servicesupport@equinix.com", "tech_phone": "", "policy_email": "servicesupport@equinix.com", "policy_phone": "", "sales_phone": "", "sales_email": "", "net_count": 32, "fac_count": 2, "ixf_net_count": 0, "ixf_last_import": null, "ixf_import_request": null, "ixf_import_request_status": "queued", "service_level": "24/7 Support", "terms": "Recurring Fees", "status_dashboard": null, "created": "2010-07-29T00:00:00Z", "updated": "2021-10-16T21:48:46Z", "status": "ok"}, {"id": 12, "org_id": 2, "name": "Equinix New York", "aka": "", "name_long": "Equinix Internet Exchange New York", "city": "New York", "country": "US", "region_continent": "North America", "media": "Ethernet", "notes": "", "proto_unicast": true, "proto_multicast": false, "proto_ipv6": true, "website": "https://ix.equinix.com", "social_media": [{"service": "website", "identifier": "https://ix.equinix.com"}], "url_stats": "", "tech_email": "servicesupport@equinix.com", "tech_phone": "+18668118720", "policy_email": "servicesupport@equinix.com", "policy_phone": "", "sales_phone": "", "sales_email": "", "net_count": 114, "fac_count": 5, "ixf_net_count": 0, "ixf_last_import": null, "ixf_import_request": null, "ixf_import_request_status": "queued", "service_level": "24/7 Support", "terms": "Recurring Fees", "status_dashboard": null, "created": "2010-07-29T00:00:00Z", "updated": "2021-10-16T21:48:48Z", "status": "ok"}, {"id": 48, "org_id": 679, "name": "INEX LAN1", "aka": "", "name_long": "Internet Neutral Exchange Association CLG", "city": "Dublin", "country": "IE", "region_continent": "Europe", "media": "Ethernet", "notes": "", "proto_unicast": true, "proto_multicast": false, "proto_ipv6": true, "website": "https://www.inex.ie/", "social_media": [{"service": "website", "identifier": "https://www.inex.ie/"}], "url_stats": "https://www.inex.ie/ixp/statistics/ixp", "tech_email": "operations@inex.ie", "tech_phone": "+35315313339", "policy_email": "operations@inex.ie", "policy_phone": "+35315313339", "sales_phone": "", "sales_email": "", "net_count": 105, "fac_count": 6, "ixf_net_count": 117, "ixf_last_import": "2024-04-25T00:04:12Z", "ixf_import_request": null, "ixf_import_request_status": "queued", "service_level": "24/7 Support", "terms": "Recurring Fees", "status_dashboard": "", "created": "2010-07-29T00:00:00Z", "updated": "2022-10-11T14:25:15Z", "status": "ok"}, {"id": 387, "org_id": 679, "name": "INEX LAN2", "aka": "", "name_long": "Internet Neutral Exchange Association CLG", "city": "Dublin", "country": "IE", "region_continent": "Europe", "media": "Ethernet", "notes": "", "proto_unicast": true, "proto_multicast": false, "proto_ipv6": true, "website": "https://www.inex.ie/", "social_media": [{"service": "website", "identifier": "https://www.inex.ie/"}], "url_stats": "https://www.inex.ie/ixp/statistics/ixp", "tech_email": "operations@inex.ie", "tech_phone": "+35315313339", "policy_email": "operations@inex.ie", "policy_phone": "+35315313339", "sales_phone": "", "sales_email": "", "net_count": 65, "fac_count": 6, "ixf_net_count": 69, "ixf_last_import": "2024-04-25T00:36:39Z", "ixf_import_request": null, "ixf_import_request_status": "queued", "service_level": "24/7 Support", "terms": "Recurring Fees", "status_dashboard": "", "created": "2010-07-29T00:00:00Z", "updated": "2022-10-11T14:25:36Z", "status": "ok"}, {"id": 1262, "org_id": 679, "name": "INEX Cork", "aka": "", "name_long": "", "city": "Cork", "country": "IE", "region_continent": "Europe", "media": "Ethernet", "notes": "", "proto_unicast": true, "proto_multicast": false, "proto_ipv6": true, "website": "https://www.inex.ie", "social_media": [{"service": "website", "identifier": "https://www.inex.ie"}], "url_stats": "", "tech_email": "operations@inex.ie", "tech_phone": "+35315313339", "policy_email": "operations@inex.ie", "policy_phone": "+35315313339", "sales_phone": "", "sales_email": "", "net_count": 13, "fac_count": 1, "ixf_net_count": 16, "ixf_last_import": "2024-04-25T01:16:38Z", "ixf_import_request": null, "ixf_import_request_status": "queued", "service_level": "24/7 Support", "terms": "Recurring Fees", "status_dashboard": "", "created": "2016-03-31T13:14:35Z", "updated": "2022-10-11T14:26:05Z", "status": "ok"}, {"id": 13, "org_id": 29, "name": "SIX Seattle", "aka": "", "name_long": "Seattle Internet Exchange (MTU 1500)", "city": "Seattle", "country": "US", "region_continent": "North America", "media": "Ethernet", "notes": "SIX port fees:\n\n- 400G: $15k NRC, no MRC\n- 100G: $7.5k NRC, no MRC\n- 10G: $1.5k NRC, no MRC\n- 1G: $100 NRC, no MRC\n- Extension port: $100 NRC, no MRC", "proto_unicast": true, "proto_multicast": false, "proto_ipv6": true, "website": "https://www.seattleix.net/", "social_media": [{"service": "website", "identifier": "https://www.seattleix.net/"}], "url_stats": "https://www.seattleix.net/statistics/", "tech_email": "info@seattleix.net", "tech_phone": "+12063674320", "policy_email": "info@seattleix.net", "policy_phone": "+12063674320", "sales_phone": "", "sales_email": "", "net_count": 369, "fac_count": 15, "ixf_net_count": 448, "ixf_last_import": "2024-04-24T00:00:45Z", "ixf_import_request": "2021-09-16T20:16:27Z", "ixf_import_request_status": "finished", "service_level": "24/7 Support", "terms": "Non-recurring Fees Only", "status_dashboard": "", "created": "2010-07-29T00:00:00Z", "updated": "2023-12-22T17:45:29Z", "status": "ok"}, {"id": 14, "org_id": 32, "name": "NYIIX New York", "aka": "", "name_long": "New York International Internet eXchange", "city": "New York", "country": "US", "region_continent": "North America", "media": "Ethernet", "notes": "For 24 x 7 support, please contact our Customer Assistance Center at cac@telehouse.com. For technical inquiries, please contact at iixeng@telehouse.com", "proto_unicast": true, "proto_multicast": false, "proto_ipv6": true, "website": "https://www.nyiix.net/locations/nyiix-new-york/", "social_media": [{"service": "website", "identifier": "https://www.nyiix.net/locations/nyiix-new-york/"}], "url_stats": "https://www.nyiix.net/mrtg/sum.html", "tech_email": "cac@telehouse.com", "tech_phone": "+17183552525", "policy_email": "iixeng@telehouse.com", "policy_phone": "", "sales_phone": "", "sales_email": "iixsales@telehouse.com", "net_count": 198, "fac_count": 7, "ixf_net_count": 0, "ixf_last_import": null, "ixf_import_request": null, "ixf_import_request_status": "queued", "service_level": "Not Disclosed", "terms": "Not Disclosed", "status_dashboard": "", "created": "2010-07-29T00:00:00Z", "updated": "2023-03-07T18:44:03Z", "status": "ok"}, {"id": 17, "org_id": 2, "name": "Equinix Miami", "aka": "", "name_long": "Equinix Internet Exchange Miami", "city": "Miami", "country": "US", "region_continent": "North America", "media": "Ethernet", "notes": "fka: NAP Of The Americas", "proto_unicast": true, "proto_multicast": false, "proto_ipv6": true, "website": "http://www.equinix.com", "social_media": [{"service": "website", "identifier": "http://www.equinix.com"}], "url_stats": "", "tech_email": "support@equinix.com", "tech_phone": "", "policy_email": "support@equinix.com", "policy_phone": "", "sales_phone": "", "sales_email": "", "net_count": 173, "fac_count": 2, "ixf_net_count": 0, "ixf_last_import": null, "ixf_import_request": null, "ixf_import_request_status": "queued", "service_level": "24/7 Support", "terms": "Recurring Fees", "status_dashboard": null, "created": "2010-07-29T00:00:00Z", "updated": "2021-10-16T21:48:51Z", "status": "ok"}, {"id": 18, "org_id": 791, "name": "LINX LON1", "aka": "", "name_long": "London Internet Exchange Ltd.", "city": "London", "country": "GB", "region_continent": "Europe", "media": "Ethernet", "notes": "used to be Juniper LAN", "proto_unicast": true, "proto_multicast": true, "proto_ipv6": true, "website": "https://www.linx.net/", "social_media": [{"service": "website", "identifier": "https://www.linx.net/"}], "url_stats": "https://portal.linx.net/", "tech_email": "support@linx.net", "tech_phone": "", "policy_email": "info@linx.net", "policy_phone": "", "sales_phone": "", "sales_email": "", "net_count": 824, "fac_count": 18, "ixf_net_count": 933, "ixf_last_import": "2023-12-14T22:20:25Z", "ixf_import_request": "2023-12-14T22:20:10Z", "ixf_import_request_status": "finished", "service_level": "Not Disclosed", "terms": "Not Disclosed", "status_dashboard": null, "created": "2010-07-29T00:00:00Z", "updated": "2020-06-29T07:53:16Z", "status": "ok"}, {"id": 21, "org_id": 9636, "name": "WA-IX", "aka": "IX Australia (Perth WA)", "name_long": "West Australian Internet Exchange", "city": "Perth", "country": "AU", "region_continent": "Australia", "media": "Ethernet", "notes": "Further technical details including policies, communities and connection information can be found at https://internet.asn.au/services/peering/", "proto_unicast": true, "proto_multicast": false, "proto_ipv6": true, "website": "https://internet.asn.au", "social_media": [{"service": "website", "identifier": "https://internet.asn.au"}], "url_stats": "https://metrics.ix.asn.au", "tech_email": "noc@ix.asn.au", "tech_phone": "+61892004980", "policy_email": "peering@ix.asn.au", "policy_phone": "+61892004980", "sales_phone": "", "sales_email": "", "net_count": 70, "fac_count": 5, "ixf_net_count": 89, "ixf_last_import": "2024-04-24T00:02:00Z", "ixf_import_request": null, "ixf_import_request_status": "queued", "service_level": "Normal Business Hours", "terms": "Recurring Fees", "status_dashboard": "", "created": "2010-07-29T00:00:00Z", "updated": "2024-02-02T04:58:01Z", "status": "ok"}, {"id": 22, "org_id": 8592, "name": "Digital Realty Atlanta", "aka": "", "name_long": "Digital Realty Atlanta Internet Exchange", "city": "Atlanta", "country": "US", "region_continent": "North America", "media": "Ethernet", "notes": "fka: Telx Internet Exchange (TIE)\nfka: Atlanta-IX", "proto_unicast": true, "proto_multicast": false, "proto_ipv6": true, "website": "https://ix.digitalrealty.com", "social_media": [{"service": "website", "identifier": "https://ix.digitalrealty.com"}], "url_stats": "", "tech_email": "ix@digitalrealty.com", "tech_phone": "+18888359832", "policy_email": "ix@digitalrealty.com", "policy_phone": "+18888359832", "sales_phone": "", "sales_email": "", "net_count": 155, "fac_count": 2, "ixf_net_count": 0, "ixf_last_import": null, "ixf_import_request": null, "ixf_import_request_status": "queued", "service_level": "Not Disclosed", "terms": "Not Disclosed", "status_dashboard": null, "created": "2010-07-29T00:00:00Z", "updated": "2021-11-01T23:23:30Z", "status": "ok"}, {"id": 23, "org_id": 32, "name": "NYIIX Los Angeles", "aka": "", "name_long": "New York International Internet eXchange - Los Angeles", "city": "Los Angeles", "country": "US", "region_continent": "North America", "media": "Ethernet", "notes": "", "proto_unicast": true, "proto_multicast": false, "proto_ipv6": true, "website": "https://www.nyiix.net/locations/nyiix-la/", "social_media": [{"service": "website", "identifier": "https://www.nyiix.net/locations/nyiix-la/"}], "url_stats": "https://www.nyiix.net/LA-mrtg/sum.html", "tech_email": "iixeng@telehouse.com", "tech_phone": "", "policy_email": "", "policy_phone": "", "sales_phone": "", "sales_email": "iixsales@telehouse.com", "net_count": 31, "fac_count": 2, "ixf_net_count": 0, "ixf_last_import": null, "ixf_import_request": null, "ixf_import_request_status": "queued", "service_level": "Not Disclosed", "terms": "Not Disclosed", "status_dashboard": "", "created": "2010-07-29T00:00:00Z", "updated": "2023-03-07T18:44:33Z", "status": "ok"}, {"id": 24, "org_id": 61, "name": "TorIX", "aka": "", "name_long": "Toronto Internet Exchange Community", "city": "Toronto", "country": "CA", "region_continent": "North America", "media": "Ethernet", "notes": "Free Maple Syrup with every new connection! (please mention Nistor's PeeringDB special)", "proto_unicast": true, "proto_multicast": false, "proto_ipv6": true, "website": "https://www.torix.ca/", "social_media": [{"service": "website", "identifier": "https://www.torix.ca/"}], "url_stats": "https://www.torix.ca/traffic-statistics/", "tech_email": "admin@torix.ca", "tech_phone": "", "policy_email": "peering@torix.ca", "policy_phone": "", "sales_phone": "", "sales_email": "", "net_count": 235, "fac_count": 8, "ixf_net_count": 291, "ixf_last_import": "2024-04-24T00:03:03Z", "ixf_import_request": null, "ixf_import_request_status": "queued", "service_level": "Best Effort (no SLA)", "terms": "Not Disclosed", "status_dashboard": "", "created": "2010-07-29T00:00:00Z", "updated": "2023-06-04T17:32:53Z", "status": "ok"}, {"id": 26, "org_id": 2634, "name": "AMS-IX", "aka": "", "name_long": "Amsterdam Internet Exchange", "city": "Amsterdam", "country": "NL", "region_continent": "Europe", "media": "Ethernet", "notes": "", "proto_unicast": true, "proto_multicast": false, "proto_ipv6": true, "website": "http://www.ams-ix.net/", "social_media": [{"service": "website", "identifier": "http://www.ams-ix.net/"}], "url_stats": "https://stats.ams-ix.net/", "tech_email": "noc@ams-ix.net", "tech_phone": "+31205141717", "policy_email": "info@ams-ix.net", "policy_phone": "+31203058999", "sales_phone": "", "sales_email": "", "net_count": 844, "fac_count": 16, "ixf_net_count": 0, "ixf_last_import": null, "ixf_import_request": null, "ixf_import_request_status": "queued", "service_level": "Not Disclosed", "terms": "Not Disclosed", "status_dashboard": "", "created": "2010-07-29T00:00:00Z", "updated": "2024-04-15T11:32:34Z", "status": "ok"}, {"id": 29, "org_id": 2, "name": "Equinix Zurich", "aka": "", "name_long": "Equinix Internet Exchange Zurich", "city": "Zurich", "country": "CH", "region_continent": "Europe", "media": "Ethernet", "notes": "Formerly TIX", "proto_unicast": true, "proto_multicast": false, "proto_ipv6": true, "website": "https://www.equinix.ch/", "social_media": [{"service": "website", "identifier": "https://www.equinix.ch/"}], "url_stats": "https://ix.equinix.com/", "tech_email": "servicedesk.zh@eu.equinix.com", "tech_phone": "+41435081038", "policy_email": "", "policy_phone": "", "sales_phone": "", "sales_email": "", "net_count": 42, "fac_count": 3, "ixf_net_count": 0, "ixf_last_import": null, "ixf_import_request": null, "ixf_import_request_status": "queued", "service_level": "24/7 Support", "terms": "Recurring Fees", "status_dashboard": null, "created": "2010-07-29T00:00:00Z", "updated": "2021-10-16T21:49:08Z", "status": "ok"}, {"id": 30, "org_id": 5600, "name": "JPIX TOKYO", "aka": "", "name_long": "", "city": "Tokyo", "country": "JP", "region_continent": "Asia Pacific", "media": "Ethernet", "notes": "", "proto_unicast": true, "proto_multicast": false, "proto_ipv6": true, "website": "https://www.jpix.ad.jp/en/", "social_media": [{"service": "website", "identifier": "https://www.jpix.ad.jp/en/"}], "url_stats": "https://www.jpix.ad.jp/en/technical_traffic.php", "tech_email": "support@jpix.ad.jp", "tech_phone": "+81352553858", "policy_email": "sales@jpix.ad.jp", "policy_phone": "", "sales_phone": "", "sales_email": "", "net_count": 262, "fac_count": 9, "ixf_net_count": 0, "ixf_last_import": null, "ixf_import_request": null, "ixf_import_request_status": "queued", "service_level": "Not Disclosed", "terms": "Not Disclosed", "status_dashboard": "", "created": "2010-07-29T00:00:00Z", "updated": "2023-06-30T06:06:44Z", "status": "ok"}], "meta": {}} diff --git a/data/ci/known-good/regenerate.sh b/data/ci/known-good/regenerate.sh new file mode 100755 index 000000000..3e054a211 --- /dev/null +++ b/data/ci/known-good/regenerate.sh @@ -0,0 +1,38 @@ +#! /bin/bash + +KEY="Syy4R8uXTquJNkSav4mmbk5eZWOgoc6FKUJPqOoGHhBjhsC9" +URL="http://127.0.0.1:8080/apiv1" +WGET="/usr/bin/wget" +BIRD="/usr/sbin/bird" + +for skin in none inex; do + + cp ../htaccess-${skin}-skin ../../../public/.htaccess + SKIN="${skin}-" + + for proto in 4 6; do + for vlanid in 1 2; do + + cp ../configs/ci-rs1-conf-vlanid${vlanid}-ipv${proto}.conf ../../../application/configs + + ${WGET} -q \ + ${URL}/router/server-conf/key/${KEY}/target/bird/vlanid/${vlanid}/proto/${proto}/config/ci-rs1-conf-vlanid${vlanid}-ipv${proto} \ + -O ${skin}-ci-rs1-vlanid${vlanid}-ipv${proto}.conf + + rm ../../../application/configs/ci-rs1-conf-vlanid${vlanid}-ipv${proto}.conf + + if [[ $proto -eq 6 ]]; then + BIRDCMD="${BIRD}6" + else + BIRDCMD="${BIRD}" + fi + + $BIRDCMD -p -c ${skin}-ci-rs1-vlanid${vlanid}-ipv${proto}.conf + + if [[ $? -ne 0 ]]; then + echo ERROR: Config check failed for: ${skin}-ci-rs1-vlanid${vlanid}-ipv${proto}.conf + fi + done + done + +done diff --git a/data/grapher/dummy/dummy-10days.png b/data/grapher/dummy/dummy-10days.png new file mode 100644 index 000000000..ca8f137b2 Binary files /dev/null and b/data/grapher/dummy/dummy-10days.png differ diff --git a/data/grapher/dummy/dummy-1year.png b/data/grapher/dummy/dummy-1year.png new file mode 100644 index 000000000..87282a008 Binary files /dev/null and b/data/grapher/dummy/dummy-1year.png differ diff --git a/data/grapher/dummy/dummy-30hours.png b/data/grapher/dummy/dummy-30hours.png new file mode 100644 index 000000000..d2a924be6 Binary files /dev/null and b/data/grapher/dummy/dummy-30hours.png differ diff --git a/data/grapher/dummy/dummy-3hours.png b/data/grapher/dummy/dummy-3hours.png new file mode 100644 index 000000000..7ff729b15 Binary files /dev/null and b/data/grapher/dummy/dummy-3hours.png differ diff --git a/data/grapher/dummy/dummy-day.png b/data/grapher/dummy/dummy-day.png new file mode 100644 index 000000000..8372d7d16 Binary files /dev/null and b/data/grapher/dummy/dummy-day.png differ diff --git a/data/grapher/dummy/dummy-month.png b/data/grapher/dummy/dummy-month.png new file mode 100644 index 000000000..9e66ac595 Binary files /dev/null and b/data/grapher/dummy/dummy-month.png differ diff --git a/data/grapher/dummy/dummy-week.png b/data/grapher/dummy/dummy-week.png new file mode 100644 index 000000000..d229790da Binary files /dev/null and b/data/grapher/dummy/dummy-week.png differ diff --git a/data/grapher/dummy/dummy-year.png b/data/grapher/dummy/dummy-year.png new file mode 100644 index 000000000..13b16a95b Binary files /dev/null and b/data/grapher/dummy/dummy-year.png differ diff --git a/data/grapher/dummy/dummy.log b/data/grapher/dummy/dummy.log new file mode 100644 index 000000000..914b51e5c --- /dev/null +++ b/data/grapher/dummy/dummy.log @@ -0,0 +1,2536 @@ +1455174216 104576781796538774 115938063844157438 +1455174216 4164873681 4166998134 4164873681 4166998134 +1455173905 4115947131 4115905948 4115947131 4115905948 +1455173700 3823592074 3826351097 4115947131 4115905948 +1455173400 4040758115 4041089099 4191732918 4190459053 +1455173100 3590010744 3592373387 4191732918 4190459053 +1455172800 3656305776 3659244334 3772084706 3774783116 +1455172500 3364618806 3362598217 3772084706 3774783116 +1455172200 3431143604 3432494116 3514200544 3517718148 +1455171900 3328354716 3330145876 3514200544 3517718148 +1455171600 3428685999 3429332214 3508122298 3508543943 +1455171300 3411727240 3413857389 3508122298 3508543943 +1455171000 3540600635 3542715910 3615622014 3617427142 +1455170700 3376440187 3377057099 3615622014 3617427142 +1455170400 3356222038 3356286550 3390673259 3390699475 +1455170100 3173249756 3174512758 3390673259 3390699475 +1455169800 3212147561 3215480565 3270007628 3274073310 +1455169500 3343163427 3349022430 3372085488 3378653478 +1455169200 3581579262 3579275525 3678661744 3672246718 +1455168900 3246324955 3245061617 3678661744 3672246718 +1455168600 3329260694 3330743712 3423110618 3425206001 +1455168300 3303489243 3304193643 3423110618 3425206001 +1455168000 3391422371 3391587209 3453729652 3453895127 +1455167700 3334615585 3339010495 3453729652 3453895127 +1455167400 3591988423 3593487808 3732725119 3732120460 +1455167100 3425645675 3425078341 3732725119 3732120460 +1455166800 3273495220 3275734908 3304242175 3303689597 +1455166500 2989778008 2992037961 3259246632 3262780297 +1455166200 3031344404 3035115273 3097342917 3102025539 +1455165900 2931015071 2931650895 3097342917 3102025539 +1455165600 3233744749 3237438129 3400432012 3406215180 +1455165300 3102151693 3099275885 3400432012 3406215180 +1455165000 3302490378 3303692531 3430087799 3434201083 +1455164700 3400281636 3401993136 3430087799 3434201083 +1455164400 3595398106 3598271500 3688096177 3691897467 +1455164100 3616137467 3618024015 3688096177 3691897467 +1455163800 3735090690 3735992146 3800899018 3801686931 +1455163500 3595562995 3596025447 3800899018 3801686931 +1455163200 3532423373 3532811758 3539341784 3539752820 +1455162900 3272400680 3274538157 3539341784 3539752820 +1455162600 3404510163 3405940595 3512262650 3513064487 +1455162300 3381865515 3383553681 3512262650 3513064487 +1455162000 3611346389 3614281911 3740804175 3744160638 +1455161700 3410170016 3409622647 3740804175 3744160638 +1455161400 3871408611 3878956688 4125882881 4137663405 +1455161100 4195268387 4194090895 4221367523 4215315915 +1455160800 4220502987 4219546046 4221367523 4221476300 +1455160500 3988567377 3991834774 4220108490 4221476300 +1455160200 4108495901 4108605174 4203632267 4201968912 +1455159900 4061905471 4063457472 4203632267 4201968912 +1455159600 4293035804 4292423235 4425686533 4423491410 +1455159300 4206050462 4206538071 4425686533 4423491410 +1455159000 4447537789 4449887697 4599686171 4602407592 +1455158700 4289138744 4293073592 4599686171 4602407592 +1455158400 4472886403 4476465908 4617132156 4620318687 +1455158100 4058212935 4059205559 4617132156 4620318687 +1455157800 4304995041 4305907727 4486767961 4487915657 +1455157500 4412619704 4412326025 4486767961 4487915657 +1455157200 4504319782 4507837254 4558473417 4563954981 +1455156900 4520751679 4525793004 4558473417 4563954981 +1455156600 4720109086 4721823484 4817883074 4818158779 +1455156300 4407616316 4406042265 4817883074 4818158779 +1455156000 4293508639 4294026007 4315452989 4317258393 +1455155700 4347209257 4348014521 4359558918 4359975238 +1455155400 4594320594 4595225640 4703112591 4704244119 +1455155100 4502126567 4505919322 4703112591 4704244119 +1455154800 4953782978 4953255339 5191816992 5188883341 +1455154500 4705393932 4706791908 5191816992 5188883341 +1455154200 4957400511 4958663281 5119709867 5120431088 +1455153900 5098291083 5100604549 5119709867 5120431088 +1455153600 5339637492 5339979352 5448170635 5447397628 +1455153300 5352953775 5354863677 5448170635 5447397628 +1455153000 5674036948 5676756525 5829172515 5831812572 +1455152700 5537541998 5537928213 5829172515 5831812572 +1455152400 5731229827 5731570140 5859687808 5860359698 +1455152100 5716492140 5716124461 5859687808 5860359698 +1455151800 6374178475 6375483290 6688504046 6690718684 +1455151500 5983810030 5984787262 6688504046 6690718684 +1455151200 6609047596 6611436060 6975223033 6978393078 +1455150900 6428613002 6431384629 6975223033 6978393078 +1455150600 6968505238 6974947347 7260913715 7268973369 +1455150300 7041589544 7041284318 7260913715 7268973369 +1455150000 7576787321 7581551412 7835603845 7843767431 +1455149700 7595000836 7594523305 7835603845 7843767431 +1455149400 7975693287 7978209787 8176985832 8182155216 +1455149100 7864261807 7865479546 8176985832 8182155216 +1455148800 8155948012 8153709429 8326133910 8323033275 +1455148500 7974763902 7973176469 8326133910 8323033275 +1455148200 8452405214 8453738225 8715061765 8717430838 +1455147900 8442220849 8439876778 8715061765 8717430838 +1455147600 9275137656 9272010167 9694684725 9691988449 +1455147300 8811311815 8810057140 9694684725 9691988449 +1455147000 9356134882 9352593082 9681042435 9676380727 +1455146700 9344178377 9337339136 9681042435 9676380727 +1455146400 9904513244 9900624954 10186234663 10183888876 +1455146100 9792410399 9785337855 10186234663 10183888876 +1455145800 10263407035 10257968887 10519432111 10515371778 +1455145500 10044308764 10038502281 10519432111 10515371778 +1455145200 10290231778 10285778181 10465601211 10461981547 +1455144900 10132278274 10125389818 10465601211 10461981547 +1455144600 10564435302 10555947607 10801591889 10792928228 +1455144300 10518855038 10512785908 10801591889 10792928228 +1455144000 11443292133 11446302764 11898431613 11905033845 +1455143700 10831588248 10824239604 11898431613 11905033845 +1455143400 11413115874 11405826011 11799243251 11793612580 +1455143100 11329813094 11321937405 11799243251 11793612580 +1455142800 11733242080 11725805559 11989782148 11982913384 +1455142500 11479927275 11471917664 11989782148 11982913384 +1455142200 11837768178 11829076044 12084699431 12075893453 +1455141900 11562414842 11553967933 12084699431 12075893453 +1455141600 11886860202 11876218630 12117258735 12105586708 +1455141300 11565746083 11562765857 12117258735 12105586708 +1455141000 11967071707 11957514404 12240579468 12226598388 +1455140700 11731202563 11719409319 12240579468 12226598388 +1455140400 12473218553 12458617362 12886164303 12869957139 +1455140100 11456870124 11456138763 12886164303 12869957139 +1455139800 11885151362 11881613020 12228273524 12221804399 +1455139500 11617516750 11616440351 12228273524 12221804399 +1455139200 11937810364 11931887876 12173969292 12165082955 +1455138900 11683119242 11677054239 12173969292 12165082955 +1455138600 12116150121 12107419157 12387982768 12377611078 +1455138300 11631822709 11621512438 12387982768 12377611078 +1455138000 11684132929 11674208591 11830972828 11821209144 +1455137700 11234074347 11225946928 11830972828 11821209144 +1455137400 11603952934 11599146877 11861125577 11857502159 +1455137100 11339558587 11331904952 11861125577 11857502159 +1455136800 11827911110 11809982805 12115267588 12093634166 +1455136500 10814278283 10817311019 12115267588 12093634166 +1455136200 11231899887 11230771217 11538104461 11532704642 +1455135900 10969408019 10961951996 11538104461 11532704642 +1455135600 11199136292 11193207814 11380316898 11375337470 +1455135300 10845380481 10840294342 11380316898 11375337470 +1455135000 11209630016 11201442313 11442548571 11433068439 +1455134700 10788238423 10781416124 11442548571 11433068439 +1455134400 10924676395 10920523337 11085120224 11081696919 +1455134100 10526365390 10521888983 11085120224 11081696919 +1455133800 10635498689 10631453270 10767234243 10763532143 +1455133500 10338541518 10333708174 10767234243 10763532143 +1455133200 11108750408 10853832880 11500504987 11140278197 +1455132900 10389700651 10320398276 11500504987 11140278197 +1455132600 10750683458 10753356098 11038339153 11039325708 +1455132300 10414445151 10409349925 11038339153 11039325708 +1455132000 10745683463 10745013751 10992539305 10994805220 +1455131700 10456648254 10456280800 10992539305 10994805220 +1455131400 10723217012 10720312653 10930280021 10926691691 +1455131100 10368956658 10367439541 10930280021 10926691691 +1455130800 10523226606 10525701077 10684850228 10688745617 +1455130500 10109394814 10106104004 10684850228 10688745617 +1455130200 10839643710 10836778651 11264974764 11263515405 +1455129900 9899601672 9894918250 11264974764 11263515405 +1455129600 10678341734 10661365124 11193119460 11171107691 +1455129300 10015558022 10017882275 11193119460 11171107691 +1455129000 10343552354 10344706197 10628731704 10626511468 +1455128700 10122249463 10122516978 10628731704 10626511468 +1455128400 10541401544 10541513881 10819597624 10819212154 +1455128100 10247547229 10242862509 10819597624 10819212154 +1455127800 10532428521 10532328817 10757091457 10759778232 +1455127500 10077346515 10079561166 10757091457 10759778232 +1455127200 10024334411 10029281629 10113977696 10120212098 +1455126900 9598805111 9601290932 10113977696 10120212098 +1455126600 9947793099 9948415222 10191645614 10192063910 +1455126300 9844931622 9847355085 10191645614 10192063910 +1455126000 10570051808 10553869913 10942557714 10917946521 +1455125700 9818802881 9830602694 10942557714 10917946521 +1455125400 10154817486 10160274262 10431185028 10429665757 +1455125100 10101135056 10101348554 10431185028 10429665757 +1455124800 10490865376 10498863589 10714611107 10725715050 +1455124500 10204064239 10208543241 10714611107 10725715050 +1455124200 10534194360 10534961058 10763735477 10763943990 +1455123900 10221326834 10221078608 10763735477 10763943990 +1455123600 10514596228 10517454274 10729637627 10733921718 +1455123300 10139443005 10142293828 10729637627 10733921718 +1455123000 10345092675 10350361145 10525206631 10531734602 +1455122700 10055961910 10057953600 10525206631 10531734602 +1455122400 10659785401 10648254024 10992948154 10976340235 +1455122100 9897287463 9911197205 10992948154 10976340235 +1455121800 10355258607 10359574188 10676354058 10673081161 +1455121500 10086135126 10089703443 10676354058 10673081161 +1455121200 10460060563 10464277464 10706775597 10710234556 +1455120900 10129317710 10131643044 10706775597 10710234556 +1455120600 10367769442 10372705967 10552433843 10558622469 +1455120300 9958183750 9963472859 10552433843 10558622469 +1455120000 10100470654 10102377111 10246200928 10246834455 +1455119700 9818625547 9820851351 10246200928 10246834455 +1455119400 10126711948 10132117512 10314979168 10321451024 +1455119100 9985769793 9989176548 10314979168 10321451024 +1455118800 10397557015 10389230949 10611150361 10598557153 +1455118500 9541638059 9551537822 10611150361 10598557153 +1455118200 9952616795 9957439173 10264988733 10264843685 +1455117900 9646328444 9650092366 10264988733 10264843685 +1455117600 9907844041 9914268629 10131648780 10138597012 +1455117300 9778725224 9783034064 10131648780 10138597012 +1455117000 10111280683 10115052978 10317089337 10321063437 +1455116700 9885169425 9887397941 10317089337 10321063437 +1455116400 10114405976 10119326410 10290387962 10296812801 +1455116100 9840379807 9839113120 10290387962 10296812801 +1455115800 10072751054 10080092720 10253198177 10265707181 +1455115500 9766253501 9771959400 10253198177 10265707181 +1455115200 10476514317 10470741576 10884192999 10874350115 +1455114900 9641697831 9647848888 10884192999 10874350115 +1455114600 10093800400 10105981397 10436932881 10449938097 +1455114300 9861473894 9863595707 10436932881 10449938097 +1455114000 10098661336 10097553984 10294606732 10293846053 +1455113700 9746097688 9749819046 10294606732 10293846053 +1455113400 9975702862 9979800120 10166991378 10170521667 +1455113100 9548703450 9553148348 10166991378 10170521667 +1455112800 9691043318 9693003856 9855149072 9855861134 +1455112500 9455131740 9455543319 9855149072 9855861134 +1455112200 9680837336 9686845878 9844624636 9853119345 +1455111900 9446623780 9448572273 9844624636 9853119345 +1455111600 10160201200 10155613217 10546929892 10540493018 +1455111300 9216007714 9219707964 10546929892 10540493018 +1455111000 9556468015 9562383010 9870031537 9875752134 +1455110700 9312715893 9316677961 9870031537 9875752134 +1455110400 9564319235 9571869164 9774731603 9784200715 +1455110100 9188405848 9192006442 9774731603 9784200715 +1455109800 9354126215 9355139572 9524974160 9525819522 +1455109500 9177766138 9176107473 9524974160 9525819522 +1455109200 9414746835 9418718941 9579892318 9586817354 +1455108900 9154352054 9158026090 9579892318 9586817354 +1455108600 9890642840 9894516734 10294566117 10299088463 +1455108300 9060430057 9061035324 10294566117 10299088463 +1455108000 9800958109 9807408967 10277906255 10287392751 +1455107700 9108510395 9110874689 10277906255 10287392751 +1455107400 9446058005 9450181059 9732850169 9738588532 +1455107100 9330649269 9332495920 9732850169 9738588532 +1455106800 9720922383 9723238895 9960458401 9963630210 +1455106500 9394014223 9396885012 9960458401 9963630210 +1455106200 9647858968 9651162500 9859244426 9862794108 +1455105900 9640211671 9646032521 9859244426 9862794108 +1455105600 9942502094 9947470385 10118035181 10122224617 +1455105300 9593203268 9592195422 10118035181 10122224617 +1455105000 9700507664 9699476457 9835286884 9835110029 +1455104700 9507347187 9514746103 9835286884 9835110029 +1455104400 10090477698 10100038980 10413802866 10423028633 +1455104100 9247037132 9244962141 10413802866 10423028633 +1455103800 9571572126 9500317011 9842538236 9742417473 +1455103500 9415810924 9276532222 9842538236 9742417473 +1455103200 9758900448 9712225836 9985156255 9986703814 +1455102900 9617171004 9624561228 9985156255 9986703814 +1455102600 10017973378 10022275208 10260231120 10262158039 +1455102300 9455010432 9452846611 10260231120 10262158039 +1455102000 9519083027 9524425434 9681389503 9690732559 +1455101700 9333444597 9338674466 9681389503 9690732559 +1455101400 9734452087 9740837347 9966038749 9973600719 +1455101100 9450096115 9454831965 9966038749 9973600719 +1455100800 10107614432 10115041774 10496203351 10505343925 +1455100500 9553903876 9551408328 10496203351 10505343925 +1455100200 9791312813 9789877953 10008259759 10008663309 +1455099900 9381627822 9382186494 10008259759 10008663309 +1455099600 9773733986 9778879860 10055794340 10062974917 +1455099300 9409814410 9419577588 10055794340 10062974917 +1455099000 9922249063 9919827248 10261639728 10253307053 +1455098700 9334929980 9340723185 10261639728 10253307053 +1455098400 9340909826 9341883837 9505380462 9501689983 +1455098100 9145871572 9147171396 9505380462 9501689983 +1455097800 9490053102 9490704039 9706464344 9705966206 +1455097500 9111547814 9116182446 9706464344 9705966206 +1455097200 9527563133 9525821082 9822965776 9817403145 +1455096900 9047246418 9061986299 9822965776 9817403145 +1455096600 9844414144 9857377932 10293350698 10303140364 +1455096300 9518029321 9517715619 10293350698 10303140364 +1455096000 9204692375 9205802180 9221477827 9217299704 +1455095700 8519050162 8521142643 9197151086 9200636626 +1455095400 8708435248 8713898539 8910047961 8917265061 +1455095100 8452099451 8459311925 8910047961 8917265061 +1455094800 8423864075 8423904664 8491190555 8488009800 +1455094500 7829320730 7829523488 8491190555 8488009800 +1455094200 7849087801 7854326500 7973626235 7980572352 +1455093900 7584375844 7584515658 7973626235 7980572352 +1455093600 7791784751 7789391367 7958050534 7955709840 +1455093300 7045945650 7049993622 7958050534 7955709840 +1455093000 7122918232 7129850880 7270845250 7278280008 +1455092700 6803331186 6803748138 7270845250 7278280008 +1455092400 6966402634 6971568116 7122410780 7130967910 +1455092100 6643870318 6643271081 7122410780 7130967910 +1455091800 6588783119 6590783733 6644904018 6649620048 +1455091500 6040059141 6043202950 6644904018 6649620048 +1455091200 6088107718 6093129026 6211909604 6218040121 +1455090900 5767264054 5769745608 6211909604 6218040121 +1455090600 5629538777 5630342794 5642804404 5643471397 +1455090300 5250533613 5252216197 5642804404 5643471397 +1455090000 5515177112 5516296815 5708510571 5709186400 +1455089700 4965155833 4968048361 5708510571 5709186400 +1455089400 4894161634 4897822601 4949286921 4953025909 +1455089100 4714047374 4716165891 4949286921 4953025909 +1455088800 4836459584 4837079144 4928285468 4928517768 +1455088500 4571523156 4574237067 4928285468 4928517768 +1455088200 4717447028 4721991625 4841345759 4846287211 +1455087900 4580140344 4581558793 4841345759 4846287211 +1455087600 4230826763 4230728463 4481888766 4481982049 +1455087300 3760221516 3761321070 4118030792 4117846418 +1455087000 3805050660 3806283799 3887707082 3888775911 +1455086700 3514714559 3516669146 3887707082 3888775911 +1455086400 3725126221 3727481293 3879231031 3881637291 +1455086100 3416089093 3418941674 3879231031 3881637291 +1455085800 3611453977 3613142962 3760208823 3761309404 +1455085500 3532863064 3532632586 3760208823 3761309404 +1455085200 3687429551 3689300508 3798303340 3801369405 +1455084900 3523218187 3526241058 3798303340 3801369405 +1455084600 3634412706 3637530072 3736340345 3739509414 +1455084300 3568593634 3568071392 3736340345 3739509414 +1455084000 3536633223 3536431712 3552555249 3553178656 +1455083700 3215212767 3215870789 3552555249 3553178656 +1455083400 3205396641 3206906614 3262652369 3264550806 +1455083100 3103590242 3105600929 3262652369 3264550806 +1455082800 3378893611 3383506091 3535615063 3541412686 +1455082500 3255283088 3255324817 3535615063 3541412686 +1455082200 3379203799 3381255153 3473271735 3477006713 +1455081900 3290323596 3290464775 3473271735 3477006713 +1455081600 3445275873 3446026574 3550601035 3552292618 +1455081300 3306187399 3306630595 3550601035 3552292618 +1455081000 3362015324 3364987597 3432665931 3437038932 +1455080700 3229218649 3231882795 3432665931 3437038932 +1455080400 3345823339 3348567899 3435133870 3438218367 +1455080100 3321897209 3322964565 3435133870 3438218367 +1455079800 3257886489 3260693669 3277860731 3278143643 +1455079500 3151659234 3152246466 3248912555 3252853827 +1455079200 3463489683 3466771456 3625234702 3630359671 +1455078900 3332106503 3333387287 3625234702 3630359671 +1455078600 3445351310 3444989966 3531237018 3530597007 +1455078300 3385159572 3387562288 3531237018 3530597007 +1455078000 3422455810 3426193723 3464314426 3468129341 +1455077700 3336315928 3336628804 3464314426 3468129341 +1455077400 3550539598 3553139288 3668415928 3672634848 +1455077100 3413838245 3412620367 3668415928 3672634848 +1455076800 3435558090 3435422433 3489999311 3491312870 +1455076500 3376309614 3377656993 3489999311 3491312870 +1455076200 3638348615 3638603925 3775613021 3775371880 +1455075900 3653377441 3657051753 3775613021 3775371880 +1455075600 3892930145 3899378487 4025608723 4032648574 +1455075300 3722591851 3722658407 4025608723 4032648574 +1455075000 3874780550 3875653748 3981722783 3983826974 +1455074700 3782863060 3782783316 3981722783 3983826974 +1455074400 3816323771 3818403129 3863655811 3867047433 +1455074100 3732669490 3732476408 3863655811 3867047433 +1455073800 3959452412 3960579253 4083476227 4085801868 +1455073500 3775357650 3776237911 4083476227 4085801868 +1455073200 3744954116 3746200512 3783364590 3785019741 +1455072900 3641910525 3642772570 3783364590 3785019741 +1455072600 3795045808 3800309309 3888153560 3895530816 +1455072300 3941452686 3941008574 3961838988 3958403293 +1455072000 4146847460 4148135968 4231268803 4234713014 +1455071700 3840412370 3839941151 4231268803 4234713014 +1455071400 3977539594 3976316679 4076725128 4075613579 +1455071100 3979106279 3981635722 4076725128 4075613579 +1455070800 4148474199 4153995261 4236015633 4242261019 +1455070500 4132416181 4133687825 4236015633 4242261019 +1455070200 4351554490 4354559439 4456187959 4460627080 +1455069900 4217481117 4215845482 4456187959 4460627080 +1455069600 4357240735 4355910197 4451567908 4451266342 +1455069300 4402755279 4404067907 4451567908 4451266342 +1455069000 4734014433 4736758168 4878637261 4881739619 +1455068700 4687262417 4689422966 4878637261 4881739619 +1455068400 5161582670 5168521532 5383261229 5392289307 +1455068100 4955260040 4955722176 5383261229 5392289307 +1455067800 5286419567 5287266661 5477299605 5479205695 +1455067500 5354565193 5357760634 5477299605 5479205695 +1455067200 5831192218 5833659476 6062086540 6064020787 +1455066900 5801911055 5803267099 6062086540 6064020787 +1455066600 6105492564 6113567718 6287341648 6298536555 +1455066300 6042459144 6039871101 6287341648 6298536555 +1455066000 6138819835 6135444281 6222182654 6220751883 +1455065700 5951428383 5952563598 6222182654 6220751883 +1455065400 6832214576 6836350147 7287579486 7292658184 +1455065100 6113170213 6114016969 7287579486 7292658184 +1455064800 6910223029 6910318103 7386767481 7386951667 +1455064500 6735085630 6735767743 7386767481 7386951667 +1455064200 7251998741 7255451987 7541640582 7546207982 +1455063900 7328109463 7331249641 7541640582 7546207982 +1455063600 7732103397 7733757697 7930404096 7931646178 +1455063300 7728919531 7727587484 7930404096 7931646178 +1455063000 8361384335 8366892974 8663308064 8672142780 +1455062700 8423764949 8418803701 8663308064 8672142780 +1455062400 8622577613 8618465998 8742555180 8740882090 +1455062100 8504209059 8498644476 8742555180 8740882090 +1455061800 8931665742 8924291072 9152006314 9144462358 +1455061500 8913126563 8908211603 9152006314 9144462358 +1455061200 9772957068 9762665149 10192345831 10179245556 +1455060900 9224803068 9218181654 10192345831 10179245556 +1455060600 9759736358 9758446276 10115620043 10115951442 +1455060300 9783437929 9779687182 10115620043 10115951442 +1455060000 10324707374 10316275383 10629671336 10619815715 +1455059700 10132622343 10125337796 10629671336 10619815715 +1455059400 10582661660 10578395846 10870267454 10866916062 +1455059100 10382538164 10376145731 10870267454 10866916062 +1455058800 10729855220 10722909069 10968319289 10961638282 +1455058500 10569634221 10560411295 10968319289 10961638282 +1455058200 11096662243 11089887653 11401954048 11396716244 +1455057900 10980701933 10974643116 11401954048 11396716244 +1455057600 11918775616 11899300923 12410228617 12384862930 +1455057300 11212963988 11207508135 12410228617 12384862930 +1455057000 11753736696 11746925844 12119164628 12109550236 +1455056700 11603130909 11596222333 12119164628 12109550236 +1455056400 12103406640 12092894686 12398231364 12385753420 +1455056100 11714673116 11713002266 12398231364 12385753420 +1455055800 12010385126 11999201110 12253006343 12235846612 +1455055500 11635846718 11626160556 12253006343 12235846612 +1455055200 12020759429 12022363164 12299745573 12305137302 +1455054900 11896179942 11887241845 12299745573 12305137302 +1455054600 12189746199 12176913785 12389838248 12377677861 +1455054300 11614842499 11610516296 12389838248 12377677861 +1455054000 12386347184 12368348496 12856527713 12831178186 +1455053700 11423826494 11420624642 12856527713 12831178186 +1455053400 11929265397 11928730460 12320632497 12318756096 +1455053100 11658610231 11650547127 12320632497 12318756096 +1455052800 12162541598 12153879306 12500822996 12492937016 +1455052500 12026119005 12019133546 12500822996 12492937016 +1455052200 12398682388 12389255500 12642447785 12631791212 +1455051900 11834004044 11829097011 12642447785 12631791212 +1455051600 11978725443 11970797839 12177239734 12167019542 +1455051300 11642313851 11637324298 12177239734 12167019542 +1455051000 12045609089 12038848663 12317198693 12308758713 +1455050700 11603584105 11598984821 12317198693 12308758713 +1455050400 12400408984 12378581244 12878998987 12848781873 +1455050100 11428972323 11439004117 12878998987 12848781873 +1455049800 11828696963 11825324596 12155367523 12141938133 +1455049500 11556622632 11552087927 12155367523 12141938133 +1455049200 11930168980 11924547458 12191693029 12184160187 +1455048900 11555808664 11548409109 12191693029 12184160187 +1455048600 11772922960 11769600239 11969859327 11968290223 +1455048300 11313929943 11310481472 11969859327 11968290223 +1455048000 11317501889 11311634261 11428294336 11421686378 +1455047700 10859128214 10852207848 11428294336 11421686378 +1455047400 11078155989 11071147806 11269727317 11262732268 +1455047100 10807432388 10801863267 11269727317 11262732268 +1455046800 11509411846 11491276000 11902918752 11878896015 +1455046500 10459734161 10460625645 11902918752 11878896015 +1455046200 10667452271 10670838556 10918910501 10920645317 +1455045900 10369538029 10364409271 10918910501 10920645317 +1455045600 10724909610 10718832758 10977409675 10972066767 +1455045300 10336901917 10332376129 10977409675 10972066767 +1455045000 10673722859 10670980051 10935114761 10933032585 +1455044700 10360985494 10358459923 10935114761 10933032585 +1455044400 10689375581 10691995273 10935573052 10940580578 +1455044100 10456058321 10450919642 10935573052 10940580578 +1455043800 11027884342 11021136728 11372506084 11366795150 +1455043500 10287521135 10284138698 11372506084 11366795150 +1455043200 11072413006 11051947263 11578979378 11550508168 +1455042900 10157603025 10162751928 11578979378 11550508168 +1455042600 10567835872 10569513837 10911790728 10908133113 +1455042300 10178574443 10176204124 10911790728 10908133113 +1455042000 10426726287 10427292962 10661624050 10663317304 +1455041700 10032304810 10030031625 10661624050 10663317304 +1455041400 10255898335 10256188089 10464497284 10466620106 +1455041100 9810270423 9808446731 10464497284 10466620106 +1455040800 10028002161 10028587929 10238247744 10240594202 +1455040500 9658477768 9654906367 10238247744 10240594202 +1455040200 9993720812 9991526041 10240677826 10240084914 +1455039900 9818715584 9819146568 10240677826 10240084914 +1455039600 10554849025 10534363321 10972028721 10941665394 +1455039300 9897179908 9911576348 10972028721 10941665394 +1455039000 10293850345 10301409390 10595676578 10594948740 +1455038700 10112258762 10113467251 10595676578 10594948740 +1455038400 10462683206 10464722929 10712195035 10714271006 +1455038100 10160126024 10161162155 10712195035 10714271006 +1455037800 10550628142 10553198189 10820940125 10824378011 +1455037500 10316264898 10319749275 10820940125 10824378011 +1455037200 10591772962 10591020931 10794803839 10792170534 +1455036900 10287504504 10293883015 10794803839 10792170534 +1455036600 10650318919 10652085174 10899052780 10897223923 +1455036300 10235243710 10236669247 10899052780 10897223923 +1455036000 10857527494 10841378186 11255418284 11230690804 +1455035700 9766521942 9783682753 11255418284 11230690804 +1455035400 10164888655 10177828568 10507626591 10514062925 +1455035100 9951872352 9956750298 10507626591 10514062925 +1455034800 10207389115 10211258568 10419373688 10423050437 +1455034500 9867724996 9870466573 10419373688 10423050437 +1455034200 10072460560 10074080048 10254795912 10256074678 +1455033900 9715715789 9717688226 10254795912 10256074678 +1455033600 10047509752 10051702703 10283952946 10289012640 +1455033300 9729315835 9730245307 10283952946 10289012640 +1455033000 10053035280 10060085539 10292204510 10302702668 +1455032700 9701375936 9700716124 10292204510 10302702668 +1455032400 10451563035 10438177619 10886781800 10869533208 +1455032100 9381006717 9388535636 10886781800 10869533208 +1455031800 9754920883 9769378028 10064906640 10079958518 +1455031500 9493099302 9497027999 10064906640 10079958518 +1455031200 9891840795 9894742529 10172981216 10177281457 +1455030900 9650687602 9654984374 10172981216 10177281457 +1455030600 9872081498 9878014732 10062802519 10069471582 +1455030300 9568984226 9569848281 10062802519 10069471582 +1455030000 9757698093 9760853525 9926108655 9931276093 +1455029700 9468451427 9475090593 9926108655 9931276093 +1455029400 9806568294 9808618821 10040731185 10040431001 +1455029100 9539902159 9540776753 10040731185 10040431001 +1455028800 10203485644 10201815353 10599772267 10596714409 +1455028500 9360186886 9367910370 10599772267 10596714409 +1455028200 9743869137 9746441545 10056403510 10055455299 +1455027900 9623312680 9624098909 10056403510 10055455299 +1455027600 9997651164 10003547573 10235278410 10243146529 +1455027300 9635821324 9641847444 10235278410 10243146529 +1455027000 9797320346 9801529880 9968485851 9972198353 +1455026700 9418680723 9421900023 9968485851 9972198353 +1455026400 9678378813 9680173533 9887968252 9889206290 +1455026100 9448825706 9449185213 9887968252 9889206290 +1455025800 9693370950 9699364130 9871768962 9880358965 +1455025500 9461003881 9465132347 9871768962 9880358965 +1455025200 10115932289 10109213185 10485286751 10474383562 +1455024900 9087246344 9091773662 10485286751 10474383562 +1455024600 9483539540 9489142625 9823857550 9828191242 +1455024300 9244599473 9247209565 9823857550 9828191242 +1455024000 9588733002 9594365305 9841234559 9848515944 +1455023700 9288597912 9290785283 9841234559 9848515944 +1455023400 9636706662 9638801638 9886495340 9889409661 +1455023100 9389167119 9391953288 9886495340 9889409661 +1455022800 9778979670 9787735713 10038158075 10049617891 +1455022500 9440022513 9441124198 10038158075 10049617891 +1455022200 10632221070 10633555551 11288965490 11292213398 +1455021900 8893314563 8900023561 11288965490 11292213398 +1455021600 9750132838 9745768766 10346139697 10336564370 +1455021300 9051779596 9057998975 10346139697 10336564370 +1455021000 9493779562 9496102006 9851702331 9850350721 +1455020700 9312702350 9316203347 9851702331 9850350721 +1455020400 9734282033 9737422581 10016310583 10018455305 +1455020100 9602668201 9608224365 10016310583 10018455305 +1455019800 9771434025 9775179886 9918337755 9920684061 +1455019500 9304485033 9304315956 9918337755 9920684061 +1455019200 9465201781 9467645130 9648486136 9652585975 +1455018900 9176596056 9180611333 9648486136 9652585975 +1455018600 9492799834 9497131595 9722976167 9727469580 +1455018300 9148460291 9152875638 9722976167 9727469580 +1455018000 9690772846 9682685219 10045625834 10031758215 +1455017700 8778518190 8786712237 10045625834 10031758215 +1455017400 9113049250 9118116336 9399518602 9400772499 +1455017100 8948723173 8950520695 9399518602 9400772499 +1455016800 9134414283 9138501018 9291546905 9296555733 +1455016500 8935604509 8939239099 9291546905 9296555733 +1455016200 9180238395 9182545268 9350298319 9352240919 +1455015900 8738181295 8739416878 9350298319 9352240919 +1455015600 8834583188 8836596582 8977360704 8979833794 +1455015300 8526668049 8527817726 8977360704 8979833794 +1455015000 8863575888 8867543182 9091104402 9096561230 +1455014700 8638142383 8642267427 9091104402 9096561230 +1455014400 9261925102 9257909962 9631283005 9623731643 +1455014100 8473141729 8478879325 9631283005 9623731643 +1455013800 8718356749 8725685067 8964072493 8970560169 +1455013500 8449498096 8450046646 8964072493 8970560169 +1455013200 8796826326 8802121169 9039832063 9048262973 +1455012900 8633513695 8639437348 9039832063 9048262973 +1455012600 8897857601 8898036280 9086443631 9084472088 +1455012300 8616578817 8619737816 9086443631 9084472088 +1455012000 8655340470 8657288756 8756212745 8756705799 +1455011700 8277818930 8283198245 8756212745 8756705799 +1455011400 8481222769 8481125715 8656191207 8652780037 +1455011100 8254813578 8256860126 8656191207 8652780037 +1455010800 8715177698 8719639451 8992135931 8996729213 +1455010500 8084039401 8087055069 8992135931 8996729213 +1455010200 8366580949 8367486341 8610121800 8610263367 +1455009900 8079733270 8081675703 8610121800 8610263367 +1455009600 8246738203 8253535387 8421303447 8430020457 +1455009300 8062970113 8065728327 8421303447 8430020457 +1455009000 8119441984 8119650547 8209854715 8209974823 +1455008700 7456870081 7456752364 8209854715 8209974823 +1455008400 7304034255 7305395851 7366674995 7368764280 +1455008100 6893983356 6896274510 7366674995 7368764280 +1455007800 6862967845 6867179353 6933781780 6938846828 +1455007500 6551305052 6552726683 6933781780 6938846828 +1455007200 6874862427 6865472060 7094877378 7081144085 +1455006900 6275847373 6283777604 7094877378 7081144085 +1455006600 6389151123 6394103725 6546639790 6547435717 +1455006300 6203065506 6204017004 6546639790 6547435717 +1455006000 6209788889 6213389467 6271850256 6276614265 +1455005700 5890465203 5892815848 6271850256 6276614265 +1455005400 5964861420 5968722154 6065817679 6070798889 +1455005100 5709025699 5712507756 6065817679 6070798889 +1455004800 5587694863 5589242832 5593389717 5594331642 +1455004500 5278769232 5281377964 5593389717 5594331642 +1455004200 5384966747 5386793784 5484282959 5485486935 +1455003900 5193525260 5195609694 5484282959 5485486935 +1455003600 5354294753 5354431020 5480334711 5479412108 +1455003300 4778586567 4784276590 5480334711 5479412108 +1455003000 4806370095 4807578451 4905691054 4904007955 +1455002700 4636181219 4637786269 4905691054 4904007955 +1455002400 4588853342 4589714994 4613515741 4613473795 +1455002100 4157106117 4159864788 4613515741 4613473795 +1455001800 4140906303 4141245414 4212956714 4211686384 +1455001500 3884694566 3887419839 4212956714 4211686384 +1455001200 3828165407 3829080228 3860153682 3859521290 +1455000900 3647093830 3647859484 3860153682 3859521290 +1455000600 4286226723 4290887640 4620175279 4626393514 +1455000300 3549831616 3549529731 4620175279 4626393514 +1455000000 3715696242 3717243001 3887321264 3890307973 +1454999700 3570222383 3570731177 3887321264 3890307973 +1454999400 3663244188 3665507781 3747798800 3751199472 +1454999100 3422663735 3425504355 3747798800 3751199472 +1454998800 3493442613 3495409727 3584837383 3586500632 +1454998500 3374179032 3374999913 3584837383 3586500632 +1454998200 3444930376 3445947470 3515056938 3516314269 +1454997900 3390657207 3392452148 3515056938 3516314269 +1454997600 3484405877 3487192862 3549900329 3553051750 +1454997300 3472557771 3474110404 3549900329 3553051750 +1454997000 3603918119 3604672816 3677811993 3678491007 +1454996700 3568361358 3571406793 3677811993 3678491007 +1454996400 3862603480 3867641524 4018684429 4024219408 +1454996100 3605626295 3604250581 4018684429 4024219408 +1454995800 3691712914 3695492857 3780323397 3787229183 +1454995500 3529909612 3533083137 3780323397 3787229183 +1454995200 3591378033 3592073995 3660227986 3660449049 +1454994900 3454118351 3453282900 3660227986 3660449049 +1454994600 3569419610 3571583751 3655470934 3659158278 +1454994300 3374691070 3377018387 3655470934 3659158278 +1454994000 3362118180 3363901191 3762331701 3773011426 +1454992200 3486909010 3487836160 3712367445 3713410354 +1454990400 3704707795 3706739330 4175536776 4185963336 +1454988600 3913185739 3914743245 4208128790 4205797086 +1454986800 4028951095 4030535450 4405887709 4410553254 +1454985000 4274908834 4276584378 4720302112 4723687346 +1454983200 4843753308 4845419883 5543307538 5543973018 +1454981400 5534065849 5535624594 5876851001 5879230881 +1454979600 6062268075 6062676935 7080407823 7082323257 +1454977800 6881832373 6883461372 7538877509 7540333841 +1454976000 7964550706 7965206112 9075937811 9051535704 +1454974200 9231688255 9234129727 10076540832 10077438278 +1454972400 10886982700 10882952278 12151746375 12150423019 +1454970600 11686005090 11679099086 12216583783 12208989992 +1454968800 11925995617 11919957150 12937568997 12921097418 +1454967000 11904565915 11898933742 12460650427 12449442490 +1454965200 11637262375 11632666914 12638492169 12620673961 +1454963400 11592858853 11589885131 12240583434 12236778538 +1454961600 11282461718 11278288296 12187810813 12160835588 +1454959800 10948814407 10947522980 11484157023 11480916539 +1454958000 10746857712 10744389878 11858768228 11860592936 +1454956200 10400633843 10402960215 10864530619 10863928804 +1454954400 10315825996 10317169018 11383181561 11360033439 +1454952600 10542680680 10546085185 10972625246 10973123974 +1454950800 10786280891 10742548558 11935236716 11610841306 +1454949000 10486106998 10491288333 11025441629 11030592806 +1454947200 10129481777 10131456066 11150979788 11129605189 +1454945400 10027008753 10030633667 10442064515 10439618894 +1454943600 9982625630 9984959118 10954068429 10939793923 +1454941800 9956010511 9959780451 10394344231 10399116338 +1454940000 9757412867 9759431631 10619159963 10605279002 +1454938200 9916150896 9919126899 10587645467 10585849090 +1454936400 10067296060 10069756053 10988263062 10981934304 +1454934600 9837932337 9840419612 10294241882 10293189718 +1454932800 9696066446 9698084228 10481490522 10470365779 +1454931000 9517955748 9521279756 10059347806 10053188057 +1454929200 9377293050 9378332713 10067454354 10056194424 +1454927400 9293773725 9298041748 9668940634 9675282406 +1454925600 9039959426 9041844852 9779135327 9775924936 +1454923800 8368129215 8371210001 8932088135 8936975998 +1454922000 7264893829 7265608097 7803865432 7798385947 +1454920200 6538899558 6542052743 7020978043 7026299025 +1454918400 5434936957 5436765546 5971532543 5972764223 +1454916600 4529304462 4531286565 5128848905 5131527045 +1454914800 3540094141 3541314650 4057888178 4060678412 +1454913000 3292877558 3294177532 3451765018 3454094650 +1454911200 3421285546 3422165521 3862082394 3864881871 +1454909400 3564339197 3565750612 3777330993 3776041925 +1454907600 3563510214 3564875259 3981323650 3987486673 +1454905800 3852197629 3853998842 4187333919 4188687406 +1454904000 3829351648 3830840995 4108397745 4106166939 +1454902200 3948355474 3950140850 4227547524 4230775964 +1454900400 4094535015 4096726581 4645106485 4656463841 +1454898600 4566257735 4566900875 4995607688 4994429391 +1454896800 4833562551 4834494322 5485096330 5480274821 +1454895000 5740331357 5742184286 6169153432 6170575797 +1454893200 6361533002 6362283234 7379779177 7383095320 +1454891400 7616796600 7618591474 8360447044 8364708235 +1454889600 8479508244 8480405917 9535489032 9534906215 +1454887800 9662741790 9663244271 10496748970 10494642309 +1454886000 10592618290 10590993935 11777909296 11761163051 +1454884200 11420640541 11419154088 12032008345 12033737393 +1454882400 11683843866 11681451789 12930703103 12906737892 +1454880600 11664463552 11666913142 12202437450 12205905519 +1454878800 11293140016 11292344895 12386517573 12357186550 +1454877000 11295897256 11298551944 11861523358 11865725028 +1454875200 11237574991 11239423145 12291802990 12272248571 +1454873400 11039426299 11043040624 11508316131 11513880318 +1454871600 10738656816 10740234757 12335090045 12336430785 +1454869800 10405150040 10408714127 10829633114 10833511911 +1454868000 10371827360 10374636768 11382831468 11373796199 +1454866200 10230931644 10234963472 10705065436 10713025034 +1454864400 10765727393 10770761398 11912686338 11933070230 +1454862600 10861078288 10862148004 11442018468 11444440046 +1454860800 10408157525 10411847489 11345371487 11361240145 +1454859000 10227804349 10228802937 10865272112 10866935879 +1454857200 9542817240 9543920483 10225846261 10214103513 +1454855400 9213174895 9215989875 9741621221 9744616000 +1454853600 9017833255 9019692925 9772467311 9761905026 +1454851800 8931244964 8934570312 9375667411 9376390441 +1454850000 8713845461 8715639860 9464253547 9451974299 +1454848200 8555489506 8557663216 9000240160 9007776450 +1454846400 8769646936 8770857622 9486288350 9465235771 +1454844600 8569195458 8572616141 8994464848 9000452400 +1454842800 8147606170 8148871444 8685887906 8686916805 +1454841000 7856845005 7859618941 8397403245 8398556323 +1454839200 7635867791 7637038967 8189759943 8171186216 +1454837400 6967362710 6970058178 7588600240 7591289607 +1454835600 5965358654 5967051372 6487608524 6493726755 +1454833800 5054417780 5057364525 5820815859 5825265456 +1454832000 3992853633 3993808458 4391111814 4392993100 +1454830200 3620082504 3621954413 3866602487 3865421950 +1454828400 3544956793 3546393941 4166143860 4168573274 +1454826600 3462963981 3464809848 3743379397 3747755277 +1454824800 3382459642 3383233381 3871147628 3871439891 +1454823000 3692301022 3693800711 4001960999 4003185538 +1454821200 3616468633 3617588753 3893078316 3896208964 +1454819400 3832746952 3834105114 4135523124 4139324613 +1454817600 4119346103 4120888797 4668043707 4672664594 +1454815800 4456173699 4457628634 5043696502 5048506325 +1454814000 5110503541 5112008476 5614436562 5618763734 +1454812200 5624833674 5627047756 5993954430 6002730154 +1454810400 5878799988 5878911950 6652873751 6653637127 +1454808600 6863446021 6865768251 7630217160 7639387289 +1454806800 7559643433 7560202024 8477022026 8467230989 +1454805000 8533375548 8535981546 9241775654 9249514638 +1454803200 9097581394 9098060136 10033401650 10013921616 +1454801400 9749829283 9753273361 10361936156 10371667329 +1454799600 10393057241 10395195530 11563156190 11552864507 +1454797800 10828221055 10831145059 11570474780 11575336818 +1454796000 11068370109 11070601092 12122032158 12101123826 +1454794200 10988587717 10992682748 11600445166 11609960109 +1454792400 10663934238 10665234910 11576934201 11561783181 +1454790600 10602101683 10606008354 11149373415 11150006384 +1454788800 10355831602 10357864876 11166936243 11147293906 +1454787000 10189320197 10194532860 10582599948 10589461543 +1454785200 10215467165 10218957392 11052539671 11052972452 +1454783400 10310968419 10313419600 10848628797 10849913770 +1454781600 10139844864 10142418776 11048514137 11040655801 +1454779800 10119989493 10123062982 10519006291 10517422108 +1454778000 9839915465 9842005829 10523146936 10512414217 +1454776200 9871240707 9875881024 10326976126 10326630996 +1454774400 9814215175 9816100669 10557832994 10556792330 +1454772600 9649441222 9652944230 10058365469 10057425854 +1454770800 9244878975 9246521777 9992532333 9985173698 +1454769000 8947230731 8949926765 9342009289 9343780315 +1454767200 8757239438 8759254226 9420327532 9406616280 +1454765400 8617311360 8620645087 9030118717 9034446178 +1454763600 8569624505 8571754597 9637575311 9639720370 +1454761800 8671684825 8674780882 9022350094 9025456408 +1454760000 8437372175 8439337456 9061143670 9047824764 +1454758200 8215869636 8218977751 8599353382 8599386030 +1454756400 7985345322 7986779470 8576895027 8559178890 +1454754600 7768107672 7771072076 8043823223 8044161813 +1454752800 7500553771 7502212409 7931899108 7933920482 +1454751000 7032137958 7035237345 7423675489 7426156641 +1454749200 6276590923 6277511581 6693330334 6695703480 +1454747400 5453961274 5456944717 5901876162 5904051864 +1454745600 4459073004 4459955780 4886405343 4887040954 +1454743800 3844577603 3846331335 4195983487 4199461744 +1454742000 3592203753 3593997087 4053946717 4057255722 +1454740200 3386220345 3387410354 3587913228 3589230222 +1454738400 3451497977 3452821041 4175479773 4178145465 +1454736600 3672267191 3673576725 3989976064 3992426272 +1454734800 3419796982 3421136164 3702172564 3702305970 +1454733000 3731016421 3732734388 3927116138 3930521144 +1454731200 3880720046 3881930104 4256517255 4256820065 +1454729400 4187231122 4188596176 4540000022 4540811586 +1454727600 4596130155 4597714330 5134442215 5139752999 +1454725800 5155388812 5156626793 5481754866 5485124209 +1454724000 5794234409 5795679189 6445663761 6440583747 +1454722200 6722512541 6724616518 7194625354 7199726237 +1454720400 7666406522 7668300518 8674874249 8695740069 +1454718600 8752723047 8753863385 9380177242 9379508460 +1454716800 9501850394 9504448905 10620064807 10626714030 +1454715000 10222701285 10224471562 10945086565 10943018904 +1454713200 10833221097 10835134885 11959657503 11951378963 +1454711400 11187582181 11192134215 11715206177 11729011895 +1454709600 11201069737 11201412660 12077032688 12063347244 +1454707800 11451540222 11455480085 11946815009 11952434655 +1454706000 11350440226 11354072055 12209459806 12215258988 +1454704200 11094283991 11096085675 11560353608 11570047980 +1454702400 10598413571 10601156922 11426118977 11413587188 +1454700600 10289847475 10292848517 10678334484 10680452913 +1454698800 10211116742 10213441245 11056316782 11036738272 +1454697000 10171982182 10175935845 10587838551 10588971724 +1454695200 10207918398 10210285986 11035457639 11022440265 +1454693400 10423976739 10427287027 10850949330 10850196787 +1454691600 10636407712 10638839943 11510162593 11490805326 +1454689800 10619237168 10623411404 11046103060 11050477146 +1454688000 10399806121 10401686771 11311974891 11280881315 +1454686200 9893103794 9897357830 10545503647 10546035974 +1454684400 9882233255 9885158052 10762658941 10749603526 +1454682600 9868642460 9872117337 10383965264 10389235427 +1454680800 9633256644 9635273301 10539323936 10539900926 +1454679000 9642322013 9645068881 10156427663 10152283139 +1454677200 9943713321 9946458359 11150372916 11145608780 +1454675400 9577264761 9580653629 10040847769 10044628020 +1454673600 9650152875 9651035435 10518115990 10507661077 +1454671800 9519428131 9521582162 9941194125 9948351754 +1454670000 9254460150 9257401900 10005028963 10002409811 +1454668200 9294316105 9296816997 9837641808 9843003691 +1454666400 8628407909 8630675616 9211580001 9201997509 +1454664600 8141604400 8144346780 8782681355 8784349892 +1454662800 7303913148 7305295191 7885758646 7887608652 +1454661000 6513252131 6515885436 7004780890 7008946861 +1454659200 5538356035 5540361151 6059368615 6062694180 +1454657400 4623215402 4624998189 5125762094 5129796095 +1454655600 3802918064 3805121702 4425201142 4427441858 +1454653800 3683099034 3684012184 3926420206 3927628525 +1454652000 3471069867 3472246050 3716067465 3719942859 +1454650200 3736943235 3738671396 3994272196 3996095877 +1454648400 3625314197 3626760981 4036000506 4034785206 +1454646600 3698654229 3700119792 3970180792 3972685332 +1454644800 4032631047 4034603293 4337648735 4340684296 +1454643000 4471441595 4472785753 4920211822 4927552890 +1454641200 4849321951 4850853115 5360460759 5359939581 +1454639400 4922923117 4924239923 5225269802 5226278230 +1454637600 5128257226 5129905952 5776832774 5777212415 +1454635800 5909698028 5910824589 6365928361 6363678705 +1454634000 6722400066 6723749729 7615949587 7609597018 +1454632200 7872726953 7874297544 8522380200 8522831013 +1454630400 8943240278 8944286427 10058447992 10054631713 +1454628600 10011320555 10009439356 10856465709 10851538783 +1454626800 10957376565 10952007502 12078555148 12064687827 +1454625000 11572455295 11566774623 12195305075 12188973985 +1454623200 11604965885 11600521565 12514687362 12498964481 +1454621400 11477475692 11473872978 12060219496 12055059222 +1454619600 11367540322 11362825374 12159587365 12134359615 +1454617800 11238473357 11236456740 11819618412 11815869608 +1454616000 10826816754 10821955963 11600611349 11562728472 +1454614200 10508956908 10508182902 11008297588 11002376375 +1454612400 10179524847 10178148495 11283071630 11284552624 +1454610600 10211503963 10213816006 10970598899 10967176437 +1454608800 10323648623 10323752173 11149886144 11120075260 +1454607000 10564128646 10567663833 10982377881 10982721094 +1454605200 10805422288 10807074726 11748941970 11720111874 +1454603400 10390834452 10395900569 10904192437 10907380438 +1454601600 10248087028 10250058368 11279631571 11262493746 +1454599800 10049013603 10053682642 10501567930 10498905889 +1454598000 9911746909 9912811908 10864445967 10849289592 +1454596200 10058353693 10062186750 10632506443 10637185047 +1454594400 9762561772 9765479982 10563904330 10557409866 +1454592600 9601196137 9603030056 10008093158 10009224517 +1454590800 9370518159 9372388817 10365108049 10368260482 +1454589000 9279632317 9282455750 9667597526 9662502922 +1454587200 9154913852 9157005818 9832641028 9832579326 +1454585400 9181877932 9184589822 9555353917 9556215906 +1454583600 9176059691 9177818045 10058193913 10047727253 +1454581800 8991359182 8994290530 9531528491 9529034637 +1454580000 8760805351 8762566001 9491415428 9485292548 +1454578200 8156514591 8159554430 8582093507 8586415686 +1454576400 7284611465 7286418929 7788152309 7784076380 +1454574600 6850919946 6853278484 7258874883 7261879877 +1454572800 5749508934 5751161784 6257772363 6257574126 +1454571000 4920858607 4922689452 5563156436 5562143984 +1454569200 4022474937 4024290462 4780530594 4777939560 +1454567400 3793611281 3795280602 3997525483 4002211269 +1454565600 3680203084 3681537563 3988574996 3990043579 +1454563800 3741782209 3743108145 3938250589 3936591347 +1454562000 3487889391 3488909750 3777875634 3774828167 +1454560200 3889644092 3890783050 4231669607 4233526783 +1454558400 3818486621 3819674513 4274138544 4274297913 +1454556600 4158672145 4159636281 4554039476 4553454570 +1454554800 4426308436 4427660410 4767345903 4769062929 +1454553000 4614626782 4615566609 5034739171 5034902737 +1454551200 5047069954 5048228689 5858972299 5852099907 +1454549400 5802316261 5803733646 6202710742 6203032749 +1454547600 6442177325 6442661690 7553996045 7558076067 +1454545800 7624471013 7626644640 8243589823 8246097041 +1454544000 8765465699 8763698320 9896561534 9882830011 +1454542200 9962642751 9958955275 10845174425 10840609523 +1454540400 10853467151 10848263375 12138086023 12110260306 +1454538600 11462042346 11456669211 12183831326 12174293664 +1454536800 11944561821 11939999004 13296555142 13271882315 +1454535000 12138824591 12137163729 12704333686 12698396860 +1454533200 11774761484 11771239944 13218661321 13188329352 +1454531400 11753280751 11738122243 12769372655 12776078519 +1454529600 11331044109 11327615134 12205247238 12183026761 +1454527800 11059606839 11059556853 11520063112 11520168896 +1454526000 10688920767 10687233697 12095315057 12095180991 +1454524200 10474248311 10475688291 10833240793 10834541146 +1454522400 10438235705 10439825148 11272052143 11248644791 +1454520600 10706481726 10711120206 11281037159 11278168381 +1454518800 10885179049 10886438429 11752685887 11738240073 +1454517000 10602015420 10605777390 11114849424 11116888821 +1454515200 10161711595 10163163698 11102745888 11079569502 +1454513400 9947138616 9951152194 10395350868 10401756250 +1454511600 9751736751 9754147211 10713764176 10700238291 +1454509800 9793225487 9796102335 10225242430 10225576403 +1454508000 9760713068 9763957485 10398168162 10398465835 +1454506200 9786515786 9788518068 10446176530 10444379809 +1454504400 9499977478 9502686105 10929648977 10936142069 +1454502600 9528574418 9530462983 10131838046 10131640741 +1454500800 9452501630 9454734981 10035436477 10027897946 +1454499000 9169004529 9172981079 9579531340 9583369252 +1454497200 9126611589 9126914650 9781985432 9770760860 +1454495400 8751631412 8755442308 9202764755 9204363841 +1454493600 8633061942 8634333940 9285544374 9272661859 +1454491800 7921667907 7924184585 8564606250 8568106926 +1454490000 7150057873 7151809921 7731737322 7722312898 +1454488200 6311171110 6313175024 6821616781 6821502407 +1454486400 5415880325 5417613567 5722498430 5723520043 +1454484600 4669635193 4671419764 5207804786 5206374692 +1454482800 3833257183 3834988020 4439472580 4437936990 +1454481000 3659977069 3660614104 3865289359 3864790295 +1454479200 3473129429 3474305576 4015345765 4010838590 +1454477400 3757923365 3759330056 3949971320 3948957045 +1454475600 2988107368 2989102078 3573754390 3576532151 +1454473800 3051789877 3052445985 3322172671 3323181117 +1454472000 3758770019 3760298202 4144222073 4146524415 +1454470200 4120961852 4122299945 4439962906 4438714490 +1454468400 4493003655 4494541891 4995105247 4998386613 +1454466600 4777097719 4778087130 5099949288 5100984288 +1454464800 5048070745 5049106084 5709842251 5704547454 +1454463000 5870635267 5872182040 6233814177 6232856263 +1454461200 6662972137 6663131608 7855663180 7866387023 +1454459400 7727612619 7729523496 8371044021 8376834811 +1454457600 8857736460 8859956438 10108790036 10117403579 +1454455800 10161795352 10160697055 11008743054 11013336845 +1454454000 11184196378 11182061773 12797398912 12796293757 +1454452200 11959393234 11953878985 12586303463 12577460176 +1454450400 12336867208 12333758385 13694311525 13697673743 +1454448600 12549643690 12544997447 13058193211 13050621716 +1454446800 12222895997 12218786110 13063305355 13042299041 +1454445000 11980478938 11976653330 12549938403 12543814596 +1454443200 11560471840 11556359113 12321889806 12298821725 +1454441400 11047508158 11046325227 11487771669 11480915367 +1454439600 11118236830 11113656254 12663349487 12681439675 +1454437800 10814253633 10814433827 11401943349 11406595432 +1454436000 10531671199 10531937295 11536841865 11520941660 +1454434200 10660550960 10663019781 11052555884 11053556396 +1454432400 10654005435 10655643686 11374200411 11352673711 +1454430600 10447917924 10452318964 10899296059 10904655869 +1454428800 10226554182 10228094908 11092049831 11071391150 +1454427000 10230455332 10233581903 10792181896 10795018982 +1454425200 10135280634 10137448111 10875358331 10862887427 +1454423400 9911850728 9915004891 10326349308 10329446335 +1454421600 9862945265 9864867329 10790449758 10775833500 +1454419800 9457486870 9460120276 10093971183 10099016625 +1454418000 9501543307 9503438889 10511697249 10504543512 +1454416200 9386318726 9389036186 9921792084 9928026635 +1454414400 9205947358 9207468692 9971245319 9961015975 +1454412600 9084742863 9087470861 9589964940 9591166703 +1454410800 8848508679 8849723994 9630097324 9611896167 +1454409000 8866248865 8869531462 9401292019 9404558636 +1454407200 8561570214 8564100051 9407272646 9401501764 +1454405400 8100828855 8103100545 8571394098 8573292510 +1454403600 7188410481 7189467717 7721051744 7723844432 +1454401800 6464601594 6467372216 6960402880 6962738300 +1454400000 5313190166 5314420113 5859260957 5862900720 +1454398200 4465179507 4466831035 4860294958 4861821571 +1454396400 3911563220 3913100915 4572819375 4571921095 +1454394600 3666761090 3667568072 3922855138 3923365608 +1454392800 3431907507 3432909361 3728122947 3727896739 +1454391000 3505294349 3506055822 3699255646 3699621864 +1454389200 3232917986 3234423297 3559271653 3567039162 +1454387400 3443595470 3444357626 3803594703 3803700669 +1454385600 3463236440 3464167564 3848986895 3851377997 +1454383800 3772330925 3773142093 4133151364 4133596130 +1454382000 3996944800 3998060474 4419734175 4422551604 +1454380200 4447631185 4448985014 4932656493 4936269916 +1454378400 4955536930 4957069927 5609201111 5616471822 +1454376600 5802097889 5803375443 6319042916 6313085564 +1454374800 5399407845 5399939961 6045299792 6054547420 +1454373000 7372775958 7373360926 8693160623 8694246507 +1454371200 9079585216 9079006453 10321713730 10324713281 +1454369400 10399619426 10397381175 11107803834 11110710808 +1454367600 11188518113 11187173604 12358380692 12351369716 +1454365800 12218082520 12215535513 12874459898 12868685532 +1454364000 12097370166 12094661438 13245453953 13237660516 +1454362200 12270461965 12267259510 12870492280 12872252682 +1454360400 11943845415 11938098245 12886774571 12851358196 +1454358600 11875675285 11874011537 12361742086 12356163185 +1454356800 11476545879 11474086483 12388533638 12365665396 +1454355000 11032066283 11031516808 11627247555 11624908502 +1454353200 10823514212 10824983597 11893483315 11899677091 +1454351400 10567427093 10569096603 11001898475 10999574308 +1454349600 10423727336 10425203422 11415195403 11391946348 +1454347800 10732981416 10736380408 11329709424 11324930296 +1454346000 10766786083 10767698940 11695921432 11660573520 +1454344200 10362719826 10367531284 10914079080 10910705036 +1454342400 10079789834 10081415316 10978896379 10955137027 +1454340600 9988681375 9993098832 10414664848 10414719561 +1454338800 9892105749 9894948396 10677534637 10670736327 +1454337000 9737593265 9740022975 10152999114 10153499451 +1454335200 9614929510 9616927467 10364385101 10350437311 +1454333400 9400617475 9403693519 9838852040 9841867630 +1454331600 9362040786 9363852416 10244106060 10236933653 +1454329800 9429004002 9432394946 9975165129 9978001113 +1454328000 9312196776 9314235708 10139624295 10129792855 +1454326200 9119322032 9122569376 9478264420 9484303308 +1454324400 8913367257 8914660891 9621438853 9606741266 +1454322600 8814849153 8817514709 9171599222 9173686707 +1454320800 8515019443 8517456510 9167242699 9156152166 +1454319000 8082001092 8084345779 8562636763 8562766492 +1454317200 7114511406 7115801611 7667025496 7668248769 +1454315400 6371330169 6374070223 6950063513 6952785881 +1454313600 5317191434 5318542811 5805744371 5807432727 +1454311800 4314863338 4316260822 4724730669 4722506322 +1454310000 3736964844 3737656581 4023135097 4013850674 +1454308200 3744749778 3746756874 3956695116 3957586075 +1454306400 3561344015 3562225589 3836639038 3840918446 +1454304600 3428772069 3430351070 3591414306 3594884091 +1454302800 3194501334 3195878219 3478386342 3479932260 +1454301000 3203704887 3204219754 3448242728 3448137027 +1454299200 3289321488 3290948238 3708694421 3717124709 +1454297400 3689898745 3690660171 4028778303 4032683063 +1454295600 3926490307 3927119456 4361367792 4360612957 +1454293800 4346650249 4347498853 4786446403 4786985115 +1454292000 4989271583 4990504704 5704936684 5708725230 +1454290200 5665386491 5667031260 6192787785 6189959619 +1454288400 6502387101 6502430607 7427368589 7411834134 +1454286600 7652256160 7653537799 8343834969 8338832676 +1454284800 8637518931 8637839504 9699330424 9694589015 +1454283000 9747291118 9747201301 10689941293 10690792028 +1454281200 10750764922 10748915191 11820302227 11813416146 +1454279400 11442638348 11440404824 11922441919 11926111680 +1454277600 11408507576 11406105469 12271932908 12248418491 +1454275800 11207430924 11207544718 11782068614 11780060643 +1454274000 11216989493 11217226561 12129379514 12090935780 +1454272200 11529984367 11532089761 12250153120 12242418133 +1454270400 11163179990 11163842244 12066616265 12044181619 +1454268600 10963221200 10966905878 11441809046 11445044716 +1454266800 10616570143 10618392819 11446251667 11429942684 +1454265000 10356121813 10359757575 10917760124 10920715700 +1454263200 10156990914 10158192891 10869889467 10852330719 +1454261400 9944055794 9948250345 10452978360 10457497343 +1454259600 9759825726 9760904100 10507115465 10479376661 +1454257800 9500489703 9505167350 9894536059 9897458022 +1454256000 9416590342 9418345863 10333682962 10311050599 +1454254200 9303729048 9307438340 9749720648 9751127385 +1454252400 9150674091 9152752342 9899478666 9889789208 +1454250600 8840032931 8842860451 9270431097 9271015137 +1454248800 8693125792 8694754611 9530766643 9508261630 +1454247000 8518496473 8521575938 8932219758 8933855349 +1454245200 8391133836 8393125759 9055075885 9033588289 +1454243400 8100641495 8103277646 8629219523 8630532399 +1454241600 7640020360 7641835356 8212406535 8199906737 +1454239800 7568195847 7570778190 7914318540 7917734154 +1454238000 7452137786 7453515748 8063397365 8052011272 +1454236200 6985585518 6987940052 7435349300 7436342059 +1454234400 6587946467 6589283368 7019314389 7010499550 +1454232600 6123393738 6126012992 6430952730 6436693634 +1454230800 5363376353 5365804031 5876129777 5884412184 +1454229000 4425363915 4426798337 4764772019 4764284915 +1454227200 3810389987 3812220856 4283786761 4293094227 +1454225400 3452307893 3453967489 3653564329 3655864534 +1454223600 3273011442 3274965399 3493410950 3501153089 +1454221800 3045052463 3046027352 3230548967 3236390293 +1454220000 3579443307 3580275258 3951837127 3968208808 +1454218200 3821072811 3823220550 4013564850 4016308686 +1454216400 3906209477 3907306552 4555053841 4566393456 +1454214600 3864977653 3865419118 4311295793 4306770884 +1454212800 3882485234 3883565781 4323087380 4326404356 +1454211000 4300487449 4301816044 4666786641 4668471815 +1454209200 4793540941 4794367938 5307994543 5303430195 +1454207400 5378781028 5380845717 5810191018 5814409112 +1454205600 5720243253 5720826414 6445227544 6431902659 +1454203800 6634847991 6636725077 7137602508 7134099661 +1454202000 7470837782 7471795130 8424723307 8413254142 +1454200200 8422356236 8424369396 9044077760 9051633770 +1454198400 9058647201 9059659220 10009624064 9997349866 +1454196600 9647695207 9650312266 10127974476 10123749602 +1454194800 10016865964 10018165543 10898186327 10879058179 +1454193000 10467519253 10470817101 10962840754 10965198966 +1454191200 10629459240 10632006314 11565379116 11553979371 +1454189400 10706933601 10710673187 11184852871 11186262551 +1454187600 10632955240 10634231879 11511880423 11495134132 +1454185800 10505838387 10509502525 11073303950 11068725757 +1454184000 10255235679 10256307687 11122979009 11102464622 +1454182200 10121017465 10125115338 10579893665 10581036254 +1454180400 9776741918 9778634609 10319033291 10294289157 +1454178600 9681925078 9685861000 10145436105 10147585907 +1454176800 9390663778 9392530570 10055218323 10039070517 +1454175000 9201099143 9204760805 9612573951 9611852809 +1454173200 9055108709 9057316005 9534461033 9540635402 +1454171400 8986670876 8990046428 9404184486 9405266447 +1454169600 8873468806 8875580128 9535246465 9516566508 +1454167800 8812501656 8816352643 9327850618 9331702209 +1454166000 8457616478 8459693853 9088806807 9078828890 +1454164200 8363413312 8366324130 8681853985 8680682126 +1454162400 8240905661 8243158295 8873443941 8865224766 +1454160600 8199103460 8201156706 8627411037 8625721427 +1454158800 8026906674 8029174966 8732146472 8736818967 +1454157000 8003637147 8006465816 8351471418 8353126330 +1454155200 7870418228 7871590912 8522280809 8506874124 +1454153400 7733029164 7736342548 8076168096 8081214659 +1454151600 7528941969 7530121358 8230682596 8217288597 +1454149800 7357535136 7360550194 7734085535 7738425348 +1454148000 6995113568 6995884275 7542985041 7521857196 +1454146200 6511373853 6514470198 6836823234 6839139575 +1454144400 5931304362 5933141333 6269620853 6270102189 +1454142600 5092663131 5094746854 5584162751 5586826794 +1454140800 4116086274 4117804240 4525550890 4526929533 +1454139000 3614261414 3615550832 3811101626 3810750487 +1454137200 3455284693 3456371230 3744196429 3742370716 +1454135400 3351878522 3353949240 3536007537 3537523573 +1454133600 3238617363 3239731705 3610180666 3612642055 +1454131800 3475989404 3477226362 3669425621 3677611255 +1454130000 3504282505 3505461528 3845943708 3855093025 +1454128200 3499878058 3501143874 3776501231 3776988859 +1454126400 3728664364 3730074484 4159894508 4170010741 +1454124600 4213004066 4213937240 4967082313 4973700840 +1454122800 4454338985 4454611209 5029061587 5018432646 +1454121000 4967167354 4969293485 5437166470 5436380310 +1454119200 5480892355 5481694376 6287042920 6282962376 +1454117400 6448006882 6449558426 6889277613 6889009911 +1454115600 7258613099 7259902866 8200209410 8202427073 +1454113800 8393615645 8395221414 9157314106 9156409506 +1454112000 9138260461 9140601806 10238258176 10242536037 +1454110200 10010730236 10012442256 10722309224 10730664304 +1454108400 10530852099 10533547232 11524661018 11530133623 +1454106600 11105412989 11107178619 11608615099 11613116431 +1454104800 11049273801 11051479911 11880332879 11873626967 +1454103000 11055118119 11057925017 11497311195 11498319848 +1454101200 10805140301 10807048544 11605909824 11591784084 +1454099400 10629550634 10632864112 11176572903 11181252844 +1454097600 10140326311 10142023161 10901367436 10885371698 +1454095800 9884977303 9888349120 10362708368 10364222477 +1454094000 9847932137 9849959148 10784981839 10790419508 +1454092200 9955069360 9958153474 10460513509 10455607566 +1454090400 9795339130 9797289902 10695641572 10678931198 +1454088600 10109819296 10112942359 10639713675 10646163529 +1454086800 10099759555 10102237408 11024817941 11005602349 +1454085000 10180600041 10184314140 10862812932 10865539687 +1454083200 9774530880 9776547647 10445933053 10432104081 +1454081400 9443032008 9446613622 9932741239 9936472403 +1454079600 9452745609 9455136128 10385543431 10372648757 +1454077800 9360906637 9364174773 9815772141 9824543520 +1454076000 9254188662 9257453507 10349138355 10355413821 +1454074200 8876011385 8877993627 9286799259 9284843769 +1454072400 8977553464 8980158469 9848657054 9846238171 +1454070600 9003683751 9005869924 9382121214 9383201335 +1454068800 9040983761 9043314877 9886515720 9880137864 +1454067000 8847617420 8850631126 9294997609 9298255966 +1454065200 8764179921 8765927148 9545370453 9537200707 +1454063400 8543840839 8546746628 8934907011 8941055448 +1454061600 8339194457 8341159885 9056708450 9052346541 +1454059800 7783476163 7785943365 8441034903 8443191423 +1454058000 6927844308 6929595120 7509901805 7508265389 +1454056200 6264537764 6266807338 6682435481 6684724468 +1454054400 5262569566 5263858926 5740177287 5740796384 +1454052600 4518720077 4520726574 4907122986 4910021524 +1454050800 3811800845 3813211697 4165781904 4167613795 +1454049000 3407871367 3409147449 3623932533 3624709083 +1454047200 3563152904 3564500833 3944057846 3945611823 +1454045400 3597983394 3599063026 3852719932 3852967970 +1454043600 3445002177 3446517637 3913515376 3918211701 +1454041800 3854296441 3856169041 4027990101 4030463439 +1454040000 3607066370 3607904282 4029300327 4029386203 +1454038200 3925900308 3927015457 4233124840 4235250980 +1454036400 4071417112 4072495531 4601885632 4602315367 +1454034600 4660861843 4662311078 5136301105 5135703683 +1454032800 5030360773 5031520821 5736620979 5739892063 +1454031000 5824499394 5825676578 6421745494 6425772294 +1454029200 6310730766 6312570451 7213777859 7219137711 +1454027400 7786656740 7791659821 8466934045 8480373054 +1454025600 9006441369 9007661743 10189019075 10186875361 +1454023800 10148362658 10147992039 11029167925 11021897883 +1454022000 10867420479 10866051921 12054040807 12051353214 +1454020200 11405058499 11404048919 12100190511 12097831028 +1454018400 11580741729 11579038589 12648806936 12636663091 +1454016600 11771791007 11771685702 12348265159 12347053306 +1454014800 11501286264 11499997417 12476611314 12459134585 +1454013000 11392045264 11391344375 11953067992 11951403026 +1454011200 10970679159 10968718076 11905801392 11881553421 +1454009400 10818632571 10819419873 11315263661 11311866516 +1454007600 10620976374 10620603605 11476863776 11477203593 +1454005800 10599885362 10600890880 11155025030 11157499091 +1454004000 10516418570 10516402525 11417018759 11388406758 +1454002200 10618970205 10622599775 11145366183 11149874875 +1454000400 10697694531 10699913900 11608060883 11596084734 +1453998600 10387249777 10391830013 10953245701 10958505920 +1453996800 10171679093 10173196726 10861129206 10846044573 +1453995000 9695745117 9698916311 10385855335 10389519222 +1453993200 9081440236 9082086585 9831209549 9823945300 +1453991400 9349443994 9352422380 9830826575 9829115263 +1453989600 9240841252 9242014259 10040676640 10038432549 +1453987800 9229861073 9231367158 10511654645 10502962609 +1453986000 9165096917 9165698635 9909989784 9913768575 +1453984200 9085504567 9086890622 9633806258 9637052973 +1453982400 8777313144 8778961663 9256087253 9249123707 +1453980600 8539651992 8541707664 9017428353 9013867699 +1453978800 8359256047 8359542719 8889068123 8875584250 +1453977000 8355372762 8356628712 8738956938 8739229577 +1453975200 8048778423 8048771366 8588810382 8573278071 +1453973400 7416679391 7418433927 7856916293 7857329475 +1453971600 6568511117 6568597579 7044316875 7046757182 +1453969800 5988311355 5989737802 6445704511 6445811162 +1453968000 5083561007 5083743075 5567670378 5568517719 +1453966200 4265364121 4266173402 4520153439 4518828420 +1453964400 3535459252 3535959166 3969701064 3967315077 +1453962600 3171997001 3172564627 3319808131 3322458351 +1453960800 2998128684 2998339389 3256549667 3254456006 +1453959000 3111779331 3112356513 3291223833 3292437260 +1453957200 2917371048 2917618835 3250006623 3250769213 +1453955400 3198992327 3199166004 3528375878 3527340852 +1453953600 3309153426 3309622190 3594762377 3596599270 +1453951800 3544894108 3545446538 3769430397 3770632115 +1453950000 3836473516 3836338832 4276192404 4274420371 +1453948200 4323597809 4324184854 4885137451 4884041588 +1453946400 4767536535 4767307409 5340837485 5337421664 +1453944600 5371723807 5372165903 5901862052 5901911730 +1453942800 6390095616 6389375157 7235501126 7234565930 +1453941000 7486044866 7486793329 8257936360 8258931214 +1453939200 8526420602 8524349716 9740203118 9719052285 +1453937400 9793048833 9790920960 10621024568 10615555341 +1453935600 10585165019 10578425807 11632535267 11602048285 +1453933800 10991186213 10986299348 11492029904 11487276519 +1453932000 11033027624 11027350740 11966798318 11932279292 +1453930200 11225542491 11222887155 11755801595 11749420302 +1453928400 10976827162 10968342746 11817056222 11771073527 +1453926600 10796667255 10793004235 11284079687 11277316021 +1453924800 10508036762 10502921706 11268862699 11228720904 +1453923000 10445763576 10443798608 11070451414 11065913874 +1453921200 10273946029 10266296265 11060932272 11040128719 +1453919400 10065466472 10058146823 10642600583 10632535639 +1453917600 10037704963 10026812400 10919413034 10872696551 +1453915800 10295173122 10287822958 10892491801 10878390280 +1453914000 10274119882 10264542735 11344220404 11299432805 +1453910400 9816272262 9800363223 10885087451 10840137344 +1453903200 9293399701 9276790807 10299112217 10258808430 +1453896000 8780724032 8764637836 9975967714 9941318446 +1453888800 7173243537 7161441837 8794492597 8756242292 +1453881600 4276686966 4272967157 5773993994 5765887458 +1453874400 3744578132 3744954864 4392871375 4430569836 +1453867200 4051783470 4052082713 4987964852 4986085551 +1453860000 6093740365 6093819418 8362011251 8361620383 +1453852800 10196443694 10193425147 12147965634 12144200751 +1453845600 11420093858 11415783673 12636972803 12619529225 +1453838400 10271368775 10262501072 11344900386 11329087865 +1453831200 10215900332 10200470831 11234735959 11185021921 +1453824000 9837289726 9822337181 10933184275 10891245953 +1453816800 9244727381 9231233732 9917455372 9882009094 +1453809600 8864095744 8854353850 9900262451 9867875784 +1453802400 7308598259 7305404785 8957710673 8936665074 +1453795200 4316246458 4316363788 5960433944 5957662343 +1453788000 3198900699 3199172062 3643443773 3644934860 +1453780800 3782536627 3782723737 4710513425 4706954238 +1453773600 5867225148 5867446144 8040395532 8040582438 +1453766400 10043138540 10040980409 11674701404 11664531534 +1453759200 11180497524 11175382430 12120043394 12072090823 +1453752000 10391462388 10385012864 11360329550 11312156093 +1453744800 10137527939 10130283684 11026689083 10981158544 +1453737600 9643522904 9636356647 10611072077 10577477299 +1453730400 9279627223 9273221385 10151480890 10123569565 +1453723200 8722028397 8717180664 9656305649 9633529221 +1453716000 7203811878 7201484757 8903576133 8893838202 +1453708800 3946917626 3946933585 5488158797 5486714318 +1453701600 2991763374 2991980601 3377860802 3379200650 +1453694400 3763779145 3764028332 4824578747 4825714629 +1453687200 6311600093 6311895205 8654056642 8651210167 +1453680000 10021108130 10019832955 11714931284 11708012406 +1453672800 11373744152 11372921979 12550481385 12517641589 +1453665600 10761991998 10756164658 12476653564 12451623941 +1453658400 9684834546 9675594929 10696364536 10673524988 +1453651200 8988364462 8978973680 9860798672 9822353107 +1453644000 8491467446 8482712342 9672107120 9629409892 +1453636800 7972204962 7963588193 8957753102 8916736853 +1453629600 5898134874 5892622825 7371329923 7340736209 +1453622400 3454305776 3453698200 4370756038 4368095240 +1453615200 3364936960 3365316688 3904501486 3905410269 +1453608000 4713044592 4712849861 5990349296 5989703165 +1453600800 7060002997 7059996710 9044488539 9040431286 +1453593600 9815431385 9815386987 11341673988 11345934153 +1453586400 10582991756 10581557427 11681073450 11650154348 +1453579200 9884957439 9879157334 10794624650 10759457084 +1453572000 9494609904 9485593260 10344698915 10300575343 +1453564800 8689059406 8694911628 9472261087 9468068921 +1453557600 8279146067 8297787510 9419758988 9415695841 +1453550400 7899960993 7910211410 8797220683 8791264616 +1453543200 6664195145 6658892555 8107303165 8079646490 +1453536000 4031386961 4031320678 5381160656 5381235157 +1453528800 3786500983 3787309916 4194301370 4194503818 +1453521600 4617563638 4617691834 5683230484 5680402488 +1453514400 6920191308 6918868050 8800203022 8796925072 +1453507200 10006223632 10006877446 11440479401 11420589795 +1453500000 10771480678 10771092174 11673628739 11665359198 +1453492800 9873126013 9867050740 11113347046 11072717816 +1453485600 9821043811 9812477829 10748850448 10724616367 +1453478400 9269069825 9260172081 10235538409 10203078708 +1453471200 8847088261 8839693464 9670130144 9651841739 +1453464000 8751712826 8746755083 9580204674 9572336858 +1453456800 7252078128 7251322393 9095818271 9093585113 +1453449600 4298144828 4299447053 5816481057 5814028835 +1453442400 3601859048 3602680976 4105853533 4108302375 +1453435200 4104839795 4105682398 4878893848 4877687068 +1453428000 6053375046 6053616228 8190517983 8195172921 +1453420800 9701038632 9700866917 11235443305 11234626881 +1453413600 10810802457 10810741984 11900869741 11892126708 +1453406400 9917783422 9918484142 11306366302 11286867456 +1453399200 9578698085 9579930575 10433725111 10404067062 +1453392000 9335392651 9336562945 10689556049 10706172114 +1453384800 9103425564 9104017644 9825411473 9830583553 +1453377600 8726774005 8726868839 9760391500 9753380477 +1453370400 7296727745 7297622136 8774476319 8764367170 +1453363200 4228502388 4228982600 5854936941 5857161124 +1453356000 3263428785 3263706567 3659504816 3660780449 +1453348800 3958464223 3958775221 4703733747 4701834043 +1453341600 5945814700 5945987874 8003978106 8004579738 +1453334400 9808576253 9808707259 11424949014 11425259308 +1453327200 11062102428 11060978864 12242231553 12235490765 +1453320000 10066913045 10067292368 10856632641 10821320955 +1453312800 9891444561 9892261168 10736577892 10708064402 +1453305600 9151284250 9152263394 10242206022 10230571807 +1453298400 8696547346 8697348572 9754723301 9749149729 +1453291200 8499704774 8500881389 9287633182 9273647071 +1453284000 7222500149 7223540442 8831725322 8815111185 +1453276800 4118250264 4118978915 5555653065 5557618414 +1453269600 3545169355 3545637551 4041269368 4042377602 +1453262400 3957351070 3957593414 4879225077 4878294678 +1453255200 6359160963 6359255900 8468638077 8470180996 +1453248000 10167127776 10167202765 12741291144 12759542342 +1453240800 11268200432 11266943111 12561526953 12539258121 +1453233600 10080793050 10080664015 11357398922 11353091903 +1453226400 9514075982 9514746568 10267343889 10256650734 +1453219200 9147493739 9148095468 9996360853 9972883616 +1453212000 8723217254 8724272422 9706518291 9686125909 +1453204800 8224721872 8225490077 9178240341 9159186662 +1453197600 7142190513 7142962316 8913513812 8936098534 +1453190400 4112235814 4113029848 6072536888 6073597714 +1453183200 3164887104 3165153920 3447630860 3449041884 +1453176000 3827327005 3827480065 4631753429 4629391083 +1453168800 5851770697 5851703439 7959364309 7964219248 +1453161600 9865776032 9865321085 11802273308 11795877080 +1453154400 11239071132 11237852948 12579472477 12555966329 +1453147200 10170983565 10171451497 11981714390 11933635924 +1453140000 9653265137 9653745442 10611367425 10590801993 +1453132800 9079724955 9080557249 10044065447 10005751505 +1453125600 8971577449 8972328464 9818112443 9799596047 +1453118400 8562996397 8563774814 9639472452 9634335551 +1453111200 7166544271 7167140457 9013471263 9015693412 +1453104000 4052295476 4052337797 5839441048 5839510141 +1453096800 3255517560 3255802339 3734940772 3736852524 +1453089600 3812876070 3812771105 4760998298 4757001578 +1453082400 5973860563 5973301896 8045050634 8047921380 +1453075200 9782419183 9781730970 11726109516 11726070902 +1453068000 11287697248 11287919141 12465452405 12472019290 +1453060800 10499364044 10499496442 11879916977 11895952120 +1453053600 9687679758 9688261843 11043219169 11075520971 +1453046400 9094693707 9094338936 10512447299 10538865249 +1453039200 8403359777 8403147126 9540320260 9513825753 +1453032000 7552127670 7552219632 8451919615 8455153007 +1453024800 5653785597 5654108218 7130302741 7112325605 +1453017600 3347196921 3347440879 4015864735 4009374291 +1453010400 3320502219 3320291457 3769398389 3768707809 +1453003200 4553607333 4553580876 5587842883 5585683310 +1452996000 7089034024 7088907382 8862593414 8862528152 +1452988800 9958036856 9957872100 11955127299 11907113297 +1452981600 10354189589 10350460107 11961417902 11953506242 +1452974400 9630765755 9631031110 11188846855 11149386406 +1452967200 9155471142 9155919796 10095887037 10064710186 +1452960000 8550229610 8551039581 9448438434 9417367437 +1452952800 7946509934 7947554809 8756822747 8739311344 +1452945600 7613916748 7614143448 8589091605 8568755475 +1452938400 6054170910 6054962939 7532159933 7509075641 +1452931200 3783601763 3783895144 4550371332 4554615176 +1452924000 3550357063 3550462388 3871387932 3875413017 +1452916800 4533660568 4533601627 5633170440 5631689901 +1452909600 7104184441 7103859222 9164880004 9157852654 +1452902400 10305593512 10305945084 12648057460 12706155047 +1452895200 10908562260 10908866317 12578262231 12530506225 +1452888000 9820418173 9820284648 11079627599 11087608692 +1452880800 9497197570 9497831353 10591118567 10615744116 +1452873600 8888138220 8888994764 9869312263 9848032887 +1452866400 8461007581 8461580202 9332312049 9305001979 +1452859200 8355553068 8356481777 9102947575 9079556998 +1452852000 6950415625 6951111909 8544798224 8531309741 +1452844800 3996455117 3996827895 5486150302 5485786522 +1452837600 3317416368 3317544915 4145666409 4145966426 +1452830400 4062587268 4062562572 4846634792 4842960978 +1452823200 5970052202 5969639646 8077042688 8077361865 +1452816000 9729691440 9728906233 11424114118 11425260649 +1452808800 10835656082 10834447355 12356291059 12309448577 +1452801600 9739901787 9740063836 10787437142 10757772323 +1452794400 9431646757 9432188774 10453746066 10417874995 +1452787200 9059148811 9059858420 9963547267 9940111416 +1452780000 8790562243 8790790174 9619898966 9594650923 +1452772800 8260465557 8261214210 9016586846 9026438571 +1452765600 6763335423 6764039882 8081111365 8081444876 +1452758400 3953797474 3954332047 5530778553 5538956941 +1452751200 3273680472 3274450793 3776101132 3785390824 +1452744000 4096048070 4096513715 5236985734 5263493350 +1452736800 6105451176 6105322610 8051965125 8015600568 +1452729600 9789731286 9789582548 12109577106 12127212926 +1452722400 11217926495 11218230303 12397383096 12377792984 +1452715200 9980210909 9980632488 10907256551 10889529445 +1452708000 9846786924 9847993793 10687574179 10647420640 +1452700800 9458660290 9459711331 10345278915 10356097408 +1452693600 8935872580 8937470780 10004744020 9970493126 +1452686400 9461853319 9463061887 10781898005 10787113718 +1452679200 8695790990 8692334968 11593968344 11604254554 +1452672000 4378596844 4379684456 5850251091 5852043338 +1452664800 3812790824 3813354906 4486954213 4499023884 +1452657600 4336739619 4337196109 5483114651 5504259445 +1452650400 6337617392 6337251636 8317564968 8311468084 +1452643200 10300278529 10300328320 12708827583 12710159363 +1452636000 11527763116 11527692809 12782933636 12796464407 +1452628800 10252411580 10253198423 11544255417 11526323207 +1452621600 9327425773 9327999289 10113906896 10074426055 +1452614400 8927888996 8928840401 9983341476 9964017103 +1452607200 8722529052 8723401919 9447354129 9443755080 +1452600000 8260313529 8261099243 9235007942 9215991159 +1452592800 6646535559 6647244263 8072273224 8056835802 +1452585600 3920010055 3920409930 5201381194 5201023886 +1452578400 3284414408 3284640754 4111973386 4119128495 +1452571200 4022419603 4022480795 4914041266 4914288530 +1452564000 6204851734 6204341953 8400901534 8403261834 +1452556800 10168749233 10167722058 12158266946 12122854853 +1452549600 11298550120 11298076397 12426024856 12376716067 +1452542400 10079958952 10080206464 11173969255 11187235781 +1452535200 9690563929 9691315733 10610349391 10579092786 +1452528000 9346154070 9346867687 10140307909 10112651610 +1452520800 9045089918 9046029434 10034000893 10010791882 +1452513600 8374105094 8374834711 9514517461 9500751969 +1452506400 6753844593 6754491800 8463070818 8464011602 +1452499200 3824211759 3824451452 5208486431 5205688285 +1452492000 3018936773 3019066388 3332303436 3333406227 +1452484800 3838473502 3838342748 5180587937 5173861850 +1452477600 6343632129 6343321961 8364749563 8365195728 +1452470400 9780836275 9781040616 11457848095 11470293646 +1452463200 11033560881 11033264043 12401604303 12387460906 +1452456000 10139620401 10139960390 11421574570 11366888247 +1452448800 9234805221 9235460076 11050844897 11001298316 +1452441600 8342743583 8343332806 9413000039 9386149941 +1452434400 7811133499 7811527404 8904135508 8877585085 +1452427200 7077356326 7077422927 8401990550 8361889603 +1452420000 5079517008 5079974240 6591875254 6564407234 +1452412800 3124826651 3125166358 3705073519 3704535898 +1452405600 3250049853 3250041604 3929546260 3929207514 +1452398400 4674346761 4674248748 5802227347 5803799118 +1452391200 7243916357 7243635764 9311004061 9311831656 +1452384000 10045658609 10046161330 11242299302 11196793954 +1452376800 10199057262 10198263310 11298205621 11256151174 +1452369600 9528550410 9528605889 10666379291 10622319126 +1452362400 8984585564 8985425629 9892857257 9857872912 +1452355200 8354919342 8355727338 9382199864 9347669073 +1452348000 7750355809 7751118922 8769282706 8745064474 +1452340800 7112229188 7112413034 7987328684 7961413067 +1452333600 5371459369 5372125909 6754196811 6754558778 +1452326400 3275569388 3275797180 4087468021 4086768479 +1452319200 3289198450 3289255842 3847525537 3846673305 +1452312000 4566573992 4566768854 5727502941 5729553484 +1452304800 7281027557 7280777442 9433789098 9437654840 +1452297600 10505122807 10504975237 11993280680 12003962264 +1452290400 10513268948 10513716876 11688569536 11703936491 +1452283200 9728462560 9728388523 11243216106 11271950128 +1452276000 9536954140 9537789697 10328400372 10308710957 +1452268800 9619762091 9443516344 10482002866 10455757709 +1452261600 9197251103 8902841742 10148212362 9801344771 +1452254400 8825861741 8560291604 9902778790 9573445667 +1452247200 6771343729 6714776127 8859211454 8652320374 +1452240000 3771332364 3771717061 4970656883 4973981687 +1452232800 3190373537 3190411826 3580199291 3579200632 +1452225600 4054998380 4054907683 5077036131 5080230249 +1452218400 6395158073 6394801007 8334210083 8335145717 +1452211200 9845396900 9845130011 11919890417 11911442381 +1452204000 10584654856 10584265119 11783291380 11758256774 +1452196800 9416746739 9417051976 10464160883 10434072370 +1452189600 9062858601 9063272725 9871444151 9839869473 +1452182400 8645555267 8646503898 9519713560 9493511218 +1452175200 8060478987 8061064259 8826449328 8801847118 +1452168000 7698550963 7699149558 8560006446 8544551654 +1452160800 6318906121 6319309112 7563926591 7555497725 +1452153600 3776278975 3776466702 5010281637 5012057590 +1452146400 3274528449 3274780733 3668430092 3666710431 +1452139200 4078951725 4078927352 5120272450 5118134743 +1452132000 6270360592 6270127797 8156531664 8154906590 +1452124800 9738518770 9737607643 11718474728 11726464974 +1452117600 10632014074 10631399638 11947826898 11915485255 +1452110400 9783604495 9783969981 11153123560 11155039633 +1452103200 9616647202 9617913221 10613790987 10586871312 +1452096000 9130484500 9131424781 10047437928 10032099142 +1452088800 8497929426 8499007170 9827871171 9822419296 +1452081600 7880473178 7881518313 8820166168 8794667150 +1452074400 6411567649 6412510908 7957296746 7946100243 +1452067200 3844657930 3845136281 5517231232 5494988916 +1452060000 3354406848 3355074321 4321777247 4306982057 +1452052800 4559181425 4559685929 5948817716 5996523284 +1452045600 6680308859 6680300815 8610616906 8611235102 +1452038400 9988321045 9987806407 11964341419 11960131489 +1452031200 10858009871 10857628829 12313236642 12311088510 +1452024000 9751594715 9752266769 10694162797 10660211048 +1452016800 9638419322 9639917901 10565215881 10573028552 +1452009600 9986966738 9995096504 10866133878 10837955698 +1452002400 9386038996 9387751367 10380027508 10351261326 +1451995200 8782468042 8783997160 9576183754 9539768182 +1451988000 6608210034 6609427629 8277686376 8256626723 +1451980800 3781572956 3782490342 4855080923 4857586551 +1451973600 3497168436 3497903358 4245269945 4245211428 +1451966400 5119164927 5120108606 6390036612 6391656699 +1451959200 7751130848 7752062040 9705273552 9703745347 +1451952000 11114630041 11116012896 12781383883 12743741782 +1451944800 11291459595 11292458007 12784754883 12736942820 +1451937600 9871101386 9871833869 10999572595 10966063541 +1451930400 9612472122 9613690628 10324351334 10298627976 +1451923200 9852919949 9854878112 10611864460 10586307598 +1451916000 9260180189 9261281809 10857664904 10809712974 +1451908800 8624440812 8625810816 9649711019 9658797140 +1451901600 6606961909 6608017354 8242862459 8248640781 +1451894400 3717897972 3718418447 4748795670 4747309125 +1451887200 3905952496 3842787987 6281811757 5457968185 +1451880000 5468626921 5345643107 9957659717 8716030311 +1451872800 7611053875 7603462016 9381730289 9380111491 +1451865600 10508777744 10509946668 12484726357 12508988267 +1451858400 10848922119 10849447713 12645388383 12647506506 +1451851200 9655429822 9655530866 11283583826 11293415936 +1451844000 8672689979 8673209656 10199205432 10204857217 +1451836800 8083132747 8083262901 9477572173 9494202859 +1451829600 7235891091 7235959386 8001766639 8013758667 +1451822400 6246134977 6246091699 7209896799 7183189977 +1451815200 4288253922 4289002585 5447922175 5448328741 +1451808000 3096189911 3095890626 3738890020 3717379488 +1451800800 3690112706 3689676512 4999111112 5006459018 +1451793600 5274626643 5275010417 6694189760 6695349013 +1451786400 8096497348 8095082815 9822525835 9825242508 +1451779200 10181135952 10181064702 11466200453 11453125596 +1451772000 10009882945 10009731523 11484692104 11461806092 +1451764800 9165605333 9165634270 10218944598 10239168761 +1451757600 8721179889 8721458064 9803511104 9811789308 +1451750400 7947516444 7948373528 9090373651 9071696869 +1451743200 7318390036 7319064583 8498315385 8514790003 +1451736000 6287110047 6287463044 7283984811 7269205568 +1451728800 4301256377 4301510411 5390945775 5387622236 +1451721600 2960585108 2960834233 3336743020 3338515924 +1451714400 3475373828 3475655808 4278652206 4279830134 +1451707200 5281965075 5282255328 6715310732 6717708934 +1451700000 8055296297 8056047318 9727676201 9726413061 +1451692800 10445715390 10447244417 11574254289 11576031475 +1451685600 10393479931 10394944497 11399311350 11414641543 +1451678400 9484896450 9486044797 10386760707 10403506650 +1451671200 8859841839 8861343668 9731073414 9754213163 +1451664000 8181415102 8182576143 9342204830 9356835472 +1451656800 7165480596 7166063879 8060894311 8051477644 +1451649600 5910086641 5911006522 6983293719 6961595904 +1451642400 3853431037 3853712951 4880486764 4878511414 +1451635200 2804411793 2804564610 3890200235 3897597629 +1451628000 3347331699 3347432118 4614337545 4622463641 +1451620800 4857493740 4858056299 6226187107 6240265541 +1451613600 6306336826 6306352823 7032290085 7043369379 +1451606400 7179043994 7179734433 7819504425 7796496495 +1451599200 7652047463 7652892343 8572167261 8562225318 +1451592000 8022641160 8023255978 8784237218 8788882032 +1451584800 8135365373 8136664996 9488762057 9498390479 +1451577600 7830385810 7831857973 9266999585 9298667451 +1451570400 7460378838 7461679998 8153540741 8146053048 +1451563200 6722345382 6723109725 7547628015 7507222380 +1451556000 5010928562 5011720987 6559683354 6572969089 +1451548800 3225481710 3226014017 3848878017 3849137775 +1451541600 3574247103 3574812849 4369236231 4371486601 +1451534400 4885371981 4885211708 6122558963 6122142828 +1451527200 7351312913 7351618869 8902478696 8903037255 +1451520000 9451575183 9452605108 10903512062 10895572204 +1451512800 9664687411 9665839504 10691543233 10665147366 +1451505600 8704159363 8705120189 9698845739 9672537652 +1451498400 8030169771 8031256759 8850118234 8864142176 +1451491200 7747119738 7747849557 8333623630 8303756587 +1451484000 7504939852 7505640966 8410832531 8433118416 +1451476800 6908437298 6908910709 7726197419 7721452103 +1451469600 5273894742 5273691952 7038832966 7051999122 +1451462400 3205187217 3205437678 3749710187 3750345483 +1451455200 3272565146 3272819181 3917398943 3917956293 +1451448000 4851391621 4851602199 6250891600 6251865389 +1451440800 7576790737 7577275270 9293396083 9285487213 +1451433600 9901715163 9902996323 11249480089 11218639878 +1451426400 10396057397 10397518187 12086090735 12106657686 +1451419200 9284355852 9285730521 10220529227 10227465707 +1451412000 8763469611 8764590112 9324011932 9304720569 +1451404800 8711144587 8712425595 9783990048 9753347823 +1451397600 8019197036 8020339184 9327610458 9358599345 +1451390400 7496559846 7497234027 8681858101 8658700273 +1451383200 5637082632 5637811380 7573489176 7584801443 +1451376000 3237822193 3238212328 4187995743 4188962842 +1451368800 3422901943 3423244551 4180646116 4180954310 +1451361600 4894751165 4895469141 5974941160 5975572514 +1451354400 7406012734 7406704746 9414973343 9430986130 +1451347200 9555479325 9556666459 11464905808 11482643479 +1451340000 9885277441 9882588310 11068452475 11038302675 +1451332800 9163994561 9163999538 10323110374 10322255757 +1451325600 8509536178 8501770253 9356544091 9337966921 +1451318400 8096485340 8097697889 9206535730 9190073861 +1451311200 7489578931 7490436596 8526201585 8489347495 +1451304000 6713449685 6714108146 7705895916 7684521192 +1451296800 4813726137 4814452202 6076022673 6078069098 +1451289600 3224123421 3223815311 3855931581 3848688180 +1451282400 3304505426 3304224724 3925454970 3927463682 +1451275200 4554843847 4554511870 5854561091 5855630690 +1451268000 6982162415 6981755118 8559358823 8560639405 +1451260800 9211976143 9211016432 10281377987 10261443237 +1451253600 9595619830 9591888859 10632901092 10609061956 +1451246400 8862366688 8858502462 10131743747 10103966598 +1451239200 8136928148 8132781453 8994168652 8974456219 +1451232000 7640979414 7641636341 8377674077 8360568214 +1451224800 6988599719 6989062899 8490438304 8458302828 +1451217600 6253586355 6254352171 7302604396 7302167423 +1451210400 4462955245 4463562632 5808868320 5809699511 +1451203200 2851046460 2851128582 3304679550 3304061807 +1451196000 3110643143 3110025132 3738472878 3735966934 +1451188800 4387437354 4387013166 5573947318 5576822329 +1451181600 6516954259 6517250683 8574445150 8560586767 +1451174400 8352860088 8352751901 9595581900 9569386477 +1451167200 8833647266 8831412851 9855594225 9840983384 +1451160000 8449328492 8450204798 9614224092 9597149341 +1451152800 8151226183 8152725534 9164422666 9154528794 +1451145600 8073072568 8074355287 9385699902 9411609340 +1451138400 7691923014 7692904060 8849717531 8855764367 +1451131200 6748448993 6749392261 7884639451 7905826441 +1451124000 4712018969 4712596132 6072290225 6065766661 +1451116800 3334013583 3331611626 3895408031 3891978953 +1451109600 3411292981 3410746040 3848508700 3845193813 +1451102400 4836122266 4827404785 6454389061 6467274020 +1451095200 7360365271 7360400454 8689869197 8689767645 +1451088000 8610827264 8609490604 9350422208 9309023593 +1451080800 8814458893 8811149949 9921590720 9911622170 +1451073600 8277690028 8273816982 9499206898 9478662326 +1451066400 7595394810 7594481760 8531176274 8514930690 +1451059200 7399618668 7401150987 8462481136 8440120387 +1451052000 8130172880 8131672032 9008092637 9030102070 +1451044800 8169083626 8170918708 9207546065 9200033945 +1451037600 6873614049 6875284299 8676354706 8658723348 +1451030400 4056988527 4057746080 5709144097 5711941169 +1451023200 3415594316 3416351174 3823956705 3825686967 +1451016000 4345636462 4345963422 5489317495 5490005682 +1451008800 6346664477 6347232566 7380330592 7379702119 +1451001600 7428063461 7429065881 7997716284 7982623048 +1450994400 7290294250 7291072632 7985880342 8010177221 +1450987200 7075230434 7075804242 7883986820 7885360265 +1450980000 7014690386 7015468263 7693026378 7697386214 +1450972800 7027346149 7028300579 7807665970 7812278457 +1450965600 6844403429 6844947307 7783223424 7798148881 +1450958400 6639886057 6640451395 7131206152 7145185753 +1450951200 5473405680 5474148058 6608967540 6596053205 +1450944000 3351495024 3351817843 4237272606 4236581468 +1450936800 3093145223 3093408885 3658801992 3659064347 +1450929600 4189605083 4190017536 5144460059 5146813912 +1450922400 6266037238 6266274258 7854344431 7853903230 +1450915200 8342047654 8343077803 9358782112 9337238013 +1450908000 8753690846 8754881571 9846415356 9824809641 +1450900800 8152579332 8153293447 9222913081 9213471741 +1450893600 7905914683 7907071477 8745800813 8730275342 +1450886400 7772616855 7773645158 8771221109 8747083651 +1450879200 7574787099 7575724164 8227272553 8198519060 +1450872000 7643816266 7644950188 8467535212 8435737761 +1450864800 6196142809 6197306483 7792652156 7766160219 +1450857600 3699589939 3700114574 5072810028 5058407724 +1450850400 3407272968 3407650289 4013712019 4011421064 +1450843200 4352655967 4352954487 5337185077 5333410428 +1450836000 6619052131 6619262705 8389663795 8392349937 +1450828800 9311787891 9312897259 10424041447 10402013149 +1450821600 9700747152 9701749077 10812025684 10795018168 +1450814400 8824596456 8825440831 9649937904 9644424613 +1450807200 8657135385 8658125422 9337315981 9325647086 +1450800000 8584663435 8585598122 9330811850 9305335037 +1450792800 8109228448 8110606946 9026913554 9011491700 +1450785600 8303126724 8304558250 9421177004 9395810353 +1450778400 6543673659 6544690274 8306945207 8310801720 +1450771200 3895217000 3895643068 5103730168 5105648784 +1450764000 3262350586 3262573333 3708243052 3713633954 +1450756800 4110498799 4111076570 5123028423 5123694434 +1450749600 6335816086 6336335749 8143126430 8145287870 +1450742400 9306625478 9307168056 10842238668 10818193996 +1450735200 9668827678 9669827010 10681814698 10648652952 +1450728000 8956925500 8957890381 10083029478 10058140437 +1450720800 8713103335 8714412994 9586929246 9553259230 +1450713600 8566961664 8568014427 9459507492 9428735722 +1450706400 8019626033 8020896693 8841061217 8839573073 +1450699200 7882508684 7883747518 8838707628 8816362981 +1450692000 6431526529 6432429768 8427433946 8418702348 +1450684800 3653058591 3653629397 4766648499 4768916153 +1450677600 3020155883 3020449341 3438708239 3440516115 +1450670400 3866936325 3867294356 4927235045 4928337337 +1450663200 6020607162 6020983445 7845026006 7847346037 +1450656000 8856436754 8857573728 10132076424 10135020911 +1450648800 9670570749 9671879895 10634388265 10609431862 +1450641600 8741403603 8742740588 9444528109 9423799649 +1450634400 7970228706 7971204881 8978169686 8963376593 +1450627200 7407634404 7405433383 8186656455 8167358117 +1450620000 6951319252 6944676921 7574119085 7575917991 +1450612800 6641103145 6642032030 7271317718 7242500861 +1450605600 5043377793 5044234947 6364542117 6346254922 +1450598400 3053335339 3053803041 3681774924 3676251207 +1450591200 3068064322 3068434131 3632329382 3632923166 +1450584000 4227010140 4227658657 5372490452 5376463506 +1450576800 6387776159 6388390183 7848980545 7848259140 +1450569600 8509238584 8510478524 9762502930 9757055430 +1450562400 8842471978 8842945773 9752291700 9721174740 +1450555200 8342921606 8343905731 9207432208 9208054460 +1450548000 7823691692 7824963647 8866161657 8832717289 +1450540800 7385336467 7386589665 8269006863 8235567724 +1450533600 6972571090 6973465903 7718181013 7686119833 +1450526400 6623784998 6624969432 7280381154 7248740411 +1450519200 5511793506 5512891640 7167441399 7156093585 +1450512000 3530991597 3531520556 4448031571 4449878056 +1450504800 3499533370 3500146761 4129835060 4132985153 +1450497600 4776339614 4776978794 6060207886 6057954057 +1450490400 6957603793 6955972022 8794898287 8802948702 +1450483200 9465897075 9467170279 10613589278 10586176656 +1450476000 9981399277 9983232743 10866516647 10839236041 +1450468800 9546535801 9468877154 10969526314 10922867918 +1450461600 9321071163 9322405782 10181149716 10156305530 +1450454400 9092721987 9094034750 10162358949 10116471950 +1450447200 8193502127 8194836418 8980582176 8957198511 +1450440000 7984077687 7985226069 8900048715 8885661991 +1450432800 6411252885 6412262992 7880989595 7865559976 +1450425600 3859736117 3860741952 4967370555 4965290759 +1450418400 3536689693 3537215350 4125923100 4129637815 +1450411200 4006793666 4007533460 4679792167 4678619457 +1450404000 5882160578 5882712419 7603010682 7604377301 +1450396800 9260356538 9260862152 11001185977 10965453991 +1450389600 9782675282 9783980589 10717442053 10681292115 +1450382400 9348727101 9350412057 10364157037 10372015551 +1450375200 9386088851 9387873296 10294628657 10262504144 +1450368000 8850774254 8852360459 9866141072 9829522994 +1450360800 8428617481 8429865572 9393915635 9391593379 +1450353600 8200423454 8201784620 9283261657 9253477710 +1450346400 6853132946 6854347674 8337394671 8322424184 +1450339200 4339924263 4340771462 5479321012 5479381863 +1450332000 3625643873 3626353180 4218444383 4220581884 +1450324800 4179606713 4180366726 5079221060 5077411921 +1450317600 6315127130 6315641067 8347469447 8345510606 +1450310400 9520774008 9521489820 11196250084 11196553786 +1450303200 10489080058 10489625968 11483239867 11451874711 +1450296000 9911707633 9913294329 10930936955 10909275395 +1450288800 10124136152 10126396084 11560400914 11520942293 +1450281600 9391985610 9393777700 10365310484 10334121014 +1450274400 8692004809 8693497524 9430302895 9438697289 +1450267200 8423490061 8424986888 9324201238 9294313568 +1450260000 7021527344 7022777102 8735183854 8718498678 +1450252800 4116147445 4116725964 5660260957 5662277132 +1450245600 3539724134 3540291892 4212388592 4208448299 +1450238400 4133011382 4133488349 5097402204 5099030315 +1450231200 6254758958 6255309966 8389172104 8393621133 +1450224000 9614645675 9614806089 11091378018 11061834434 +1450216800 10741748419 10740366679 11803481549 11764443048 +1450209600 10091968125 10092137317 10997968728 10982367445 +1450202400 9775273940 9776391996 10742498493 10701291327 +1450195200 9487912447 9489416308 10378729721 10348328388 +1450188000 9091430519 9092846100 10295782773 10295813441 +1450180800 8523631685 8524981354 9659076572 9644859247 +1450173600 6978739651 6980117126 8401890794 8378508957 +1450166400 4380389772 4381195903 5998624167 6004632833 +1450159200 3579162765 3579578228 3981724622 3980869599 +1450152000 4349023587 4349455384 5100269053 5102283935 +1450144800 6312275626 6312390045 8205387407 8206858461 +1450137600 9677597007 9677960774 11118626034 11121737528 +1450130400 10769029170 10769123947 11800735078 11755516036 +1450123200 9933472761 9933990330 11435163500 11372441528 +1450116000 9451617866 9453234411 10308530705 10266053900 +1450108800 9146449166 9147790355 10075334614 10042725465 +1450101600 8627196098 8628643268 9325341452 9297254653 +1450094400 8242311379 8306062898 9231831522 9199932161 +1450087200 6782513298 6783592562 8383882193 8361061203 +1450080000 3884464182 3885218684 5251045882 5252540859 +1450072800 3099878263 3100357182 3435930011 3435543722 +1450065600 3836886806 3837392592 4807308110 4807054396 +1450058400 5938403025 5938954232 7769908405 7773546248 +1450051200 9132687060 9133435575 10491675178 10468106084 +1450044000 10215209718 10215602809 11101465009 11048386032 +1450036800 9449879373 9451163552 10355190915 10313331455 +1450029600 8726837097 8727811413 9668587496 9647452357 +1450022400 8438900585 8440453082 9270387288 9232327728 +1450015200 7953207823 7954838847 8750119702 8731024300 +1450008000 7403689772 7404823873 8430006031 8403808162 +1450000800 5607061198 5608109749 7106734943 7104960219 +1449993600 3900109083 3901069805 4378386902 4379445980 +1449986400 4626177967 4617572974 5195520083 5196229440 +1449979200 4997123838 4998191287 5803429839 5806352525 +1449972000 6982170069 6983069036 8823123605 8824781609 +1449964800 9702815837 9704466221 11267263960 11258146588 +1449957600 9919226611 9920943292 10796033105 10765522169 +1449950400 9294760360 9295951304 10107207299 10109726604 +1449943200 9151968098 9153451792 10645368546 10626007037 +1449936000 8789185198 8790773493 9723498478 9702852460 +1449928800 8317843432 8319561779 9364576899 9367839910 +1449921600 8080978454 8082659610 9136685342 9116325446 +1449914400 6238662013 6239888176 7554707346 7532491477 +1449907200 3819924347 3820755005 4566676163 4566894413 +1449900000 4002325169 4003121300 4502830939 4503267323 +1449892800 4647772349 4648422044 5485668362 5489920733 +1449885600 6681260561 6681928991 8616318802 8620531470 +1449878400 9828560399 9830065257 11239499797 11212839199 +1449871200 10145645671 10146859990 11349077081 11320897130 +1449864000 9441219656 9442747329 10488722204 10454333994 +1449856800 9451488889 9453120781 10214148158 10179728879 +1449849600 8962781906 8964163991 9797145288 9767013154 +1449842400 8567259201 8568723641 9365268673 9340433044 +1449835200 8228919806 8230528415 8968486702 8939238519 +1449828000 6896500609 6897202418 8370411773 8351298199 +1449820800 4112814627 4113776489 5528156538 5530297386 +1449813600 3656210031 3656937931 4242810699 4243210614 +1449806400 4258619403 4259215168 5373918151 5375819585 +1449799200 6271856896 6272519088 8313409453 8311996359 +1449792000 9740790897 9741841430 11106098073 11103567242 +1449784800 10875144242 10875412354 11858161863 11829350878 +1449777600 10378003701 10378597750 11527496853 11532157834 +1449770400 10032366035 10033844703 10927520781 10899654408 +1449763200 9594637655 9596440520 10702163195 10666835479 +1449756000 9434811568 9436957909 10476334339 10460284044 +1449748800 8986453387 8988337368 10135815543 10110796792 +1449741600 7462768847 7464261235 9491239443 9472461970 +1449734400 4362994901 4363956118 6167810384 6167644993 +1449727200 3566451654 3567154565 4420412112 4421700256 +1449720000 4160051414 4160669138 4940966729 4940770592 +1449712800 6206059368 6206871886 8176083529 8177109816 +1449705600 10078423200 10080193975 12312866339 12286780044 +1449698400 10735886967 10735070539 11848090557 11798563470 +1449691200 10212789178 10213975556 11388047778 11346022852 +1449684000 9877255744 9878556719 10820478010 10790130737 +1449676800 9520105420 9521359258 10449008047 10426549761 +1449669600 9118932429 9120505345 9822020560 9811902659 +1449662400 9609149542 9609065201 11010017573 10987351342 +1449655200 7952090355 7953161374 10558001554 10545746562 +1449648000 4338679460 4339406271 5879968690 5879629252 +1449640800 3765157267 3765983261 4422785655 4425003434 +1449633600 3954922443 3955497800 4735750510 4737196278 +1449626400 6368910285 6369775644 8604305655 8605832352 +1449619200 10386608676 10387719772 12213385044 12212490564 +1449612000 11419794414 11416848153 12816369158 12776955956 +1449604800 10422746446 10423536550 11812508845 11777652467 +1449597600 9656636414 9648068427 10609129178 10583136729 +1449590400 6337043660 6332687228 10048631822 10032577517 +1449532800 7574948364 7575572760 12013462948 11989047716 +1449446400 6811074935 6804618786 11048581234 11020024287 +1449360000 7092587491 7089851741 11103636908 11071556011 +1449273600 7410287559 7405000660 11004812243 10985985970 +1449187200 7640038011 7641689400 11699599616 11717985734 +1449100800 7552526246 7553472281 11268195367 11259762427 +1449014400 7663162244 7664327420 11921895423 11906352714 +1448928000 7420620457 7420994001 12140012402 12164704249 +1448841600 6951268902 6951852002 10939292968 10921324145 +1448755200 6782730199 6783585458 10338578531 10350182985 +1448668800 6987701155 6987615334 10639087743 10585927777 +1448582400 7370248651 7370857029 11171181220 11118814344 +1448496000 7750109210 7750720553 12485798780 12455052998 +1448409600 7492559036 7492510687 11430833261 11418432249 +1448323200 7597219279 7597697848 11921585944 11862514438 +1448236800 6959686601 6960097776 11716081923 11690485413 +1448150400 6968293521 6968967702 10754889348 10760662524 +1448064000 7445572143 7446150228 11012228762 10979810421 +1447977600 7741904145 7742703811 11661725705 11627530775 +1447891200 7710714086 7711283645 11675832831 11644946019 +1447804800 7950110887 7950594349 12193156458 12158868429 +1447718400 7379189252 7379639139 11256436044 11241468830 +1447632000 7053734319 7054481510 11062983807 11049003858 +1447545600 7215865915 7216145056 10880635624 10876756372 +1447459200 7811133422 7812108049 11947396657 11949988742 +1447372800 7724547066 7725637354 12139708279 12139234147 +1447286400 7916953076 7914535413 11678687986 11659529234 +1447200000 6884971533 6885019362 12240756289 12223177522 +1447113600 7419875547 7420021897 11677330935 11654570283 +1447027200 7051110134 7051919012 11055219960 11043981120 +1446940800 6969228498 6970017454 10510098329 10506638779 +1446854400 7623870990 7624930119 11037847049 11021768194 +1446768000 7333599022 7334318042 11199009833 11192855245 +1446681600 7179329516 7178988975 11415641042 11384157349 +1446595200 7243911144 7243666713 11336896086 11310977847 +1446508800 7099037605 7098949733 11193893832 11193020851 +1446422400 6502110307 6502260132 11092990390 11064894435 +1446336000 5921330791 5921209194 8213170818 8184248670 +1446249600 7224654391 7224763617 10773332446 10757834642 +1446163200 7483618185 7481547576 11722126342 11667734021 +1446076800 7613169254 7612330857 11723821452 11675118402 +1445990400 7900642775 7898793247 12413709016 12369941748 +1445904000 7609291285 7606347691 12448568377 12429873019 +1445817600 6281476023 6280797781 9828774399 9809491324 +1445731200 6120514695 6119787704 9601041594 9594801077 +1445644800 7076466095 7076324382 10799953542 10760674028 +1445558400 7018575045 7004741769 11348269957 11221026070 +1445472000 7365710928 7363776741 12001845132 11992186904 +1445385600 7122746721 7120587122 11183656494 11175045314 +1445299200 6909339017 6907273359 11051256778 11016559633 +1445212800 6333564187 6327571563 10593565525 10560822401 +1445126400 6412194784 6409136862 9773414796 9741718647 +1445040000 6784766576 6782544395 10538419642 10515018472 +1444953600 6949492928 6946612935 10508869232 10482539859 +1444867200 7333233682 7326810866 11380915120 11301251202 +1444780800 7049662804 7044368074 11571738870 11509577939 +1444694400 6697411204 6695802746 11212851101 11185675392 +1444608000 5998213828 5996195294 9789745109 9768665241 +1444521600 6246781792 6244706187 9981399624 9958322585 +1444435200 6735826393 6733353108 10823582184 10789551636 +1444348800 6574579230 6571531033 10135260861 10098692765 +1444262400 6652995333 6640864106 11174835652 11114401665 +1444176000 6928541828 6923511515 10968492322 10938902911 +1444089600 6853764339 6851578636 11135720777 11101211165 +1444003200 5977158505 5976291895 9875122909 9836586404 +1443916800 6052944575 6052364010 9417341662 9394168576 +1443830400 6667425856 6662772374 10958085618 10927384244 +1443744000 6601047651 6595614324 10388504644 10363556007 +1443657600 6721275231 6717427349 11419682219 11386349131 +1443571200 6479172902 6477719388 10903899291 10881985204 +1443484800 6316794865 6313828408 11055287844 11032158196 +1443398400 5783672981 5782310979 9948222390 9926319633 +1443312000 5717408119 5717373392 9154474258 9145332979 +1443225600 6652455476 6652069206 10671849432 10643232055 +1443139200 6499266713 6498492688 10266885990 10248144356 +1443052800 6483159273 6483007856 10145431788 10122897483 +1442966400 6656539166 6654933387 10951264279 10921620508 +1442880000 6515013228 6514724410 10687288490 10667752164 +1442793600 6221819320 6220188619 10044820582 10017337948 +1442707200 6598832693 6593290133 10247042075 10194203245 +1442620800 6501546333 6501665218 9667463469 9650005538 +1442534400 6367305916 6367301622 10017135438 10000442034 +1442448000 6161952560 6161194088 10261297254 10246378385 +1442361600 6278472139 6277639082 9818120575 9790293532 +1442275200 6249910777 6248812450 10128194273 10095400909 +1442188800 5814489927 5812431388 9683533897 9660667563 +1442102400 5711536797 5709749442 9022906694 8999447554 +1442016000 6084773911 6081433367 10109291094 10070769904 +1441929600 6208047591 6207081126 9466212812 9439181083 +1441843200 6592766010 6587334081 10562881191 10547477999 +1441756800 6014635339 6012650804 11228819777 11203590372 +1441670400 6062957496 6061030721 9955538314 9935277700 +1441584000 5629931226 5628503904 9713826389 9697956887 +1441497600 5511119238 5509594660 8916184744 8888707156 +1441411200 6189757494 6187003367 9964474330 10027584400 +1441324800 6072826196 6071630524 9951772226 9920379257 +1441238400 6114898792 6113518938 10102476281 10074352959 +1441152000 6635724920 6620874777 10390447835 10351382952 +1441065600 6250690062 6249168005 10411034793 10390651277 +1440979200 5617915253 5616404161 9900096294 9878804613 +1440892800 5562267298 5557237767 8605717090 8579944453 +1440806400 6080996712 6079583513 9591658641 9573748010 +1440720000 5951934899 5949910394 9628325918 9592218016 +1440633600 6203836145 6201293942 10212086690 10187000918 +1440547200 6382578659 6378829101 10151836316 10124424628 +1440460800 6689023585 6678761444 10440449640 10403152288 +1440374400 6097216764 6085012231 11161835416 11120514637 +1440288000 5509797340 5480582846 8733441852 8700339104 +1440201600 6162162834 6143349582 9460944362 9356957452 +1440115200 6616289592 6609926478 10539875290 10477389436 +1440028800 6483710151 6480746855 10688349882 10630790905 +1439942400 6362030697 6355716168 10471477836 10422492231 +1439856000 6224889883 6220399758 10098962123 10085979063 +1439769600 5740692832 5737678437 9345066935 9315954129 +1439683200 5583725626 5580908589 8734974192 8728407304 +1439596800 6002386499 5999859776 9312547801 9309058102 +1439510400 5734424738 5730236582 9272233266 9255456674 +1439424000 6480021920 6474574338 10487350089 10469765970 +1439337600 6070915538 6067463260 9966752345 9928405454 +1439251200 6500997903 6494254679 10658854178 10588770766 +1439164800 5705753764 5701938629 10001755595 9943150768 +1439078400 5554523514 5551023518 9211317651 9169132685 +1438992000 6057045184 6054401889 9413228111 9364865984 +1438905600 6614440395 6609852486 11106440908 11035626309 +1438819200 6823215686 6819866481 10237676716 10209400493 +1438732800 6969722573 6962639849 11132653370 11073739959 +1438646400 6299781984 6292198301 11044356993 10987615807 +1438560000 6153959805 6145831677 9940808656 9859495673 +1438473600 5681260433 5675168852 10076029046 10000077105 +1438387200 6364083408 6358251932 10115013107 10029426199 +1438300800 6266077431 6285066454 10076213613 10014043351 +1438214400 6555726398 6552390804 10273308576 10211600567 +1438128000 6550011035 6546710395 10969569900 10914550843 +1438041600 6375820580 6372905982 10645511765 10608757347 +1437955200 5805631014 5802469933 10131630423 10093376999 +1437868800 5267627243 5264503062 8363417415 8343664272 +1437782400 6012499923 6009528386 9023674013 8989369083 +1437696000 5922937552 5919896998 9173273729 9138367861 +1437609600 5932309694 5929142342 9514786131 9475642257 +1437523200 5997256206 5989621846 9713035713 9655577231 +1437436800 5829006790 5824367955 9637804036 9601783171 +1437350400 5196926817 5191494431 9131651427 9092665730 +1437264000 5150322509 5145979048 8311664444 8277883973 +1437177600 5742484079 5738024238 8196694492 8168950716 +1437091200 6218520083 6212986273 9294729691 9263439865 +1437004800 6172479239 6168294876 9173440248 9135134079 +1436918400 6120610280 6115932076 10569763415 10560853894 +1436832000 6046174404 6041111650 9196336727 9152288824 +1436745600 5417806535 5412378543 9173695523 9136068674 +1436659200 5315526152 5310344686 8024406473 7989945315 +1436572800 5740664617 5736401108 8607799561 8581476570 +1436486400 5863515067 5859485595 9167795998 9143178482 +1436400000 6468165637 6460794989 9931104593 9895468416 +1436313600 6637971214 6631789366 9994431394 9940755925 +1436227200 6570319903 6564491945 10050817040 10010940886 +1436140800 5846329623 5841057142 9651963719 9596984903 +1436054400 5615280541 5608536807 8678802350 8626494204 +1435968000 6221377768 6215826200 9156036586 9107831100 +1435881600 6523718184 6518045309 9987260181 9943531260 +1435795200 6434844320 6428362711 9855153006 9794173081 +1435708800 6315184741 6309008743 9530441262 9469596737 +1435622400 6330661048 6323886999 9829496340 9763079756 +1435536000 5634924769 5628819238 9508558489 9472125976 +1435449600 5645973174 5640483726 8685381771 8635261367 +1435363200 6103592656 6097727105 8935444509 8876050249 +1435276800 6553381454 6547252423 9693231996 9647245178 +1435190400 6411679691 6404418893 10030474215 9979637758 +1435104000 6278344456 6270852227 9699659182 9634092229 +1435017600 6261558512 6254691982 9987970963 9912843972 +1434931200 5991143885 5982235416 9975588840 9916729806 +1434844800 5625229699 5617653589 8637919673 8604008842 +1434758400 6290712296 6282628028 9041521873 8987588772 +1434672000 6235260437 6229788246 9364953320 9309708602 +1434585600 6394323213 6387026568 9974851129 9925259375 +1434499200 6070054064 6065757102 9989671743 9919401753 +1434412800 6158330681 6149697302 9677127818 9610209144 +1434326400 5640477537 5632077282 9860628270 9797971344 +1434240000 5649260624 5641124207 8334344176 8319246903 +1434153600 6173205035 6165325036 9509792245 9447606565 +1434067200 6220515853 6212987471 9358885971 9308572855 +1433980800 6495096526 6486767182 9406651141 9344328576 +1433894400 6148863927 6140796027 9454933144 9388628172 +1433808000 6066972629 6058276798 9298882396 9226130184 +1433721600 5457520513 5449271648 10729546299 10455455139 +1433635200 5374477900 5367733916 8349438740 8282813652 +1433548800 5893217840 5883610902 8851443514 8790568981 +1433462400 5829831400 5819743728 8928717971 8915168445 +1433376000 5891218740 5880778417 8988092465 8908957226 +1433289600 5995782093 5984164652 9104457084 9015701082 +1433203200 6192216859 6179137500 9111885586 9019586196 +1433116800 5556001740 5545745712 8295926921 8211891238 +1433030400 5510248827 5496826032 8279862570 8208436236 +1432944000 5813943585 5793172590 8278282133 8211694657 +1432857600 6008552197 5976123581 8959356914 8843176526 +1432771200 6293726742 6258352421 9119125033 9016681016 +1432684800 6526264456 6513616191 10654096329 10584072200 +1432598400 6483290407 6475254612 10525027519 10448078863 +1432512000 6339826050 6330063812 11033809345 10953642364 +1432425600 6367785951 6360073377 9779139800 9761768926 +1432339200 6310001472 6301251452 10263608037 10186492715 +1432252800 6438329348 6429021852 10375276527 10286800910 +1432166400 6599489584 6589774989 11033500080 10966948035 +1432080000 6669360794 6661864151 10901229602 10892381455 +1431993600 6676687895 6668556301 11032682253 10963506941 +1431907200 6762291051 6752032512 11529652428 11466638665 +1431820800 6320160063 6309102804 10171905524 10118162451 +1431734400 6268343072 6258964532 10581759505 10524286404 +1431648000 6408057953 6400001493 10135211139 10079905625 +1431561600 7015777881 7007821864 11798080375 11731239451 +1431475200 6705544062 6697843340 11848011109 11807210308 +1431388800 6608412372 6603746932 11338220082 11285101839 +1431302400 6761935036 6755173725 11795012748 11720144640 +1431216000 6388616593 6383058799 10752731803 10693189875 +1431129600 6743175342 6736870115 11124794723 11058279675 +1431043200 6309018943 6305208894 10872535983 10803432590 +1430956800 6682395718 6677000266 11033595053 11029212093 +1430870400 6792891939 6786487253 11452801090 11406068292 +1430784000 6750014112 6742641906 11199079709 11138876675 +1430697600 6594024304 6585475855 10715435704 10652897055 +1430611200 6760079724 6751638645 10306435526 10244746093 +1430524800 6485613380 6480017746 10755681097 10680973481 +1430438400 6531414313 6526056520 11010106791 11001414465 +1430352000 6664386900 6655024619 10677103277 10605844190 +1430265600 6889132636 6875476270 11002062980 10933154571 +1430179200 6714918668 6708876628 11140765189 11084042556 +1430092800 6657394585 6650327365 11525426664 11479723152 +1430006400 6793816868 6787686002 11201957177 11139051670 +1429920000 6419419401 6413829850 10901983654 10861798303 +1429833600 6128567419 6125577319 10675272083 10637718829 +1429747200 6315230379 6311508494 11384013978 11326721768 +1429660800 6203570881 6195331877 10817242555 10808891098 +1429574400 6314038475 6307392484 10533351907 10503861084 +1429488000 6315143493 6307540568 11270181239 11199770103 +1429401600 6115004987 6109785039 10595501254 10537219295 +1429315200 6471615381 6467315045 11275635300 11239647606 +1429228800 6332634768 6328052809 11110770724 11065556838 +1429142400 6819917154 6813758895 11535666746 11495510501 +1429056000 6550277303 6544126876 12292918144 12303560890 +1428969600 6523435011 6517547612 11836912344 11779480027 +1428883200 6599706378 6592637559 11181550063 11130811268 +1428796800 6396208694 6390372948 10953223468 10901999936 +1428710400 6686953247 6681299757 11192602430 11147669289 +1428624000 6660139483 6653378419 11766361071 11703012423 +1428537600 6655254140 6649815533 12575541264 12547311611 +1428451200 6518760703 6513048614 11848002210 11808468343 +1428364800 6003261843 5998741872 11558722703 11535033617 +1428278400 5969030890 5965186365 10534090465 10501817282 +1428192000 5851126236 5847242248 9782472233 9749207682 +1428105600 6543441000 6538740181 11651945533 11617940454 +1428019200 7260419403 7254900583 11661335510 11642579057 +1427932800 7020854695 7015983314 11933888655 11913568738 +1427846400 7283736450 7274386824 11896238466 11866463139 +1427760000 7513643207 7502042956 12322676295 12268558088 +1427673600 7057011136 7044097628 12453950806 12426187711 +1427587200 6775242487 6765049131 10793410148 10757334467 +1427500800 6823253373 6815685526 11425231386 11406414859 +1427414400 6065169614 6058741748 10780748714 10745018990 +1427328000 5936328335 5930993017 10513525209 10434858995 +1427241600 6593585234 6587561446 11503926609 11457903075 +1427155200 6522789232 6517403086 11700120796 11668975662 +1427068800 6354409413 6348315671 11364452164 11324776112 +1426982400 6144684715 6136294436 10725824438 10698628181 +1426896000 6631912212 6626508950 11263566073 11227430334 +1426809600 6733954917 6729486342 11972041885 11959409725 +1426723200 7119288322 7114258769 12408958411 12387955732 +1426636800 7248858567 7243340700 12461451993 12460177635 +1426550400 6860555936 6854446437 12188643046 12197836880 +1426464000 6499054422 6490387366 11593510581 11551113740 +1426377600 6403707533 6395686476 10976426194 10950304329 +1426291200 6464780750 6460439387 10555988505 10562260480 +1426204800 6805855612 6802110263 11072211865 11044766647 +1426118400 5842911208 5836759519 10241220051 10240739258 +1426032000 5871026687 5867055176 10618574220 10547709805 +1425945600 5581435905 5576405883 10110266389 10113270745 +1425859200 5531907695 5526082805 10120971510 10068527282 +1425772800 5537850892 5532492778 9478592293 9427734763 +1425686400 5494800917 5495161533 10049620771 9988982685 +1425600000 6160313911 6160589088 9890712481 9839937491 +1425513600 6900654654 6903191813 12081278045 12089815102 +1425427200 7355180946 7359462300 13134117258 13122654133 +1425340800 7021558773 7022306122 12782917254 12760657416 +1425254400 7055578476 7054188993 12013843073 12006793933 +1425168000 7093676223 7092654737 11461159634 11462475177 +1425081600 6957139152 6956722923 11473706057 11406651338 +1424995200 6887965100 6890999061 11718571312 11743170341 +1424908800 6712554325 6716477495 10537788383 10514618473 +1424822400 6876229956 6878864960 10848138221 10845503645 +1424736000 7135410625 7136334252 12290238452 12295528897 +1424649600 7317111131 7313637378 12517875645 12493944657 +1424563200 7069987080 7066883486 11316020226 11309925506 +1424476800 7304936296 7301064912 11654571999 11660010974 +1424390400 7332703986 7330379130 12167522478 12169562970 +1424304000 7399153741 7401391025 12028939863 11992141089 +1424217600 7011112236 7014448775 12141219404 12097007065 +1424131200 7352424666 7353989762 11708280603 11700302353 +1424044800 7152462972 7149465444 12039990859 11950248201 +1423958400 6771060065 6773208224 10809789176 10809587415 +1423872000 6952536259 6955484607 11117379127 11104419692 +1423785600 7058948387 7064428648 12507120328 12489612958 +1423699200 7425637328 7430956375 11252230647 11251224610 +1423612800 7035629539 7043174425 12104973931 12105268160 +1423526400 6846444126 6855873339 10926291841 10924065875 +1423440000 6826034140 6824921868 11466402438 11471425312 +1423353600 6792593147 6792853630 11045925429 11048160169 +1423267200 6752728980 6750253225 10691988363 10686582349 +1423180800 6851432829 6847485390 10997601549 10990897107 +1423094400 7123832279 7120820508 11428495514 11427692268 +1423008000 7184711570 7181830889 11858179336 11863197028 +1422921600 6573468451 6569755404 10688263863 10693523266 +1422835200 6457078300 6453061629 10516245625 10502147059 +1422748800 6422444321 6419898612 9960863036 9949138565 +1422662400 6670359222 6668173404 10970872592 10976004503 +1422576000 6690649172 6688318864 10452367072 10450282432 +1422489600 6785902184 6783228699 10987025216 10974877137 +1422403200 6627200995 6624604498 10717853780 10716104850 +1422316800 6485805441 6482846235 10197263616 10194373106 +1422230400 6561115633 6556700598 10807959570 10802253569 +1422144000 6045247849 6041978345 9440921337 9439187445 +1422057600 6599056959 6596534694 10030564801 10028857826 +1421971200 6779851004 6777755180 10407009743 10402631421 +1421884800 6741720737 6738795729 10889955663 10870982112 +1421798400 6485430548 6481224350 9907888935 9897297310 +1421712000 6785683937 6782500224 11490060268 11452066260 +1421625600 6690862054 6685062437 11237246325 11212739661 +1421539200 6759319490 6754158551 10847309935 10820281559 +1421452800 6846782206 6842062404 11030670810 10999506923 +1421366400 7171716654 7172691213 11352943719 11321598965 +1421280000 6963899160 6961266527 11228071731 11211097250 +1421193600 6815836074 6810098978 11631176952 11607409416 +1421107200 6316557801 6313435154 11218468418 11186207240 +1421020800 6748218450 6745207377 10971245310 10967552189 +1420934400 6451959738 6449255193 10561381095 10550590247 +1420848000 6322838723 6320020505 9912556759 9901400766 +1420761600 6001508442 5999748842 9915296266 9904213427 +1420675200 5615491143 5616634725 9643268032 9644502520 +1420588800 5812198902 5813523506 9604563398 9594017895 +1420502400 5847421413 5848895752 9138411161 9133414416 +1420416000 5811628269 5813062875 9107046260 9109980087 +1420329600 5767697643 5769114502 9223228456 9222049583 +1420243200 5830467491 5831710760 9168301467 9176731575 +1420156800 5482394243 5483714277 9086875263 9084929921 +1420070400 5217468069 5217550046 8221646897 8217038338 +1419984000 5702695669 5699865884 8601900921 8593992041 +1419897600 5833943636 5831111207 8906459620 8897076772 +1419811200 5608413767 5606140995 8588367321 8578698846 +1419724800 5449595103 5447297151 8339104056 8336518659 +1419638400 5457287220 5455572694 7512101643 7506004216 +1419552000 5363323946 5362183789 7387577893 7383981546 +1419465600 5069599961 5067591907 7344380402 7337455388 +1419379200 5433207111 5434378523 7787398414 7779793866 +1419292800 5633370137 5634193098 8201592831 8205402666 +1419206400 5232164831 5233527041 8178078887 8174048385 +1419120000 5233787238 5234717664 7433386285 7434943572 +1419033600 5702919637 5700697517 7972929405 7966662863 +1418947200 5917711112 5915831958 8489120107 8490346113 +1418860800 5682131934 5680022919 8662939094 8660751303 +1418774400 5746530349 5740437422 8663865726 8650535178 +1418688000 5522566199 5518789232 8108951059 8096349902 +1418601600 5446224680 5436734787 8236250569 8200134333 +1418515200 5395392956 5387440511 7741281162 7727492954 +1418428800 5505895467 5501266723 7860177342 7845628397 +1418342400 5751379126 5744448059 8560175052 8523959984 +1418256000 5879504719 5872139398 8830764620 8821190903 +1418169600 5741308339 5734582552 8200461012 8198934410 +1418083200 5615663287 5601680778 8533023856 8485147407 +1417996800 5470225928 5451998671 8285125322 8264444770 +1417910400 5271981789 5264043296 7604015120 7595927571 +1417824000 5436436445 5423865237 7779821362 7703337518 +1417737600 5474501128 5466908548 8015672104 8002889574 +1417651200 5387822246 5381458757 7805460518 7791539458 +1417564800 5335340333 5332818505 7903779321 7898558592 +1417478400 5354521805 5349538311 8121658919 8101486192 +1417392000 4943085229 4930845992 7676617529 7609282895 +1417305600 4978711722 4971463083 7209644613 7203134801 +1417219200 4949904172 4948521755 7034221870 7012634453 +1417132800 5087174273 5082416697 7910050321 7894603694 +1417046400 5352060705 5339847150 7885461879 7883387699 +1416960000 5353877249 5339504030 8103961010 8078976252 +1416873600 5351677610 5341766345 7949726015 7927374884 +1416787200 5120260914 5118557400 7770666044 7753639512 +1416700800 5076685984 5072834798 7180985305 7146573640 +1416614400 5383412626 5378682356 7383773370 7356207568 +1416528000 5371262525 5362754549 8017069332 7992933289 +1416441600 5556648375 5534296088 7887709167 7865594772 +1416355200 5599030552 5567805475 8207368485 8175302410 +1416268800 5506737566 5489267669 7943194332 7958347550 +1416182400 5205065218 5195825331 7700091959 7701640697 +1416096000 5107191195 5100894891 7561434821 7531109239 +1416009600 5279121246 5263912706 7724879943 7779924913 +1415923200 5421555131 5405443396 7887862837 7872226276 +1415836800 5373785345 5356959976 8090458912 8083353952 +1415750400 5398229765 5387446452 8421300662 8399671668 +1415664000 5620136615 5607639380 8607532343 8592314895 +1415577600 5298694043 5293434600 8081689467 8071381794 +1415491200 5252067584 5245970726 7743597902 7737908029 +1415404800 5425078697 5409017975 7966245626 8014889720 +1415318400 5200293467 5192935077 8452584953 8486026530 +1415232000 5190341794 5184003115 8070758247 8066750552 +1415145600 4973186700 4965411873 7761234537 7744368045 +1415059200 5359960861 5352313802 8021648831 8002098261 +1414972800 5122043805 5120263208 8086548234 8072096165 +1414886400 5125991307 5125782093 7680668412 7673753877 +1414800000 5091662581 5081963307 7138134675 7138477917 +1414713600 5319485098 5310356613 7782605511 7766890266 +1414627200 5352700222 5342060302 8011884488 8007667040 +1414540800 5500468952 5489129214 8260375689 8189465777 +1414454400 5520642287 5511067178 8796803836 8769742384 +1414368000 4862787485 4859200455 7050578548 7049639215 +1414281600 4807221992 4836443668 7082989848 7105292911 +1414195200 4940891935 4966518048 7102107451 7143278950 +1414108800 5134055550 5170372224 8323410861 8375638664 +1414022400 5128095979 5155516362 8013322181 8056169437 +1413936000 5206767712 5220174348 8161436409 8180111582 +1413849600 5127958952 5155257029 8040280800 8071634466 +1413763200 5001958213 5038743870 7561493774 7591343077 +1413676800 4980553341 5020224087 7195291347 7263826802 +1413590400 5234170678 5266188983 7893301389 7924270082 +1413504000 5163365048 5190600878 7828240448 7891571237 +1413417600 5223030544 5250161990 8338211253 8381310783 +1413331200 5130212709 5151840938 8240333607 8268829104 +1413244800 5119857687 5139012890 8388241877 8416507010 +1413158400 5291076775 5318337570 8845982613 8878839557 +1413072000 5228882977 5255382645 8351649740 8385183834 +1412985600 5232629873 5250133504 8854355350 8925764804 +1412899200 5247460111 5267180670 9059002330 9091115850 +1412812800 5350962719 5369746459 9005030992 9020911212 +1412726400 5298421981 5313020202 9233434536 9235139744 +1412640000 5241057210 5252442672 8717611463 8731857503 +1412553600 5307948650 5326291778 8202938796 8216615412 +1412467200 4997122956 5013567900 8383808874 8400686197 +1412380800 5261280165 5283830309 8573689254 8584648057 +1412294400 5060312365 5080966578 8620569118 8638110400 +1412208000 5158159310 5181245623 8673245815 8702089219 +1412121600 5027810991 5052641079 9021942020 9042599309 +1412035200 5177414529 5203278088 8798383561 8826739900 +1411948800 5325258362 5353857666 9066958521 9091467652 +1411862400 5034441039 5061065970 7884527389 7912322743 +1411776000 5098037871 5125049169 8233261862 8268810216 +1411689600 4931581658 4951644736 8612903286 8634342386 +1411603200 4993694665 5085432487 8521403329 8665611071 +1411516800 5127361796 5220818287 8533013242 8676777287 +1411430400 5162088902 5254325230 8773566994 8945061119 +1411344000 5130969252 5238382041 8020714460 8196482251 +1411257600 5348112756 5455365169 8349937485 8524104297 +1411171200 5510969009 5569223647 9076958727 9247343015 +1411084800 5095710757 5095559494 8747830587 8753679488 +1410998400 5084072924 5084320971 8904170172 8910282943 +1410912000 4858850007 4858809337 8771250906 8756917002 +1410825600 5042835122 5043163082 9026075672 9024379596 +1410739200 5117546558 5117188208 8902527376 8898263066 +1410652800 4825138093 4824721367 8035017605 8037019379 +1410566400 4980640982 4980670139 8479767188 8473823542 +1410480000 4980936020 4981046632 8889732793 8884738608 +1410393600 4814023323 4814195994 8396335835 8391535709 +1410307200 4833479408 4833944023 8737926145 8742639237 +1410220800 4862954235 4863120383 9020631991 9015975586 +1410134400 4900877096 4900924175 8451683713 8440052515 +1410048000 4914901320 4915042440 8135680707 8131741818 +1409961600 4923952738 4924392642 8415280019 8412523981 +1409875200 4574355766 4574794639 7905042854 7905472706 +1409788800 4723982513 4724499908 7661229317 7666809306 +1409702400 4750243678 4750604476 8374643592 8374922455 +1409616000 4719973290 4720233828 8441866572 8435696149 +1409529600 4604604971 4604739820 8003070961 8006882145 +1409443200 4420484761 4420626473 7052409098 7049199624 +1409356800 4901669019 4902215564 8064978601 8059275641 +1409270400 4935657748 4936432626 8733720851 8733338857 +1409184000 5196051364 5196222221 8518381953 8519444520 +1409097600 5209960067 5210166435 8757758770 8756750734 +1409011200 5291133823 5290915802 8754504256 8748289349 +1408924800 5020008369 5018470284 9135752687 9100358428 +1408838400 4569667251 4569785660 7385974813 7383731786 +1408752000 4944304104 4944407429 7978362162 7980073290 +1408665600 5108434333 5108308163 8847827881 8844764071 +1408579200 5074321289 5074716922 8886553039 8880814834 +1408492800 5160520106 5161523477 8095275345 8093588042 +1408406400 5177774495 5177740854 9081611677 9073128655 +1408320000 4939419603 4938271607 9152570254 9124526025 +1408233600 4714644790 4714737368 7534467980 7531533883 +1408147200 4866552054 4867492790 8362004362 8365013070 +1408060800 5078660553 5078927376 8529043451 8519874860 +1407974400 5101053811 5101003531 8683488767 8682608018 +1407888000 5255573245 5254969951 8954273085 8945828053 +1407801600 5256256084 5255949882 9044011086 9042689228 +1407715200 5010428576 5010345046 8939938388 8927019441 +1407628800 4694575002 4694622572 7348159933 7351168741 +1407542400 4906679532 4906915651 8129581536 8125474565 +1407456000 4946308298 4946611354 8339975178 8342757894 +1407369600 4916654601 4917642789 7840781416 7843669090 +1407283200 5136350786 5136395853 8705336633 8695168985 +1407196800 4892350897 4891462801 8682943642 8655667589 +1407110400 4512274473 4513299143 7278745365 7279883585 +1407024000 4706008048 4706656128 7836658885 7835369550 +1406937600 4870375313 4870942150 7734103448 7732578409 +1406851200 4918737642 4918705490 8359287280 8350312857 +1406764800 4992562017 4992375474 8395735456 8390030601 +1406678400 4932258994 4931462177 8431792023 8423465299 +1406592000 5006142837 5005720200 8578608682 8579821256 +1406505600 4781641575 4781823096 8846073553 8839897486 +1406419200 4524489580 4525022936 7356124054 7357864463 +1406332800 4667369483 4668541932 7450795445 7451577669 +1406246400 4732331495 4733353014 7783651903 7781736049 +1406160000 4619202725 4620305433 8002431339 7994492733 +1406073600 4765201072 4766510554 8065224991 8066828315 +1405987200 4860909784 4861743070 8363425656 8365212717 +1405900800 4440696686 4441430686 8049537987 8050004342 +1405814400 4281017467 4281356829 6942169717 6941688608 +1405728000 4474161097 4475223601 7396007779 7391706680 +1405641600 4568717732 4569704119 7651302071 7653514908 +1405555200 4803721976 4804554188 8171947739 8174479369 +1405468800 4938643514 4938640400 8594291807 8592865241 +1405382400 4885152268 4885113579 8696154205 8695793114 +1405296000 4244595285 4244831748 7147576767 7148840709 +1405209600 4389897893 4390326143 7108848600 7106465205 +1405123200 4494196237 4494429854 7414478453 7408704056 +1405036800 4556734902 4556602378 7106398614 7102078282 +1404950400 4441947296 4442091493 7573868821 7572506277 +1404864000 4612744315 4612998419 7974414051 7973128983 +1404777600 4655327108 4655520590 7874625802 7880033537 +1404691200 4407541866 4407616238 8158265572 8161283621 +1404604800 4217668266 4217876633 7037901065 7039974435 +1404518400 4657660730 4658034211 7302877678 7304590972 +1404432000 4587989437 4588302106 7541122556 7547486405 +1404345600 4509746238 4510063370 7172917773 7177018209 +1404259200 4345702624 4345993057 6491952603 6492773389 +1404172800 4571913506 4572258210 7229575107 7232866466 +1404086400 4249985326 4250198395 7808045647 7810598576 +1404000000 4333469078 4333528487 6457684789 6455019679 +1403913600 4541721029 4542072098 7275469685 7273190271 +1403827200 4636661591 4637063150 7667083223 7667895961 +1403740800 4833716767 4833853231 7331181455 7332094100 +1403654400 4535273929 4535587368 7725524083 7725397676 +1403568000 4521849130 4521809427 8205711851 8205197932 +1403481600 4321850923 4321600515 7752536693 7745845652 +1403395200 4049362063 4049400664 6228180673 6226412151 +1403308800 4373430864 4373865966 6700942706 6702904373 +1403222400 4405467502 4405904581 6526923261 6524381956 +1403136000 4282696043 4283030541 7361852547 7354920670 +1403049600 4343341522 4343376318 7184314340 7188131874 +1402963200 4343175133 4343403336 7502632234 7508863410 +1402876800 4181997553 4182055080 7482028424 7477860760 +1402790400 4137030441 4137451397 6480894247 6477624161 +1402704000 4317612634 4318511771 6357053026 6359718201 +1402617600 4386987052 4387011262 7028197708 7034043549 +1402531200 4480480967 4480598388 7630892413 7627269186 +1402444800 4869108291 4869198122 8145346533 8130845459 +1402358400 4771275663 4771368814 8163421791 8154409475 +1402272000 5033194360 5031829579 9021027361 8994793635 +1402185600 4745035143 4744956688 7709853107 7703991503 +1402099200 5000769709 5000167430 8467638069 8467337396 +1402012800 4565217942 4565054439 7255667981 7251868958 +1401926400 4599360312 4599753266 7595216720 7595392697 +1401840000 4689536707 4689820229 7890178055 7894398498 +1401753600 4730745557 4730288569 7588175835 7578726328 +1401667200 4364944530 4364808354 6938203283 6928738762 +1401580800 4023387239 4023569878 6207023114 6204337841 +1401494400 4492867790 4493134567 6597266493 6596746372 +1401408000 4524530434 4525008334 7154409928 7158366707 +1401321600 4479778208 4480382091 6983527664 6985974380 +1401235200 4540302271 4540949737 7854284512 7848438189 +1401148800 4787467710 4787825308 7794888775 7790600657 +1401062400 4741386712 4741618350 8391041910 8390813874 +1400976000 4471871220 4472406984 6653203455 6654777071 +1400889600 4725841742 4726451250 7489566424 7489570346 +1400803200 4585977433 4586478365 8021200154 8018074059 +1400716800 4502273542 4502839172 8021543986 8018978796 +1400630400 4612634504 4613205158 7856637960 7853578479 +1400544000 4574244008 4583268569 8078931196 8087739970 +1400457600 4799733732 4803726225 8436691770 8432577154 +1400371200 4283756898 4284691579 7166605488 7168171230 +1400284800 4289798034 4290265027 7250737267 7250501532 +1400198400 4467233021 4467872181 7657873601 7655174703 +1400112000 4681274470 4681971927 7607620119 7611100324 +1400025600 4671577040 4672062028 8038789079 8045089427 +1399939200 4639668813 4639926532 7894416394 7889040633 +1399852800 4761305242 4760874469 8374025719 8358402871 +1399766400 4604989168 4605245552 7249585273 7255202775 +1399680000 4468018528 4468606665 7576398541 7581694310 +1399593600 4459799472 4460224144 7123498754 7130326515 +1399507200 4678904345 4679368593 8121578389 8121041669 +1399420800 4614059895 4595224589 7932573588 7937561636 +1399334400 4900225063 4900198992 7636200395 7635130433 +1399248000 4704851332 4703893661 7933677535 7914957558 +1399161600 4545333433 4545975988 7507912427 7508779580 +1399075200 4482598632 4483257879 7475969314 7469877135 +1398988800 4585863052 4586188276 7450293817 7454020680 +1398902400 4139192814 4139775645 5137443507 5138976026 +1398816000 4513544777 4513226040 7979965232 7981021832 +1398729600 4432897643 4433104989 8203150892 8205994666 +1398643200 4739465337 4738984038 8573425764 8544496186 +1398556800 4792229246 4791729037 8097002321 8079302379 +1398470400 4812469796 4812873502 7942072286 7943182315 +1398384000 4922743469 4923049813 8417763605 8417874324 +1398297600 5040450296 5040689937 8258795614 8256678166 +1398211200 5117738554 5117755177 8447463685 8446068502 +1398124800 4921226135 4918469545 8853190141 8798381637 +1398038400 4503149600 4501489245 7927562265 7915922281 +1397952000 4417582535 4416770304 7385457561 7380430937 +1397865600 4959290407 4955580958 8264618370 8222525915 +1397779200 5212518738 5208423239 8589501972 8550479822 +1397692800 4802221429 4801145995 7909749169 7905755331 +1397606400 4781693734 4780545881 8321598022 8315521089 +1397520000 4691190143 4690003663 7761418263 7753631566 +1397433600 4664855483 4663489686 7925431154 7922383701 +1397347200 4600805310 4599724941 7025781607 7029282663 +1397260800 4760493223 4759670108 7457595952 7456833237 +1397174400 4763202328 4762569954 8027723824 8034457090 +1397088000 4770471421 4769686630 7872822096 7869522245 +1397001600 4692106723 4691282646 8030101931 8030532603 +1396915200 4762334824 4761456785 7868020529 7870193891 +1396828800 4714641492 4713503843 8120411619 8124091116 +1396742400 4588916335 4588108841 7179408524 7177307217 +1396656000 4584810610 4584130208 7535301634 7537023069 +1396569600 4236442902 4235807793 7812973954 7816038543 +1396483200 4720863166 4720021357 7746512071 7749370056 +1396396800 4470952324 4470280181 7729131730 7725938397 +1396310400 4588889726 4587930320 7895342931 7887935073 +1396224000 4593750436 4592015924 8023280729 8017185477 +1396137600 4465719210 4464242590 7136243524 7134729991 +1396051200 4451862427 4451293663 7353809803 7352260615 +1395964800 4486473835 4485687707 7481602529 7480506703 +1395878400 4627170868 4626269086 7537597371 7535002608 +1395792000 4534539560 4533947328 7290961884 7297871688 +1395705600 4678399957 4677527409 7699989635 7701033487 +1395619200 4455876155 4454255269 7437489550 7426153788 +1395532800 4520489116 4519193155 7416214939 7410604526 +1395446400 4482014686 4481376716 7339203277 7334572746 +1395360000 4352947681 4352156784 7274676630 7275375060 +1395273600 3831066404 3830687722 6367771548 6366296944 +1395187200 4748243656 4747099647 7737314071 7734362280 +1395100800 4698015670 4696556208 7478133484 7471144090 +1395014400 4550213821 4549117817 7432455723 7431389903 +1394928000 4280032937 4279566908 6859491762 6857279460 +1394841600 4513460517 4513109067 6894395211 6896784785 +1394755200 4433736398 4433202559 7043572627 7037461315 +1394668800 4381275821 4380878070 7123261053 7119452536 +1394582400 4417740325 4417462725 7236862000 7236374894 +1394496000 4482335392 4481723466 7286634802 7282108398 +1394409600 4659216307 4657929552 7703570054 7703352427 +1394323200 4457249029 4456356374 7022592764 7019925453 +1394236800 4476211476 4475773032 7086285428 7086346223 +1394150400 4466529381 4466043880 7067926383 7072507954 +1394064000 4616985492 4616203780 7463063575 7455583782 +1393977600 4539711037 4538924655 7621093888 7622202319 +1393891200 4638175686 4636989784 7288577281 7284332218 +1393804800 5102974038 5097940104 8634081747 8572381958 +1393718400 4765296837 4761999326 7720662586 7690366372 +1393632000 4638474202 4637296640 7992412512 7973036092 +1393545600 4736546235 4734981992 8244390386 8218573208 +1393459200 4793870793 4792060935 8373589339 8347674904 +1393372800 4751717061 4750160203 8400352403 8375308429 +1393286400 4940100274 4936680600 8878227748 8832376620 +1393200000 5124830639 5119435349 8659835604 8601655629 +1393113600 4789310426 4784185175 8097365526 8048132548 +1393027200 4941111261 4938619330 8275441698 8239614219 +1392940800 5043704898 5039835902 8669125975 8626362664 +1392854400 5088787228 5084329699 8866466032 8853717232 +1392768000 5030179490 5025850398 8846967170 8802772913 +1392681600 4721376355 4714215978 9031908559 8950310602 +1392595200 4060508912 4057116262 7404625178 7382943024 +1392508800 3726714662 3723789248 6726442853 6689945633 +1392422400 4072026510 4068747383 6806212553 6769898763 +1392336000 4404894980 4404081578 7981062737 7973386319 +1392249600 4613381475 4612365548 7665614638 7657543848 +1392163200 4571371610 4570203896 8234118103 8220497988 +1392076800 4547355969 4546201109 8343814296 8336106420 +1391990400 4837498833 4834857379 8437072382 8407667130 +1391904000 4743972628 4740980557 7959820623 7928966862 +1391817600 4513926476 4511911812 8008859909 7985566550 +1391731200 4424350877 4423746196 7892860684 7888131434 +1391644800 4574504360 4584973631 7805699632 7817287110 +1391558400 4627181911 4636242617 8385018343 8379577108 +1391472000 4635664601 4644778933 8213203668 8216315085 +1391385600 4741039565 4748855576 8251320946 8248783071 +1391299200 4739393495 4747525371 7664471602 7650549496 +1391212800 4493824722 4503446173 7665193415 7671210192 +1391126400 4433601040 4444293105 7723494410 7731722989 +1391040000 4576218949 4585814959 7989104679 8003169010 +1390953600 4540466969 4550394209 7735509083 7744165955 +1390867200 4710831995 4716849260 8147737197 8104024670 +1390780800 5027426861 5030301098 8380758150 8352739757 +1390694400 4715804937 4721340094 8034321177 8016493581 +1390608000 4573850136 4581123528 7764086792 7753429289 +1390521600 4633774324 4645438682 7947866640 7914636824 +1390435200 4482896768 4500762043 7607072250 7611333503 +1390348800 4420455363 4438030694 7437224936 7435421563 +1390262400 4461223140 4472303803 7760813538 7768272302 +1390176000 4511817902 4519453402 7234213303 7228012736 +1390089600 4345283242 4354638466 6718776128 6728479805 +1390003200 4313714128 4322746238 7239803931 7242978693 +1389916800 4231094687 4239181754 7184737631 7162568180 +1389830400 4151983960 4158860372 7318966496 7314725247 +1389744000 4396477913 4396270850 7311442190 7284593737 +1389657600 4298577682 4299531481 7091032364 7049575993 +1389571200 4406269583 4406445055 6908949290 6895522527 +1389484800 4081811791 4085219563 6438040927 6432493237 +1389398400 4184120615 4187264494 6796273098 6778959471 +1389312000 4264760231 4267247601 7101710561 7092803210 +1389225600 4154661664 4157523397 7225975565 7219264479 +1389139200 4174702003 4177306059 7063743962 7031193992 +1389052800 4326670984 4328832471 7053678438 7049732656 +1388966400 4423228765 4424802624 6917553569 6921112721 +1388880000 4135254945 4137149923 6439877897 6431304560 +1388793600 4266081523 4269216206 6550902951 6558655092 +1388707200 4204838683 4208057182 6790521990 6789922970 +1388620800 3907722803 3910148701 6457407495 6453428194 +1388534400 3734915697 3738361133 6201296682 6205202943 +1388448000 3904979779 3908518565 6338191458 6341193655 +1388361600 3872370362 3874815354 6232774929 6235360545 +1388275200 3775369586 3778032807 6351498773 6414259128 +1388188800 3852597594 3855298798 6786038581 6774704169 +1388102400 3592758083 3595352539 5540956043 5491921284 +1388016000 3331610355 3333664239 4956165726 4950155178 +1387929600 3251002337 3253746065 5136563684 5133276362 +1387843200 3430394217 3433067966 5264023573 5269029212 +1387756800 3377122689 3379295144 5708070716 5710141998 +1387670400 3367064289 3369483942 5216710195 5221115067 +1387584000 3681240354 3684341573 5319992172 5325468453 +1387497600 3711216573 3714914295 5693553075 5698448600 +1387411200 3698848398 3702642071 5709095523 5714983242 +1387324800 3622462805 3626058583 5696772078 5702063474 +1387238400 3646462225 3650227683 5954970885 5960303622 +1387152000 3386586968 3389016008 5679890068 5688285360 +1387065600 3318371743 3321496546 4964123999 4968010255 +1386979200 3563585339 3566669991 5343297414 5346409302 +1386892800 3424983542 3427956941 5424308242 5427703474 +1386806400 3816327777 3819692169 5970113705 5974224557 +1386720000 3767437809 3770319301 6010650243 6013810725 +1386633600 3693736956 3697146698 6141370548 6147281058 +1386547200 3514125786 3516992262 5594645599 5597627175 +1386460800 3321300192 3324155156 5172554291 5179728421 +1386374400 3502796476 3506216970 5213485756 5220110413 diff --git a/data/oui/README b/data/oui/README deleted file mode 100644 index d6945419e..000000000 --- a/data/oui/README +++ /dev/null @@ -1,6 +0,0 @@ - -These were used by SEC when auto-emailing members leaking packets into the -peering LAN. This is done separately now but will be integrated back into -IXP Manager in time. - - - Barry 20121212 diff --git a/data/oui/oui-update.sh b/data/oui/oui-update.sh deleted file mode 100755 index e8eb1f1f2..000000000 --- a/data/oui/oui-update.sh +++ /dev/null @@ -1,12 +0,0 @@ -#! /bin/bash - - -wget -O ./oui-new.txt http://standards.ieee.org/regauth/oui/oui.txt - -if [[ $0 ]]; then - cat oui-new.txt | grep "(hex)" > oui-new-filtered.txt - cat oui-new-filtered.txt >oui.txt - rm oui-new-filtered.txt - rm oui-new.txt -fi - diff --git a/data/oui/oui.txt b/data/oui/oui.txt deleted file mode 100644 index ca60ccf5d..000000000 --- a/data/oui/oui.txt +++ /dev/null @@ -1,13361 +0,0 @@ -00-00-00 (hex) XEROX CORPORATION -00-00-01 (hex) XEROX CORPORATION -00-00-02 (hex) XEROX CORPORATION -00-00-03 (hex) XEROX CORPORATION -00-00-04 (hex) XEROX CORPORATION -00-00-05 (hex) XEROX CORPORATION -00-00-06 (hex) XEROX CORPORATION -00-00-07 (hex) XEROX CORPORATION -00-00-08 (hex) XEROX CORPORATION -00-00-09 (hex) XEROX CORPORATION -00-00-0A (hex) OMRON TATEISI ELECTRONICS CO. -00-00-0B (hex) MATRIX CORPORATION -00-00-0C (hex) CISCO SYSTEMS, INC. -00-00-0D (hex) FIBRONICS LTD. -00-00-0E (hex) FUJITSU LIMITED -00-00-0F (hex) NEXT, INC. -00-00-10 (hex) SYTEK INC. -00-00-11 (hex) NORMEREL SYSTEMES -00-00-12 (hex) INFORMATION TECHNOLOGY LIMITED -00-00-13 (hex) CAMEX -00-00-14 (hex) NETRONIX -00-00-15 (hex) DATAPOINT CORPORATION -00-00-16 (hex) DU PONT PIXEL SYSTEMS . -00-00-17 (hex) TEKELEC -00-00-18 (hex) WEBSTER COMPUTER CORPORATION -00-00-19 (hex) APPLIED DYNAMICS INTERNATIONAL -00-00-1A (hex) ADVANCED MICRO DEVICES -00-00-1B (hex) NOVELL INC. -00-00-1C (hex) BELL TECHNOLOGIES -00-00-1D (hex) CABLETRON SYSTEMS, INC. -00-00-1E (hex) TELSIST INDUSTRIA ELECTRONICA -00-00-1F (hex) Telco Systems, Inc. -00-00-20 (hex) DATAINDUSTRIER DIAB AB -00-00-21 (hex) SUREMAN COMP. & COMMUN. CORP. -00-00-22 (hex) VISUAL TECHNOLOGY INC. -00-00-23 (hex) ABB INDUSTRIAL SYSTEMS AB -00-00-24 (hex) CONNECT AS -00-00-25 (hex) RAMTEK CORP. -00-00-26 (hex) SHA-KEN CO., LTD. -00-00-27 (hex) JAPAN RADIO COMPANY -00-00-28 (hex) PRODIGY SYSTEMS CORPORATION -00-00-29 (hex) IMC NETWORKS CORP. -00-00-2A (hex) TRW - SEDD/INP -00-00-2B (hex) CRISP AUTOMATION, INC -00-00-2C (hex) AUTOTOTE LIMITED -00-00-2D (hex) CHROMATICS INC -00-00-2E (hex) SOCIETE EVIRA -00-00-2F (hex) TIMEPLEX INC. -00-00-30 (hex) VG LABORATORY SYSTEMS LTD -00-00-31 (hex) QPSX COMMUNICATIONS PTY LTD -00-00-32 (hex) Marconi plc -00-00-33 (hex) EGAN MACHINERY COMPANY -00-00-34 (hex) NETWORK RESOURCES CORPORATION -00-00-35 (hex) SPECTRAGRAPHICS CORPORATION -00-00-36 (hex) ATARI CORPORATION -00-00-37 (hex) OXFORD METRICS LIMITED -00-00-38 (hex) CSS LABS -00-00-39 (hex) TOSHIBA CORPORATION -00-00-3A (hex) CHYRON CORPORATION -00-00-3B (hex) i Controls, Inc. -00-00-3C (hex) AUSPEX SYSTEMS INC. -00-00-3D (hex) UNISYS -00-00-3E (hex) SIMPACT -00-00-3F (hex) SYNTREX, INC. -00-00-40 (hex) APPLICON, INC. -00-00-41 (hex) ICE CORPORATION -00-00-42 (hex) METIER MANAGEMENT SYSTEMS LTD. -00-00-43 (hex) MICRO TECHNOLOGY -00-00-44 (hex) CASTELLE CORPORATION -00-00-45 (hex) FORD AEROSPACE & COMM. CORP. -00-00-46 (hex) OLIVETTI NORTH AMERICA -00-00-47 (hex) NICOLET INSTRUMENTS CORP. -00-00-48 (hex) SEIKO EPSON CORPORATION -00-00-49 (hex) APRICOT COMPUTERS, LTD -00-00-4A (hex) ADC CODENOLL TECHNOLOGY CORP. -00-00-4B (hex) ICL DATA OY -00-00-4C (hex) NEC CORPORATION -00-00-4D (hex) DCI CORPORATION -00-00-4E (hex) AMPEX CORPORATION -00-00-4F (hex) LOGICRAFT, INC. -00-00-50 (hex) RADISYS CORPORATION -00-00-51 (hex) HOB ELECTRONIC GMBH & CO. KG -00-00-52 (hex) Intrusion.com, Inc. -00-00-53 (hex) COMPUCORP -00-00-54 (hex) MODICON, INC. -00-00-55 (hex) COMMISSARIAT A L`ENERGIE ATOM. -00-00-56 (hex) DR. B. STRUCK -00-00-57 (hex) SCITEX CORPORATION LTD. -00-00-58 (hex) RACORE COMPUTER PRODUCTS INC. -00-00-59 (hex) HELLIGE GMBH -00-00-5A (hex) SysKonnect GmbH -00-00-5B (hex) ELTEC ELEKTRONIK AG -00-00-5C (hex) TELEMATICS INTERNATIONAL INC. -00-00-5D (hex) CS TELECOM -00-00-5E (hex) USC INFORMATION SCIENCES INST -00-00-5F (hex) SUMITOMO ELECTRIC IND., LTD. -00-00-60 (hex) KONTRON ELEKTRONIK GMBH -00-00-61 (hex) GATEWAY COMMUNICATIONS -00-00-62 (hex) BULL HN INFORMATION SYSTEMS -00-00-63 (hex) BARCO CONTROL ROOMS GMBH -00-00-64 (hex) YOKOGAWA DIGITAL COMPUTER CORP -00-00-65 (hex) Network General Corporation -00-00-66 (hex) TALARIS SYSTEMS, INC. -00-00-67 (hex) SOFT * RITE, INC. -00-00-68 (hex) ROSEMOUNT CONTROLS -00-00-69 (hex) CONCORD COMMUNICATIONS INC -00-00-6A (hex) COMPUTER CONSOLES INC. -00-00-6B (hex) SILICON GRAPHICS INC./MIPS -00-00-6C (hex) PRIVATE -00-00-6D (hex) CRAY COMMUNICATIONS, LTD. -00-00-6E (hex) ARTISOFT, INC. -00-00-6F (hex) Madge Ltd. -00-00-70 (hex) HCL LIMITED -00-00-71 (hex) ADRA SYSTEMS INC. -00-00-72 (hex) MINIWARE TECHNOLOGY -00-00-73 (hex) SIECOR CORPORATION -00-00-74 (hex) RICOH COMPANY LTD. -00-00-75 (hex) Nortel Networks -00-00-76 (hex) ABEKAS VIDEO SYSTEM -00-00-77 (hex) INTERPHASE CORPORATION -00-00-78 (hex) LABTAM LIMITED -00-00-79 (hex) NETWORTH INCORPORATED -00-00-7A (hex) DANA COMPUTER INC. -00-00-7B (hex) RESEARCH MACHINES -00-00-7C (hex) AMPERE INCORPORATED -00-00-7D (hex) SUN MICROSYSTEMS, INC. -00-00-7E (hex) CLUSTRIX CORPORATION -00-00-7F (hex) LINOTYPE-HELL AG -00-00-80 (hex) CRAY COMMUNICATIONS A/S -00-00-81 (hex) BAY NETWORKS -00-00-82 (hex) LECTRA SYSTEMES SA -00-00-83 (hex) TADPOLE TECHNOLOGY PLC -00-00-84 (hex) SUPERNET -00-00-85 (hex) CANON INC. -00-00-86 (hex) MEGAHERTZ CORPORATION -00-00-87 (hex) HITACHI, LTD. -00-00-88 (hex) Brocade Communications Systems, Inc. -00-00-89 (hex) CAYMAN SYSTEMS INC. -00-00-8A (hex) DATAHOUSE INFORMATION SYSTEMS -00-00-8B (hex) INFOTRON -00-00-8C (hex) Alloy Computer Products (Australia) Pty Ltd -00-00-8D (hex) Cryptek Inc. -00-00-8E (hex) SOLBOURNE COMPUTER, INC. -00-00-8F (hex) Raytheon -00-00-90 (hex) MICROCOM -00-00-91 (hex) ANRITSU CORPORATION -00-00-92 (hex) COGENT DATA TECHNOLOGIES -00-00-93 (hex) PROTEON INC. -00-00-94 (hex) ASANTE TECHNOLOGIES -00-00-95 (hex) SONY TEKTRONIX CORP. -00-00-96 (hex) MARCONI ELECTRONICS LTD. -00-00-97 (hex) EMC Corporation -00-00-98 (hex) CROSSCOMM CORPORATION -00-00-99 (hex) MTX, INC. -00-00-9A (hex) RC COMPUTER A/S -00-00-9B (hex) INFORMATION INTERNATIONAL, INC -00-00-9C (hex) ROLM MIL-SPEC COMPUTERS -00-00-9D (hex) LOCUS COMPUTING CORPORATION -00-00-9E (hex) MARLI S.A. -00-00-9F (hex) AMERISTAR TECHNOLOGIES INC. -00-00-A0 (hex) SANYO Electric Co., Ltd. -00-00-A1 (hex) MARQUETTE ELECTRIC CO. -00-00-A2 (hex) BAY NETWORKS -00-00-A3 (hex) NETWORK APPLICATION TECHNOLOGY -00-00-A4 (hex) ACORN COMPUTERS LIMITED -00-00-A5 (hex) COMPATIBLE SYSTEMS CORP. -00-00-A6 (hex) NETWORK GENERAL CORPORATION -00-00-A7 (hex) NETWORK COMPUTING DEVICES INC. -00-00-A8 (hex) STRATUS COMPUTER INC. -00-00-A9 (hex) NETWORK SYSTEMS CORP. -00-00-AA (hex) XEROX CORPORATION -00-00-AB (hex) LOGIC MODELING CORPORATION -00-00-AC (hex) CONWARE COMPUTER CONSULTING -00-00-AD (hex) BRUKER INSTRUMENTS INC. -00-00-AE (hex) DASSAULT ELECTRONIQUE -00-00-AF (hex) NUCLEAR DATA INSTRUMENTATION -00-00-B0 (hex) RND-RAD NETWORK DEVICES -00-00-B1 (hex) ALPHA MICROSYSTEMS INC. -00-00-B2 (hex) TELEVIDEO SYSTEMS, INC. -00-00-B3 (hex) CIMLINC INCORPORATED -00-00-B4 (hex) EDIMAX COMPUTER COMPANY -00-00-B5 (hex) DATABILITY SOFTWARE SYS. INC. -00-00-B6 (hex) MICRO-MATIC RESEARCH -00-00-B7 (hex) DOVE COMPUTER CORPORATION -00-00-B8 (hex) SEIKOSHA CO., LTD. -00-00-B9 (hex) MCDONNELL DOUGLAS COMPUTER SYS -00-00-BA (hex) SIIG, INC. -00-00-BB (hex) TRI-DATA -00-00-BC (hex) Rockwell Automation -00-00-BD (hex) MITSUBISHI CABLE COMPANY -00-00-BE (hex) THE NTI GROUP -00-00-BF (hex) SYMMETRIC COMPUTER SYSTEMS -00-00-C0 (hex) WESTERN DIGITAL CORPORATION -00-00-C1 (hex) Madge Ltd. -00-00-C2 (hex) INFORMATION PRESENTATION TECH. -00-00-C3 (hex) HARRIS CORP COMPUTER SYS DIV -00-00-C4 (hex) WATERS DIV. OF MILLIPORE -00-00-C5 (hex) FARALLON COMPUTING/NETOPIA -00-00-C6 (hex) EON SYSTEMS -00-00-C7 (hex) ARIX CORPORATION -00-00-C8 (hex) ALTOS COMPUTER SYSTEMS -00-00-C9 (hex) Emulex Corporation -00-00-CA (hex) ARRIS International -00-00-CB (hex) COMPU-SHACK ELECTRONIC GMBH -00-00-CC (hex) DENSAN CO., LTD. -00-00-CD (hex) Allied Telesis Labs Ltd -00-00-CE (hex) MEGADATA CORP. -00-00-CF (hex) HAYES MICROCOMPUTER PRODUCTS -00-00-D0 (hex) DEVELCON ELECTRONICS LTD. -00-00-D1 (hex) ADAPTEC INCORPORATED -00-00-D2 (hex) SBE, INC. -00-00-D3 (hex) WANG LABORATORIES INC. -00-00-D4 (hex) PURE DATA LTD. -00-00-D5 (hex) MICROGNOSIS INTERNATIONAL -00-00-D6 (hex) PUNCH LINE HOLDING -00-00-D7 (hex) DARTMOUTH COLLEGE -00-00-D8 (hex) NOVELL, INC. -00-00-D9 (hex) NIPPON TELEGRAPH & TELEPHONE -00-00-DA (hex) ATEX -00-00-DB (hex) BRITISH TELECOMMUNICATIONS PLC -00-00-DC (hex) HAYES MICROCOMPUTER PRODUCTS -00-00-DD (hex) TCL INCORPORATED -00-00-DE (hex) CETIA -00-00-DF (hex) BELL & HOWELL PUB SYS DIV -00-00-E0 (hex) QUADRAM CORP. -00-00-E1 (hex) GRID SYSTEMS -00-00-E2 (hex) ACER TECHNOLOGIES CORP. -00-00-E3 (hex) INTEGRATED MICRO PRODUCTS LTD -00-00-E4 (hex) IN2 GROUPE INTERTECHNIQUE -00-00-E5 (hex) SIGMEX LTD. -00-00-E6 (hex) APTOR PRODUITS DE COMM INDUST -00-00-E7 (hex) STAR GATE TECHNOLOGIES -00-00-E8 (hex) ACCTON TECHNOLOGY CORP. -00-00-E9 (hex) ISICAD, INC. -00-00-EA (hex) UPNOD AB -00-00-EB (hex) MATSUSHITA COMM. IND. CO. LTD. -00-00-EC (hex) MICROPROCESS -00-00-ED (hex) APRIL -00-00-EE (hex) NETWORK DESIGNERS, LTD. -00-00-EF (hex) KTI -00-00-F0 (hex) SAMSUNG ELECTRONICS CO., LTD. -00-00-F1 (hex) MAGNA COMPUTER CORPORATION -00-00-F2 (hex) SPIDER COMMUNICATIONS -00-00-F3 (hex) GANDALF DATA LIMITED -00-00-F4 (hex) Allied Telesis -00-00-F5 (hex) DIAMOND SALES LIMITED -00-00-F6 (hex) APPLIED MICROSYSTEMS CORP. -00-00-F7 (hex) YOUTH KEEP ENTERPRISE CO LTD -00-00-F8 (hex) DIGITAL EQUIPMENT CORPORATION -00-00-F9 (hex) QUOTRON SYSTEMS INC. -00-00-FA (hex) MICROSAGE COMPUTER SYSTEMS INC -00-00-FB (hex) RECHNER ZUR KOMMUNIKATION -00-00-FC (hex) MEIKO -00-00-FD (hex) HIGH LEVEL HARDWARE -00-00-FE (hex) ANNAPOLIS MICRO SYSTEMS -00-00-FF (hex) CAMTEC ELECTRONICS LTD. -00-01-00 (hex) EQUIP'TRANS -00-01-01 (hex) PRIVATE -00-01-02 (hex) 3COM CORPORATION -00-01-03 (hex) 3COM CORPORATION -00-01-04 (hex) DVICO Co., Ltd. -00-01-05 (hex) Beckhoff Automation GmbH -00-01-06 (hex) Tews Datentechnik GmbH -00-01-07 (hex) Leiser GmbH -00-01-08 (hex) AVLAB Technology, Inc. -00-01-09 (hex) Nagano Japan Radio Co., Ltd. -00-01-0A (hex) CIS TECHNOLOGY INC. -00-01-0B (hex) Space CyberLink, Inc. -00-01-0C (hex) System Talks Inc. -00-01-0D (hex) CORECO, INC. -00-01-0E (hex) Bri-Link Technologies Co., Ltd -00-01-0F (hex) Brocade Communications Systems, Inc. -00-01-10 (hex) Gotham Networks -00-01-11 (hex) iDigm Inc. -00-01-12 (hex) Shark Multimedia Inc. -00-01-13 (hex) OLYMPUS CORPORATION -00-01-14 (hex) KANDA TSUSHIN KOGYO CO., LTD. -00-01-15 (hex) EXTRATECH CORPORATION -00-01-16 (hex) Netspect Technologies, Inc. -00-01-17 (hex) CANAL + -00-01-18 (hex) EZ Digital Co., Ltd. -00-01-19 (hex) RTUnet (Australia) -00-01-1A (hex) EEH DataLink GmbH -00-01-1B (hex) Unizone Technologies, Inc. -00-01-1C (hex) Universal Talkware Corporation -00-01-1D (hex) Centillium Communications -00-01-1E (hex) Precidia Technologies, Inc. -00-01-1F (hex) RC Networks, Inc. -00-01-20 (hex) OSCILLOQUARTZ S.A. -00-01-21 (hex) Watchguard Technologies, Inc. -00-01-22 (hex) Trend Communications, Ltd. -00-01-23 (hex) DIGITAL ELECTRONICS CORP. -00-01-24 (hex) Acer Incorporated -00-01-25 (hex) YAESU MUSEN CO., LTD. -00-01-26 (hex) PAC Labs -00-01-27 (hex) OPEN Networks Pty Ltd -00-01-28 (hex) EnjoyWeb, Inc. -00-01-29 (hex) DFI Inc. -00-01-2A (hex) Telematica Sistems Inteligente -00-01-2B (hex) TELENET Co., Ltd. -00-01-2C (hex) Aravox Technologies, Inc. -00-01-2D (hex) Komodo Technology -00-01-2E (hex) PC Partner Ltd. -00-01-2F (hex) Twinhead International Corp -00-01-30 (hex) Extreme Networks -00-01-31 (hex) Detection Systems, Inc. -00-01-32 (hex) Dranetz - BMI -00-01-33 (hex) KYOWA Electronic Instruments C -00-01-34 (hex) Selectron Systems AG -00-01-35 (hex) KDC Corp. -00-01-36 (hex) CyberTAN Technology, Inc. -00-01-37 (hex) IT Farm Corporation -00-01-38 (hex) XAVi Technologies Corp. -00-01-39 (hex) Point Multimedia Systems -00-01-3A (hex) SHELCAD COMMUNICATIONS, LTD. -00-01-3B (hex) BNA SYSTEMS -00-01-3C (hex) TIW SYSTEMS -00-01-3D (hex) RiscStation Ltd. -00-01-3E (hex) Ascom Tateco AB -00-01-3F (hex) Neighbor World Co., Ltd. -00-01-40 (hex) Sendtek Corporation -00-01-41 (hex) CABLE PRINT -00-01-42 (hex) Cisco Systems, Inc. -00-01-43 (hex) Cisco Systems, Inc. -00-01-44 (hex) EMC Corporation -00-01-45 (hex) WINSYSTEMS, INC. -00-01-46 (hex) Tesco Controls, Inc. -00-01-47 (hex) Zhone Technologies -00-01-48 (hex) X-traWeb Inc. -00-01-49 (hex) T.D.T. Transfer Data Test GmbH -00-01-4A (hex) Sony Corporation -00-01-4B (hex) Ennovate Networks, Inc. -00-01-4C (hex) Berkeley Process Control -00-01-4D (hex) Shin Kin Enterprises Co., Ltd -00-01-4E (hex) WIN Enterprises, Inc. -00-01-4F (hex) ADTRAN INC -00-01-50 (hex) GILAT COMMUNICATIONS, LTD. -00-01-51 (hex) Ensemble Communications -00-01-52 (hex) CHROMATEK INC. -00-01-53 (hex) ARCHTEK TELECOM CORPORATION -00-01-54 (hex) G3M Corporation -00-01-55 (hex) Promise Technology, Inc. -00-01-56 (hex) FIREWIREDIRECT.COM, INC. -00-01-57 (hex) SYSWAVE CO., LTD -00-01-58 (hex) Electro Industries/Gauge Tech -00-01-59 (hex) S1 Corporation -00-01-5A (hex) Digital Video Broadcasting -00-01-5B (hex) ITALTEL S.p.A/RF-UP-I -00-01-5C (hex) CADANT INC. -00-01-5D (hex) Sun Microsystems, Inc -00-01-5E (hex) BEST TECHNOLOGY CO., LTD. -00-01-5F (hex) DIGITAL DESIGN GmbH -00-01-60 (hex) ELMEX Co., LTD. -00-01-61 (hex) Meta Machine Technology -00-01-62 (hex) Cygnet Technologies, Inc. -00-01-63 (hex) Cisco Systems, Inc. -00-01-64 (hex) Cisco Systems, Inc. -00-01-65 (hex) AirSwitch Corporation -00-01-66 (hex) TC GROUP A/S -00-01-67 (hex) HIOKI E.E. CORPORATION -00-01-68 (hex) VITANA CORPORATION -00-01-69 (hex) Celestix Networks Pte Ltd. -00-01-6A (hex) ALITEC -00-01-6B (hex) LightChip, Inc. -00-01-6C (hex) FOXCONN -00-01-6D (hex) CarrierComm Inc. -00-01-6E (hex) Conklin Corporation -00-01-6F (hex) Inkel Corp. -00-01-70 (hex) ESE Embedded System Engineer'g -00-01-71 (hex) Allied Data Technologies -00-01-72 (hex) TechnoLand Co., LTD. -00-01-73 (hex) AMCC -00-01-74 (hex) CyberOptics Corporation -00-01-75 (hex) Radiant Communications Corp. -00-01-76 (hex) Orient Silver Enterprises -00-01-77 (hex) EDSL -00-01-78 (hex) MARGI Systems, Inc. -00-01-79 (hex) WIRELESS TECHNOLOGY, INC. -00-01-7A (hex) Chengdu Maipu Electric Industrial Co., Ltd. -00-01-7B (hex) Heidelberger Druckmaschinen AG -00-01-7C (hex) AG-E GmbH -00-01-7D (hex) ThermoQuest -00-01-7E (hex) ADTEK System Science Co., Ltd. -00-01-7F (hex) Experience Music Project -00-01-80 (hex) AOpen, Inc. -00-01-81 (hex) Nortel Networks -00-01-82 (hex) DICA TECHNOLOGIES AG -00-01-83 (hex) ANITE TELECOMS -00-01-84 (hex) SIEB & MEYER AG -00-01-85 (hex) Aloka Co., Ltd. -00-01-86 (hex) Uwe Disch -00-01-87 (hex) i2SE GmbH -00-01-88 (hex) LXCO Technologies ag -00-01-89 (hex) Refraction Technology, Inc. -00-01-8A (hex) ROI COMPUTER AG -00-01-8B (hex) NetLinks Co., Ltd. -00-01-8C (hex) Mega Vision -00-01-8D (hex) AudeSi Technologies -00-01-8E (hex) Logitec Corporation -00-01-8F (hex) Kenetec, Inc. -00-01-90 (hex) SMK-M -00-01-91 (hex) SYRED Data Systems -00-01-92 (hex) Texas Digital Systems -00-01-93 (hex) Hanbyul Telecom Co., Ltd. -00-01-94 (hex) Capital Equipment Corporation -00-01-95 (hex) Sena Technologies, Inc. -00-01-96 (hex) Cisco Systems, Inc. -00-01-97 (hex) Cisco Systems, Inc. -00-01-98 (hex) Darim Vision -00-01-99 (hex) HeiSei Electronics -00-01-9A (hex) LEUNIG GmbH -00-01-9B (hex) Kyoto Microcomputer Co., Ltd. -00-01-9C (hex) JDS Uniphase Inc. -00-01-9D (hex) E-Control Systems, Inc. -00-01-9E (hex) ESS Technology, Inc. -00-01-9F (hex) Phonex Broadband -00-01-A0 (hex) Infinilink Corporation -00-01-A1 (hex) Mag-Tek, Inc. -00-01-A2 (hex) Logical Co., Ltd. -00-01-A3 (hex) GENESYS LOGIC, INC. -00-01-A4 (hex) Microlink Corporation -00-01-A5 (hex) Nextcomm, Inc. -00-01-A6 (hex) Scientific-Atlanta Arcodan A/S -00-01-A7 (hex) UNEX TECHNOLOGY CORPORATION -00-01-A8 (hex) Welltech Computer Co., Ltd. -00-01-A9 (hex) BMW AG -00-01-AA (hex) Airspan Communications, Ltd. -00-01-AB (hex) Main Street Networks -00-01-AC (hex) Sitara Networks, Inc. -00-01-AD (hex) Coach Master International d.b.a. CMI Worldwide, Inc. -00-01-AE (hex) Trex Enterprises -00-01-AF (hex) Emerson Network Power -00-01-B0 (hex) Fulltek Technology Co., Ltd. -00-01-B1 (hex) General Bandwidth -00-01-B2 (hex) Digital Processing Systems, Inc. -00-01-B3 (hex) Precision Electronic Manufacturing -00-01-B4 (hex) Wayport, Inc. -00-01-B5 (hex) Turin Networks, Inc. -00-01-B6 (hex) SAEJIN T&M Co., Ltd. -00-01-B7 (hex) Centos, Inc. -00-01-B8 (hex) Netsensity, Inc. -00-01-B9 (hex) SKF Condition Monitoring -00-01-BA (hex) IC-Net, Inc. -00-01-BB (hex) Frequentis -00-01-BC (hex) Brains Corporation -00-01-BD (hex) Peterson Electro-Musical Products, Inc. -00-01-BE (hex) Gigalink Co., Ltd. -00-01-BF (hex) Teleforce Co., Ltd. -00-01-C0 (hex) CompuLab, Ltd. -00-01-C1 (hex) Vitesse Semiconductor Corporation -00-01-C2 (hex) ARK Research Corp. -00-01-C3 (hex) Acromag, Inc. -00-01-C4 (hex) NeoWave, Inc. -00-01-C5 (hex) Simpler Networks -00-01-C6 (hex) Quarry Technologies -00-01-C7 (hex) Cisco Systems, Inc. -00-01-C8 (hex) THOMAS CONRAD CORP. -00-01-C8 (hex) CONRAD CORP. -00-01-C9 (hex) Cisco Systems, Inc. -00-01-CA (hex) Geocast Network Systems, Inc. -00-01-CB (hex) EVR -00-01-CC (hex) Japan Total Design Communication Co., Ltd. -00-01-CD (hex) ARtem -00-01-CE (hex) Custom Micro Products, Ltd. -00-01-CF (hex) Alpha Data Parallel Systems, Ltd. -00-01-D0 (hex) VitalPoint, Inc. -00-01-D1 (hex) CoNet Communications, Inc. -00-01-D2 (hex) MacPower Peripherals, Ltd. -00-01-D3 (hex) PAXCOMM, Inc. -00-01-D4 (hex) Leisure Time, Inc. -00-01-D5 (hex) HAEDONG INFO & COMM CO., LTD -00-01-D6 (hex) manroland AG -00-01-D7 (hex) F5 Networks, Inc. -00-01-D8 (hex) Teltronics, Inc. -00-01-D9 (hex) Sigma, Inc. -00-01-DA (hex) WINCOMM Corporation -00-01-DB (hex) Freecom Technologies GmbH -00-01-DC (hex) Activetelco -00-01-DD (hex) Avail Networks -00-01-DE (hex) Trango Systems, Inc. -00-01-DF (hex) ISDN Communications, Ltd. -00-01-E0 (hex) Fast Systems, Inc. -00-01-E1 (hex) Kinpo Electronics, Inc. -00-01-E2 (hex) Ando Electric Corporation -00-01-E3 (hex) Siemens AG -00-01-E4 (hex) Sitera, Inc. -00-01-E5 (hex) Supernet, Inc. -00-01-E6 (hex) Hewlett-Packard Company -00-01-E7 (hex) Hewlett-Packard Company -00-01-E8 (hex) Force10 Networks, Inc. -00-01-E9 (hex) Litton Marine Systems B.V. -00-01-EA (hex) Cirilium Corp. -00-01-EB (hex) C-COM Corporation -00-01-EC (hex) Ericsson Group -00-01-ED (hex) SETA Corp. -00-01-EE (hex) Comtrol Europe, Ltd. -00-01-EF (hex) Camtel Technology Corp. -00-01-F0 (hex) Tridium, Inc. -00-01-F1 (hex) Innovative Concepts, Inc. -00-01-F2 (hex) Mark of the Unicorn, Inc. -00-01-F3 (hex) QPS, Inc. -00-01-F4 (hex) Enterasys Networks -00-01-F5 (hex) ERIM S.A. -00-01-F6 (hex) Association of Musical Electronics Industry -00-01-F7 (hex) Image Display Systems, Inc. -00-01-F8 (hex) Adherent Systems, Ltd. -00-01-F9 (hex) TeraGlobal Communications Corp. -00-01-FA (hex) HOROSCAS -00-01-FB (hex) DoTop Technology, Inc. -00-01-FC (hex) Keyence Corporation -00-01-FD (hex) Digital Voice Systems, Inc. -00-01-FE (hex) DIGITAL EQUIPMENT CORPORATION -00-01-FF (hex) Data Direct Networks, Inc. -00-02-00 (hex) Net & Sys Co., Ltd. -00-02-01 (hex) IFM Electronic gmbh -00-02-02 (hex) Amino Communications, Ltd. -00-02-03 (hex) Woonsang Telecom, Inc. -00-02-04 (hex) Bodmann Industries Elektronik GmbH -00-02-05 (hex) Hitachi Denshi, Ltd. -00-02-06 (hex) Telital R&D Denmark A/S -00-02-07 (hex) VisionGlobal Network Corp. -00-02-08 (hex) Unify Networks, Inc. -00-02-09 (hex) Shenzhen SED Information Technology Co., Ltd. -00-02-0A (hex) Gefran Spa -00-02-0B (hex) Native Networks, Inc. -00-02-0C (hex) Metro-Optix -00-02-0D (hex) Micronpc.com -00-02-0E (hex) ECI Telecom, Ltd., NSD-US -00-02-0F (hex) AATR -00-02-10 (hex) Fenecom -00-02-11 (hex) Nature Worldwide Technology Corp. -00-02-12 (hex) SierraCom -00-02-13 (hex) S.D.E.L. -00-02-14 (hex) DTVRO -00-02-15 (hex) Cotas Computer Technology A/B -00-02-16 (hex) Cisco Systems, Inc. -00-02-17 (hex) Cisco Systems, Inc. -00-02-18 (hex) Advanced Scientific Corp -00-02-19 (hex) Paralon Technologies -00-02-1A (hex) Zuma Networks -00-02-1B (hex) Kollmorgen-Servotronix -00-02-1C (hex) Network Elements, Inc. -00-02-1D (hex) Data General Communication Ltd. -00-02-1E (hex) SIMTEL S.R.L. -00-02-1F (hex) Aculab PLC -00-02-20 (hex) Canon Aptex, Inc. -00-02-21 (hex) DSP Application, Ltd. -00-02-22 (hex) Chromisys, Inc. -00-02-23 (hex) ClickTV -00-02-24 (hex) C-COR -00-02-25 (hex) One Stop Systems -00-02-26 (hex) XESystems, Inc. -00-02-27 (hex) ESD Electronic System Design GmbH -00-02-28 (hex) Necsom, Ltd. -00-02-29 (hex) Adtec Corporation -00-02-2A (hex) Asound Electronic -00-02-2B (hex) SAXA, Inc. -00-02-2C (hex) ABB Bomem, Inc. -00-02-2D (hex) Agere Systems -00-02-2E (hex) TEAC Corp. R& D -00-02-2F (hex) P-Cube, Ltd. -00-02-30 (hex) Intersoft Electronics -00-02-31 (hex) Ingersoll-Rand -00-02-32 (hex) Avision, Inc. -00-02-33 (hex) Mantra Communications, Inc. -00-02-34 (hex) Imperial Technology, Inc. -00-02-35 (hex) Paragon Networks International -00-02-36 (hex) INIT GmbH -00-02-37 (hex) Cosmo Research Corp. -00-02-38 (hex) Serome Technology, Inc. -00-02-39 (hex) Visicom -00-02-3A (hex) ZSK Stickmaschinen GmbH -00-02-3B (hex) Redback Networks -00-02-3C (hex) Creative Technology, Ltd. -00-02-3D (hex) NuSpeed, Inc. -00-02-3E (hex) Selta Telematica S.p.a -00-02-3F (hex) Compal Electronics, Inc. -00-02-40 (hex) Seedek Co., Ltd. -00-02-41 (hex) Amer.com -00-02-42 (hex) Videoframe Systems -00-02-43 (hex) Raysis Co., Ltd. -00-02-44 (hex) SURECOM Technology Co. -00-02-45 (hex) Lampus Co, Ltd. -00-02-46 (hex) All-Win Tech Co., Ltd. -00-02-47 (hex) Great Dragon Information Technology (Group) Co., Ltd. -00-02-48 (hex) Pilz GmbH & Co. -00-02-49 (hex) Aviv Infocom Co, Ltd. -00-02-4A (hex) Cisco Systems, Inc. -00-02-4B (hex) Cisco Systems, Inc. -00-02-4C (hex) SiByte, Inc. -00-02-4D (hex) Mannesman Dematic Colby Pty. Ltd. -00-02-4E (hex) Datacard Group -00-02-4F (hex) IPM Datacom S.R.L. -00-02-50 (hex) Geyser Networks, Inc. -00-02-51 (hex) Soma Networks, Inc. -00-02-52 (hex) Carrier Corporation -00-02-53 (hex) Televideo, Inc. -00-02-54 (hex) WorldGate -00-02-55 (hex) IBM Corporation -00-02-56 (hex) Alpha Processor, Inc. -00-02-57 (hex) Microcom Corp. -00-02-58 (hex) Flying Packets Communications -00-02-59 (hex) Tsann Kuen China (Shanghai)Enterprise Co., Ltd. IT Group -00-02-5A (hex) Catena Networks -00-02-5B (hex) Cambridge Silicon Radio -00-02-5C (hex) SCI Systems (Kunshan) Co., Ltd. -00-02-5D (hex) Calix Networks -00-02-5E (hex) High Technology Ltd -00-02-5F (hex) Nortel Networks -00-02-60 (hex) Accordion Networks, Inc. -00-02-61 (hex) Tilgin AB -00-02-62 (hex) Soyo Group Soyo Com Tech Co., Ltd -00-02-63 (hex) UPS Manufacturing SRL -00-02-64 (hex) AudioRamp.com -00-02-65 (hex) Virditech Co. Ltd. -00-02-66 (hex) Thermalogic Corporation -00-02-67 (hex) NODE RUNNER, INC. -00-02-68 (hex) Harris Government Communications -00-02-69 (hex) Nadatel Co., Ltd -00-02-6A (hex) Cocess Telecom Co., Ltd. -00-02-6B (hex) BCM Computers Co., Ltd. -00-02-6C (hex) Philips CFT -00-02-6D (hex) Adept Telecom -00-02-6E (hex) NeGeN Access, Inc. -00-02-6F (hex) Senao International Co., Ltd. -00-02-70 (hex) Crewave Co., Ltd. -00-02-71 (hex) Vpacket Communications -00-02-72 (hex) CC&C Technologies, Inc. -00-02-73 (hex) Coriolis Networks -00-02-74 (hex) Tommy Technologies Corp. -00-02-75 (hex) SMART Technologies, Inc. -00-02-76 (hex) Primax Electronics Ltd. -00-02-77 (hex) Cash Systemes Industrie -00-02-78 (hex) Samsung Electro-Mechanics Co., Ltd. -00-02-79 (hex) Control Applications, Ltd. -00-02-7A (hex) IOI Technology Corporation -00-02-7B (hex) Amplify Net, Inc. -00-02-7C (hex) Trilithic, Inc. -00-02-7D (hex) Cisco Systems, Inc. -00-02-7E (hex) Cisco Systems, Inc. -00-02-7F (hex) ask-technologies.com -00-02-80 (hex) Mu Net, Inc. -00-02-81 (hex) Madge Ltd. -00-02-82 (hex) ViaClix, Inc. -00-02-83 (hex) Spectrum Controls, Inc. -00-02-84 (hex) AREVA T&D -00-02-85 (hex) Riverstone Networks -00-02-86 (hex) Occam Networks -00-02-87 (hex) Adapcom -00-02-88 (hex) GLOBAL VILLAGE COMMUNICATION -00-02-89 (hex) DNE Technologies -00-02-8A (hex) Ambit Microsystems Corporation -00-02-8B (hex) VDSL Systems OY -00-02-8C (hex) Micrel-Synergy Semiconductor -00-02-8D (hex) Movita Technologies, Inc. -00-02-8E (hex) Rapid 5 Networks, Inc. -00-02-8F (hex) Globetek, Inc. -00-02-90 (hex) Woorigisool, Inc. -00-02-91 (hex) Open Network Co., Ltd. -00-02-92 (hex) Logic Innovations, Inc. -00-02-93 (hex) Solid Data Systems -00-02-94 (hex) Tokyo Sokushin Co., Ltd. -00-02-95 (hex) IP.Access Limited -00-02-96 (hex) Lectron Co,. Ltd. -00-02-97 (hex) C-COR.net -00-02-98 (hex) Broadframe Corporation -00-02-99 (hex) Apex, Inc. -00-02-9A (hex) Storage Apps -00-02-9B (hex) Kreatel Communications AB -00-02-9C (hex) 3COM -00-02-9D (hex) Merix Corp. -00-02-9E (hex) Information Equipment Co., Ltd. -00-02-9F (hex) L-3 Communication Aviation Recorders -00-02-A0 (hex) Flatstack Ltd. -00-02-A1 (hex) World Wide Packets -00-02-A2 (hex) Hilscher GmbH -00-02-A3 (hex) ABB Switzerland Ltd, Power Systems -00-02-A4 (hex) AddPac Technology Co., Ltd. -00-02-A5 (hex) Hewlett Packard -00-02-A6 (hex) Effinet Systems Co., Ltd. -00-02-A7 (hex) Vivace Networks -00-02-A8 (hex) Air Link Technology -00-02-A9 (hex) RACOM, s.r.o. -00-02-AA (hex) PLcom Co., Ltd. -00-02-AB (hex) CTC Union Technologies Co., Ltd. -00-02-AC (hex) 3PAR data -00-02-AD (hex) HOYA Corporation -00-02-AE (hex) Scannex Electronics Ltd. -00-02-AF (hex) TeleCruz Technology, Inc. -00-02-B0 (hex) Hokubu Communication & Industrial Co., Ltd. -00-02-B1 (hex) Anritsu, Ltd. -00-02-B2 (hex) Cablevision -00-02-B3 (hex) Intel Corporation -00-02-B4 (hex) DAPHNE -00-02-B5 (hex) Avnet, Inc. -00-02-B6 (hex) Acrosser Technology Co., Ltd. -00-02-B7 (hex) Watanabe Electric Industry Co., Ltd. -00-02-B8 (hex) WHI KONSULT AB -00-02-B9 (hex) Cisco Systems, Inc. -00-02-BA (hex) Cisco Systems, Inc. -00-02-BB (hex) Continuous Computing Corp -00-02-BC (hex) LVL 7 Systems, Inc. -00-02-BD (hex) Bionet Co., Ltd. -00-02-BE (hex) Totsu Engineering, Inc. -00-02-BF (hex) dotRocket, Inc. -00-02-C0 (hex) Bencent Tzeng Industry Co., Ltd. -00-02-C1 (hex) Innovative Electronic Designs, Inc. -00-02-C2 (hex) Net Vision Telecom -00-02-C3 (hex) Arelnet Ltd. -00-02-C4 (hex) Vector International BVBA -00-02-C5 (hex) Evertz Microsystems Ltd. -00-02-C6 (hex) Data Track Technology PLC -00-02-C7 (hex) ALPS ELECTRIC Co., Ltd. -00-02-C8 (hex) Technocom Communications Technology (pte) Ltd -00-02-C9 (hex) Mellanox Technologies -00-02-CA (hex) EndPoints, Inc. -00-02-CB (hex) TriState Ltd. -00-02-CC (hex) M.C.C.I -00-02-CD (hex) TeleDream, Inc. -00-02-CE (hex) FoxJet, Inc. -00-02-CF (hex) ZyGate Communications, Inc. -00-02-D0 (hex) Comdial Corporation -00-02-D1 (hex) Vivotek, Inc. -00-02-D2 (hex) Workstation AG -00-02-D3 (hex) NetBotz, Inc. -00-02-D4 (hex) PDA Peripherals, Inc. -00-02-D5 (hex) ACR -00-02-D6 (hex) NICE Systems -00-02-D7 (hex) EMPEG Ltd -00-02-D8 (hex) BRECIS Communications Corporation -00-02-D9 (hex) Reliable Controls -00-02-DA (hex) ExiO Communications, Inc. -00-02-DB (hex) NETSEC -00-02-DC (hex) Fujitsu General Limited -00-02-DD (hex) Bromax Communications, Ltd. -00-02-DE (hex) Astrodesign, Inc. -00-02-DF (hex) Net Com Systems, Inc. -00-02-E0 (hex) ETAS GmbH -00-02-E1 (hex) Integrated Network Corporation -00-02-E2 (hex) NDC Infared Engineering -00-02-E3 (hex) LITE-ON Communications, Inc. -00-02-E4 (hex) JC HYUN Systems, Inc. -00-02-E5 (hex) Timeware Ltd. -00-02-E6 (hex) Gould Instrument Systems, Inc. -00-02-E7 (hex) CAB GmbH & Co KG -00-02-E8 (hex) E.D.&A. -00-02-E9 (hex) CS Systemes De Securite - C3S -00-02-EA (hex) Focus Enhancements -00-02-EB (hex) Pico Communications -00-02-EC (hex) Maschoff Design Engineering -00-02-ED (hex) DXO Telecom Co., Ltd. -00-02-EE (hex) Nokia Danmark A/S -00-02-EF (hex) CCC Network Systems Group Ltd. -00-02-F0 (hex) AME Optimedia Technology Co., Ltd. -00-02-F1 (hex) Pinetron Co., Ltd. -00-02-F2 (hex) eDevice, Inc. -00-02-F3 (hex) Media Serve Co., Ltd. -00-02-F4 (hex) PCTEL, Inc. -00-02-F5 (hex) VIVE Synergies, Inc. -00-02-F6 (hex) Equipe Communications -00-02-F7 (hex) ARM -00-02-F8 (hex) SEAKR Engineering, Inc. -00-02-F9 (hex) Mimos Semiconductor SDN BHD -00-02-FA (hex) DX Antenna Co., Ltd. -00-02-FB (hex) Baumuller Aulugen-Systemtechnik GmbH -00-02-FC (hex) Cisco Systems, Inc. -00-02-FD (hex) Cisco Systems, Inc. -00-02-FE (hex) Viditec, Inc. -00-02-FF (hex) Handan BroadInfoCom -00-03-00 (hex) NetContinuum, Inc. -00-03-01 (hex) Avantas Networks Corporation -00-03-02 (hex) Charles Industries, Ltd. -00-03-03 (hex) JAMA Electronics Co., Ltd. -00-03-04 (hex) Pacific Broadband Communications -00-03-05 (hex) MSC Vertriebs GmbH -00-03-06 (hex) Fusion In Tech Co., Ltd. -00-03-07 (hex) Secure Works, Inc. -00-03-08 (hex) AM Communications, Inc. -00-03-09 (hex) Texcel Technology PLC -00-03-0A (hex) Argus Technologies -00-03-0B (hex) Hunter Technology, Inc. -00-03-0C (hex) Telesoft Technologies Ltd. -00-03-0D (hex) Uniwill Computer Corp. -00-03-0E (hex) Core Communications Co., Ltd. -00-03-0F (hex) Digital China (Shanghai) Networks Ltd. -00-03-10 (hex) ITX E-Globaledge Corporation -00-03-11 (hex) Micro Technology Co., Ltd. -00-03-12 (hex) TR-Systemtechnik GmbH -00-03-13 (hex) Access Media SPA -00-03-14 (hex) Teleware Network Systems -00-03-15 (hex) Cidco Incorporated -00-03-16 (hex) Nobell Communications, Inc. -00-03-17 (hex) Merlin Systems, Inc. -00-03-18 (hex) Cyras Systems, Inc. -00-03-19 (hex) Infineon AG -00-03-1A (hex) Beijing Broad Telecom Ltd., China -00-03-1B (hex) Cellvision Systems, Inc. -00-03-1C (hex) Svenska Hardvarufabriken AB -00-03-1D (hex) Taiwan Commate Computer, Inc. -00-03-1E (hex) Optranet, Inc. -00-03-1F (hex) Condev Ltd. -00-03-20 (hex) Xpeed, Inc. -00-03-21 (hex) Reco Research Co., Ltd. -00-03-22 (hex) IDIS Co., Ltd. -00-03-23 (hex) Cornet Technology, Inc. -00-03-24 (hex) SANYO Consumer Electronics Co., Ltd. -00-03-25 (hex) Arima Computer Corp. -00-03-26 (hex) Iwasaki Information Systems Co., Ltd. -00-03-27 (hex) ACT'L -00-03-28 (hex) Mace Group, Inc. -00-03-29 (hex) F3, Inc. -00-03-2A (hex) UniData Communication Systems, Inc. -00-03-2B (hex) GAI Datenfunksysteme GmbH -00-03-2C (hex) ABB Switzerland Ltd -00-03-2D (hex) IBASE Technology, Inc. -00-03-2E (hex) Scope Information Management, Ltd. -00-03-2F (hex) Global Sun Technology, Inc. -00-03-30 (hex) Imagenics, Co., Ltd. -00-03-31 (hex) Cisco Systems, Inc. -00-03-32 (hex) Cisco Systems, Inc. -00-03-33 (hex) Digitel Co., Ltd. -00-03-34 (hex) Newport Electronics -00-03-35 (hex) Mirae Technology -00-03-36 (hex) Zetes Technologies -00-03-37 (hex) Vaone, Inc. -00-03-38 (hex) Oak Technology -00-03-39 (hex) Eurologic Systems, Ltd. -00-03-3A (hex) Silicon Wave, Inc. -00-03-3B (hex) TAMI Tech Co., Ltd. -00-03-3C (hex) Daiden Co., Ltd. -00-03-3D (hex) ILSHin Lab -00-03-3E (hex) Tateyama System Laboratory Co., Ltd. -00-03-3F (hex) BigBand Networks, Ltd. -00-03-40 (hex) Floware Wireless Systems, Ltd. -00-03-41 (hex) Axon Digital Design -00-03-42 (hex) Nortel Networks -00-03-43 (hex) Martin Professional A/S -00-03-44 (hex) Tietech.Co., Ltd. -00-03-45 (hex) Routrek Networks Corporation -00-03-46 (hex) Hitachi Kokusai Electric, Inc. -00-03-47 (hex) Intel Corporation -00-03-48 (hex) Norscan Instruments, Ltd. -00-03-49 (hex) Vidicode Datacommunicatie B.V. -00-03-4A (hex) RIAS Corporation -00-03-4B (hex) Nortel Networks -00-03-4C (hex) Shanghai DigiVision Technology Co., Ltd. -00-03-4D (hex) Chiaro Networks, Ltd. -00-03-4E (hex) Pos Data Company, Ltd. -00-03-4F (hex) Sur-Gard Security -00-03-50 (hex) BTICINO SPA -00-03-51 (hex) Diebold, Inc. -00-03-52 (hex) Colubris Networks -00-03-53 (hex) Mitac, Inc. -00-03-54 (hex) Fiber Logic Communications -00-03-55 (hex) TeraBeam Internet Systems -00-03-56 (hex) Wincor Nixdorf International GmbH -00-03-57 (hex) Intervoice-Brite, Inc. -00-03-58 (hex) Hanyang Digitech Co., Ltd. -00-03-59 (hex) DigitalSis -00-03-5A (hex) Photron Limited -00-03-5B (hex) BridgeWave Communications -00-03-5C (hex) Saint Song Corp. -00-03-5D (hex) Bosung Hi-Net Co., Ltd. -00-03-5E (hex) Metropolitan Area Networks, Inc. -00-03-5F (hex) Prueftechnik Condition Monitoring GmbH & Co. KG -00-03-60 (hex) PAC Interactive Technology, Inc. -00-03-61 (hex) Widcomm, Inc. -00-03-62 (hex) Vodtel Communications, Inc. -00-03-63 (hex) Miraesys Co., Ltd. -00-03-64 (hex) Scenix Semiconductor, Inc. -00-03-65 (hex) Kira Information & Communications, Ltd. -00-03-66 (hex) ASM Pacific Technology -00-03-67 (hex) Jasmine Networks, Inc. -00-03-68 (hex) Embedone Co., Ltd. -00-03-69 (hex) Nippon Antenna Co., Ltd. -00-03-6A (hex) Mainnet, Ltd. -00-03-6B (hex) Cisco Systems, Inc. -00-03-6C (hex) Cisco Systems, Inc. -00-03-6D (hex) Runtop, Inc. -00-03-6E (hex) Nicon Systems (Pty) Limited -00-03-6F (hex) Telsey SPA -00-03-70 (hex) NXTV, Inc. -00-03-71 (hex) Acomz Networks Corp. -00-03-72 (hex) ULAN -00-03-73 (hex) Aselsan A.S -00-03-74 (hex) Control Microsystems -00-03-75 (hex) NetMedia, Inc. -00-03-76 (hex) Graphtec Technology, Inc. -00-03-77 (hex) Gigabit Wireless -00-03-78 (hex) HUMAX Co., Ltd. -00-03-79 (hex) Proscend Communications, Inc. -00-03-7A (hex) Taiyo Yuden Co., Ltd. -00-03-7B (hex) IDEC IZUMI Corporation -00-03-7C (hex) Coax Media -00-03-7D (hex) Stellcom -00-03-7E (hex) PORTech Communications, Inc. -00-03-7F (hex) Atheros Communications, Inc. -00-03-80 (hex) SSH Communications Security Corp. -00-03-81 (hex) Ingenico International -00-03-82 (hex) A-One Co., Ltd. -00-03-83 (hex) Metera Networks, Inc. -00-03-84 (hex) AETA -00-03-85 (hex) Actelis Networks, Inc. -00-03-86 (hex) Ho Net, Inc. -00-03-87 (hex) Blaze Network Products -00-03-88 (hex) Fastfame Technology Co., Ltd. -00-03-89 (hex) Plantronics -00-03-8A (hex) America Online, Inc. -00-03-8B (hex) PLUS-ONE I&T, Inc. -00-03-8C (hex) Total Impact -00-03-8D (hex) PCS Revenue Control Systems, Inc. -00-03-8E (hex) Atoga Systems, Inc. -00-03-8F (hex) Weinschel Corporation -00-03-90 (hex) Digital Video Communications, Inc. -00-03-91 (hex) Advanced Digital Broadcast, Ltd. -00-03-92 (hex) Hyundai Teletek Co., Ltd. -00-03-93 (hex) Apple Computer, Inc. -00-03-94 (hex) Connect One -00-03-95 (hex) California Amplifier -00-03-96 (hex) EZ Cast Co., Ltd. -00-03-97 (hex) Watchfront Limited -00-03-98 (hex) WISI -00-03-99 (hex) Dongju Informations & Communications Co., Ltd. -00-03-9A (hex) SiConnect -00-03-9B (hex) NetChip Technology, Inc. -00-03-9C (hex) OptiMight Communications, Inc. -00-03-9D (hex) Qisda Corporation -00-03-9E (hex) Tera System Co., Ltd. -00-03-9F (hex) Cisco Systems, Inc. -00-03-A0 (hex) Cisco Systems, Inc. -00-03-A1 (hex) HIPER Information & Communication, Inc. -00-03-A2 (hex) Catapult Communications -00-03-A3 (hex) MAVIX, Ltd. -00-03-A4 (hex) Imation Corp. -00-03-A5 (hex) Medea Corporation -00-03-A6 (hex) Traxit Technology, Inc. -00-03-A7 (hex) Unixtar Technology, Inc. -00-03-A8 (hex) IDOT Computers, Inc. -00-03-A9 (hex) AXCENT Media AG -00-03-AA (hex) Watlow -00-03-AB (hex) Bridge Information Systems -00-03-AC (hex) Fronius Schweissmaschinen -00-03-AD (hex) Emerson Energy Systems AB -00-03-AE (hex) Allied Advanced Manufacturing Pte, Ltd. -00-03-AF (hex) Paragea Communications -00-03-B0 (hex) Xsense Technology Corp. -00-03-B1 (hex) Hospira Inc. -00-03-B2 (hex) Radware -00-03-B3 (hex) IA Link Systems Co., Ltd. -00-03-B4 (hex) Macrotek International Corp. -00-03-B5 (hex) Entra Technology Co. -00-03-B6 (hex) QSI Corporation -00-03-B7 (hex) ZACCESS Systems -00-03-B8 (hex) NetKit Solutions, LLC -00-03-B9 (hex) Hualong Telecom Co., Ltd. -00-03-BA (hex) Sun Microsystems Inc. -00-03-BB (hex) Signal Communications Limited -00-03-BC (hex) COT GmbH -00-03-BD (hex) OmniCluster Technologies, Inc. -00-03-BE (hex) Netility -00-03-BF (hex) Centerpoint Broadband Technologies, Inc. -00-03-C0 (hex) RFTNC Co., Ltd. -00-03-C1 (hex) Packet Dynamics Ltd -00-03-C2 (hex) Solphone K.K. -00-03-C3 (hex) Micronik Multimedia -00-03-C4 (hex) Tomra Systems ASA -00-03-C5 (hex) Mobotix AG -00-03-C6 (hex) ICUE Systems, Inc. -00-03-C7 (hex) hopf Elektronik GmbH -00-03-C8 (hex) CML Emergency Services -00-03-C9 (hex) TECOM Co., Ltd. -00-03-CA (hex) MTS Systems Corp. -00-03-CB (hex) Nippon Systems Development Co., Ltd. -00-03-CC (hex) Momentum Computer, Inc. -00-03-CD (hex) Clovertech, Inc. -00-03-CE (hex) ETEN Technologies, Inc. -00-03-CF (hex) Muxcom, Inc. -00-03-D0 (hex) KOANKEISO Co., Ltd. -00-03-D1 (hex) Takaya Corporation -00-03-D2 (hex) Crossbeam Systems, Inc. -00-03-D3 (hex) Internet Energy Systems, Inc. -00-03-D4 (hex) Alloptic, Inc. -00-03-D5 (hex) Advanced Communications Co., Ltd. -00-03-D6 (hex) RADVision, Ltd. -00-03-D7 (hex) NextNet Wireless, Inc. -00-03-D8 (hex) iMPath Networks, Inc. -00-03-D9 (hex) Secheron SA -00-03-DA (hex) Takamisawa Cybernetics Co., Ltd. -00-03-DB (hex) Apogee Electronics Corp. -00-03-DC (hex) Lexar Media, Inc. -00-03-DD (hex) Comark Corp. -00-03-DE (hex) OTC Wireless -00-03-DF (hex) Desana Systems -00-03-E0 (hex) RadioFrame Networks, Inc. -00-03-E1 (hex) Winmate Communication, Inc. -00-03-E2 (hex) Comspace Corporation -00-03-E3 (hex) Cisco Systems, Inc. -00-03-E4 (hex) Cisco Systems, Inc. -00-03-E5 (hex) Hermstedt SG -00-03-E6 (hex) Entone, Inc. -00-03-E7 (hex) Logostek Co. Ltd. -00-03-E8 (hex) Wavelength Digital Limited -00-03-E9 (hex) Akara Canada, Inc. -00-03-EA (hex) Mega System Technologies, Inc. -00-03-EB (hex) Atrica -00-03-EC (hex) ICG Research, Inc. -00-03-ED (hex) Shinkawa Electric Co., Ltd. -00-03-EE (hex) MKNet Corporation -00-03-EF (hex) Oneline AG -00-03-F0 (hex) Redfern Broadband Networks -00-03-F1 (hex) Cicada Semiconductor, Inc. -00-03-F2 (hex) Seneca Networks -00-03-F3 (hex) Dazzle Multimedia, Inc. -00-03-F4 (hex) NetBurner -00-03-F5 (hex) Chip2Chip -00-03-F6 (hex) Allegro Networks, Inc. -00-03-F7 (hex) Plast-Control GmbH -00-03-F8 (hex) SanCastle Technologies, Inc. -00-03-F9 (hex) Pleiades Communications, Inc. -00-03-FA (hex) TiMetra Networks -00-03-FB (hex) ENEGATE Co.,Ltd. -00-03-FC (hex) Intertex Data AB -00-03-FD (hex) Cisco Systems, Inc. -00-03-FE (hex) Cisco Systems, Inc. -00-03-FF (hex) Microsoft Corporation -00-04-00 (hex) LEXMARK INTERNATIONAL, INC. -00-04-01 (hex) Osaki Electric Co., Ltd. -00-04-02 (hex) Nexsan Technologies, Ltd. -00-04-03 (hex) Nexsi Corporation -00-04-04 (hex) Makino Milling Machine Co., Ltd. -00-04-05 (hex) ACN Technologies -00-04-06 (hex) Fa. Metabox AG -00-04-07 (hex) Topcon Positioning Systems, Inc. -00-04-08 (hex) Sanko Electronics Co., Ltd. -00-04-09 (hex) Cratos Networks -00-04-0A (hex) Sage Systems -00-04-0B (hex) 3com Europe Ltd. -00-04-0C (hex) KANNO Work's Ltd. -00-04-0D (hex) Avaya, Inc. -00-04-0E (hex) AVM GmbH -00-04-0F (hex) Asus Network Technologies, Inc. -00-04-10 (hex) Spinnaker Networks, Inc. -00-04-11 (hex) Inkra Networks, Inc. -00-04-12 (hex) WaveSmith Networks, Inc. -00-04-13 (hex) SNOM Technology AG -00-04-14 (hex) Umezawa Musen Denki Co., Ltd. -00-04-15 (hex) Rasteme Systems Co., Ltd. -00-04-16 (hex) Parks S/A Comunicacoes Digitais -00-04-17 (hex) ELAU AG -00-04-18 (hex) Teltronic S.A.U. -00-04-19 (hex) Fibercycle Networks, Inc. -00-04-1A (hex) Ines Test and Measurement GmbH & CoKG -00-04-1B (hex) Bridgeworks Ltd. -00-04-1C (hex) ipDialog, Inc. -00-04-1D (hex) Corega of America -00-04-1E (hex) Shikoku Instrumentation Co., Ltd. -00-04-1F (hex) Sony Computer Entertainment, Inc. -00-04-20 (hex) Slim Devices, Inc. -00-04-21 (hex) Ocular Networks -00-04-22 (hex) Gordon Kapes, Inc. -00-04-23 (hex) Intel Corporation -00-04-24 (hex) TMC s.r.l. -00-04-25 (hex) Atmel Corporation -00-04-26 (hex) Autosys -00-04-27 (hex) Cisco Systems, Inc. -00-04-28 (hex) Cisco Systems, Inc. -00-04-29 (hex) Pixord Corporation -00-04-2A (hex) Wireless Networks, Inc. -00-04-2B (hex) IT Access Co., Ltd. -00-04-2C (hex) Minet, Inc. -00-04-2D (hex) Sarian Systems, Ltd. -00-04-2E (hex) Netous Technologies, Ltd. -00-04-2F (hex) International Communications Products, Inc. -00-04-30 (hex) Netgem -00-04-31 (hex) GlobalStreams, Inc. -00-04-32 (hex) Voyetra Turtle Beach, Inc. -00-04-33 (hex) Cyberboard A/S -00-04-34 (hex) Accelent Systems, Inc. -00-04-35 (hex) Comptek International, Inc. -00-04-36 (hex) ELANsat Technologies, Inc. -00-04-37 (hex) Powin Information Technology, Inc. -00-04-38 (hex) Nortel Networks -00-04-39 (hex) Rosco Entertainment Technology, Inc. -00-04-3A (hex) Intelligent Telecommunications, Inc. -00-04-3B (hex) Lava Computer Mfg., Inc. -00-04-3C (hex) SONOS Co., Ltd. -00-04-3D (hex) INDEL AG -00-04-3E (hex) Telencomm -00-04-3F (hex) ESTeem Wireless Modems, Inc -00-04-40 (hex) cyberPIXIE, Inc. -00-04-41 (hex) Half Dome Systems, Inc. -00-04-42 (hex) NACT -00-04-43 (hex) Agilent Technologies, Inc. -00-04-44 (hex) Western Multiplex Corporation -00-04-45 (hex) LMS Skalar Instruments GmbH -00-04-46 (hex) CYZENTECH Co., Ltd. -00-04-47 (hex) Acrowave Systems Co., Ltd. -00-04-48 (hex) Polaroid Corporation -00-04-49 (hex) Mapletree Networks -00-04-4A (hex) iPolicy Networks, Inc. -00-04-4B (hex) NVIDIA -00-04-4C (hex) JENOPTIK -00-04-4D (hex) Cisco Systems, Inc. -00-04-4E (hex) Cisco Systems, Inc. -00-04-4F (hex) Leukhardt Systemelektronik GmbH -00-04-50 (hex) DMD Computers SRL -00-04-51 (hex) Medrad, Inc. -00-04-52 (hex) RocketLogix, Inc. -00-04-53 (hex) YottaYotta, Inc. -00-04-54 (hex) Quadriga UK -00-04-55 (hex) ANTARA.net -00-04-56 (hex) Motorola PTP Inc -00-04-57 (hex) Universal Access Technology, Inc. -00-04-58 (hex) Fusion X Co., Ltd. -00-04-59 (hex) Veristar Corporation -00-04-5A (hex) The Linksys Group, Inc. -00-04-5B (hex) Techsan Electronics Co., Ltd. -00-04-5C (hex) Mobiwave Pte Ltd -00-04-5D (hex) BEKA Elektronik -00-04-5E (hex) PolyTrax Information Technology AG -00-04-5F (hex) Evalue Technology, Inc. -00-04-60 (hex) Knilink Technology, Inc. -00-04-61 (hex) EPOX Computer Co., Ltd. -00-04-62 (hex) DAKOS Data & Communication Co., Ltd. -00-04-63 (hex) Bosch Security Systems -00-04-64 (hex) Fantasma Networks, Inc. -00-04-65 (hex) i.s.t isdn-support technik GmbH -00-04-66 (hex) ARMITEL Co. -00-04-67 (hex) Wuhan Research Institute of MII -00-04-68 (hex) Vivity, Inc. -00-04-69 (hex) Innocom, Inc. -00-04-6A (hex) Navini Networks -00-04-6B (hex) Palm Wireless, Inc. -00-04-6C (hex) Cyber Technology Co., Ltd. -00-04-6D (hex) Cisco Systems, Inc. -00-04-6E (hex) Cisco Systems, Inc. -00-04-6F (hex) Digitel S/A Industria Eletronica -00-04-70 (hex) ipUnplugged AB -00-04-71 (hex) IPrad -00-04-72 (hex) Telelynx, Inc. -00-04-73 (hex) Photonex Corporation -00-04-74 (hex) LEGRAND -00-04-75 (hex) 3 Com Corporation -00-04-76 (hex) 3 Com Corporation -00-04-77 (hex) Scalant Systems, Inc. -00-04-78 (hex) G. Star Technology Corporation -00-04-79 (hex) Radius Co., Ltd. -00-04-7A (hex) AXXESSIT ASA -00-04-7B (hex) Schlumberger -00-04-7C (hex) Skidata AG -00-04-7D (hex) Pelco -00-04-7E (hex) Optelecom=NKF -00-04-7F (hex) Chr. Mayr GmbH & Co. KG -00-04-80 (hex) Brocade Communications Systems, Inc -00-04-81 (hex) Econolite Control Products, Inc. -00-04-82 (hex) Medialogic Corp. -00-04-83 (hex) Deltron Technology, Inc. -00-04-84 (hex) Amann GmbH -00-04-85 (hex) PicoLight -00-04-86 (hex) ITTC, University of Kansas -00-04-87 (hex) Cogency Semiconductor, Inc. -00-04-88 (hex) Eurotherm Controls -00-04-89 (hex) YAFO Networks, Inc. -00-04-8A (hex) Temia Vertriebs GmbH -00-04-8B (hex) Poscon Corporation -00-04-8C (hex) Nayna Networks, Inc. -00-04-8D (hex) Tone Commander Systems, Inc. -00-04-8E (hex) Ohm Tech Labs, Inc. -00-04-8F (hex) TD Systems Corporation -00-04-90 (hex) Optical Access -00-04-91 (hex) Technovision, Inc. -00-04-92 (hex) Hive Internet, Ltd. -00-04-93 (hex) Tsinghua Unisplendour Co., Ltd. -00-04-94 (hex) Breezecom, Ltd. -00-04-95 (hex) Tejas Networks India Limited -00-04-96 (hex) Extreme Networks -00-04-97 (hex) MacroSystem Digital Video AG -00-04-98 (hex) Mahi Networks -00-04-99 (hex) Chino Corporation -00-04-9A (hex) Cisco Systems, Inc. -00-04-9B (hex) Cisco Systems, Inc. -00-04-9C (hex) Surgient Networks, Inc. -00-04-9D (hex) Ipanema Technologies -00-04-9E (hex) Wirelink Co., Ltd. -00-04-9F (hex) Freescale Semiconductor -00-04-A0 (hex) Verity Instruments, Inc. -00-04-A1 (hex) Pathway Connectivity -00-04-A2 (hex) L.S.I. Japan Co., Ltd. -00-04-A3 (hex) Microchip Technology, Inc. -00-04-A4 (hex) NetEnabled, Inc. -00-04-A5 (hex) Barco Projection Systems NV -00-04-A6 (hex) SAF Tehnika Ltd. -00-04-A7 (hex) FabiaTech Corporation -00-04-A8 (hex) Broadmax Technologies, Inc. -00-04-A9 (hex) SandStream Technologies, Inc. -00-04-AA (hex) Jetstream Communications -00-04-AB (hex) Comverse Network Systems, Inc. -00-04-AC (hex) IBM CORP. -00-04-AD (hex) Malibu Networks -00-04-AE (hex) Sullair Corporation -00-04-AF (hex) Digital Fountain, Inc. -00-04-B0 (hex) ELESIGN Co., Ltd. -00-04-B1 (hex) Signal Technology, Inc. -00-04-B2 (hex) ESSEGI SRL -00-04-B3 (hex) Videotek, Inc. -00-04-B4 (hex) CIAC -00-04-B5 (hex) Equitrac Corporation -00-04-B6 (hex) Stratex Networks, Inc. -00-04-B7 (hex) AMB i.t. Holding -00-04-B8 (hex) Kumahira Co., Ltd. -00-04-B9 (hex) S.I. Soubou, Inc. -00-04-BA (hex) KDD Media Will Corporation -00-04-BB (hex) Bardac Corporation -00-04-BC (hex) Giantec, Inc. -00-04-BD (hex) Motorola BCS -00-04-BE (hex) OptXCon, Inc. -00-04-BF (hex) VersaLogic Corp. -00-04-C0 (hex) Cisco Systems, Inc. -00-04-C1 (hex) Cisco Systems, Inc. -00-04-C2 (hex) Magnipix, Inc. -00-04-C3 (hex) CASTOR Informatique -00-04-C4 (hex) Allen & Heath Limited -00-04-C5 (hex) ASE Technologies, USA -00-04-C6 (hex) Yamaha Motor Co., Ltd. -00-04-C7 (hex) NetMount -00-04-C8 (hex) LIBA Maschinenfabrik GmbH -00-04-C9 (hex) Micro Electron Co., Ltd. -00-04-CA (hex) FreeMs Corp. -00-04-CB (hex) Tdsoft Communication, Ltd. -00-04-CC (hex) Peek Traffic B.V. -00-04-CD (hex) Informedia Research Group -00-04-CE (hex) Patria Ailon -00-04-CF (hex) Seagate Technology -00-04-D0 (hex) Softlink s.r.o. -00-04-D1 (hex) Drew Technologies, Inc. -00-04-D2 (hex) Adcon Telemetry GmbH -00-04-D3 (hex) Toyokeiki Co., Ltd. -00-04-D4 (hex) Proview Electronics Co., Ltd. -00-04-D5 (hex) Hitachi Information & Communication Engineering, Ltd. -00-04-D6 (hex) Takagi Industrial Co., Ltd. -00-04-D7 (hex) Omitec Instrumentation Ltd. -00-04-D8 (hex) IPWireless, Inc. -00-04-D9 (hex) Titan Electronics, Inc. -00-04-DA (hex) Relax Technology, Inc. -00-04-DB (hex) Tellus Group Corp. -00-04-DC (hex) Nortel Networks -00-04-DD (hex) Cisco Systems, Inc. -00-04-DE (hex) Cisco Systems, Inc. -00-04-DF (hex) Teracom Telematica Ltda. -00-04-E0 (hex) Procket Networks -00-04-E1 (hex) Infinior Microsystems -00-04-E2 (hex) SMC Networks, Inc. -00-04-E3 (hex) Accton Technology Corp. -00-04-E4 (hex) Daeryung Ind., Inc. -00-04-E5 (hex) Glonet Systems, Inc. -00-04-E6 (hex) Banyan Network Private Limited -00-04-E7 (hex) Lightpointe Communications, Inc -00-04-E8 (hex) IER, Inc. -00-04-E9 (hex) Infiniswitch Corporation -00-04-EA (hex) Hewlett-Packard Company -00-04-EB (hex) Paxonet Communications, Inc. -00-04-EC (hex) Memobox SA -00-04-ED (hex) Billion Electric Co., Ltd. -00-04-EE (hex) Lincoln Electric Company -00-04-EF (hex) Polestar Corp. -00-04-F0 (hex) International Computers, Ltd -00-04-F1 (hex) WhereNet -00-04-F2 (hex) Polycom -00-04-F3 (hex) FS FORTH-SYSTEME GmbH -00-04-F4 (hex) Infinite Electronics Inc. -00-04-F5 (hex) SnowShore Networks, Inc. -00-04-F6 (hex) Amphus -00-04-F7 (hex) Omega Band, Inc. -00-04-F8 (hex) QUALICABLE TV Industria E Com., Ltda -00-04-F9 (hex) Xtera Communications, Inc. -00-04-FA (hex) NBS Technologies Inc. -00-04-FB (hex) Commtech, Inc. -00-04-FC (hex) Stratus Computer (DE), Inc. -00-04-FD (hex) Japan Control Engineering Co., Ltd. -00-04-FE (hex) Pelago Networks -00-04-FF (hex) Acronet Co., Ltd. -00-05-00 (hex) Cisco Systems, Inc. -00-05-01 (hex) Cisco Systems, Inc. -00-05-02 (hex) APPLE COMPUTER -00-05-03 (hex) ICONAG -00-05-04 (hex) Naray Information & Communication Enterprise -00-05-05 (hex) Systems Integration Solutions, Inc. -00-05-06 (hex) Reddo Networks AB -00-05-07 (hex) Fine Appliance Corp. -00-05-08 (hex) Inetcam, Inc. -00-05-09 (hex) AVOC Nishimura Ltd. -00-05-0A (hex) ICS Spa -00-05-0B (hex) SICOM Systems, Inc. -00-05-0C (hex) Network Photonics, Inc. -00-05-0D (hex) Midstream Technologies, Inc. -00-05-0E (hex) 3ware, Inc. -00-05-0F (hex) Tanaka S/S Ltd. -00-05-10 (hex) Infinite Shanghai Communication Terminals Ltd. -00-05-11 (hex) Complementary Technologies Ltd -00-05-12 (hex) MeshNetworks, Inc. -00-05-13 (hex) VTLinx Multimedia Systems, Inc. -00-05-14 (hex) KDT Systems Co., Ltd. -00-05-15 (hex) Nuark Co., Ltd. -00-05-16 (hex) SMART Modular Technologies -00-05-17 (hex) Shellcomm, Inc. -00-05-18 (hex) Jupiters Technology -00-05-19 (hex) Siemens Building Technologies AG, -00-05-1A (hex) 3Com Europe Ltd. -00-05-1B (hex) Magic Control Technology Corporation -00-05-1C (hex) Xnet Technology Corp. -00-05-1D (hex) Airocon, Inc. -00-05-1E (hex) Brocade Communications Systems, Inc. -00-05-1F (hex) Taijin Media Co., Ltd. -00-05-20 (hex) Smartronix, Inc. -00-05-21 (hex) Control Microsystems -00-05-22 (hex) LEA*D Corporation, Inc. -00-05-23 (hex) AVL List GmbH -00-05-24 (hex) BTL System (HK) Limited -00-05-25 (hex) Puretek Industrial Co., Ltd. -00-05-26 (hex) IPAS GmbH -00-05-27 (hex) SJ Tek Co. Ltd -00-05-28 (hex) New Focus, Inc. -00-05-29 (hex) Shanghai Broadan Communication Technology Co., Ltd -00-05-2A (hex) Ikegami Tsushinki Co., Ltd. -00-05-2B (hex) HORIBA, Ltd. -00-05-2C (hex) Supreme Magic Corporation -00-05-2D (hex) Zoltrix International Limited -00-05-2E (hex) Cinta Networks -00-05-2F (hex) Leviton Network Solutions -00-05-30 (hex) Andiamo Systems, Inc. -00-05-31 (hex) Cisco Systems, Inc. -00-05-32 (hex) Cisco Systems, Inc. -00-05-33 (hex) Brocade Communications Systems, Inc. -00-05-34 (hex) Northstar Engineering Ltd. -00-05-35 (hex) Chip PC Ltd. -00-05-36 (hex) Danam Communications, Inc. -00-05-37 (hex) Nets Technology Co., Ltd. -00-05-38 (hex) Merilus, Inc. -00-05-39 (hex) A Brand New World in Sweden AB -00-05-3A (hex) Willowglen Services Pte Ltd -00-05-3B (hex) Harbour Networks Ltd., Co. Beijing -00-05-3C (hex) Xircom -00-05-3D (hex) Agere Systems -00-05-3E (hex) KID Systeme GmbH -00-05-3F (hex) VisionTek, Inc. -00-05-40 (hex) FAST Corporation -00-05-41 (hex) Advanced Systems Co., Ltd. -00-05-42 (hex) Otari, Inc. -00-05-43 (hex) IQ Wireless GmbH -00-05-44 (hex) Valley Technologies, Inc. -00-05-45 (hex) Internet Photonics -00-05-46 (hex) KDDI Network & Solultions Inc. -00-05-47 (hex) Starent Networks -00-05-48 (hex) Disco Corporation -00-05-49 (hex) Salira Optical Network Systems -00-05-4A (hex) Ario Data Networks, Inc. -00-05-4B (hex) Micro Innovation AG -00-05-4C (hex) RF Innovations Pty Ltd -00-05-4D (hex) Brans Technologies, Inc. -00-05-4E (hex) Philips -00-05-4F (hex) PRIVATE -00-05-50 (hex) Vcomms Connect Limited -00-05-51 (hex) F & S Elektronik Systeme GmbH -00-05-52 (hex) Xycotec Computer GmbH -00-05-53 (hex) DVC Company, Inc. -00-05-54 (hex) Rangestar Wireless -00-05-55 (hex) Japan Cash Machine Co., Ltd. -00-05-56 (hex) 360 Systems -00-05-57 (hex) Agile TV Corporation -00-05-58 (hex) Synchronous, Inc. -00-05-59 (hex) Intracom S.A. -00-05-5A (hex) Power Dsine Ltd. -00-05-5B (hex) Charles Industries, Ltd. -00-05-5C (hex) Kowa Company, Ltd. -00-05-5D (hex) D-Link Systems, Inc. -00-05-5E (hex) Cisco Systems, Inc. -00-05-5F (hex) Cisco Systems, Inc. -00-05-60 (hex) LEADER COMM.CO., LTD -00-05-61 (hex) nac Image Technology, Inc. -00-05-62 (hex) Digital View Limited -00-05-63 (hex) J-Works, Inc. -00-05-64 (hex) Tsinghua Bitway Co., Ltd. -00-05-65 (hex) Tailyn Communication Company Ltd. -00-05-66 (hex) Secui.com Corporation -00-05-67 (hex) Etymonic Design, Inc. -00-05-68 (hex) Piltofish Networks AB -00-05-69 (hex) VMware, Inc. -00-05-6A (hex) Heuft Systemtechnik GmbH -00-05-6B (hex) C.P. Technology Co., Ltd. -00-05-6C (hex) Hung Chang Co., Ltd. -00-05-6D (hex) Pacific Corporation -00-05-6E (hex) National Enhance Technology, Inc. -00-05-6F (hex) Innomedia Technologies Pvt. Ltd. -00-05-70 (hex) Baydel Ltd. -00-05-71 (hex) Seiwa Electronics Co. -00-05-72 (hex) Deonet Co., Ltd. -00-05-73 (hex) Cisco Systems, Inc. -00-05-74 (hex) Cisco Systems, Inc. -00-05-75 (hex) CDS-Electronics BV -00-05-76 (hex) NSM Technology Ltd. -00-05-77 (hex) SM Information & Communication -00-05-78 (hex) PRIVATE -00-05-79 (hex) Universal Control Solution Corp. -00-05-7A (hex) Hatteras Networks -00-05-7B (hex) Chung Nam Electronic Co., Ltd. -00-05-7C (hex) RCO Security AB -00-05-7D (hex) Sun Communications, Inc. -00-05-7E (hex) Eckelmann Steuerungstechnik GmbH -00-05-7F (hex) Acqis Technology -00-05-80 (hex) Fibrolan Ltd. -00-05-81 (hex) Snell & Wilcox Ltd. -00-05-82 (hex) ClearCube Technology -00-05-83 (hex) ImageCom Limited -00-05-84 (hex) AbsoluteValue Systems, Inc. -00-05-85 (hex) Juniper Networks, Inc. -00-05-86 (hex) Lucent Technologies -00-05-87 (hex) Locus, Incorporated -00-05-88 (hex) Sensoria Corp. -00-05-89 (hex) National Datacomputer -00-05-8A (hex) Netcom Co., Ltd. -00-05-8B (hex) IPmental, Inc. -00-05-8C (hex) Opentech Inc. -00-05-8D (hex) Lynx Photonic Networks, Inc. -00-05-8E (hex) Flextronics International GmbH & Co. Nfg. KG -00-05-8F (hex) CLCsoft co. -00-05-90 (hex) Swissvoice Ltd. -00-05-91 (hex) Active Silicon Ltd. -00-05-92 (hex) Pultek Corp. -00-05-93 (hex) Grammar Engine Inc. -00-05-94 (hex) IXXAT Automation GmbH -00-05-95 (hex) Alesis Corporation -00-05-96 (hex) Genotech Co., Ltd. -00-05-97 (hex) Eagle Traffic Control Systems -00-05-98 (hex) CRONOS S.r.l. -00-05-99 (hex) DRS Test and Energy Management or DRS-TEM -00-05-9A (hex) Cisco Systems, Inc. -00-05-9B (hex) Cisco Systems, Inc. -00-05-9C (hex) Kleinknecht GmbH, Ing. Buero -00-05-9D (hex) Daniel Computing Systems, Inc. -00-05-9E (hex) Zinwell Corporation -00-05-9F (hex) Yotta Networks, Inc. -00-05-A0 (hex) MOBILINE Kft. -00-05-A1 (hex) Zenocom -00-05-A2 (hex) CELOX Networks -00-05-A3 (hex) QEI, Inc. -00-05-A4 (hex) Lucid Voice Ltd. -00-05-A5 (hex) KOTT -00-05-A6 (hex) Extron Electronics -00-05-A7 (hex) Hyperchip, Inc. -00-05-A8 (hex) WYLE ELECTRONICS -00-05-A9 (hex) Princeton Networks, Inc. -00-05-AA (hex) Moore Industries International Inc. -00-05-AB (hex) Cyber Fone, Inc. -00-05-AC (hex) Northern Digital, Inc. -00-05-AD (hex) Topspin Communications, Inc. -00-05-AE (hex) Mediaport USA -00-05-AF (hex) InnoScan Computing A/S -00-05-B0 (hex) Korea Computer Technology Co., Ltd. -00-05-B1 (hex) ASB Technology BV -00-05-B2 (hex) Medison Co., Ltd. -00-05-B3 (hex) Asahi-Engineering Co., Ltd. -00-05-B4 (hex) Aceex Corporation -00-05-B5 (hex) Broadcom Technologies -00-05-B6 (hex) INSYS Microelectronics GmbH -00-05-B7 (hex) Arbor Technology Corp. -00-05-B8 (hex) Electronic Design Associates, Inc. -00-05-B9 (hex) Airvana, Inc. -00-05-BA (hex) Area Netwoeks, Inc. -00-05-BB (hex) Myspace AB -00-05-BC (hex) Resorsys Ltd. -00-05-BD (hex) ROAX BV -00-05-BE (hex) Kongsberg Seatex AS -00-05-BF (hex) JustEzy Technology, Inc. -00-05-C0 (hex) Digital Network Alacarte Co., Ltd. -00-05-C1 (hex) A-Kyung Motion, Inc. -00-05-C2 (hex) Soronti, Inc. -00-05-C3 (hex) Pacific Instruments, Inc. -00-05-C4 (hex) Telect, Inc. -00-05-C5 (hex) Flaga HF -00-05-C6 (hex) Triz Communications -00-05-C7 (hex) I/F-COM A/S -00-05-C8 (hex) VERYTECH -00-05-C9 (hex) LG Innotek Co., Ltd. -00-05-CA (hex) Hitron Technology, Inc. -00-05-CB (hex) ROIS Technologies, Inc. -00-05-CC (hex) Sumtel Communications, Inc. -00-05-CD (hex) Denon, Ltd. -00-05-CE (hex) Prolink Microsystems Corporation -00-05-CF (hex) Thunder River Technologies, Inc. -00-05-D0 (hex) Solinet Systems -00-05-D1 (hex) Metavector Technologies -00-05-D2 (hex) DAP Technologies -00-05-D3 (hex) eProduction Solutions, Inc. -00-05-D4 (hex) FutureSmart Networks, Inc. -00-05-D5 (hex) Speedcom Wireless -00-05-D6 (hex) Titan Wireless -00-05-D7 (hex) Vista Imaging, Inc. -00-05-D8 (hex) Arescom, Inc. -00-05-D9 (hex) Techno Valley, Inc. -00-05-DA (hex) Apex Automationstechnik -00-05-DB (hex) NENTEC Netzwerktechnologie GmbH -00-05-DC (hex) Cisco Systems, Inc. -00-05-DD (hex) Cisco Systems, Inc. -00-05-DE (hex) Gi Fone Korea, Inc. -00-05-DF (hex) Electronic Innovation, Inc. -00-05-E0 (hex) Empirix Corp. -00-05-E1 (hex) Trellis Photonics, Ltd. -00-05-E2 (hex) Creativ Network Technologies -00-05-E3 (hex) LightSand Communications, Inc. -00-05-E4 (hex) Red Lion Controls Inc. -00-05-E5 (hex) Renishaw PLC -00-05-E6 (hex) Egenera, Inc. -00-05-E7 (hex) Netrake an AudioCodes Company -00-05-E8 (hex) TurboWave, Inc. -00-05-E9 (hex) Unicess Network, Inc. -00-05-EA (hex) Rednix -00-05-EB (hex) Blue Ridge Networks, Inc. -00-05-EC (hex) Mosaic Systems Inc. -00-05-ED (hex) Technikum Joanneum GmbH -00-05-EE (hex) BEWATOR Group -00-05-EF (hex) ADOIR Digital Technology -00-05-F0 (hex) SATEC -00-05-F1 (hex) Vrcom, Inc. -00-05-F2 (hex) Power R, Inc. -00-05-F3 (hex) Weboyn -00-05-F4 (hex) System Base Co., Ltd. -00-05-F5 (hex) OYO Geospace -00-05-F6 (hex) Young Chang Co. Ltd. -00-05-F7 (hex) Analog Devices, Inc. -00-05-F8 (hex) Real Time Access, Inc. -00-05-F9 (hex) TOA Corporation -00-05-FA (hex) IPOptical, Inc. -00-05-FB (hex) ShareGate, Inc. -00-05-FC (hex) Schenck Pegasus Corp. -00-05-FD (hex) PacketLight Networks Ltd. -00-05-FE (hex) Traficon N.V. -00-05-FF (hex) SNS Solutions, Inc. -00-06-00 (hex) Toshiba Teli Corporation -00-06-01 (hex) Otanikeiki Co., Ltd. -00-06-02 (hex) Cirkitech Electronics Co. -00-06-03 (hex) Baker Hughes Inc. -00-06-04 (hex) @Track Communications, Inc. -00-06-05 (hex) Inncom International, Inc. -00-06-06 (hex) RapidWAN, Inc. -00-06-07 (hex) Omni Directional Control Technology Inc. -00-06-08 (hex) At-Sky SAS -00-06-09 (hex) Crossport Systems -00-06-0A (hex) Blue2space -00-06-0B (hex) Paceline Systems Corporation -00-06-0C (hex) Melco Industries, Inc. -00-06-0D (hex) Wave7 Optics -00-06-0E (hex) IGYS Systems, Inc. -00-06-0F (hex) Narad Networks Inc -00-06-10 (hex) Abeona Networks Inc -00-06-11 (hex) Zeus Wireless, Inc. -00-06-12 (hex) Accusys, Inc. -00-06-13 (hex) Kawasaki Microelectronics Incorporated -00-06-14 (hex) Prism Holdings -00-06-15 (hex) Kimoto Electric Co., Ltd. -00-06-16 (hex) Tel Net Co., Ltd. -00-06-17 (hex) Redswitch Inc. -00-06-18 (hex) DigiPower Manufacturing Inc. -00-06-19 (hex) Connection Technology Systems -00-06-1A (hex) Zetari Inc. -00-06-1B (hex) Notebook Development Lab. Lenovo Japan Ltd. -00-06-1C (hex) Hoshino Metal Industries, Ltd. -00-06-1D (hex) MIP Telecom, Inc. -00-06-1E (hex) Maxan Systems -00-06-1F (hex) Vision Components GmbH -00-06-20 (hex) Serial System Ltd. -00-06-21 (hex) Hinox, Co., Ltd. -00-06-22 (hex) Chung Fu Chen Yeh Enterprise Corp. -00-06-23 (hex) MGE UPS Systems France -00-06-24 (hex) Gentner Communications Corp. -00-06-25 (hex) The Linksys Group, Inc. -00-06-26 (hex) MWE GmbH -00-06-27 (hex) Uniwide Technologies, Inc. -00-06-28 (hex) Cisco Systems, Inc. -00-06-29 (hex) IBM CORPORATION -00-06-2A (hex) Cisco Systems, Inc. -00-06-2B (hex) INTRASERVER TECHNOLOGY -00-06-2C (hex) Bivio Networks -00-06-2D (hex) TouchStar Technologies, L.L.C. -00-06-2E (hex) Aristos Logic Corp. -00-06-2F (hex) Pivotech Systems Inc. -00-06-30 (hex) Adtranz Sweden -00-06-31 (hex) Optical Solutions, Inc. -00-06-32 (hex) Mesco Engineering GmbH -00-06-33 (hex) Cross Match Technologies GmbH -00-06-34 (hex) GTE Airfone Inc. -00-06-35 (hex) PacketAir Networks, Inc. -00-06-36 (hex) Jedai Broadband Networks -00-06-37 (hex) Toptrend-Meta Information (ShenZhen) Inc. -00-06-38 (hex) Sungjin C&C Co., Ltd. -00-06-39 (hex) Newtec -00-06-3A (hex) Dura Micro, Inc. -00-06-3B (hex) Arcturus Networks, Inc. -00-06-3C (hex) Intrinsyc Europe Ltd -00-06-3D (hex) Microwave Data Systems Inc. -00-06-3E (hex) Opthos Inc. -00-06-3F (hex) Everex Communications Inc. -00-06-40 (hex) White Rock Networks -00-06-41 (hex) ITCN -00-06-42 (hex) Genetel Systems Inc. -00-06-43 (hex) SONO Computer Co., Ltd. -00-06-44 (hex) Neix,Inc -00-06-45 (hex) Meisei Electric Co. Ltd. -00-06-46 (hex) ShenZhen XunBao Network Technology Co Ltd -00-06-47 (hex) Etrali S.A. -00-06-48 (hex) Seedsware, Inc. -00-06-49 (hex) 3M Deutschland GmbH -00-06-4A (hex) Honeywell Co., Ltd. (KOREA) -00-06-4B (hex) Alexon Co., Ltd. -00-06-4C (hex) Invicta Networks, Inc. -00-06-4D (hex) Sencore -00-06-4E (hex) Broad Net Technology Inc. -00-06-4F (hex) PRO-NETS Technology Corporation -00-06-50 (hex) Tiburon Networks, Inc. -00-06-51 (hex) Aspen Networks Inc. -00-06-52 (hex) Cisco Systems, Inc. -00-06-53 (hex) Cisco Systems, Inc. -00-06-54 (hex) Winpresa Building Automation Technologies GmbH -00-06-55 (hex) Yipee, Inc. -00-06-56 (hex) Tactel AB -00-06-57 (hex) Market Central, Inc. -00-06-58 (hex) Helmut Fischer GmbH Institut fr Elektronik und Messtechnik -00-06-59 (hex) EAL (Apeldoorn) B.V. -00-06-5A (hex) Strix Systems -00-06-5B (hex) Dell Computer Corp. -00-06-5C (hex) Malachite Technologies, Inc. -00-06-5D (hex) Heidelberg Web Systems -00-06-5E (hex) Photuris, Inc. -00-06-5F (hex) ECI Telecom - NGTS Ltd. -00-06-60 (hex) NADEX Co., Ltd. -00-06-61 (hex) NIA Home Technologies Corp. -00-06-62 (hex) MBM Technology Ltd. -00-06-63 (hex) Human Technology Co., Ltd. -00-06-64 (hex) Fostex Corporation -00-06-65 (hex) Sunny Giken, Inc. -00-06-66 (hex) Roving Networks -00-06-67 (hex) Tripp Lite -00-06-68 (hex) Vicon Industries Inc. -00-06-69 (hex) Datasound Laboratories Ltd -00-06-6A (hex) InfiniCon Systems, Inc. -00-06-6B (hex) Sysmex Corporation -00-06-6C (hex) Robinson Corporation -00-06-6D (hex) Compuprint S.P.A. -00-06-6E (hex) Delta Electronics, Inc. -00-06-6F (hex) Korea Data Systems -00-06-70 (hex) Upponetti Oy -00-06-71 (hex) Softing AG -00-06-72 (hex) Netezza -00-06-73 (hex) Optelecom-nkf -00-06-74 (hex) Spectrum Control, Inc. -00-06-75 (hex) Banderacom, Inc. -00-06-76 (hex) Novra Technologies Inc. -00-06-77 (hex) SICK AG -00-06-78 (hex) Marantz Brand Company -00-06-79 (hex) Konami Corporation -00-06-7A (hex) JMP Systems -00-06-7B (hex) Toplink C&C Corporation -00-06-7C (hex) CISCO SYSTEMS, INC. -00-06-7D (hex) Takasago Ltd. -00-06-7E (hex) WinCom Systems, Inc. -00-06-7F (hex) Digeo, Inc. -00-06-80 (hex) Card Access, Inc. -00-06-81 (hex) Goepel Electronic GmbH -00-06-82 (hex) Convedia -00-06-83 (hex) Bravara Communications, Inc. -00-06-84 (hex) Biacore AB -00-06-85 (hex) NetNearU Corporation -00-06-86 (hex) ZARDCOM Co., Ltd. -00-06-87 (hex) Omnitron Systems Technology, Inc. -00-06-88 (hex) Telways Communication Co., Ltd. -00-06-89 (hex) yLez Technologies Pte Ltd -00-06-8A (hex) NeuronNet Co. Ltd. R&D Center -00-06-8B (hex) AirRunner Technologies, Inc. -00-06-8C (hex) 3Com Corporation -00-06-8D (hex) SEPATON, Inc. -00-06-8E (hex) HID Corporation -00-06-8F (hex) Telemonitor, Inc. -00-06-90 (hex) Euracom Communication GmbH -00-06-91 (hex) PT Inovacao -00-06-92 (hex) Intruvert Networks, Inc. -00-06-93 (hex) Flexus Computer Technology, Inc. -00-06-94 (hex) Mobillian Corporation -00-06-95 (hex) Ensure Technologies, Inc. -00-06-96 (hex) Advent Networks -00-06-97 (hex) R & D Center -00-06-98 (hex) egnite Software GmbH -00-06-99 (hex) Vida Design Co. -00-06-9A (hex) e & Tel -00-06-9B (hex) AVT Audio Video Technologies GmbH -00-06-9C (hex) Transmode Systems AB -00-06-9D (hex) Petards Mobile Intelligence -00-06-9E (hex) UNIQA, Inc. -00-06-9F (hex) Kuokoa Networks -00-06-A0 (hex) Mx Imaging -00-06-A1 (hex) Celsian Technologies, Inc. -00-06-A2 (hex) Microtune, Inc. -00-06-A3 (hex) Bitran Corporation -00-06-A4 (hex) INNOWELL Corp. -00-06-A5 (hex) PINON Corp. -00-06-A6 (hex) Artistic Licence (UK) Ltd -00-06-A7 (hex) Primarion -00-06-A8 (hex) KC Technology, Inc. -00-06-A9 (hex) Universal Instruments Corp. -00-06-AA (hex) VT Miltope -00-06-AB (hex) W-Link Systems, Inc. -00-06-AC (hex) Intersoft Co. -00-06-AD (hex) KB Electronics Ltd. -00-06-AE (hex) Himachal Futuristic Communications Ltd -00-06-AF (hex) Xalted Networks -00-06-B0 (hex) Comtech EF Data Corp. -00-06-B1 (hex) Sonicwall -00-06-B2 (hex) Linxtek Co. -00-06-B3 (hex) Diagraph Corporation -00-06-B4 (hex) Vorne Industries, Inc. -00-06-B5 (hex) Luminent, Inc. -00-06-B6 (hex) Nir-Or Israel Ltd. -00-06-B7 (hex) TELEM GmbH -00-06-B8 (hex) Bandspeed Pty Ltd -00-06-B9 (hex) A5TEK Corp. -00-06-BA (hex) Westwave Communications -00-06-BB (hex) ATI Technologies Inc. -00-06-BC (hex) Macrolink, Inc. -00-06-BD (hex) BNTECHNOLOGY Co., Ltd. -00-06-BE (hex) Baumer Optronic GmbH -00-06-BF (hex) Accella Technologies Co., Ltd. -00-06-C0 (hex) United Internetworks, Inc. -00-06-C1 (hex) CISCO SYSTEMS, INC. -00-06-C2 (hex) Smartmatic Corporation -00-06-C3 (hex) Schindler Elevator Ltd. -00-06-C4 (hex) Piolink Inc. -00-06-C5 (hex) INNOVI Technologies Limited -00-06-C6 (hex) lesswire AG -00-06-C7 (hex) RFNET Technologies Pte Ltd (S) -00-06-C8 (hex) Sumitomo Metal Micro Devices, Inc. -00-06-C9 (hex) Technical Marketing Research, Inc. -00-06-CA (hex) American Computer & Digital Components, Inc. (ACDC) -00-06-CB (hex) Jotron Electronics A/S -00-06-CC (hex) JMI Electronics Co., Ltd. -00-06-CD (hex) Leaf Imaging Ltd. -00-06-CE (hex) DATENO -00-06-CF (hex) Thales Avionics In-Flight Systems, LLC -00-06-D0 (hex) Elgar Electronics Corp. -00-06-D1 (hex) Tahoe Networks, Inc. -00-06-D2 (hex) Tundra Semiconductor Corp. -00-06-D3 (hex) Alpha Telecom, Inc. U.S.A. -00-06-D4 (hex) Interactive Objects, Inc. -00-06-D5 (hex) Diamond Systems Corp. -00-06-D6 (hex) Cisco Systems, Inc. -00-06-D7 (hex) Cisco Systems, Inc. -00-06-D8 (hex) Maple Optical Systems -00-06-D9 (hex) IPM-Net S.p.A. -00-06-DA (hex) ITRAN Communications Ltd. -00-06-DB (hex) ICHIPS Co., Ltd. -00-06-DC (hex) Syabas Technology (Amquest) -00-06-DD (hex) AT & T Laboratories - Cambridge Ltd -00-06-DE (hex) Flash Technology -00-06-DF (hex) AIDONIC Corporation -00-06-E0 (hex) MAT Co., Ltd. -00-06-E1 (hex) Techno Trade s.a -00-06-E2 (hex) Ceemax Technology Co., Ltd. -00-06-E3 (hex) Quantitative Imaging Corporation -00-06-E4 (hex) Citel Technologies Ltd. -00-06-E5 (hex) Fujian Newland Computer Ltd. Co. -00-06-E6 (hex) DongYang Telecom Co., Ltd. -00-06-E7 (hex) Bit Blitz Communications Inc. -00-06-E8 (hex) Optical Network Testing, Inc. -00-06-E9 (hex) Intime Corp. -00-06-EA (hex) ELZET80 Mikrocomputer GmbH&Co. KG -00-06-EB (hex) Global Data -00-06-EC (hex) Harris Corporation -00-06-ED (hex) Inara Networks -00-06-EE (hex) Shenyang Neu-era Information & Technology Stock Co., Ltd -00-06-EF (hex) Maxxan Systems, Inc. -00-06-F0 (hex) Digeo, Inc. -00-06-F1 (hex) Optillion -00-06-F2 (hex) Platys Communications -00-06-F3 (hex) AcceLight Networks -00-06-F4 (hex) Prime Electronics & Satellitics Inc. -00-06-F8 (hex) CPU Technology, Inc. -00-06-F9 (hex) Mitsui Zosen Systems Research Inc. -00-06-FA (hex) IP SQUARE Co, Ltd. -00-06-FB (hex) Hitachi Printing Solutions, Ltd. -00-06-FC (hex) Fnet Co., Ltd. -00-06-FD (hex) Comjet Information Systems Corp. -00-06-FE (hex) Ambrado, Inc -00-06-FF (hex) Sheba Systems Co., Ltd. -00-07-00 (hex) Zettamedia Korea -00-07-01 (hex) RACAL-DATACOM -00-07-02 (hex) Varian Medical Systems -00-07-03 (hex) CSEE Transport -00-07-05 (hex) Endress & Hauser GmbH & Co -00-07-06 (hex) Sanritz Corporation -00-07-07 (hex) Interalia Inc. -00-07-08 (hex) Bitrage Inc. -00-07-09 (hex) Westerstrand Urfabrik AB -00-07-0A (hex) Unicom Automation Co., Ltd. -00-07-0B (hex) Novabase SGPS, SA -00-07-0C (hex) SVA-Intrusion.com Co. Ltd. -00-07-0D (hex) Cisco Systems Inc. -00-07-0E (hex) Cisco Systems Inc. -00-07-0F (hex) Fujant, Inc. -00-07-10 (hex) Adax, Inc. -00-07-11 (hex) Acterna -00-07-12 (hex) JAL Information Technology -00-07-13 (hex) IP One, Inc. -00-07-14 (hex) Brightcom -00-07-15 (hex) General Research of Electronics, Inc. -00-07-16 (hex) J & S Marine Ltd. -00-07-17 (hex) Wieland Electric GmbH -00-07-18 (hex) iCanTek Co., Ltd. -00-07-19 (hex) Mobiis Co., Ltd. -00-07-1A (hex) Finedigital Inc. -00-07-1B (hex) CDV Americas Ltd -00-07-1C (hex) AT&T Fixed Wireless Services -00-07-1D (hex) Satelsa Sistemas Y Aplicaciones De Telecomunicaciones, S.A. -00-07-1E (hex) Tri-M Engineering / Nupak Dev. Corp. -00-07-1F (hex) European Systems Integration -00-07-20 (hex) Trutzschler GmbH & Co. KG -00-07-21 (hex) Formac Elektronik GmbH -00-07-22 (hex) The Nielsen Company -00-07-23 (hex) ELCON Systemtechnik GmbH -00-07-24 (hex) Telemax Co., Ltd. -00-07-25 (hex) Bematech International Corp. -00-07-27 (hex) Zi Corporation (HK) Ltd. -00-07-28 (hex) Neo Telecom -00-07-29 (hex) Kistler Instrumente AG -00-07-2A (hex) Innovance Networks -00-07-2B (hex) Jung Myung Telecom Co., Ltd. -00-07-2C (hex) Fabricom -00-07-2D (hex) CNSystems -00-07-2E (hex) North Node AB -00-07-2F (hex) Intransa, Inc. -00-07-30 (hex) Hutchison OPTEL Telecom Technology Co., Ltd. -00-07-31 (hex) Ophir-Spiricon Inc -00-07-32 (hex) AAEON Technology Inc. -00-07-33 (hex) DANCONTROL Engineering -00-07-34 (hex) ONStor, Inc. -00-07-35 (hex) Flarion Technologies, Inc. -00-07-36 (hex) Data Video Technologies Co., Ltd. -00-07-37 (hex) Soriya Co. Ltd. -00-07-38 (hex) Young Technology Co., Ltd. -00-07-39 (hex) Scotty Group Austria Gmbh -00-07-3A (hex) Inventel Systemes -00-07-3B (hex) Tenovis GmbH & Co KG -00-07-3C (hex) Telecom Design -00-07-3D (hex) Nanjing Postel Telecommunications Co., Ltd. -00-07-3E (hex) China Great-Wall Computer Shenzhen Co., Ltd. -00-07-3F (hex) Woojyun Systec Co., Ltd. -00-07-40 (hex) Buffalo, Inc -00-07-41 (hex) Sierra Automated Systems -00-07-42 (hex) Current Technologies, LLC -00-07-43 (hex) Chelsio Communications -00-07-44 (hex) Unico, Inc. -00-07-45 (hex) Radlan Computer Communications Ltd. -00-07-46 (hex) TURCK, Inc. -00-07-47 (hex) Mecalc -00-07-48 (hex) The Imaging Source Europe -00-07-49 (hex) CENiX Inc. -00-07-4A (hex) Carl Valentin GmbH -00-07-4B (hex) Daihen Corporation -00-07-4C (hex) Beicom Inc. -00-07-4D (hex) Zebra Technologies Corp. -00-07-4E (hex) Naughty boy co., Ltd. -00-07-4F (hex) Cisco Systems, Inc. -00-07-50 (hex) Cisco Systems, Inc. -00-07-51 (hex) mut AG -00-07-52 (hex) Rhythm Watch Co., Ltd. -00-07-53 (hex) Beijing Qxcomm Technology Co., Ltd. -00-07-54 (hex) Xyterra Computing, Inc. -00-07-55 (hex) Lafon SA -00-07-56 (hex) Juyoung Telecom -00-07-57 (hex) Topcall International AG -00-07-58 (hex) Dragonwave -00-07-59 (hex) Boris Manufacturing Corp. -00-07-5A (hex) Air Products and Chemicals, Inc. -00-07-5B (hex) Gibson Guitars -00-07-5C (hex) Eastman Kodak Company -00-07-5D (hex) Celleritas Inc. -00-07-5E (hex) Ametek Power Instruments -00-07-5F (hex) VCS Video Communication Systems AG -00-07-60 (hex) TOMIS Information & Telecom Corp. -00-07-61 (hex) Logitech SA -00-07-62 (hex) Group Sense Limited -00-07-63 (hex) Sunniwell Cyber Tech. Co., Ltd. -00-07-64 (hex) YoungWoo Telecom Co. Ltd. -00-07-65 (hex) Jade Quantum Technologies, Inc. -00-07-66 (hex) Chou Chin Industrial Co., Ltd. -00-07-67 (hex) Yuxing Electronics Company Limited -00-07-68 (hex) Danfoss A/S -00-07-69 (hex) Italiana Macchi SpA -00-07-6A (hex) NEXTEYE Co., Ltd. -00-07-6B (hex) Stralfors AB -00-07-6C (hex) Daehanet, Inc. -00-07-6D (hex) Flexlight Networks -00-07-6E (hex) Sinetica Corporation Limited -00-07-6F (hex) Synoptics Limited -00-07-70 (hex) Locusnetworks Corporation -00-07-71 (hex) Embedded System Corporation -00-07-72 (hex) Alcatel Shanghai Bell Co., Ltd. -00-07-73 (hex) Ascom Powerline Communications Ltd. -00-07-74 (hex) GuangZhou Thinker Technology Co. Ltd. -00-07-75 (hex) Valence Semiconductor, Inc. -00-07-76 (hex) Federal APD -00-07-77 (hex) Motah Ltd. -00-07-78 (hex) GERSTEL GmbH & Co. KG -00-07-79 (hex) Sungil Telecom Co., Ltd. -00-07-7A (hex) Infoware System Co., Ltd. -00-07-7B (hex) Millimetrix Broadband Networks -00-07-7C (hex) Westermo Teleindustri AB -00-07-7E (hex) Elrest GmbH -00-07-7F (hex) J Communications Co., Ltd. -00-07-80 (hex) Bluegiga Technologies OY -00-07-81 (hex) Itron Inc. -00-07-82 (hex) Sun Microsystems, Inc. -00-07-83 (hex) SynCom Network, Inc. -00-07-84 (hex) Cisco Systems Inc. -00-07-85 (hex) Cisco Systems Inc. -00-07-86 (hex) Wireless Networks Inc. -00-07-87 (hex) Idea System Co., Ltd. -00-07-88 (hex) Clipcomm, Inc. -00-07-89 (hex) Eastel Systems Corporation -00-07-8A (hex) Mentor Data System Inc. -00-07-8B (hex) Wegener Communications, Inc. -00-07-8C (hex) Elektronikspecialisten i Borlange AB -00-07-8D (hex) NetEngines Ltd. -00-07-8E (hex) Garz & Friche GmbH -00-07-8F (hex) Emkay Innovative Products -00-07-90 (hex) Tri-M Technologies (s) Limited -00-07-91 (hex) International Data Communications, Inc. -00-07-92 (hex) Suetron Electronic GmbH -00-07-93 (hex) Shin Satellite Public Company Limited -00-07-94 (hex) Simple Devices, Inc. -00-07-95 (hex) Elitegroup Computer System Co. (ECS) -00-07-96 (hex) LSI Systems, Inc. -00-07-97 (hex) Netpower Co., Ltd. -00-07-98 (hex) Selea SRL -00-07-99 (hex) Tipping Point Technologies, Inc. -00-07-9A (hex) Verint Systems Inc -00-07-9B (hex) Aurora Networks -00-07-9C (hex) Golden Electronics Technology Co., Ltd. -00-07-9D (hex) Musashi Co., Ltd. -00-07-9E (hex) Ilinx Co., Ltd. -00-07-9F (hex) Action Digital Inc. -00-07-A0 (hex) e-Watch Inc. -00-07-A1 (hex) VIASYS Healthcare GmbH -00-07-A2 (hex) Opteon Corporation -00-07-A3 (hex) Ositis Software, Inc. -00-07-A4 (hex) GN Netcom Ltd. -00-07-A5 (hex) Y.D.K Co. Ltd. -00-07-A6 (hex) Home Automation, Inc. -00-07-A7 (hex) A-Z Inc. -00-07-A8 (hex) Haier Group Technologies Ltd. -00-07-A9 (hex) Novasonics -00-07-AA (hex) Quantum Data Inc. -00-07-AC (hex) Eolring -00-07-AD (hex) Pentacon GmbH Foto-und Feinwerktechnik -00-07-AE (hex) Britestream Networks, Inc. -00-07-AF (hex) N-Tron Corp. -00-07-B0 (hex) Office Details, Inc. -00-07-B1 (hex) Equator Technologies -00-07-B2 (hex) Transaccess S.A. -00-07-B3 (hex) Cisco Systems Inc. -00-07-B4 (hex) Cisco Systems Inc. -00-07-B5 (hex) Any One Wireless Ltd. -00-07-B6 (hex) Telecom Technology Ltd. -00-07-B7 (hex) Samurai Ind. Prods Eletronicos Ltda -00-07-B8 (hex) Corvalent Corporation -00-07-B9 (hex) Ginganet Corporation -00-07-BA (hex) UTStarcom, Inc. -00-07-BB (hex) Candera Inc. -00-07-BC (hex) Identix Inc. -00-07-BD (hex) Radionet Ltd. -00-07-BE (hex) DataLogic SpA -00-07-BF (hex) Armillaire Technologies, Inc. -00-07-C0 (hex) NetZerver Inc. -00-07-C1 (hex) Overture Networks, Inc. -00-07-C2 (hex) Netsys Telecom -00-07-C3 (hex) Thomson -00-07-C4 (hex) JEAN Co. Ltd. -00-07-C5 (hex) Gcom, Inc. -00-07-C6 (hex) VDS Vosskuhler GmbH -00-07-C7 (hex) Synectics Systems Limited -00-07-C8 (hex) Brain21, Inc. -00-07-C9 (hex) Technol Seven Co., Ltd. -00-07-CA (hex) Creatix Polymedia Ges Fur Kommunikaitonssysteme -00-07-CB (hex) Freebox SA -00-07-CC (hex) Kaba Benzing GmbH -00-07-CD (hex) NMTEL Co., Ltd. -00-07-CE (hex) Cabletime Limited -00-07-CF (hex) Anoto AB -00-07-D0 (hex) Automat Engenharia de Automaoa Ltda. -00-07-D1 (hex) Spectrum Signal Processing Inc. -00-07-D2 (hex) Logopak Systeme -00-07-D3 (hex) Stork Digital Imaging B.V. -00-07-D4 (hex) Zhejiang Yutong Network Communication Co Ltd. -00-07-D5 (hex) 3e Technologies Int;., Inc. -00-07-D6 (hex) Commil Ltd. -00-07-D7 (hex) Caporis Networks AG -00-07-D8 (hex) Hitron Systems Inc. -00-07-D9 (hex) Splicecom -00-07-DA (hex) Neuro Telecom Co., Ltd. -00-07-DB (hex) Kirana Networks, Inc. -00-07-DC (hex) Atek Co, Ltd. -00-07-DD (hex) Cradle Technologies -00-07-DE (hex) eCopilt AB -00-07-DF (hex) Vbrick Systems Inc. -00-07-E0 (hex) Palm Inc. -00-07-E1 (hex) WIS Communications Co. Ltd. -00-07-E2 (hex) Bitworks, Inc. -00-07-E3 (hex) Navcom Technology, Inc. -00-07-E4 (hex) SoftRadio Co., Ltd. -00-07-E5 (hex) Coup Corporation -00-07-E6 (hex) edgeflow Canada Inc. -00-07-E7 (hex) FreeWave Technologies -00-07-E8 (hex) St. Bernard Software -00-07-E9 (hex) Intel Corporation -00-07-EA (hex) Massana, Inc. -00-07-EB (hex) Cisco Systems Inc. -00-07-EC (hex) Cisco Systems Inc. -00-07-ED (hex) Altera Corporation -00-07-EE (hex) telco Informationssysteme GmbH -00-07-EF (hex) Lockheed Martin Tactical Systems -00-07-F0 (hex) Beckett LogiSync LLC -00-07-F1 (hex) TeraBurst Networks Inc. -00-07-F2 (hex) IOA Corporation -00-07-F3 (hex) Thinkengine Networks -00-07-F4 (hex) Eletex Co., Ltd. -00-07-F5 (hex) Bridgeco Co AG -00-07-F6 (hex) Qqest Software Systems -00-07-F7 (hex) Galtronics -00-07-F8 (hex) ITDevices, Inc. -00-07-F9 (hex) Phonetics, Inc. -00-07-FA (hex) ITT Co., Ltd. -00-07-FB (hex) Giga Stream UMTS Technologies GmbH -00-07-FC (hex) Adept Systems Inc. -00-07-FD (hex) LANergy Ltd. -00-07-FE (hex) Rigaku Corporation -00-07-FF (hex) Gluon Networks -00-08-00 (hex) MULTITECH SYSTEMS, INC. -00-08-01 (hex) HighSpeed Surfing Inc. -00-08-02 (hex) Hewlett Packard -00-08-03 (hex) Cos Tron -00-08-04 (hex) ICA Inc. -00-08-05 (hex) Techno-Holon Corporation -00-08-06 (hex) Raonet Systems, Inc. -00-08-07 (hex) Access Devices Limited -00-08-08 (hex) PPT Vision, Inc. -00-08-09 (hex) Systemonic AG -00-08-0A (hex) Espera-Werke GmbH -00-08-0B (hex) Birka BPA Informationssystem AB -00-08-0C (hex) VDA Elettronica spa -00-08-0D (hex) Toshiba -00-08-0E (hex) Motorola, BCS -00-08-0F (hex) Proximion Fiber Optics AB -00-08-10 (hex) Key Technology, Inc. -00-08-11 (hex) VOIX Corporation -00-08-12 (hex) GM-2 Corporation -00-08-13 (hex) Diskbank, Inc. -00-08-14 (hex) TIL Technologies -00-08-15 (hex) CATS Co., Ltd. -00-08-16 (hex) Bluetags A/S -00-08-17 (hex) EmergeCore Networks LLC -00-08-18 (hex) Pixelworks, Inc. -00-08-19 (hex) Banksys -00-08-1A (hex) Sanrad Intelligence Storage Communications (2000) Ltd. -00-08-1B (hex) Windigo Systems -00-08-1C (hex) @pos.com -00-08-1D (hex) Ipsil, Incorporated -00-08-1E (hex) Repeatit AB -00-08-1F (hex) Pou Yuen Tech Corp. Ltd. -00-08-20 (hex) Cisco Systems Inc. -00-08-21 (hex) Cisco Systems Inc. -00-08-22 (hex) InPro Comm -00-08-23 (hex) Texa Corp. -00-08-24 (hex) Copitrak Inc -00-08-25 (hex) Acme Packet -00-08-26 (hex) Colorado Med Tech -00-08-27 (hex) Pirelli Broadband Solutions -00-08-28 (hex) Koei Engineering Ltd. -00-08-29 (hex) Aval Nagasaki Corporation -00-08-2A (hex) Powerwallz Network Security -00-08-2B (hex) Wooksung Electronics, Inc. -00-08-2C (hex) Homag AG -00-08-2D (hex) Indus Teqsite Private Limited -00-08-2E (hex) Multitone Electronics PLC -00-08-4E (hex) DivergeNet, Inc. -00-08-4F (hex) Qualstar Corporation -00-08-50 (hex) Arizona Instrument Corp. -00-08-51 (hex) Canadian Bank Note Company, Ltd. -00-08-52 (hex) Davolink Co. Inc. -00-08-53 (hex) Schleicher GmbH & Co. Relaiswerke KG -00-08-54 (hex) Netronix, Inc. -00-08-55 (hex) NASA-Goddard Space Flight Center -00-08-56 (hex) Gamatronic Electronic Industries Ltd. -00-08-57 (hex) Polaris Networks, Inc. -00-08-58 (hex) Novatechnology Inc. -00-08-59 (hex) ShenZhen Unitone Electronics Co., Ltd. -00-08-5A (hex) IntiGate Inc. -00-08-5B (hex) Hanbit Electronics Co., Ltd. -00-08-5C (hex) Shanghai Dare Technologies Co. Ltd. -00-08-5D (hex) Aastra -00-08-5E (hex) PCO AG -00-08-5F (hex) Picanol N.V. -00-08-60 (hex) LodgeNet Entertainment Corp. -00-08-61 (hex) SoftEnergy Co., Ltd. -00-08-62 (hex) NEC Eluminant Technologies, Inc. -00-08-63 (hex) Entrisphere Inc. -00-08-64 (hex) Fasy S.p.A. -00-08-65 (hex) JASCOM CO., LTD -00-08-66 (hex) DSX Access Systems, Inc. -00-08-67 (hex) Uptime Devices -00-08-68 (hex) PurOptix -00-08-69 (hex) Command-e Technology Co.,Ltd. -00-08-6A (hex) Industrie Technik IPS GmbH -00-08-6B (hex) MIPSYS -00-08-6C (hex) Plasmon LMS -00-08-6D (hex) Missouri FreeNet -00-08-6E (hex) Hyglo AB -00-08-6F (hex) Resources Computer Network Ltd. -00-08-70 (hex) Rasvia Systems, Inc. -00-08-71 (hex) NORTHDATA Co., Ltd. -00-08-72 (hex) Sorenson Communications -00-08-73 (hex) DAP Design B.V. -00-08-74 (hex) Dell Computer Corp. -00-08-75 (hex) Acorp Electronics Corp. -00-08-76 (hex) SDSystem -00-08-77 (hex) Liebert-Hiross Spa -00-08-78 (hex) Benchmark Storage Innovations -00-08-79 (hex) CEM Corporation -00-08-7A (hex) Wipotec GmbH -00-08-7B (hex) RTX Telecom A/S -00-08-7C (hex) Cisco Systems, Inc. -00-08-7D (hex) Cisco Systems Inc. -00-08-7E (hex) Bon Electro-Telecom Inc. -00-08-7F (hex) SPAUN electronic GmbH & Co. KG -00-08-80 (hex) BroadTel Canada Communications inc. -00-08-81 (hex) DIGITAL HANDS CO.,LTD. -00-08-82 (hex) SIGMA CORPORATION -00-08-83 (hex) Hewlett-Packard Company -00-08-84 (hex) Index Braille AB -00-08-85 (hex) EMS Dr. Thomas Wuensche -00-08-86 (hex) Hansung Teliann, Inc. -00-08-87 (hex) Maschinenfabrik Reinhausen GmbH -00-08-88 (hex) OULLIM Information Technology Inc,. -00-08-89 (hex) Echostar Technologies Corp -00-08-8A (hex) Minds@Work -00-08-8B (hex) Tropic Networks Inc. -00-08-8C (hex) Quanta Network Systems Inc. -00-08-8D (hex) Sigma-Links Inc. -00-08-8E (hex) Nihon Computer Co., Ltd. -00-08-8F (hex) ADVANCED DIGITAL TECHNOLOGY -00-08-90 (hex) AVILINKS SA -00-08-91 (hex) Lyan Inc. -00-08-92 (hex) EM Solutions -00-08-93 (hex) LE INFORMATION COMMUNICATION INC. -00-08-94 (hex) InnoVISION Multimedia Ltd. -00-08-95 (hex) DIRC Technologie GmbH & Co.KG -00-08-96 (hex) Printronix, Inc. -00-08-97 (hex) Quake Technologies -00-08-98 (hex) Gigabit Optics Corporation -00-08-99 (hex) Netbind, Inc. -00-08-9A (hex) Alcatel Microelectronics -00-08-9B (hex) ICP Electronics Inc. -00-08-9C (hex) Elecs Industry Co., Ltd. -00-08-9D (hex) UHD-Elektronik -00-08-9E (hex) Beijing Enter-Net co.LTD -00-08-9F (hex) EFM Networks -00-08-A0 (hex) Stotz Feinmesstechnik GmbH -00-08-A1 (hex) CNet Technology Inc. -00-08-A2 (hex) ADI Engineering, Inc. -00-08-A3 (hex) Cisco Systems -00-08-A4 (hex) Cisco Systems -00-08-A5 (hex) Peninsula Systems Inc. -00-08-A6 (hex) Multiware & Image Co., Ltd. -00-08-A7 (hex) iLogic Inc. -00-08-A8 (hex) Systec Co., Ltd. -00-08-A9 (hex) SangSang Technology, Inc. -00-08-AA (hex) KARAM -00-08-AB (hex) EnerLinx.com, Inc. -00-08-AC (hex) Eltromat GmbH -00-08-AD (hex) Toyo-Linx Co., Ltd. -00-08-AE (hex) PacketFront Sweden AB -00-08-AF (hex) Novatec Corporation -00-08-B0 (hex) BKtel communications GmbH -00-08-B1 (hex) ProQuent Systems -00-08-B2 (hex) SHENZHEN COMPASS TECHNOLOGY DEVELOPMENT CO.,LTD -00-08-B3 (hex) Fastwel -00-08-B4 (hex) SYSPOL -00-08-B5 (hex) TAI GUEN ENTERPRISE CO., LTD -00-08-B6 (hex) RouteFree, Inc. -00-08-B7 (hex) HIT Incorporated -00-08-B8 (hex) E.F. Johnson -00-08-B9 (hex) KAON MEDIA Co., Ltd. -00-08-BA (hex) Erskine Systems Ltd -00-08-BB (hex) NetExcell -00-08-BC (hex) Ilevo AB -00-08-BD (hex) TEPG-US -00-08-BE (hex) XENPAK MSA Group -00-08-BF (hex) Aptus Elektronik AB -00-08-C0 (hex) ASA SYSTEMS -00-08-C1 (hex) Avistar Communications Corporation -00-08-C2 (hex) Cisco Systems -00-08-C3 (hex) Contex A/S -00-08-C4 (hex) Hikari Co.,Ltd. -00-08-C5 (hex) Liontech Co., Ltd. -00-08-C6 (hex) Philips Consumer Communications -00-08-C7 (hex) Hewlett Packard -00-08-C8 (hex) Soneticom, Inc. -00-08-C9 (hex) TechniSat Digital GmbH -00-08-CA (hex) TwinHan Technology Co.,Ltd -00-08-CB (hex) Zeta Broadband Inc. -00-08-CC (hex) Remotec, Inc. -00-08-CD (hex) With-Net Inc -00-08-CE (hex) IPMobileNet Inc. -00-08-CF (hex) Nippon Koei Power Systems Co., Ltd. -00-08-D0 (hex) Musashi Engineering Co., LTD. -00-08-D1 (hex) KAREL INC. -00-08-D2 (hex) ZOOM Networks Inc. -00-08-D3 (hex) Hercules Technologies S.A. -00-08-D4 (hex) IneoQuest Technologies, Inc -00-08-D5 (hex) Vanguard Networks Solutions, LLC -00-08-D6 (hex) HASSNET Inc. -00-08-D7 (hex) HOW CORPORATION -00-08-D8 (hex) Dowkey Microwave -00-08-D9 (hex) Mitadenshi Co.,LTD -00-08-DA (hex) SofaWare Technologies Ltd. -00-08-DB (hex) Corrigent Systems -00-08-DC (hex) Wiznet -00-08-DD (hex) Telena Communications, Inc. -00-08-DE (hex) 3UP Systems -00-08-DF (hex) Alistel Inc. -00-08-E0 (hex) ATO Technology Ltd. -00-08-E1 (hex) Barix AG -00-08-E2 (hex) Cisco Systems -00-08-E3 (hex) Cisco Systems -00-08-E4 (hex) Envenergy Inc -00-08-E5 (hex) IDK Corporation -00-08-E6 (hex) Littlefeet -00-08-E7 (hex) SHI ControlSystems,Ltd. -00-08-E8 (hex) Excel Master Ltd. -00-08-E9 (hex) NextGig -00-08-EA (hex) Motion Control Engineering, Inc -00-08-EB (hex) ROMWin Co.,Ltd. -00-08-EC (hex) Zonu, Inc. -00-08-ED (hex) ST&T Instrument Corp. -00-08-EE (hex) Logic Product Development -00-08-EF (hex) DIBAL,S.A. -00-08-F0 (hex) Next Generation Systems, Inc. -00-08-F1 (hex) Voltaire -00-08-F2 (hex) C&S Technology -00-08-F3 (hex) WANY -00-08-F4 (hex) Bluetake Technology Co., Ltd. -00-08-F5 (hex) YESTECHNOLOGY Co.,Ltd. -00-08-F6 (hex) Sumitomo Electric System Solutions Co.,Ltd. -00-08-F7 (hex) Hitachi Ltd, Semiconductor & Integrated Circuits Gr -00-08-F8 (hex) Guardall Ltd -00-08-F9 (hex) Padcom, Inc. -00-08-FA (hex) Karl E.Brinkmann GmbH -00-08-FB (hex) SonoSite, Inc. -00-08-FC (hex) Gigaphoton Inc. -00-08-FD (hex) BlueKorea Co., Ltd. -00-08-FE (hex) UNIK C&C Co.,Ltd. -00-08-FF (hex) Trilogy Communications Ltd -00-09-00 (hex) TMT -00-09-01 (hex) Shenzhen Shixuntong Information & Technoligy Co -00-09-02 (hex) Redline Communications Inc. -00-09-03 (hex) Panasas, Inc -00-09-04 (hex) MONDIAL electronic -00-09-05 (hex) iTEC Technologies Ltd. -00-09-06 (hex) Esteem Networks -00-09-07 (hex) Chrysalis Development -00-09-08 (hex) VTech Technology Corp. -00-09-09 (hex) Telenor Connect A/S -00-09-0A (hex) SnedFar Technology Co., Ltd. -00-09-0B (hex) MTL Instruments PLC -00-09-0C (hex) Mayekawa Mfg. Co. Ltd. -00-09-0D (hex) LEADER ELECTRONICS CORP. -00-09-0E (hex) Helix Technology Inc. -00-09-0F (hex) Fortinet Inc. -00-09-10 (hex) Simple Access Inc. -00-09-11 (hex) Cisco Systems -00-09-12 (hex) Cisco Systems -00-09-13 (hex) SystemK Corporation -00-09-14 (hex) COMPUTROLS INC. -00-09-15 (hex) CAS Corp. -00-09-16 (hex) Listman Home Technologies, Inc. -00-09-17 (hex) WEM Technology Inc -00-09-18 (hex) SAMSUNG TECHWIN CO.,LTD -00-09-19 (hex) MDS Gateways -00-09-1A (hex) Macat Optics & Electronics Co., Ltd. -00-09-1B (hex) Digital Generation Inc. -00-09-1C (hex) CacheVision, Inc -00-09-1D (hex) Proteam Computer Corporation -00-09-1E (hex) Firstech Technology Corp. -00-09-1F (hex) A&D Co., Ltd. -00-09-20 (hex) EpoX COMPUTER CO.,LTD. -00-09-21 (hex) Planmeca Oy -00-09-22 (hex) TST Biometrics GmbH -00-09-23 (hex) Heaman System Co., Ltd -00-09-24 (hex) Telebau GmbH -00-09-25 (hex) VSN Systemen BV -00-09-26 (hex) YODA COMMUNICATIONS, INC. -00-09-27 (hex) TOYOKEIKI CO.,LTD. -00-09-28 (hex) Telecore -00-09-29 (hex) Sanyo Industries (UK) Limited -00-09-2A (hex) MYTECS Co.,Ltd. -00-09-2B (hex) iQstor Networks, Inc. -00-09-2C (hex) Hitpoint Inc. -00-09-2D (hex) HTC Corporation -00-09-2E (hex) B&Tech System Inc. -00-09-2F (hex) Akom Technology Corporation -00-09-30 (hex) AeroConcierge Inc. -00-09-31 (hex) Future Internet, Inc. -00-09-32 (hex) Omnilux -00-09-33 (hex) OPTOVALLEY Co. Ltd. -00-09-34 (hex) Dream-Multimedia-Tv GmbH -00-09-35 (hex) Sandvine Incorporated -00-09-36 (hex) Ipetronik GmbH & Co.KG -00-09-37 (hex) Inventec Appliance Corp -00-09-38 (hex) Allot Communications -00-09-39 (hex) ShibaSoku Co.,Ltd. -00-09-3A (hex) Molex Fiber Optics -00-09-3B (hex) HYUNDAI NETWORKS INC. -00-09-3C (hex) Jacques Technologies P/L -00-09-3D (hex) Newisys,Inc. -00-09-3E (hex) C&I Technologies -00-09-3F (hex) Double-Win Enterpirse CO., LTD -00-09-40 (hex) AGFEO GmbH & Co. KG -00-09-41 (hex) Allied Telesis K.K. -00-09-42 (hex) CRESCO, LTD. -00-09-43 (hex) Cisco Systems -00-09-44 (hex) Cisco Systems -00-09-45 (hex) Palmmicro Communications Inc -00-09-46 (hex) Cluster Labs GmbH -00-09-47 (hex) Aztek, Inc. -00-09-48 (hex) Vista Control Systems, Corp. -00-09-49 (hex) Glyph Technologies Inc. -00-09-4A (hex) Homenet Communications -00-09-4B (hex) FillFactory NV -00-09-4C (hex) Communication Weaver Co.,Ltd. -00-09-4D (hex) Braintree Communications Pty Ltd -00-09-4E (hex) BARTECH SYSTEMS INTERNATIONAL, INC -00-09-4F (hex) elmegt GmbH & Co. KG -00-09-50 (hex) Independent Storage Corporation -00-09-51 (hex) Apogee Instruments, Inc -00-09-52 (hex) Auerswald GmbH & Co. KG -00-09-53 (hex) Linkage System Integration Co.Ltd. -00-09-54 (hex) AMiT spol. s. r. o. -00-09-55 (hex) Young Generation International Corp. -00-09-56 (hex) Network Systems Group, Ltd. (NSG) -00-09-57 (hex) Supercaller, Inc. -00-09-58 (hex) INTELNET S.A. -00-09-59 (hex) Sitecsoft -00-09-5A (hex) RACEWOOD TECHNOLOGY -00-09-5B (hex) Netgear, Inc. -00-09-5C (hex) Philips Medical Systems - Cardiac and Monitoring Systems (CM -00-09-5D (hex) Dialogue Technology Corp. -00-09-5E (hex) Masstech Group Inc. -00-09-5F (hex) Telebyte, Inc. -00-09-60 (hex) YOZAN Inc. -00-09-61 (hex) Switchgear and Instrumentation Ltd -00-09-62 (hex) Sonitor Technologies AS -00-09-63 (hex) Dominion Lasercom Inc. -00-09-64 (hex) Hi-Techniques, Inc. -00-09-65 (hex) HyunJu Computer Co., Ltd. -00-09-66 (hex) Thales Navigation -00-09-67 (hex) Tachyon, Inc -00-09-68 (hex) TECHNOVENTURE, INC. -00-09-69 (hex) Meret Optical Communications -00-09-6A (hex) Cloverleaf Communications Inc. -00-09-6B (hex) IBM Corporation -00-09-6C (hex) Imedia Semiconductor Corp. -00-09-6D (hex) Powernet Technologies Corp. -00-09-6E (hex) GIANT ELECTRONICS LTD. -00-09-6F (hex) Beijing Zhongqing Elegant Tech. Corp.,Limited -00-09-70 (hex) Vibration Research Corporation -00-09-71 (hex) Time Management, Inc. -00-09-72 (hex) Securebase,Inc -00-09-73 (hex) Lenten Technology Co., Ltd. -00-09-74 (hex) Innopia Technologies, Inc. -00-09-75 (hex) fSONA Communications Corporation -00-09-76 (hex) Datasoft ISDN Systems GmbH -00-09-77 (hex) Brunner Elektronik AG -00-09-78 (hex) AIJI System Co., Ltd. -00-09-79 (hex) Advanced Television Systems Committee, Inc. -00-09-7A (hex) Louis Design Labs. -00-09-7B (hex) Cisco Systems -00-09-7C (hex) Cisco Systems -00-09-7D (hex) SecWell Networks Oy -00-09-7E (hex) IMI TECHNOLOGY CO., LTD -00-09-7F (hex) Vsecure 2000 LTD. -00-09-80 (hex) Power Zenith Inc. -00-09-81 (hex) Newport Networks -00-09-82 (hex) Loewe Opta GmbH -00-09-83 (hex) Gvision Incorporated -00-09-84 (hex) MyCasa Network Inc. -00-09-85 (hex) Auto Telecom Company -00-09-86 (hex) Metalink LTD. -00-09-87 (hex) NISHI NIPPON ELECTRIC WIRE & CABLE CO.,LTD. -00-09-88 (hex) Nudian Electron Co., Ltd. -00-09-89 (hex) VividLogic Inc. -00-09-8A (hex) EqualLogic Inc -00-09-8B (hex) Entropic Communications, Inc. -00-09-8C (hex) Option Wireless Sweden -00-09-8D (hex) Velocity Semiconductor -00-09-8E (hex) ipcas GmbH -00-09-8F (hex) Cetacean Networks -00-09-90 (hex) ACKSYS Communications & systems -00-09-91 (hex) GE Fanuc Automation Manufacturing, Inc. -00-09-92 (hex) InterEpoch Technology,INC. -00-09-93 (hex) Visteon Corporation -00-09-94 (hex) Cronyx Engineering -00-09-95 (hex) Castle Technology Ltd -00-09-96 (hex) RDI -00-09-97 (hex) Nortel Networks -00-09-98 (hex) Capinfo Company Limited -00-09-99 (hex) CP GEORGES RENAULT -00-09-9A (hex) ELMO COMPANY, LIMITED -00-09-9B (hex) Western Telematic Inc. -00-09-9C (hex) Naval Research Laboratory -00-09-9D (hex) Haliplex Communications -00-09-9E (hex) Testech, Inc. -00-09-9F (hex) VIDEX INC. -00-09-A0 (hex) Microtechno Corporation -00-09-A1 (hex) Telewise Communications, Inc. -00-09-A2 (hex) Interface Co., Ltd. -00-09-A3 (hex) Leadfly Techologies Corp. Ltd. -00-09-A4 (hex) HARTEC Corporation -00-09-A5 (hex) HANSUNG ELETRONIC INDUSTRIES DEVELOPMENT CO., LTD -00-09-A6 (hex) Ignis Optics, Inc. -00-09-A7 (hex) Bang & Olufsen A/S -00-09-A8 (hex) Eastmode Pte Ltd -00-09-A9 (hex) Ikanos Communications -00-09-AA (hex) Data Comm for Business, Inc. -00-09-AB (hex) Netcontrol Oy -00-09-AC (hex) LANVOICE -00-09-AD (hex) HYUNDAI SYSCOMM, INC. -00-09-AE (hex) OKANO ELECTRIC CO.,LTD -00-09-AF (hex) e-generis -00-09-B0 (hex) Onkyo Corporation -00-09-B1 (hex) Kanematsu Electronics, Ltd. -00-09-B2 (hex) L&F Inc. -00-09-B3 (hex) MCM Systems Ltd -00-09-B4 (hex) KISAN TELECOM CO., LTD. -00-09-B5 (hex) 3J Tech. Co., Ltd. -00-09-B6 (hex) Cisco Systems -00-09-B7 (hex) Cisco Systems -00-09-B8 (hex) Entise Systems -00-09-B9 (hex) Action Imaging Solutions -00-09-BA (hex) MAKU Informationstechik GmbH -00-09-BB (hex) MathStar, Inc. -00-09-BC (hex) Digital Safety Technologies, Inc -00-09-BD (hex) Epygi Technologies, Ltd. -00-09-BE (hex) Mamiya-OP Co.,Ltd. -00-09-BF (hex) Nintendo Co.,Ltd. -00-09-C0 (hex) 6WIND -00-09-C1 (hex) PROCES-DATA A/S -00-09-C2 (hex) Onity, Inc. -00-09-C3 (hex) NETAS -00-09-C4 (hex) Medicore Co., Ltd -00-09-C5 (hex) KINGENE Technology Corporation -00-09-C6 (hex) Visionics Corporation -00-09-C7 (hex) Movistec -00-09-C8 (hex) SINAGAWA TSUSHIN KEISOU SERVICE -00-09-C9 (hex) BlueWINC Co., Ltd. -00-09-CA (hex) iMaxNetworks(Shenzhen)Limited. -00-09-CB (hex) HBrain -00-09-CC (hex) Moog GmbH -00-09-CD (hex) HUDSON SOFT CO.,LTD. -00-09-CE (hex) SpaceBridge Semiconductor Corp. -00-09-CF (hex) iAd GmbH -00-09-D0 (hex) Solacom Technologies Inc. -00-09-D1 (hex) SERANOA NETWORKS INC -00-09-D2 (hex) Mai Logic Inc. -00-09-D3 (hex) Western DataCom Co., Inc. -00-09-D4 (hex) Transtech Networks -00-09-D5 (hex) Signal Communication, Inc. -00-09-D6 (hex) KNC One GmbH -00-09-D7 (hex) DC Security Products -00-09-D8 (hex) Flt Communications AB -00-09-D9 (hex) Neoscale Systems, Inc -00-09-DA (hex) Control Module Inc. -00-09-DB (hex) eSpace -00-09-DC (hex) Galaxis Technology AG -00-09-DD (hex) Mavin Technology Inc. -00-09-DE (hex) Samjin Information & Communications Co., Ltd. -00-09-DF (hex) Vestel Komunikasyon Sanayi ve Ticaret A.S. -00-09-E0 (hex) XEMICS S.A. -00-09-E1 (hex) Gemtek Technology Co., Ltd. -00-09-E2 (hex) Sinbon Electronics Co., Ltd. -00-09-E3 (hex) Angel Iglesias S.A. -00-09-E4 (hex) K Tech Infosystem Inc. -00-09-E5 (hex) Hottinger Baldwin Messtechnik GmbH -00-09-E6 (hex) Cyber Switching Inc. -00-09-E7 (hex) ADC Techonology -00-09-E8 (hex) Cisco Systems -00-09-E9 (hex) Cisco Systems -00-09-EA (hex) YEM Inc. -00-09-EB (hex) HuMANDATA LTD. -00-09-EC (hex) Daktronics, Inc. -00-09-ED (hex) CipherOptics -00-09-EE (hex) MEIKYO ELECTRIC CO.,LTD -00-09-EF (hex) Vocera Communications -00-09-F0 (hex) Shimizu Technology Inc. -00-09-F1 (hex) Yamaki Electric Corporation -00-09-F2 (hex) Cohu, Inc., Electronics Division -00-09-F3 (hex) WELL Communication Corp. -00-09-F4 (hex) Alcon Laboratories, Inc. -00-09-F5 (hex) Emerson Network Power Co.,Ltd -00-09-F6 (hex) Shenzhen Eastern Digital Tech Ltd. -00-09-F7 (hex) SED, a division of Calian -00-09-F8 (hex) UNIMO TECHNOLOGY CO., LTD. -00-09-F9 (hex) ART JAPAN CO., LTD. -00-09-FB (hex) Philips Patient Monitoring -00-09-FC (hex) IPFLEX Inc. -00-09-FD (hex) Ubinetics Limited -00-09-FE (hex) Daisy Technologies, Inc. -00-09-FF (hex) X.net 2000 GmbH -00-0A-00 (hex) Mediatek Corp. -00-0A-01 (hex) SOHOware, Inc. -00-0A-02 (hex) ANNSO CO., LTD. -00-0A-03 (hex) ENDESA SERVICIOS, S.L. -00-0A-04 (hex) 3Com Ltd -00-0A-05 (hex) Widax Corp. -00-0A-06 (hex) Teledex LLC -00-0A-07 (hex) WebWayOne Ltd -00-0A-08 (hex) ALPINE ELECTRONICS, INC. -00-0A-09 (hex) TaraCom Integrated Products, Inc. -00-0A-0A (hex) SUNIX Co., Ltd. -00-0A-0B (hex) Sealevel Systems, Inc. -00-0A-0C (hex) Scientific Research Corporation -00-0A-0D (hex) MergeOptics GmbH -00-0A-0E (hex) Invivo Research Inc. -00-0A-0F (hex) Ilryung Telesys, Inc -00-0A-10 (hex) FAST media integrations AG -00-0A-11 (hex) ExPet Technologies, Inc -00-0A-12 (hex) Azylex Technology, Inc -00-0A-13 (hex) Honeywell Video Systems -00-0A-14 (hex) TECO a.s. -00-0A-15 (hex) Silicon Data, Inc -00-0A-16 (hex) Lassen Research -00-0A-17 (hex) NESTAR COMMUNICATIONS, INC -00-0A-18 (hex) Vichel Inc. -00-0A-19 (hex) Valere Power, Inc. -00-0A-1A (hex) Imerge Ltd -00-0A-1B (hex) Stream Labs -00-0A-1C (hex) Bridge Information Co., Ltd. -00-0A-1D (hex) Optical Communications Products Inc. -00-0A-1E (hex) Red-M Products Limited -00-0A-1F (hex) ART WARE Telecommunication Co., Ltd. -00-0A-20 (hex) SVA Networks, Inc. -00-0A-21 (hex) Integra Telecom Co. Ltd -00-0A-22 (hex) Amperion Inc -00-0A-23 (hex) Parama Networks Inc -00-0A-24 (hex) Octave Communications -00-0A-25 (hex) CERAGON NETWORKS -00-0A-26 (hex) CEIA S.p.A. -00-0A-27 (hex) Apple Computer, Inc. -00-0A-28 (hex) Motorola -00-0A-29 (hex) Pan Dacom Networking AG -00-0A-2A (hex) QSI Systems Inc. -00-0A-2B (hex) Etherstuff -00-0A-2C (hex) Active Tchnology Corporation -00-0A-2D (hex) Cabot Communications Limited -00-0A-2E (hex) MAPLE NETWORKS CO., LTD -00-0A-2F (hex) Artnix Inc. -00-0A-30 (hex) Johnson Controls-ASG -00-0A-31 (hex) HCV Wireless -00-0A-32 (hex) Xsido Corporation -00-0A-33 (hex) Emulex Corporation -00-0A-34 (hex) Identicard Systems Incorporated -00-0A-35 (hex) Xilinx -00-0A-36 (hex) Synelec Telecom Multimedia -00-0A-37 (hex) Procera Networks, Inc. -00-0A-38 (hex) Apani Networks -00-0A-39 (hex) LoPA Information Technology -00-0A-3A (hex) J-THREE INTERNATIONAL Holding Co., Ltd. -00-0A-3B (hex) GCT Semiconductor, Inc -00-0A-3C (hex) Enerpoint Ltd. -00-0A-3D (hex) Elo Sistemas Eletronicos S.A. -00-0A-3E (hex) EADS Telecom -00-0A-3F (hex) Data East Corporation -00-0A-40 (hex) Crown Audio -- Harmanm International -00-0A-41 (hex) Cisco Systems -00-0A-42 (hex) Cisco Systems -00-0A-43 (hex) Chunghwa Telecom Co., Ltd. -00-0A-44 (hex) Avery Dennison Deutschland GmbH -00-0A-45 (hex) Audio-Technica Corp. -00-0A-46 (hex) ARO Controls SAS -00-0A-47 (hex) Allied Vision Technologies -00-0A-48 (hex) Albatron Technology -00-0A-49 (hex) F5 Networks, Inc. -00-0A-4A (hex) Targa Systems Ltd. -00-0A-4B (hex) DataPower Technology, Inc. -00-0A-4C (hex) Molecular Devices Corporation -00-0A-4D (hex) Noritz Corporation -00-0A-4E (hex) UNITEK Electronics INC. -00-0A-4F (hex) Brain Boxes Limited -00-0A-50 (hex) REMOTEK CORPORATION -00-0A-51 (hex) GyroSignal Technology Co., Ltd. -00-0A-52 (hex) AsiaRF Ltd. -00-0A-53 (hex) Intronics, Incorporated -00-0A-54 (hex) Laguna Hills, Inc. -00-0A-55 (hex) MARKEM Corporation -00-0A-56 (hex) HITACHI Maxell Ltd. -00-0A-57 (hex) Hewlett-Packard Company - Standards -00-0A-58 (hex) Ingenieur-Buero Freyer & Siegel -00-0A-59 (hex) HW server -00-0A-5A (hex) GreenNET Technologies Co.,Ltd. -00-0A-5B (hex) Power-One as -00-0A-5C (hex) Carel s.p.a. -00-0A-5D (hex) PUC Founder (MSC) Berhad -00-0A-5E (hex) 3COM Corporation -00-0A-5F (hex) almedio inc. -00-0A-60 (hex) Autostar Technology Pte Ltd -00-0A-61 (hex) Cellinx Systems Inc. -00-0A-62 (hex) Crinis Networks, Inc. -00-0A-63 (hex) DHD GmbH -00-0A-64 (hex) Eracom Technologies -00-0A-65 (hex) GentechMedia.co.,ltd. -00-0A-66 (hex) MITSUBISHI ELECTRIC SYSTEM & SERVICE CO.,LTD. -00-0A-67 (hex) OngCorp -00-0A-68 (hex) SolarFlare Communications, Inc. -00-0A-69 (hex) SUNNY bell Technology Co., Ltd. -00-0A-6A (hex) SVM Microwaves s.r.o. -00-0A-6B (hex) Tadiran Telecom Business Systems LTD -00-0A-6C (hex) Walchem Corporation -00-0A-6D (hex) EKS Elektronikservice GmbH -00-0A-6E (hex) Broadcast Technology Limited -00-0A-6F (hex) ZyFLEX Technologies Inc -00-0A-70 (hex) MPLS Forum -00-0A-71 (hex) Avrio Technologies, Inc -00-0A-72 (hex) STEC, INC. -00-0A-73 (hex) Scientific Atlanta -00-0A-74 (hex) Manticom Networks Inc. -00-0A-75 (hex) Caterpillar, Inc -00-0A-76 (hex) Beida Jade Bird Huaguang Technology Co.,Ltd -00-0A-77 (hex) Bluewire Technologies LLC -00-0A-78 (hex) OLITEC -00-0A-79 (hex) Allied Telesis K.K. corega division -00-0A-7A (hex) Kyoritsu Electric Co., Ltd. -00-0A-7B (hex) Cornelius Consult -00-0A-7C (hex) Tecton Ltd -00-0A-7D (hex) Valo, Inc. -00-0A-7E (hex) The Advantage Group -00-0A-7F (hex) Teradon Industries, Inc -00-0A-80 (hex) Telkonet Inc. -00-0A-81 (hex) TEIMA Audiotex S.L. -00-0A-82 (hex) TATSUTA SYSTEM ELECTRONICS CO.,LTD. -00-0A-83 (hex) SALTO SYSTEMS S.L. -00-0A-84 (hex) Rainsun Enterprise Co., Ltd. -00-0A-85 (hex) PLAT'C2,Inc -00-0A-86 (hex) Lenze -00-0A-87 (hex) Integrated Micromachines Inc. -00-0A-88 (hex) InCypher S.A. -00-0A-89 (hex) Creval Systems, Inc. -00-0A-8A (hex) Cisco Systems -00-0A-8B (hex) Cisco Systems -00-0A-8C (hex) Guardware Systems Ltd. -00-0A-8D (hex) EUROTHERM LIMITED -00-0A-8E (hex) Invacom Ltd -00-0A-8F (hex) Aska International Inc. -00-0A-90 (hex) Bayside Interactive, Inc. -00-0A-91 (hex) HemoCue AB -00-0A-92 (hex) Presonus Corporation -00-0A-93 (hex) W2 Networks, Inc. -00-0A-94 (hex) ShangHai cellink CO., LTD -00-0A-95 (hex) Apple Computer, Inc. -00-0A-96 (hex) MEWTEL TECHNOLOGY INC. -00-0A-97 (hex) SONICblue, Inc. -00-0A-98 (hex) M+F Gwinner GmbH & Co -00-0A-99 (hex) Dataradio Inc. -00-0A-9A (hex) Aiptek International Inc -00-0A-9B (hex) Towa Meccs Corporation -00-0A-9C (hex) Server Technology, Inc. -00-0A-9D (hex) King Young Technology Co. Ltd. -00-0A-9E (hex) BroadWeb Corportation -00-0A-9F (hex) Pannaway Technologies, Inc. -00-0A-A0 (hex) Cedar Point Communications -00-0A-A1 (hex) V V S Limited -00-0A-A2 (hex) SYSTEK INC. -00-0A-A3 (hex) SHIMAFUJI ELECTRIC CO.,LTD. -00-0A-A4 (hex) SHANGHAI SURVEILLANCE TECHNOLOGY CO,LTD -00-0A-A5 (hex) MAXLINK INDUSTRIES LIMITED -00-0A-A6 (hex) Hochiki Corporation -00-0A-A7 (hex) FEI Electron Optics -00-0A-A8 (hex) ePipe Pty. Ltd. -00-0A-A9 (hex) Brooks Automation GmbH -00-0A-AA (hex) AltiGen Communications Inc. -00-0A-AB (hex) Toyota Technical Development Corporation -00-0A-AC (hex) TerraTec Electronic GmbH -00-0A-AD (hex) Stargames Corporation -00-0A-AE (hex) Rosemount Process Analytical -00-0A-AF (hex) Pipal Systems -00-0A-B0 (hex) LOYTEC electronics GmbH -00-0A-B1 (hex) GENETEC Corporation -00-0A-B2 (hex) Fresnel Wireless Systems -00-0A-B3 (hex) Fa. GIRA -00-0A-B4 (hex) ETIC Telecommunications -00-0A-B5 (hex) Digital Electronic Network -00-0A-B6 (hex) COMPUNETIX, INC -00-0A-B7 (hex) Cisco Systems -00-0A-B8 (hex) Cisco Systems -00-0A-B9 (hex) Astera Technologies Corp. -00-0A-BA (hex) Arcon Technology Limited -00-0A-BB (hex) Taiwan Secom Co,. Ltd -00-0A-BC (hex) Seabridge Ltd. -00-0A-BD (hex) Rupprecht & Patashnick Co. -00-0A-BE (hex) OPNET Technologies CO., LTD. -00-0A-BF (hex) HIROTA SS -00-0A-C0 (hex) Fuyoh Video Industry CO., LTD. -00-0A-C1 (hex) Futuretel -00-0A-C2 (hex) FiberHome Telecommunication Technologies CO.,LTD -00-0A-C3 (hex) eM Technics Co., Ltd. -00-0A-C4 (hex) Daewoo Teletech Co., Ltd -00-0A-C5 (hex) Color Kinetics -00-0A-C6 (hex) Ceterus Networks, Inc. -00-0A-C7 (hex) Unication Group -00-0A-C8 (hex) ZPSYS CO.,LTD. (Planning&Management) -00-0A-C9 (hex) Zambeel Inc -00-0A-CA (hex) YOKOYAMA SHOKAI CO.,Ltd. -00-0A-CB (hex) XPAK MSA Group -00-0A-CC (hex) Winnow Networks, Inc. -00-0A-CD (hex) Sunrich Technology Limited -00-0A-CE (hex) RADIANTECH, INC. -00-0A-CF (hex) PROVIDEO Multimedia Co. Ltd. -00-0A-D0 (hex) Niigata Develoment Center, F.I.T. Co., Ltd. -00-0A-D1 (hex) MWS -00-0A-D2 (hex) JEPICO Corporation -00-0A-D3 (hex) INITECH Co., Ltd -00-0A-D4 (hex) CoreBell Systems Inc. -00-0A-D5 (hex) Brainchild Electronic Co., Ltd. -00-0A-D6 (hex) BeamReach Networks -00-0A-D7 (hex) Origin ELECTRIC CO.,LTD. -00-0A-D8 (hex) IPCserv Technology Corp. -00-0A-D9 (hex) Sony Ericsson Mobile Communications AB -00-0A-DA (hex) Vindicator Technologies -00-0A-DB (hex) SkyPilot Network, Inc -00-0A-DC (hex) RuggedCom Inc. -00-0A-DD (hex) Allworx Corp. -00-0A-DE (hex) Happy Communication Co., Ltd. -00-0A-DF (hex) Gennum Corporation -00-0A-E0 (hex) Fujitsu Softek -00-0A-E1 (hex) EG Technology -00-0A-E2 (hex) Binatone Electronics International, Ltd -00-0A-E3 (hex) YANG MEI TECHNOLOGY CO., LTD -00-0A-E4 (hex) Wistron Corp. -00-0A-E5 (hex) ScottCare Corporation -00-0A-E6 (hex) Elitegroup Computer System Co. (ECS) -00-0A-E7 (hex) ELIOP S.A. -00-0A-E8 (hex) Cathay Roxus Information Technology Co. LTD -00-0A-E9 (hex) AirVast Technology Inc. -00-0A-EA (hex) ADAM ELEKTRONIK LTD.STI. -00-0A-EB (hex) Shenzhen Tp-Link Technology Co; Ltd. -00-0A-EC (hex) Koatsu Gas Kogyo Co., Ltd. -00-0A-ED (hex) HARTING Systems GmbH & Co KG -00-0A-EE (hex) GCD Hard- & Software GmbH -00-0A-EF (hex) OTRUM ASA -00-0A-F0 (hex) SHIN-OH ELECTRONICS CO., LTD. R&D -00-0A-F1 (hex) Clarity Design, Inc. -00-0A-F2 (hex) NeoAxiom Corp. -00-0A-F3 (hex) Cisco Systems -00-0A-F4 (hex) Cisco Systems -00-0A-F5 (hex) Airgo Networks, Inc. -00-0A-F6 (hex) Emerson Climate Technologies Retail Solutions, Inc. -00-0A-F7 (hex) Broadcom Corp. -00-0A-F8 (hex) American Telecare Inc. -00-0A-F9 (hex) HiConnect, Inc. -00-0A-FA (hex) Traverse Technologies Australia -00-0A-FB (hex) Ambri Limited -00-0A-FC (hex) Core Tec Communications, LLC -00-0A-FD (hex) Viking Electronic Services -00-0A-FE (hex) NovaPal Ltd -00-0A-FF (hex) Kilchherr Elektronik AG -00-0B-00 (hex) FUJIAN START COMPUTER EQUIPMENT CO.,LTD -00-0B-01 (hex) DAIICHI ELECTRONICS CO., LTD. -00-0B-02 (hex) Dallmeier electronic -00-0B-03 (hex) Taekwang Industrial Co., Ltd -00-0B-04 (hex) Volktek Corporation -00-0B-05 (hex) Pacific Broadband Networks -00-0B-06 (hex) Motorola BCS -00-0B-07 (hex) Voxpath Networks -00-0B-08 (hex) Pillar Data Systems -00-0B-09 (hex) Ifoundry Systems Singapore -00-0B-0A (hex) dBm Optics -00-0B-0B (hex) Corrent Corporation -00-0B-0C (hex) Agile Systems Inc. -00-0B-0D (hex) Air2U, Inc. -00-0B-0E (hex) Trapeze Networks -00-0B-0F (hex) Nyquist Industrial Control BV -00-0B-10 (hex) 11wave Technonlogy Co.,Ltd -00-0B-11 (hex) HIMEJI ABC TRADING CO.,LTD. -00-0B-12 (hex) NURI Telecom Co., Ltd. -00-0B-13 (hex) ZETRON INC -00-0B-14 (hex) ViewSonic Corporation -00-0B-15 (hex) Platypus Technology -00-0B-16 (hex) Communication Machinery Corporation -00-0B-17 (hex) MKS Instruments -00-0B-18 (hex) PRIVATE -00-0B-19 (hex) Vernier Networks, Inc. -00-0B-1A (hex) Industrial Defender, Inc. -00-0B-1B (hex) Systronix, Inc. -00-0B-1C (hex) SIBCO bv -00-0B-1D (hex) LayerZero Power Systems, Inc. -00-0B-1E (hex) KAPPA opto-electronics GmbH -00-0B-1F (hex) I CON Computer Co. -00-0B-20 (hex) Hirata corporation -00-0B-21 (hex) G-Star Communications Inc. -00-0B-22 (hex) Environmental Systems and Services -00-0B-23 (hex) Siemens Subscriber Networks -00-0B-24 (hex) AirLogic -00-0B-25 (hex) Aeluros -00-0B-26 (hex) Wetek Corporation -00-0B-27 (hex) Scion Corporation -00-0B-28 (hex) Quatech Inc. -00-0B-29 (hex) LS(LG) Industrial Systems co.,Ltd -00-0B-2A (hex) HOWTEL Co., Ltd. -00-0B-2B (hex) HOSTNET CORPORATION -00-0B-2C (hex) Eiki Industrial Co. Ltd. -00-0B-2D (hex) Danfoss Inc. -00-0B-2E (hex) Cal-Comp Electronics (Thailand) Public Company Limited Taipe -00-0B-2F (hex) bplan GmbH -00-0B-30 (hex) Beijing Gongye Science & Technology Co.,Ltd -00-0B-31 (hex) Yantai ZhiYang Scientific and technology industry CO., LTD -00-0B-32 (hex) VORMETRIC, INC. -00-0B-33 (hex) Vivato -00-0B-34 (hex) ShangHai Broadband Technologies CO.LTD -00-0B-35 (hex) Quad Bit System co., Ltd. -00-0B-36 (hex) Productivity Systems, Inc. -00-0B-37 (hex) MANUFACTURE DES MONTRES ROLEX SA -00-0B-38 (hex) Knuerr AG -00-0B-39 (hex) Keisoku Giken Co.,Ltd. -00-0B-3A (hex) QuStream Corporation -00-0B-3B (hex) devolo AG -00-0B-3C (hex) Cygnal Integrated Products, Inc. -00-0B-3D (hex) CONTAL OK Ltd. -00-0B-3E (hex) BittWare, Inc -00-0B-3F (hex) Anthology Solutions Inc. -00-0B-40 (hex) OpNext Inc. -00-0B-41 (hex) Ing. Buero Dr. Beutlhauser -00-0B-42 (hex) commax Co., Ltd. -00-0B-43 (hex) Microscan Systems, Inc. -00-0B-44 (hex) Concord IDea Corp. -00-0B-45 (hex) Cisco -00-0B-46 (hex) Cisco -00-0B-47 (hex) Advanced Energy -00-0B-48 (hex) sofrel -00-0B-49 (hex) RF-Link System Inc. -00-0B-4A (hex) Visimetrics (UK) Ltd -00-0B-4B (hex) VISIOWAVE SA -00-0B-4C (hex) Clarion (M) Sdn Bhd -00-0B-4D (hex) Emuzed -00-0B-4E (hex) VertexRSI, General Dynamics SatCOM Technologies, Inc. -00-0B-4F (hex) Verifone, INC. -00-0B-50 (hex) Oxygnet -00-0B-51 (hex) Micetek International Inc. -00-0B-52 (hex) JOYMAX ELECTRONICS CORP. -00-0B-53 (hex) INITIUM Co., Ltd. -00-0B-54 (hex) BiTMICRO Networks, Inc. -00-0B-55 (hex) ADInstruments -00-0B-56 (hex) Cybernetics -00-0B-57 (hex) Silicon Laboratories -00-0B-58 (hex) Astronautics C.A LTD -00-0B-59 (hex) ScriptPro, LLC -00-0B-5A (hex) HyperEdge -00-0B-5B (hex) Rincon Research Corporation -00-0B-5C (hex) Newtech Co.,Ltd -00-0B-5D (hex) FUJITSU LIMITED -00-0B-5E (hex) Audio Engineering Society Inc. -00-0B-5F (hex) Cisco Systems -00-0B-60 (hex) Cisco Systems -00-0B-61 (hex) Friedrich Ltze GmbH &Co. -00-0B-62 (hex) Ingenieurbuero fuer Elektronikdesign Ingo Mohnen -00-0B-63 (hex) Kaleidescape -00-0B-64 (hex) Kieback & Peter GmbH & Co KG -00-0B-65 (hex) Sy.A.C. srl -00-0B-66 (hex) Teralink Communications -00-0B-67 (hex) Topview Technology Corporation -00-0B-68 (hex) Addvalue Communications Pte Ltd -00-0B-69 (hex) Franke Finland Oy -00-0B-6A (hex) Asiarock Incorporation -00-0B-6B (hex) Wistron Neweb Corp. -00-0B-6C (hex) Sychip Inc. -00-0B-6D (hex) SOLECTRON JAPAN NAKANIIDA -00-0B-6E (hex) Neff Instrument Corp. -00-0B-6F (hex) Media Streaming Networks Inc -00-0B-70 (hex) Load Technology, Inc. -00-0B-71 (hex) Litchfield Communications Inc. -00-0B-72 (hex) Lawo AG -00-0B-73 (hex) Kodeos Communications -00-0B-74 (hex) Kingwave Technology Co., Ltd. -00-0B-75 (hex) Iosoft Ltd. -00-0B-76 (hex) ET&T Technology Co. Ltd. -00-0B-77 (hex) Cogent Systems, Inc. -00-0B-78 (hex) TAIFATECH INC. -00-0B-79 (hex) X-COM, Inc. -00-0B-7A (hex) Wave Science Inc. -00-0B-7B (hex) Test-Um Inc. -00-0B-7C (hex) Telex Communications -00-0B-7D (hex) SOLOMON EXTREME INTERNATIONAL LTD. -00-0B-7E (hex) SAGINOMIYA Seisakusho Inc. -00-0B-7F (hex) OmniWerks -00-0B-80 (hex) Lycium Networks -00-0B-81 (hex) Kaparel Corporation -00-0B-82 (hex) Grandstream Networks, Inc. -00-0B-83 (hex) DATAWATT B.V. -00-0B-84 (hex) BODET -00-0B-85 (hex) Cisco Systems -00-0B-86 (hex) Aruba Networks -00-0B-87 (hex) American Reliance Inc. -00-0B-88 (hex) Vidisco ltd. -00-0B-89 (hex) Top Global Technology, Ltd. -00-0B-8A (hex) MITEQ Inc. -00-0B-8B (hex) KERAJET, S.A. -00-0B-8C (hex) Flextronics -00-0B-8D (hex) Avvio Networks -00-0B-8E (hex) Ascent Corporation -00-0B-8F (hex) AKITA ELECTRONICS SYSTEMS CO.,LTD. -00-0B-90 (hex) Adva Optical Networking Inc. -00-0B-91 (hex) Aglaia Gesellschaft fr Bildverarbeitung und Kommunikation m -00-0B-92 (hex) Ascom Danmark A/S -00-0B-93 (hex) Ritter Elektronik -00-0B-94 (hex) Digital Monitoring Products, Inc. -00-0B-95 (hex) eBet Gaming Systems Pty Ltd -00-0B-96 (hex) Innotrac Diagnostics Oy -00-0B-97 (hex) Matsushita Electric Industrial Co.,Ltd. -00-0B-98 (hex) NiceTechVision -00-0B-99 (hex) SensAble Technologies, Inc. -00-0B-9A (hex) Shanghai Ulink Telecom Equipment Co. Ltd. -00-0B-9B (hex) Sirius System Co, Ltd. -00-0B-9C (hex) TriBeam Technologies, Inc. -00-0B-9D (hex) TwinMOS Technologies Inc. -00-0B-9E (hex) Yasing Technology Corp. -00-0B-9F (hex) Neue ELSA GmbH -00-0B-A0 (hex) T&L Information Inc. -00-0B-A1 (hex) SYSCOM Ltd. -00-0B-A2 (hex) Sumitomo Electric Networks, Inc -00-0B-A3 (hex) Siemens AG, I&S -00-0B-A4 (hex) Shiron Satellite Communications Ltd. (1996) -00-0B-A5 (hex) Quasar Cipta Mandiri, PT -00-0B-A6 (hex) Miyakawa Electric Works Ltd. -00-0B-A7 (hex) Maranti Networks -00-0B-A8 (hex) HANBACK ELECTRONICS CO., LTD. -00-0B-A9 (hex) CloudShield Technologies, Inc. -00-0B-AA (hex) Aiphone co.,Ltd -00-0B-AB (hex) Advantech Technology (CHINA) Co., Ltd. -00-0B-AC (hex) 3Com Ltd -00-0B-AD (hex) PC-PoS Inc. -00-0B-AE (hex) Vitals System Inc. -00-0B-AF (hex) WOOJU COMMUNICATIONS Co,.Ltd -00-0B-B0 (hex) Sysnet Telematica srl -00-0B-B1 (hex) Super Star Technology Co., Ltd. -00-0B-B2 (hex) SMALLBIG TECHNOLOGY -00-0B-B3 (hex) RiT technologies Ltd. -00-0B-B4 (hex) RDC Semiconductor Inc., -00-0B-B5 (hex) nStor Technologies, Inc. -00-0B-B6 (hex) Mototech Inc. -00-0B-B7 (hex) Micro Systems Co.,Ltd. -00-0B-B8 (hex) Kihoku Electronic Co. -00-0B-B9 (hex) Imsys AB -00-0B-BA (hex) Harmonic Broadband Access Networks -00-0B-BB (hex) Etin Systems Co., Ltd -00-0B-BC (hex) En Garde Systems, Inc. -00-0B-BD (hex) Connexionz Limited -00-0B-BE (hex) Cisco Systems -00-0B-BF (hex) Cisco Systems -00-0B-C0 (hex) China IWNComm Co., Ltd. -00-0B-C1 (hex) Bay Microsystems, Inc. -00-0B-C2 (hex) Corinex Communication Corp. -00-0B-C3 (hex) Multiplex, Inc. -00-0B-C4 (hex) BIOTRONIK GmbH & Co -00-0B-C5 (hex) SMC Networks, Inc. -00-0B-C6 (hex) ISAC, Inc. -00-0B-C7 (hex) ICET S.p.A. -00-0B-C8 (hex) AirFlow Networks -00-0B-C9 (hex) Electroline Equipment -00-0B-CA (hex) DATAVAN International Corporation -00-0B-CB (hex) Fagor Automation , S. Coop -00-0B-CC (hex) JUSAN, S.A. -00-0B-CD (hex) Hewlett Packard -00-0B-CE (hex) Free2move AB -00-0B-CF (hex) AGFA NDT INC. -00-0B-D0 (hex) XiMeta Technology Americas Inc. -00-0B-D1 (hex) Aeronix, Inc. -00-0B-D2 (hex) Remopro Technology Inc. -00-0B-D3 (hex) cd3o -00-0B-D4 (hex) Beijing Wise Technology & Science Development Co.Ltd -00-0B-D5 (hex) Nvergence, Inc. -00-0B-D6 (hex) Paxton Access Ltd -00-0B-D7 (hex) DORMA Time + Access GmbH -00-0B-D8 (hex) Industrial Scientific Corp. -00-0B-D9 (hex) General Hydrogen -00-0B-DA (hex) EyeCross Co.,Inc. -00-0B-DB (hex) Dell ESG PCBA Test -00-0B-DC (hex) AKCP -00-0B-DD (hex) TOHOKU RICOH Co., LTD. -00-0B-DE (hex) TELDIX GmbH -00-0B-DF (hex) Shenzhen RouterD Networks Limited -00-0B-E0 (hex) SercoNet Ltd. -00-0B-E1 (hex) Nokia NET Product Operations -00-0B-E2 (hex) Lumenera Corporation -00-0B-E3 (hex) Key Stream Co., Ltd. -00-0B-E4 (hex) Hosiden Corporation -00-0B-E5 (hex) HIMS Korea Co., Ltd. -00-0B-E6 (hex) Datel Electronics -00-0B-E7 (hex) COMFLUX TECHNOLOGY INC. -00-0B-E8 (hex) AOIP -00-0B-E9 (hex) Actel Corporation -00-0B-EA (hex) Zultys Technologies -00-0B-EB (hex) Systegra AG -00-0B-EC (hex) NIPPON ELECTRIC INSTRUMENT, INC. -00-0B-ED (hex) ELM Inc. -00-0B-EE (hex) inc.jet, Incorporated -00-0B-EF (hex) Code Corporation -00-0B-F0 (hex) MoTEX Products Co., Ltd. -00-0B-F1 (hex) LAP Laser Applikations -00-0B-F2 (hex) Chih-Kan Technology Co., Ltd. -00-0B-F3 (hex) BAE SYSTEMS -00-0B-F4 (hex) PRIVATE -00-0B-F5 (hex) Shanghai Sibo Telecom Technology Co.,Ltd -00-0B-F6 (hex) Nitgen Co., Ltd -00-0B-F7 (hex) NIDEK CO.,LTD -00-0B-F8 (hex) Infinera -00-0B-F9 (hex) Gemstone communications, Inc. -00-0B-FA (hex) EXEMYS SRL -00-0B-FB (hex) D-NET International Corporation -00-0B-FC (hex) Cisco Systems -00-0B-FD (hex) Cisco Systems -00-0B-FE (hex) CASTEL Broadband Limited -00-0B-FF (hex) Berkeley Camera Engineering -00-0C-00 (hex) BEB Industrie-Elektronik AG -00-0C-01 (hex) Abatron AG -00-0C-02 (hex) ABB Oy -00-0C-03 (hex) HDMI Licensing, LLC -00-0C-04 (hex) Tecnova -00-0C-05 (hex) RPA Reserch Co., Ltd. -00-0C-06 (hex) Nixvue Systems Pte Ltd -00-0C-07 (hex) Iftest AG -00-0C-08 (hex) HUMEX Technologies Corp. -00-0C-09 (hex) Hitachi IE Systems Co., Ltd -00-0C-0A (hex) Guangdong Province Electronic Technology Research Institute -00-0C-0B (hex) Broadbus Technologies -00-0C-0C (hex) APPRO TECHNOLOGY INC. -00-0C-0D (hex) Communications & Power Industries / Satcom Division -00-0C-0E (hex) XtremeSpectrum, Inc. -00-0C-0F (hex) Techno-One Co., Ltd -00-0C-10 (hex) PNI Corporation -00-0C-11 (hex) NIPPON DEMPA CO.,LTD. -00-0C-12 (hex) Micro-Optronic-Messtechnik GmbH -00-0C-13 (hex) MediaQ -00-0C-14 (hex) Diagnostic Instruments, Inc. -00-0C-15 (hex) CyberPower Systems, Inc. -00-0C-16 (hex) Concorde Microsystems Inc. -00-0C-17 (hex) AJA Video Systems Inc -00-0C-18 (hex) Zenisu Keisoku Inc. -00-0C-19 (hex) Telio Communications GmbH -00-0C-1A (hex) Quest Technical Solutions Inc. -00-0C-1B (hex) ORACOM Co, Ltd. -00-0C-1C (hex) MicroWeb Co., Ltd. -00-0C-1D (hex) Mettler & Fuchs AG -00-0C-1E (hex) Global Cache -00-0C-1F (hex) Glimmerglass Networks -00-0C-20 (hex) Fi WIn, Inc. -00-0C-21 (hex) Faculty of Science and Technology, Keio University -00-0C-22 (hex) Double D Electronics Ltd -00-0C-23 (hex) Beijing Lanchuan Tech. Co., Ltd. -00-0C-24 (hex) ANATOR -00-0C-25 (hex) Allied Telesyn Networks -00-0C-26 (hex) Weintek Labs. Inc. -00-0C-27 (hex) Sammy Corporation -00-0C-28 (hex) RIFATRON -00-0C-29 (hex) VMware, Inc. -00-0C-2A (hex) OCTTEL Communication Co., Ltd. -00-0C-2B (hex) ELIAS Technology, Inc. -00-0C-2C (hex) Enwiser Inc. -00-0C-2D (hex) FullWave Technology Co., Ltd. -00-0C-2E (hex) Openet information technology(shenzhen) Co., Ltd. -00-0C-2F (hex) SeorimTechnology Co.,Ltd. -00-0C-30 (hex) Cisco -00-0C-31 (hex) Cisco -00-0C-32 (hex) Avionic Design Development GmbH -00-0C-33 (hex) Compucase Enterprise Co. Ltd. -00-0C-34 (hex) Vixen Co., Ltd. -00-0C-35 (hex) KaVo Dental GmbH & Co. KG -00-0C-36 (hex) SHARP TAKAYA ELECTRONICS INDUSTRY CO.,LTD. -00-0C-37 (hex) Geomation, Inc. -00-0C-38 (hex) TelcoBridges Inc. -00-0C-39 (hex) Sentinel Wireless Inc. -00-0C-3A (hex) Oxance -00-0C-3B (hex) Orion Electric Co., Ltd. -00-0C-3C (hex) MediaChorus, Inc. -00-0C-3D (hex) Glsystech Co., Ltd. -00-0C-3E (hex) Crest Audio -00-0C-3F (hex) Cogent Defence & Security Networks, -00-0C-40 (hex) Altech Controls -00-0C-41 (hex) Cisco-Linksys -00-0C-42 (hex) Routerboard.com -00-0C-43 (hex) Ralink Technology, Corp. -00-0C-44 (hex) Automated Interfaces, Inc. -00-0C-45 (hex) Animation Technologies Inc. -00-0C-46 (hex) Allied Telesyn Inc. -00-0C-47 (hex) SK Teletech(R&D Planning Team) -00-0C-48 (hex) QoStek Corporation -00-0C-49 (hex) Dangaard Telecom RTC Division A/S -00-0C-4A (hex) Cygnus Microsystems (P) Limited -00-0C-4B (hex) Cheops Elektronik -00-0C-4C (hex) Arcor AG&Co. -00-0C-4D (hex) ACRA CONTROL -00-0C-4E (hex) Winbest Technology CO,LT -00-0C-4F (hex) UDTech Japan Corporation -00-0C-50 (hex) Seagate Technology -00-0C-51 (hex) Scientific Technologies Inc. -00-0C-52 (hex) Roll Systems Inc. -00-0C-53 (hex) PRIVATE -00-0C-54 (hex) Pedestal Networks, Inc -00-0C-55 (hex) Microlink Communications Inc. -00-0C-56 (hex) Megatel Computer (1986) Corp. -00-0C-57 (hex) MACKIE Engineering Services Belgium BVBA -00-0C-58 (hex) M&S Systems -00-0C-59 (hex) Indyme Electronics, Inc. -00-0C-5A (hex) IBSmm Industrieelektronik Multimedia -00-0C-5B (hex) HANWANG TECHNOLOGY CO.,LTD -00-0C-5C (hex) GTN Systems B.V. -00-0C-5D (hex) CHIC TECHNOLOGY (CHINA) CORP. -00-0C-5E (hex) Calypso Medical -00-0C-5F (hex) Avtec, Inc. -00-0C-60 (hex) ACM Systems -00-0C-61 (hex) AC Tech corporation DBA Advanced Digital -00-0C-62 (hex) ABB Automation Technology Products AB, Control -00-0C-63 (hex) Zenith Electronics Corporation -00-0C-64 (hex) X2 MSA Group -00-0C-65 (hex) Sunin Telecom -00-0C-66 (hex) Pronto Networks Inc -00-0C-67 (hex) OYO ELECTRIC CO.,LTD -00-0C-68 (hex) SigmaTel, Inc. -00-0C-69 (hex) National Radio Astronomy Observatory -00-0C-6A (hex) MBARI -00-0C-6B (hex) Kurz Industrie-Elektronik GmbH -00-0C-6C (hex) Elgato Systems LLC -00-0C-6D (hex) Edwards Ltd. -00-0C-6E (hex) ASUSTEK COMPUTER INC. -00-0C-6F (hex) Amtek system co.,LTD. -00-0C-70 (hex) ACC GmbH -00-0C-71 (hex) Wybron, Inc -00-0C-72 (hex) Tempearl Industrial Co., Ltd. -00-0C-73 (hex) TELSON ELECTRONICS CO., LTD -00-0C-74 (hex) RIVERTEC CORPORATION -00-0C-75 (hex) Oriental integrated electronics. LTD -00-0C-76 (hex) MICRO-STAR INTERNATIONAL CO., LTD. -00-0C-77 (hex) Life Racing Ltd -00-0C-78 (hex) In-Tech Electronics Limited -00-0C-79 (hex) Extel Communications P/L -00-0C-7A (hex) DaTARIUS Technologies GmbH -00-0C-7B (hex) ALPHA PROJECT Co.,Ltd. -00-0C-7C (hex) Internet Information Image Inc. -00-0C-7D (hex) TEIKOKU ELECTRIC MFG. CO., LTD -00-0C-7E (hex) Tellium Incorporated -00-0C-7F (hex) synertronixx GmbH -00-0C-80 (hex) Opelcomm Inc. -00-0C-81 (hex) Nulec Industries Pty Ltd -00-0C-82 (hex) NETWORK TECHNOLOGIES INC -00-0C-83 (hex) Logical Solutions -00-0C-84 (hex) Eazix, Inc. -00-0C-85 (hex) Cisco Systems -00-0C-86 (hex) Cisco Systems -00-0C-87 (hex) AMD -00-0C-88 (hex) Apache Micro Peripherals, Inc. -00-0C-89 (hex) AC Electric Vehicles, Ltd. -00-0C-8A (hex) Bose Corporation -00-0C-8B (hex) Connect Tech Inc -00-0C-8C (hex) KODICOM CO.,LTD. -00-0C-8D (hex) MATRIX VISION GmbH -00-0C-8E (hex) Mentor Engineering Inc -00-0C-8F (hex) Nergal s.r.l. -00-0C-90 (hex) Octasic Inc. -00-0C-91 (hex) Riverhead Networks Inc. -00-0C-92 (hex) WolfVision Gmbh -00-0C-93 (hex) Xeline Co., Ltd. -00-0C-94 (hex) United Electronic Industries, Inc. (EUI) -00-0C-95 (hex) PrimeNet -00-0C-96 (hex) OQO, Inc. -00-0C-97 (hex) NV ADB TTV Technologies SA -00-0C-98 (hex) LETEK Communications Inc. -00-0C-99 (hex) HITEL LINK Co.,Ltd -00-0C-9A (hex) Hitech Electronics Corp. -00-0C-9B (hex) EE Solutions, Inc -00-0C-9C (hex) Chongho information & communications -00-0C-9D (hex) AirWalk Communications, Inc. -00-0C-9E (hex) MemoryLink Corp. -00-0C-9F (hex) NKE Corporation -00-0C-A0 (hex) StorCase Technology, Inc. -00-0C-A1 (hex) SIGMACOM Co., LTD. -00-0C-A2 (hex) Scopus Network Technologies Ltd -00-0C-A3 (hex) Rancho Technology, Inc. -00-0C-A4 (hex) Prompttec Product Management GmbH -00-0C-A5 (hex) Naman NZ LTd -00-0C-A6 (hex) Mintera Corporation -00-0C-A7 (hex) Metro (Suzhou) Technologies Co., Ltd. -00-0C-A8 (hex) Garuda Networks Corporation -00-0C-A9 (hex) Ebtron Inc. -00-0C-AA (hex) Cubic Transportation Systems Inc -00-0C-AB (hex) COMMEND International -00-0C-AC (hex) Citizen Watch Co., Ltd. -00-0C-AD (hex) BTU International -00-0C-AE (hex) Ailocom Oy -00-0C-AF (hex) TRI TERM CO.,LTD. -00-0C-B0 (hex) Star Semiconductor Corporation -00-0C-B1 (hex) Salland Engineering (Europe) BV -00-0C-B2 (hex) Comstar Co., Ltd. -00-0C-B3 (hex) ROUND Co.,Ltd. -00-0C-B4 (hex) AutoCell Laboratories, Inc. -00-0C-B5 (hex) Premier Technolgies, Inc -00-0C-B6 (hex) NANJING SEU MOBILE & INTERNET TECHNOLOGY CO.,LTD -00-0C-B7 (hex) Nanjing Huazhuo Electronics Co., Ltd. -00-0C-B8 (hex) MEDION AG -00-0C-B9 (hex) LEA -00-0C-BA (hex) Jamex, Inc. -00-0C-BB (hex) ISKRAEMECO -00-0C-BC (hex) Iscutum -00-0C-BD (hex) Interface Masters, Inc -00-0C-BE (hex) Innominate Security Technologies AG -00-0C-BF (hex) Holy Stone Ent. Co., Ltd. -00-0C-C0 (hex) Genera Oy -00-0C-C1 (hex) Cooper Industries Inc. -00-0C-C2 (hex) ControlNet (India) Private Limited -00-0C-C3 (hex) BeWAN systems -00-0C-C4 (hex) Tiptel AG -00-0C-C5 (hex) Nextlink Co., Ltd. -00-0C-C6 (hex) Ka-Ro electronics GmbH -00-0C-C7 (hex) Intelligent Computer Solutions Inc. -00-0C-C8 (hex) Xytronix Research & Design, Inc. -00-0C-C9 (hex) ILWOO DATA & TECHNOLOGY CO.,LTD -00-0C-CA (hex) Hitachi Global Storage Technologies -00-0C-CB (hex) Design Combus Ltd -00-0C-CC (hex) Aeroscout Ltd. -00-0C-CD (hex) IEC - TC57 -00-0C-CE (hex) Cisco Systems -00-0C-CF (hex) Cisco Systems -00-0C-D0 (hex) Symetrix -00-0C-D1 (hex) SFOM Technology Corp. -00-0C-D2 (hex) Schaffner EMV AG -00-0C-D3 (hex) Prettl Elektronik Radeberg GmbH -00-0C-D4 (hex) Positron Public Safety Systems inc. -00-0C-D5 (hex) Passave Inc. -00-0C-D6 (hex) PARTNER TECH -00-0C-D7 (hex) Nallatech Ltd -00-0C-D8 (hex) M. K. Juchheim GmbH & Co -00-0C-D9 (hex) Itcare Co., Ltd -00-0C-DA (hex) FreeHand Systems, Inc. -00-0C-DB (hex) Brocade Communications Systems, Inc -00-0C-DC (hex) BECS Technology, Inc -00-0C-DD (hex) AOS Technologies AG -00-0C-DE (hex) ABB STOTZ-KONTAKT GmbH -00-0C-DF (hex) PULNiX America, Inc -00-0C-E0 (hex) Trek Diagnostics Inc. -00-0C-E1 (hex) The Open Group -00-0C-E2 (hex) Rolls-Royce -00-0C-E3 (hex) Option International N.V. -00-0C-E4 (hex) NeuroCom International, Inc. -00-0C-E5 (hex) Motorola BCS -00-0C-E6 (hex) Meru Networks Inc -00-0C-E7 (hex) MediaTek Inc. -00-0C-E8 (hex) GuangZhou AnJuBao Co., Ltd -00-0C-E9 (hex) BLOOMBERG L.P. -00-0C-EA (hex) aphona Kommunikationssysteme -00-0C-EB (hex) CNMP Networks, Inc. -00-0C-EC (hex) Spectracom Corp. -00-0C-ED (hex) Real Digital Media -00-0C-EE (hex) jp-embedded -00-0C-EF (hex) Open Networks Engineering Ltd -00-0C-F0 (hex) M & N GmbH -00-0C-F1 (hex) Intel Corporation -00-0C-F2 (hex) GAMESA ELICA -00-0C-F3 (hex) CALL IMAGE SA -00-0C-F4 (hex) AKATSUKI ELECTRIC MFG.CO.,LTD. -00-0C-F5 (hex) InfoExpress -00-0C-F6 (hex) Sitecom Europe BV -00-0C-F7 (hex) Nortel Networks -00-0C-F8 (hex) Nortel Networks -00-0C-F9 (hex) ITT Flygt AB -00-0C-FA (hex) Digital Systems Corp -00-0C-FB (hex) Korea Network Systems -00-0C-FC (hex) S2io Technologies Corp -00-0C-FD (hex) Hyundai ImageQuest Co.,Ltd. -00-0C-FE (hex) Grand Electronic Co., Ltd -00-0C-FF (hex) MRO-TEK LIMITED -00-0D-00 (hex) Seaway Networks Inc. -00-0D-01 (hex) P&E Microcomputer Systems, Inc. -00-0D-02 (hex) NEC AccessTechnica,Ltd -00-0D-03 (hex) Matrics, Inc. -00-0D-04 (hex) Foxboro Eckardt Development GmbH -00-0D-05 (hex) cybernet manufacturing inc. -00-0D-06 (hex) Compulogic Limited -00-0D-07 (hex) Calrec Audio Ltd -00-0D-08 (hex) AboveCable, Inc. -00-0D-09 (hex) Yuehua(Zhuhai) Electronic CO. LTD -00-0D-0A (hex) Projectiondesign as -00-0D-0B (hex) Buffalo Inc. -00-0D-0C (hex) MDI Security Systems -00-0D-0D (hex) ITSupported, LLC -00-0D-0E (hex) Inqnet Systems, Inc. -00-0D-0F (hex) Finlux Ltd -00-0D-10 (hex) Embedtronics Oy -00-0D-11 (hex) DENTSPLY - Gendex -00-0D-12 (hex) AXELL Corporation -00-0D-13 (hex) Wilhelm Rutenbeck GmbH&Co. -00-0D-14 (hex) Vtech Innovation LP dba Advanced American Telephones -00-0D-15 (hex) Voipac s.r.o. -00-0D-16 (hex) UHS Systems Pty Ltd -00-0D-17 (hex) Turbo Networks Co.Ltd -00-0D-18 (hex) Mega-Trend Electronics CO., LTD. -00-0D-19 (hex) ROBE Show lighting -00-0D-1A (hex) Mustek System Inc. -00-0D-1B (hex) Kyoto Electronics Manufacturing Co., Ltd. -00-0D-1C (hex) Amesys Defense -00-0D-1D (hex) HIGH-TEK HARNESS ENT. CO., LTD. -00-0D-1E (hex) Control Techniques -00-0D-1F (hex) AV Digital -00-0D-20 (hex) ASAHIKASEI TECHNOSYSTEM CO.,LTD. -00-0D-21 (hex) WISCORE Inc. -00-0D-22 (hex) Unitronics LTD -00-0D-23 (hex) Smart Solution, Inc -00-0D-24 (hex) SENTEC E&E CO., LTD. -00-0D-25 (hex) SANDEN CORPORATION -00-0D-26 (hex) Primagraphics Limited -00-0D-27 (hex) MICROPLEX Printware AG -00-0D-28 (hex) Cisco -00-0D-29 (hex) Cisco -00-0D-2A (hex) Scanmatic AS -00-0D-2B (hex) Racal Instruments -00-0D-2C (hex) Patapsco Designs Ltd -00-0D-2D (hex) NCT Deutschland GmbH -00-0D-2E (hex) Matsushita Avionics Systems Corporation -00-0D-2F (hex) AIN Comm.Tech.Co., LTD -00-0D-30 (hex) IceFyre Semiconductor -00-0D-31 (hex) Compellent Technologies, Inc. -00-0D-32 (hex) DispenseSource, Inc. -00-0D-33 (hex) Prediwave Corp. -00-0D-34 (hex) Shell International Exploration and Production, Inc. -00-0D-35 (hex) PAC International Ltd -00-0D-36 (hex) Wu Han Routon Electronic Co., Ltd -00-0D-37 (hex) WIPLUG -00-0D-38 (hex) NISSIN INC. -00-0D-39 (hex) Network Electronics -00-0D-3A (hex) Microsoft Corp. -00-0D-3B (hex) Microelectronics Technology Inc. -00-0D-3C (hex) i.Tech Dynamic Ltd -00-0D-3D (hex) Hammerhead Systems, Inc. -00-0D-3E (hex) APLUX Communications Ltd. -00-0D-3F (hex) VTI Instruments Corporation -00-0D-40 (hex) Verint Loronix Video Solutions -00-0D-41 (hex) Siemens AG ICM MP UC RD IT KLF1 -00-0D-42 (hex) Newbest Development Limited -00-0D-43 (hex) DRS Tactical Systems Inc. -00-0D-44 (hex) Audio BU - Logitech -00-0D-45 (hex) Tottori SANYO Electric Co., Ltd. -00-0D-46 (hex) Parker SSD Drives -00-0D-47 (hex) Collex -00-0D-48 (hex) AEWIN Technologies Co., Ltd. -00-0D-49 (hex) Triton Systems of Delaware, Inc. -00-0D-4A (hex) Steag ETA-Optik -00-0D-4B (hex) Roku, LLC -00-0D-4C (hex) Outline Electronics Ltd. -00-0D-4D (hex) Ninelanes -00-0D-4E (hex) NDR Co.,LTD. -00-0D-4F (hex) Kenwood Corporation -00-0D-50 (hex) Galazar Networks -00-0D-51 (hex) DIVR Systems, Inc. -00-0D-52 (hex) Comart system -00-0D-53 (hex) Beijing 5w Communication Corp. -00-0D-54 (hex) 3Com Ltd -00-0D-55 (hex) SANYCOM Technology Co.,Ltd -00-0D-56 (hex) Dell PCBA Test -00-0D-57 (hex) Fujitsu I-Network Systems Limited. -00-0D-58 (hex) PRIVATE -00-0D-59 (hex) Amity Systems, Inc. -00-0D-5A (hex) Tiesse SpA -00-0D-5B (hex) Smart Empire Investments Limited -00-0D-5C (hex) Robert Bosch GmbH, VT-ATMO -00-0D-5D (hex) Raritan Computer, Inc -00-0D-5E (hex) NEC Personal Products -00-0D-5F (hex) Minds Inc -00-0D-60 (hex) IBM Corporation -00-0D-61 (hex) Giga-Byte Technology Co., Ltd. -00-0D-62 (hex) Funkwerk Dabendorf GmbH -00-0D-63 (hex) DENT Instruments, Inc. -00-0D-64 (hex) COMAG Handels AG -00-0D-65 (hex) Cisco Systems -00-0D-66 (hex) Cisco Systems -00-0D-67 (hex) BelAir Networks Inc. -00-0D-68 (hex) Vinci Systems, Inc. -00-0D-69 (hex) TMT&D Corporation -00-0D-6A (hex) Redwood Technologies LTD -00-0D-6B (hex) Mita-Teknik A/S -00-0D-6C (hex) M-Audio -00-0D-6D (hex) K-Tech Devices Corp. -00-0D-6E (hex) K-Patents Oy -00-0D-6F (hex) Ember Corporation -00-0D-70 (hex) Datamax Corporation -00-0D-71 (hex) boca systems -00-0D-72 (hex) 2Wire, Inc -00-0D-73 (hex) Technical Support, Inc. -00-0D-74 (hex) Sand Network Systems, Inc. -00-0D-75 (hex) Kobian Pte Ltd - Taiwan Branch -00-0D-76 (hex) Hokuto Denshi Co,. Ltd. -00-0D-77 (hex) FalconStor Software -00-0D-78 (hex) Engineering & Security -00-0D-79 (hex) Dynamic Solutions Co,.Ltd. -00-0D-7A (hex) DiGATTO Asia Pacific Pte Ltd -00-0D-7B (hex) Consensys Computers Inc. -00-0D-7C (hex) Codian Ltd -00-0D-7D (hex) Afco Systems -00-0D-7E (hex) Axiowave Networks, Inc. -00-0D-7F (hex) MIDAS COMMUNICATION TECHNOLOGIES PTE LTD ( Foreign Branch) -00-0D-80 (hex) Online Development Inc -00-0D-81 (hex) Pepperl+Fuchs GmbH -00-0D-82 (hex) PHS srl -00-0D-83 (hex) Sanmina-SCI Hungary Ltd. -00-0D-84 (hex) Makus Inc. -00-0D-85 (hex) Tapwave, Inc. -00-0D-86 (hex) Huber + Suhner AG -00-0D-87 (hex) Elitegroup Computer System Co. (ECS) -00-0D-88 (hex) D-Link Corporation -00-0D-89 (hex) Bils Technology Inc -00-0D-8A (hex) Winners Electronics Co., Ltd. -00-0D-8B (hex) T&D Corporation -00-0D-8C (hex) Shanghai Wedone Digital Ltd. CO. -00-0D-8D (hex) ProLinx Communication Gateways, Inc. -00-0D-8E (hex) Koden Electronics Co., Ltd. -00-0D-8F (hex) King Tsushin Kogyo Co., LTD. -00-0D-90 (hex) Factum Electronics AB -00-0D-91 (hex) Eclipse (HQ Espana) S.L. -00-0D-92 (hex) Arima Communication Corporation -00-0D-93 (hex) Apple Computer -00-0D-94 (hex) AFAR Communications,Inc -00-0D-95 (hex) Opti-cell, Inc. -00-0D-96 (hex) Vtera Technology Inc. -00-0D-97 (hex) Tropos Networks, Inc. -00-0D-98 (hex) S.W.A.C. Schmitt-Walter Automation Consult GmbH -00-0D-99 (hex) Orbital Sciences Corp.; Launch Systems Group -00-0D-9A (hex) INFOTEC LTD -00-0D-9B (hex) Heraeus Electro-Nite International N.V. -00-0D-9C (hex) Elan GmbH & Co KG -00-0D-9D (hex) Hewlett Packard -00-0D-9E (hex) TOKUDEN OHIZUMI SEISAKUSYO Co.,Ltd. -00-0D-9F (hex) RF Micro Devices -00-0D-A0 (hex) NEDAP N.V. -00-0D-A1 (hex) MIRAE ITS Co.,LTD. -00-0D-A2 (hex) Infrant Technologies, Inc. -00-0D-A3 (hex) Emerging Technologies Limited -00-0D-A4 (hex) DOSCH & AMAND SYSTEMS AG -00-0D-A5 (hex) Fabric7 Systems, Inc -00-0D-A6 (hex) Universal Switching Corporation -00-0D-A7 (hex) PRIVATE -00-0D-A8 (hex) Teletronics Technology Corporation -00-0D-A9 (hex) T.E.A.M. S.L. -00-0D-AA (hex) S.A.Tehnology co.,Ltd. -00-0D-AB (hex) Parker Hannifin GmbH Electromechanical Division Europe -00-0D-AC (hex) Japan CBM Corporation -00-0D-AD (hex) Dataprobe Inc -00-0D-AE (hex) SAMSUNG HEAVY INDUSTRIES CO., LTD. -00-0D-AF (hex) Plexus Corp (UK) Ltd -00-0D-B0 (hex) Olym-tech Co.,Ltd. -00-0D-B1 (hex) Japan Network Service Co., Ltd. -00-0D-B2 (hex) Ammasso, Inc. -00-0D-B3 (hex) SDO Communication Corperation -00-0D-B4 (hex) NETASQ -00-0D-B5 (hex) GLOBALSAT TECHNOLOGY CORPORATION -00-0D-B6 (hex) Teknovus, Inc. -00-0D-B7 (hex) SANKO ELECTRIC CO,.LTD -00-0D-B8 (hex) SCHILLER AG -00-0D-B9 (hex) PC Engines GmbH -00-0D-BA (hex) Oc Document Technologies GmbH -00-0D-BB (hex) Nippon Dentsu Co.,Ltd. -00-0D-BC (hex) Cisco Systems -00-0D-BD (hex) Cisco Systems -00-0D-BE (hex) Bel Fuse Europe Ltd.,UK -00-0D-BF (hex) TekTone Sound & Signal Mfg., Inc. -00-0D-C0 (hex) Spagat AS -00-0D-C1 (hex) SafeWeb Inc -00-0D-C2 (hex) PRIVATE -00-0D-C3 (hex) First Communication, Inc. -00-0D-C4 (hex) Emcore Corporation -00-0D-C5 (hex) EchoStar International Corporation -00-0D-C6 (hex) DigiRose Technology Co., Ltd. -00-0D-C7 (hex) COSMIC ENGINEERING INC. -00-0D-C8 (hex) AirMagnet, Inc -00-0D-C9 (hex) THALES Elektronik Systeme GmbH -00-0D-CA (hex) Tait Electronics -00-0D-CB (hex) Petcomkorea Co., Ltd. -00-0D-CC (hex) NEOSMART Corp. -00-0D-CD (hex) GROUPE TXCOM -00-0D-CE (hex) Dynavac Technology Pte Ltd -00-0D-CF (hex) Cidra Corp. -00-0D-D0 (hex) TetraTec Instruments GmbH -00-0D-D1 (hex) Stryker Corporation -00-0D-D2 (hex) Simrad Optronics ASA -00-0D-D3 (hex) SAMWOO Telecommunication Co.,Ltd. -00-0D-D4 (hex) Symantec Corporation -00-0D-D5 (hex) O'RITE TECHNOLOGY CO.,LTD -00-0D-D6 (hex) ITI LTD -00-0D-D7 (hex) Bright -00-0D-D8 (hex) BBN -00-0D-D9 (hex) Anton Paar GmbH -00-0D-DA (hex) ALLIED TELESIS K.K. -00-0D-DB (hex) AIRWAVE TECHNOLOGIES INC. -00-0D-DC (hex) VAC -00-0D-DD (hex) PROFLO TELRA ELEKTRONK SANAY VE TCARET A.. -00-0D-DE (hex) Joyteck Co., Ltd. -00-0D-DF (hex) Japan Image & Network Inc. -00-0D-E0 (hex) ICPDAS Co.,LTD -00-0D-E1 (hex) Control Products, Inc. -00-0D-E2 (hex) CMZ Sistemi Elettronici -00-0D-E3 (hex) AT Sweden AB -00-0D-E4 (hex) DIGINICS, Inc. -00-0D-E5 (hex) Samsung Thales -00-0D-E6 (hex) YOUNGBO ENGINEERING CO.,LTD -00-0D-E7 (hex) Snap-on OEM Group -00-0D-E8 (hex) Nasaco Electronics Pte. Ltd -00-0D-E9 (hex) Napatech Aps -00-0D-EA (hex) Kingtel Telecommunication Corp. -00-0D-EB (hex) CompXs Limited -00-0D-EC (hex) Cisco Systems -00-0D-ED (hex) Cisco Systems -00-0D-EE (hex) Andrew RF Power Amplifier Group -00-0D-EF (hex) Soc. Coop. Bilanciai -00-0D-F0 (hex) QCOM TECHNOLOGY INC. -00-0D-F1 (hex) IONIX INC. -00-0D-F2 (hex) PRIVATE -00-0D-F3 (hex) Asmax Solutions -00-0D-F4 (hex) Watertek Co. -00-0D-F5 (hex) Teletronics International Inc. -00-0D-F6 (hex) Technology Thesaurus Corp. -00-0D-F7 (hex) Space Dynamics Lab -00-0D-F8 (hex) ORGA Kartensysteme GmbH -00-0D-F9 (hex) NDS Limited -00-0D-FA (hex) Micro Control Systems Ltd. -00-0D-FB (hex) Komax AG -00-0D-FC (hex) ITFOR Inc. -00-0D-FD (hex) Huges Hi-Tech Inc., -00-0D-FE (hex) Hauppauge Computer Works, Inc. -00-0D-FF (hex) CHENMING MOLD INDUSTRY CORP. -00-0E-00 (hex) Atrie -00-0E-01 (hex) ASIP Technologies Inc. -00-0E-02 (hex) Advantech AMT Inc. -00-0E-03 (hex) Emulex -00-0E-04 (hex) CMA/Microdialysis AB -00-0E-05 (hex) WIRELESS MATRIX CORP. -00-0E-06 (hex) Team Simoco Ltd -00-0E-07 (hex) Sony Ericsson Mobile Communications AB -00-0E-08 (hex) Cisco Linksys LLC -00-0E-09 (hex) Shenzhen Coship Software Co.,LTD. -00-0E-0A (hex) SAKUMA DESIGN OFFICE -00-0E-0B (hex) Netac Technology Co., Ltd. -00-0E-0C (hex) Intel Corporation -00-0E-0D (hex) HESCH Schrder GmbH -00-0E-0E (hex) ESA elettronica S.P.A. -00-0E-0F (hex) ERMME -00-0E-10 (hex) C-guys, Inc. -00-0E-11 (hex) BDT Bro- und Datentechnik GmbH & Co. KG -00-0E-12 (hex) Adaptive Micro Systems Inc. -00-0E-13 (hex) Accu-Sort Systems inc. -00-0E-14 (hex) Visionary Solutions, Inc. -00-0E-15 (hex) Tadlys LTD -00-0E-16 (hex) SouthWing S.L. -00-0E-17 (hex) PRIVATE -00-0E-18 (hex) MyA Technology -00-0E-19 (hex) LogicaCMG Pty Ltd -00-0E-1A (hex) JPS Communications -00-0E-1B (hex) IAV GmbH -00-0E-1C (hex) Hach Company -00-0E-1D (hex) ARION Technology Inc. -00-0E-1E (hex) NetXen, Inc. -00-0E-1F (hex) TCL Networks Equipment Co., Ltd. -00-0E-20 (hex) ACCESS Systems Americas, Inc. -00-0E-21 (hex) MTU Friedrichshafen GmbH -00-0E-22 (hex) PRIVATE -00-0E-23 (hex) Incipient, Inc. -00-0E-24 (hex) Huwell Technology Inc. -00-0E-25 (hex) Hannae Technology Co., Ltd -00-0E-26 (hex) Gincom Technology Corp. -00-0E-27 (hex) Crere Networks, Inc. -00-0E-28 (hex) Dynamic Ratings P/L -00-0E-29 (hex) Shester Communications Inc -00-0E-2A (hex) PRIVATE -00-0E-2B (hex) Safari Technologies -00-0E-2C (hex) Netcodec co. -00-0E-2D (hex) Hyundai Digital Technology Co.,Ltd. -00-0E-2E (hex) Edimax Technology Co., Ltd. -00-0E-2F (hex) Disetronic Medical Systems AG -00-0E-30 (hex) AERAS Networks, Inc. -00-0E-31 (hex) Olympus Soft Imaging Solutions GmbH -00-0E-32 (hex) Kontron Medical -00-0E-33 (hex) Shuko Electronics Co.,Ltd -00-0E-34 (hex) NexGen City, LP -00-0E-35 (hex) Intel Corp -00-0E-36 (hex) HEINESYS, Inc. -00-0E-37 (hex) Harms & Wende GmbH & Co.KG -00-0E-38 (hex) Cisco Systems -00-0E-39 (hex) Cisco Systems -00-0E-3A (hex) Cirrus Logic -00-0E-3B (hex) Hawking Technologies, Inc. -00-0E-3C (hex) Transact Technologies Inc -00-0E-3D (hex) Televic N.V. -00-0E-3E (hex) Sun Optronics Inc -00-0E-3F (hex) Soronti, Inc. -00-0E-40 (hex) Nortel Networks -00-0E-41 (hex) NIHON MECHATRONICS CO.,LTD. -00-0E-42 (hex) Motic Incoporation Ltd. -00-0E-43 (hex) G-Tek Electronics Sdn. Bhd. -00-0E-44 (hex) Digital 5, Inc. -00-0E-45 (hex) Beijing Newtry Electronic Technology Ltd -00-0E-46 (hex) Niigata Seimitsu Co.,Ltd. -00-0E-47 (hex) NCI System Co.,Ltd. -00-0E-48 (hex) Lipman TransAction Solutions -00-0E-49 (hex) Forsway Scandinavia AB -00-0E-4A (hex) Changchun Huayu WEBPAD Co.,LTD -00-0E-4B (hex) atrium c and i -00-0E-4C (hex) Bermai Inc. -00-0E-4D (hex) Numesa Inc. -00-0E-4E (hex) Waveplus Technology Co., Ltd. -00-0E-4F (hex) Trajet GmbH -00-0E-50 (hex) Thomson Telecom Belgium -00-0E-51 (hex) tecna elettronica srl -00-0E-52 (hex) Optium Corporation -00-0E-53 (hex) AV TECH CORPORATION -00-0E-54 (hex) AlphaCell Wireless Ltd. -00-0E-55 (hex) AUVITRAN -00-0E-56 (hex) 4G Systems GmbH & Co. KG -00-0E-57 (hex) Iworld Networking, Inc. -00-0E-58 (hex) Sonos, Inc. -00-0E-59 (hex) SAGEM SA -00-0E-5A (hex) TELEFIELD inc. -00-0E-5B (hex) ParkerVision - Direct2Data -00-0E-5C (hex) Motorola BCS -00-0E-5D (hex) Triple Play Technologies A/S -00-0E-5E (hex) Raisecom Technology -00-0E-5F (hex) activ-net GmbH & Co. KG -00-0E-60 (hex) 360SUN Digital Broadband Corporation -00-0E-61 (hex) MICROTROL LIMITED -00-0E-62 (hex) Nortel Networks -00-0E-63 (hex) Lemke Diagnostics GmbH -00-0E-64 (hex) Elphel, Inc -00-0E-65 (hex) TransCore -00-0E-66 (hex) Hitachi Advanced Digital, Inc. -00-0E-67 (hex) Eltis Microelectronics Ltd. -00-0E-68 (hex) E-TOP Network Technology Inc. -00-0E-69 (hex) China Electric Power Research Institute -00-0E-6A (hex) 3Com Ltd -00-0E-6B (hex) Janitza electronics GmbH -00-0E-6C (hex) Device Drivers Limited -00-0E-6D (hex) Murata Manufacturing Co., Ltd. -00-0E-6E (hex) MICRELEC ELECTRONICS S.A -00-0E-6F (hex) IRIS Corporation Berhad -00-0E-70 (hex) in2 Networks -00-0E-71 (hex) Gemstar Technology Development Ltd. -00-0E-72 (hex) CTS electronics -00-0E-73 (hex) Tpack A/S -00-0E-74 (hex) Solar Telecom. Tech -00-0E-75 (hex) New York Air Brake Corp. -00-0E-76 (hex) GEMSOC INNOVISION INC. -00-0E-77 (hex) Decru, Inc. -00-0E-78 (hex) Amtelco -00-0E-79 (hex) Ample Communications Inc. -00-0E-7A (hex) GemWon Communications Co., Ltd. -00-0E-7B (hex) Toshiba -00-0E-7C (hex) Televes S.A. -00-0E-7D (hex) Electronics Line 3000 Ltd. -00-0E-7E (hex) ionSign Oy -00-0E-7F (hex) Hewlett Packard -00-0E-80 (hex) Thomson Technology Inc -00-0E-81 (hex) Devicescape Software, Inc. -00-0E-82 (hex) Commtech Wireless -00-0E-83 (hex) Cisco Systems -00-0E-84 (hex) Cisco Systems -00-0E-85 (hex) Catalyst Enterprises, Inc. -00-0E-86 (hex) Alcatel North America -00-0E-87 (hex) adp Gauselmann GmbH -00-0E-88 (hex) VIDEOTRON CORP. -00-0E-89 (hex) CLEMATIC -00-0E-8A (hex) Avara Technologies Pty. Ltd. -00-0E-8B (hex) Astarte Technology Co, Ltd. -00-0E-8C (hex) Siemens AG A&D ET -00-0E-8D (hex) Systems in Progress Holding GmbH -00-0E-8E (hex) SparkLAN Communications, Inc. -00-0E-8F (hex) Sercomm Corp. -00-0E-90 (hex) PONICO CORP. -00-0E-91 (hex) Navico Auckland Ltd -00-0E-92 (hex) Millinet Co., Ltd. -00-0E-93 (hex) Milnio 3 Sistemas Electrnicos, Lda. -00-0E-94 (hex) Maas International BV -00-0E-95 (hex) Fujiya Denki Seisakusho Co.,Ltd. -00-0E-96 (hex) Cubic Defense Applications, Inc. -00-0E-97 (hex) Ultracker Technology CO., Inc -00-0E-98 (hex) Vitec CC, INC. -00-0E-99 (hex) Spectrum Digital, Inc -00-0E-9A (hex) BOE TECHNOLOGY GROUP CO.,LTD -00-0E-9B (hex) Ambit Microsystems Corporation -00-0E-9C (hex) Pemstar -00-0E-9D (hex) Tiscali UK Ltd -00-0E-9E (hex) Topfield Co., Ltd -00-0E-9F (hex) TEMIC SDS GmbH -00-0E-A0 (hex) NetKlass Technology Inc. -00-0E-A1 (hex) Formosa Teletek Corporation -00-0E-A2 (hex) McAfee, Inc -00-0E-A3 (hex) CNCR-IT CO.,LTD,HangZhou P.R.CHINA -00-0E-A4 (hex) Certance Inc. -00-0E-A5 (hex) BLIP Systems -00-0E-A6 (hex) ASUSTEK COMPUTER INC. -00-0E-A7 (hex) Endace Technology -00-0E-A8 (hex) United Technologists Europe Limited -00-0E-A9 (hex) Shanghai Xun Shi Communications Equipment Ltd. Co. -00-0E-AA (hex) Scalent Systems, Inc. -00-0E-AB (hex) Cray Inc -00-0E-AC (hex) MINTRON ENTERPRISE CO., LTD. -00-0E-AD (hex) Metanoia Technologies, Inc. -00-0E-AE (hex) GAWELL TECHNOLOGIES CORP. -00-0E-AF (hex) CASTEL -00-0E-B0 (hex) Solutions Radio BV -00-0E-B1 (hex) Newcotech,Ltd -00-0E-B2 (hex) Micro-Research Finland Oy -00-0E-B3 (hex) Hewlett-Packard -00-0E-B4 (hex) GUANGZHOU GAOKE COMMUNICATIONS TECHNOLOGY CO.LTD. -00-0E-B5 (hex) Ecastle Electronics Co., Ltd. -00-0E-B6 (hex) Riverbed Technology, Inc. -00-0E-B7 (hex) Knovative, Inc. -00-0E-B8 (hex) Iiga co.,Ltd -00-0E-B9 (hex) HASHIMOTO Electronics Industry Co.,Ltd. -00-0E-BA (hex) HANMI SEMICONDUCTOR CO., LTD. -00-0E-BB (hex) Everbee Networks -00-0E-BC (hex) Paragon Fidelity GmbH -00-0E-BD (hex) Burdick, a Quinton Compny -00-0E-BE (hex) B&B Electronics Manufacturing Co. -00-0E-BF (hex) Remsdaq Limited -00-0E-C0 (hex) Nortel Networks -00-0E-C1 (hex) MYNAH Technologies -00-0E-C2 (hex) Lowrance Electronics, Inc. -00-0E-C3 (hex) Logic Controls, Inc. -00-0E-C4 (hex) Iskra Transmission d.d. -00-0E-C5 (hex) Digital Multitools Inc -00-0E-C6 (hex) ASIX ELECTRONICS CORP. -00-0E-C7 (hex) Motorola Korea -00-0E-C8 (hex) Zoran Corporation -00-0E-C9 (hex) YOKO Technology Corp. -00-0E-CA (hex) WTSS Inc -00-0E-CB (hex) VineSys Technology -00-0E-CC (hex) Tableau, LLC -00-0E-CD (hex) SKOV A/S -00-0E-CE (hex) S.I.T.T.I. S.p.A. -00-0E-CF (hex) PROFIBUS Nutzerorganisation e.V. -00-0E-D0 (hex) Privaris, Inc. -00-0E-D1 (hex) Osaka Micro Computer. -00-0E-D2 (hex) Filtronic plc -00-0E-D3 (hex) Epicenter, Inc. -00-0E-D4 (hex) CRESITT INDUSTRIE -00-0E-D5 (hex) COPAN Systems Inc. -00-0E-D6 (hex) Cisco Systems -00-0E-D7 (hex) Cisco Systems -00-0E-D8 (hex) Aktino, Inc. -00-0E-D9 (hex) Aksys, Ltd. -00-0E-DA (hex) C-TECH UNITED CORP. -00-0E-DB (hex) XiNCOM Corp. -00-0E-DC (hex) Tellion INC. -00-0E-DD (hex) SHURE INCORPORATED -00-0E-DE (hex) REMEC, Inc. -00-0E-DF (hex) PLX Technology -00-0E-E0 (hex) Mcharge -00-0E-E1 (hex) ExtremeSpeed Inc. -00-0E-E2 (hex) Custom Engineering S.p.A. -00-0E-E3 (hex) Chiyu Technology Co.,Ltd -00-0E-E4 (hex) BOE TECHNOLOGY GROUP CO.,LTD -00-0E-E5 (hex) bitWallet, Inc. -00-0E-E6 (hex) Adimos Systems LTD -00-0E-E7 (hex) AAC ELECTRONICS CORP. -00-0E-E8 (hex) zioncom -00-0E-E9 (hex) WayTech Development, Inc. -00-0E-EA (hex) Shadong Luneng Jicheng Electronics,Co.,Ltd -00-0E-EB (hex) Sandmartin(zhong shan)Electronics Co.,Ltd -00-0E-EC (hex) Orban -00-0E-ED (hex) Nokia Danmark A/S -00-0E-EE (hex) Muco Industrie BV -00-0E-EF (hex) PRIVATE -00-0E-F0 (hex) Festo AG & Co. KG -00-0E-F1 (hex) EZQUEST INC. -00-0E-F2 (hex) Infinico Corporation -00-0E-F3 (hex) Smarthome -00-0E-F4 (hex) Kasda Digital Technology Co.,Ltd -00-0E-F5 (hex) iPAC Technology Co., Ltd. -00-0E-F6 (hex) E-TEN Information Systems Co., Ltd. -00-0E-F7 (hex) Vulcan Portals Inc -00-0E-F8 (hex) SBC ASI -00-0E-F9 (hex) REA Elektronik GmbH -00-0E-FA (hex) Optoway Technology Incorporation -00-0E-FB (hex) Macey Enterprises -00-0E-FC (hex) JTAG Technologies B.V. -00-0E-FD (hex) FUJINON CORPORATION -00-0E-FE (hex) EndRun Technologies LLC -00-0E-FF (hex) Megasolution,Inc. -00-0F-00 (hex) Legra Systems, Inc. -00-0F-01 (hex) DIGITALKS INC -00-0F-02 (hex) Digicube Technology Co., Ltd -00-0F-03 (hex) COM&C CO., LTD -00-0F-04 (hex) cim-usa inc -00-0F-05 (hex) 3B SYSTEM INC. -00-0F-06 (hex) Nortel Networks -00-0F-07 (hex) Mangrove Systems, Inc. -00-0F-08 (hex) Indagon Oy -00-0F-09 (hex) PRIVATE -00-0F-0A (hex) Clear Edge Networks -00-0F-0B (hex) Kentima Technologies AB -00-0F-0C (hex) SYNCHRONIC ENGINEERING -00-0F-0D (hex) Hunt Electronic Co., Ltd. -00-0F-0E (hex) WaveSplitter Technologies, Inc. -00-0F-0F (hex) Real ID Technology Co., Ltd. -00-0F-10 (hex) RDM Corporation -00-0F-11 (hex) Prodrive B.V. -00-0F-12 (hex) Panasonic Europe Ltd. -00-0F-13 (hex) Nisca corporation -00-0F-14 (hex) Mindray Co., Ltd. -00-0F-15 (hex) Kjaerulff1 A/S -00-0F-16 (hex) JAY HOW TECHNOLOGY CO., -00-0F-17 (hex) Insta Elektro GmbH -00-0F-18 (hex) Industrial Control Systems -00-0F-19 (hex) Boston Scientific -00-0F-1A (hex) Gaming Support B.V. -00-0F-1B (hex) Ego Systems Inc. -00-0F-1C (hex) DigitAll World Co., Ltd -00-0F-1D (hex) Cosmo Techs Co., Ltd. -00-0F-1E (hex) Chengdu KT Electric Co.of High & New Technology -00-0F-1F (hex) WW PCBA Test -00-0F-20 (hex) Hewlett Packard -00-0F-21 (hex) Scientific Atlanta, Inc -00-0F-22 (hex) Helius, Inc. -00-0F-23 (hex) Cisco Systems -00-0F-24 (hex) Cisco Systems -00-0F-25 (hex) AimValley B.V. -00-0F-26 (hex) WorldAccxx LLC -00-0F-27 (hex) TEAL Electronics, Inc. -00-0F-28 (hex) Itronix Corporation -00-0F-29 (hex) Augmentix Corporation -00-0F-2A (hex) Cableware Electronics -00-0F-2B (hex) GREENBELL SYSTEMS -00-0F-2C (hex) Uplogix, Inc. -00-0F-2D (hex) CHUNG-HSIN ELECTRIC & MACHINERY MFG.CORP. -00-0F-2E (hex) Megapower International Corp. -00-0F-2F (hex) W-LINX TECHNOLOGY CO., LTD. -00-0F-30 (hex) Raza Microelectronics Inc -00-0F-31 (hex) Allied Vision Technologies Canada Inc -00-0F-32 (hex) LuTong Electronic Technology Co.,Ltd -00-0F-33 (hex) DUALi Inc. -00-0F-34 (hex) Cisco Systems -00-0F-35 (hex) Cisco Systems -00-0F-36 (hex) Accurate Techhnologies, Inc. -00-0F-37 (hex) Xambala Incorporated -00-0F-38 (hex) Netstar -00-0F-39 (hex) IRIS SENSORS -00-0F-3A (hex) HISHARP -00-0F-3B (hex) Fuji System Machines Co., Ltd. -00-0F-3C (hex) Endeleo Limited -00-0F-3D (hex) D-Link Corporation -00-0F-3E (hex) CardioNet, Inc -00-0F-3F (hex) Big Bear Networks -00-0F-40 (hex) Optical Internetworking Forum -00-0F-41 (hex) Zipher Ltd -00-0F-42 (hex) Xalyo Systems -00-0F-43 (hex) Wasabi Systems Inc. -00-0F-44 (hex) Tivella Inc. -00-0F-45 (hex) Stretch, Inc. -00-0F-46 (hex) SINAR AG -00-0F-47 (hex) ROBOX SPA -00-0F-48 (hex) Polypix Inc. -00-0F-49 (hex) Northover Solutions Limited -00-0F-4A (hex) Kyushu-kyohan co.,ltd -00-0F-4B (hex) Virtual Iron Software, Inc. -00-0F-4C (hex) Elextech INC -00-0F-4D (hex) TalkSwitch -00-0F-4E (hex) Cellink -00-0F-4F (hex) Cadmus Technology Ltd -00-0F-50 (hex) StreamScale Limited -00-0F-51 (hex) Azul Systems, Inc. -00-0F-52 (hex) YORK Refrigeration, Marine & Controls -00-0F-53 (hex) Solarflare Communications Inc -00-0F-54 (hex) Entrelogic Corporation -00-0F-55 (hex) Datawire Communication Networks Inc. -00-0F-56 (hex) Continuum Photonics Inc -00-0F-57 (hex) CABLELOGIC Co., Ltd. -00-0F-58 (hex) Adder Technology Limited -00-0F-59 (hex) Phonak Communications AG -00-0F-5A (hex) Peribit Networks -00-0F-5B (hex) Delta Information Systems, Inc. -00-0F-5C (hex) Day One Digital Media Limited -00-0F-5D (hex) 42Networks AB -00-0F-5E (hex) Veo -00-0F-5F (hex) Nicety Technologies Inc. (NTS) -00-0F-60 (hex) Lifetron Co.,Ltd -00-0F-61 (hex) Hewlett Packard -00-0F-62 (hex) Alcatel Bell Space N.V. -00-0F-63 (hex) Obzerv Technologies -00-0F-64 (hex) D&R Electronica Weesp BV -00-0F-65 (hex) icube Corp. -00-0F-66 (hex) Cisco-Linksys -00-0F-67 (hex) West Instruments -00-0F-68 (hex) Vavic Network Technology, Inc. -00-0F-69 (hex) SEW Eurodrive GmbH & Co. KG -00-0F-6A (hex) Nortel Networks -00-0F-6B (hex) GateWare Communications GmbH -00-0F-6C (hex) ADDI-DATA GmbH -00-0F-6D (hex) Midas Engineering -00-0F-6E (hex) BBox -00-0F-6F (hex) FTA Communication Technologies -00-0F-70 (hex) Wintec Industries, inc. -00-0F-71 (hex) Sanmei Electronics Co.,Ltd -00-0F-72 (hex) Sandburst -00-0F-73 (hex) Rockwell Automation Korea -00-0F-74 (hex) Qamcom Technology AB -00-0F-75 (hex) First Silicon Solutions -00-0F-76 (hex) Digital Keystone, Inc. -00-0F-77 (hex) DENTUM CO.,LTD -00-0F-78 (hex) Datacap Systems Inc -00-0F-79 (hex) Bluetooth Interest Group Inc. -00-0F-7A (hex) BeiJing NuQX Technology CO.,LTD -00-0F-7B (hex) Arce Sistemas, S.A. -00-0F-7C (hex) ACTi Corporation -00-0F-7D (hex) Xirrus -00-0F-7E (hex) Ablerex Electronics Co., LTD -00-0F-7F (hex) UBSTORAGE Co.,Ltd. -00-0F-80 (hex) Trinity Security Systems,Inc. -00-0F-81 (hex) Secure Info Imaging -00-0F-82 (hex) Mortara Instrument, Inc. -00-0F-83 (hex) Brainium Technologies Inc. -00-0F-84 (hex) Astute Networks, Inc. -00-0F-85 (hex) ADDO-Japan Corporation -00-0F-86 (hex) Research In Motion Limited -00-0F-87 (hex) Maxcess International -00-0F-88 (hex) AMETEK, Inc. -00-0F-89 (hex) Winnertec System Co., Ltd. -00-0F-8A (hex) WideView -00-0F-8B (hex) Orion MultiSystems Inc -00-0F-8C (hex) Gigawavetech Pte Ltd -00-0F-8D (hex) FAST TV-Server AG -00-0F-8E (hex) DONGYANG TELECOM CO.,LTD. -00-0F-8F (hex) Cisco Systems -00-0F-90 (hex) Cisco Systems -00-0F-91 (hex) Aerotelecom Co.,Ltd. -00-0F-92 (hex) Microhard Systems Inc. -00-0F-93 (hex) Landis+Gyr Ltd. -00-0F-94 (hex) Genexis -00-0F-95 (hex) ELECOM Co.,LTD Laneed Division -00-0F-96 (hex) Critical Telecom Corp. -00-0F-97 (hex) Avanex Corporation -00-0F-98 (hex) Avamax Co. Ltd. -00-0F-99 (hex) APAC opto Electronics Inc. -00-0F-9A (hex) Synchrony, Inc. -00-0F-9B (hex) Ross Video Limited -00-0F-9C (hex) Panduit Corp -00-0F-9D (hex) DisplayLink (UK) Ltd -00-0F-9E (hex) Murrelektronik GmbH -00-0F-9F (hex) Motorola BCS -00-0F-A0 (hex) CANON KOREA BUSINESS SOLUTIONS INC. -00-0F-A1 (hex) Gigabit Systems Inc. -00-0F-A2 (hex) Digital Path Networks -00-0F-A3 (hex) Alpha Networks Inc. -00-0F-A4 (hex) Sprecher Automation GmbH -00-0F-A5 (hex) SMP / BWA Technology GmbH -00-0F-A6 (hex) S2 Security Corporation -00-0F-A7 (hex) Raptor Networks Technology -00-0F-A8 (hex) Photometrics, Inc. -00-0F-A9 (hex) PC Fabrik -00-0F-AA (hex) Nexus Technologies -00-0F-AB (hex) Kyushu Electronics Systems Inc. -00-0F-AC (hex) IEEE 802.11 -00-0F-AD (hex) FMN communications GmbH -00-0F-AE (hex) E2O Communications -00-0F-AF (hex) Dialog Inc. -00-0F-B0 (hex) Compal Electronics,INC. -00-0F-B1 (hex) Cognio Inc. -00-0F-B2 (hex) Broadband Pacenet (India) Pvt. Ltd. -00-0F-B3 (hex) Actiontec Electronics, Inc -00-0F-B4 (hex) Timespace Technology -00-0F-B5 (hex) NETGEAR Inc -00-0F-B6 (hex) Europlex Technologies -00-0F-B7 (hex) Cavium Networks -00-0F-B8 (hex) CallURL Inc. -00-0F-B9 (hex) Adaptive Instruments -00-0F-BA (hex) Tevebox AB -00-0F-BB (hex) Nokia Siemens Networks GmbH & Co. KG -00-0F-BC (hex) Onkey Technologies, Inc. -00-0F-BD (hex) MRV Communications (Networks) LTD -00-0F-BE (hex) e-w/you Inc. -00-0F-BF (hex) DGT Sp. z o.o. -00-0F-C0 (hex) DELCOMp -00-0F-C1 (hex) WAVE Corporation -00-0F-C2 (hex) Uniwell Corporation -00-0F-C3 (hex) PalmPalm Technology, Inc. -00-0F-C4 (hex) NST co.,LTD. -00-0F-C5 (hex) KeyMed Ltd -00-0F-C6 (hex) Eurocom Industries A/S -00-0F-C7 (hex) Dionica R&D Ltd. -00-0F-C8 (hex) Chantry Networks -00-0F-C9 (hex) Allnet GmbH -00-0F-CA (hex) A-JIN TECHLINE CO, LTD -00-0F-CB (hex) 3Com Ltd -00-0F-CC (hex) Netopia, Inc. -00-0F-CD (hex) Nortel Networks -00-0F-CE (hex) Kikusui Electronics Corp. -00-0F-CF (hex) Datawind Research -00-0F-D0 (hex) ASTRI -00-0F-D1 (hex) Applied Wireless Identifications Group, Inc. -00-0F-D2 (hex) EWA Technologies, Inc. -00-0F-D3 (hex) Digium -00-0F-D4 (hex) Soundcraft -00-0F-D5 (hex) Schwechat - RISE -00-0F-D6 (hex) Sarotech Co., Ltd -00-0F-D7 (hex) Harman Music Group -00-0F-D8 (hex) Force, Inc. -00-0F-D9 (hex) FlexDSL Telecommunications AG -00-0F-DA (hex) YAZAKI CORPORATION -00-0F-DB (hex) Westell Technologies -00-0F-DC (hex) Ueda Japan Radio Co., Ltd. -00-0F-DD (hex) SORDIN AB -00-0F-DE (hex) Sony Ericsson Mobile Communications AB -00-0F-DF (hex) SOLOMON Technology Corp. -00-0F-E0 (hex) NComputing Co.,Ltd. -00-0F-E1 (hex) ID DIGITAL CORPORATION -00-0F-E2 (hex) Hangzhou H3C Technologies Co., Ltd. -00-0F-E3 (hex) Damm Cellular Systems A/S -00-0F-E4 (hex) Pantech Co.,Ltd -00-0F-E5 (hex) MERCURY SECURITY CORPORATION -00-0F-E6 (hex) MBTech Systems, Inc. -00-0F-E7 (hex) Lutron Electronics Co., Inc. -00-0F-E8 (hex) Lobos, Inc. -00-0F-E9 (hex) GW TECHNOLOGIES CO.,LTD. -00-0F-EA (hex) Giga-Byte Technology Co.,LTD. -00-0F-EB (hex) Cylon Controls -00-0F-EC (hex) Arkus Inc. -00-0F-ED (hex) Anam Electronics Co., Ltd -00-0F-EE (hex) XTec, Incorporated -00-0F-EF (hex) Thales e-Transactions GmbH -00-0F-F0 (hex) Sunray Co. Ltd. -00-0F-F1 (hex) nex-G Systems Pte.Ltd -00-0F-F2 (hex) Loud Technologies Inc. -00-0F-F3 (hex) Jung Myoung Communications&Technology -00-0F-F4 (hex) Guntermann & Drunck GmbH -00-0F-F5 (hex) GN&S company -00-0F-F6 (hex) Darfon Electronics Corp. -00-0F-F7 (hex) Cisco Systems -00-0F-F8 (hex) Cisco Systems -00-0F-F9 (hex) Valcretec, Inc. -00-0F-FA (hex) Optinel Systems, Inc. -00-0F-FB (hex) Nippon Denso Industry Co., Ltd. -00-0F-FC (hex) Merit Li-Lin Ent. -00-0F-FD (hex) Glorytek Network Inc. -00-0F-FE (hex) G-PRO COMPUTER -00-0F-FF (hex) Control4 -00-10-00 (hex) CABLE TELEVISION LABORATORIES, INC. -00-10-01 (hex) Citel -00-10-02 (hex) ACTIA -00-10-03 (hex) IMATRON, INC. -00-10-04 (hex) THE BRANTLEY COILE COMPANY,INC -00-10-05 (hex) UEC COMMERCIAL -00-10-06 (hex) Thales Contact Solutions Ltd. -00-10-07 (hex) CISCO SYSTEMS, INC. -00-10-08 (hex) VIENNA SYSTEMS CORPORATION -00-10-09 (hex) HORO QUARTZ -00-10-0A (hex) WILLIAMS COMMUNICATIONS GROUP -00-10-0B (hex) CISCO SYSTEMS, INC. -00-10-0C (hex) ITO CO., LTD. -00-10-0D (hex) CISCO SYSTEMS, INC. -00-10-0E (hex) MICRO LINEAR COPORATION -00-10-0F (hex) INDUSTRIAL CPU SYSTEMS -00-10-10 (hex) INITIO CORPORATION -00-10-11 (hex) CISCO SYSTEMS, INC. -00-10-12 (hex) PROCESSOR SYSTEMS (I) PVT LTD -00-10-13 (hex) Kontron America, Inc. -00-10-14 (hex) CISCO SYSTEMS, INC. -00-10-15 (hex) OOmon Inc. -00-10-16 (hex) T.SQWARE -00-10-17 (hex) Bosch Access Systems GmbH -00-10-18 (hex) BROADCOM CORPORATION -00-10-19 (hex) SIRONA DENTAL SYSTEMS GmbH & Co. KG -00-10-1A (hex) PictureTel Corp. -00-10-1B (hex) CORNET TECHNOLOGY, INC. -00-10-1C (hex) OHM TECHNOLOGIES INTL, LLC -00-10-1D (hex) WINBOND ELECTRONICS CORP. -00-10-1E (hex) MATSUSHITA ELECTRONIC INSTRUMENTS CORP. -00-10-1F (hex) CISCO SYSTEMS, INC. -00-10-20 (hex) Hand Held Products Inc -00-10-21 (hex) ENCANTO NETWORKS, INC. -00-10-22 (hex) SatCom Media Corporation -00-10-23 (hex) Network Equipment Technologies -00-10-24 (hex) NAGOYA ELECTRIC WORKS CO., LTD -00-10-25 (hex) Grayhill, Inc -00-10-26 (hex) ACCELERATED NETWORKS, INC. -00-10-27 (hex) L-3 COMMUNICATIONS EAST -00-10-28 (hex) COMPUTER TECHNICA, INC. -00-10-29 (hex) CISCO SYSTEMS, INC. -00-10-2A (hex) ZF MICROSYSTEMS, INC. -00-10-2B (hex) UMAX DATA SYSTEMS, INC. -00-10-2C (hex) Lasat Networks A/S -00-10-2D (hex) HITACHI SOFTWARE ENGINEERING -00-10-2E (hex) NETWORK SYSTEMS & TECHNOLOGIES PVT. LTD. -00-10-2F (hex) CISCO SYSTEMS, INC. -00-10-30 (hex) EION Inc. -00-10-31 (hex) OBJECTIVE COMMUNICATIONS, INC. -00-10-32 (hex) ALTA TECHNOLOGY -00-10-33 (hex) ACCESSLAN COMMUNICATIONS, INC. -00-10-34 (hex) GNP Computers -00-10-35 (hex) ELITEGROUP COMPUTER SYSTEMS CO., LTD -00-10-36 (hex) INTER-TEL INTEGRATED SYSTEMS -00-10-37 (hex) CYQ've Technology Co., Ltd. -00-10-38 (hex) MICRO RESEARCH INSTITUTE, INC. -00-10-39 (hex) Vectron Systems AG -00-10-3A (hex) DIAMOND NETWORK TECH -00-10-3B (hex) HIPPI NETWORKING FORUM -00-10-3C (hex) IC ENSEMBLE, INC. -00-10-3D (hex) PHASECOM, LTD. -00-10-3E (hex) NETSCHOOLS CORPORATION -00-10-3F (hex) TOLLGRADE COMMUNICATIONS, INC. -00-10-40 (hex) INTERMEC CORPORATION -00-10-41 (hex) BRISTOL BABCOCK, INC. -00-10-42 (hex) Alacritech, Inc. -00-10-43 (hex) A2 CORPORATION -00-10-44 (hex) InnoLabs Corporation -00-10-45 (hex) Nortel Networks -00-10-46 (hex) ALCORN MCBRIDE INC. -00-10-47 (hex) ECHO ELETRIC CO. LTD. -00-10-48 (hex) HTRC AUTOMATION, INC. -00-10-49 (hex) ShoreTel, Inc -00-10-4A (hex) The Parvus Corporation -00-10-4B (hex) 3COM CORPORATION -00-10-4C (hex) LeCroy Corporation -00-10-4D (hex) SURTEC INDUSTRIES, INC. -00-10-4E (hex) CEOLOGIC -00-10-4F (hex) Sun Microsystems, Inc. -00-10-50 (hex) RION CO., LTD. -00-10-51 (hex) CMICRO CORPORATION -00-10-52 (hex) METTLER-TOLEDO (ALBSTADT) GMBH -00-10-53 (hex) COMPUTER TECHNOLOGY CORP. -00-10-54 (hex) CISCO SYSTEMS, INC. -00-10-55 (hex) FUJITSU MICROELECTRONICS, INC. -00-10-56 (hex) SODICK CO., LTD. -00-10-57 (hex) Rebel.com, Inc. -00-10-58 (hex) ArrowPoint Communications -00-10-59 (hex) DIABLO RESEARCH CO. LLC -00-10-5A (hex) 3COM CORPORATION -00-10-5B (hex) NET INSIGHT AB -00-10-5C (hex) QUANTUM DESIGNS (H.K.) LTD. -00-10-5D (hex) Draeger Medical -00-10-5E (hex) HEKIMIAN LABORATORIES, INC. -00-10-5F (hex) IN-SNEC -00-10-60 (hex) BILLIONTON SYSTEMS, INC. -00-10-61 (hex) HOSTLINK CORP. -00-10-62 (hex) NX SERVER, ILNC. -00-10-63 (hex) STARGUIDE DIGITAL NETWORKS -00-10-64 (hex) DNPG, LLC -00-10-65 (hex) RADYNE CORPORATION -00-10-66 (hex) ADVANCED CONTROL SYSTEMS, INC. -00-10-67 (hex) REDBACK NETWORKS, INC. -00-10-68 (hex) COMOS TELECOM -00-10-69 (hex) HELIOSS COMMUNICATIONS, INC. -00-10-6A (hex) DIGITAL MICROWAVE CORPORATION -00-10-6B (hex) SONUS NETWORKS, INC. -00-10-6C (hex) Infratec AG -00-10-6D (hex) Axxcelera Broadband Wireless -00-10-6E (hex) TADIRAN COM. LTD. -00-10-6F (hex) TRENTON TECHNOLOGY INC. -00-10-70 (hex) CARADON TREND LTD. -00-10-71 (hex) ADVANET INC. -00-10-72 (hex) GVN TECHNOLOGIES, INC. -00-10-73 (hex) Technobox, Inc. -00-10-74 (hex) ATEN INTERNATIONAL CO., LTD. -00-10-75 (hex) Maxtor Corporation -00-10-76 (hex) EUREM GmbH -00-10-77 (hex) SAF DRIVE SYSTEMS, LTD. -00-10-78 (hex) NUERA COMMUNICATIONS, INC. -00-10-79 (hex) CISCO SYSTEMS, INC. -00-10-7A (hex) AmbiCom, Inc. -00-10-7B (hex) CISCO SYSTEMS, INC. -00-10-7C (hex) P-COM, INC. -00-10-7D (hex) AURORA COMMUNICATIONS, LTD. -00-10-7E (hex) BACHMANN ELECTRONIC GmbH -00-10-7F (hex) CRESTRON ELECTRONICS, INC. -00-10-80 (hex) METAWAVE COMMUNICATIONS -00-10-81 (hex) DPS, INC. -00-10-82 (hex) JNA TELECOMMUNICATIONS LIMITED -00-10-83 (hex) HEWLETT-PACKARD COMPANY -00-10-84 (hex) K-BOT COMMUNICATIONS -00-10-85 (hex) POLARIS COMMUNICATIONS, INC. -00-10-86 (hex) ATTO Technology, Inc. -00-10-87 (hex) Xstreamis PLC -00-10-88 (hex) AMERICAN NETWORKS INC. -00-10-89 (hex) WebSonic -00-10-8A (hex) TeraLogic, Inc. -00-10-8B (hex) LASERANIMATION SOLLINGER GmbH -00-10-8C (hex) FUJITSU TELECOMMUNICATIONS EUROPE, LTD. -00-10-8D (hex) Johnson Controls, Inc. -00-10-8E (hex) HUGH SYMONS CONCEPT Technologies Ltd. -00-10-8F (hex) RAPTOR SYSTEMS -00-10-90 (hex) CIMETRICS, INC. -00-10-91 (hex) NO WIRES NEEDED BV -00-10-92 (hex) NETCORE INC. -00-10-93 (hex) CMS COMPUTERS, LTD. -00-10-94 (hex) Performance Analysis Broadband, Spirent plc -00-10-95 (hex) Thomson Inc. -00-10-96 (hex) TRACEWELL SYSTEMS, INC. -00-10-97 (hex) WinNet Metropolitan Communications Systems, Inc. -00-10-98 (hex) STARNET TECHNOLOGIES, INC. -00-10-99 (hex) InnoMedia, Inc. -00-10-9A (hex) NETLINE -00-10-9B (hex) Emulex Corporation -00-10-9C (hex) M-SYSTEM CO., LTD. -00-10-9D (hex) CLARINET SYSTEMS, INC. -00-10-9E (hex) AWARE, INC. -00-10-9F (hex) PAVO, INC. -00-10-A0 (hex) INNOVEX TECHNOLOGIES, INC. -00-10-A1 (hex) KENDIN SEMICONDUCTOR, INC. -00-10-A2 (hex) TNS -00-10-A3 (hex) OMNITRONIX, INC. -00-10-A4 (hex) XIRCOM -00-10-A5 (hex) OXFORD INSTRUMENTS -00-10-A6 (hex) CISCO SYSTEMS, INC. -00-10-A7 (hex) UNEX TECHNOLOGY CORPORATION -00-10-A8 (hex) RELIANCE COMPUTER CORP. -00-10-A9 (hex) ADHOC TECHNOLOGIES -00-10-AA (hex) MEDIA4, INC. -00-10-AB (hex) KOITO INDUSTRIES, LTD. -00-10-AC (hex) IMCI TECHNOLOGIES -00-10-AD (hex) SOFTRONICS USB, INC. -00-10-AE (hex) SHINKO ELECTRIC INDUSTRIES CO. -00-10-AF (hex) TAC SYSTEMS, INC. -00-10-B0 (hex) MERIDIAN TECHNOLOGY CORP. -00-10-B1 (hex) FOR-A CO., LTD. -00-10-B2 (hex) COACTIVE AESTHETICS -00-10-B3 (hex) NOKIA MULTIMEDIA TERMINALS -00-10-B4 (hex) ATMOSPHERE NETWORKS -00-10-B5 (hex) ACCTON TECHNOLOGY CORPORATION -00-10-B6 (hex) ENTRATA COMMUNICATIONS CORP. -00-10-B7 (hex) COYOTE TECHNOLOGIES, LLC -00-10-B8 (hex) ISHIGAKI COMPUTER SYSTEM CO. -00-10-B9 (hex) MAXTOR CORP. -00-10-BA (hex) MARTINHO-DAVIS SYSTEMS, INC. -00-10-BB (hex) DATA & INFORMATION TECHNOLOGY -00-10-BC (hex) Aastra Telecom -00-10-BD (hex) THE TELECOMMUNICATION TECHNOLOGY COMMITTEE -00-10-BE (hex) TELEXIS CORP. -00-10-BF (hex) InterAir Wireless -00-10-C0 (hex) ARMA, Inc. -00-10-C1 (hex) OI ELECTRIC CO., LTD. -00-10-C2 (hex) WILLNET, INC. -00-10-C3 (hex) CSI-CONTROL SYSTEMS -00-10-C4 (hex) MEDIA LINKS CO., LTD. -00-10-C5 (hex) PROTOCOL TECHNOLOGIES, INC. -00-10-C6 (hex) USI -00-10-C7 (hex) DATA TRANSMISSION NETWORK -00-10-C8 (hex) COMMUNICATIONS ELECTRONICS SECURITY GROUP -00-10-C9 (hex) MITSUBISHI ELECTRONICS LOGISTIC SUPPORT CO. -00-10-CA (hex) INTEGRAL ACCESS -00-10-CB (hex) FACIT K.K. -00-10-CC (hex) CLP COMPUTER LOGISTIK PLANUNG GmbH -00-10-CD (hex) INTERFACE CONCEPT -00-10-CE (hex) VOLAMP, LTD. -00-10-CF (hex) FIBERLANE COMMUNICATIONS -00-10-D0 (hex) WITCOM, LTD. -00-10-D1 (hex) Top Layer Networks, Inc. -00-10-D2 (hex) NITTO TSUSHINKI CO., LTD -00-10-D3 (hex) GRIPS ELECTRONIC GMBH -00-10-D4 (hex) STORAGE COMPUTER CORPORATION -00-10-D5 (hex) IMASDE CANARIAS, S.A. -00-10-D6 (hex) ITT - A/CD -00-10-D7 (hex) ARGOSY RESEARCH INC. -00-10-D8 (hex) CALISTA -00-10-D9 (hex) IBM JAPAN, FUJISAWA MT+D -00-10-DA (hex) MOTION ENGINEERING, INC. -00-10-DB (hex) Juniper Networks, Inc. -00-10-DC (hex) MICRO-STAR INTERNATIONAL CO., LTD. -00-10-DD (hex) ENABLE SEMICONDUCTOR, INC. -00-10-DE (hex) INTERNATIONAL DATACASTING CORPORATION -00-10-DF (hex) RISE COMPUTER INC. -00-10-E0 (hex) Sun Microsystems, Inc. -00-10-E1 (hex) S.I. TECH, INC. -00-10-E2 (hex) ArrayComm, Inc. -00-10-E3 (hex) Hewlett Packard -00-10-E4 (hex) NSI CORPORATION -00-10-E5 (hex) SOLECTRON TEXAS -00-10-E6 (hex) APPLIED INTELLIGENT SYSTEMS, INC. -00-10-E7 (hex) BreezeCom -00-10-E8 (hex) TELOCITY, INCORPORATED -00-10-E9 (hex) RAIDTEC LTD. -00-10-EA (hex) ADEPT TECHNOLOGY -00-10-EB (hex) SELSIUS SYSTEMS, INC. -00-10-EC (hex) RPCG, LLC -00-10-ED (hex) SUNDANCE TECHNOLOGY, INC. -00-10-EE (hex) CTI PRODUCTS, INC. -00-10-EF (hex) DBTEL INCORPORATED -00-10-F1 (hex) I-O CORPORATION -00-10-F2 (hex) ANTEC -00-10-F3 (hex) Nexcom International Co., Ltd. -00-10-F4 (hex) Vertical Communications -00-10-F5 (hex) AMHERST SYSTEMS, INC. -00-10-F6 (hex) CISCO SYSTEMS, INC. -00-10-F7 (hex) IRIICHI TECHNOLOGIES Inc. -00-10-F8 (hex) Niikke Techno System Co. Ltd -00-10-F9 (hex) UNIQUE SYSTEMS, INC. -00-10-FA (hex) Apple Inc -00-10-FB (hex) ZIDA TECHNOLOGIES LIMITED -00-10-FC (hex) BROADBAND NETWORKS, INC. -00-10-FD (hex) COCOM A/S -00-10-FE (hex) DIGITAL EQUIPMENT CORPORATION -00-10-FF (hex) CISCO SYSTEMS, INC. -00-11-00 (hex) Schneider Electric -00-11-01 (hex) CET Technologies Pte Ltd -00-11-02 (hex) Aurora Multimedia Corp. -00-11-03 (hex) kawamura electric inc. -00-11-04 (hex) TELEXY -00-11-05 (hex) Sunplus Technology Co., Ltd. -00-11-06 (hex) Siemens NV (Belgium) -00-11-07 (hex) RGB Networks Inc. -00-11-08 (hex) Orbital Data Corporation -00-11-09 (hex) Micro-Star International -00-11-0A (hex) Hewlett Packard -00-11-0B (hex) Franklin Technology Systems -00-11-0C (hex) Atmark Techno, Inc. -00-11-0D (hex) SANBlaze Technology, Inc. -00-11-0E (hex) Tsurusaki Sealand Transportation Co. Ltd. -00-11-0F (hex) netplat,Inc. -00-11-10 (hex) Maxanna Technology Co., Ltd. -00-11-11 (hex) Intel Corporation -00-11-12 (hex) Honeywell CMSS -00-11-13 (hex) Fraunhofer FOKUS -00-11-14 (hex) EverFocus Electronics Corp. -00-11-15 (hex) EPIN Technologies, Inc. -00-11-16 (hex) COTEAU VERT CO., LTD. -00-11-17 (hex) CESNET -00-11-18 (hex) BLX IC Design Corp., Ltd. -00-11-19 (hex) Solteras, Inc. -00-11-1A (hex) Motorola BCS -00-11-1B (hex) Targa Systems Div L-3 Communications Canada -00-11-1C (hex) Pleora Technologies Inc. -00-11-1D (hex) Hectrix Limited -00-11-1E (hex) EPSG (Ethernet Powerlink Standardization Group) -00-11-1F (hex) Doremi Labs, Inc. -00-11-20 (hex) Cisco Systems -00-11-21 (hex) Cisco Systems -00-11-22 (hex) CIMSYS Inc -00-11-23 (hex) Appointech, Inc. -00-11-24 (hex) Apple Computer -00-11-25 (hex) IBM Corporation -00-11-26 (hex) Venstar Inc. -00-11-27 (hex) TASI, Inc -00-11-28 (hex) Streamit -00-11-29 (hex) Paradise Datacom Ltd. -00-11-2A (hex) Niko NV -00-11-2B (hex) NetModule -00-11-2C (hex) IZT GmbH -00-11-2D (hex) iPulse Systems -00-11-2E (hex) CEICOM -00-11-2F (hex) ASUSTek Computer Inc. -00-11-30 (hex) Allied Telesis (Hong Kong) Ltd. -00-11-31 (hex) UNATECH. CO.,LTD -00-11-32 (hex) Synology Incorporated -00-11-33 (hex) Siemens Austria SIMEA -00-11-34 (hex) MediaCell, Inc. -00-11-35 (hex) Grandeye Ltd -00-11-36 (hex) Goodrich Sensor Systems -00-11-37 (hex) AICHI ELECTRIC CO., LTD. -00-11-38 (hex) TAISHIN CO., LTD. -00-11-39 (hex) STOEBER ANTRIEBSTECHNIK GmbH + Co. KG. -00-11-3A (hex) SHINBORAM -00-11-3B (hex) Micronet Communications Inc. -00-11-3C (hex) Micronas GmbH -00-11-3D (hex) KN SOLTEC CO.,LTD. -00-11-3E (hex) JL Corporation -00-11-3F (hex) Alcatel DI -00-11-40 (hex) Nanometrics Inc. -00-11-41 (hex) GoodMan Corporation -00-11-42 (hex) e-SMARTCOM INC. -00-11-43 (hex) DELL INC. -00-11-44 (hex) Assurance Technology Corp -00-11-45 (hex) ValuePoint Networks -00-11-46 (hex) Telecard-Pribor Ltd -00-11-47 (hex) Secom-Industry co.LTD. -00-11-48 (hex) Prolon Control Systems -00-11-49 (hex) Proliphix Inc. -00-11-4A (hex) KAYABA INDUSTRY Co,.Ltd. -00-11-4B (hex) Francotyp-Postalia GmbH -00-11-4C (hex) caffeina applied research ltd. -00-11-4D (hex) Atsumi Electric Co.,LTD. -00-11-4E (hex) 690885 Ontario Inc. -00-11-4F (hex) US Digital Television, Inc -00-11-50 (hex) Belkin Corporation -00-11-51 (hex) Mykotronx -00-11-52 (hex) Eidsvoll Electronics AS -00-11-53 (hex) Trident Tek, Inc. -00-11-54 (hex) Webpro Technologies Inc. -00-11-55 (hex) Sevis Systems -00-11-56 (hex) Pharos Systems NZ -00-11-57 (hex) OF Networks Co., Ltd. -00-11-58 (hex) Nortel Networks -00-11-59 (hex) MATISSE NETWORKS INC -00-11-5A (hex) Ivoclar Vivadent AG -00-11-5B (hex) Elitegroup Computer System Co. (ECS) -00-11-5C (hex) Cisco -00-11-5D (hex) Cisco -00-11-5E (hex) ProMinent Dosiertechnik GmbH -00-11-5F (hex) ITX Security Co., Ltd. -00-11-60 (hex) ARTDIO Company Co., LTD -00-11-61 (hex) NetStreams, LLC -00-11-62 (hex) STAR MICRONICS CO.,LTD. -00-11-63 (hex) SYSTEM SPA DEPT. ELECTRONICS -00-11-64 (hex) ACARD Technology Corp. -00-11-65 (hex) Znyx Networks -00-11-66 (hex) Taelim Electronics Co., Ltd. -00-11-67 (hex) Integrated System Solution Corp. -00-11-68 (hex) HomeLogic LLC -00-11-69 (hex) EMS Satcom -00-11-6A (hex) Domo Ltd -00-11-6B (hex) Digital Data Communications Asia Co.,Ltd -00-11-6C (hex) Nanwang Multimedia Inc.,Ltd -00-11-6D (hex) American Time and Signal -00-11-6E (hex) PePLink Ltd. -00-11-6F (hex) Netforyou Co., LTD. -00-11-70 (hex) GSC SRL -00-11-71 (hex) DEXTER Communications, Inc. -00-11-72 (hex) COTRON CORPORATION -00-11-73 (hex) SMART Modular Technologies -00-11-74 (hex) Wibhu Technologies, Inc. -00-11-75 (hex) PathScale, Inc. -00-11-76 (hex) Intellambda Systems, Inc. -00-11-77 (hex) Coaxial Networks, Inc. -00-11-78 (hex) Chiron Technology Ltd -00-11-79 (hex) Singular Technology Co. Ltd. -00-11-7A (hex) Singim International Corp. -00-11-7B (hex) Bchi Labortechnik AG -00-11-7C (hex) e-zy.net -00-11-7D (hex) ZMD America, Inc. -00-11-7E (hex) Progeny Inc. -00-11-7F (hex) Neotune Information Technology Corporation,.LTD -00-11-80 (hex) Motorola BCS -00-11-81 (hex) InterEnergy Co.Ltd, -00-11-82 (hex) IMI Norgren Ltd -00-11-83 (hex) Datalogic Mobile, Inc. -00-11-84 (hex) Humo Laboratory,Ltd. -00-11-85 (hex) Hewlett Packard -00-11-86 (hex) Prime Systems, Inc. -00-11-87 (hex) Category Solutions, Inc -00-11-88 (hex) Enterasys -00-11-89 (hex) Aerotech Inc -00-11-8A (hex) Viewtran Technology Limited -00-11-8B (hex) Alcatel-Lucent, Enterprise Business Group -00-11-8C (hex) Missouri Department of Transportation -00-11-8D (hex) Hanchang System Corp. -00-11-8E (hex) Halytech Mace -00-11-8F (hex) EUTECH INSTRUMENTS PTE. LTD. -00-11-90 (hex) Digital Design Corporation -00-11-91 (hex) CTS-Clima Temperatur Systeme GmbH -00-11-92 (hex) Cisco Systems -00-11-93 (hex) Cisco Systems -00-11-94 (hex) Chi Mei Communication Systems, Inc. -00-11-95 (hex) D-Link Corporation -00-11-96 (hex) Actuality Systems, Inc. -00-11-97 (hex) Monitoring Technologies Limited -00-11-98 (hex) Prism Media Products Limited -00-11-99 (hex) 2wcom GmbH -00-11-9A (hex) Alkeria srl -00-11-9B (hex) Telesynergy Research Inc. -00-11-9C (hex) EP&T Energy -00-11-9D (hex) Diginfo Technology Corporation -00-11-9E (hex) Solectron Brazil -00-11-9F (hex) Nokia Danmark A/S -00-11-A0 (hex) Vtech Engineering Canada Ltd -00-11-A1 (hex) VISION NETWARE CO.,LTD -00-11-A2 (hex) Manufacturing Technology Inc -00-11-A3 (hex) LanReady Technologies Inc. -00-11-A4 (hex) JStream Technologies Inc. -00-11-A5 (hex) Fortuna Electronic Corp. -00-11-A6 (hex) Sypixx Networks -00-11-A7 (hex) Infilco Degremont Inc. -00-11-A8 (hex) Quest Technologies -00-11-A9 (hex) MOIMSTONE Co., LTD -00-11-AA (hex) Uniclass Technology, Co., LTD -00-11-AB (hex) TRUSTABLE TECHNOLOGY CO.,LTD. -00-11-AC (hex) Simtec Electronics -00-11-AD (hex) Shanghai Ruijie Technology -00-11-AE (hex) Motorola BCS -00-11-AF (hex) Medialink-i,Inc -00-11-B0 (hex) Fortelink Inc. -00-11-B1 (hex) BlueExpert Technology Corp. -00-11-B2 (hex) 2001 Technology Inc. -00-11-B3 (hex) YOSHIMIYA CO.,LTD. -00-11-B4 (hex) Westermo Teleindustri AB -00-11-B5 (hex) Shenzhen Powercom Co.,Ltd -00-11-B6 (hex) Open Systems International -00-11-B7 (hex) Octalix B.V. -00-11-B8 (hex) Liebherr - Elektronik GmbH -00-11-B9 (hex) Inner Range Pty. Ltd. -00-11-BA (hex) Elexol Pty Ltd -00-11-BB (hex) Cisco Systems -00-11-BC (hex) Cisco Systems -00-11-BD (hex) Bombardier Transportation -00-11-BE (hex) AGP Telecom Co. Ltd -00-11-BF (hex) AESYS S.p.A. -00-11-C0 (hex) Aday Technology Inc -00-11-C1 (hex) 4P MOBILE DATA PROCESSING -00-11-C2 (hex) United Fiber Optic Communication -00-11-C3 (hex) Transceiving System Technology Corporation -00-11-C4 (hex) Terminales de Telecomunicacion Terrestre, S.L. -00-11-C5 (hex) TEN Technology -00-11-C6 (hex) Seagate Technology LLC -00-11-C7 (hex) Raymarine UK Ltd -00-11-C8 (hex) Powercom Co., Ltd. -00-11-C9 (hex) MTT Corporation -00-11-CA (hex) Long Range Systems, Inc. -00-11-CB (hex) Jacobsons AB -00-11-CC (hex) Guangzhou Jinpeng Group Co.,Ltd. -00-11-CD (hex) Axsun Technologies -00-11-CE (hex) Ubisense Limited -00-11-CF (hex) Thrane & Thrane A/S -00-11-D0 (hex) Tandberg Data ASA -00-11-D1 (hex) Soft Imaging System GmbH -00-11-D2 (hex) Perception Digital Ltd -00-11-D3 (hex) NextGenTel Holding ASA -00-11-D4 (hex) NetEnrich, Inc -00-11-D5 (hex) Hangzhou Sunyard System Engineering Co.,Ltd. -00-11-D6 (hex) HandEra, Inc. -00-11-D7 (hex) eWerks Inc -00-11-D8 (hex) ASUSTek Computer Inc. -00-11-D9 (hex) TiVo -00-11-DA (hex) Vivaas Technology Inc. -00-11-DB (hex) Land-Cellular Corporation -00-11-DC (hex) Glunz & Jensen -00-11-DD (hex) FROMUS TEC. Co., Ltd. -00-11-DE (hex) EURILOGIC -00-11-DF (hex) Current Energy -00-11-E0 (hex) U-MEDIA Communications, Inc. -00-11-E1 (hex) BEKO Electronics Co. -00-11-E2 (hex) Hua Jung Components Co., Ltd. -00-11-E3 (hex) Thomson, Inc. -00-11-E4 (hex) Danelec Electronics A/S -00-11-E5 (hex) KCodes Corporation -00-11-E6 (hex) Scientific Atlanta -00-11-E7 (hex) WORLDSAT - Texas de France -00-11-E8 (hex) Tixi.Com -00-11-E9 (hex) STARNEX CO., LTD. -00-11-EA (hex) IWICS Inc. -00-11-EB (hex) Innovative Integration -00-11-EC (hex) AVIX INC. -00-11-ED (hex) 802 Global -00-11-EE (hex) Estari, Inc. -00-11-EF (hex) Conitec Datensysteme GmbH -00-11-F0 (hex) Wideful Limited -00-11-F1 (hex) QinetiQ Ltd -00-11-F2 (hex) Institute of Network Technologies -00-11-F3 (hex) NeoMedia Europe AG -00-11-F4 (hex) woori-net -00-11-F5 (hex) ASKEY COMPUTER CORP. -00-11-F6 (hex) Asia Pacific Microsystems , Inc. -00-11-F7 (hex) Shenzhen Forward Industry Co., Ltd -00-11-F8 (hex) AIRAYA Corp -00-11-F9 (hex) Nortel Networks -00-11-FA (hex) Rane Corporation -00-11-FB (hex) Heidelberg Engineering GmbH -00-11-FC (hex) HARTING Electric Gmbh & Co.KG -00-11-FD (hex) KORG INC. -00-11-FE (hex) Keiyo System Research, Inc. -00-11-FF (hex) Digitro Tecnologia Ltda -00-12-00 (hex) Cisco -00-12-01 (hex) Cisco -00-12-02 (hex) Decrane Aerospace - Audio International Inc. -00-12-03 (hex) Activ Networks -00-12-04 (hex) u10 Networks, Inc. -00-12-05 (hex) Terrasat Communications, Inc. -00-12-06 (hex) iQuest (NZ) Ltd -00-12-07 (hex) Head Strong International Limited -00-12-08 (hex) Gantner Instruments GmbH -00-12-09 (hex) Fastrax Ltd -00-12-0A (hex) Emerson Electric GmbH & Co. OHG -00-12-0B (hex) Chinasys Technologies Limited -00-12-0C (hex) CE-Infosys Pte Ltd -00-12-0D (hex) Advanced Telecommunication Technologies, Inc. -00-12-0E (hex) AboCom -00-12-0F (hex) IEEE 802.3 -00-12-10 (hex) WideRay Corp -00-12-11 (hex) Protechna Herbst GmbH & Co. KG -00-12-12 (hex) PLUS Corporation -00-12-13 (hex) Metrohm AG -00-12-14 (hex) Koenig & Bauer AG -00-12-15 (hex) iStor Networks, Inc. -00-12-16 (hex) ICP Internet Communication Payment AG -00-12-17 (hex) Cisco-Linksys, LLC -00-12-18 (hex) ARUZE Corporation -00-12-19 (hex) Ahead Communication Systems Inc -00-12-1A (hex) Techno Soft Systemnics Inc. -00-12-1B (hex) Sound Devices, LLC -00-12-1C (hex) PARROT S.A. -00-12-1D (hex) Netfabric Corporation -00-12-1E (hex) Juniper Networks, Inc. -00-12-1F (hex) Harding Intruments -00-12-20 (hex) Cadco Systems -00-12-21 (hex) B.Braun Melsungen AG -00-12-22 (hex) Skardin (UK) Ltd -00-12-23 (hex) Pixim -00-12-24 (hex) NexQL Corporation -00-12-25 (hex) Motorola BCS -00-12-26 (hex) Japan Direx Corporation -00-12-27 (hex) Franklin Electric Co., Inc. -00-12-28 (hex) Data Ltd. -00-12-29 (hex) BroadEasy Technologies Co.,Ltd -00-12-2A (hex) VTech Telecommunications Ltd. -00-12-2B (hex) Virbiage Pty Ltd -00-12-2C (hex) Soenen Controls N.V. -00-12-2D (hex) SiNett Corporation -00-12-2E (hex) Signal Technology - AISD -00-12-2F (hex) Sanei Electric Inc. -00-12-30 (hex) Picaso Infocommunication CO., LTD. -00-12-31 (hex) Motion Control Systems, Inc. -00-12-32 (hex) LeWiz Communications Inc. -00-12-33 (hex) JRC TOKKI Co.,Ltd. -00-12-34 (hex) Camille Bauer -00-12-35 (hex) Andrew Corporation -00-12-36 (hex) ConSentry Networks -00-12-37 (hex) Texas Instruments -00-12-38 (hex) SetaBox Technology Co., Ltd. -00-12-39 (hex) S Net Systems Inc. -00-12-3A (hex) Posystech Inc., Co. -00-12-3B (hex) KeRo Systems ApS -00-12-3C (hex) Second Rule LLC -00-12-3D (hex) GES -00-12-3E (hex) ERUNE technology Co., Ltd. -00-12-3F (hex) Dell Inc -00-12-40 (hex) AMOI ELECTRONICS CO.,LTD -00-12-41 (hex) a2i marketing center -00-12-42 (hex) Millennial Net -00-12-43 (hex) Cisco -00-12-44 (hex) Cisco -00-12-45 (hex) Zellweger Analytics, Inc. -00-12-46 (hex) T.O.M TECHNOLOGY INC.. -00-12-47 (hex) Samsung Electronics Co., Ltd. -00-12-48 (hex) EMC Corporation (Kashya) -00-12-49 (hex) Delta Elettronica S.p.A. -00-12-4A (hex) Dedicated Devices, Inc. -00-12-4B (hex) Texas Instruments -00-12-4C (hex) BBWM Corporation -00-12-4D (hex) Inducon BV -00-12-4E (hex) XAC AUTOMATION CORP. -00-12-4F (hex) Tyco Thermal Controls LLC. -00-12-50 (hex) Tokyo Aircaft Instrument Co., Ltd. -00-12-51 (hex) SILINK -00-12-52 (hex) Citronix, LLC -00-12-53 (hex) AudioDev AB -00-12-54 (hex) Spectra Technologies Holdings Company Ltd -00-12-55 (hex) NetEffect Incorporated -00-12-56 (hex) LG INFORMATION & COMM. -00-12-57 (hex) LeapComm Communication Technologies Inc. -00-12-58 (hex) Activis Polska -00-12-59 (hex) THERMO ELECTRON KARLSRUHE -00-12-5A (hex) Microsoft Corporation -00-12-5B (hex) KAIMEI ELECTRONI -00-12-5C (hex) Green Hills Software, Inc. -00-12-5D (hex) CyberNet Inc. -00-12-5E (hex) CAEN -00-12-5F (hex) AWIND Inc. -00-12-60 (hex) Stanton Magnetics,inc. -00-12-61 (hex) Adaptix, Inc -00-12-62 (hex) Nokia Danmark A/S -00-12-63 (hex) Data Voice Technologies GmbH -00-12-64 (hex) daum electronic gmbh -00-12-65 (hex) Enerdyne Technologies, Inc. -00-12-66 (hex) Swisscom Hospitality Services SA -00-12-67 (hex) Matsushita Electronic Components Co., Ltd. -00-12-68 (hex) IPS d.o.o. -00-12-69 (hex) Value Electronics -00-12-6A (hex) OPTOELECTRONICS Co., Ltd. -00-12-6B (hex) Ascalade Communications Limited -00-12-6C (hex) Visonic Ltd. -00-12-6D (hex) University of California, Berkeley -00-12-6E (hex) Seidel Elektronik GmbH Nfg.KG -00-12-6F (hex) Rayson Technology Co., Ltd. -00-12-70 (hex) NGES Denro Systems -00-12-71 (hex) Measurement Computing Corp -00-12-72 (hex) Redux Communications Ltd. -00-12-73 (hex) Stoke Inc -00-12-74 (hex) NIT lab -00-12-75 (hex) Sentilla Corporation -00-12-76 (hex) Microsol Holdings Ltd. -00-12-77 (hex) Korenix Technologies Co., Ltd. -00-12-78 (hex) International Bar Code -00-12-79 (hex) Hewlett Packard -00-12-7A (hex) Sanyu Industry Co.,Ltd. -00-12-7B (hex) VIA Networking Technologies, Inc. -00-12-7C (hex) SWEGON AB -00-12-7D (hex) MobileAria -00-12-7E (hex) Digital Lifestyles Group, Inc. -00-12-7F (hex) Cisco -00-12-80 (hex) Cisco -00-12-81 (hex) CIEFFE srl -00-12-82 (hex) Qovia -00-12-83 (hex) Nortel Networks -00-12-84 (hex) Lab33 Srl -00-12-85 (hex) Gizmondo Europe Ltd -00-12-86 (hex) ENDEVCO CORP -00-12-87 (hex) Digital Everywhere Unterhaltungselektronik GmbH -00-12-88 (hex) 2Wire, Inc -00-12-89 (hex) Advance Sterilization Products -00-12-8A (hex) Motorola PCS -00-12-8B (hex) Sensory Networks Inc -00-12-8C (hex) Woodward Governor -00-12-8D (hex) STB Datenservice GmbH -00-12-8E (hex) Q-Free ASA -00-12-8F (hex) Montilio -00-12-90 (hex) KYOWA Electric & Machinery Corp. -00-12-91 (hex) KWS Computersysteme GmbH -00-12-92 (hex) Griffin Technology -00-12-93 (hex) GE Energy -00-12-94 (hex) SUMITOMO ELECTRIC DEVICE INNOVATIONS, INC -00-12-95 (hex) Aiware Inc. -00-12-96 (hex) Addlogix -00-12-97 (hex) O2Micro, Inc. -00-12-98 (hex) MICO ELECTRIC(SHENZHEN) LIMITED -00-12-99 (hex) Ktech Telecommunications Inc -00-12-9A (hex) IRT Electronics Pty Ltd -00-12-9B (hex) E2S Electronic Engineering Solutions, S.L. -00-12-9C (hex) Yulinet -00-12-9D (hex) First International Computer do Brasil -00-12-9E (hex) Surf Communications Inc. -00-12-9F (hex) RAE Systems, Inc. -00-12-A0 (hex) NeoMeridian Sdn Bhd -00-12-A1 (hex) BluePacket Communications Co., Ltd. -00-12-A2 (hex) VITA -00-12-A3 (hex) Trust International B.V. -00-12-A4 (hex) ThingMagic, LLC -00-12-A5 (hex) Stargen, Inc. -00-12-A6 (hex) Dolby Australia -00-12-A7 (hex) ISR TECHNOLOGIES Inc -00-12-A8 (hex) intec GmbH -00-12-A9 (hex) 3Com Ltd -00-12-AA (hex) IEE, Inc. -00-12-AB (hex) WiLife, Inc. -00-12-AC (hex) ONTIMETEK INC. -00-12-AD (hex) IDS GmbH -00-12-AE (hex) HLS HARD-LINE Solutions Inc. -00-12-AF (hex) ELPRO Technologies -00-12-B0 (hex) Efore Oyj (Plc) -00-12-B1 (hex) Dai Nippon Printing Co., Ltd -00-12-B2 (hex) AVOLITES LTD. -00-12-B3 (hex) Advance Wireless Technology Corp. -00-12-B4 (hex) Work Microwave GmbH -00-12-B5 (hex) Vialta, Inc. -00-12-B6 (hex) Santa Barbara Infrared, Inc. -00-12-B7 (hex) PTW Freiburg -00-12-B8 (hex) G2 Microsystems -00-12-B9 (hex) Fusion Digital Technology -00-12-BA (hex) FSI Systems, Inc. -00-12-BB (hex) Telecommunications Industry Association TR-41 Committee -00-12-BC (hex) Echolab LLC -00-12-BD (hex) Avantec Manufacturing Limited -00-12-BE (hex) Astek Corporation -00-12-BF (hex) Arcadyan Technology Corporation -00-12-C0 (hex) HotLava Systems, Inc. -00-12-C1 (hex) Check Point Software Technologies -00-12-C2 (hex) Apex Electronics Factory -00-12-C3 (hex) WIT S.A. -00-12-C4 (hex) Viseon, Inc. -00-12-C5 (hex) V-Show Technology (China) Co.,Ltd -00-12-C6 (hex) TGC America, Inc -00-12-C7 (hex) SECURAY Technologies Ltd.Co. -00-12-C8 (hex) Perfect tech -00-12-C9 (hex) Motorola BCS -00-12-CA (hex) Mechatronic Brick Aps -00-12-CB (hex) CSS Inc. -00-12-CC (hex) Bitatek CO., LTD -00-12-CD (hex) ASEM SpA -00-12-CE (hex) Advanced Cybernetics Group -00-12-CF (hex) Accton Technology Corporation -00-12-D0 (hex) Gossen-Metrawatt-GmbH -00-12-D1 (hex) Texas Instruments Inc -00-12-D2 (hex) Texas Instruments -00-12-D3 (hex) Zetta Systems, Inc. -00-12-D4 (hex) Princeton Technology, Ltd -00-12-D5 (hex) Motion Reality Inc. -00-12-D6 (hex) Jiangsu Yitong High-Tech Co.,Ltd -00-12-D7 (hex) Invento Networks, Inc. -00-12-D8 (hex) International Games System Co., Ltd. -00-12-D9 (hex) Cisco Systems -00-12-DA (hex) Cisco Systems -00-12-DB (hex) ZIEHL industrie-elektronik GmbH + Co KG -00-12-DC (hex) SunCorp Industrial Limited -00-12-DD (hex) Shengqu Information Technology (Shanghai) Co., Ltd. -00-12-DE (hex) Radio Components Sweden AB -00-12-DF (hex) Novomatic AG -00-12-E0 (hex) Codan Limited -00-12-E1 (hex) Alliant Networks, Inc -00-12-E2 (hex) ALAXALA Networks Corporation -00-12-E3 (hex) Agat-RT, Ltd. -00-12-E4 (hex) ZIEHL industrie-electronik GmbH + Co KG -00-12-E5 (hex) Time America, Inc. -00-12-E6 (hex) SPECTEC COMPUTER CO., LTD. -00-12-E7 (hex) Projectek Networking Electronics Corp. -00-12-E8 (hex) Fraunhofer IMS -00-12-E9 (hex) Abbey Systems Ltd -00-12-EA (hex) Trane -00-12-EB (hex) R2DI, LLC -00-12-EC (hex) Movacolor b.v. -00-12-ED (hex) AVG Advanced Technologies -00-12-EE (hex) Sony Ericsson Mobile Communications AB -00-12-EF (hex) OneAccess SA -00-12-F0 (hex) Intel Corporate -00-12-F1 (hex) IFOTEC -00-12-F2 (hex) Brocade Communications Systems, Inc -00-12-F3 (hex) connectBlue AB -00-12-F4 (hex) Belco International Co.,Ltd. -00-12-F5 (hex) Imarda New Zealand Limited -00-12-F6 (hex) MDK CO.,LTD. -00-12-F7 (hex) Xiamen Xinglian Electronics Co., Ltd. -00-12-F8 (hex) WNI Resources, LLC -00-12-F9 (hex) URYU SEISAKU, LTD. -00-12-FA (hex) THX LTD -00-12-FB (hex) Samsung Electronics -00-12-FC (hex) PLANET System Co.,LTD -00-12-FD (hex) OPTIMUS IC S.A. -00-12-FE (hex) Lenovo Mobile Communication Technology Ltd. -00-12-FF (hex) Lely Industries N.V. -00-13-00 (hex) IT-FACTORY, INC. -00-13-01 (hex) IronGate S.L. -00-13-02 (hex) Intel Corporate -00-13-03 (hex) GateConnect Technologies GmbH -00-13-04 (hex) Flaircomm Technologies Co. LTD -00-13-05 (hex) Epicom, Inc. -00-13-06 (hex) Always On Wireless -00-13-07 (hex) Paravirtual Corporation -00-13-08 (hex) Nuvera Fuel Cells -00-13-09 (hex) Ocean Broadband Networks -00-13-0A (hex) Nortel -00-13-0B (hex) Mextal B.V. -00-13-0C (hex) HF System Corporation -00-13-0D (hex) GALILEO AVIONICA -00-13-0E (hex) Focusrite Audio Engineering Limited -00-13-0F (hex) EGEMEN Bilgisayar Muh San ve Tic LTD STI -00-13-10 (hex) Cisco-Linksys, LLC -00-13-11 (hex) ARRIS International -00-13-12 (hex) Amedia Networks Inc. -00-13-13 (hex) GuangZhou Post & Telecom Equipment ltd -00-13-14 (hex) Asiamajor Inc. -00-13-15 (hex) SONY Computer Entertainment inc, -00-13-16 (hex) L-S-B Broadcast Technologies GmbH -00-13-17 (hex) GN Netcom as -00-13-18 (hex) DGSTATION Co., Ltd. -00-13-19 (hex) Cisco Systems -00-13-1A (hex) Cisco Systems -00-13-1B (hex) BeCell Innovations Corp. -00-13-1C (hex) LiteTouch, Inc. -00-13-1D (hex) Scanvaegt International A/S -00-13-1E (hex) Peiker acustic GmbH & Co. KG -00-13-1F (hex) NxtPhase T&D, Corp. -00-13-20 (hex) Intel Corporate -00-13-21 (hex) Hewlett Packard -00-13-22 (hex) DAQ Electronics, Inc. -00-13-23 (hex) Cap Co., Ltd. -00-13-24 (hex) Schneider Electric Ultra Terminal -00-13-25 (hex) Cortina Systems Inc -00-13-26 (hex) ECM Systems Ltd -00-13-27 (hex) Data Acquisitions limited -00-13-28 (hex) Westech Korea Inc., -00-13-29 (hex) VSST Co., LTD -00-13-2A (hex) Sitronics Telecom Solutions -00-13-2B (hex) Phoenix Digital -00-13-2C (hex) MAZ Brandenburg GmbH -00-13-2D (hex) iWise Communications -00-13-2E (hex) ITian Coporation -00-13-2F (hex) Interactek -00-13-30 (hex) EURO PROTECTION SURVEILLANCE -00-13-31 (hex) CellPoint Connect -00-13-32 (hex) Beijing Topsec Network Security Technology Co., Ltd. -00-13-33 (hex) BaudTec Corporation -00-13-34 (hex) Arkados, Inc. -00-13-35 (hex) VS Industry Berhad -00-13-36 (hex) Tianjin 712 Communication Broadcasting co., ltd. -00-13-37 (hex) Orient Power Home Network Ltd. -00-13-38 (hex) FRESENIUS-VIAL -00-13-39 (hex) EL-ME AG -00-13-3A (hex) VadaTech Inc. -00-13-3B (hex) Speed Dragon Multimedia Limited -00-13-3C (hex) QUINTRON SYSTEMS INC. -00-13-3D (hex) Micro Memory Curtiss Wright Co -00-13-3E (hex) MetaSwitch -00-13-3F (hex) Eppendorf Instrumente GmbH -00-13-40 (hex) AD.EL s.r.l. -00-13-41 (hex) Shandong New Beiyang Information Technology Co.,Ltd -00-13-42 (hex) Vision Research, Inc. -00-13-43 (hex) Matsushita Electronic Components (Europe) GmbH -00-13-44 (hex) Fargo Electronics Inc. -00-13-45 (hex) Eaton Corporation -00-13-46 (hex) D-Link Corporation -00-13-47 (hex) BlueTree Wireless Data Inc. -00-13-48 (hex) Artila Electronics Co., Ltd. -00-13-49 (hex) ZyXEL Communications Corporation -00-13-4A (hex) Engim, Inc. -00-13-4B (hex) ToGoldenNet Technology Inc. -00-13-4C (hex) YDT Technology International -00-13-4D (hex) IPC systems -00-13-4E (hex) Valox Systems, Inc. -00-13-4F (hex) Tranzeo Wireless Technologies Inc. -00-13-50 (hex) Silver Spring Networks, Inc -00-13-51 (hex) Niles Audio Corporation -00-13-52 (hex) Naztec, Inc. -00-13-53 (hex) HYDAC Filtertechnik GMBH -00-13-54 (hex) Zcomax Technologies, Inc. -00-13-55 (hex) TOMEN Cyber-business Solutions, Inc. -00-13-56 (hex) target systemelectronic gmbh -00-13-57 (hex) Soyal Technology Co., Ltd. -00-13-58 (hex) Realm Systems, Inc. -00-13-59 (hex) ProTelevision Technologies A/S -00-13-5A (hex) Project T&E Limited -00-13-5B (hex) PanelLink Cinema, LLC -00-13-5C (hex) OnSite Systems, Inc. -00-13-5D (hex) NTTPC Communications, Inc. -00-13-5E (hex) EAB/RWI/K -00-13-5F (hex) Cisco Systems -00-13-60 (hex) Cisco Systems -00-13-61 (hex) Biospace Co., Ltd. -00-13-62 (hex) ShinHeung Precision Co., Ltd. -00-13-63 (hex) Verascape, Inc. -00-13-64 (hex) Paradigm Technology Inc.. -00-13-65 (hex) Nortel -00-13-66 (hex) Neturity Technologies Inc. -00-13-67 (hex) Narayon. Co., Ltd. -00-13-68 (hex) Maersk Data Defence -00-13-69 (hex) Honda Electron Co., LED. -00-13-6A (hex) Hach Lange SA -00-13-6B (hex) E-TEC -00-13-6C (hex) TomTom -00-13-6D (hex) Tentaculus AB -00-13-6E (hex) Techmetro Corp. -00-13-6F (hex) PacketMotion, Inc. -00-13-70 (hex) Nokia Danmark A/S -00-13-71 (hex) Motorola CHS -00-13-72 (hex) Dell Inc. -00-13-73 (hex) BLwave Electronics Co., Ltd -00-13-74 (hex) Atheros Communications, Inc. -00-13-75 (hex) American Security Products Co. -00-13-76 (hex) Tabor Electronics Ltd. -00-13-77 (hex) Samsung Electronics CO., LTD -00-13-78 (hex) QSAN Technology, Inc. -00-13-79 (hex) PONDER INFORMATION INDUSTRIES LTD. -00-13-7A (hex) Netvox Technology Co., Ltd. -00-13-7B (hex) Movon Corporation -00-13-7C (hex) Kaicom co., Ltd. -00-13-7D (hex) Dynalab, Inc. -00-13-7E (hex) CorEdge Networks, Inc. -00-13-7F (hex) Cisco Systems -00-13-80 (hex) Cisco Systems -00-13-81 (hex) CHIPS & Systems, Inc. -00-13-82 (hex) Cetacea Networks Corporation -00-13-83 (hex) Application Technologies and Engineering Research Laboratory -00-13-84 (hex) Advanced Motion Controls -00-13-85 (hex) Add-On Technology Co., LTD. -00-13-86 (hex) ABB Inc./Totalflow -00-13-87 (hex) 27M Technologies AB -00-13-88 (hex) WiMedia Alliance -00-13-89 (hex) Redes de Telefona Mvil S.A. -00-13-8A (hex) QINGDAO GOERTEK ELECTRONICS CO.,LTD. -00-13-8B (hex) Phantom Technologies LLC -00-13-8C (hex) Kumyoung.Co.Ltd -00-13-8D (hex) Kinghold -00-13-8E (hex) FOAB Elektronik AB -00-13-8F (hex) Asiarock Incorporation -00-13-90 (hex) Termtek Computer Co., Ltd -00-13-91 (hex) OUEN CO.,LTD. -00-13-92 (hex) Ruckus Wireless -00-13-93 (hex) Panta Systems, Inc. -00-13-94 (hex) Infohand Co.,Ltd -00-13-95 (hex) congatec AG -00-13-96 (hex) Acbel Polytech Inc. -00-13-97 (hex) Xsigo Systems, Inc. -00-13-98 (hex) TrafficSim Co.,Ltd -00-13-99 (hex) STAC Corporation. -00-13-9A (hex) K-ubique ID Corp. -00-13-9B (hex) ioIMAGE Ltd. -00-13-9C (hex) Exavera Technologies, Inc. -00-13-9D (hex) Design of Systems on Silicon S.A. -00-13-9E (hex) Ciara Technologies Inc. -00-13-9F (hex) Electronics Design Services, Co., Ltd. -00-13-A0 (hex) ALGOSYSTEM Co., Ltd. -00-13-A1 (hex) Crow Electronic Engeneering -00-13-A2 (hex) MaxStream, Inc -00-13-A3 (hex) Siemens Com CPE Devices -00-13-A4 (hex) KeyEye Communications -00-13-A5 (hex) General Solutions, LTD. -00-13-A6 (hex) Extricom Ltd -00-13-A7 (hex) BATTELLE MEMORIAL INSTITUTE -00-13-A8 (hex) Tanisys Technology -00-13-A9 (hex) Sony Corporation -00-13-AA (hex) ALS & TEC Ltd. -00-13-AB (hex) Telemotive AG -00-13-AC (hex) Sunmyung Electronics Co., LTD -00-13-AD (hex) Sendo Ltd -00-13-AE (hex) Radiance Technologies, Inc. -00-13-AF (hex) NUMA Technology,Inc. -00-13-B0 (hex) Jablotron -00-13-B1 (hex) Intelligent Control Systems (Asia) Pte Ltd -00-13-B2 (hex) Carallon Limited -00-13-B3 (hex) Ecom Communications Technology Co., Ltd. -00-13-B4 (hex) Appear TV -00-13-B5 (hex) Wavesat -00-13-B6 (hex) Sling Media, Inc. -00-13-B7 (hex) Scantech ID -00-13-B8 (hex) RyCo Electronic Systems Limited -00-13-B9 (hex) BM SPA -00-13-BA (hex) ReadyLinks Inc -00-13-BB (hex) Smartvue Corporation -00-13-BC (hex) Artimi Ltd -00-13-BD (hex) HYMATOM SA -00-13-BE (hex) Virtual Conexions -00-13-BF (hex) Media System Planning Corp. -00-13-C0 (hex) Trix Tecnologia Ltda. -00-13-C1 (hex) Asoka USA Corporation -00-13-C2 (hex) WACOM Co.,Ltd -00-13-C3 (hex) Cisco Systems -00-13-C4 (hex) Cisco Systems -00-13-C5 (hex) LIGHTRON FIBER-OPTIC DEVICES INC. -00-13-C6 (hex) OpenGear, Inc -00-13-C7 (hex) IONOS Co.,Ltd. -00-13-C8 (hex) PIRELLI BROADBAND SOLUTIONS S.P.A. -00-13-C9 (hex) Beyond Achieve Enterprises Ltd. -00-13-CA (hex) X-Digital Systems, Inc. -00-13-CB (hex) Zenitel Norway AS -00-13-CC (hex) Tall Maple Systems -00-13-CD (hex) MTI co. LTD -00-13-CE (hex) Intel Corporate -00-13-CF (hex) 4Access Communications -00-13-D0 (hex) t+ Medical Ltd -00-13-D1 (hex) KIRK telecom A/S -00-13-D2 (hex) PAGE IBERICA, S.A. -00-13-D3 (hex) MICRO-STAR INTERNATIONAL CO., LTD. -00-13-D4 (hex) ASUSTek COMPUTER INC. -00-13-D5 (hex) WiNetworks LTD -00-13-D6 (hex) TII NETWORK TECHNOLOGIES, INC. -00-13-D7 (hex) SPIDCOM Technologies SA -00-13-D8 (hex) Princeton Instruments -00-13-D9 (hex) Matrix Product Development, Inc. -00-13-DA (hex) Diskware Co., Ltd -00-13-DB (hex) SHOEI Electric Co.,Ltd -00-13-DC (hex) IBTEK INC. -00-13-DD (hex) Abbott Diagnostics -00-13-DE (hex) Adapt4, LLC -00-13-DF (hex) Ryvor Corp. -00-13-E0 (hex) Murata Manufacturing Co., Ltd. -00-13-E1 (hex) Iprobe AB -00-13-E2 (hex) GeoVision Inc. -00-13-E3 (hex) CoVi Technologies, Inc. -00-13-E4 (hex) YANGJAE SYSTEMS CORP. -00-13-E5 (hex) TENOSYS, INC. -00-13-E6 (hex) Technolution -00-13-E7 (hex) Halcro -00-13-E8 (hex) Intel Corporate -00-13-E9 (hex) VeriWave, Inc. -00-13-EA (hex) Kamstrup A/S -00-13-EB (hex) Sysmaster Corporation -00-13-EC (hex) Sunbay Software AG -00-13-ED (hex) PSIA -00-13-EE (hex) JBX Designs Inc. -00-13-EF (hex) Kingjon Digital Technology Co.,Ltd -00-13-F0 (hex) Wavefront Semiconductor -00-13-F1 (hex) AMOD Technology Co., Ltd. -00-13-F2 (hex) Klas Ltd -00-13-F3 (hex) Giga-byte Communications Inc. -00-13-F4 (hex) Psitek (Pty) Ltd -00-13-F5 (hex) Akimbi Systems -00-13-F6 (hex) Cintech -00-13-F7 (hex) SMC Networks, Inc. -00-13-F8 (hex) Dex Security Solutions -00-13-F9 (hex) Cavera Systems -00-13-FA (hex) LifeSize Communications, Inc -00-13-FB (hex) RKC INSTRUMENT INC. -00-13-FC (hex) SiCortex, Inc -00-13-FD (hex) Nokia Danmark A/S -00-13-FE (hex) GRANDTEC ELECTRONIC CORP. -00-13-FF (hex) Dage-MTI of MC, Inc. -00-14-00 (hex) MINERVA KOREA CO., LTD -00-14-01 (hex) Rivertree Networks Corp. -00-14-02 (hex) kk-electronic a/s -00-14-03 (hex) Renasis, LLC -00-14-04 (hex) Motorola CHS -00-14-05 (hex) OpenIB, Inc. -00-14-06 (hex) Go Networks -00-14-07 (hex) Sperian Protection Instrumentation -00-14-08 (hex) Eka Systems Inc. -00-14-09 (hex) MAGNETI MARELLI S.E. S.p.A. -00-14-0A (hex) WEPIO Co., Ltd. -00-14-0B (hex) FIRST INTERNATIONAL COMPUTER, INC. -00-14-0C (hex) GKB CCTV CO., LTD. -00-14-0D (hex) Nortel -00-14-0E (hex) Nortel -00-14-0F (hex) Federal State Unitary Enterprise Leningrad R&D Institute of -00-14-10 (hex) Suzhou Keda Technology CO.,Ltd -00-14-11 (hex) Deutschmann Automation GmbH & Co. KG -00-14-12 (hex) S-TEC electronics AG -00-14-13 (hex) Trebing & Himstedt Prozessautomation GmbH & Co. KG -00-14-14 (hex) Jumpnode Systems LLC. -00-14-15 (hex) Intec Automation Inc. -00-14-16 (hex) Scosche Industries, Inc. -00-14-17 (hex) RSE Informations Technologie GmbH -00-14-18 (hex) C4Line -00-14-19 (hex) SIDSA -00-14-1A (hex) DEICY CORPORATION -00-14-1B (hex) Cisco Systems -00-14-1C (hex) Cisco Systems -00-14-1D (hex) Lust Antriebstechnik GmbH -00-14-1E (hex) P.A. Semi, Inc. -00-14-1F (hex) SunKwang Electronics Co., Ltd -00-14-20 (hex) G-Links networking company -00-14-21 (hex) Total Wireless Technologies Pte. Ltd. -00-14-22 (hex) Dell Inc. -00-14-23 (hex) J-S Co. NEUROCOM -00-14-24 (hex) Merry Electrics CO., LTD. -00-14-25 (hex) Galactic Computing Corp. -00-14-26 (hex) NL Technology -00-14-27 (hex) JazzMutant -00-14-28 (hex) Vocollect, Inc -00-14-29 (hex) V Center Technologies Co., Ltd. -00-14-2A (hex) Elitegroup Computer System Co., Ltd -00-14-2B (hex) Edata Communication Inc. -00-14-2C (hex) Koncept International, Inc. -00-14-2D (hex) Toradex AG -00-14-2E (hex) 77 Elektronika Kft. -00-14-2F (hex) WildPackets -00-14-30 (hex) ViPowER, Inc -00-14-31 (hex) PDL Electronics Ltd -00-14-32 (hex) Tarallax Wireless, Inc. -00-14-33 (hex) Empower Technologies(Canada) Inc. -00-14-34 (hex) Keri Systems, Inc -00-14-35 (hex) CityCom Corp. -00-14-36 (hex) Qwerty Elektronik AB -00-14-37 (hex) GSTeletech Co.,Ltd. -00-14-38 (hex) Hewlett Packard -00-14-39 (hex) Blonder Tongue Laboratories, Inc. -00-14-3A (hex) RAYTALK INTERNATIONAL SRL -00-14-3B (hex) Sensovation AG -00-14-3C (hex) Rheinmetall Canada Inc. -00-14-3D (hex) Aevoe Inc. -00-14-3E (hex) AirLink Communications, Inc. -00-14-3F (hex) Hotway Technology Corporation -00-14-40 (hex) ATOMIC Corporation -00-14-41 (hex) Innovation Sound Technology Co., LTD. -00-14-42 (hex) ATTO CORPORATION -00-14-43 (hex) Consultronics Europe Ltd -00-14-44 (hex) Grundfos Electronics -00-14-45 (hex) Telefon-Gradnja d.o.o. -00-14-46 (hex) SuperVision Solutions LLC -00-14-47 (hex) BOAZ Inc. -00-14-48 (hex) Inventec Multimedia & Telecom Corporation -00-14-49 (hex) Sichuan Changhong Electric Ltd. -00-14-4A (hex) Taiwan Thick-Film Ind. Corp. -00-14-4B (hex) Hifn, Inc. -00-14-4C (hex) General Meters Corp. -00-14-4D (hex) Intelligent Systems -00-14-4E (hex) SRISA -00-14-4F (hex) Sun Microsystems, Inc. -00-14-50 (hex) Heim Systems GmbH -00-14-51 (hex) Apple Computer Inc. -00-14-52 (hex) CALCULEX,INC. -00-14-53 (hex) ADVANTECH TECHNOLOGIES CO.,LTD -00-14-54 (hex) Symwave -00-14-55 (hex) Coder Electronics Corporation -00-14-56 (hex) Edge Products -00-14-57 (hex) T-VIPS AS -00-14-58 (hex) HS Automatic ApS -00-14-59 (hex) Moram Co., Ltd. -00-14-5A (hex) Neratec AG -00-14-5B (hex) SeekerNet Inc. -00-14-5C (hex) Intronics B.V. -00-14-5D (hex) WJ Communications, Inc. -00-14-5E (hex) IBM -00-14-5F (hex) ADITEC CO. LTD -00-14-60 (hex) Kyocera Wireless Corp. -00-14-61 (hex) CORONA CORPORATION -00-14-62 (hex) Digiwell Technology, inc -00-14-63 (hex) IDCS N.V. -00-14-64 (hex) Cryptosoft -00-14-65 (hex) Novo Nordisk A/S -00-14-66 (hex) Kleinhenz Elektronik GmbH -00-14-67 (hex) ArrowSpan Inc. -00-14-68 (hex) CelPlan International, Inc. -00-14-69 (hex) Cisco Systems -00-14-6A (hex) Cisco Systems -00-14-6B (hex) Anagran, Inc. -00-14-6C (hex) Netgear Inc. -00-14-6D (hex) RF Technologies -00-14-6E (hex) H. Stoll GmbH & Co. KG -00-14-6F (hex) Kohler Co -00-14-70 (hex) Prokom Software SA -00-14-71 (hex) Eastern Asia Technology Limited -00-14-72 (hex) China Broadband Wireless IP Standard Group -00-14-73 (hex) Bookham Inc -00-14-74 (hex) K40 Electronics -00-14-75 (hex) Wiline Networks, Inc. -00-14-76 (hex) MultiCom Industries Limited -00-14-77 (hex) Nertec Inc. -00-14-78 (hex) ShenZhen TP-LINK Technologies Co., Ltd. -00-14-79 (hex) NEC Magnus Communications,Ltd. -00-14-7A (hex) Eubus GmbH -00-14-7B (hex) Iteris, Inc. -00-14-7C (hex) 3Com Ltd -00-14-7D (hex) Aeon Digital International -00-14-7E (hex) InnerWireless -00-14-7F (hex) Thomson Telecom Belgium -00-14-80 (hex) Hitachi-LG Data Storage Korea, Inc -00-14-81 (hex) Multilink Inc -00-14-82 (hex) GoBackTV, Inc -00-14-83 (hex) eXS Inc. -00-14-84 (hex) Cermate Technologies Inc. -00-14-85 (hex) Giga-Byte -00-14-86 (hex) Echo Digital Audio Corporation -00-14-87 (hex) American Technology Integrators -00-14-88 (hex) Akorri -00-14-89 (hex) B15402100 - JANDEI, S.L. -00-14-8A (hex) Elin Ebg Traction Gmbh -00-14-8B (hex) Globo Electronic GmbH & Co. KG -00-14-8C (hex) Fortress Technologies -00-14-8D (hex) Cubic Defense Simulation Systems -00-14-8E (hex) Tele Power Inc. -00-14-8F (hex) Protronic (Far East) Ltd. -00-14-90 (hex) ASP Corporation -00-14-91 (hex) Daniels Electronics Ltd. -00-14-92 (hex) Liteon, Mobile Media Solution SBU -00-14-93 (hex) Systimax Solutions -00-14-94 (hex) ESU AG -00-14-95 (hex) 2Wire, Inc. -00-14-96 (hex) Phonic Corp. -00-14-97 (hex) ZHIYUAN Eletronics co.,ltd. -00-14-98 (hex) Viking Design Technology -00-14-99 (hex) Helicomm Inc -00-14-9A (hex) Motorola Mobile Devices Business -00-14-9B (hex) Nokota Communications, LLC -00-14-9C (hex) HF Company -00-14-9D (hex) Sound ID Inc. -00-14-9E (hex) UbONE Co., Ltd -00-14-9F (hex) System and Chips, Inc. -00-14-A0 (hex) Accsense, Inc. -00-14-A1 (hex) Synchronous Communication Corp -00-14-A2 (hex) Core Micro Systems Inc. -00-14-A3 (hex) Vitelec BV -00-14-A4 (hex) Hon Hai Precision Ind. Co., Ltd. -00-14-A5 (hex) Gemtek Technology Co., Ltd. -00-14-A6 (hex) Teranetics, Inc. -00-14-A7 (hex) Nokia Danmark A/S -00-14-A8 (hex) Cisco Systems -00-14-A9 (hex) Cisco Systems -00-14-AA (hex) Ashly Audio, Inc. -00-14-AB (hex) Senhai Electronic Technology Co., Ltd. -00-14-AC (hex) Bountiful WiFi -00-14-AD (hex) Gassner Wiege- u. Metechnik GmbH -00-14-AE (hex) Wizlogics Co., Ltd. -00-14-AF (hex) Datasym Inc. -00-14-B0 (hex) Naeil Community -00-14-B1 (hex) Avitec AB -00-14-B2 (hex) mCubelogics Corporation -00-14-B3 (hex) CoreStar International Corp -00-14-B4 (hex) General Dynamics United Kingdom Ltd -00-14-B5 (hex) PHYSIOMETRIX,INC -00-14-B6 (hex) Enswer Technology Inc. -00-14-B7 (hex) AR Infotek Inc. -00-14-B8 (hex) Hill-Rom -00-14-B9 (hex) MSTAR SEMICONDUCTOR -00-14-BA (hex) Carvers SA de CV -00-14-BB (hex) Open Interface North America -00-14-BC (hex) SYNECTIC TELECOM EXPORTS PVT. LTD. -00-14-BD (hex) incNETWORKS, Inc -00-14-BE (hex) Wink communication technology CO.LTD -00-14-BF (hex) Cisco-Linksys LLC -00-14-C0 (hex) Symstream Technology Group Ltd -00-14-C1 (hex) U.S. Robotics Corporation -00-14-C2 (hex) Hewlett Packard -00-14-C3 (hex) Seagate Technology LLC -00-14-C4 (hex) Vitelcom Mobile Technology -00-14-C5 (hex) Alive Technologies Pty Ltd -00-14-C6 (hex) Quixant Ltd -00-14-C7 (hex) Nortel -00-14-C8 (hex) Contemporary Research Corp -00-14-C9 (hex) Brocade Communications Systems, Inc. -00-14-CA (hex) Key Radio Systems Limited -00-14-CB (hex) LifeSync Corporation -00-14-CC (hex) Zetec, Inc. -00-14-CD (hex) DigitalZone Co., Ltd. -00-14-CE (hex) NF CORPORATION -00-14-CF (hex) Nextlink.to A/S -00-14-D0 (hex) BTI Systems Inc. -00-14-D1 (hex) TRENDnet -00-14-D2 (hex) KYUKI CORPORATION -00-14-D3 (hex) SEPSA -00-14-D4 (hex) K Technology Corporation -00-14-D5 (hex) Datang Telecom Technology CO. , LCD,Optical Communication Br -00-14-D6 (hex) Jeongmin Electronics Co.,Ltd. -00-14-D7 (hex) Datastore Technology Corp -00-14-D8 (hex) bio-logic SA -00-14-D9 (hex) IP Fabrics, Inc. -00-14-DA (hex) Huntleigh Healthcare -00-14-DB (hex) Elma Trenew Electronic GmbH -00-14-DC (hex) Communication System Design & Manufacturing (CSDM) -00-14-DD (hex) Covergence Inc. -00-14-DE (hex) Sage Instruments Inc. -00-14-DF (hex) HI-P Tech Corporation -00-14-E0 (hex) LET'S Corporation -00-14-E1 (hex) Data Display AG -00-14-E2 (hex) datacom systems inc. -00-14-E3 (hex) mm-lab GmbH -00-14-E4 (hex) Integral Technologies -00-14-E5 (hex) Alticast -00-14-E6 (hex) AIM Infrarotmodule GmbH -00-14-E7 (hex) Stolinx,. Inc -00-14-E8 (hex) Motorola CHS -00-14-E9 (hex) Nortech International -00-14-EA (hex) S Digm Inc. (Safe Paradigm Inc.) -00-14-EB (hex) AwarePoint Corporation -00-14-EC (hex) Acro Telecom -00-14-ED (hex) Airak, Inc. -00-14-EE (hex) Western Digital Technologies, Inc. -00-14-EF (hex) TZero Technologies, Inc. -00-14-F0 (hex) Business Security OL AB -00-14-F1 (hex) Cisco Systems -00-14-F2 (hex) Cisco Systems -00-14-F3 (hex) ViXS Systems Inc -00-14-F4 (hex) DekTec Digital Video B.V. -00-14-F5 (hex) OSI Security Devices -00-14-F6 (hex) Juniper Networks, Inc. -00-14-F7 (hex) Crevis -00-14-F8 (hex) Scientific Atlanta -00-14-F9 (hex) Vantage Controls -00-14-FA (hex) AsGa S.A. -00-14-FB (hex) Technical Solutions Inc. -00-14-FC (hex) Extandon, Inc. -00-14-FD (hex) Thecus Technology Corp. -00-14-FE (hex) Artech Electronics -00-14-FF (hex) Precise Automation, Inc. -00-15-00 (hex) Intel Corporate -00-15-01 (hex) LexBox -00-15-02 (hex) BETA tech -00-15-03 (hex) PROFIcomms s.r.o. -00-15-04 (hex) GAME PLUS CO., LTD. -00-15-05 (hex) Actiontec Electronics, Inc -00-15-06 (hex) Neo Photonics -00-15-07 (hex) Renaissance Learning Inc -00-15-08 (hex) Global Target Enterprise Inc -00-15-09 (hex) Plus Technology Co., Ltd -00-15-0A (hex) Sonoa Systems, Inc -00-15-0B (hex) SAGE INFOTECH LTD. -00-15-0C (hex) AVM GmbH -00-15-0D (hex) Hoana Medical, Inc. -00-15-0E (hex) OPENBRAIN TECHNOLOGIES CO., LTD. -00-15-0F (hex) mingjong -00-15-10 (hex) Techsphere Co., Ltd -00-15-11 (hex) Data Center Systems -00-15-12 (hex) Zurich University of Applied Sciences -00-15-13 (hex) EFS sas -00-15-14 (hex) Hu Zhou NAVA Networks&Electronics Ltd. -00-15-15 (hex) Leipold+Co.GmbH -00-15-16 (hex) URIEL SYSTEMS INC. -00-15-17 (hex) Intel Corporate -00-15-18 (hex) Shenzhen 10MOONS Technology Development CO.,Ltd -00-15-19 (hex) StoreAge Networking Technologies -00-15-1A (hex) Hunter Engineering Company -00-15-1B (hex) Isilon Systems Inc. -00-15-1C (hex) LENECO -00-15-1D (hex) M2I CORPORATION -00-15-1E (hex) Ethernet Powerlink Standardization Group (EPSG) -00-15-1F (hex) Multivision Intelligent Surveillance (Hong Kong) Ltd -00-15-20 (hex) Radiocrafts AS -00-15-21 (hex) Horoquartz -00-15-22 (hex) Dea Security -00-15-23 (hex) Meteor Communications Corporation -00-15-24 (hex) Numatics, Inc. -00-15-25 (hex) Chamberlain Access Solutions -00-15-26 (hex) Remote Technologies Inc -00-15-27 (hex) Balboa Instruments -00-15-28 (hex) Beacon Medical Products LLC d.b.a. BeaconMedaes -00-15-29 (hex) N3 Corporation -00-15-2A (hex) Nokia GmbH -00-15-2B (hex) Cisco Systems -00-15-2C (hex) Cisco Systems -00-15-2D (hex) TenX Networks, LLC -00-15-2E (hex) PacketHop, Inc. -00-15-2F (hex) Motorola CHS -00-15-30 (hex) Bus-Tech, Inc. -00-15-31 (hex) KOCOM -00-15-32 (hex) Consumer Technologies Group, LLC -00-15-33 (hex) NADAM.CO.,LTD -00-15-34 (hex) A BELTRNICA, Companhia de Comunicaes, Lda -00-15-35 (hex) OTE Spa -00-15-36 (hex) Powertech co.,Ltd -00-15-37 (hex) Ventus Networks -00-15-38 (hex) RFID, Inc. -00-15-39 (hex) Technodrive SRL -00-15-3A (hex) Shenzhen Syscan Technology Co.,Ltd. -00-15-3B (hex) EMH Elektrizittszhler GmbH & CoKG -00-15-3C (hex) Kprotech Co., Ltd. -00-15-3D (hex) ELIM PRODUCT CO. -00-15-3E (hex) Q-Matic Sweden AB -00-15-3F (hex) Alcatel Alenia Space Italia -00-15-40 (hex) Nortel -00-15-41 (hex) StrataLight Communications, Inc. -00-15-42 (hex) MICROHARD S.R.L. -00-15-43 (hex) Aberdeen Test Center -00-15-44 (hex) coM.s.a.t. AG -00-15-45 (hex) SEECODE Co., Ltd. -00-15-46 (hex) ITG Worldwide Sdn Bhd -00-15-47 (hex) AiZen Solutions Inc. -00-15-48 (hex) CUBE TECHNOLOGIES -00-15-49 (hex) Dixtal Biomedica Ind. Com. Ltda -00-15-4A (hex) WANSHIH ELECTRONIC CO., LTD -00-15-4B (hex) Wonde Proud Technology Co., Ltd -00-15-4C (hex) Saunders Electronics -00-15-4D (hex) Netronome Systems, Inc. -00-15-4E (hex) IEC -00-15-4F (hex) one RF Technology -00-15-50 (hex) Nits Technology Inc -00-15-51 (hex) RadioPulse Inc. -00-15-52 (hex) Wi-Gear Inc. -00-15-53 (hex) Cytyc Corporation -00-15-54 (hex) Atalum Wireless S.A. -00-15-55 (hex) DFM GmbH -00-15-56 (hex) SAGEM COMMUNICATION -00-15-57 (hex) Olivetti -00-15-58 (hex) FOXCONN -00-15-59 (hex) Securaplane Technologies, Inc. -00-15-5A (hex) DAINIPPON PHARMACEUTICAL CO., LTD. -00-15-5B (hex) Sampo Corporation -00-15-5C (hex) Dresser Wayne -00-15-5D (hex) Microsoft Corporation -00-15-5E (hex) Morgan Stanley -00-15-5F (hex) GreenPeak Technologies -00-15-60 (hex) Hewlett Packard -00-15-61 (hex) JJPlus Corporation -00-15-62 (hex) Cisco Systems -00-15-63 (hex) Cisco Systems -00-15-64 (hex) BEHRINGER Spezielle Studiotechnik GmbH -00-15-65 (hex) XIAMEN YEALINK NETWORK TECHNOLOGY CO.,LTD -00-15-66 (hex) A-First Technology Co., Ltd. -00-15-67 (hex) RADWIN Inc. -00-15-68 (hex) Dilithium Networks -00-15-69 (hex) PECO II, Inc. -00-15-6A (hex) DG2L Technologies Pvt. Ltd. -00-15-6B (hex) Perfisans Networks Corp. -00-15-6C (hex) SANE SYSTEM CO., LTD -00-15-6D (hex) Ubiquiti Networks Inc. -00-15-6E (hex) A. W. Communication Systems Ltd -00-15-6F (hex) Xiranet Communications GmbH -00-15-70 (hex) Symbol TechnologiesWholly owned Subsidiary of Motorola -00-15-71 (hex) Nolan Systems -00-15-72 (hex) Red-Lemon -00-15-73 (hex) NewSoft Technology Corporation -00-15-74 (hex) Horizon Semiconductors Ltd. -00-15-75 (hex) Nevis Networks Inc. -00-15-76 (hex) scil animal care company GmbH -00-15-77 (hex) Allied Telesis -00-15-78 (hex) Audio / Video Innovations -00-15-79 (hex) Lunatone Industrielle Elektronik GmbH -00-15-7A (hex) Telefin S.p.A. -00-15-7B (hex) Leuze electronic GmbH + Co. KG -00-15-7C (hex) Dave Networks, Inc. -00-15-7D (hex) POSDATA CO., LTD. -00-15-7E (hex) HEYFRA ELECTRONIC gmbH -00-15-7F (hex) ChuanG International Holding CO.,LTD. -00-15-80 (hex) U-WAY CORPORATION -00-15-81 (hex) MAKUS Inc. -00-15-82 (hex) TVonics Ltd -00-15-83 (hex) IVT corporation -00-15-84 (hex) Schenck Process GmbH -00-15-85 (hex) Aonvision Technolopy Corp. -00-15-86 (hex) Xiamen Overseas Chinese Electronic Co., Ltd. -00-15-87 (hex) Takenaka Seisakusho Co.,Ltd -00-15-88 (hex) Balda-Thong Fook Solutions Sdn. Bhd. -00-15-89 (hex) D-MAX Technology Co.,Ltd -00-15-8A (hex) SURECOM Technology Corp. -00-15-8B (hex) Park Air Systems Ltd -00-15-8C (hex) Liab ApS -00-15-8D (hex) Jennic Ltd -00-15-8E (hex) Plustek.INC -00-15-8F (hex) NTT Advanced Technology Corporation -00-15-90 (hex) Hectronic GmbH -00-15-91 (hex) RLW Inc. -00-15-92 (hex) Facom UK Ltd (Melksham) -00-15-93 (hex) U4EA Technologies Inc. -00-15-94 (hex) BIXOLON CO.,LTD -00-15-95 (hex) Quester Tangent Corporation -00-15-96 (hex) ARRIS International -00-15-97 (hex) AETA AUDIO SYSTEMS -00-15-98 (hex) Kolektor group -00-15-99 (hex) Samsung Electronics Co., LTD -00-15-9A (hex) Motorola CHS -00-15-9B (hex) Nortel -00-15-9C (hex) B-KYUNG SYSTEM Co.,Ltd. -00-15-9D (hex) Minicom Advanced Systems ltd -00-15-9E (hex) Mad Catz Interactive Inc -00-15-9F (hex) Terascala, Inc. -00-15-A0 (hex) Nokia Danmark A/S -00-15-A1 (hex) ECA-SINTERS -00-15-A2 (hex) ARRIS International -00-15-A3 (hex) ARRIS International -00-15-A4 (hex) ARRIS International -00-15-A5 (hex) DCI Co., Ltd. -00-15-A6 (hex) Digital Electronics Products Ltd. -00-15-A7 (hex) Robatech AG -00-15-A8 (hex) Motorola Mobile Devices -00-15-A9 (hex) KWANG WOO I&C CO.,LTD -00-15-AA (hex) Rextechnik International Co., -00-15-AB (hex) PRO CO SOUND INC -00-15-AC (hex) Capelon AB -00-15-AD (hex) Accedian Networks -00-15-AE (hex) kyung il -00-15-AF (hex) AzureWave Technologies, Inc. -00-15-B0 (hex) AUTOTELENET CO.,LTD -00-15-B1 (hex) Ambient Corporation -00-15-B2 (hex) Advanced Industrial Computer, Inc. -00-15-B3 (hex) Caretech AB -00-15-B4 (hex) Polymap Wireless LLC -00-15-B5 (hex) CI Network Corp. -00-15-B6 (hex) ShinMaywa Industries, Ltd. -00-15-B7 (hex) Toshiba -00-15-B8 (hex) Tahoe -00-15-B9 (hex) Samsung Electronics Co., Ltd. -00-15-BA (hex) iba AG -00-15-BB (hex) SMA Solar Technology AG -00-15-BC (hex) Develco -00-15-BD (hex) Group 4 Technology Ltd -00-15-BE (hex) Iqua Ltd. -00-15-BF (hex) technicob -00-15-C0 (hex) DIGITAL TELEMEDIA CO.,LTD. -00-15-C1 (hex) SONY Computer Entertainment inc, -00-15-C2 (hex) 3M Germany -00-15-C3 (hex) Ruf Telematik AG -00-15-C4 (hex) FLOVEL CO., LTD. -00-15-C5 (hex) Dell Inc -00-15-C6 (hex) Cisco Systems -00-15-C7 (hex) Cisco Systems -00-15-C8 (hex) FlexiPanel Ltd -00-15-C9 (hex) Gumstix, Inc -00-15-CA (hex) TeraRecon, Inc. -00-15-CB (hex) Surf Communication Solutions Ltd. -00-15-CC (hex) TEPCO UQUEST, LTD. -00-15-CD (hex) Exartech International Corp. -00-15-CE (hex) ARRIS International -00-15-CF (hex) ARRIS International -00-15-D0 (hex) ARRIS International -00-15-D1 (hex) ARRIS Group, Inc. -00-15-D2 (hex) Xantech Corporation -00-15-D3 (hex) Pantech&Curitel Communications, Inc. -00-15-D4 (hex) Emitor AB -00-15-D5 (hex) NICEVT -00-15-D6 (hex) OSLiNK Sp. z o.o. -00-15-D7 (hex) Reti Corporation -00-15-D8 (hex) Interlink Electronics -00-15-D9 (hex) PKC Electronics Oy -00-15-DA (hex) IRITEL A.D. -00-15-DB (hex) Canesta Inc. -00-15-DC (hex) KT&C Co., Ltd. -00-15-DD (hex) IP Control Systems Ltd. -00-15-DE (hex) Nokia Danmark A/S -00-15-DF (hex) Clivet S.p.A. -00-15-E0 (hex) ST-Ericsson -00-15-E1 (hex) picoChip Designs Ltd -00-15-E2 (hex) Dr.Ing. Herbert Knauer GmbH -00-15-E3 (hex) Dream Technologies Corporation -00-15-E4 (hex) Zimmer Elektromedizin -00-15-E5 (hex) Cheertek Inc. -00-15-E6 (hex) MOBILE TECHNIKA Inc. -00-15-E7 (hex) Quantec ProAudio -00-15-E8 (hex) Nortel -00-15-E9 (hex) D-Link Corporation -00-15-EA (hex) Tellumat (Pty) Ltd -00-15-EB (hex) ZTE CORPORATION -00-15-EC (hex) Boca Devices LLC -00-15-ED (hex) Fulcrum Microsystems, Inc. -00-15-EE (hex) Omnex Control Systems -00-15-EF (hex) NEC TOKIN Corporation -00-15-F0 (hex) EGO BV -00-15-F1 (hex) KYLINK Communications Corp. -00-15-F2 (hex) ASUSTek COMPUTER INC. -00-15-F3 (hex) PELTOR AB -00-15-F4 (hex) Eventide -00-15-F5 (hex) Sustainable Energy Systems -00-15-F6 (hex) SCIENCE AND ENGINEERING SERVICES, INC. -00-15-F7 (hex) Wintecronics Ltd. -00-15-F8 (hex) Kingtronics Industrial Co. Ltd. -00-15-F9 (hex) Cisco Systems -00-15-FA (hex) Cisco Systems -00-15-FB (hex) setex schermuly textile computer gmbh -00-15-FC (hex) Littelfuse Startco -00-15-FD (hex) Complete Media Systems -00-15-FE (hex) SCHILLING ROBOTICS LLC -00-15-FF (hex) Novatel Wireless, Inc. -00-16-00 (hex) CelleBrite Mobile Synchronization -00-16-01 (hex) Buffalo Inc. -00-16-02 (hex) CEYON TECHNOLOGY CO.,LTD. -00-16-03 (hex) COOLKSKY Co., LTD -00-16-04 (hex) Sigpro -00-16-05 (hex) YORKVILLE SOUND INC. -00-16-06 (hex) Ideal Industries -00-16-07 (hex) Curves International Inc. -00-16-08 (hex) Sequans Communications -00-16-09 (hex) Unitech electronics co., ltd. -00-16-0A (hex) SWEEX Europe BV -00-16-0B (hex) TVWorks LLC -00-16-0C (hex) LPL DEVELOPMENT S.A. DE C.V -00-16-0D (hex) Be Here Corporation -00-16-0E (hex) Optica Technologies Inc. -00-16-0F (hex) BADGER METER INC -00-16-10 (hex) Carina Technology -00-16-11 (hex) Altecon Srl -00-16-12 (hex) Otsuka Electronics Co., Ltd. -00-16-13 (hex) LibreStream Technologies Inc. -00-16-14 (hex) Picosecond Pulse Labs -00-16-15 (hex) Nittan Company, Limited -00-16-16 (hex) BROWAN COMMUNICATION INC. -00-16-17 (hex) MSI -00-16-18 (hex) HIVION Co., Ltd. -00-16-19 (hex) La Factora de Comunicaciones Aplicadas,S.L. -00-16-1A (hex) Dametric AB -00-16-1B (hex) Micronet Corporation -00-16-1C (hex) e:cue -00-16-1D (hex) Innovative Wireless Technologies, Inc. -00-16-1E (hex) Woojinnet -00-16-1F (hex) SUNWAVETEC Co., Ltd. -00-16-20 (hex) Sony Ericsson Mobile Communications AB -00-16-21 (hex) Colorado Vnet -00-16-22 (hex) BBH SYSTEMS GMBH -00-16-23 (hex) Interval Media -00-16-24 (hex) Teneros, Inc. -00-16-25 (hex) Impinj, Inc. -00-16-26 (hex) Motorola CHS -00-16-27 (hex) embedded-logic DESIGN AND MORE GmbH -00-16-28 (hex) Ultra Electronics Manufacturing and Card Systems -00-16-29 (hex) Nivus GmbH -00-16-2A (hex) Antik computers & communications s.r.o. -00-16-2B (hex) Togami Electric Mfg.co.,Ltd. -00-16-2C (hex) Xanboo -00-16-2D (hex) STNet Co., Ltd. -00-16-2E (hex) Space Shuttle Hi-Tech Co., Ltd. -00-16-2F (hex) Geutebrck GmbH -00-16-30 (hex) Vativ Technologies -00-16-31 (hex) Xteam -00-16-32 (hex) SAMSUNG ELECTRONICS CO., LTD. -00-16-33 (hex) Oxford Diagnostics Ltd. -00-16-34 (hex) Mathtech, Inc. -00-16-35 (hex) Hewlett Packard -00-16-36 (hex) Quanta Computer Inc. -00-16-37 (hex) Citel Srl -00-16-38 (hex) TECOM Co., Ltd. -00-16-39 (hex) UBIQUAM Co.,Ltd -00-16-3A (hex) YVES TECHNOLOGY CO., LTD. -00-16-3B (hex) VertexRSI/General Dynamics -00-16-3C (hex) Rebox B.V. -00-16-3D (hex) Tsinghua Tongfang Legend Silicon Tech. Co., Ltd. -00-16-3E (hex) Xensource, Inc. -00-16-3F (hex) CReTE SYSTEMS Inc. -00-16-40 (hex) Asmobile Communication Inc. -00-16-41 (hex) USI -00-16-42 (hex) Pangolin -00-16-43 (hex) Sunhillo Corporation -00-16-44 (hex) LITE-ON Technology Corp. -00-16-45 (hex) Power Distribution, Inc. -00-16-46 (hex) Cisco Systems -00-16-47 (hex) Cisco Systems -00-16-48 (hex) SSD Company Limited -00-16-49 (hex) SetOne GmbH -00-16-4A (hex) Vibration Technology Limited -00-16-4B (hex) Quorion Data Systems GmbH -00-16-4C (hex) PLANET INT Co., Ltd -00-16-4D (hex) Alcatel North America IP Division -00-16-4E (hex) Nokia Danmark A/S -00-16-4F (hex) World Ethnic Broadcastin Inc. -00-16-50 (hex) Herley General Microwave Israel. -00-16-51 (hex) Exeo Systems -00-16-52 (hex) Hoatech Technologies, Inc. -00-16-53 (hex) LEGO System A/S IE Electronics Division -00-16-54 (hex) Flex-P Industries Sdn. Bhd. -00-16-55 (hex) FUHO TECHNOLOGY Co., LTD -00-16-56 (hex) Nintendo Co., Ltd. -00-16-57 (hex) Aegate Ltd -00-16-58 (hex) Fusiontech Technologies Inc. -00-16-59 (hex) Z.M.P. RADWAG -00-16-5A (hex) Harman Specialty Group -00-16-5B (hex) Grip Audio -00-16-5C (hex) Trackflow Ltd -00-16-5D (hex) AirDefense, Inc. -00-16-5E (hex) Precision I/O -00-16-5F (hex) Fairmount Automation -00-16-60 (hex) Nortel -00-16-61 (hex) Novatium Solutions (P) Ltd -00-16-62 (hex) Liyuh Technology Ltd. -00-16-63 (hex) KBT Mobile -00-16-64 (hex) Prod-El SpA -00-16-65 (hex) Cellon France -00-16-66 (hex) Quantier Communication Inc. -00-16-67 (hex) A-TEC Subsystem INC. -00-16-68 (hex) Eishin Electronics -00-16-69 (hex) MRV Communication (Networks) LTD -00-16-6A (hex) TPS -00-16-6B (hex) Samsung Electronics -00-16-6C (hex) Samsung Electonics Digital Video System Division -00-16-6D (hex) Yulong Computer Telecommunication Scientific(shenzhen)Co.,Lt -00-16-6E (hex) Arbitron Inc. -00-16-6F (hex) Intel Corporation -00-16-70 (hex) SKNET Corporation -00-16-71 (hex) Symphox Information Co. -00-16-72 (hex) Zenway enterprise ltd -00-16-73 (hex) Bury GmbH & Co. KG -00-16-74 (hex) EuroCB (Phils.), Inc. -00-16-75 (hex) Motorola MDb -00-16-76 (hex) Intel Corporation -00-16-77 (hex) Bihl+Wiedemann GmbH -00-16-78 (hex) SHENZHEN BAOAN GAOKE ELECTRONICS CO., LTD -00-16-79 (hex) eOn Communications -00-16-7A (hex) Skyworth Overseas Dvelopment Ltd. -00-16-7B (hex) Haver&Boecker -00-16-7C (hex) iRex Technologies BV -00-16-7D (hex) Sky-Line Information Co., Ltd. -00-16-7E (hex) DIBOSS.CO.,LTD -00-16-7F (hex) Bluebird Soft Inc. -00-16-80 (hex) Bally Gaming + Systems -00-16-81 (hex) Vector Informatik GmbH -00-16-82 (hex) Pro Dex, Inc -00-16-83 (hex) WEBIO International Co.,.Ltd. -00-16-84 (hex) Donjin Co.,Ltd. -00-16-85 (hex) Elisa Oyj -00-16-86 (hex) Karl Storz Imaging -00-16-87 (hex) Chubb CSC-Vendor AP -00-16-88 (hex) ServerEngines LLC -00-16-89 (hex) Pilkor Electronics Co., Ltd -00-16-8A (hex) id-Confirm Inc -00-16-8B (hex) Paralan Corporation -00-16-8C (hex) DSL Partner AS -00-16-8D (hex) KORWIN CO., Ltd. -00-16-8E (hex) Vimicro corporation -00-16-8F (hex) GN Netcom as -00-16-90 (hex) J-TEK INCORPORATION -00-16-91 (hex) Moser-Baer AG -00-16-92 (hex) Scientific-Atlanta, Inc. -00-16-93 (hex) PowerLink Technology Inc. -00-16-94 (hex) Sennheiser Communications A/S -00-16-95 (hex) AVC Technology Limited -00-16-96 (hex) QDI Technology (H.K.) Limited -00-16-97 (hex) NEC Corporation -00-16-98 (hex) T&A Mobile Phones -00-16-99 (hex) Tonic DVB Marketing Ltd -00-16-9A (hex) Quadrics Ltd -00-16-9B (hex) Alstom Transport -00-16-9C (hex) Cisco Systems -00-16-9D (hex) Cisco Systems -00-16-9E (hex) TV One Ltd -00-16-9F (hex) Vimtron Electronics Co., Ltd. -00-16-A0 (hex) Auto-Maskin -00-16-A1 (hex) 3Leaf Networks -00-16-A2 (hex) CentraLite Systems, Inc. -00-16-A3 (hex) Ingeteam Transmission&Distribution, S.A. -00-16-A4 (hex) Ezurio Ltd -00-16-A5 (hex) Tandberg Storage ASA -00-16-A6 (hex) Dovado FZ-LLC -00-16-A7 (hex) AWETA G&P -00-16-A8 (hex) CWT CO., LTD. -00-16-A9 (hex) 2EI -00-16-AA (hex) Kei Communication Technology Inc. -00-16-AB (hex) PBI-Dansensor A/S -00-16-AC (hex) Toho Technology Corp. -00-16-AD (hex) BT-Links Company Limited -00-16-AE (hex) INVENTEL -00-16-AF (hex) Shenzhen Union Networks Equipment Co.,Ltd. -00-16-B0 (hex) VK Corporation -00-16-B1 (hex) KBS -00-16-B2 (hex) DriveCam Inc -00-16-B3 (hex) Photonicbridges (China) Co., Ltd. -00-16-B4 (hex) PRIVATE -00-16-B5 (hex) Motorola CHS -00-16-B6 (hex) Cisco-Linksys -00-16-B7 (hex) Seoul Commtech -00-16-B8 (hex) Sony Ericsson Mobile Communications -00-16-B9 (hex) ProCurve Networking -00-16-BA (hex) WEATHERNEWS INC. -00-16-BB (hex) Law-Chain Computer Technology Co Ltd -00-16-BC (hex) Nokia Danmark A/S -00-16-BD (hex) ATI Industrial Automation -00-16-BE (hex) INFRANET, Inc. -00-16-BF (hex) PaloDEx Group Oy -00-16-C0 (hex) Semtech Corporation -00-16-C1 (hex) Eleksen Ltd -00-16-C2 (hex) Avtec Systems Inc -00-16-C3 (hex) BA Systems Inc -00-16-C4 (hex) SiRF Technology, Inc. -00-16-C5 (hex) Shenzhen Xing Feng Industry Co.,Ltd -00-16-C6 (hex) North Atlantic Industries -00-16-C7 (hex) Cisco Systems -00-16-C8 (hex) Cisco Systems -00-16-C9 (hex) NAT Seattle, Inc. -00-16-CA (hex) Nortel -00-16-CB (hex) Apple Computer -00-16-CC (hex) Xcute Mobile Corp. -00-16-CD (hex) HIJI HIGH-TECH CO., LTD. -00-16-CE (hex) Hon Hai Precision Ind. Co., Ltd. -00-16-CF (hex) Hon Hai Precision Ind. Co., Ltd. -00-16-D0 (hex) ATech elektronika d.o.o. -00-16-D1 (hex) ZAT a.s. -00-16-D2 (hex) Caspian -00-16-D3 (hex) Wistron Corporation -00-16-D4 (hex) Compal Communications, Inc. -00-16-D5 (hex) Synccom Co., Ltd -00-16-D6 (hex) TDA Tech Pty Ltd -00-16-D7 (hex) Sunways AG -00-16-D8 (hex) Senea AB -00-16-D9 (hex) NINGBO BIRD CO.,LTD. -00-16-DA (hex) Futronic Technology Co. Ltd. -00-16-DB (hex) Samsung Electronics Co., Ltd. -00-16-DC (hex) ARCHOS -00-16-DD (hex) Gigabeam Corporation -00-16-DE (hex) FAST Inc -00-16-DF (hex) Lundinova AB -00-16-E0 (hex) 3Com Ltd -00-16-E1 (hex) SiliconStor, Inc. -00-16-E2 (hex) American Fibertek, Inc. -00-16-E3 (hex) ASKEY COMPUTER CORP. -00-16-E4 (hex) VANGUARD SECURITY ENGINEERING CORP. -00-16-E5 (hex) FORDLEY DEVELOPMENT LIMITED -00-16-E6 (hex) GIGA-BYTE TECHNOLOGY CO.,LTD. -00-16-E7 (hex) Dynamix Promotions Limited -00-16-E8 (hex) Sigma Designs, Inc. -00-16-E9 (hex) Tiba Medical Inc -00-16-EA (hex) Intel Corporation -00-16-EB (hex) Intel Corporation -00-16-EC (hex) Elitegroup Computer Systems Co., Ltd. -00-16-ED (hex) Digital Safety Technologies, Inc -00-16-EE (hex) RoyalDigital Inc. -00-16-EF (hex) Koko Fitness, Inc. -00-16-F0 (hex) Dell -00-16-F1 (hex) OmniSense, LLC -00-16-F2 (hex) Dmobile System Co., Ltd. -00-16-F3 (hex) CAST Information Co., Ltd -00-16-F4 (hex) Eidicom Co., Ltd. -00-16-F5 (hex) Dalian Golden Hualu Digital Technology Co.,Ltd -00-16-F6 (hex) Video Products Group -00-16-F7 (hex) L-3 Communications, Electrodynamics, Inc. -00-16-F8 (hex) AVIQTECH TECHNOLOGY CO., LTD. -00-16-F9 (hex) CETRTA POT, d.o.o., Kranj -00-16-FA (hex) ECI Telecom Ltd. -00-16-FB (hex) SHENZHEN MTC CO.,LTD. -00-16-FC (hex) TOHKEN CO.,LTD. -00-16-FD (hex) Jaty Electronics -00-16-FE (hex) Alps Electric Co., Ltd -00-16-FF (hex) Wamin Optocomm Mfg Corp -00-17-00 (hex) Motorola MDb -00-17-01 (hex) KDE, Inc. -00-17-02 (hex) Osung Midicom Co., Ltd -00-17-03 (hex) MOSDAN Internation Co.,Ltd -00-17-04 (hex) Shinco Electronics Group Co.,Ltd -00-17-05 (hex) Methode Electronics -00-17-06 (hex) Techfaith Wireless Communication Technology Limited. -00-17-07 (hex) InGrid, Inc -00-17-08 (hex) Hewlett Packard -00-17-09 (hex) Exalt Communications -00-17-0A (hex) INEW DIGITAL COMPANY -00-17-0B (hex) Contela, Inc. -00-17-0C (hex) GeoSentric OYj -00-17-0D (hex) Dust Networks Inc. -00-17-0E (hex) Cisco Systems -00-17-0F (hex) Cisco Systems -00-17-10 (hex) Casa Systems Inc. -00-17-11 (hex) GE Healthcare Bio-Sciences AB -00-17-12 (hex) ISCO International -00-17-13 (hex) Tiger NetCom -00-17-14 (hex) BR Controls Nederland bv -00-17-15 (hex) Qstik -00-17-16 (hex) Qno Technology Inc. -00-17-17 (hex) Leica Geosystems AG -00-17-18 (hex) Vansco Electronics Oy -00-17-19 (hex) AudioCodes USA, Inc -00-17-1A (hex) Winegard Company -00-17-1B (hex) Innovation Lab Corp. -00-17-1C (hex) NT MicroSystems, Inc. -00-17-1D (hex) DIGIT -00-17-1E (hex) Theo Benning GmbH & Co. KG -00-17-1F (hex) IMV Corporation -00-17-20 (hex) Image Sensing Systems, Inc. -00-17-21 (hex) FITRE S.p.A. -00-17-22 (hex) Hanazeder Electronic GmbH -00-17-23 (hex) Summit Data Communications -00-17-24 (hex) Studer Professional Audio GmbH -00-17-25 (hex) Liquid Computing -00-17-26 (hex) m2c Electronic Technology Ltd. -00-17-27 (hex) Thermo Ramsey Italia s.r.l. -00-17-28 (hex) Selex Communications -00-17-29 (hex) Ubicod Co.LTD -00-17-2A (hex) Proware Technology Corp. -00-17-2B (hex) Global Technologies Inc. -00-17-2C (hex) TAEJIN INFOTECH -00-17-2D (hex) Axcen Photonics Corporation -00-17-2E (hex) FXC Inc. -00-17-2F (hex) NeuLion Incorporated -00-17-30 (hex) Automation Electronics -00-17-31 (hex) ASUSTek COMPUTER INC. -00-17-32 (hex) Science-Technical Center "RISSA" -00-17-33 (hex) SFR -00-17-34 (hex) ADC Telecommunications -00-17-35 (hex) PRIVATE -00-17-36 (hex) iiTron Inc. -00-17-37 (hex) Industrie Dial Face S.p.A. -00-17-38 (hex) International Business Machines -00-17-39 (hex) Bright Headphone Electronics Company -00-17-3A (hex) Reach Systems Inc. -00-17-3B (hex) Arched Rock Corporation -00-17-3C (hex) Extreme Engineering Solutions -00-17-3D (hex) Neology -00-17-3E (hex) LeucotronEquipamentos Ltda. -00-17-3F (hex) Belkin Corporation -00-17-40 (hex) Technologies Labtronix -00-17-41 (hex) DEFIDEV -00-17-42 (hex) FUJITSU LIMITED -00-17-43 (hex) Deck Srl -00-17-44 (hex) Araneo Ltd. -00-17-45 (hex) INNOTZ CO., Ltd -00-17-46 (hex) Freedom9 Inc. -00-17-47 (hex) Trimble -00-17-48 (hex) Neokoros Brasil Ltda -00-17-49 (hex) HYUNDAE YONG-O-SA CO.,LTD -00-17-4A (hex) SOCOMEC -00-17-4B (hex) Nokia Danmark A/S -00-17-4C (hex) Millipore -00-17-4D (hex) DYNAMIC NETWORK FACTORY, INC. -00-17-4E (hex) Parama-tech Co.,Ltd. -00-17-4F (hex) iCatch Inc. -00-17-50 (hex) GSI Group, MicroE Systems -00-17-51 (hex) Online Corporation -00-17-52 (hex) DAGS, Inc -00-17-53 (hex) nFore Technology Inc. -00-17-54 (hex) Arkino Corporation., Ltd -00-17-55 (hex) GE Security -00-17-56 (hex) Vinci Labs Oy -00-17-57 (hex) RIX TECHNOLOGY LIMITED -00-17-58 (hex) ThruVision Ltd -00-17-59 (hex) Cisco Systems -00-17-5A (hex) Cisco Systems -00-17-5B (hex) ACS Solutions Switzerland Ltd. -00-17-5C (hex) SHARP CORPORATION -00-17-5D (hex) Dongseo system. -00-17-5E (hex) Zed-3 -00-17-5F (hex) XENOLINK Communications Co., Ltd. -00-17-60 (hex) Naito Densei Machida MFG.CO.,LTD -00-17-61 (hex) ZKSoftware Inc. -00-17-62 (hex) Solar Technology, Inc. -00-17-63 (hex) Essentia S.p.A. -00-17-64 (hex) ATMedia GmbH -00-17-65 (hex) Nortel -00-17-66 (hex) Accense Technology, Inc. -00-17-67 (hex) Earforce AS -00-17-68 (hex) Zinwave Ltd -00-17-69 (hex) Cymphonix Corp -00-17-6A (hex) Avago Technologies -00-17-6B (hex) Kiyon, Inc. -00-17-6C (hex) Pivot3, Inc. -00-17-6D (hex) CORE CORPORATION -00-17-6E (hex) DUCATI SISTEMI -00-17-6F (hex) PAX Computer Technology(Shenzhen) Ltd. -00-17-70 (hex) Arti Industrial Electronics Ltd. -00-17-71 (hex) APD Communications Ltd -00-17-72 (hex) ASTRO Strobel Kommunikationssysteme GmbH -00-17-73 (hex) Laketune Technologies Co. Ltd -00-17-74 (hex) Elesta GmbH -00-17-75 (hex) TTE Germany GmbH -00-17-76 (hex) Meso Scale Diagnostics, LLC -00-17-77 (hex) Obsidian Research Corporation -00-17-78 (hex) Central Music Co. -00-17-79 (hex) QuickTel -00-17-7A (hex) ASSA ABLOY AB -00-17-7B (hex) Azalea Networks inc -00-17-7C (hex) Smartlink Network Systems Limited -00-17-7D (hex) IDT International Limited -00-17-7E (hex) Meshcom Technologies Inc. -00-17-7F (hex) Worldsmart Retech -00-17-80 (hex) Applera Holding B.V. Singapore Operations -00-17-81 (hex) Greystone Data System, Inc. -00-17-82 (hex) LoBenn Inc. -00-17-83 (hex) Texas Instruments -00-17-84 (hex) Motorola Mobile Devices -00-17-85 (hex) Sparr Electronics Ltd -00-17-86 (hex) wisembed -00-17-87 (hex) Brother, Brother & Sons ApS -00-17-88 (hex) Philips Lighting BV -00-17-89 (hex) Zenitron Corporation -00-17-8A (hex) DARTS TECHNOLOGIES CORP. -00-17-8B (hex) Teledyne Technologies Incorporated -00-17-8C (hex) Independent Witness, Inc -00-17-8D (hex) Checkpoint Systems, Inc. -00-17-8E (hex) Gunnebo Cash Automation AB -00-17-8F (hex) NINGBO YIDONG ELECTRONIC CO.,LTD. -00-17-90 (hex) HYUNDAI DIGITECH Co, Ltd. -00-17-91 (hex) LinTech GmbH -00-17-92 (hex) Falcom Wireless Comunications Gmbh -00-17-93 (hex) Tigi Corporation -00-17-94 (hex) Cisco Systems -00-17-95 (hex) Cisco Systems -00-17-96 (hex) Rittmeyer AG -00-17-97 (hex) Telsy Elettronica S.p.A. -00-17-98 (hex) Azonic Technology Co., LTD -00-17-99 (hex) SmarTire Systems Inc. -00-17-9A (hex) D-Link Corporation -00-17-9B (hex) Chant Sincere CO., LTD. -00-17-9C (hex) DEPRAG SCHULZ GMBH u. CO. -00-17-9D (hex) Kelman Limited -00-17-9E (hex) Sirit Inc -00-17-9F (hex) Apricorn -00-17-A0 (hex) RoboTech srl -00-17-A1 (hex) 3soft inc. -00-17-A2 (hex) Camrivox Ltd. -00-17-A3 (hex) MIX s.r.l. -00-17-A4 (hex) Hewlett Packard -00-17-A5 (hex) TrendChip Technologies Corp. -00-17-A6 (hex) YOSIN ELECTRONICS CO., LTD. -00-17-A7 (hex) Mobile Computing Promotion Consortium -00-17-A8 (hex) EDM Corporation -00-17-A9 (hex) Sentivision -00-17-AA (hex) elab-experience inc. -00-17-AB (hex) Nintendo Co., Ltd. -00-17-AC (hex) O'Neil Product Development Inc. -00-17-AD (hex) AceNet Corporation -00-17-AE (hex) GAI-Tronics -00-17-AF (hex) Enermet -00-17-B0 (hex) Nokia Danmark A/S -00-17-B1 (hex) ACIST Medical Systems, Inc. -00-17-B2 (hex) SK Telesys -00-17-B3 (hex) Aftek Infosys Limited -00-17-B4 (hex) Remote Security Systems, LLC -00-17-B5 (hex) Peerless Systems Corporation -00-17-B6 (hex) Aquantia -00-17-B7 (hex) Tonze Technology Co. -00-17-B8 (hex) NOVATRON CO., LTD. -00-17-B9 (hex) Gambro Lundia AB -00-17-BA (hex) SEDO CO., LTD. -00-17-BB (hex) Syrinx Industrial Electronics -00-17-BC (hex) Touchtunes Music Corporation -00-17-BD (hex) Tibetsystem -00-17-BE (hex) Tratec Telecom B.V. -00-17-BF (hex) Coherent Research Limited -00-17-C0 (hex) PureTech Systems, Inc. -00-17-C1 (hex) CM Precision Technology LTD. -00-17-C2 (hex) Pirelli Broadband Solutions -00-17-C3 (hex) KTF Technologies Inc. -00-17-C4 (hex) Quanta Microsystems, INC. -00-17-C5 (hex) SonicWALL -00-17-C6 (hex) Cross Match Technologies Inc -00-17-C7 (hex) MARA Systems Consulting AB -00-17-C8 (hex) Kyocera Mita Corporation -00-17-C9 (hex) Samsung Electronics Co., Ltd. -00-17-CA (hex) Qisda Corporation -00-17-CB (hex) Juniper Networks -00-17-CC (hex) Alcatel-Lucent -00-17-CD (hex) CEC Wireless R&D Ltd. -00-17-CE (hex) MB International Telecom Labs srl -00-17-CF (hex) iMCA-GmbH -00-17-D0 (hex) Opticom Communications, LLC -00-17-D1 (hex) Nortel -00-17-D2 (hex) THINLINX PTY LTD -00-17-D3 (hex) Etymotic Research, Inc. -00-17-D4 (hex) Monsoon Multimedia, Inc -00-17-D5 (hex) Samsung Electronics Co., Ltd. -00-17-D6 (hex) Bluechips Microhouse Co.,Ltd. -00-17-D7 (hex) ION Geophysical Corporation Inc. -00-17-D8 (hex) Magnum Semiconductor, Inc. -00-17-D9 (hex) AAI Corporation -00-17-DA (hex) Spans Logic -00-17-DB (hex) CANKO TECHNOLOGIES INC. -00-17-DC (hex) DAEMYUNG ZERO1 -00-17-DD (hex) Clipsal Australia -00-17-DE (hex) Advantage Six Ltd -00-17-DF (hex) Cisco Systems -00-17-E0 (hex) Cisco Systems -00-17-E1 (hex) DACOS Technologies Co., Ltd. -00-17-E2 (hex) Motorola Mobile Devices -00-17-E3 (hex) Texas Instruments -00-17-E4 (hex) Texas Instruments -00-17-E5 (hex) Texas Instruments -00-17-E6 (hex) Texas Instruments -00-17-E7 (hex) Texas Instruments -00-17-E8 (hex) Texas Instruments -00-17-E9 (hex) Texas Instruments -00-17-EA (hex) Texas Instruments -00-17-EB (hex) Texas Instruments -00-17-EC (hex) Texas Instruments -00-17-ED (hex) WooJooIT Ltd. -00-17-EE (hex) Motorola CHS -00-17-EF (hex) Blade Network Technologies, Inc. -00-17-F0 (hex) SZCOM Broadband Network Technology Co.,Ltd -00-17-F1 (hex) Renu Electronics Pvt Ltd -00-17-F2 (hex) Apple Computer -00-17-F3 (hex) Harris Corparation -00-17-F4 (hex) ZERON ALLIANCE -00-17-F5 (hex) LIG NEOPTEK -00-17-F6 (hex) Pyramid Meriden Inc. -00-17-F7 (hex) CEM Solutions Pvt Ltd -00-17-F8 (hex) Motech Industries Inc. -00-17-F9 (hex) Forcom Sp. z o.o. -00-17-FA (hex) Microsoft Corporation -00-17-FB (hex) FA -00-17-FC (hex) Suprema Inc. -00-17-FD (hex) Amulet Hotkey -00-17-FE (hex) TALOS SYSTEM INC. -00-17-FF (hex) PLAYLINE Co.,Ltd. -00-18-00 (hex) UNIGRAND LTD -00-18-01 (hex) Actiontec Electronics, Inc -00-18-02 (hex) Alpha Networks Inc. -00-18-03 (hex) ArcSoft Shanghai Co. LTD -00-18-04 (hex) E-TEK DIGITAL TECHNOLOGY LIMITED -00-18-05 (hex) Beijing InHand Networking Technology Co.,Ltd. -00-18-06 (hex) Hokkei Industries Co., Ltd. -00-18-07 (hex) Fanstel Corp. -00-18-08 (hex) SightLogix, Inc. -00-18-09 (hex) CRESYN -00-18-0A (hex) Meraki, Inc. -00-18-0B (hex) Brilliant Telecommunications -00-18-0C (hex) Optelian Access Networks -00-18-0D (hex) Terabytes Server Storage Tech Corp -00-18-0E (hex) Avega Systems -00-18-0F (hex) Nokia Danmark A/S -00-18-10 (hex) IPTrade S.A. -00-18-11 (hex) Neuros Technology International, LLC. -00-18-12 (hex) Beijing Xinwei Telecom Technology Co., Ltd. -00-18-13 (hex) Sony Ericsson Mobile Communications -00-18-14 (hex) Mitutoyo Corporation -00-18-15 (hex) GZ Technologies, Inc. -00-18-16 (hex) Ubixon Co., Ltd. -00-18-17 (hex) D. E. Shaw Research, LLC -00-18-18 (hex) Cisco Systems -00-18-19 (hex) Cisco Systems -00-18-1A (hex) AVerMedia Information Inc. -00-18-1B (hex) TaiJin Metal Co., Ltd. -00-18-1C (hex) Exterity Limited -00-18-1D (hex) ASIA ELECTRONICS CO.,LTD -00-18-1E (hex) GDX Technologies Ltd. -00-18-1F (hex) Palmmicro Communications -00-18-20 (hex) w5networks -00-18-21 (hex) SINDORICOH -00-18-22 (hex) CEC TELECOM CO.,LTD. -00-18-23 (hex) Delta Electronics, Inc. -00-18-24 (hex) Kimaldi Electronics, S.L. -00-18-25 (hex) Wavion LTD -00-18-26 (hex) Cale Access AB -00-18-27 (hex) NEC PHILIPS UNIFIED SOLUTIONS NEDERLAND BV -00-18-28 (hex) e2v technologies (UK) ltd. -00-18-29 (hex) Gatsometer -00-18-2A (hex) Taiwan Video & Monitor -00-18-2B (hex) Softier -00-18-2C (hex) Ascend Networks, Inc. -00-18-2D (hex) Artec Group O -00-18-2E (hex) XStreamHD, LLC -00-18-2F (hex) Texas Instruments -00-18-30 (hex) Texas Instruments -00-18-31 (hex) Texas Instruments -00-18-32 (hex) Texas Instruments -00-18-33 (hex) Texas Instruments -00-18-34 (hex) Texas Instruments -00-18-35 (hex) Thoratec / ITC -00-18-36 (hex) Reliance Electric Limited -00-18-37 (hex) Universal ABIT Co., Ltd. -00-18-38 (hex) PanAccess Communications,Inc. -00-18-39 (hex) Cisco-Linksys LLC -00-18-3A (hex) Westell Technologies -00-18-3B (hex) CENITS Co., Ltd. -00-18-3C (hex) Encore Software Limited -00-18-3D (hex) Vertex Link Corporation -00-18-3E (hex) Digilent, Inc -00-18-3F (hex) 2Wire, Inc -00-18-40 (hex) 3 Phoenix, Inc. -00-18-41 (hex) High Tech Computer Corp -00-18-42 (hex) Nokia Danmark A/S -00-18-43 (hex) Dawevision Ltd -00-18-44 (hex) Heads Up Technologies, Inc. -00-18-45 (hex) NPL Pulsar Ltd. -00-18-46 (hex) Crypto S.A. -00-18-47 (hex) AceNet Technology Inc. -00-18-48 (hex) Vecima Networks Inc. -00-18-49 (hex) Pigeon Point Systems -00-18-4A (hex) Catcher, Inc. -00-18-4B (hex) Las Vegas Gaming, Inc. -00-18-4C (hex) Bogen Communications -00-18-4D (hex) Netgear Inc. -00-18-4E (hex) Lianhe Technologies, Inc. -00-18-4F (hex) 8 Ways Technology Corp. -00-18-50 (hex) Secfone Kft -00-18-51 (hex) SWsoft -00-18-52 (hex) StorLink Semiconductors, Inc. -00-18-53 (hex) Atera Networks LTD. -00-18-54 (hex) Argard Co., Ltd -00-18-55 (hex) Aeromaritime Systembau GmbH -00-18-56 (hex) EyeFi, Inc -00-18-57 (hex) Unilever R&D -00-18-58 (hex) TagMaster AB -00-18-59 (hex) Strawberry Linux Co.,Ltd. -00-18-5A (hex) uControl, Inc. -00-18-5B (hex) Network Chemistry, Inc -00-18-5C (hex) EDS Lab Pte Ltd -00-18-5D (hex) TAIGUEN TECHNOLOGY (SHEN-ZHEN) CO., LTD. -00-18-5E (hex) Nexterm Inc. -00-18-5F (hex) TAC Inc. -00-18-60 (hex) SIM Technology Group Shanghai Simcom Ltd., -00-18-61 (hex) Ooma, Inc. -00-18-62 (hex) Seagate Technology -00-18-63 (hex) Veritech Electronics Limited -00-18-64 (hex) Cybectec Inc. -00-18-65 (hex) Siemens Healthcare Diagnostics Manufacturing Ltd -00-18-66 (hex) Leutron Vision -00-18-67 (hex) Evolution Robotics Retail -00-18-68 (hex) Scientific Atlanta, A Cisco Company -00-18-69 (hex) KINGJIM -00-18-6A (hex) Global Link Digital Technology Co,.LTD -00-18-6B (hex) Sambu Communics CO., LTD. -00-18-6C (hex) Neonode AB -00-18-6D (hex) Zhenjiang Sapphire Electronic Industry CO. -00-18-6E (hex) 3Com Ltd -00-18-6F (hex) Setha Industria Eletronica LTDA -00-18-70 (hex) E28 Shanghai Limited -00-18-71 (hex) Hewlett Packard -00-18-72 (hex) Expertise Engineering -00-18-73 (hex) Cisco Systems -00-18-74 (hex) Cisco Systems -00-18-75 (hex) AnaCise Testnology Pte Ltd -00-18-76 (hex) WowWee Ltd. -00-18-77 (hex) Amplex A/S -00-18-78 (hex) Mackware GmbH -00-18-79 (hex) dSys -00-18-7A (hex) Wiremold -00-18-7B (hex) 4NSYS Co. Ltd. -00-18-7C (hex) INTERCROSS, LLC -00-18-7D (hex) Armorlink shanghai Co. Ltd -00-18-7E (hex) RGB Spectrum -00-18-7F (hex) ZODIANET -00-18-80 (hex) Maxim Integrated Circuits -00-18-81 (hex) Buyang Electronics Industrial Co., Ltd -00-18-82 (hex) Huawei Technologies Co., Ltd. -00-18-83 (hex) FORMOSA21 INC. -00-18-84 (hex) FON -00-18-85 (hex) Avigilon Corporation -00-18-86 (hex) EL-TECH, INC. -00-18-87 (hex) Metasystem SpA -00-18-88 (hex) GOTIVE a.s. -00-18-89 (hex) WinNet Solutions Limited -00-18-8A (hex) Infinova LLC -00-18-8B (hex) Dell -00-18-8C (hex) Mobile Action Technology Inc. -00-18-8D (hex) Nokia Danmark A/S -00-18-8E (hex) Ekahau, Inc. -00-18-8F (hex) Montgomery Technology, Inc. -00-18-90 (hex) RadioCOM, s.r.o. -00-18-91 (hex) Zhongshan General K-mate Electronics Co., Ltd -00-18-92 (hex) ads-tec GmbH -00-18-93 (hex) SHENZHEN PHOTON BROADBAND TECHNOLOGY CO.,LTD -00-18-94 (hex) zimocom -00-18-95 (hex) Hansun Technologies Inc. -00-18-96 (hex) Great Well Electronic LTD -00-18-97 (hex) JESS-LINK PRODUCTS Co., LTD -00-18-98 (hex) KINGSTATE ELECTRONICS CORPORATION -00-18-99 (hex) ShenZhen jieshun Science&Technology Industry CO,LTD. -00-18-9A (hex) HANA Micron Inc. -00-18-9B (hex) Thomson Inc. -00-18-9C (hex) Weldex Corporation -00-18-9D (hex) Navcast Inc. -00-18-9E (hex) OMNIKEY GmbH. -00-18-9F (hex) Lenntek Corporation -00-18-A0 (hex) Cierma Ascenseurs -00-18-A1 (hex) Tiqit Computers, Inc. -00-18-A2 (hex) XIP Technology AB -00-18-A3 (hex) ZIPPY TECHNOLOGY CORP. -00-18-A4 (hex) Motorola Mobile Devices -00-18-A5 (hex) ADigit Technologies Corp. -00-18-A6 (hex) Persistent Systems, LLC -00-18-A7 (hex) Yoggie Security Systems LTD. -00-18-A8 (hex) AnNeal Technology Inc. -00-18-A9 (hex) Ethernet Direct Corporation -00-18-AA (hex) Protec Fire Detection plc -00-18-AB (hex) BEIJING LHWT MICROELECTRONICS INC. -00-18-AC (hex) Shanghai Jiao Da HISYS Technology Co. Ltd. -00-18-AD (hex) NIDEC SANKYO CORPORATION -00-18-AE (hex) TVT CO.,LTD -00-18-AF (hex) Samsung Electronics Co., Ltd. -00-18-B0 (hex) Nortel -00-18-B1 (hex) Blade Network Technologies -00-18-B2 (hex) ADEUNIS RF -00-18-B3 (hex) TEC WizHome Co., Ltd. -00-18-B4 (hex) Dawon Media Inc. -00-18-B5 (hex) Magna Carta -00-18-B6 (hex) S3C, Inc. -00-18-B7 (hex) D3 LED, LLC -00-18-B8 (hex) New Voice International AG -00-18-B9 (hex) Cisco Systems -00-18-BA (hex) Cisco Systems -00-18-BB (hex) Eliwell Controls srl -00-18-BC (hex) ZAO NVP Bolid -00-18-BD (hex) SHENZHEN DVBWORLD TECHNOLOGY CO., LTD. -00-18-BE (hex) ANSA Corporation -00-18-BF (hex) Essence Technology Solution, Inc. -00-18-C0 (hex) Motorola CHS -00-18-C1 (hex) Almitec Informtica e Comrcio Ltda. -00-18-C2 (hex) Firetide, Inc -00-18-C3 (hex) C&S Microwave -00-18-C4 (hex) Raba Technologies LLC -00-18-C5 (hex) Nokia Danmark A/S -00-18-C6 (hex) OPW Fuel Management Systems -00-18-C7 (hex) Real Time Automation -00-18-C8 (hex) ISONAS Inc. -00-18-C9 (hex) EOps Technology Limited -00-18-CA (hex) Viprinet GmbH -00-18-CB (hex) Tecobest Technology Limited -00-18-CC (hex) AXIOHM SAS -00-18-CD (hex) Erae Electronics Industry Co., Ltd -00-18-CE (hex) Dreamtech Co., Ltd -00-18-CF (hex) Baldor Electric Company -00-18-D0 (hex) AtRoad, A Trimble Company -00-18-D1 (hex) Siemens Home & Office Comm. Devices -00-18-D2 (hex) High-Gain Antennas LLC -00-18-D3 (hex) TEAMCAST -00-18-D4 (hex) Unified Display Interface SIG -00-18-D5 (hex) REIGNCOM -00-18-D6 (hex) Swirlnet A/S -00-18-D7 (hex) Javad Navigation Systems Inc. -00-18-D8 (hex) ARCH METER Corporation -00-18-D9 (hex) Santosha Internatonal, Inc -00-18-DA (hex) AMBER wireless GmbH -00-18-DB (hex) EPL Technology Ltd -00-18-DC (hex) Prostar Co., Ltd. -00-18-DD (hex) Silicondust Engineering Ltd -00-18-DE (hex) Intel Corporation -00-18-DF (hex) The Morey Corporation -00-18-E0 (hex) ANAVEO -00-18-E1 (hex) Verkerk Service Systemen -00-18-E2 (hex) Topdata Sistemas de Automacao Ltda -00-18-E3 (hex) Visualgate Systems, Inc. -00-18-E4 (hex) YIGUANG -00-18-E5 (hex) Adhoco AG -00-18-E6 (hex) Computer Hardware Design SIA -00-18-E7 (hex) Cameo Communications, INC. -00-18-E8 (hex) Hacetron Corporation -00-18-E9 (hex) Numata Corporation -00-18-EA (hex) Alltec GmbH -00-18-EB (hex) BroVis Wireless Networks -00-18-EC (hex) Welding Technology Corporation -00-18-ED (hex) Accutech Ultrasystems Co., Ltd. -00-18-EE (hex) Videology Imaging Solutions, Inc. -00-18-EF (hex) Escape Communications, Inc. -00-18-F0 (hex) JOYTOTO Co., Ltd. -00-18-F1 (hex) Chunichi Denshi Co.,LTD. -00-18-F2 (hex) Beijing Tianyu Communication Equipment Co., Ltd -00-18-F3 (hex) ASUSTek COMPUTER INC. -00-18-F4 (hex) EO TECHNICS Co., Ltd. -00-18-F5 (hex) Shenzhen Streaming Video Technology Company Limited -00-18-F6 (hex) Thomson Telecom Belgium -00-18-F7 (hex) Kameleon Technologies -00-18-F8 (hex) Cisco-Linksys LLC -00-18-F9 (hex) VVOND, Inc. -00-18-FA (hex) Yushin Precision Equipment Co.,Ltd. -00-18-FB (hex) Compro Technology -00-18-FC (hex) Altec Electronic AG -00-18-FD (hex) Optimal Technologies International Inc. -00-18-FE (hex) Hewlett Packard -00-18-FF (hex) PowerQuattro Co. -00-19-00 (hex) Intelliverese - DBA Voicecom -00-19-01 (hex) F1MEDIA -00-19-02 (hex) Cambridge Consultants Ltd -00-19-03 (hex) Bigfoot Networks Inc -00-19-04 (hex) WB Electronics Sp. z o.o. -00-19-05 (hex) SCHRACK Seconet AG -00-19-06 (hex) Cisco Systems -00-19-07 (hex) Cisco Systems -00-19-08 (hex) Duaxes Corporation -00-19-09 (hex) Devi A/S -00-19-0A (hex) HASWARE INC. -00-19-0B (hex) Southern Vision Systems, Inc. -00-19-0C (hex) Encore Electronics, Inc. -00-19-0D (hex) IEEE 1394c -00-19-0E (hex) Atech Technology Co., Ltd. -00-19-0F (hex) Advansus Corp. -00-19-10 (hex) Knick Elektronische Messgeraete GmbH & Co. KG -00-19-11 (hex) Just In Mobile Information Technologies (Shanghai) Co., Ltd. -00-19-12 (hex) Welcat Inc -00-19-13 (hex) Chuang-Yi Network Equipment Co.Ltd. -00-19-14 (hex) Winix Co., Ltd -00-19-15 (hex) TECOM Co., Ltd. -00-19-16 (hex) PayTec AG -00-19-17 (hex) Posiflex Inc. -00-19-18 (hex) Interactive Wear AG -00-19-19 (hex) ASTEL Inc. -00-19-1A (hex) IRLINK -00-19-1B (hex) Sputnik Engineering AG -00-19-1C (hex) Sensicast Systems -00-19-1D (hex) Nintendo Co.,Ltd. -00-19-1E (hex) Beyondwiz Co., Ltd. -00-19-1F (hex) Microlink communications Inc. -00-19-20 (hex) KUME electric Co.,Ltd. -00-19-21 (hex) Elitegroup Computer System Co. -00-19-22 (hex) CM Comandos Lineares -00-19-23 (hex) Phonex Korea Co., LTD. -00-19-24 (hex) LBNL Engineering -00-19-25 (hex) Intelicis Corporation -00-19-26 (hex) BitsGen Co., Ltd. -00-19-27 (hex) ImCoSys Ltd -00-19-28 (hex) Siemens AG, Transportation Systems -00-19-29 (hex) 2M2B Montadora de Maquinas Bahia Brasil LTDA -00-19-2A (hex) Antiope Associates -00-19-2B (hex) Aclara RF Systems Inc. -00-19-2C (hex) Motorola Mobile Devices -00-19-2D (hex) Nokia Corporation -00-19-2E (hex) Spectral Instruments, Inc. -00-19-2F (hex) Cisco Systems -00-19-30 (hex) Cisco Systems -00-19-31 (hex) Balluff GmbH -00-19-32 (hex) Gude Analog- und Digialsysteme GmbH -00-19-33 (hex) Strix Systems, Inc. -00-19-34 (hex) TRENDON TOUCH TECHNOLOGY CORP. -00-19-35 (hex) Duerr Dental GmbH & Co. KG -00-19-36 (hex) STERLITE OPTICAL TECHNOLOGIES LIMITED -00-19-37 (hex) CommerceGuard AB -00-19-38 (hex) UMB Communications Co., Ltd. -00-19-39 (hex) Gigamips -00-19-3A (hex) OESOLUTIONS -00-19-3B (hex) Wilibox Deliberant Group LLC -00-19-3C (hex) HighPoint Technologies Incorporated -00-19-3D (hex) GMC Guardian Mobility Corp. -00-19-3E (hex) PIRELLI BROADBAND SOLUTIONS -00-19-3F (hex) RDI technology(Shenzhen) Co.,LTD -00-19-40 (hex) Rackable Systems -00-19-41 (hex) Pitney Bowes, Inc -00-19-42 (hex) ON SOFTWARE INTERNATIONAL LIMITED -00-19-43 (hex) Belden -00-19-44 (hex) Fossil Partners, L.P. -00-19-45 (hex) Ten-Tec Inc. -00-19-46 (hex) Cianet Industria e Comercio S/A -00-19-47 (hex) Scientific Atlanta, A Cisco Company -00-19-48 (hex) AireSpider Networks -00-19-49 (hex) TENTEL COMTECH CO., LTD. -00-19-4A (hex) TESTO AG -00-19-4B (hex) SAGEM COMMUNICATION -00-19-4C (hex) Fujian Stelcom information & Technology CO.,Ltd -00-19-4D (hex) Avago Technologies Sdn Bhd -00-19-4E (hex) Ultra Electronics - TCS (Tactical Communication Systems) -00-19-4F (hex) Nokia Danmark A/S -00-19-50 (hex) Harman Multimedia -00-19-51 (hex) NETCONS, s.r.o. -00-19-52 (hex) ACOGITO Co., Ltd -00-19-53 (hex) Chainleader Communications Corp. -00-19-54 (hex) Leaf Corporation. -00-19-55 (hex) Cisco Systems -00-19-56 (hex) Cisco Systems -00-19-57 (hex) Saafnet Canada Inc. -00-19-58 (hex) Bluetooth SIG, Inc. -00-19-59 (hex) Staccato Communications Inc. -00-19-5A (hex) Jenaer Antriebstechnik GmbH -00-19-5B (hex) D-Link Corporation -00-19-5C (hex) Innotech Corporation -00-19-5D (hex) ShenZhen XinHuaTong Opto Electronics Co.,Ltd -00-19-5E (hex) Motorola CHS -00-19-5F (hex) Valemount Networks Corporation -00-19-60 (hex) DoCoMo Systems, Inc. -00-19-61 (hex) Blaupunkt GmbH -00-19-62 (hex) Commerciant, LP -00-19-63 (hex) Sony Ericsson Mobile Communications AB -00-19-64 (hex) Doorking Inc. -00-19-65 (hex) YuHua TelTech (ShangHai) Co., Ltd. -00-19-66 (hex) Asiarock Technology Limited -00-19-67 (hex) TELDAT Sp.J. -00-19-68 (hex) Digital Video Networks(Shanghai) CO. LTD. -00-19-69 (hex) Nortel -00-19-6A (hex) MikroM GmbH -00-19-6B (hex) Danpex Corporation -00-19-6C (hex) ETROVISION TECHNOLOGY -00-19-6D (hex) Raybit Systems Korea, Inc -00-19-6E (hex) Metacom (Pty) Ltd. -00-19-6F (hex) SensoPart GmbH -00-19-70 (hex) Z-Com, Inc. -00-19-71 (hex) Guangzhou Unicomp Technology Co.,Ltd -00-19-72 (hex) Plexus (Xiamen) Co.,ltd -00-19-73 (hex) Zeugma Systems -00-19-74 (hex) AboCom Systems, Inc. -00-19-75 (hex) Beijing Huisen networks technology Inc -00-19-76 (hex) Xipher Technologies, LLC -00-19-77 (hex) Aerohive Networks, Inc. -00-19-78 (hex) Datum Systems, Inc. -00-19-79 (hex) Nokia Danmark A/S -00-19-7A (hex) MAZeT GmbH -00-19-7B (hex) Picotest Corp. -00-19-7C (hex) Riedel Communications GmbH -00-19-7D (hex) Hon Hai Precision Ind. Co., Ltd -00-19-7E (hex) Hon Hai Precision Ind. Co., Ltd -00-19-7F (hex) PLANTRONICS, INC. -00-19-80 (hex) Gridpoint Systems -00-19-81 (hex) Vivox Inc -00-19-82 (hex) SmarDTV -00-19-83 (hex) CCT R&D Limited -00-19-84 (hex) ESTIC Corporation -00-19-85 (hex) IT Watchdogs, Inc -00-19-86 (hex) Cheng Hongjian -00-19-87 (hex) Panasonic Mobile Communications Co., Ltd. -00-19-88 (hex) Wi2Wi, Inc -00-19-89 (hex) Sonitrol Corporation -00-19-8A (hex) Northrop Grumman Systems Corp. -00-19-8B (hex) Novera Optics Korea, Inc. -00-19-8C (hex) iXSea -00-19-8D (hex) Ocean Optics, Inc. -00-19-8E (hex) Oticon A/S -00-19-8F (hex) Alcatel Bell N.V. -00-19-90 (hex) ELM DATA Co., Ltd. -00-19-91 (hex) avinfo -00-19-92 (hex) Bluesocket, Inc -00-19-93 (hex) Changshu Switchgear MFG. Co.,Ltd. (Former Changshu Switchgea -00-19-94 (hex) Jorjin Technologies Inc. -00-19-95 (hex) Jurong Hi-Tech (Suzhou)Co.ltd -00-19-96 (hex) TurboChef Technologies Inc. -00-19-97 (hex) Soft Device Sdn Bhd -00-19-98 (hex) SATO CORPORATION -00-19-99 (hex) Fujitsu Technology Solutions -00-19-9A (hex) EDO-EVI -00-19-9B (hex) Diversified Technical Systems, Inc. -00-19-9C (hex) CTRING -00-19-9D (hex) VIZIO, Inc. -00-19-9E (hex) SHOWADENSHI ELECTRONICS,INC. -00-19-9F (hex) DKT A/S -00-19-A0 (hex) NIHON DATA SYSTENS, INC. -00-19-A1 (hex) LG INFORMATION & COMM. -00-19-A2 (hex) ORDYN TECHNOLOGIES -00-19-A3 (hex) asteel electronique atlantique -00-19-A4 (hex) Austar Technology (hang zhou) Co.,Ltd -00-19-A5 (hex) RadarFind Corporation -00-19-A6 (hex) Motorola CHS -00-19-A7 (hex) ITU-T -00-19-A8 (hex) WiQuest Communications -00-19-A9 (hex) Cisco Systems -00-19-AA (hex) Cisco Systems -00-19-AB (hex) Raycom CO ., LTD -00-19-AC (hex) GSP SYSTEMS Inc. -00-19-AD (hex) BOBST SA -00-19-AE (hex) Hopling Technologies b.v. -00-19-AF (hex) Rigol Technologies, Inc. -00-19-B0 (hex) HanYang System -00-19-B1 (hex) Arrow7 Corporation -00-19-B2 (hex) XYnetsoft Co.,Ltd -00-19-B3 (hex) Stanford Research Systems -00-19-B4 (hex) VideoCast Ltd. -00-19-B5 (hex) Famar Fueguina S.A. -00-19-B6 (hex) Euro Emme s.r.l. -00-19-B7 (hex) Nokia Danmark A/S -00-19-B8 (hex) Boundary Devices -00-19-B9 (hex) Dell Inc. -00-19-BA (hex) Paradox Security Systems Ltd -00-19-BB (hex) Hewlett Packard -00-19-BC (hex) ELECTRO CHANCE SRL -00-19-BD (hex) New Media Life -00-19-BE (hex) Altai Technologies Limited -00-19-BF (hex) Citiway technology Co.,ltd -00-19-C0 (hex) Motorola Mobile Devices -00-19-C1 (hex) Alps Electric Co., Ltd -00-19-C2 (hex) Equustek Solutions, Inc. -00-19-C3 (hex) Qualitrol -00-19-C4 (hex) Infocrypt Inc. -00-19-C5 (hex) SONY Computer Entertainment inc, -00-19-C6 (hex) ZTE Corporation -00-19-C7 (hex) Cambridge Industries(Group) Co.,Ltd. -00-19-C8 (hex) AnyDATA Corporation -00-19-C9 (hex) S&C ELECTRIC COMPANY -00-19-CA (hex) Broadata Communications, Inc -00-19-CB (hex) ZyXEL Communications Corporation -00-19-CC (hex) RCG (HK) Ltd -00-19-CD (hex) Chengdu ethercom information technology Ltd. -00-19-CE (hex) Progressive Gaming International -00-19-CF (hex) SALICRU, S.A. -00-19-D0 (hex) Cathexis -00-19-D1 (hex) Intel Corporation -00-19-D2 (hex) Intel Corporation -00-19-D3 (hex) TRAK Microwave -00-19-D4 (hex) ICX Technologies -00-19-D5 (hex) IP Innovations, Inc. -00-19-D6 (hex) LS Cable Ltd. -00-19-D7 (hex) FORTUNETEK CO., LTD -00-19-D8 (hex) MAXFOR -00-19-D9 (hex) Zeutschel GmbH -00-19-DA (hex) Welltrans O&E Technology Co. , Ltd. -00-19-DB (hex) MICRO-STAR INTERNATIONAL CO., LTD. -00-19-DC (hex) ENENSYS Technologies -00-19-DD (hex) FEI-Zyfer, Inc. -00-19-DE (hex) MOBITEK -00-19-DF (hex) Thomson Inc. -00-19-E0 (hex) TP-LINK Technologies Co., Ltd. -00-19-E1 (hex) Nortel -00-19-E2 (hex) Juniper Networks -00-19-E3 (hex) Apple Computer Inc. -00-19-E4 (hex) 2Wire, Inc -00-19-E5 (hex) Lynx Studio Technology, Inc. -00-19-E6 (hex) TOYO MEDIC CO.,LTD. -00-19-E7 (hex) Cisco Systems -00-19-E8 (hex) Cisco Systems -00-19-E9 (hex) S-Information Technolgy, Co., Ltd. -00-19-EA (hex) TeraMage Technologies Co., Ltd. -00-19-EB (hex) Pyronix Ltd -00-19-EC (hex) Sagamore Systems, Inc. -00-19-ED (hex) Axesstel Inc. -00-19-EE (hex) CARLO GAVAZZI CONTROLS SPA-Controls Division -00-19-EF (hex) SHENZHEN LINNKING ELECTRONICS CO.,LTD -00-19-F0 (hex) UNIONMAN TECHNOLOGY CO.,LTD -00-19-F1 (hex) Star Communication Network Technology Co.,Ltd -00-19-F2 (hex) Teradyne K.K. -00-19-F3 (hex) Telematrix, Inc -00-19-F4 (hex) Convergens Oy Ltd -00-19-F5 (hex) Imagination Technologies Ltd -00-19-F6 (hex) Acconet (PTE) Ltd -00-19-F7 (hex) Onset Computer Corporation -00-19-F8 (hex) Embedded Systems Design, Inc. -00-19-F9 (hex) TDK-Lambda -00-19-FA (hex) Cable Vision Electronics CO., LTD. -00-19-FB (hex) AMSTRAD PLC -00-19-FC (hex) PT. Ufoakses Sukses Luarbiasa -00-19-FD (hex) Nintendo Co., Ltd. -00-19-FE (hex) SHENZHEN SEECOMM TECHNOLOGY CO.,LTD. -00-19-FF (hex) Finnzymes -00-1A-00 (hex) MATRIX INC. -00-1A-01 (hex) Smiths Medical -00-1A-02 (hex) SECURE CARE PRODUCTS, INC -00-1A-03 (hex) Angel Electronics Co., Ltd. -00-1A-04 (hex) Interay Solutions BV -00-1A-05 (hex) OPTIBASE LTD -00-1A-06 (hex) OpVista, Inc. -00-1A-07 (hex) Arecont Vision -00-1A-08 (hex) Dalman Technical Services -00-1A-09 (hex) Wayfarer Transit Systems Ltd -00-1A-0A (hex) Adaptive Micro-Ware Inc. -00-1A-0B (hex) BONA TECHNOLOGY INC. -00-1A-0C (hex) Swe-Dish Satellite Systems AB -00-1A-0D (hex) HandHeld entertainment, Inc. -00-1A-0E (hex) Cheng Uei Precision Industry Co.,Ltd -00-1A-0F (hex) Sistemas Avanzados de Control, S.A. -00-1A-10 (hex) LUCENT TRANS ELECTRONICS CO.,LTD -00-1A-11 (hex) Google Inc. -00-1A-12 (hex) Essilor -00-1A-13 (hex) Wanlida Group Co., LTD -00-1A-14 (hex) Xin Hua Control Engineering Co.,Ltd. -00-1A-15 (hex) gemalto e-Payment -00-1A-16 (hex) Nokia Danmark A/S -00-1A-17 (hex) Teak Technologies, Inc. -00-1A-18 (hex) Advanced Simulation Technology inc. -00-1A-19 (hex) Computer Engineering Limited -00-1A-1A (hex) Gentex Corporation/Electro-Acoustic Products -00-1A-1B (hex) Motorola Mobile Devices -00-1A-1C (hex) GT&T Engineering Pte Ltd -00-1A-1D (hex) PChome Online Inc. -00-1A-1E (hex) Aruba Networks -00-1A-1F (hex) Coastal Environmental Systems -00-1A-20 (hex) CMOTECH Co. Ltd. -00-1A-21 (hex) Indac B.V. -00-1A-22 (hex) eq-3 GmbH -00-1A-23 (hex) Ice Qube, Inc -00-1A-24 (hex) Galaxy Telecom Technologies Ltd -00-1A-25 (hex) DELTA DORE -00-1A-26 (hex) Deltanode Solutions AB -00-1A-27 (hex) Ubistar -00-1A-28 (hex) ASWT Co., LTD. Taiwan Branch H.K. -00-1A-29 (hex) Techsonic Industries d/b/a Humminbird -00-1A-2A (hex) Arcadyan Technology Corporation -00-1A-2B (hex) Ayecom Technology Co., Ltd. -00-1A-2C (hex) SATEC Co.,LTD -00-1A-2D (hex) The Navvo Group -00-1A-2E (hex) Ziova Coporation -00-1A-2F (hex) Cisco Systems -00-1A-30 (hex) Cisco Systems -00-1A-31 (hex) SCAN COIN Industries AB -00-1A-32 (hex) ACTIVA MULTIMEDIA -00-1A-33 (hex) ASI Communications, Inc. -00-1A-34 (hex) Konka Group Co., Ltd. -00-1A-35 (hex) BARTEC GmbH -00-1A-36 (hex) Aipermon GmbH & Co. KG -00-1A-37 (hex) Lear Corporation -00-1A-38 (hex) Sanmina-SCI -00-1A-39 (hex) Merten GmbH&CoKG -00-1A-3A (hex) Dongahelecomm -00-1A-3B (hex) Doah Elecom Inc. -00-1A-3C (hex) Technowave Ltd. -00-1A-3D (hex) Ajin Vision Co.,Ltd -00-1A-3E (hex) Faster Technology LLC -00-1A-3F (hex) intelbras -00-1A-40 (hex) A-FOUR TECH CO., LTD. -00-1A-41 (hex) INOCOVA Co.,Ltd -00-1A-42 (hex) Techcity Technology co., Ltd. -00-1A-43 (hex) Logical Link Communications -00-1A-44 (hex) JWTrading Co., Ltd -00-1A-45 (hex) GN Netcom as -00-1A-46 (hex) Digital Multimedia Technology Co., Ltd -00-1A-47 (hex) Agami Systems, Inc. -00-1A-48 (hex) Takacom Corporation -00-1A-49 (hex) Micro Vision Co.,LTD -00-1A-4A (hex) Qumranet Inc. -00-1A-4B (hex) Hewlett Packard -00-1A-4C (hex) Crossbow Technology, Inc -00-1A-4D (hex) GIGA-BYTE TECHNOLOGY CO.,LTD. -00-1A-4E (hex) NTI AG / LinMot -00-1A-4F (hex) AVM GmbH -00-1A-50 (hex) PheeNet Technology Corp. -00-1A-51 (hex) Alfred Mann Foundation -00-1A-52 (hex) Meshlinx Wireless Inc. -00-1A-53 (hex) Zylaya -00-1A-54 (hex) Hip Shing Electronics Ltd. -00-1A-55 (hex) ACA-Digital Corporation -00-1A-56 (hex) ViewTel Co,. Ltd. -00-1A-57 (hex) Matrix Design Group, LLC -00-1A-58 (hex) CCV Deutschland GmbH - Celectronic eHealth Div. -00-1A-59 (hex) Ircona -00-1A-5A (hex) Korea Electric Power Data Network (KDN) Co., Ltd -00-1A-5B (hex) NetCare Service Co., Ltd. -00-1A-5C (hex) Euchner GmbH+Co. KG -00-1A-5D (hex) Mobinnova Corp. -00-1A-5E (hex) Thincom Technology Co.,Ltd -00-1A-5F (hex) KitWorks.fi Ltd. -00-1A-60 (hex) Wave Electronics Co.,Ltd. -00-1A-61 (hex) PacStar Corp. -00-1A-62 (hex) Data Robotics, Incorporated -00-1A-63 (hex) Elster Solutions, LLC, -00-1A-64 (hex) IBM Corp. -00-1A-65 (hex) Seluxit -00-1A-66 (hex) Motorola CHS -00-1A-67 (hex) Infinite QL Sdn Bhd -00-1A-68 (hex) Weltec Enterprise Co., Ltd. -00-1A-69 (hex) Wuhan Yangtze Optical Technology CO.,Ltd. -00-1A-6A (hex) Tranzas, Inc. -00-1A-6B (hex) USI -00-1A-6C (hex) Cisco Systems -00-1A-6D (hex) Cisco Systems -00-1A-6E (hex) Impro Technologies -00-1A-6F (hex) MI.TEL s.r.l. -00-1A-70 (hex) Cisco-Linksys, LLC -00-1A-71 (hex) Diostech Co., Ltd. -00-1A-72 (hex) Mosart Semiconductor Corp. -00-1A-73 (hex) Gemtek Technology Co., Ltd. -00-1A-74 (hex) Procare International Co -00-1A-75 (hex) Sony Ericsson Mobile Communications -00-1A-76 (hex) SDT information Technology Co.,LTD. -00-1A-77 (hex) Motorola Mobile Devices -00-1A-78 (hex) ubtos -00-1A-79 (hex) TELECOMUNICATION TECHNOLOGIES LTD. -00-1A-7A (hex) Lismore Instruments Limited -00-1A-7B (hex) Teleco, Inc. -00-1A-7C (hex) Hirschmann Automation and Control B.V. -00-1A-7D (hex) cyber-blue(HK)Ltd -00-1A-7E (hex) LN Srithai Comm Ltd. -00-1A-7F (hex) GCI Science&Technology Co.,Ltd. -00-1A-80 (hex) Sony Corporation -00-1A-81 (hex) Zelax -00-1A-82 (hex) PROBA Building Automation Co.,LTD -00-1A-83 (hex) Pegasus Technologies Inc. -00-1A-84 (hex) V One Multimedia Pte Ltd -00-1A-85 (hex) NV Michel Van de Wiele -00-1A-86 (hex) AdvancedIO Systems Inc -00-1A-87 (hex) Canhold International Limited -00-1A-88 (hex) Venergy,Co,Ltd -00-1A-89 (hex) Nokia Danmark A/S -00-1A-8A (hex) Samsung Electronics Co., Ltd. -00-1A-8B (hex) CHUNIL ELECTRIC IND., CO. -00-1A-8C (hex) Astaro AG -00-1A-8D (hex) AVECS Bergen GmbH -00-1A-8E (hex) 3Way Networks Ltd -00-1A-8F (hex) Nortel -00-1A-90 (hex) Trpico Sistemas e Telecomunicaes da Amaznia LTDA. -00-1A-91 (hex) FusionDynamic Ltd. -00-1A-92 (hex) ASUSTek COMPUTER INC. -00-1A-93 (hex) ERCO Leuchten GmbH -00-1A-94 (hex) Votronic GmbH -00-1A-95 (hex) Hisense Mobile Communications Technoligy Co.,Ltd. -00-1A-96 (hex) ECLER S.A. -00-1A-97 (hex) fitivision technology Inc. -00-1A-98 (hex) Asotel Communication Limited Taiwan Branch -00-1A-99 (hex) Smarty (HZ) Information Electronics Co., Ltd -00-1A-9A (hex) Skyworth Digital technology(shenzhen)co.ltd. -00-1A-9B (hex) ADEC & Parter AG -00-1A-9C (hex) RightHand Technologies, Inc. -00-1A-9D (hex) Skipper Wireless, Inc. -00-1A-9E (hex) ICON Digital International Limited -00-1A-9F (hex) A-Link Europe Ltd -00-1A-A0 (hex) Dell Inc -00-1A-A1 (hex) Cisco Systems -00-1A-A2 (hex) Cisco Systems -00-1A-A3 (hex) DELORME -00-1A-A4 (hex) Future University-Hakodate -00-1A-A5 (hex) BRN Phoenix -00-1A-A6 (hex) Telefunken Radio Communication Systems GmbH &CO.KG -00-1A-A7 (hex) Torian Wireless -00-1A-A8 (hex) Mamiya Digital Imaging Co., Ltd. -00-1A-A9 (hex) FUJIAN STAR-NET COMMUNICATION CO.,LTD -00-1A-AA (hex) Analogic Corp. -00-1A-AB (hex) eWings s.r.l. -00-1A-AC (hex) Corelatus AB -00-1A-AD (hex) Motorola CHS -00-1A-AE (hex) Savant Systems LLC -00-1A-AF (hex) BLUSENS TECHNOLOGY -00-1A-B0 (hex) Signal Networks Pvt. Ltd., -00-1A-B1 (hex) Asia Pacific Satellite Industries Co., Ltd. -00-1A-B2 (hex) Cyber Solutions Inc. -00-1A-B3 (hex) VISIONITE INC. -00-1A-B4 (hex) FFEI Ltd. -00-1A-B5 (hex) Home Network System -00-1A-B6 (hex) Texas Instruments -00-1A-B7 (hex) Ethos Networks LTD. -00-1A-B8 (hex) Anseri Corporation -00-1A-B9 (hex) PMC -00-1A-BA (hex) Caton Overseas Limited -00-1A-BB (hex) Fontal Technology Incorporation -00-1A-BC (hex) U4EA Technologies Ltd -00-1A-BD (hex) Impatica Inc. -00-1A-BE (hex) COMPUTER HI-TECH INC. -00-1A-BF (hex) TRUMPF Laser Marking Systems AG -00-1A-C0 (hex) JOYBIEN TECHNOLOGIES CO., LTD. -00-1A-C1 (hex) 3Com Ltd -00-1A-C2 (hex) YEC Co.,Ltd. -00-1A-C3 (hex) Scientific-Atlanta, Inc -00-1A-C4 (hex) 2Wire, Inc -00-1A-C5 (hex) BreakingPoint Systems, Inc. -00-1A-C6 (hex) Micro Control Designs -00-1A-C7 (hex) UNIPOINT -00-1A-C8 (hex) ISL (Instrumentation Scientifique de Laboratoire) -00-1A-C9 (hex) SUZUKEN CO.,LTD -00-1A-CA (hex) Tilera Corporation -00-1A-CB (hex) Autocom Products Ltd -00-1A-CC (hex) Celestial Semiconductor, Ltd -00-1A-CD (hex) Tidel Engineering LP -00-1A-CE (hex) YUPITERU CORPORATION -00-1A-CF (hex) C.T. ELETTRONICA -00-1A-D0 (hex) Albis Technologies AG -00-1A-D1 (hex) FARGO CO., LTD. -00-1A-D2 (hex) Eletronica Nitron Ltda -00-1A-D3 (hex) Vamp Ltd. -00-1A-D4 (hex) iPOX Technology Co., Ltd. -00-1A-D5 (hex) KMC CHAIN INDUSTRIAL CO., LTD. -00-1A-D6 (hex) JIAGNSU AETNA ELECTRIC CO.,LTD -00-1A-D7 (hex) Christie Digital Systems, Inc. -00-1A-D8 (hex) AlsterAero GmbH -00-1A-D9 (hex) International Broadband Electric Communications, Inc. -00-1A-DA (hex) Biz-2-Me Inc. -00-1A-DB (hex) Motorola Mobile Devices -00-1A-DC (hex) Nokia Danmark A/S -00-1A-DD (hex) PePWave Ltd -00-1A-DE (hex) Motorola CHS -00-1A-DF (hex) Interactivetv Pty Limited -00-1A-E0 (hex) Mythology Tech Express Inc. -00-1A-E1 (hex) EDGE ACCESS INC -00-1A-E2 (hex) Cisco Systems -00-1A-E3 (hex) Cisco Systems -00-1A-E4 (hex) Medicis Technologies Corporation -00-1A-E5 (hex) Mvox Technologies Inc. -00-1A-E6 (hex) Atlanta Advanced Communications Holdings Limited -00-1A-E7 (hex) Aztek Networks, Inc. -00-1A-E8 (hex) Siemens Enterprise Communications GmbH & Co. KG -00-1A-E9 (hex) Nintendo Co., Ltd. -00-1A-EA (hex) Radio Terminal Systems Pty Ltd -00-1A-EB (hex) Allied Telesis K.K. -00-1A-EC (hex) Keumbee Electronics Co.,Ltd. -00-1A-ED (hex) INCOTEC GmbH -00-1A-EE (hex) Shenztech Ltd -00-1A-EF (hex) Loopcomm Technology, Inc. -00-1A-F0 (hex) Alcatel - IPD -00-1A-F1 (hex) Embedded Artists AB -00-1A-F2 (hex) Dynavisions Schweiz AG -00-1A-F3 (hex) Samyoung Electronics -00-1A-F4 (hex) Handreamnet -00-1A-F5 (hex) PENTAONE. CO., LTD. -00-1A-F6 (hex) Woven Systems, Inc. -00-1A-F7 (hex) dataschalt e+a GmbH -00-1A-F8 (hex) Copley Controls Corporation -00-1A-F9 (hex) AeroVIronment (AV Inc) -00-1A-FA (hex) Welch Allyn, Inc. -00-1A-FB (hex) Joby Inc. -00-1A-FC (hex) ModusLink Corporation -00-1A-FD (hex) EVOLIS -00-1A-FE (hex) SOFACREAL -00-1A-FF (hex) Wizyoung Tech. -00-1B-00 (hex) Neopost Technologies -00-1B-01 (hex) Applied Radio Technologies -00-1B-02 (hex) ED Co.Ltd -00-1B-03 (hex) Action Technology (SZ) Co., Ltd -00-1B-04 (hex) Affinity International S.p.a -00-1B-05 (hex) YMC AG -00-1B-06 (hex) Ateliers R. LAUMONIER -00-1B-07 (hex) Mendocino Software -00-1B-08 (hex) Danfoss Drives A/S -00-1B-09 (hex) Matrix Telecom Pvt. Ltd. -00-1B-0A (hex) Intelligent Distributed Controls Ltd -00-1B-0B (hex) Phidgets Inc. -00-1B-0C (hex) Cisco Systems -00-1B-0D (hex) Cisco Systems -00-1B-0E (hex) InoTec GmbH Organisationssysteme -00-1B-0F (hex) Petratec -00-1B-10 (hex) ShenZhen Kang Hui Technology Co.,ltd -00-1B-11 (hex) D-Link Corporation -00-1B-12 (hex) Apprion -00-1B-13 (hex) Icron Technologies Corporation -00-1B-14 (hex) Carex Lighting Equipment Factory -00-1B-15 (hex) Voxtel, Inc. -00-1B-16 (hex) Celtro Ltd. -00-1B-17 (hex) Palo Alto Networks -00-1B-18 (hex) Tsuken Electric Ind. Co.,Ltd -00-1B-19 (hex) IEEE 1588 Standard -00-1B-1A (hex) e-trees Japan, Inc. -00-1B-1B (hex) Siemens AG, -00-1B-1C (hex) Coherent -00-1B-1D (hex) Phoenix International Co., Ltd -00-1B-1E (hex) HART Communication Foundation -00-1B-1F (hex) DELTA - Danish Electronics, Light & Acoustics -00-1B-20 (hex) TPine Technology -00-1B-21 (hex) Intel Corporate -00-1B-22 (hex) Palit Microsystems ( H.K.) Ltd. -00-1B-23 (hex) SimpleComTools -00-1B-24 (hex) Quanta Computer Inc. -00-1B-25 (hex) Nortel -00-1B-26 (hex) RON-Telecom ZAO -00-1B-27 (hex) Merlin CSI -00-1B-28 (hex) POLYGON, JSC -00-1B-29 (hex) Avantis.Co.,Ltd -00-1B-2A (hex) Cisco Systems -00-1B-2B (hex) Cisco Systems -00-1B-2C (hex) ATRON electronic GmbH -00-1B-2D (hex) Med-Eng Systems Inc. -00-1B-2E (hex) Sinkyo Electron Inc -00-1B-2F (hex) NETGEAR Inc. -00-1B-30 (hex) Solitech Inc. -00-1B-31 (hex) Neural Image. Co. Ltd. -00-1B-32 (hex) QLogic Corporation -00-1B-33 (hex) Nokia Danmark A/S -00-1B-34 (hex) Focus System Inc. -00-1B-35 (hex) ChongQing JINOU Science & Technology Development CO.,Ltd -00-1B-36 (hex) Tsubata Engineering Co.,Ltd. (Head Office) -00-1B-37 (hex) Computec Oy -00-1B-38 (hex) COMPAL INFORMATION (KUNSHAN) CO., LTD. -00-1B-39 (hex) Proxicast -00-1B-3A (hex) SIMS Corp. -00-1B-3B (hex) Yi-Qing CO., LTD -00-1B-3C (hex) Software Technologies Group,Inc. -00-1B-3D (hex) EuroTel Spa -00-1B-3E (hex) Curtis, Inc. -00-1B-3F (hex) ProCurve Networking by HP -00-1B-40 (hex) Network Automation mxc AB -00-1B-41 (hex) General Infinity Co.,Ltd. -00-1B-42 (hex) Wise & Blue -00-1B-43 (hex) Beijing DG Telecommunications equipment Co.,Ltd -00-1B-44 (hex) SanDisk Corporation -00-1B-45 (hex) ABB AS, Division Automation Products -00-1B-46 (hex) Blueone Technology Co.,Ltd -00-1B-47 (hex) Futarque A/S -00-1B-48 (hex) Shenzhen Lantech Electronics Co., Ltd. -00-1B-49 (hex) Roberts Radio limited -00-1B-4A (hex) W&W Communications, Inc. -00-1B-4B (hex) SANION Co., Ltd. -00-1B-4C (hex) Signtech -00-1B-4D (hex) Areca Technology Corporation -00-1B-4E (hex) Navman New Zealand -00-1B-4F (hex) Avaya Inc. -00-1B-50 (hex) Nizhny Novgorod Factory named after M.Frunze, FSUE (NZiF) -00-1B-51 (hex) Vector Technology Corp. -00-1B-52 (hex) Motorola Mobile Devices -00-1B-53 (hex) Cisco Systems -00-1B-54 (hex) Cisco Systems -00-1B-55 (hex) Hurco Automation Ltd. -00-1B-56 (hex) Tehuti Networks Ltd. -00-1B-57 (hex) SEMINDIA SYSTEMS PRIVATE LIMITED -00-1B-58 (hex) ACE CAD Enterprise Co., Ltd. -00-1B-59 (hex) Sony Ericsson Mobile Communications AB -00-1B-5A (hex) Apollo Imaging Technologies, Inc. -00-1B-5B (hex) 2Wire, Inc. -00-1B-5C (hex) Azuretec Co., Ltd. -00-1B-5D (hex) Vololink Pty Ltd -00-1B-5E (hex) BPL Limited -00-1B-5F (hex) Alien Technology -00-1B-60 (hex) NAVIGON AG -00-1B-61 (hex) Digital Acoustics, LLC -00-1B-62 (hex) JHT Optoelectronics Co.,Ltd. -00-1B-63 (hex) Apple Computer Inc. -00-1B-64 (hex) IsaacLandKorea Co., Ltd, -00-1B-65 (hex) China Gridcom Co., Ltd -00-1B-66 (hex) Sennheiser electronic GmbH & Co. KG -00-1B-67 (hex) Ubiquisys Ltd -00-1B-68 (hex) Modnnet Co., Ltd -00-1B-69 (hex) Equaline Corporation -00-1B-6A (hex) Powerwave Technologies Sweden AB -00-1B-6B (hex) Swyx Solutions AG -00-1B-6C (hex) LookX Digital Media BV -00-1B-6D (hex) Midtronics, Inc. -00-1B-6E (hex) Anue Systems, Inc. -00-1B-6F (hex) Teletrak Ltd -00-1B-70 (hex) IRI Ubiteq, INC. -00-1B-71 (hex) Telular Corp. -00-1B-72 (hex) Sicep s.p.a. -00-1B-73 (hex) DTL Broadcast Ltd -00-1B-74 (hex) MiraLink Corporation -00-1B-75 (hex) Hypermedia Systems -00-1B-76 (hex) Ripcode, Inc. -00-1B-77 (hex) Intel Corporate -00-1B-78 (hex) Hewlett Packard -00-1B-79 (hex) FAIVELEY TRANSPORT -00-1B-7A (hex) Nintendo Co., Ltd. -00-1B-7B (hex) The Tintometer Ltd -00-1B-7C (hex) A & R Cambridge -00-1B-7D (hex) CXR Anderson Jacobson -00-1B-7E (hex) Beckmann GmbH -00-1B-7F (hex) TMN Technologies Telecomunicacoes Ltda -00-1B-80 (hex) LORD Corporation -00-1B-81 (hex) DATAQ Instruments, Inc. -00-1B-82 (hex) Taiwan Semiconductor Co., Ltd. -00-1B-83 (hex) Finsoft Ltd -00-1B-84 (hex) Scan Engineering Telecom -00-1B-85 (hex) MAN Diesel SE -00-1B-86 (hex) Bosch Access Systems GmbH -00-1B-87 (hex) Deepsound Tech. Co., Ltd -00-1B-88 (hex) Divinet Access Technologies Ltd -00-1B-89 (hex) EMZA Visual Sense Ltd. -00-1B-8A (hex) 2M Electronic A/S -00-1B-8B (hex) NEC AccessTechnica,Ltd. -00-1B-8C (hex) JMicron Technology Corp. -00-1B-8D (hex) Electronic Computer Systems, Inc. -00-1B-8E (hex) Hulu Sweden AB -00-1B-8F (hex) Cisco Systems -00-1B-90 (hex) Cisco Systems -00-1B-91 (hex) EFKON AG -00-1B-92 (hex) l-acoustics -00-1B-93 (hex) JC Decaux SA DNT -00-1B-94 (hex) T.E.M.A. S.p.A. -00-1B-95 (hex) VIDEO SYSTEMS SRL -00-1B-96 (hex) Snif Labs, Inc. -00-1B-97 (hex) Violin Technologies -00-1B-98 (hex) Samsung Electronics Co., Ltd. -00-1B-99 (hex) KS System GmbH -00-1B-9A (hex) Apollo Fire Detectors Ltd -00-1B-9B (hex) Hose-McCann Communications -00-1B-9C (hex) SATEL sp. z o.o. -00-1B-9D (hex) Novus Security Sp. z o.o. -00-1B-9E (hex) ASKEY COMPUTER CORP -00-1B-9F (hex) Calyptech Pty Ltd -00-1B-A0 (hex) Awox -00-1B-A1 (hex) mic AB -00-1B-A2 (hex) IDS Imaging Development Systems GmbH -00-1B-A3 (hex) Flexit Group GmbH -00-1B-A4 (hex) S.A.E Afikim -00-1B-A5 (hex) MyungMin Systems, Inc. -00-1B-A6 (hex) intotech inc. -00-1B-A7 (hex) Lorica Solutions -00-1B-A8 (hex) UBI&MOBI,.Inc -00-1B-A9 (hex) BROTHER INDUSTRIES, LTD. -00-1B-AA (hex) XenICs nv -00-1B-AB (hex) Telchemy, Incorporated -00-1B-AC (hex) Curtiss Wright Controls Embedded Computing -00-1B-AD (hex) iControl Incorporated -00-1B-AE (hex) Micro Control Systems, Inc -00-1B-AF (hex) Nokia Danmark A/S -00-1B-B0 (hex) BHARAT ELECTRONICS -00-1B-B1 (hex) Wistron Neweb Corp. -00-1B-B2 (hex) Intellect International NV -00-1B-B3 (hex) Condalo GmbH -00-1B-B4 (hex) Airvod Limited -00-1B-B5 (hex) ZF Electronics GmbH -00-1B-B6 (hex) Bird Electronic Corp. -00-1B-B7 (hex) Alta Heights Technology Corp. -00-1B-B8 (hex) BLUEWAY ELECTRONIC CO;LTD -00-1B-B9 (hex) Elitegroup Computer System Co. -00-1B-BA (hex) Nortel -00-1B-BB (hex) RFTech Co.,Ltd -00-1B-BC (hex) Silver Peak Systems, Inc. -00-1B-BD (hex) FMC Kongsberg Subsea AS -00-1B-BE (hex) ICOP Digital -00-1B-BF (hex) SAGEM COMMUNICATION -00-1B-C0 (hex) Juniper Networks -00-1B-C1 (hex) HOLUX Technology, Inc. -00-1B-C2 (hex) Integrated Control Technology Limitied -00-1B-C3 (hex) Mobisolution Co.,Ltd -00-1B-C4 (hex) Ultratec, Inc. -00-1B-C5 (hex) IEEE Registration Authority -00-1B-C6 (hex) Strato Rechenzentrum AG -00-1B-C7 (hex) StarVedia Technology Inc. -00-1B-C8 (hex) MIURA CO.,LTD -00-1B-C9 (hex) FSN DISPLAY INC -00-1B-CA (hex) Beijing Run Technology LTD. Company -00-1B-CB (hex) PEMPEK SYSTEMS PTY LTD -00-1B-CC (hex) KINGTEK CCTV ALLIANCE CO., LTD. -00-1B-CD (hex) DAVISCOMMS (S) PTE LTD -00-1B-CE (hex) Measurement Devices Ltd -00-1B-CF (hex) Dataupia Corporation -00-1B-D0 (hex) IDENTEC SOLUTIONS -00-1B-D1 (hex) SOGESTMATIC -00-1B-D2 (hex) ULTRA-X ASIA PACIFIC Inc. -00-1B-D3 (hex) Matsushita Electric Panasonic AVC -00-1B-D4 (hex) Cisco Systems -00-1B-D5 (hex) Cisco Systems -00-1B-D6 (hex) Kelvin Hughes Ltd -00-1B-D7 (hex) Scientific Atlanta, A Cisco Company -00-1B-D8 (hex) DVTel LTD -00-1B-D9 (hex) Edgewater Computer Systems -00-1B-DA (hex) UTStarcom Inc -00-1B-DB (hex) Valeo VECS -00-1B-DC (hex) Vencer Co., Ltd. -00-1B-DD (hex) Motorola CHS -00-1B-DE (hex) Renkus-Heinz, Inc. -00-1B-DF (hex) Iskra MIS -00-1B-E0 (hex) TELENOT ELECTRONIC GmbH -00-1B-E1 (hex) ViaLogy -00-1B-E2 (hex) AhnLab,Inc. -00-1B-E3 (hex) Health Hero Network, Inc. -00-1B-E4 (hex) TOWNET SRL -00-1B-E5 (hex) 802automation Limited -00-1B-E6 (hex) VR AG -00-1B-E7 (hex) Postek Electronics Co., Ltd. -00-1B-E8 (hex) Ultratronik GmbH -00-1B-E9 (hex) Broadcom Corporation -00-1B-EA (hex) Nintendo Co., Ltd. -00-1B-EB (hex) DMP Electronics INC. -00-1B-EC (hex) Netio Technologies Co., Ltd -00-1B-ED (hex) Brocade Communications Systems, Inc -00-1B-EE (hex) Nokia Danmark A/S -00-1B-EF (hex) Blossoms Digital Technology Co.,Ltd. -00-1B-F0 (hex) Value Platforms Limited -00-1B-F1 (hex) Nanjing SilverNet Software Co., Ltd. -00-1B-F2 (hex) KWORLD COMPUTER CO., LTD -00-1B-F3 (hex) TRANSRADIO SenderSysteme Berlin AG -00-1B-F4 (hex) KENWIN INDUSTRIAL(HK) LTD. -00-1B-F5 (hex) Tellink Sistemas de Telecomunicacin S.L. -00-1B-F6 (hex) CONWISE Technology Corporation Ltd. -00-1B-F7 (hex) Lund IP Products AB -00-1B-F8 (hex) Digitrax Inc. -00-1B-F9 (hex) Intellitect Water Ltd -00-1B-FA (hex) G.i.N. mbH -00-1B-FB (hex) Alps Electric Co., Ltd -00-1B-FC (hex) ASUSTek COMPUTER INC. -00-1B-FD (hex) Dignsys Inc. -00-1B-FE (hex) Zavio Inc. -00-1B-FF (hex) Millennia Media inc. -00-1C-00 (hex) Entry Point, LLC -00-1C-01 (hex) ABB Oy Drives -00-1C-02 (hex) Atto Devices, Inc -00-1C-03 (hex) Betty TV Technology AG -00-1C-04 (hex) Airgain, Inc. -00-1C-05 (hex) Nonin Medical Inc. -00-1C-06 (hex) Siemens Numerical Control Ltd., Nanjing -00-1C-07 (hex) Cwlinux Limited -00-1C-08 (hex) Anystream, Inc. -00-1C-09 (hex) SAE Electronic Co.,Ltd. -00-1C-0A (hex) Shenzhen AEE Technology Co.,Ltd. -00-1C-0B (hex) SmartAnt Telecom -00-1C-0C (hex) TANITA Corporation -00-1C-0D (hex) G-Technology, Inc. -00-1C-0E (hex) Cisco Systems -00-1C-0F (hex) Cisco Systems -00-1C-10 (hex) Cisco-Linksys, LLC -00-1C-11 (hex) Motorola CHS -00-1C-12 (hex) Motorola Mobile Devices -00-1C-13 (hex) OPTSYS TECHNOLOGY CO., LTD. -00-1C-14 (hex) VMware, Inc -00-1C-15 (hex) TXP Corporation -00-1C-16 (hex) ThyssenKrupp Elevator -00-1C-17 (hex) Nortel -00-1C-18 (hex) Sicert S.r.L. -00-1C-19 (hex) secunet Security Networks AG -00-1C-1A (hex) Thomas Instrumentation, Inc -00-1C-1B (hex) Hyperstone GmbH -00-1C-1C (hex) Center Communication Systems GmbH -00-1C-1D (hex) CHENZHOU GOSPELL DIGITAL TECHNOLOGY CO.,LTD -00-1C-1E (hex) emtrion GmbH -00-1C-1F (hex) Quest Retail Technology Pty Ltd -00-1C-20 (hex) CLB Benelux -00-1C-21 (hex) Nucsafe Inc. -00-1C-22 (hex) Aeris Elettronica s.r.l. -00-1C-23 (hex) Dell Inc -00-1C-24 (hex) Formosa Wireless Systems Corp. -00-1C-25 (hex) Hon Hai Precision Ind. Co.,Ltd. -00-1C-26 (hex) Hon Hai Precision Ind. Co.,Ltd. -00-1C-27 (hex) Sunell Electronics Co. -00-1C-28 (hex) Sphairon Access Systems GmbH -00-1C-29 (hex) CORE DIGITAL ELECTRONICS CO., LTD -00-1C-2A (hex) Envisacor Technologies Inc. -00-1C-2B (hex) Alertme.com Limited -00-1C-2C (hex) Synapse -00-1C-2D (hex) FlexRadio Systems -00-1C-2E (hex) ProCurve Networking by HP -00-1C-2F (hex) Pfister GmbH -00-1C-30 (hex) Mode Lighting (UK ) Ltd. -00-1C-31 (hex) Mobile XP Technology Co., LTD -00-1C-32 (hex) Telian Corporation -00-1C-33 (hex) Sutron -00-1C-34 (hex) HUEY CHIAO INTERNATIONAL CO., LTD. -00-1C-35 (hex) Nokia Danmark A/S -00-1C-36 (hex) iNEWiT NV -00-1C-37 (hex) Callpod, Inc. -00-1C-38 (hex) Bio-Rad Laboratories, Inc. -00-1C-39 (hex) S Netsystems Inc. -00-1C-3A (hex) Element Labs, Inc. -00-1C-3B (hex) AmRoad Technology Inc. -00-1C-3C (hex) Seon Design Inc. -00-1C-3D (hex) WaveStorm -00-1C-3E (hex) ECKey Limited -00-1C-3F (hex) International Police Technologies, Inc. -00-1C-40 (hex) VDG-Security bv -00-1C-41 (hex) scemtec Transponder Technology GmbH -00-1C-42 (hex) Parallels, Inc. -00-1C-43 (hex) Samsung Electronics Co.,Ltd -00-1C-44 (hex) Bosch Security Systems BV -00-1C-45 (hex) Chenbro Micom Co., Ltd. -00-1C-46 (hex) QTUM -00-1C-47 (hex) Hangzhou Hollysys Automation Co., Ltd -00-1C-48 (hex) WiDeFi, Inc. -00-1C-49 (hex) Zoltan Technology Inc. -00-1C-4A (hex) AVM GmbH -00-1C-4B (hex) Gener8, Inc. -00-1C-4C (hex) Petrotest Instruments -00-1C-4D (hex) Zeetoo, Inc. -00-1C-4E (hex) TASA International Limited -00-1C-4F (hex) MACAB AB -00-1C-50 (hex) TCL Technoly Electronics(Huizhou)Co.,Ltd -00-1C-51 (hex) Celeno Communications -00-1C-52 (hex) VISIONEE SRL -00-1C-53 (hex) Synergy Lighting Controls -00-1C-54 (hex) Hillstone Networks Inc -00-1C-55 (hex) Shenzhen Kaifa Technology Co. -00-1C-56 (hex) Pado Systems, Inc. -00-1C-57 (hex) Cisco Systems -00-1C-58 (hex) Cisco Systems -00-1C-59 (hex) DEVON IT -00-1C-5A (hex) Advanced Relay Corporation -00-1C-5B (hex) Chubb Electronic Security Systems Ltd -00-1C-5C (hex) Integrated Medical Systems, Inc. -00-1C-5D (hex) Leica Microsystems -00-1C-5E (hex) ASTON France -00-1C-5F (hex) Winland Electronics, Inc. -00-1C-60 (hex) CSP Frontier Technologies,Inc. -00-1C-61 (hex) Galaxy Technology (HK) Ltd. -00-1C-62 (hex) LG Electronics Inc -00-1C-63 (hex) TRUEN -00-1C-64 (hex) Cellnet+Hunt -00-1C-65 (hex) JoeScan, Inc. -00-1C-66 (hex) UCAMP CO.,LTD -00-1C-67 (hex) Pumpkin Networks, Inc. -00-1C-68 (hex) Anhui Sun Create Electronics Co., Ltd -00-1C-69 (hex) Packet Vision Ltd -00-1C-6A (hex) Weiss Engineering Ltd. -00-1C-6B (hex) COVAX Co. Ltd -00-1C-6C (hex) Jabil Circuit (Guangzhou) Limited -00-1C-6D (hex) KYOHRITSU ELECTRONIC INDUSTRY CO., LTD. -00-1C-6E (hex) Newbury Networks, Inc. -00-1C-6F (hex) Emfit Ltd -00-1C-70 (hex) NOVACOMM LTDA -00-1C-71 (hex) Emergent Electronics -00-1C-72 (hex) Mayer & Cie GmbH & Co KG -00-1C-73 (hex) Arista Networks, Inc. -00-1C-74 (hex) Syswan Technologies Inc. -00-1C-75 (hex) RF Systems GmbH -00-1C-76 (hex) The Wandsworth Group Ltd -00-1C-77 (hex) Prodys -00-1C-78 (hex) WYPLAY SAS -00-1C-79 (hex) Cohesive Financial Technologies LLC -00-1C-7A (hex) Perfectone Netware Company Ltd -00-1C-7B (hex) Castlenet Technology Inc. -00-1C-7C (hex) PERQ SYSTEMS CORPORATION -00-1C-7D (hex) Excelpoint Manufacturing Pte Ltd -00-1C-7E (hex) Toshiba -00-1C-7F (hex) Check Point Software Technologies -00-1C-80 (hex) New Business Division/Rhea-Information CO., LTD. -00-1C-81 (hex) NextGen Venturi LTD -00-1C-82 (hex) Genew Technologies -00-1C-83 (hex) New Level Telecom Co., Ltd. -00-1C-84 (hex) STL Solution Co.,Ltd. -00-1C-85 (hex) Eunicorn -00-1C-86 (hex) Cranite Systems, Inc. -00-1C-87 (hex) Uriver Inc. -00-1C-88 (hex) TRANSYSTEM INC. -00-1C-89 (hex) Force Communications, Inc. -00-1C-8A (hex) Verari Systems Inc -00-1C-8B (hex) MJ Innovations Ltd. -00-1C-8C (hex) DIAL TECHNOLOGY LTD. -00-1C-8D (hex) Mesa Imaging -00-1C-8E (hex) Alcatel-Lucent IPD -00-1C-8F (hex) Advanced Electronic Design, Inc. -00-1C-90 (hex) Empacket Corporation -00-1C-91 (hex) Gefen Inc. -00-1C-92 (hex) Tervela -00-1C-93 (hex) ExaDigm Inc -00-1C-94 (hex) LI-COR Biosciences -00-1C-95 (hex) Opticomm Corporation -00-1C-96 (hex) Linkwise Technology Pte Ltd -00-1C-97 (hex) Enzytek Technology Inc., -00-1C-98 (hex) LUCKY TECHNOLOGY (HK) COMPANY LIMITED -00-1C-99 (hex) Shunra Software Ltd. -00-1C-9A (hex) Nokia Danmark A/S -00-1C-9B (hex) FEIG ELECTRONIC GmbH -00-1C-9C (hex) Nortel -00-1C-9D (hex) Liecthi AG -00-1C-9E (hex) Dualtech IT AB -00-1C-9F (hex) Razorstream, LLC -00-1C-A0 (hex) Production Resource Group, LLC -00-1C-A1 (hex) AKAMAI TECHNOLOGIES, INC. -00-1C-A2 (hex) PIRELLI BROADBAND SOLUTIONS -00-1C-A3 (hex) Terra -00-1C-A4 (hex) Sony Ericsson Mobile Communications -00-1C-A5 (hex) Zygo Corporation -00-1C-A6 (hex) Win4NET -00-1C-A7 (hex) International Quartz Limited -00-1C-A8 (hex) AirTies Wireless Networks -00-1C-A9 (hex) Audiomatica Srl -00-1C-AA (hex) Bellon Pty Ltd -00-1C-AB (hex) Meyer Sound Laboratories, Inc. -00-1C-AC (hex) Qniq Technology Corp. -00-1C-AD (hex) Wuhan Telecommunication Devices Co.,Ltd -00-1C-AE (hex) WiChorus, Inc. -00-1C-AF (hex) Plato Networks Inc. -00-1C-B0 (hex) Cisco Systems -00-1C-B1 (hex) Cisco Systems -00-1C-B2 (hex) BPT SPA -00-1C-B3 (hex) APPLE, INC -00-1C-B4 (hex) Iridium Satellite LLC -00-1C-B5 (hex) Neihua Network Technology Co.,LTD.(NHN) -00-1C-B6 (hex) Duzon CNT Co., Ltd. -00-1C-B7 (hex) USC DigiArk Corporation -00-1C-B8 (hex) CBC Co., Ltd -00-1C-B9 (hex) KWANG SUNG ELECTRONICS CO., LTD. -00-1C-BA (hex) VerScient, Inc. -00-1C-BB (hex) MusicianLink -00-1C-BC (hex) CastGrabber, LLC -00-1C-BD (hex) Ezze Mobile Tech., Inc. -00-1C-BE (hex) Nintendo Co., Ltd. -00-1C-BF (hex) Intel Corporate -00-1C-C0 (hex) Intel Corporate -00-1C-C1 (hex) Motorola Mobile Devices -00-1C-C2 (hex) Part II Research, Inc. -00-1C-C3 (hex) Pace Micro Technology plc -00-1C-C4 (hex) Hewlett Packard -00-1C-C5 (hex) 3COM LTD -00-1C-C6 (hex) ProStor Systems -00-1C-C7 (hex) Rembrandt Technologies, LLC d/b/a REMSTREAM -00-1C-C8 (hex) INDUSTRONIC Industrie-Electronic GmbH & Co. KG -00-1C-C9 (hex) Kaise Electronic Technology Co., Ltd. -00-1C-CA (hex) Shanghai Gaozhi Science & Technology Development Co. -00-1C-CB (hex) Forth Corporation Public Company Limited -00-1C-CC (hex) Research In Motion Limited -00-1C-CD (hex) Alektrona Corporation -00-1C-CE (hex) By Techdesign -00-1C-CF (hex) LIMETEK -00-1C-D0 (hex) Circleone Co.,Ltd. -00-1C-D1 (hex) Waves Audio LTD -00-1C-D2 (hex) King Champion (Hong Kong) Limited -00-1C-D3 (hex) ZP Engineering SEL -00-1C-D4 (hex) Nokia Danmark A/S -00-1C-D5 (hex) ZeeVee, Inc. -00-1C-D6 (hex) Nokia Danmark A/S -00-1C-D7 (hex) Harman/Becker Automotive Systems GmbH -00-1C-D8 (hex) BlueAnt Wireless -00-1C-D9 (hex) GlobalTop Technology Inc. -00-1C-DA (hex) Exegin Technologies Limited -00-1C-DB (hex) CARPOINT CO.,LTD -00-1C-DC (hex) Custom Computer Services, Inc. -00-1C-DD (hex) COWBELL ENGINEERING CO., LTD. -00-1C-DE (hex) Interactive Multimedia eXchange Inc. -00-1C-DF (hex) Belkin International Inc. -00-1C-E0 (hex) DASAN TPS -00-1C-E1 (hex) INDRA SISTEMAS, S.A. -00-1C-E2 (hex) Attero Tech, LLC. -00-1C-E3 (hex) Optimedical Systems -00-1C-E4 (hex) EleSy JSC -00-1C-E5 (hex) MBS Electronic Systems GmbH -00-1C-E6 (hex) INNES -00-1C-E7 (hex) Rocon PLC Research Centre -00-1C-E8 (hex) Cummins Inc -00-1C-E9 (hex) Galaxy Technology Limited -00-1C-EA (hex) Scientific-Atlanta, Inc -00-1C-EB (hex) Nortel -00-1C-EC (hex) Mobilesoft (Aust.) Pty Ltd -00-1C-ED (hex) ENVIRONNEMENT SA -00-1C-EE (hex) SHARP Corporation -00-1C-EF (hex) Primax Electronics LTD -00-1C-F0 (hex) D-Link Corporation -00-1C-F1 (hex) SUPoX Technology Co. , LTD. -00-1C-F2 (hex) Tenlon Technology Co.,Ltd. -00-1C-F3 (hex) EVS BROADCAST EQUIPMENT -00-1C-F4 (hex) Media Technology Systems Inc -00-1C-F5 (hex) Wiseblue Technology Limited -00-1C-F6 (hex) Cisco Systems -00-1C-F7 (hex) AudioScience -00-1C-F8 (hex) Parade Technologies, Ltd. -00-1C-F9 (hex) Cisco Systems -00-1C-FA (hex) Alarm.com -00-1C-FB (hex) Motorola CHS -00-1C-FC (hex) Suminet Communication Technologies (Shanghai) Co., Ltd. -00-1C-FD (hex) Universal Electronics -00-1C-FE (hex) Quartics Inc -00-1C-FF (hex) Napera Networks Inc -00-1D-00 (hex) Brivo Systems, LLC -00-1D-01 (hex) Neptune Digital -00-1D-02 (hex) Cybertech Telecom Development -00-1D-03 (hex) Design Solutions Inc. -00-1D-04 (hex) Zipit Wireless, Inc. -00-1D-05 (hex) iLight -00-1D-06 (hex) HM Electronics, Inc. -00-1D-07 (hex) Shenzhen Sang Fei Consumer Communications Co.,Ltd -00-1D-08 (hex) JIANGSU YINHE ELECTRONICS CO., LTD -00-1D-09 (hex) Dell Inc -00-1D-0A (hex) Davis Instruments, Inc. -00-1D-0B (hex) Power Standards Lab -00-1D-0C (hex) MobileCompia -00-1D-0D (hex) Sony Computer Entertainment inc. -00-1D-0E (hex) Agapha Technology co., Ltd. -00-1D-0F (hex) TP-LINK Technologies Co., Ltd. -00-1D-10 (hex) LightHaus Logic, Inc. -00-1D-11 (hex) Analogue & Micro Ltd -00-1D-12 (hex) ROHM CO., LTD. -00-1D-13 (hex) NextGTV -00-1D-14 (hex) SPERADTONE INFORMATION TECHNOLOGY LIMITED -00-1D-15 (hex) Shenzhen Dolphin Electronic Co., Ltd -00-1D-16 (hex) Efixo -00-1D-17 (hex) Digital Sky Corporation -00-1D-18 (hex) Power Innovation GmbH -00-1D-19 (hex) Arcadyan Technology Corporation -00-1D-1A (hex) OvisLink S.A. -00-1D-1B (hex) Sangean Electronics Inc. -00-1D-1C (hex) Gennet s.a. -00-1D-1D (hex) Inter-M Corporation -00-1D-1E (hex) KYUSHU TEN CO.,LTD -00-1D-1F (hex) Siauliu Tauro Televizoriai, JSC -00-1D-20 (hex) COMTREND CO. -00-1D-21 (hex) Alcad SL -00-1D-22 (hex) Foss Analytical A/S -00-1D-23 (hex) SENSUS -00-1D-24 (hex) Aclara Power-Line Systems Inc. -00-1D-25 (hex) Samsung Electronics Co.,Ltd -00-1D-26 (hex) Rockridgesound Technology Co. -00-1D-27 (hex) NAC-INTERCOM -00-1D-28 (hex) Sony Ericsson Mobile Communications AB -00-1D-29 (hex) Doro AB -00-1D-2A (hex) Tideway Electronic LTD -00-1D-2B (hex) Wuhan Pont Technology CO. , LTD -00-1D-2C (hex) Wavetrend Technologies (Pty) Limited -00-1D-2D (hex) Pylone, Inc. -00-1D-2E (hex) Ruckus Wireless -00-1D-2F (hex) QuantumVision Corporation -00-1D-30 (hex) YX Wireless S.A. -00-1D-31 (hex) HIGHPRO INTERNATIONAL R&D CO,.LTD. -00-1D-32 (hex) Longkay Communication & Technology (Shanghai) Co. Ltd -00-1D-33 (hex) Maverick Systems Inc. -00-1D-34 (hex) SYRIS Technology Corp -00-1D-35 (hex) Viconics Electronics Inc. -00-1D-36 (hex) ELECTRONICS CORPORATION OF INDIA LIMITED -00-1D-37 (hex) Thales-Panda Transportation System -00-1D-38 (hex) Seagate Technology -00-1D-39 (hex) MOOHADIGITAL CO., LTD -00-1D-3A (hex) mh acoustics LLC -00-1D-3B (hex) Nokia Danmark A/S -00-1D-3C (hex) Muscle Corporation -00-1D-3D (hex) Avidyne Corporation -00-1D-3E (hex) SAKA TECHNO SCIENCE CO.,LTD -00-1D-3F (hex) Mitron Pty Ltd -00-1D-40 (hex) Living Independently Group, Inc. -00-1D-41 (hex) Hardy Instruments -00-1D-42 (hex) Nortel -00-1D-43 (hex) Shenzhen G-link Digital Technology Co., Ltd. -00-1D-44 (hex) Krohne -00-1D-45 (hex) Cisco Systems -00-1D-46 (hex) Cisco Systems -00-1D-47 (hex) Covote GmbH & Co KG -00-1D-48 (hex) Sensor-Technik Wiedemann GmbH -00-1D-49 (hex) Innovation Wireless Inc. -00-1D-4A (hex) Carestream Health, Inc. -00-1D-4B (hex) Grid Connect Inc. -00-1D-4C (hex) Alcatel-Lucent -00-1D-4D (hex) Adaptive Recognition Hungary, Inc -00-1D-4E (hex) TCM Mobile LLC -00-1D-4F (hex) Apple Computer Inc. -00-1D-50 (hex) SPINETIX SA -00-1D-51 (hex) GE Energy -00-1D-52 (hex) Defzone B.V. -00-1D-53 (hex) S&O Electronics (Malaysia) Sdn. Bhd. -00-1D-54 (hex) Sunnic Technology & Merchandise INC. -00-1D-55 (hex) ZANTAZ, Inc -00-1D-56 (hex) Kramer Electronics Ltd. -00-1D-57 (hex) CAETEC Messtechnik -00-1D-58 (hex) CQ Inc -00-1D-59 (hex) Mitra Energy & Infrastructure -00-1D-5A (hex) 2Wire Inc. -00-1D-5B (hex) Tecvan Informatica Ltda -00-1D-5C (hex) Tom Communication Industrial Co.,Ltd. -00-1D-5D (hex) Control Dynamics Pty. Ltd. -00-1D-5E (hex) COMING MEDIA CORP. -00-1D-5F (hex) OverSpeed SARL -00-1D-60 (hex) ASUSTek COMPUTER INC. -00-1D-61 (hex) BIJ Corporation -00-1D-62 (hex) InPhase Technologies -00-1D-63 (hex) Miele & Cie. KG -00-1D-64 (hex) Adam Communications Systems Int Ltd -00-1D-65 (hex) Microwave Radio Communications -00-1D-66 (hex) Hyundai Telecom -00-1D-67 (hex) AMEC -00-1D-68 (hex) Thomson Telecom Belgium -00-1D-69 (hex) Knorr-Bremse AG -00-1D-6A (hex) Alpha Networks Inc. -00-1D-6B (hex) Motorola (formerly Netopia, Inc -00-1D-6C (hex) ClariPhy Communications, Inc. -00-1D-6D (hex) Confidant International LLC -00-1D-6E (hex) Nokia Danmark A/S -00-1D-6F (hex) Chainzone Technology Co., Ltd -00-1D-70 (hex) Cisco Systems -00-1D-71 (hex) Cisco Systems -00-1D-72 (hex) Wistron Corporation -00-1D-73 (hex) Buffalo Inc. -00-1D-74 (hex) Tianjin China-Silicon Microelectronics Co., Ltd. -00-1D-75 (hex) Radioscape PLC -00-1D-76 (hex) Eyeheight Ltd. -00-1D-77 (hex) NSGate -00-1D-78 (hex) Invengo Information Technology Co.,Ltd -00-1D-79 (hex) SIGNAMAX LLC -00-1D-7A (hex) Wideband Semiconductor, Inc. -00-1D-7B (hex) Ice Energy, Inc. -00-1D-7C (hex) ABE Elettronica S.p.A. -00-1D-7D (hex) GIGA-BYTE TECHNOLOGY CO.,LTD. -00-1D-7E (hex) Cisco-Linksys, LLC -00-1D-7F (hex) Tekron International Ltd -00-1D-80 (hex) Beijing Huahuan Eletronics Co.,Ltd -00-1D-81 (hex) GUANGZHOU GATEWAY ELECTRONICS CO., LTD -00-1D-82 (hex) GN A/S (GN Netcom A/S) -00-1D-83 (hex) Emitech Corporation -00-1D-84 (hex) Gateway, Inc. -00-1D-85 (hex) Call Direct Cellular Solutions -00-1D-86 (hex) Shinwa Industries(China) Ltd. -00-1D-87 (hex) VigTech Labs Sdn Bhd -00-1D-88 (hex) Clearwire -00-1D-89 (hex) VaultStor Corporation -00-1D-8A (hex) TechTrex Inc -00-1D-8B (hex) PIRELLI BROADBAND SOLUTIONS -00-1D-8C (hex) La Crosse Technology LTD -00-1D-8D (hex) Raytek GmbH -00-1D-8E (hex) Alereon, Inc. -00-1D-8F (hex) PureWave Networks -00-1D-90 (hex) EMCO Flow Systems -00-1D-91 (hex) Digitize, Inc -00-1D-92 (hex) MICRO-STAR INT'L CO.,LTD. -00-1D-93 (hex) Modacom -00-1D-94 (hex) Climax Technology Co., Ltd -00-1D-95 (hex) Flash, Inc. -00-1D-96 (hex) WatchGuard Video -00-1D-97 (hex) Alertus Technologies LLC -00-1D-98 (hex) Nokia Danmark A/S -00-1D-99 (hex) Cyan Optic, Inc. -00-1D-9A (hex) GODEX INTERNATIONAL CO., LTD -00-1D-9B (hex) Hokuyo Automatic Co., Ltd. -00-1D-9C (hex) Rockwell Automation -00-1D-9D (hex) ARTJOY INTERNATIONAL LIMITED -00-1D-9E (hex) AXION TECHNOLOGIES -00-1D-9F (hex) MATT R.P.Traczynscy Sp.J. -00-1D-A0 (hex) Heng Yu Electronic Manufacturing Company Limited -00-1D-A1 (hex) Cisco Systems -00-1D-A2 (hex) Cisco Systems -00-1D-A3 (hex) SabiOso -00-1D-A4 (hex) Hangzhou System Technology CO., LTD -00-1D-A5 (hex) WB Electronics -00-1D-A6 (hex) Media Numerics Limited -00-1D-A7 (hex) Seamless Internet -00-1D-A8 (hex) Takahata Electronics Co.,Ltd -00-1D-A9 (hex) Castles Technology, Co., LTD -00-1D-AA (hex) DrayTek Corp. -00-1D-AB (hex) SwissQual License AG -00-1D-AC (hex) Gigamon Systems LLC -00-1D-AD (hex) Sinotech Engineering Consultants, Inc. Geotechnical Enginee -00-1D-AE (hex) CHANG TSENG TECHNOLOGY CO., LTD -00-1D-AF (hex) Nortel -00-1D-B0 (hex) FuJian HengTong Information Technology Co.,Ltd -00-1D-B1 (hex) Crescendo Networks -00-1D-B2 (hex) HOKKAIDO ELECTRIC ENGINEERING CO.,LTD. -00-1D-B3 (hex) ProCurve Networking by HP -00-1D-B4 (hex) KUMHO ENG CO.,LTD -00-1D-B5 (hex) Juniper networks -00-1D-B6 (hex) BestComm Networks, Inc. -00-1D-B7 (hex) Tendril Networks, Inc. -00-1D-B8 (hex) Intoto Inc. -00-1D-B9 (hex) Wellspring Wireless -00-1D-BA (hex) Sony Corporation -00-1D-BB (hex) Dynamic System Electronics Corp. -00-1D-BC (hex) Nintendo Co., Ltd. -00-1D-BD (hex) Versamed Inc. -00-1D-BE (hex) Motorola Mobile Devices -00-1D-BF (hex) Radiient Technologies, Inc. -00-1D-C0 (hex) Enphase Energy -00-1D-C1 (hex) Audinate -00-1D-C2 (hex) XORTEC OY -00-1D-C3 (hex) RIKOR TV, Ltd -00-1D-C4 (hex) AIOI Systems Co., Ltd. -00-1D-C5 (hex) Beijing Jiaxun Feihong Electricial Co., Ltd. -00-1D-C6 (hex) SNR Inc. -00-1D-C7 (hex) L-3 Communications Geneva Aerospace -00-1D-C8 (hex) ScadaMetrcs, LLC. -00-1D-C9 (hex) GainSpan Corp. -00-1D-CA (hex) PAV Electronics Limited -00-1D-CB (hex) Exns Development Oy -00-1D-CC (hex) Hetra Secure Solutions -00-1D-CD (hex) ARRIS Group, Inc. -00-1D-CE (hex) ARRIS Group, Inc. -00-1D-CF (hex) ARRIS Group, Inc. -00-1D-D0 (hex) ARRIS Group, Inc. -00-1D-D1 (hex) ARRIS Group, Inc. -00-1D-D2 (hex) ARRIS Group, Inc. -00-1D-D3 (hex) ARRIS Group, Inc. -00-1D-D4 (hex) ARRIS Group, Inc. -00-1D-D5 (hex) ARRIS Group, Inc. -00-1D-D6 (hex) ARRIS Group, Inc. -00-1D-D7 (hex) Algolith -00-1D-D8 (hex) Microsoft Corporation -00-1D-D9 (hex) Hon Hai Precision Ind.Co.,Ltd. -00-1D-DA (hex) Mikroelektronika spol. s r. o. -00-1D-DB (hex) C-BEL Corporation -00-1D-DC (hex) HangZhou DeChangLong Tech&Info Co.,Ltd -00-1D-DD (hex) DAT H.K. LIMITED -00-1D-DE (hex) Zhejiang Broadcast&Television Technology Co.,Ltd. -00-1D-DF (hex) Sunitec Enterprise Co., Ltd. -00-1D-E0 (hex) Intel Corporate -00-1D-E1 (hex) Intel Corporate -00-1D-E2 (hex) Radionor Communications -00-1D-E3 (hex) Intuicom -00-1D-E4 (hex) Visioneered Image Systems -00-1D-E5 (hex) Cisco Systems -00-1D-E6 (hex) Cisco Systems -00-1D-E7 (hex) Marine Sonic Technology, Ltd. -00-1D-E8 (hex) Nikko Denki Tsushin Company(NDTC) -00-1D-E9 (hex) Nokia Danmark A/S -00-1D-EA (hex) Commtest Instruments Ltd -00-1D-EB (hex) DINEC International -00-1D-EC (hex) Marusys -00-1D-ED (hex) Grid Net, Inc. -00-1D-EE (hex) NEXTVISION SISTEMAS DIGITAIS DE TELEVISO LTDA. -00-1D-EF (hex) TRIMM, INC. -00-1D-F0 (hex) Vidient Systems, Inc. -00-1D-F1 (hex) Intego Systems, Inc. -00-1D-F2 (hex) Netflix, Inc. -00-1D-F3 (hex) SBS Science & Technology Co., Ltd -00-1D-F4 (hex) Magellan Technology Pty Limited -00-1D-F5 (hex) Sunshine Co,LTD -00-1D-F6 (hex) Samsung Electronics Co.,Ltd -00-1D-F7 (hex) R. STAHL Schaltgerte GmbH -00-1D-F8 (hex) Webpro Vision Technology Corporation -00-1D-F9 (hex) Cybiotronics (Far East) Limited -00-1D-FA (hex) Fujian LANDI Commercial Equipment Co.,Ltd -00-1D-FB (hex) NETCLEUS Systems Corporation -00-1D-FC (hex) KSIC -00-1D-FD (hex) Nokia Danmark A/S -00-1D-FE (hex) Palm, Inc -00-1D-FF (hex) Network Critical Solutions Ltd -00-1E-00 (hex) Shantou Institute of Ultrasonic Instruments -00-1E-01 (hex) Renesas Technology Sales Co., Ltd. -00-1E-02 (hex) Sougou Keikaku Kougyou Co.,Ltd. -00-1E-03 (hex) LiComm Co., Ltd. -00-1E-04 (hex) Hanson Research Corporation -00-1E-05 (hex) Xseed Technologies & Computing -00-1E-06 (hex) WIBRAIN -00-1E-07 (hex) Winy Technology Co., Ltd. -00-1E-08 (hex) Centec Networks Inc -00-1E-09 (hex) ZEFATEK Co.,LTD -00-1E-0A (hex) Syba Tech Limited -00-1E-0B (hex) Hewlett Packard -00-1E-0C (hex) Sherwood Information Partners, Inc. -00-1E-0D (hex) Micran Ltd. -00-1E-0E (hex) MAXI VIEW HOLDINGS LIMITED -00-1E-0F (hex) Briot International -00-1E-10 (hex) ShenZhen Huawei Communication Technologies Co.,Ltd. -00-1E-11 (hex) ELELUX INTERNATIONAL LTD -00-1E-12 (hex) Ecolab -00-1E-13 (hex) Cisco Systems -00-1E-14 (hex) Cisco Systems -00-1E-15 (hex) Beech Hill Electronics -00-1E-16 (hex) Keytronix -00-1E-17 (hex) STN BV -00-1E-18 (hex) Radio Activity srl -00-1E-19 (hex) GTRI -00-1E-1A (hex) Best Source Taiwan Inc. -00-1E-1B (hex) Digital Stream Technology, Inc. -00-1E-1C (hex) SWS Australia Pty Limited -00-1E-1D (hex) East Coast Datacom, Inc. -00-1E-1E (hex) Honeywell Life Safety -00-1E-1F (hex) Nortel -00-1E-20 (hex) Intertain Inc. -00-1E-21 (hex) Qisda Co. -00-1E-22 (hex) ARVOO Imaging Products BV -00-1E-23 (hex) Electronic Educational Devices, Inc -00-1E-24 (hex) Zhejiang Bell Technology Co.,ltd -00-1E-25 (hex) Intek Digital Inc -00-1E-26 (hex) Digifriends Co. Ltd -00-1E-27 (hex) SBN TECH Co.,Ltd. -00-1E-28 (hex) Lumexis Corporation -00-1E-29 (hex) Hypertherm Inc -00-1E-2A (hex) Netgear Inc. -00-1E-2B (hex) Radio Systems Design, Inc. -00-1E-2C (hex) CyVerse Corporation -00-1E-2D (hex) STIM -00-1E-2E (hex) SIRTI S.p.A. -00-1E-2F (hex) DiMoto Pty Ltd -00-1E-30 (hex) Shireen Inc -00-1E-31 (hex) INFOMARK CO.,LTD. -00-1E-32 (hex) Zensys -00-1E-33 (hex) Inventec Corporation -00-1E-34 (hex) CryptoMetrics -00-1E-35 (hex) Nintendo Co., Ltd. -00-1E-36 (hex) IPTE -00-1E-37 (hex) USI -00-1E-38 (hex) Bluecard Software Technology Co., Ltd. -00-1E-39 (hex) Comsys Communication Ltd. -00-1E-3A (hex) Nokia Danmark A/S -00-1E-3B (hex) Nokia Danmark A/S -00-1E-3C (hex) Lyngbox Media AB -00-1E-3D (hex) Alps Electric Co., Ltd -00-1E-3E (hex) KMW Inc. -00-1E-3F (hex) TrellisWare Technologies, Inc. -00-1E-40 (hex) Shanghai DareGlobal Technologies Co.,Ltd. -00-1E-41 (hex) Microwave Communication & Component, Inc. -00-1E-42 (hex) Teltonika -00-1E-43 (hex) AISIN AW CO.,LTD. -00-1E-44 (hex) SANTEC -00-1E-45 (hex) Sony Ericsson Mobile Communications AB -00-1E-46 (hex) Motorola CHS -00-1E-47 (hex) PT. Hariff Daya Tunggal Engineering -00-1E-48 (hex) Wi-Links -00-1E-49 (hex) Cisco Systems -00-1E-4A (hex) Cisco Systems -00-1E-4B (hex) City Theatrical -00-1E-4C (hex) Hon Hai Precision Ind.Co., Ltd. -00-1E-4D (hex) Welkin Sciences, LLC -00-1E-4E (hex) DAKO EDV-Ingenieur- und Systemhaus GmbH -00-1E-4F (hex) Dell Inc. -00-1E-50 (hex) BATTISTONI RESEARCH -00-1E-51 (hex) Converter Industry Srl -00-1E-52 (hex) Apple Computer Inc -00-1E-53 (hex) Further Tech Co., LTD -00-1E-54 (hex) TOYO ELECTRIC Corporation -00-1E-55 (hex) COWON SYSTEMS,Inc. -00-1E-56 (hex) Bally Wulff Entertainment GmbH -00-1E-57 (hex) ALCOMA, spol. s r.o. -00-1E-58 (hex) D-Link Corporation -00-1E-59 (hex) Silicon Turnkey Express, LLC -00-1E-5A (hex) Motorola CHS -00-1E-5B (hex) Unitron Company, Inc. -00-1E-5C (hex) RB GeneralEkonomik -00-1E-5D (hex) Holosys d.o.o. -00-1E-5E (hex) COmputime Ltd. -00-1E-5F (hex) KwikByte, LLC -00-1E-60 (hex) Digital Lighting Systems, Inc -00-1E-61 (hex) ITEC GmbH -00-1E-62 (hex) Siemon -00-1E-63 (hex) Vibro-Meter SA -00-1E-64 (hex) Intel Corporate -00-1E-65 (hex) Intel Corporate -00-1E-66 (hex) RESOL Elektronische Regelungen GmbH -00-1E-67 (hex) Intel Corporate -00-1E-68 (hex) Quanta Computer -00-1E-69 (hex) Thomson Inc. -00-1E-6A (hex) Beijing Bluexon Technology Co.,Ltd -00-1E-6B (hex) Scientific Atlanta, A Cisco Company -00-1E-6C (hex) Carbon Mountain LLC -00-1E-6D (hex) IT R&D Center -00-1E-6E (hex) Shenzhen First Mile Communications Ltd -00-1E-6F (hex) Magna-Power Electronics, Inc. -00-1E-70 (hex) Cobham Defence Communications Ltd -00-1E-71 (hex) IgeaCare Systems Inc. -00-1E-72 (hex) PCS -00-1E-73 (hex) ZTE CORPORATION -00-1E-74 (hex) SAGEM COMMUNICATION -00-1E-75 (hex) LG Electronics -00-1E-76 (hex) Thermo Fisher Scientific -00-1E-77 (hex) Air2App -00-1E-78 (hex) Owitek Technology Ltd., -00-1E-79 (hex) Cisco Systems -00-1E-7A (hex) Cisco Systems -00-1E-7B (hex) R.I.CO. S.r.l. -00-1E-7C (hex) Taiwick Limited -00-1E-7D (hex) Samsung Electronics Co.,Ltd -00-1E-7E (hex) Nortel -00-1E-7F (hex) CBM of America -00-1E-80 (hex) Last Mile Ltd. -00-1E-81 (hex) CNB Technology Inc. -00-1E-82 (hex) Pliant Technology, Inc. -00-1E-83 (hex) LAN/MAN Standards Association (LMSC) -00-1E-84 (hex) Pika Technologies Inc. -00-1E-85 (hex) Lagotek Corporation -00-1E-86 (hex) MEL Co.,Ltd. -00-1E-87 (hex) Realease Limited -00-1E-88 (hex) ANDOR SYSTEM SUPPORT CO., LTD. -00-1E-89 (hex) CRFS Limited -00-1E-8A (hex) eCopy, Inc -00-1E-8B (hex) Infra Access Korea Co., Ltd. -00-1E-8C (hex) ASUSTek COMPUTER INC. -00-1E-8D (hex) Motorola Mobile Devices -00-1E-8E (hex) Hunkeler AG -00-1E-8F (hex) CANON INC. -00-1E-90 (hex) Elitegroup Computer Systems Co -00-1E-91 (hex) KIMIN Electronic Co., Ltd. -00-1E-92 (hex) JEULIN S.A. -00-1E-93 (hex) CiriTech Systems Inc -00-1E-94 (hex) SUPERCOM TECHNOLOGY CORPORATION -00-1E-95 (hex) SIGMALINK -00-1E-96 (hex) Sepura Plc -00-1E-97 (hex) Medium Link System Technology CO., LTD, -00-1E-98 (hex) GreenLine Communications -00-1E-99 (hex) Vantanol Industrial Corporation -00-1E-9A (hex) HAMILTON Bonaduz AG -00-1E-9B (hex) San-Eisha, Ltd. -00-1E-9C (hex) Fidustron INC -00-1E-9D (hex) Recall Technologies, Inc. -00-1E-9E (hex) ddm hopt + schuler Gmbh + Co. KG -00-1E-9F (hex) Visioneering Systems, Inc. -00-1E-A0 (hex) XLN-t -00-1E-A1 (hex) Brunata a/s -00-1E-A2 (hex) Symx Systems, Inc. -00-1E-A3 (hex) Nokia Danmark A/S -00-1E-A4 (hex) Nokia Danmark A/S -00-1E-A5 (hex) ROBOTOUS, Inc. -00-1E-A6 (hex) Best IT World (India) Pvt. Ltd. -00-1E-A7 (hex) ActionTec Electronics, Inc -00-1E-A8 (hex) Datang Mobile Communications Equipment CO.,LTD -00-1E-A9 (hex) Nintendo Co., Ltd. -00-1E-AA (hex) E-Senza Technologies GmbH -00-1E-AB (hex) TeleWell Oy -00-1E-AC (hex) Armadeus Systems -00-1E-AD (hex) Wingtech Group Limited -00-1E-AE (hex) Continental Automotive Systems -00-1E-AF (hex) Ophir Optronics Ltd -00-1E-B0 (hex) ImesD Electronica S.L. -00-1E-B1 (hex) Cryptsoft Pty Ltd -00-1E-B2 (hex) LG innotek -00-1E-B3 (hex) Primex Wireless -00-1E-B4 (hex) UNIFAT TECHNOLOGY LTD. -00-1E-B5 (hex) Ever Sparkle Technologies Ltd -00-1E-B6 (hex) TAG Heuer SA -00-1E-B7 (hex) TBTech, Co., Ltd. -00-1E-B8 (hex) Fortis, Inc. -00-1E-B9 (hex) Sing Fai Technology Limited -00-1E-BA (hex) High Density Devices AS -00-1E-BB (hex) BLUELIGHT TECHNOLOGY INC. -00-1E-BC (hex) WINTECH AUTOMATION CO.,LTD. -00-1E-BD (hex) Cisco Systems -00-1E-BE (hex) Cisco Systems -00-1E-BF (hex) Haas Automation Inc. -00-1E-C0 (hex) ZeroG Wireless Inc. -00-1E-C1 (hex) 3COM EUROPE LTD -00-1E-C2 (hex) Apple, Inc -00-1E-C3 (hex) Kozio, Inc. -00-1E-C4 (hex) Celio Corp -00-1E-C5 (hex) Middle Atlantic Products Inc -00-1E-C6 (hex) Obvius Holdings LLC -00-1E-C7 (hex) 2Wire -00-1E-C8 (hex) Rapid Mobile (Pty) Ltd -00-1E-C9 (hex) Dell Inc -00-1E-CA (hex) Nortel -00-1E-CB (hex) "RPC "Energoautomatika" Ltd -00-1E-CC (hex) CDVI -00-1E-CD (hex) KYLAND -00-1E-CE (hex) BISA Technologies (Hong Kong) Limited -00-1E-CF (hex) PHILIPS ELECTRONICS UK LTD -00-1E-D0 (hex) CONNEXIUM -00-1E-D1 (hex) Keyprocessor B.V. -00-1E-D2 (hex) Ray Shine Video Technology Inc -00-1E-D3 (hex) Dot Technology Int'l Co., Ltd. -00-1E-D4 (hex) Doble Engineering -00-1E-D5 (hex) Tekon-Automatics -00-1E-D6 (hex) Alentec & Orion AB -00-1E-D7 (hex) H-Stream Wireless, Inc. -00-1E-D8 (hex) Digital United Inc. -00-1E-D9 (hex) Mitsubishi Precision Co.,LTd. -00-1E-DA (hex) Wesemann Elektrotechniek B.V. -00-1E-DB (hex) Giken Trastem Co., Ltd. -00-1E-DC (hex) Sony Ericsson Mobile Communications AB -00-1E-DD (hex) WASKO S.A. -00-1E-DE (hex) BYD COMPANY LIMITED -00-1E-DF (hex) Master Industrialization Center Kista -00-1E-E0 (hex) Urmet Domus SpA -00-1E-E1 (hex) Samsung Electronics Co.,Ltd -00-1E-E2 (hex) Samsung Electronics Co.,Ltd -00-1E-E3 (hex) T&W Electronics (ShenZhen) Co.,Ltd -00-1E-E4 (hex) ACS Solutions France -00-1E-E5 (hex) Cisco-Linksys, LLC -00-1E-E6 (hex) Shenzhen Advanced Video Info-Tech Co., Ltd. -00-1E-E7 (hex) Epic Systems Inc -00-1E-E8 (hex) Mytek -00-1E-E9 (hex) Stoneridge Electronics AB -00-1E-EA (hex) Sensor Switch, Inc. -00-1E-EB (hex) Talk-A-Phone Co. -00-1E-EC (hex) COMPAL INFORMATION (KUNSHAN) CO., LTD. -00-1E-ED (hex) Adventiq Ltd. -00-1E-EE (hex) ETL Systems Ltd -00-1E-EF (hex) Cantronic International Limited -00-1E-F0 (hex) Gigafin Networks -00-1E-F1 (hex) Servimat -00-1E-F2 (hex) Micro Motion Inc -00-1E-F3 (hex) From2 -00-1E-F4 (hex) L-3 Communications Display Systems -00-1E-F5 (hex) Hitek Automated Inc. -00-1E-F6 (hex) Cisco Systems -00-1E-F7 (hex) Cisco Systems -00-1E-F8 (hex) Emfinity Inc. -00-1E-F9 (hex) Pascom Kommunikations systeme GmbH. -00-1E-FA (hex) PROTEI Ltd. -00-1E-FB (hex) Trio Motion Technology Ltd -00-1E-FC (hex) JSC "MASSA-K" -00-1E-FD (hex) Microbit 2.0 AB -00-1E-FE (hex) LEVEL s.r.o. -00-1E-FF (hex) Mueller-Elektronik GmbH & Co. KG -00-1F-00 (hex) Nokia Danmark A/S -00-1F-01 (hex) Nokia Danmark A/S -00-1F-02 (hex) Pixelmetrix Corporation Pte Ltd -00-1F-03 (hex) NUM AG -00-1F-04 (hex) Granch Ltd. -00-1F-05 (hex) iTAS Technology Corp. -00-1F-06 (hex) Integrated Dispatch Solutions -00-1F-07 (hex) AZTEQ Mobile -00-1F-08 (hex) RISCO LTD -00-1F-09 (hex) JASTEC CO., LTD. -00-1F-0A (hex) Nortel -00-1F-0B (hex) Federal State Unitary Enterprise Industrial Union"Electropribor" -00-1F-0C (hex) Intelligent Digital Services GmbH -00-1F-0D (hex) L3 Communications - Telemetry West -00-1F-0E (hex) Japan Kyastem Co., Ltd -00-1F-0F (hex) Select Engineered Systems -00-1F-10 (hex) TOLEDO DO BRASIL INDUSTRIA DE BALANCAS LTDA -00-1F-11 (hex) OPENMOKO, INC. -00-1F-12 (hex) Juniper Networks -00-1F-13 (hex) S.& A.S. Ltd. -00-1F-14 (hex) NexG -00-1F-15 (hex) Bioscrypt Inc -00-1F-16 (hex) Wistron Corporation -00-1F-17 (hex) IDX Company, Ltd. -00-1F-18 (hex) Hakusan.Mfg.Co,.Ltd -00-1F-19 (hex) BEN-RI ELECTRONICA S.A. -00-1F-1A (hex) Prominvest -00-1F-1B (hex) RoyalTek Company Ltd. -00-1F-1C (hex) KOBISHI ELECTRIC Co.,Ltd. -00-1F-1D (hex) Atlas Material Testing Technology LLC -00-1F-1E (hex) Astec Technology Co., Ltd -00-1F-1F (hex) Edimax Technology Co. Ltd. -00-1F-20 (hex) Logitech Europe SA -00-1F-21 (hex) Inner Mongolia Yin An Science & Technology Development Co.,L -00-1F-22 (hex) Fiberxon, Inc. -00-1F-23 (hex) Interacoustics -00-1F-24 (hex) DIGITVIEW TECHNOLOGY CO., LTD. -00-1F-25 (hex) MBS GmbH -00-1F-26 (hex) Cisco Systems -00-1F-27 (hex) Cisco Systems -00-1F-28 (hex) ProCurve Networking by HP -00-1F-29 (hex) Hewlett Packard -00-1F-2A (hex) ACCM -00-1F-2B (hex) Orange Logic -00-1F-2C (hex) Starbridge Networks -00-1F-2D (hex) Electro-Optical Imaging, Inc. -00-1F-2E (hex) Triangle Research Int'l Pte Ltd -00-1F-2F (hex) Berker GmbH & Co. KG -00-1F-30 (hex) Travelping -00-1F-31 (hex) Radiocomp -00-1F-32 (hex) Nintendo Co., Ltd. -00-1F-33 (hex) Netgear Inc. -00-1F-34 (hex) Lung Hwa Electronics Co., Ltd. -00-1F-35 (hex) AIR802 LLC -00-1F-36 (hex) Bellwin Information Co. Ltd., -00-1F-37 (hex) Genesis I&C -00-1F-38 (hex) POSITRON -00-1F-39 (hex) Construcciones y Auxiliar de Ferrocarriles, S.A. -00-1F-3A (hex) Hon Hai Precision Ind.Co., Ltd. -00-1F-3B (hex) Intel Corporate -00-1F-3C (hex) Intel Corporate -00-1F-3D (hex) Qbit GmbH -00-1F-3E (hex) RP-Technik e.K. -00-1F-3F (hex) AVM GmbH -00-1F-40 (hex) Speakercraft Inc. -00-1F-41 (hex) Ruckus Wireless -00-1F-42 (hex) Etherstack Pty Ltd -00-1F-43 (hex) ENTES ELEKTRONIK -00-1F-44 (hex) GE Transportation Systems -00-1F-45 (hex) Enterasys -00-1F-46 (hex) Nortel -00-1F-47 (hex) MCS Logic Inc. -00-1F-48 (hex) Mojix Inc. -00-1F-49 (hex) Eurosat Distribution Ltd -00-1F-4A (hex) Albentia Systems S.A. -00-1F-4B (hex) Lineage Power -00-1F-4C (hex) Roseman Engineering Ltd -00-1F-4D (hex) Segnetics LLC -00-1F-4E (hex) ConMed Linvatec -00-1F-4F (hex) Thinkware Co. Ltd. -00-1F-50 (hex) Swissdis AG -00-1F-51 (hex) HD Communications Corp -00-1F-52 (hex) UVT Unternehmensberatung fr Verkehr und Technik GmbH -00-1F-53 (hex) GEMAC Gesellschaft fr Mikroelektronikanwendung Chemnitz mbH -00-1F-54 (hex) Lorex Technology Inc. -00-1F-55 (hex) Honeywell Security (China) Co., Ltd. -00-1F-56 (hex) DIGITAL FORECAST -00-1F-57 (hex) Phonik Innovation Co.,LTD -00-1F-58 (hex) EMH Energiemesstechnik GmbH -00-1F-59 (hex) Kronback Tracers -00-1F-5A (hex) Beckwith Electric Co. -00-1F-5B (hex) Apple, Inc. -00-1F-5C (hex) Nokia Danmark A/S -00-1F-5D (hex) Nokia Danmark A/S -00-1F-5E (hex) Dyna Technology Co.,Ltd. -00-1F-5F (hex) Blatand GmbH -00-1F-60 (hex) COMPASS SYSTEMS CORP. -00-1F-61 (hex) Talent Communication Networks Inc. -00-1F-62 (hex) JSC "Stilsoft" -00-1F-63 (hex) JSC Goodwin-Europa -00-1F-64 (hex) Beijing Autelan Technology Inc. -00-1F-65 (hex) KOREA ELECTRIC TERMINAL CO., LTD. -00-1F-66 (hex) PLANAR LLC -00-1F-67 (hex) Hitachi,Ltd. -00-1F-68 (hex) Martinsson Elektronik AB -00-1F-69 (hex) Pingood Technology Co., Ltd. -00-1F-6A (hex) PacketFlux Technologies, Inc. -00-1F-6B (hex) LG Electronics -00-1F-6C (hex) Cisco Systems -00-1F-6D (hex) Cisco Systems -00-1F-6E (hex) Vtech Engineering Corporation -00-1F-6F (hex) Fujian Sunnada Communication Co.,Ltd. -00-1F-70 (hex) Botik Technologies LTD -00-1F-71 (hex) xG Technology, Inc. -00-1F-72 (hex) QingDao Hiphone Technology Co,.Ltd -00-1F-73 (hex) Teraview Technology Co., Ltd. -00-1F-74 (hex) Eigen Development -00-1F-75 (hex) GiBahn Media -00-1F-76 (hex) AirLogic Systems Inc. -00-1F-77 (hex) HEOL DESIGN -00-1F-78 (hex) Blue Fox Porini Textile -00-1F-79 (hex) Lodam Electronics A/S -00-1F-7A (hex) WiWide Inc. -00-1F-7B (hex) TechNexion Ltd. -00-1F-7C (hex) Witelcom AS -00-1F-7D (hex) embedded wireless GmbH -00-1F-7E (hex) Motorola Mobile Devices -00-1F-7F (hex) Phabrix Limited -00-1F-80 (hex) Lucas Holding bv -00-1F-81 (hex) Accel Semiconductor Corp -00-1F-82 (hex) Cal-Comp Electronics & Communications Co., Ltd -00-1F-83 (hex) Teleplan Technology Services Sdn Bhd -00-1F-84 (hex) Gigle Semiconductor -00-1F-85 (hex) Apriva ISS, LLC -00-1F-86 (hex) digEcor -00-1F-87 (hex) Skydigital Inc. -00-1F-88 (hex) FMS Force Measuring Systems AG -00-1F-89 (hex) Signalion GmbH -00-1F-8A (hex) Ellion Digital Inc. -00-1F-8B (hex) Storspeed, Inc. -00-1F-8C (hex) CCS Inc. -00-1F-8D (hex) Ingenieurbuero Stark GmbH und Ko. KG -00-1F-8E (hex) Metris USA Inc. -00-1F-8F (hex) Shanghai Bellmann Digital Source Co.,Ltd. -00-1F-90 (hex) Actiontec Electronics, Inc -00-1F-91 (hex) DBS Lodging Technologies, LLC -00-1F-92 (hex) VideoIQ, Inc. -00-1F-93 (hex) Xiotech Corporation -00-1F-94 (hex) Lascar Electronics Ltd -00-1F-95 (hex) SAGEM COMMUNICATION -00-1F-96 (hex) APROTECH CO.LTD -00-1F-97 (hex) BERTANA SRL -00-1F-98 (hex) DAIICHI-DENTSU LTD. -00-1F-99 (hex) SERONICS co.ltd -00-1F-9A (hex) Nortel Networks -00-1F-9B (hex) POSBRO -00-1F-9C (hex) LEDCO -00-1F-9D (hex) Cisco Systems -00-1F-9E (hex) Cisco Systems -00-1F-9F (hex) Thomson Telecom Belgium -00-1F-A0 (hex) A10 Networks -00-1F-A1 (hex) Gtran Inc -00-1F-A2 (hex) Datron World Communications, Inc. -00-1F-A3 (hex) T&W Electronics(Shenzhen)Co.,Ltd. -00-1F-A4 (hex) ShenZhen Gongjin Electronics Co.,Ltd -00-1F-A5 (hex) Blue-White Industries -00-1F-A6 (hex) Stilo srl -00-1F-A7 (hex) Sony Computer Entertainment Inc. -00-1F-A8 (hex) ANI Technologies Corp. -00-1F-A9 (hex) Atlanta DTH, Inc. -00-1F-AA (hex) Taseon, Inc. -00-1F-AB (hex) I.S HIGH TECH.INC -00-1F-AC (hex) Goodmill Systems Ltd -00-1F-AD (hex) Brown Innovations, Inc -00-1F-AE (hex) Blick South Africa (Pty) Ltd -00-1F-AF (hex) NextIO, Inc. -00-1F-B0 (hex) TimeIPS, Inc. -00-1F-B1 (hex) Cybertech Inc. -00-1F-B2 (hex) Sontheim Industrie Elektronik GmbH -00-1F-B3 (hex) 2Wire -00-1F-B4 (hex) SmartShare Systems -00-1F-B5 (hex) I/O Interconnect Inc. -00-1F-B6 (hex) Chi Lin Technology Co., Ltd. -00-1F-B7 (hex) WiMate Technologies Corp. -00-1F-B8 (hex) Universal Remote Control, Inc. -00-1F-B9 (hex) Paltronics -00-1F-BA (hex) BoYoung Tech. & Marketing, Inc. -00-1F-BB (hex) Xenatech Co.,LTD -00-1F-BC (hex) EVGA Corporation -00-1F-BD (hex) Kyocera Wireless Corp. -00-1F-BE (hex) Shenzhen Mopnet Industrial Co.,Ltd -00-1F-BF (hex) Fulhua Microelectronics Corp. Taiwan Branch -00-1F-C0 (hex) Control Express Finland Oy -00-1F-C1 (hex) Hanlong Technology Co.,LTD -00-1F-C2 (hex) Jow Tong Technology Co Ltd -00-1F-C3 (hex) SmartSynch, Inc -00-1F-C4 (hex) Motorola CHS -00-1F-C5 (hex) Nintendo Co., Ltd. -00-1F-C6 (hex) ASUSTek COMPUTER INC. -00-1F-C7 (hex) Casio Hitachi Mobile Comunications Co., Ltd. -00-1F-C8 (hex) Up-Today Industrial Co., Ltd. -00-1F-C9 (hex) Cisco Systems -00-1F-CA (hex) Cisco Systems -00-1F-CB (hex) NIW Solutions -00-1F-CC (hex) Samsung Electronics Co.,Ltd -00-1F-CD (hex) Samsung Electronics -00-1F-CE (hex) QTECH LLC -00-1F-CF (hex) MSI Technology GmbH -00-1F-D0 (hex) GIGA-BYTE TECHNOLOGY CO.,LTD. -00-1F-D1 (hex) OPTEX CO.,LTD. -00-1F-D2 (hex) COMMTECH TECHNOLOGY MACAO COMMERCIAL OFFSHORE LTD. -00-1F-D3 (hex) RIVA Networks Inc. -00-1F-D4 (hex) 4IPNET, INC. -00-1F-D5 (hex) MICRORISC s.r.o. -00-1F-D6 (hex) Shenzhen Allywll -00-1F-D7 (hex) TELERAD SA -00-1F-D8 (hex) A-TRUST COMPUTER CORPORATION -00-1F-D9 (hex) RSD Communications Ltd -00-1F-DA (hex) Nortel Networks -00-1F-DB (hex) Network Supply Corp., -00-1F-DC (hex) Mobile Safe Track Ltd -00-1F-DD (hex) GDI LLC -00-1F-DE (hex) Nokia Danmark A/S -00-1F-DF (hex) Nokia Danmark A/S -00-1F-E0 (hex) EdgeVelocity Corp -00-1F-E1 (hex) Hon Hai Precision Ind. Co., Ltd. -00-1F-E2 (hex) Hon Hai Precision Ind. Co., Ltd. -00-1F-E3 (hex) LG Electronics -00-1F-E4 (hex) Sony Ericsson Mobile Communications -00-1F-E5 (hex) In-Circuit GmbH -00-1F-E6 (hex) Alphion Corporation -00-1F-E7 (hex) Simet -00-1F-E8 (hex) KURUSUGAWA Electronics Industry Inc,. -00-1F-E9 (hex) Printrex, Inc. -00-1F-EA (hex) Applied Media Technologies Corporation -00-1F-EB (hex) Trio Datacom Pty Ltd -00-1F-EC (hex) Synapse lectronique -00-1F-ED (hex) Tecan Systems Inc. -00-1F-EE (hex) ubisys technologies GmbH -00-1F-EF (hex) SHINSEI INDUSTRIES CO.,LTD -00-1F-F0 (hex) Audio Partnership -00-1F-F1 (hex) Paradox Hellas S.A. -00-1F-F2 (hex) VIA Technologies, Inc. -00-1F-F3 (hex) Apple, Inc -00-1F-F4 (hex) Power Monitors, Inc. -00-1F-F5 (hex) Kongsberg Defence & Aerospace -00-1F-F6 (hex) PS Audio International -00-1F-F7 (hex) Nakajima All Precision Co., Ltd. -00-1F-F8 (hex) Siemens AG, Sector Industry, Drive Technologies, Motion Control Systems -00-1F-F9 (hex) Advanced Knowledge Associates -00-1F-FA (hex) Coretree, Co, Ltd -00-1F-FB (hex) Green Packet Bhd -00-1F-FC (hex) Riccius+Sohn GmbH -00-1F-FD (hex) Indigo Mobile Technologies Corp. -00-1F-FE (hex) ProCurve Networking by HP -00-1F-FF (hex) Respironics, Inc. -00-20-00 (hex) LEXMARK INTERNATIONAL, INC. -00-20-01 (hex) DSP SOLUTIONS, INC. -00-20-02 (hex) SERITECH ENTERPRISE CO., LTD. -00-20-03 (hex) PIXEL POWER LTD. -00-20-04 (hex) YAMATAKE-HONEYWELL CO., LTD. -00-20-05 (hex) SIMPLE TECHNOLOGY -00-20-06 (hex) GARRETT COMMUNICATIONS, INC. -00-20-07 (hex) SFA, INC. -00-20-08 (hex) CABLE & COMPUTER TECHNOLOGY -00-20-09 (hex) PACKARD BELL ELEC., INC. -00-20-0A (hex) SOURCE-COMM CORP. -00-20-0B (hex) OCTAGON SYSTEMS CORP. -00-20-0C (hex) ADASTRA SYSTEMS CORP. -00-20-0D (hex) CARL ZEISS -00-20-0E (hex) SATELLITE TECHNOLOGY MGMT, INC -00-20-0F (hex) TANBAC CO., LTD. -00-20-10 (hex) JEOL SYSTEM TECHNOLOGY CO. LTD -00-20-11 (hex) CANOPUS CO., LTD. -00-20-12 (hex) CAMTRONICS MEDICAL SYSTEMS -00-20-13 (hex) DIVERSIFIED TECHNOLOGY, INC. -00-20-14 (hex) GLOBAL VIEW CO., LTD. -00-20-15 (hex) ACTIS COMPUTER SA -00-20-16 (hex) SHOWA ELECTRIC WIRE & CABLE CO -00-20-17 (hex) ORBOTECH -00-20-18 (hex) CIS TECHNOLOGY INC. -00-20-19 (hex) OHLER GmbH -00-20-1A (hex) MRV Communications, Inc. -00-20-1B (hex) NORTHERN TELECOM/NETWORK -00-20-1C (hex) EXCEL, INC. -00-20-1D (hex) KATANA PRODUCTS -00-20-1E (hex) NETQUEST CORPORATION -00-20-1F (hex) BEST POWER TECHNOLOGY, INC. -00-20-20 (hex) MEGATRON COMPUTER INDUSTRIES PTY, LTD. -00-20-21 (hex) ALGORITHMS SOFTWARE PVT. LTD. -00-20-22 (hex) NMS Communications -00-20-23 (hex) T.C. TECHNOLOGIES PTY. LTD -00-20-24 (hex) PACIFIC COMMUNICATION SCIENCES -00-20-25 (hex) CONTROL TECHNOLOGY, INC. -00-20-26 (hex) AMKLY SYSTEMS, INC. -00-20-27 (hex) MING FORTUNE INDUSTRY CO., LTD -00-20-28 (hex) WEST EGG SYSTEMS, INC. -00-20-29 (hex) TELEPROCESSING PRODUCTS, INC. -00-20-2A (hex) N.V. DZINE -00-20-2B (hex) ADVANCED TELECOMMUNICATIONS MODULES, LTD. -00-20-2C (hex) WELLTRONIX CO., LTD. -00-20-2D (hex) TAIYO CORPORATION -00-20-2E (hex) DAYSTAR DIGITAL -00-20-2F (hex) ZETA COMMUNICATIONS, LTD. -00-20-30 (hex) ANALOG & DIGITAL SYSTEMS -00-20-31 (hex) ERTEC GmbH -00-20-32 (hex) ALCATEL TAISEL -00-20-33 (hex) SYNAPSE TECHNOLOGIES, INC. -00-20-34 (hex) ROTEC INDUSTRIEAUTOMATION GMBH -00-20-35 (hex) IBM CORPORATION -00-20-36 (hex) BMC SOFTWARE -00-20-37 (hex) SEAGATE TECHNOLOGY -00-20-38 (hex) VME MICROSYSTEMS INTERNATIONAL CORPORATION -00-20-39 (hex) SCINETS -00-20-3A (hex) DIGITAL BI0METRICS INC. -00-20-3B (hex) WISDM LTD. -00-20-3C (hex) EUROTIME AB -00-20-3D (hex) Honeywell ECC -00-20-3E (hex) LogiCan Technologies, Inc. -00-20-3F (hex) JUKI CORPORATION -00-20-40 (hex) Motorola Broadband Communications Sector -00-20-41 (hex) DATA NET -00-20-42 (hex) DATAMETRICS CORP. -00-20-43 (hex) NEURON COMPANY LIMITED -00-20-44 (hex) GENITECH PTY LTD -00-20-45 (hex) ION Networks, Inc. -00-20-46 (hex) CIPRICO, INC. -00-20-47 (hex) STEINBRECHER CORP. -00-20-48 (hex) Marconi Communications -00-20-49 (hex) COMTRON, INC. -00-20-4A (hex) PRONET GMBH -00-20-4B (hex) AUTOCOMPUTER CO., LTD. -00-20-4C (hex) MITRON COMPUTER PTE LTD. -00-20-4D (hex) INOVIS GMBH -00-20-4E (hex) NETWORK SECURITY SYSTEMS, INC. -00-20-4F (hex) DEUTSCHE AEROSPACE AG -00-20-50 (hex) KOREA COMPUTER INC. -00-20-51 (hex) Verilink Corporation -00-20-52 (hex) RAGULA SYSTEMS -00-20-53 (hex) HUNTSVILLE MICROSYSTEMS, INC. -00-20-54 (hex) Sycamore Networks -00-20-55 (hex) ALTECH CO., LTD. -00-20-56 (hex) NEOPRODUCTS -00-20-57 (hex) TITZE DATENTECHNIK GmbH -00-20-58 (hex) ALLIED SIGNAL INC. -00-20-59 (hex) MIRO COMPUTER PRODUCTS AG -00-20-5A (hex) COMPUTER IDENTICS -00-20-5B (hex) Kentrox, LLC -00-20-5C (hex) InterNet Systems of Florida, Inc. -00-20-5D (hex) NANOMATIC OY -00-20-5E (hex) CASTLE ROCK, INC. -00-20-5F (hex) GAMMADATA COMPUTER GMBH -00-20-60 (hex) ALCATEL ITALIA S.p.A. -00-20-61 (hex) GarrettCom, Inc. -00-20-62 (hex) SCORPION LOGIC, LTD. -00-20-63 (hex) WIPRO INFOTECH LTD. -00-20-64 (hex) PROTEC MICROSYSTEMS, INC. -00-20-65 (hex) SUPERNET NETWORKING INC. -00-20-66 (hex) GENERAL MAGIC, INC. -00-20-67 (hex) PRIVATE -00-20-68 (hex) ISDYNE -00-20-69 (hex) ISDN SYSTEMS CORPORATION -00-20-6A (hex) OSAKA COMPUTER CORP. -00-20-6B (hex) KONICA MINOLTA HOLDINGS, INC. -00-20-6C (hex) EVERGREEN TECHNOLOGY CORP. -00-20-6D (hex) DATA RACE, INC. -00-20-6E (hex) XACT, INC. -00-20-6F (hex) FLOWPOINT CORPORATION -00-20-70 (hex) HYNET, LTD. -00-20-71 (hex) IBR GMBH -00-20-72 (hex) WORKLINK INNOVATIONS -00-20-73 (hex) FUSION SYSTEMS CORPORATION -00-20-74 (hex) SUNGWOON SYSTEMS -00-20-75 (hex) MOTOROLA COMMUNICATION ISRAEL -00-20-76 (hex) REUDO CORPORATION -00-20-77 (hex) KARDIOS SYSTEMS CORP. -00-20-78 (hex) RUNTOP, INC. -00-20-79 (hex) MIKRON GMBH -00-20-7A (hex) WiSE Communications, Inc. -00-20-7B (hex) Intel Corporation -00-20-7C (hex) AUTEC GmbH -00-20-7D (hex) ADVANCED COMPUTER APPLICATIONS -00-20-7E (hex) FINECOM Co., Ltd. -00-20-7F (hex) KYOEI SANGYO CO., LTD. -00-20-80 (hex) SYNERGY (UK) LTD. -00-20-81 (hex) TITAN ELECTRONICS -00-20-82 (hex) ONEAC CORPORATION -00-20-83 (hex) PRESTICOM INCORPORATED -00-20-84 (hex) OCE PRINTING SYSTEMS, GMBH -00-20-85 (hex) EXIDE ELECTRONICS -00-20-86 (hex) MICROTECH ELECTRONICS LIMITED -00-20-87 (hex) MEMOTEC, INC. -00-20-88 (hex) GLOBAL VILLAGE COMMUNICATION -00-20-89 (hex) T3PLUS NETWORKING, INC. -00-20-8A (hex) SONIX COMMUNICATIONS, LTD. -00-20-8B (hex) LAPIS TECHNOLOGIES, INC. -00-20-8C (hex) GALAXY NETWORKS, INC. -00-20-8D (hex) CMD TECHNOLOGY -00-20-8E (hex) CHEVIN SOFTWARE ENG. LTD. -00-20-8F (hex) ECI TELECOM LTD. -00-20-90 (hex) ADVANCED COMPRESSION TECHNOLOGY, INC. -00-20-91 (hex) J125, NATIONAL SECURITY AGENCY -00-20-92 (hex) CHESS ENGINEERING B.V. -00-20-93 (hex) LANDINGS TECHNOLOGY CORP. -00-20-94 (hex) CUBIX CORPORATION -00-20-95 (hex) RIVA ELECTRONICS -00-20-96 (hex) Invensys -00-20-97 (hex) APPLIED SIGNAL TECHNOLOGY -00-20-98 (hex) HECTRONIC AB -00-20-99 (hex) BON ELECTRIC CO., LTD. -00-20-9A (hex) THE 3DO COMPANY -00-20-9B (hex) ERSAT ELECTRONIC GMBH -00-20-9C (hex) PRIMARY ACCESS CORP. -00-20-9D (hex) LIPPERT AUTOMATIONSTECHNIK -00-20-9E (hex) BROWN'S OPERATING SYSTEM SERVICES, LTD. -00-20-9F (hex) MERCURY COMPUTER SYSTEMS, INC. -00-20-A0 (hex) OA LABORATORY CO., LTD. -00-20-A1 (hex) DOVATRON -00-20-A2 (hex) GALCOM NETWORKING LTD. -00-20-A3 (hex) DIVICOM INC. -00-20-A4 (hex) MULTIPOINT NETWORKS -00-20-A5 (hex) API ENGINEERING -00-20-A6 (hex) PROXIM, INC. -00-20-A7 (hex) PAIRGAIN TECHNOLOGIES, INC. -00-20-A8 (hex) SAST TECHNOLOGY CORP. -00-20-A9 (hex) WHITE HORSE INDUSTRIAL -00-20-AA (hex) DIGIMEDIA VISION LTD. -00-20-AB (hex) MICRO INDUSTRIES CORP. -00-20-AC (hex) INTERFLEX DATENSYSTEME GMBH -00-20-AD (hex) LINQ SYSTEMS -00-20-AE (hex) ORNET DATA COMMUNICATION TECH. -00-20-AF (hex) 3COM CORPORATION -00-20-B0 (hex) GATEWAY DEVICES, INC. -00-20-B1 (hex) COMTECH RESEARCH INC. -00-20-B2 (hex) GKD Gesellschaft Fur Kommunikation Und Datentechnik -00-20-B3 (hex) SCLTEC COMMUNICATIONS SYSTEMS -00-20-B4 (hex) TERMA ELEKTRONIK AS -00-20-B5 (hex) YASKAWA ELECTRIC CORPORATION -00-20-B6 (hex) AGILE NETWORKS, INC. -00-20-B7 (hex) NAMAQUA COMPUTERWARE -00-20-B8 (hex) PRIME OPTION, INC. -00-20-B9 (hex) METRICOM, INC. -00-20-BA (hex) CENTER FOR HIGH PERFORMANCE -00-20-BB (hex) ZAX CORPORATION -00-20-BC (hex) Long Reach Networks Pty Ltd -00-20-BD (hex) NIOBRARA R & D CORPORATION -00-20-BE (hex) LAN ACCESS CORP. -00-20-BF (hex) AEHR TEST SYSTEMS -00-20-C0 (hex) PULSE ELECTRONICS, INC. -00-20-C1 (hex) SAXA, Inc. -00-20-C2 (hex) TEXAS MEMORY SYSTEMS, INC. -00-20-C3 (hex) COUNTER SOLUTIONS LTD. -00-20-C4 (hex) INET,INC. -00-20-C5 (hex) EAGLE TECHNOLOGY -00-20-C6 (hex) NECTEC -00-20-C7 (hex) AKAI Professional M.I. Corp. -00-20-C8 (hex) LARSCOM INCORPORATED -00-20-C9 (hex) VICTRON BV -00-20-CA (hex) DIGITAL OCEAN -00-20-CB (hex) PRETEC ELECTRONICS CORP. -00-20-CC (hex) DIGITAL SERVICES, LTD. -00-20-CD (hex) HYBRID NETWORKS, INC. -00-20-CE (hex) LOGICAL DESIGN GROUP, INC. -00-20-CF (hex) TEST & MEASUREMENT SYSTEMS INC -00-20-D0 (hex) VERSALYNX CORPORATION -00-20-D1 (hex) MICROCOMPUTER SYSTEMS (M) SDN. -00-20-D2 (hex) RAD DATA COMMUNICATIONS, LTD. -00-20-D3 (hex) OST (OUEST STANDARD TELEMATIQU -00-20-D4 (hex) CABLETRON - ZEITTNET INC. -00-20-D5 (hex) VIPA GMBH -00-20-D6 (hex) BREEZECOM -00-20-D7 (hex) JAPAN MINICOMPUTER SYSTEMS CO., Ltd. -00-20-D8 (hex) Nortel Networks -00-20-D9 (hex) PANASONIC TECHNOLOGIES, INC./MIECO-US -00-20-DA (hex) Alcatel North America ESD -00-20-DB (hex) XNET TECHNOLOGY, INC. -00-20-DC (hex) DENSITRON TAIWAN LTD. -00-20-DD (hex) Cybertec Pty Ltd -00-20-DE (hex) JAPAN DIGITAL LABORAT'Y CO.LTD -00-20-DF (hex) KYOSAN ELECTRIC MFG. CO., LTD. -00-20-E0 (hex) Actiontec Electronics, Inc. -00-20-E1 (hex) ALAMAR ELECTRONICS -00-20-E2 (hex) INFORMATION RESOURCE ENGINEERING -00-20-E3 (hex) MCD KENCOM CORPORATION -00-20-E4 (hex) HSING TECH ENTERPRISE CO., LTD -00-20-E5 (hex) APEX DATA, INC. -00-20-E6 (hex) LIDKOPING MACHINE TOOLS AB -00-20-E7 (hex) B&W NUCLEAR SERVICE COMPANY -00-20-E8 (hex) DATATREK CORPORATION -00-20-E9 (hex) DANTEL -00-20-EA (hex) EFFICIENT NETWORKS, INC. -00-20-EB (hex) CINCINNATI MICROWAVE, INC. -00-20-EC (hex) TECHWARE SYSTEMS CORP. -00-20-ED (hex) GIGA-BYTE TECHNOLOGY CO., LTD. -00-20-EE (hex) GTECH CORPORATION -00-20-EF (hex) USC CORPORATION -00-20-F0 (hex) UNIVERSAL MICROELECTRONICS CO. -00-20-F1 (hex) ALTOS INDIA LIMITED -00-20-F2 (hex) SUN MICROSYSTEMS, INC. -00-20-F3 (hex) RAYNET CORPORATION -00-20-F4 (hex) SPECTRIX CORPORATION -00-20-F5 (hex) PANDATEL AG -00-20-F6 (hex) NET TEK AND KARLNET, INC. -00-20-F7 (hex) CYBERDATA -00-20-F8 (hex) CARRERA COMPUTERS, INC. -00-20-F9 (hex) PARALINK NETWORKS, INC. -00-20-FA (hex) GDE SYSTEMS, INC. -00-20-FB (hex) OCTEL COMMUNICATIONS CORP. -00-20-FC (hex) MATROX -00-20-FD (hex) ITV TECHNOLOGIES, INC. -00-20-FE (hex) TOPWARE INC. / GRAND COMPUTER -00-20-FF (hex) SYMMETRICAL TECHNOLOGIES -00-21-00 (hex) GemTek Technology Co., Ltd. -00-21-01 (hex) Aplicaciones Electronicas Quasar (AEQ) -00-21-02 (hex) UpdateLogic Inc. -00-21-03 (hex) GHI Electronics, LLC -00-21-04 (hex) Gigaset Communications GmbH -00-21-05 (hex) Alcatel-Lucent -00-21-06 (hex) RIM Testing Services -00-21-07 (hex) Seowonintech Co Ltd. -00-21-08 (hex) Nokia Danmark A/S -00-21-09 (hex) Nokia Danmark A/S -00-21-0A (hex) byd:sign Corporation -00-21-0B (hex) GEMINI TRAZE RFID PVT. LTD. -00-21-0C (hex) Cymtec Systems, Inc. -00-21-0D (hex) SAMSIN INNOTEC -00-21-0E (hex) Orpak Systems L.T.D. -00-21-0F (hex) Cernium Corp -00-21-10 (hex) Clearbox Systems -00-21-11 (hex) Uniphone Inc. -00-21-12 (hex) WISCOM SYSTEM CO.,LTD -00-21-13 (hex) Padtec S/A -00-21-14 (hex) Hylab Technology Inc. -00-21-15 (hex) PHYWE Systeme GmbH & Co. KG -00-21-16 (hex) Transcon Electronic Systems, spol. s r. o. -00-21-17 (hex) Tellord -00-21-18 (hex) Athena Tech, Inc. -00-21-19 (hex) Samsung Electro-Mechanics -00-21-1A (hex) LInTech Corporation -00-21-1B (hex) Cisco Systems -00-21-1C (hex) Cisco Systems -00-21-1D (hex) Dataline AB -00-21-1E (hex) Motorola CHS -00-21-1F (hex) SHINSUNG DELTATECH CO.,LTD. -00-21-20 (hex) Sequel Technologies, LLC -00-21-21 (hex) VRmagic GmbH -00-21-22 (hex) Chip-pro Ltd. -00-21-23 (hex) Aerosat Avionics -00-21-24 (hex) Optos Plc -00-21-25 (hex) KUK JE TONG SHIN Co.,LTD -00-21-26 (hex) Shenzhen Torch Equipment Co., Ltd. -00-21-27 (hex) TP-LINK Technology Co., Ltd. -00-21-28 (hex) Sun Microsystems Inc -00-21-29 (hex) Cisco-Linksys, LLC -00-21-2A (hex) Audiovox Corporation -00-21-2B (hex) MSA Auer -00-21-2C (hex) SemIndia System Private Limited -00-21-2D (hex) SCIMOLEX CORPORATION -00-21-2E (hex) dresden-elektronik -00-21-2F (hex) Phoebe Micro Inc. -00-21-30 (hex) Keico Hightech Inc. -00-21-31 (hex) Blynke Inc. -00-21-32 (hex) Masterclock, Inc. -00-21-33 (hex) Building B, Inc -00-21-34 (hex) Brandywine Communications -00-21-35 (hex) ALCATEL-LUCENT -00-21-36 (hex) Motorola Mobile Devices business (MDb) -00-21-37 (hex) Bay Controls, LLC -00-21-38 (hex) Cepheid -00-21-39 (hex) Escherlogic Inc. -00-21-3A (hex) Winchester Systems Inc. -00-21-3B (hex) Berkshire Products, Inc -00-21-3C (hex) AliphCom -00-21-3D (hex) Cermetek Microelectronics, Inc. -00-21-3E (hex) TomTom -00-21-3F (hex) A-Team Technology Ltd. -00-21-40 (hex) EN Technologies Inc. -00-21-41 (hex) RADLIVE -00-21-42 (hex) Advanced Control Systems doo -00-21-43 (hex) Motorola CHS -00-21-44 (hex) SS Telecoms -00-21-45 (hex) Semptian Technologies Ltd. -00-21-46 (hex) SCI Technology -00-21-47 (hex) Nintendo Co., Ltd. -00-21-48 (hex) Kaco Solar Korea -00-21-49 (hex) China Daheng Group ,Inc. -00-21-4A (hex) Pixel Velocity, Inc -00-21-4B (hex) Shenzhen HAMP Science & Technology Co.,Ltd -00-21-4C (hex) SAMSUNG ELECTRONICS CO., LTD. -00-21-4D (hex) Guangzhou Skytone Transmission Technology Com. Ltd. -00-21-4E (hex) GS Yuasa Power Supply Ltd. -00-21-4F (hex) ALPS Electric Co., Ltd -00-21-50 (hex) EYEVIEW ELECTRONICS -00-21-51 (hex) Millinet Co., Ltd. -00-21-52 (hex) General Satellite Trading Limited -00-21-53 (hex) SeaMicro Inc. -00-21-54 (hex) D-TACQ Solutions Ltd -00-21-55 (hex) Cisco Systems -00-21-56 (hex) Cisco Systems -00-21-57 (hex) National Datacast, Inc. -00-21-58 (hex) Style Flying Technology Co. -00-21-59 (hex) Juniper Networks -00-21-5A (hex) Hewlett Packard -00-21-5B (hex) Inotive -00-21-5C (hex) Intel Corporate -00-21-5D (hex) Intel Corporate -00-21-5E (hex) IBM -00-21-5F (hex) IHSE GmbH -00-21-60 (hex) Hidea Solutions Co. Ltd. -00-21-61 (hex) Yournet Inc. -00-21-62 (hex) Nortel -00-21-63 (hex) ASKEY COMPUTER CORP -00-21-64 (hex) Special Design Bureau for Seismic Instrumentation -00-21-65 (hex) Presstek Inc. -00-21-66 (hex) NovAtel Inc. -00-21-67 (hex) HWA JIN T&I Corp. -00-21-68 (hex) iVeia, LLC -00-21-69 (hex) Prologix, LLC. -00-21-6A (hex) Intel Corporate -00-21-6B (hex) Intel Corporate -00-21-6C (hex) ODVA -00-21-6D (hex) Soltech Co., Ltd. -00-21-6E (hex) Function ATI (Huizhou) Telecommunications Co., Ltd. -00-21-6F (hex) SymCom, Inc. -00-21-70 (hex) Dell Inc -00-21-71 (hex) Wesung TNC Co., Ltd. -00-21-72 (hex) Seoultek Valley -00-21-73 (hex) Ion Torrent Systems, Inc. -00-21-74 (hex) AvaLAN Wireless -00-21-75 (hex) Pacific Satellite International Ltd. -00-21-76 (hex) YMax Telecom Ltd. -00-21-77 (hex) W. L. Gore & Associates -00-21-78 (hex) Matuschek Messtechnik GmbH -00-21-79 (hex) IOGEAR, Inc. -00-21-7A (hex) Sejin Electron, Inc. -00-21-7B (hex) Bastec AB -00-21-7C (hex) 2Wire -00-21-7D (hex) PYXIS S.R.L. -00-21-7E (hex) Telit Communication s.p.a -00-21-7F (hex) Intraco Technology Pte Ltd -00-21-80 (hex) Motorola CHS -00-21-81 (hex) Si2 Microsystems Limited -00-21-82 (hex) SandLinks Systems, Ltd. -00-21-83 (hex) VATECH HYDRO -00-21-84 (hex) POWERSOFT SRL -00-21-85 (hex) MICRO-STAR INT'L CO.,LTD. -00-21-86 (hex) USI -00-21-87 (hex) Imacs GmbH -00-21-88 (hex) Data Domain, Inc. -00-21-89 (hex) AppTech, Inc. -00-21-8A (hex) Electronic Design and Manufacturing Company -00-21-8B (hex) Wescon Technology, Inc. -00-21-8C (hex) TopControl GMBH -00-21-8D (hex) AP Router Ind. Eletronica LTDA -00-21-8E (hex) MEKICS CO., LTD. -00-21-8F (hex) Avantgarde Acoustic Lautsprechersysteme GmbH -00-21-90 (hex) Goliath Solutions -00-21-91 (hex) D-Link Corporation -00-21-92 (hex) Baoding Galaxy Electronic Technology Co.,Ltd -00-21-93 (hex) Videofon MV -00-21-94 (hex) Ping Communication -00-21-95 (hex) GWD Media Limited -00-21-96 (hex) Telsey S.p.A. -00-21-97 (hex) ELITEGROUP COMPUTER SYSTEM -00-21-98 (hex) Thai Radio Co, LTD -00-21-99 (hex) Vacon Plc -00-21-9A (hex) Cambridge Visual Networks Ltd -00-21-9B (hex) Dell Inc -00-21-9C (hex) Honeywld Technology Corp. -00-21-9D (hex) Adesys BV -00-21-9E (hex) Sony Ericsson Mobile Communications -00-21-9F (hex) SATEL OY -00-21-A0 (hex) Cisco Systems -00-21-A1 (hex) Cisco Systems -00-21-A2 (hex) EKE-Electronics Ltd. -00-21-A3 (hex) Micromint -00-21-A4 (hex) Dbii Networks -00-21-A5 (hex) ERLPhase Power Technologies Ltd. -00-21-A6 (hex) Videotec Spa -00-21-A7 (hex) Hantle System Co., Ltd. -00-21-A8 (hex) Telephonics Corporation -00-21-A9 (hex) Mobilink Telecom Co.,Ltd -00-21-AA (hex) Nokia Danmark A/S -00-21-AB (hex) Nokia Danmark A/S -00-21-AC (hex) Infrared Integrated Systems Ltd -00-21-AD (hex) Nordic ID Oy -00-21-AE (hex) ALCATEL-LUCENT FRANCE - WTD -00-21-AF (hex) Radio Frequency Systems -00-21-B0 (hex) Tyco Telecommunications -00-21-B1 (hex) DIGITAL SOLUTIONS LTD -00-21-B2 (hex) Fiberblaze A/S -00-21-B3 (hex) Ross Controls -00-21-B4 (hex) APRO MEDIA CO., LTD -00-21-B5 (hex) Vyro Games Limited -00-21-B6 (hex) Triacta Power Technologies Inc. -00-21-B7 (hex) Lexmark International Inc. -00-21-B8 (hex) Inphi Corporation -00-21-B9 (hex) Universal Devices Inc. -00-21-BA (hex) Texas Instruments -00-21-BB (hex) Riken Keiki Co., Ltd. -00-21-BC (hex) ZALA COMPUTER -00-21-BD (hex) Nintendo Co., Ltd. -00-21-BE (hex) Cisco, Service Provider Video Technology Group -00-21-BF (hex) Hitachi High-Tech Control Systems Corporation -00-21-C0 (hex) Mobile Appliance, Inc. -00-21-C1 (hex) ABB Oy / Distribution Automation -00-21-C2 (hex) GL Communications Inc -00-21-C3 (hex) CORNELL Communications, Inc. -00-21-C4 (hex) Consilium AB -00-21-C5 (hex) 3DSP Corp -00-21-C6 (hex) CSJ Global, Inc. -00-21-C7 (hex) Russound -00-21-C8 (hex) LOHUIS Networks -00-21-C9 (hex) Wavecom Asia Pacific Limited -00-21-CA (hex) ART System Co., Ltd. -00-21-CB (hex) SMS TECNOLOGIA ELETRONICA LTDA -00-21-CC (hex) Flextronics International -00-21-CD (hex) LiveTV -00-21-CE (hex) NTC-Metrotek -00-21-CF (hex) The Crypto Group -00-21-D0 (hex) Global Display Solutions Spa -00-21-D1 (hex) Samsung Electronics Co.,Ltd -00-21-D2 (hex) Samsung Electronics Co.,Ltd -00-21-D3 (hex) BOCOM SECURITY(ASIA PACIFIC) LIMITED -00-21-D4 (hex) Vollmer Werke GmbH -00-21-D5 (hex) X2E GmbH -00-21-D6 (hex) LXI Consortium -00-21-D7 (hex) Cisco Systems -00-21-D8 (hex) Cisco Systems -00-21-D9 (hex) SEKONIC CORPORATION -00-21-DA (hex) Automation Products Group Inc. -00-21-DB (hex) Santachi Video Technology (Shenzhen) Co., Ltd. -00-21-DC (hex) TECNOALARM S.r.l. -00-21-DD (hex) Northstar Systems Corp -00-21-DE (hex) Firepro Wireless -00-21-DF (hex) Martin Christ GmbH -00-21-E0 (hex) CommAgility Ltd -00-21-E1 (hex) Nortel Networks -00-21-E2 (hex) Creative Electronic GmbH -00-21-E3 (hex) SerialTek LLC -00-21-E4 (hex) I-WIN -00-21-E5 (hex) Display Solution AG -00-21-E6 (hex) Starlight Video Limited -00-21-E7 (hex) Informatics Services Corporation -00-21-E8 (hex) Murata Manufacturing Co., Ltd. -00-21-E9 (hex) Apple, Inc -00-21-EA (hex) Bystronic Laser AG -00-21-EB (hex) ESP SYSTEMS, LLC -00-21-EC (hex) Solutronic GmbH -00-21-ED (hex) Telegesis -00-21-EE (hex) Full Spectrum Inc. -00-21-EF (hex) Kapsys -00-21-F0 (hex) EW3 Technologies LLC -00-21-F1 (hex) Tutus Data AB -00-21-F2 (hex) EASY3CALL Technology Limited -00-21-F3 (hex) Si14 SpA -00-21-F4 (hex) INRange Systems, Inc -00-21-F5 (hex) Western Engravers Supply, Inc. -00-21-F6 (hex) Virtual Iron Software -00-21-F7 (hex) ProCurve Networking by HP -00-21-F8 (hex) Enseo, Inc. -00-21-F9 (hex) WIRECOM Technologies -00-21-FA (hex) A4SP Technologies Ltd. -00-21-FB (hex) LG Electronics -00-21-FC (hex) Nokia Danmark A/S -00-21-FD (hex) DSTA S.L. -00-21-FE (hex) Nokia Danmark A/S -00-21-FF (hex) Cyfrowy Polsat SA -00-22-00 (hex) BLADE Network Technology -00-22-01 (hex) Aksys Networks Inc -00-22-02 (hex) Excito Elektronik i Skne AB -00-22-03 (hex) Glensound Electronics Ltd -00-22-04 (hex) KORATEK -00-22-05 (hex) WeLink Solutions, Inc. -00-22-06 (hex) Cyberdyne Inc. -00-22-07 (hex) Inteno Broadband Technology AB -00-22-08 (hex) Certicom Corp -00-22-09 (hex) Omron Healthcare Co., Ltd -00-22-0A (hex) Rearden Labs -00-22-0B (hex) National Source Coding Center -00-22-0C (hex) Cisco Systems -00-22-0D (hex) Cisco Systems -00-22-0E (hex) Indigo Security Co., Ltd. -00-22-0F (hex) MoCA (Multimedia over Coax Alliance) -00-22-10 (hex) Motorola CHS -00-22-11 (hex) Rohati Systems -00-22-12 (hex) CAI Networks, Inc. -00-22-13 (hex) PCI CORPORATION -00-22-14 (hex) RINNAI KOREA -00-22-15 (hex) ASUSTek COMPUTER INC. -00-22-16 (hex) SHIBAURA VENDING MACHINE CORPORATION -00-22-17 (hex) Neat Electronics -00-22-18 (hex) Verivue Inc. -00-22-19 (hex) Dell Inc -00-22-1A (hex) Audio Precision -00-22-1B (hex) Morega Systems -00-22-1C (hex) PRIVATE -00-22-1D (hex) Freegene Technology LTD -00-22-1E (hex) Media Devices Co., Ltd. -00-22-1F (hex) eSang Technologies Co., Ltd. -00-22-20 (hex) Mitac Technology Corp -00-22-21 (hex) ITOH DENKI CO,LTD. -00-22-22 (hex) Betec Engineering GmbH -00-22-23 (hex) TimeKeeping Systems, Inc. -00-22-24 (hex) Good Will Instrument Co., Ltd. -00-22-25 (hex) Thales Avionics Ltd -00-22-26 (hex) Avaak, Inc. -00-22-27 (hex) uv-electronic GmbH -00-22-28 (hex) Breeze Innovations Ltd. -00-22-29 (hex) Compumedics Ltd -00-22-2A (hex) SoundEar A/S -00-22-2B (hex) Nucomm, Inc. -00-22-2C (hex) Ceton Corp -00-22-2D (hex) SMC Networks Inc. -00-22-2E (hex) maintech GmbH -00-22-2F (hex) Open Grid Computing, Inc. -00-22-30 (hex) FutureLogic Inc. -00-22-31 (hex) SMT&C Co., Ltd. -00-22-32 (hex) Design Design Technology Ltd -00-22-33 (hex) Pirelli Broadband Solutions -00-22-34 (hex) Corventis Inc. -00-22-35 (hex) Strukton Systems bv -00-22-36 (hex) VECTOR SP. Z O.O. -00-22-37 (hex) Shinhint Group -00-22-38 (hex) LOGIPLUS -00-22-39 (hex) Indiana Life Sciences Incorporated -00-22-3A (hex) Scientific Atlanta, Cisco SPVT Group -00-22-3B (hex) Communication Networks, LLC -00-22-3C (hex) RATIO Entwicklungen GmbH -00-22-3D (hex) JumpGen Systems, LLC -00-22-3E (hex) IRTrans GmbH -00-22-3F (hex) Netgear Inc. -00-22-40 (hex) Universal Telecom S/A -00-22-41 (hex) Apple, Inc -00-22-42 (hex) Alacron Inc. -00-22-43 (hex) AzureWave Technologies, Inc. -00-22-44 (hex) Chengdu Linkon Communications Device Co., Ltd -00-22-45 (hex) Leine & Linde AB -00-22-46 (hex) Evoc Intelligent Technology Co.,Ltd. -00-22-47 (hex) DAC ENGINEERING CO., LTD. -00-22-48 (hex) Microsoft Corporation -00-22-49 (hex) HOME MULTIENERGY SL -00-22-4A (hex) RAYLASE AG -00-22-4B (hex) AIRTECH TECHNOLOGIES, INC. -00-22-4C (hex) Nintendo Co., Ltd. -00-22-4D (hex) MITAC INTERNATIONAL CORP. -00-22-4E (hex) SEEnergy Corp. -00-22-4F (hex) Byzoro Networks Ltd. -00-22-50 (hex) Point Six Wireless, LLC -00-22-51 (hex) Lumasense Technologies -00-22-52 (hex) ZOLL Lifecor Corporation -00-22-53 (hex) Entorian Technologies -00-22-54 (hex) Bigelow Aerospace -00-22-55 (hex) Cisco Systems -00-22-56 (hex) Cisco Systems -00-22-57 (hex) 3Com Europe Ltd -00-22-58 (hex) Taiyo Yuden Co., Ltd. -00-22-59 (hex) Guangzhou New Postcom Equipment Co.,Ltd. -00-22-5A (hex) Garde Security AB -00-22-5B (hex) Teradici Corporation -00-22-5C (hex) Multimedia & Communication Technology -00-22-5D (hex) Digicable Network India Pvt. Ltd. -00-22-5E (hex) Uwin Technologies Co.,LTD -00-22-5F (hex) Liteon Technology Corporation -00-22-60 (hex) AFREEY Inc. -00-22-61 (hex) Frontier Silicon Ltd -00-22-62 (hex) BEP Marine -00-22-63 (hex) Koos Technical Services, Inc. -00-22-64 (hex) Hewlett Packard -00-22-65 (hex) Nokia Danmark A/S -00-22-66 (hex) Nokia Danmark A/S -00-22-67 (hex) Nortel Networks -00-22-68 (hex) Hon Hai Precision Ind. Co., Ltd. -00-22-69 (hex) Hon Hai Precision Ind. Co., Ltd. -00-22-6A (hex) Honeywell -00-22-6B (hex) Cisco-Linksys, LLC -00-22-6C (hex) LinkSprite Technologies, Inc. -00-22-6D (hex) Shenzhen GIEC Electronics Co., Ltd. -00-22-6E (hex) Gowell Electronic Limited -00-22-6F (hex) 3onedata Technology Co. Ltd. -00-22-70 (hex) ABK North America, LLC -00-22-71 (hex) Jger Computergesteuerte Messtechnik GmbH -00-22-72 (hex) American Micro-Fuel Device Corp. -00-22-73 (hex) Techway -00-22-74 (hex) FamilyPhone AB -00-22-75 (hex) Belkin International, Inc. -00-22-76 (hex) Triple EYE B.V. -00-22-77 (hex) NEC Australia Pty Ltd -00-22-78 (hex) Shenzhen Tongfang Multimedia Technology Co.,Ltd. -00-22-79 (hex) Nippon Conlux Co., Ltd. -00-22-7A (hex) Telecom Design -00-22-7B (hex) Apogee Labs, Inc. -00-22-7C (hex) Woori SMT Co.,ltd -00-22-7D (hex) YE DATA INC. -00-22-7E (hex) Chengdu 30Kaitian Communication Industry Co.Ltd -00-22-7F (hex) Ruckus Wireless -00-22-80 (hex) A2B Electronics AB -00-22-81 (hex) Daintree Networks Inc -00-22-82 (hex) 8086 Limited -00-22-83 (hex) Juniper Networks -00-22-84 (hex) DESAY A&V SCIENCE AND TECHNOLOGY CO.,LTD -00-22-85 (hex) NOMUS COMM SYSTEMS -00-22-86 (hex) ASTRON -00-22-87 (hex) Titan Wireless LLC -00-22-88 (hex) Sagrad, Inc. -00-22-89 (hex) Optosecurity Inc. -00-22-8A (hex) Teratronik elektronische systeme gmbh -00-22-8B (hex) Kensington Computer Products Group -00-22-8C (hex) Photon Europe GmbH -00-22-8D (hex) GBS Laboratories LLC -00-22-8E (hex) TV-NUMERIC -00-22-8F (hex) CNRS -00-22-90 (hex) Cisco Systems -00-22-91 (hex) Cisco Systems -00-22-92 (hex) Cinetal -00-22-93 (hex) ZTE Corporation -00-22-94 (hex) Kyocera Corporation -00-22-95 (hex) SGM Technology for lighting spa -00-22-96 (hex) LinoWave Corporation -00-22-97 (hex) XMOS Semiconductor -00-22-98 (hex) Sony Ericsson Mobile Communications -00-22-99 (hex) SeaMicro Inc. -00-22-9A (hex) Lastar, Inc. -00-22-9B (hex) AverLogic Technologies, Inc. -00-22-9C (hex) Verismo Networks Inc -00-22-9D (hex) PYUNG-HWA IND.CO.,LTD -00-22-9E (hex) Social Aid Research Co., Ltd. -00-22-9F (hex) Sensys Traffic AB -00-22-A0 (hex) Delphi Corporation -00-22-A1 (hex) Huawei Symantec Technologies Co.,Ltd. -00-22-A2 (hex) Xtramus Technologies -00-22-A3 (hex) California Eastern Laboratories -00-22-A4 (hex) 2Wire -00-22-A5 (hex) Texas Instruments -00-22-A6 (hex) Sony Computer Entertainment America -00-22-A7 (hex) Tyco Electronics AMP GmbH -00-22-A8 (hex) Ouman Finland Oy -00-22-A9 (hex) LG Electronics Inc -00-22-AA (hex) Nintendo Co., Ltd. -00-22-AB (hex) Shenzhen Turbosight Technology Ltd -00-22-AC (hex) Hangzhou Siyuan Tech. Co., Ltd -00-22-AD (hex) TELESIS TECHNOLOGIES, INC. -00-22-AE (hex) Mattel Inc. -00-22-AF (hex) Safety Vision -00-22-B0 (hex) D-Link Corporation -00-22-B1 (hex) Elbit Systems -00-22-B2 (hex) 4RF Communications Ltd -00-22-B3 (hex) Sei S.p.A. -00-22-B4 (hex) Motorola Mobile Devices -00-22-B5 (hex) NOVITA -00-22-B6 (hex) Superflow Technologies Group -00-22-B7 (hex) GSS Grundig SAT-Systems GmbH -00-22-B8 (hex) Norcott -00-22-B9 (hex) Analogix Seminconductor, Inc -00-22-BA (hex) HUTH Elektronik Systeme GmbH -00-22-BB (hex) beyerdynamic GmbH & Co. KG -00-22-BC (hex) JDSU France SAS -00-22-BD (hex) Cisco Systems -00-22-BE (hex) Cisco Systems -00-22-BF (hex) SieAmp Group of Companies -00-22-C0 (hex) Shenzhen Forcelink Electronic Co, Ltd -00-22-C1 (hex) Active Storage Inc. -00-22-C2 (hex) Proview Eletronica do Brasil LTDA -00-22-C3 (hex) Zeeport Technology Inc. -00-22-C4 (hex) epro GmbH -00-22-C5 (hex) INFORSON Co,Ltd. -00-22-C6 (hex) Sutus Inc -00-22-C7 (hex) SEGGER Microcontroller GmbH & Co. KG -00-22-C8 (hex) Applied Instruments -00-22-C9 (hex) Lenord, Bauer & Co GmbH -00-22-CA (hex) Anviz Biometric Tech. Co., Ltd. -00-22-CB (hex) IONODES Inc. -00-22-CC (hex) SciLog, Inc. -00-22-CD (hex) Ared Technology Co., Ltd. -00-22-CE (hex) Cisco, Service Provider Video Technology Group -00-22-CF (hex) PLANEX Communications INC -00-22-D0 (hex) Polar Electro Oy -00-22-D1 (hex) Albrecht Jung GmbH & Co. KG -00-22-D2 (hex) All Earth Comrcio de Eletrnicos LTDA. -00-22-D3 (hex) Hub-Tech -00-22-D4 (hex) ComWorth Co., Ltd. -00-22-D5 (hex) Eaton Corp. Electrical Group Data Center Solutions - Pulizzi -00-22-D6 (hex) Cypak AB -00-22-D7 (hex) Nintendo Co., Ltd. -00-22-D8 (hex) Shenzhen GST Security and Safety Technology Limited -00-22-D9 (hex) Fortex Industrial Ltd. -00-22-DA (hex) ANATEK, LLC -00-22-DB (hex) Translogic Corporation -00-22-DC (hex) Vigil Health Solutions Inc. -00-22-DD (hex) Protecta Electronics Ltd -00-22-DE (hex) OPPO Digital, Inc. -00-22-DF (hex) TAMUZ Monitors -00-22-E0 (hex) Atlantic Software Technologies S.r.L. -00-22-E1 (hex) ZORT Labs, LLC. -00-22-E2 (hex) WABTEC Transit Division -00-22-E3 (hex) Amerigon -00-22-E4 (hex) APASS TECHNOLOGY CO., LTD. -00-22-E5 (hex) Fisher-Rosemount Systems Inc. -00-22-E6 (hex) Intelligent Data -00-22-E7 (hex) WPS Parking Systems -00-22-E8 (hex) Applition Co., Ltd. -00-22-E9 (hex) ProVision Communications -00-22-EA (hex) Rustelcom Inc. -00-22-EB (hex) Data Respons A/S -00-22-EC (hex) IDEALBT TECHNOLOGY CORPORATION -00-22-ED (hex) TSI Power Corporation -00-22-EE (hex) Algo Communication Products Ltd -00-22-EF (hex) Ibis Tek, LLC -00-22-F0 (hex) 3 Greens Aviation Limited -00-22-F1 (hex) PRIVATE -00-22-F2 (hex) SunPower Corp -00-22-F3 (hex) SHARP CORPORATION -00-22-F4 (hex) AMPAK Technology, Inc. -00-22-F5 (hex) Advanced Realtime Tracking GmbH -00-22-F6 (hex) Syracuse Research Corporation -00-22-F7 (hex) Conceptronic -00-22-F8 (hex) PIMA Electronic Systems Ltd. -00-22-F9 (hex) Pollin Electronic GmbH -00-22-FA (hex) Intel Corporate -00-22-FB (hex) Intel Corporate -00-22-FC (hex) Nokia Danmark A/S -00-22-FD (hex) Nokia Danmark A/S -00-22-FE (hex) Microprocessor Designs Inc -00-22-FF (hex) NIVIS LLC -00-23-00 (hex) Cayee Computer Ltd. -00-23-01 (hex) Witron Technology Limited -00-23-02 (hex) Cobalt Digital, Inc. -00-23-03 (hex) LITE-ON IT Corporation -00-23-04 (hex) Cisco Systems -00-23-05 (hex) Cisco Systems -00-23-06 (hex) ALPS Electric Co., Ltd -00-23-07 (hex) FUTURE INNOVATION TECH CO.,LTD -00-23-08 (hex) Arcadyan Technology Corporation -00-23-09 (hex) Janam Technologies LLC -00-23-0A (hex) ARBURG GmbH & Co KG -00-23-0B (hex) Motorola CHS -00-23-0C (hex) CLOVER ELECTRONICS CO.,LTD. -00-23-0D (hex) Nortel Networks -00-23-0E (hex) Gorba AG -00-23-0F (hex) Hirsch Electronics Corporation -00-23-10 (hex) LNC Technology Co., Ltd. -00-23-11 (hex) Gloscom Co., Ltd. -00-23-12 (hex) Apple, Inc -00-23-13 (hex) Qool Technologies Ltd. -00-23-14 (hex) Intel Corporate -00-23-15 (hex) Intel Corporate -00-23-16 (hex) KISAN ELECTRONICS CO -00-23-17 (hex) Lasercraft Inc -00-23-18 (hex) Toshiba -00-23-19 (hex) Sielox LLC -00-23-1A (hex) ITF Co., Ltd. -00-23-1B (hex) Danaher Motion - Kollmorgen -00-23-1C (hex) Fourier Systems Ltd. -00-23-1D (hex) Deltacom Electronics Ltd -00-23-1E (hex) Cezzer Multimedia Technologies -00-23-1F (hex) Guangda Electronic & Telecommunication Technology Development Co., Ltd. -00-23-20 (hex) Nicira Network -00-23-21 (hex) Avitech International Corp -00-23-22 (hex) KISS Teknical Solutions, Inc. -00-23-23 (hex) Zylin AS -00-23-24 (hex) G-PRO COMPUTER -00-23-25 (hex) IOLAN Holding -00-23-26 (hex) Fujitsu Limited -00-23-27 (hex) Shouyo Electronics CO., LTD -00-23-28 (hex) ALCON TELECOMMUNICATIONS CO., LTD. -00-23-29 (hex) DDRdrive LLC -00-23-2A (hex) eonas IT-Beratung und -Entwicklung GmbH -00-23-2B (hex) IRD A/S -00-23-2C (hex) Senticare -00-23-2D (hex) SandForce -00-23-2E (hex) Kedah Electronics Engineering, LLC -00-23-2F (hex) Advanced Card Systems Ltd. -00-23-30 (hex) DIZIPIA, INC. -00-23-31 (hex) Nintendo Co., Ltd. -00-23-32 (hex) Apple, Inc -00-23-33 (hex) Cisco Systems -00-23-34 (hex) Cisco Systems -00-23-35 (hex) Linkflex Co.,Ltd -00-23-36 (hex) METEL s.r.o. -00-23-37 (hex) Global Star Solutions ULC -00-23-38 (hex) OJ-Electronics A/S -00-23-39 (hex) Samsung Electronics -00-23-3A (hex) Samsung Electronics Co.,Ltd -00-23-3B (hex) C-Matic Systems Ltd -00-23-3C (hex) Alflex -00-23-3D (hex) novero GmbH -00-23-3E (hex) Alcatel-Lucent-IPD -00-23-3F (hex) Purechoice Inc -00-23-40 (hex) MiX Telematics -00-23-41 (hex) Siemens Building Technologies Fire & Security Products GmbH & Co. oHG -00-23-42 (hex) Coffee Equipment Company -00-23-43 (hex) TEM AG -00-23-44 (hex) Objective Interface Systems -00-23-45 (hex) Sony Ericsson Mobile Communications -00-23-46 (hex) Vestac -00-23-47 (hex) ProCurve Networking by HP -00-23-48 (hex) SAGEM COMMUNICATION -00-23-49 (hex) Helmholtz Centre Berlin for Material and Energy -00-23-4A (hex) PRIVATE -00-23-4B (hex) Inyuan Technology Inc. -00-23-4C (hex) KTC AB -00-23-4D (hex) Hon Hai Precision Ind. Co., Ltd. -00-23-4E (hex) Hon Hai Precision Ind. Co., Ltd. -00-23-4F (hex) Luminous Power Technologies Pvt. Ltd. -00-23-50 (hex) LynTec -00-23-51 (hex) 2Wire -00-23-52 (hex) DATASENSOR S.p.A. -00-23-53 (hex) F E T Elettronica snc -00-23-54 (hex) ASUSTek COMPUTER INC. -00-23-55 (hex) Kinco Automation(Shanghai) Ltd. -00-23-56 (hex) Packet Forensics LLC -00-23-57 (hex) Pitronot Technologies and Engineering P.T.E. Ltd. -00-23-58 (hex) SYSTEL SA -00-23-59 (hex) Benchmark Electronics ( Thailand ) Public Company Limited -00-23-5A (hex) COMPAL INFORMATION (KUNSHAN) CO., Ltd. -00-23-5B (hex) Gulfstream -00-23-5C (hex) Aprius, Inc. -00-23-5D (hex) Cisco Systems -00-23-5E (hex) Cisco Systems -00-23-5F (hex) Silicon Micro Sensors GmbH -00-23-60 (hex) Lookit Technology Co., Ltd -00-23-61 (hex) Unigen Corporation -00-23-62 (hex) Goldline Controls -00-23-63 (hex) Zhuhai RaySharp Technology Co., Ltd. -00-23-64 (hex) Power Instruments Pte Ltd -00-23-65 (hex) ELKA-Elektronik GmbH -00-23-66 (hex) Beijing Siasun Electronic System Co.,Ltd. -00-23-67 (hex) UniControls a.s. -00-23-68 (hex) Motorola -00-23-69 (hex) Cisco-Linksys, LLC -00-23-6A (hex) ClearAccess, Inc. -00-23-6B (hex) Xembedded, Inc. -00-23-6C (hex) Apple, Inc -00-23-6D (hex) ResMed Ltd -00-23-6E (hex) Burster GmbH & Co KG -00-23-6F (hex) DAQ System -00-23-70 (hex) PRO-BEL LIMITED -00-23-71 (hex) SOAM Systel -00-23-72 (hex) MORE STAR INDUSTRIAL GROUP LIMITED -00-23-73 (hex) GridIron Systems, Inc. -00-23-74 (hex) Motorola CHS -00-23-75 (hex) Motorola CHS -00-23-76 (hex) HTC Corporation -00-23-77 (hex) Isotek Electronics Ltd -00-23-78 (hex) GN Netcom A/S -00-23-79 (hex) Union Business Machines Co. Ltd. -00-23-7A (hex) RIM -00-23-7B (hex) WHDI LLC -00-23-7C (hex) NEOTION -00-23-7D (hex) Hewlett Packard -00-23-7E (hex) ELSTER GMBH -00-23-7F (hex) PLANTRONICS -00-23-80 (hex) Nanoteq -00-23-81 (hex) Lengda Technology(Xiamen) Co.,Ltd. -00-23-82 (hex) Lih Rong Electronic Enterprise Co., Ltd. -00-23-83 (hex) InMage Systems Inc -00-23-84 (hex) GGH Engineering s.r.l. -00-23-85 (hex) ANTIPODE -00-23-86 (hex) Tour & Andersson AB -00-23-87 (hex) ThinkFlood, Inc. -00-23-88 (hex) V.T. Telematica S.p.a. -00-23-89 (hex) HANGZHOU H3C Technologies Co., Ltd. -00-23-8A (hex) Ciena Corporation -00-23-8B (hex) Quanta Computer Inc. -00-23-8C (hex) PRIVATE -00-23-8D (hex) Techno Design Co., Ltd. -00-23-8E (hex) PIRELLI BROADBAND SOLUTIONS -00-23-8F (hex) NIDEC COPAL CORPORATION -00-23-90 (hex) Algolware Corporation -00-23-91 (hex) Maxian -00-23-92 (hex) Proteus Industries Inc. -00-23-93 (hex) AJINEXTEK -00-23-94 (hex) Samjeon -00-23-95 (hex) Motorola CHS -00-23-96 (hex) ANDES TECHNOLOGY CORPORATION -00-23-97 (hex) Westell Technologies Inc. -00-23-98 (hex) Sky Control -00-23-99 (hex) VD Division, Samsung Electronics Co. -00-23-9A (hex) EasyData Software GmbH -00-23-9B (hex) Elster Integrated Solutions -00-23-9C (hex) Juniper Networks -00-23-9D (hex) Mapower Electronics Co., Ltd -00-23-9E (hex) Jiangsu Lemote Technology Corporation Limited -00-23-9F (hex) Institut fr Prftechnik -00-23-A0 (hex) Hana CNS Co., LTD. -00-23-A1 (hex) Trend Electronics Ltd -00-23-A2 (hex) Motorola CHS -00-23-A3 (hex) Motorola CHS -00-23-A4 (hex) New Concepts Development Corp. -00-23-A5 (hex) SageTV, LLC -00-23-A6 (hex) E-Mon -00-23-A7 (hex) Redpine Signals, Inc. -00-23-A8 (hex) Marshall Electronics -00-23-A9 (hex) Beijing Detianquan Electromechanical Equipment Co., Ltd -00-23-AA (hex) HFR, Inc. -00-23-AB (hex) Cisco Systems -00-23-AC (hex) Cisco Systems -00-23-AD (hex) Xmark Corporation -00-23-AE (hex) Dell Inc. -00-23-AF (hex) Motorola Mobile Devices -00-23-B0 (hex) COMXION Technology Inc. -00-23-B1 (hex) Longcheer Technology (Singapore) Pte Ltd -00-23-B2 (hex) Intelligent Mechatronic Systems Inc -00-23-B3 (hex) Lyyn AB -00-23-B4 (hex) Nokia Danmark A/S -00-23-B5 (hex) ORTANA LTD -00-23-B6 (hex) SECURITE COMMUNICATIONS / HONEYWELL -00-23-B7 (hex) Q-Light Co., Ltd. -00-23-B8 (hex) Sichuan Jiuzhou Electronic Technology Co.,Ltd -00-23-B9 (hex) EADS Deutschland GmbH -00-23-BA (hex) Chroma -00-23-BB (hex) Schmitt Industries -00-23-BC (hex) EQ-SYS GmbH -00-23-BD (hex) Digital Ally, Inc. -00-23-BE (hex) Cisco SPVTG -00-23-BF (hex) Mainpine, Inc. -00-23-C0 (hex) Broadway Networks -00-23-C1 (hex) Securitas Direct AB -00-23-C2 (hex) SAMSUNG Electronics. Co. LTD -00-23-C3 (hex) LogMeIn, Inc. -00-23-C4 (hex) Lux Lumen -00-23-C5 (hex) Radiation Safety and Control Services Inc -00-23-C6 (hex) SMC Corporation -00-23-C7 (hex) AVSystem -00-23-C8 (hex) TEAM-R -00-23-C9 (hex) Sichuan Tianyi Information Science & Technology Stock CO.,LTD -00-23-CA (hex) Behind The Set, LLC -00-23-CB (hex) Shenzhen Full-join Technology Co.,Ltd -00-23-CC (hex) Nintendo Co., Ltd. -00-23-CD (hex) TP-LINK TECHNOLOGIES CO., LTD. -00-23-CE (hex) KITA DENSHI CORPORATION -00-23-CF (hex) CUMMINS-ALLISON CORP. -00-23-D0 (hex) Uniloc USA Inc. -00-23-D1 (hex) TRG -00-23-D2 (hex) Inhand Electronics, Inc. -00-23-D3 (hex) AirLink WiFi Networking Corp. -00-23-D4 (hex) Texas Instruments -00-23-D5 (hex) WAREMA electronic GmbH -00-23-D6 (hex) Samsung Electronics Co.,LTD -00-23-D7 (hex) Samsung Electronics -00-23-D8 (hex) Ball-It Oy -00-23-D9 (hex) Banner Engineering -00-23-DA (hex) Industrial Computer Source (Deutschland)GmbH -00-23-DB (hex) saxnet gmbh -00-23-DC (hex) Benein, Inc -00-23-DD (hex) ELGIN S.A. -00-23-DE (hex) Ansync Inc. -00-23-DF (hex) Apple, Inc -00-23-E0 (hex) INO Therapeutics LLC -00-23-E1 (hex) Cavena Image Products AB -00-23-E2 (hex) SEA Signalisation -00-23-E3 (hex) Microtronic AG -00-23-E4 (hex) IPnect co. ltd. -00-23-E5 (hex) IPaXiom Networks -00-23-E6 (hex) Pirkus, Inc. -00-23-E7 (hex) Hinke A/S -00-23-E8 (hex) Demco Corp. -00-23-E9 (hex) F5 Networks, Inc. -00-23-EA (hex) Cisco Systems -00-23-EB (hex) Cisco Systems -00-23-EC (hex) Algorithmix GmbH -00-23-ED (hex) Motorola CHS -00-23-EE (hex) Motorola CHS -00-23-EF (hex) Zuend Systemtechnik AG -00-23-F0 (hex) Shanghai Jinghan Weighing Apparatus Co. Ltd. -00-23-F1 (hex) Sony Ericsson Mobile Communications -00-23-F2 (hex) TVLogic -00-23-F3 (hex) Glocom, Inc. -00-23-F4 (hex) Masternaut -00-23-F5 (hex) WILO SE -00-23-F6 (hex) Softwell Technology Co., Ltd. -00-23-F7 (hex) PRIVATE -00-23-F8 (hex) ZyXEL Communications Corporation -00-23-F9 (hex) Double-Take Software, INC. -00-23-FA (hex) RG Nets, Inc. -00-23-FB (hex) IP Datatel, Inc. -00-23-FC (hex) Ultra Stereo Labs, Inc -00-23-FD (hex) AFT Atlas Fahrzeugtechnik GmbH -00-23-FE (hex) Biodevices, SA -00-23-FF (hex) Beijing HTTC Technology Ltd. -00-24-00 (hex) Nortel Networks -00-24-01 (hex) D-Link Corporation -00-24-02 (hex) Op-Tection GmbH -00-24-03 (hex) Nokia Danmark A/S -00-24-04 (hex) Nokia Danmark A/S -00-24-05 (hex) Dilog Nordic AB -00-24-06 (hex) Pointmobile -00-24-07 (hex) TELEM SAS -00-24-08 (hex) Pacific Biosciences -00-24-09 (hex) The Toro Company -00-24-0A (hex) US Beverage Net -00-24-0B (hex) Virtual Computer Inc. -00-24-0C (hex) DELEC GmbH -00-24-0D (hex) OnePath Networks LTD. -00-24-0E (hex) Inventec Besta Co., Ltd. -00-24-0F (hex) Ishii Tool & Engineering Corporation -00-24-10 (hex) NUETEQ Technology,Inc. -00-24-11 (hex) PharmaSmart LLC -00-24-12 (hex) Benign Technologies Co, Ltd. -00-24-13 (hex) Cisco Systems -00-24-14 (hex) Cisco Systems -00-24-15 (hex) Magnetic Autocontrol GmbH -00-24-16 (hex) Any Use -00-24-17 (hex) Thomson Telecom Belgium -00-24-18 (hex) Nextwave Semiconductor -00-24-19 (hex) PRIVATE -00-24-1A (hex) Red Beetle Inc. -00-24-1B (hex) iWOW Communications Pte Ltd -00-24-1C (hex) FuGang Electronic (DG) Co.,Ltd -00-24-1D (hex) GIGA-BYTE TECHNOLOGY CO.,LTD. -00-24-1E (hex) Nintendo Co., Ltd. -00-24-1F (hex) DCT-Delta GmbH -00-24-20 (hex) NetUP Inc. -00-24-21 (hex) MICRO-STAR INT'L CO., LTD. -00-24-22 (hex) Knapp Logistik Automation GmbH -00-24-23 (hex) AzureWave Technologies (Shanghai) Inc. -00-24-24 (hex) Axis Network Technology -00-24-25 (hex) Shenzhenshi chuangzhicheng Technology Co.,Ltd -00-24-26 (hex) NOHMI BOSAI LTD. -00-24-27 (hex) SSI COMPUTER CORP -00-24-28 (hex) EnergyICT -00-24-29 (hex) MK MASTER INC. -00-24-2A (hex) Hittite Microwave Corporation -00-24-2B (hex) Hon Hai Precision Ind.Co.,Ltd. -00-24-2C (hex) Hon Hai Precision Ind. Co., Ltd. -00-24-2E (hex) Datastrip Inc. -00-24-2F (hex) VirtenSys Inc -00-24-30 (hex) Ruby Tech Corp. -00-24-31 (hex) Uni-v co.,ltd -00-24-32 (hex) Neostar Technology Co.,LTD -00-24-33 (hex) Alps Electric Co., Ltd -00-24-34 (hex) Lectrosonics, Inc. -00-24-35 (hex) WIDE CORPORATION -00-24-36 (hex) Apple, Inc -00-24-37 (hex) Motorola - BSG -00-24-38 (hex) Brocade Communications Systems, Inc -00-24-39 (hex) Essential Viewing Systems Limited -00-24-3A (hex) Ludl Electronic Products -00-24-3B (hex) CSSI (S) Pte Ltd -00-24-3C (hex) S.A.A.A. -00-24-3D (hex) Emerson Appliance Motors and Controls -00-24-3F (hex) Storwize, Inc. -00-24-40 (hex) Halo Monitoring, Inc. -00-24-41 (hex) Wanzl Metallwarenfabrik GmbH -00-24-42 (hex) Axona Limited -00-24-43 (hex) Nortel Networks -00-24-44 (hex) Nintendo Co., Ltd. -00-24-45 (hex) LiquidxStream Systems Inc. -00-24-46 (hex) MMB Research Inc. -00-24-47 (hex) Kaztek Systems -00-24-48 (hex) SpiderCloud Wireless, Inc -00-24-49 (hex) Shen Zhen Lite Star Electronics Technology Co., Ltd -00-24-4A (hex) Voyant International -00-24-4B (hex) PERCEPTRON INC -00-24-4C (hex) Solartron Metrology Ltd -00-24-4D (hex) Hokkaido Electronics Corporation -00-24-4E (hex) RadChips, Inc. -00-24-4F (hex) Asantron Technologies Ltd. -00-24-50 (hex) Cisco Systems -00-24-51 (hex) Cisco Systems -00-24-52 (hex) Silicon Software GmbH -00-24-53 (hex) Initra d.o.o. -00-24-54 (hex) Samsung Electronics Co., LTD -00-24-55 (hex) MuLogic BV -00-24-56 (hex) 2Wire -00-24-58 (hex) PA Bastion CC -00-24-59 (hex) ABB STOTZ-KONTAKT GmbH -00-24-5A (hex) Nanjing Panda Electronics Company Limited -00-24-5B (hex) RAIDON TECHNOLOGY, INC. -00-24-5C (hex) Design-Com Technologies Pty. Ltd. -00-24-5D (hex) Terberg besturingstechniek B.V. -00-24-5E (hex) Hivision Co.,ltd -00-24-5F (hex) Vine Telecom CO.,Ltd. -00-24-60 (hex) Giaval Science Development Co. Ltd. -00-24-61 (hex) Shin Wang Tech. -00-24-62 (hex) Rayzone Corporation -00-24-63 (hex) Phybridge Inc -00-24-64 (hex) Bridge Technologies Co AS -00-24-65 (hex) Elentec -00-24-66 (hex) Unitron nv -00-24-67 (hex) AOC International (Europe) GmbH -00-24-68 (hex) Sumavision Technologies Co.,Ltd -00-24-69 (hex) Smart Doorphones -00-24-6A (hex) Solid Year Co., Ltd. -00-24-6B (hex) Coventive -00-24-6C (hex) ARUBA NETWORKS, INC. -00-24-6D (hex) Weinzierl Engineering GmbH -00-24-6E (hex) Phihong USA Corp. -00-24-6F (hex) Onda Communication spa -00-24-70 (hex) AUROTECH ultrasound AS. -00-24-71 (hex) Fusion MultiSystems dba Fusion-io -00-24-72 (hex) ReDriven Power Inc. -00-24-73 (hex) 3Com Europe Ltd -00-24-74 (hex) Autronica Fire And Securirty -00-24-75 (hex) Compass System(Embedded Dept.) -00-24-76 (hex) TAP.tv -00-24-77 (hex) Tibbo Technology -00-24-78 (hex) Mag Tech Electronics Co Limited -00-24-79 (hex) Optec Displays, Inc. -00-24-7A (hex) FU YI CHENG Technology Co., Ltd. -00-24-7B (hex) Actiontec Electronics, Inc -00-24-7C (hex) Nokia Danmark A/S -00-24-7D (hex) Nokia Danmark A/S -00-24-7E (hex) USI -00-24-7F (hex) Nortel Networks -00-24-80 (hex) Meteocontrol GmbH -00-24-81 (hex) Hewlett Packard -00-24-82 (hex) Ruckus Wireless -00-24-83 (hex) LG Electronics -00-24-84 (hex) Bang and Olufsen Medicom a/s -00-24-85 (hex) ConteXtream Ltd -00-24-86 (hex) DesignArt Networks -00-24-87 (hex) Blackboard Inc. -00-24-88 (hex) Centre For Development Of Telematics -00-24-89 (hex) Vodafone Omnitel N.V. -00-24-8A (hex) Kaga Electronics Co., Ltd. -00-24-8B (hex) HYBUS CO., LTD. -00-24-8C (hex) ASUSTek COMPUTER INC. -00-24-8D (hex) Sony Computer Entertainment Inc. -00-24-8E (hex) Infoware ZRt. -00-24-8F (hex) DO-MONIX -00-24-90 (hex) Samsung Electronics Co.,LTD -00-24-91 (hex) Samsung Electronics -00-24-92 (hex) Motorola, Broadband Solutions Group -00-24-93 (hex) Motorola, Inc -00-24-94 (hex) Shenzhen Baoxin Tech CO., Ltd. -00-24-95 (hex) Motorola Mobile Devices -00-24-96 (hex) Ginzinger electronic systems -00-24-97 (hex) Cisco Systems -00-24-98 (hex) Cisco Systems -00-24-99 (hex) Aquila Technologies -00-24-9A (hex) Beijing Zhongchuang Telecommunication Test Co., Ltd. -00-24-9B (hex) Action Star Enterprise Co., Ltd. -00-24-9C (hex) Bimeng Comunication System Co. Ltd -00-24-9D (hex) NES Technology Inc. -00-24-9E (hex) ADC-Elektronik GmbH -00-24-9F (hex) RIM Testing Services -00-24-A0 (hex) Motorola CHS -00-24-A1 (hex) Motorola CHS -00-24-A2 (hex) Hong Kong Middleware Technology Limited -00-24-A3 (hex) Sonim Technologies Inc -00-24-A4 (hex) Siklu Communication -00-24-A5 (hex) Buffalo Inc. -00-24-A6 (hex) TELESTAR DIGITAL GmbH -00-24-A7 (hex) Advanced Video Communications Inc. -00-24-A8 (hex) ProCurve Networking by HP -00-24-A9 (hex) Ag Leader Technology -00-24-AA (hex) Dycor Technologies Ltd. -00-24-AB (hex) A7 Engineering, Inc. -00-24-AC (hex) Hangzhou DPtech Technologies Co., Ltd. -00-24-AD (hex) Adolf Thies Gmbh & Co. KG -00-24-AE (hex) SAGEM SECURITE -00-24-AF (hex) EchoStar Technologies -00-24-B0 (hex) ESAB AB -00-24-B1 (hex) Coulomb Technologies -00-24-B2 (hex) Netgear -00-24-B3 (hex) Graf-Syteco GmbH & Co. KG -00-24-B4 (hex) ESCATRONIC GmbH -00-24-B5 (hex) Nortel Networks -00-24-B6 (hex) Seagate Technology -00-24-B7 (hex) GridPoint, Inc. -00-24-B8 (hex) free alliance sdn bhd -00-24-B9 (hex) Wuhan Higheasy Electronic Technology Development Co.Ltd -00-24-BA (hex) Texas Instruments -00-24-BB (hex) CENTRAL Corporation -00-24-BC (hex) HuRob Co.,Ltd -00-24-BD (hex) Hainzl Industriesysteme GmbH -00-24-BE (hex) Sony Corporation -00-24-BF (hex) CIAT -00-24-C0 (hex) NTI COMODO INC -00-24-C1 (hex) Hangzhou Motorola Technologies LTD. -00-24-C2 (hex) Asumo Co.,Ltd. -00-24-C3 (hex) Cisco Systems -00-24-C4 (hex) Cisco Systems -00-24-C5 (hex) Meridian Audio Limited -00-24-C6 (hex) Hager Electro SAS -00-24-C7 (hex) Mobilarm Ltd -00-24-C8 (hex) Broadband Solutions Group -00-24-C9 (hex) Broadband Solutions Group -00-24-CA (hex) Tobii Technology AB -00-24-CB (hex) Autonet Mobile -00-24-CC (hex) Fascinations Toys and Gifts, Inc. -00-24-CD (hex) Willow Garage, Inc. -00-24-CE (hex) Exeltech Inc -00-24-CF (hex) Inscape Data Corporation -00-24-D0 (hex) Shenzhen SOGOOD Industry CO.,LTD. -00-24-D1 (hex) Thomson Inc. -00-24-D2 (hex) Askey Computer -00-24-D3 (hex) QUALICA Inc. -00-24-D4 (hex) FREEBOX SA -00-24-D5 (hex) Winward Industrial Limited -00-24-D6 (hex) Intel Corporate -00-24-D7 (hex) Intel Corporate -00-24-D8 (hex) IlSung Precision -00-24-D9 (hex) BICOM, Inc. -00-24-DA (hex) Innovar Systems Limited -00-24-DB (hex) Alcohol Monitoring Systems -00-24-DC (hex) Juniper Networks -00-24-DD (hex) Centrak, Inc. -00-24-DE (hex) GLOBAL Technology Inc. -00-24-DF (hex) Digitalbox Europe GmbH -00-24-E0 (hex) DS Tech, LLC -00-24-E1 (hex) Convey Computer Corp. -00-24-E2 (hex) HASEGAWA ELECTRIC CO.,LTD. -00-24-E3 (hex) CAO Group -00-24-E4 (hex) Withings -00-24-E5 (hex) Seer Technology, Inc -00-24-E6 (hex) In Motion Technology Inc. -00-24-E7 (hex) Plaster Networks -00-24-E8 (hex) Dell Inc. -00-24-E9 (hex) Samsung Electronics Co., Ltd., Storage System Division -00-24-EA (hex) iris-GmbH infrared & intelligent sensors -00-24-EB (hex) ClearPath Networks, Inc. -00-24-EC (hex) United Information Technology Co.,Ltd. -00-24-ED (hex) YT Elec. Co,.Ltd. -00-24-EE (hex) Wynmax Inc. -00-24-EF (hex) Sony Ericsson Mobile Communications -00-24-F0 (hex) Seanodes -00-24-F1 (hex) Shenzhen Fanhai Sanjiang Electronics Co., Ltd. -00-24-F2 (hex) Uniphone Telecommunication Co., Ltd. -00-24-F3 (hex) Nintendo Co., Ltd. -00-24-F4 (hex) Kaminario Technologies Ltd. -00-24-F5 (hex) NDS Surgical Imaging -00-24-F6 (hex) MIYOSHI ELECTRONICS CORPORATION -00-24-F7 (hex) Cisco Systems -00-24-F8 (hex) Technical Solutions Company Ltd. -00-24-F9 (hex) Cisco Systems -00-24-FA (hex) Hilger u. Kern GMBH -00-24-FB (hex) PRIVATE -00-24-FC (hex) QuoPin Co., Ltd. -00-24-FD (hex) Prosilient Technologies AB -00-24-FE (hex) AVM GmbH -00-24-FF (hex) QLogic Corporation -00-25-00 (hex) Apple, Inc -00-25-01 (hex) JSC "Supertel" -00-25-02 (hex) NaturalPoint -00-25-03 (hex) BLADE Network Technology -00-25-04 (hex) Valiant Communications Limited -00-25-05 (hex) eks Engel GmbH & Co. KG -00-25-06 (hex) A.I. ANTITACCHEGGIO ITALIA SRL -00-25-07 (hex) ASTAK Inc. -00-25-08 (hex) Maquet Cardiopulmonary AG -00-25-09 (hex) SHARETRONIC Group LTD -00-25-0A (hex) Security Expert Co. Ltd -00-25-0B (hex) CENTROFACTOR INC -00-25-0C (hex) Enertrac -00-25-0D (hex) GZT Telkom-Telmor sp. z o.o. -00-25-0E (hex) gt german telematics gmbh -00-25-0F (hex) On-Ramp Wireless, Inc. -00-25-10 (hex) Pico-Tesla Magnetic Therapies -00-25-11 (hex) ELITEGROUP COMPUTER SYSTEM CO., LTD. -00-25-12 (hex) ZTE Corporation -00-25-13 (hex) CXP DIGITAL BV -00-25-14 (hex) PC Worth Int'l Co., Ltd. -00-25-15 (hex) SFR -00-25-16 (hex) Integrated Design Tools, Inc. -00-25-17 (hex) Venntis, LLC -00-25-18 (hex) Power PLUS Communications AG -00-25-19 (hex) Viaas Inc -00-25-1A (hex) Psiber Data Systems Inc. -00-25-1B (hex) Philips CareServant -00-25-1C (hex) EDT -00-25-1D (hex) DSA Encore, LLC -00-25-1E (hex) ROTEL TECHNOLOGIES -00-25-1F (hex) ZYNUS VISION INC. -00-25-20 (hex) SMA Railway Technology GmbH -00-25-21 (hex) Logitek Electronic Systems, Inc. -00-25-22 (hex) ASRock Incorporation -00-25-23 (hex) OCP Inc. -00-25-24 (hex) Lightcomm Technology Co., Ltd -00-25-25 (hex) CTERA Networks Ltd. -00-25-26 (hex) Genuine Technologies Co., Ltd. -00-25-27 (hex) Bitrode Corp. -00-25-28 (hex) Daido Signal Co., Ltd. -00-25-29 (hex) COMELIT GROUP S.P.A -00-25-2A (hex) Chengdu GeeYa Technology Co.,LTD -00-25-2B (hex) Stirling Energy Systems -00-25-2C (hex) Entourage Systems, Inc. -00-25-2D (hex) Kiryung Electronics -00-25-2E (hex) Cisco SPVTG -00-25-2F (hex) Energy, Inc. -00-25-30 (hex) Aetas Systems Inc. -00-25-31 (hex) Cloud Engines, Inc. -00-25-32 (hex) Digital Recorders -00-25-33 (hex) WITTENSTEIN AG -00-25-35 (hex) Minimax GmbH & Co KG -00-25-36 (hex) Oki Electric Industry Co., Ltd. -00-25-37 (hex) Runcom Technologies Ltd. -00-25-38 (hex) Samsung Electronics Co., Ltd., Memory Division -00-25-39 (hex) IfTA GmbH -00-25-3A (hex) CEVA, Ltd. -00-25-3B (hex) din Dietmar Nocker Facilitymanagement GmbH -00-25-3C (hex) 2Wire -00-25-3D (hex) DRS Consolidated Controls -00-25-3E (hex) Sensus Metering Systems -00-25-40 (hex) Quasar Technologies, Inc. -00-25-41 (hex) Maquet Critical Care AB -00-25-42 (hex) Pittasoft -00-25-43 (hex) MONEYTECH -00-25-44 (hex) LoJack Corporation -00-25-45 (hex) Cisco Systems -00-25-46 (hex) Cisco Systems -00-25-47 (hex) Nokia Danmark A/S -00-25-48 (hex) Nokia Danmark A/S -00-25-49 (hex) Jeorich Tech. Co.,Ltd. -00-25-4A (hex) RingCube Technologies, Inc. -00-25-4B (hex) Apple, Inc -00-25-4C (hex) Videon Central, Inc. -00-25-4D (hex) Singapore Technologies Electronics Limited -00-25-4E (hex) Vertex Wireless Co., Ltd. -00-25-4F (hex) ELETTROLAB Srl -00-25-50 (hex) Riverbed Technology -00-25-51 (hex) SE-Elektronic GmbH -00-25-52 (hex) VXI CORPORATION -00-25-53 (hex) PIRELLI BROADBAND SOLUTIONS -00-25-54 (hex) Pixel8 Networks -00-25-55 (hex) Visonic Technologies 1993 Ltd -00-25-56 (hex) Hon Hai Precision Ind. Co., Ltd. -00-25-57 (hex) Research In Motion -00-25-58 (hex) MPEDIA -00-25-59 (hex) Syphan Technologies Ltd -00-25-5A (hex) Tantalus Systems Corp. -00-25-5B (hex) CoachComm, LLC -00-25-5C (hex) NEC Corporation -00-25-5D (hex) Morningstar Corporation -00-25-5E (hex) Shanghai Dare Technologies Co.,Ltd. -00-25-5F (hex) SenTec AG -00-25-60 (hex) Ibridge Networks & Communications Ltd. -00-25-61 (hex) ProCurve Networking by HP -00-25-62 (hex) interbro Co. Ltd. -00-25-63 (hex) Luxtera Inc -00-25-64 (hex) Dell Inc. -00-25-65 (hex) Vizimax Inc. -00-25-66 (hex) Samsung Electronics Co.,Ltd -00-25-67 (hex) Samsung Electronics -00-25-68 (hex) Shenzhen Huawei Communication Technologies Co., Ltd -00-25-69 (hex) SAGEM COMMUNICATION -00-25-6A (hex) inIT - Institut Industrial IT -00-25-6B (hex) ATENIX E.E. s.r.l. -00-25-6C (hex) "Azimut" Production Association JSC -00-25-6D (hex) Broadband Forum -00-25-6E (hex) Van Breda B.V. -00-25-6F (hex) Dantherm Power -00-25-70 (hex) Eastern Communications Company Limited -00-25-71 (hex) Zhejiang Tianle Digital Electric Co.,Ltd -00-25-72 (hex) Nemo-Q International AB -00-25-73 (hex) ST Electronics (Info-Security) Pte Ltd -00-25-74 (hex) KUNIMI MEDIA DEVICE Co., Ltd. -00-25-75 (hex) FiberPlex Inc -00-25-76 (hex) NELI TECHNOLOGIES -00-25-77 (hex) D-BOX Technologies -00-25-78 (hex) JSC "Concern "Sozvezdie" -00-25-79 (hex) J & F Labs -00-25-7A (hex) CAMCO Produktions- und Vertriebs-GmbH fr Beschallungs- und Beleuchtungsanlagen -00-25-7B (hex) STJ ELECTRONICS PVT LTD -00-25-7C (hex) Huachentel Technology Development Co., Ltd -00-25-7D (hex) PointRed Telecom Private Ltd. -00-25-7E (hex) NEW POS Technology Limited -00-25-7F (hex) CallTechSolution Co.,Ltd -00-25-80 (hex) Equipson S.A. -00-25-81 (hex) x-star networks Inc. -00-25-82 (hex) Maksat Technologies (P) Ltd -00-25-83 (hex) Cisco Systems -00-25-84 (hex) Cisco Systems -00-25-85 (hex) KOKUYO S&T Co., Ltd. -00-25-86 (hex) TP-LINK Technologies Co., Ltd. -00-25-87 (hex) Vitality, Inc. -00-25-88 (hex) Genie Industries, Inc. -00-25-89 (hex) Hills Industries Limited -00-25-8A (hex) Pole/Zero Corporation -00-25-8B (hex) Mellanox Technologies Ltd -00-25-8C (hex) ESUS ELEKTRONIK SAN. VE DIS. TIC. LTD. STI. -00-25-8D (hex) Haier -00-25-8E (hex) The Weather Channel -00-25-8F (hex) Trident Microsystems, Inc. -00-25-90 (hex) Super Micro Computer, Inc. -00-25-91 (hex) NEXTEK, Inc. -00-25-92 (hex) Guangzhou Shirui Electronic Co., Ltd -00-25-93 (hex) DatNet Informatikai Kft. -00-25-94 (hex) Eurodesign BG LTD -00-25-95 (hex) Northwest Signal Supply, Inc -00-25-96 (hex) GIGAVISION srl -00-25-97 (hex) Kalki Communication Technologies -00-25-98 (hex) Zhong Shan City Litai Electronic Industrial Co. Ltd -00-25-99 (hex) Hedon e.d. B.V. -00-25-9A (hex) CEStronics GmbH -00-25-9B (hex) Beijing PKUNITY Microsystems Technology Co., Ltd -00-25-9C (hex) Cisco-Linksys, LLC -00-25-9D (hex) PRIVATE -00-25-9E (hex) Huawei Technologies Co., Ltd. -00-25-9F (hex) TechnoDigital Technologies GmbH -00-25-A0 (hex) Nintendo Co., Ltd. -00-25-A1 (hex) Enalasys -00-25-A2 (hex) Alta Definicion LINCEO S.L. -00-25-A3 (hex) Trimax Wireless, Inc. -00-25-A4 (hex) EuroDesign embedded technologies GmbH -00-25-A5 (hex) Walnut Media Network -00-25-A6 (hex) Central Network Solution Co., Ltd. -00-25-A7 (hex) Comverge, Inc. -00-25-A8 (hex) Kontron (BeiJing) Technology Co.,Ltd -00-25-A9 (hex) Shanghai Embedway Information Technologies Co.,Ltd -00-25-AA (hex) Beijing Soul Technology Co.,Ltd. -00-25-AB (hex) AIO LCD PC BU / TPV -00-25-AC (hex) I-Tech corporation -00-25-AD (hex) Manufacturing Resources International -00-25-AE (hex) Microsoft Corporation -00-25-AF (hex) COMFILE Technology -00-25-B0 (hex) Schmartz Inc -00-25-B1 (hex) Maya-Creation Corporation -00-25-B2 (hex) LFK-Lenkflugkrpersysteme GmbH -00-25-B3 (hex) Hewlett Packard -00-25-B4 (hex) Cisco Systems -00-25-B5 (hex) Cisco Systems -00-25-B6 (hex) Telecom FM -00-25-B7 (hex) Costar electronics, inc., -00-25-B8 (hex) Agile Communications, Inc. -00-25-B9 (hex) Agilink Systems Corp. -00-25-BA (hex) Alcatel-Lucent IPD -00-25-BB (hex) INNERINT Co., Ltd. -00-25-BC (hex) Apple, Inc -00-25-BD (hex) Italdata Ingegneria dell'Idea S.p.A. -00-25-BE (hex) Tektrap Systems Inc. -00-25-BF (hex) Wireless Cables Inc. -00-25-C0 (hex) ZillionTV Corporation -00-25-C1 (hex) Nawoo Korea Corp. -00-25-C2 (hex) RingBell Co.,Ltd. -00-25-C3 (hex) Nortel Networks -00-25-C4 (hex) Ruckus Wireless -00-25-C5 (hex) Star Link Communication Pvt. Ltd. -00-25-C6 (hex) kasercorp, ltd -00-25-C7 (hex) altek Corporation -00-25-C8 (hex) S-Access GmbH -00-25-C9 (hex) SHENZHEN HUAPU DIGITAL CO., LTD -00-25-CA (hex) LS Research, LLC -00-25-CB (hex) Reiner SCT -00-25-CC (hex) Mobile Communications Korea Incorporated -00-25-CD (hex) Skylane Optics -00-25-CE (hex) InnerSpace -00-25-CF (hex) Nokia Danmark A/S -00-25-D0 (hex) Nokia Danmark A/S -00-25-D1 (hex) Eastech Electronics (Taiwan) Inc. -00-25-D2 (hex) InpegVision Co., Ltd -00-25-D3 (hex) AzureWave Technologies, Inc -00-25-D4 (hex) Fortress Technologies -00-25-D5 (hex) Robonica (Pty) Ltd -00-25-D6 (hex) The Kroger Co. -00-25-D7 (hex) CEDO -00-25-D8 (hex) KOREA MAINTENANCE -00-25-D9 (hex) DataFab Systems Inc. -00-25-DA (hex) Secura Key -00-25-DB (hex) ATI Electronics(Shenzhen) Co., LTD -00-25-DC (hex) Sumitomo Electric Networks, Inc -00-25-DD (hex) SUNNYTEK INFORMATION CO., LTD. -00-25-DE (hex) Probits Co., LTD. -00-25-DF (hex) PRIVATE -00-25-E0 (hex) CeedTec Sdn Bhd -00-25-E1 (hex) SHANGHAI SEEYOO ELECTRONIC & TECHNOLOGY CO., LTD -00-25-E2 (hex) Everspring Industry Co., Ltd. -00-25-E3 (hex) Hanshinit Inc. -00-25-E4 (hex) OMNI-WiFi, LLC -00-25-E5 (hex) LG Electronics Inc -00-25-E6 (hex) Belgian Monitoring Systems bvba -00-25-E7 (hex) Sony Ericsson Mobile Communications -00-25-E8 (hex) Idaho Technology -00-25-E9 (hex) i-mate Development, Inc. -00-25-EA (hex) Iphion BV -00-25-EB (hex) Reutech Radar Systems (PTY) Ltd -00-25-EC (hex) Humanware -00-25-ED (hex) NuVo Technologies LLC -00-25-EE (hex) Avtex Ltd -00-25-EF (hex) I-TEC Co., Ltd. -00-25-F0 (hex) Suga Electronics Limited -00-25-F1 (hex) Motorola CHS -00-25-F2 (hex) Motorola CHS -00-25-F3 (hex) Nordwestdeutsche Zhlerrevision -00-25-F4 (hex) KoCo Connector AG -00-25-F5 (hex) DVS Korea, Co., Ltd -00-25-F6 (hex) netTALK.com, Inc. -00-25-F7 (hex) Ansaldo STS USA -00-25-F9 (hex) GMK electronic design GmbH -00-25-FA (hex) J&M Analytik AG -00-25-FB (hex) Tunstall Healthcare A/S -00-25-FC (hex) ENDA ENDUSTRIYEL ELEKTRONIK LTD. STI. -00-25-FD (hex) OBR Centrum Techniki Morskiej S.A. -00-25-FE (hex) Pilot Electronics Corporation -00-25-FF (hex) CreNova Technology GmbH -00-26-00 (hex) TEAC Australia Pty Ltd. -00-26-01 (hex) PRIVATE -00-26-02 (hex) SMART Temps LLC -00-26-03 (hex) Shenzhen Wistar Technology Co., Ltd -00-26-04 (hex) Audio Processing Technology Ltd -00-26-05 (hex) CC Systems AB -00-26-06 (hex) RAUMFELD GmbH -00-26-07 (hex) Enabling Technology Pty Ltd -00-26-08 (hex) Apple, Inc -00-26-09 (hex) Phyllis Co., Ltd. -00-26-0A (hex) Cisco Systems -00-26-0B (hex) Cisco Systems -00-26-0C (hex) Dataram -00-26-0D (hex) Micronetics, Inc. -00-26-0E (hex) Ablaze Systems, LLC -00-26-0F (hex) Linn Products Ltd -00-26-10 (hex) Apacewave Technologies -00-26-11 (hex) Licera AB -00-26-12 (hex) Space Exploration Technologies -00-26-13 (hex) Engel Axil S.L. -00-26-14 (hex) KTNF -00-26-15 (hex) Teracom Limited -00-26-16 (hex) Rosemount Inc. -00-26-17 (hex) OEM Worldwide -00-26-18 (hex) ASUSTek COMPUTER INC. -00-26-19 (hex) FRC -00-26-1A (hex) Femtocomm System Technology Corp. -00-26-1B (hex) LAUREL BANK MACHINES CO., LTD. -00-26-1C (hex) NEOVIA INC. -00-26-1D (hex) COP SECURITY SYSTEM CORP. -00-26-1E (hex) QINGBANG ELEC(SZ) CO., LTD -00-26-1F (hex) SAE Magnetics (H.K.) Ltd. -00-26-20 (hex) ISGUS GmbH -00-26-21 (hex) InteliCloud Technology Inc. -00-26-22 (hex) COMPAL INFORMATION (KUNSHAN) CO., LTD. -00-26-23 (hex) JRD Communication Inc -00-26-24 (hex) Thomson Inc. -00-26-25 (hex) MediaSputnik -00-26-26 (hex) Geophysical Survey Systems, Inc. -00-26-27 (hex) Truesell -00-26-28 (hex) companytec automao e controle ltda -00-26-29 (hex) Juphoon System Software Inc. -00-26-2A (hex) Proxense, LLC -00-26-2B (hex) Wongs Electronics Co. Ltd. -00-26-2C (hex) IKT Advanced Technologies s.r.o. -00-26-2D (hex) Wistron Corporation -00-26-2E (hex) Chengdu Jiuzhou Electronic Technology Inc -00-26-2F (hex) HAMAMATSU TOA ELECTRONICS -00-26-30 (hex) ACOREL S.A.S -00-26-31 (hex) COMMTACT LTD -00-26-32 (hex) Instrumentation Technologies d.d. -00-26-33 (hex) MIR - Medical International Research -00-26-34 (hex) Infineta Systems, Inc -00-26-35 (hex) Bluetechnix GmbH -00-26-36 (hex) Motorola Mobile Devices -00-26-37 (hex) Samsung Electro-Mechanics -00-26-38 (hex) Xia Men Joyatech Co., Ltd. -00-26-39 (hex) T.M. Electronics, Inc. -00-26-3A (hex) Digitec Systems -00-26-3B (hex) Onbnetech -00-26-3C (hex) Bachmann GmbH & Co. KG -00-26-3D (hex) MIA Corporation -00-26-3E (hex) Trapeze Networks -00-26-3F (hex) LIOS Technology GmbH -00-26-40 (hex) Baustem Broadband Technologies, Ltd. -00-26-41 (hex) Motorola, Inc -00-26-42 (hex) Motorola, Inc -00-26-43 (hex) Alps Electric Co., Ltd -00-26-44 (hex) Thomson Telecom Belgium -00-26-45 (hex) Circontrol S.A. -00-26-46 (hex) SHENYANG TONGFANG MULTIMEDIA TECHNOLOGY COMPANY LIMITED -00-26-47 (hex) WFE TECHNOLOGY CORP. -00-26-48 (hex) Emitech Corp. -00-26-4A (hex) Apple, Inc -00-26-4C (hex) Shanghai DigiVision Technology Co., Ltd. -00-26-4D (hex) Arcadyan Technology Corporation -00-26-4E (hex) Rail & Road Protec GmbH -00-26-4F (hex) Krger&Gothe GmbH -00-26-50 (hex) 2Wire -00-26-51 (hex) Cisco Systems -00-26-52 (hex) Cisco Systems -00-26-53 (hex) DaySequerra Corporation -00-26-54 (hex) 3Com Corporation -00-26-55 (hex) Hewlett Packard -00-26-56 (hex) Sansonic Electronics USA -00-26-57 (hex) OOO NPP EKRA -00-26-58 (hex) T-Platforms (Cyprus) Limited -00-26-59 (hex) Nintendo Co., Ltd. -00-26-5A (hex) D-Link Corporation -00-26-5B (hex) Hitron Technologies. Inc -00-26-5C (hex) Hon Hai Precision Ind. Co.,Ltd. -00-26-5D (hex) Samsung Electronics -00-26-5E (hex) Hon Hai Precision Ind. Co.,Ltd. -00-26-5F (hex) Samsung Electronics Co.,Ltd -00-26-60 (hex) Logiways -00-26-61 (hex) Irumtek Co., Ltd. -00-26-62 (hex) Actiontec Electronics, Inc -00-26-63 (hex) Shenzhen Huitaiwei Tech. Ltd, co. -00-26-64 (hex) Core System Japan -00-26-65 (hex) ProtectedLogic Corporation -00-26-66 (hex) EFM Networks -00-26-67 (hex) CARECOM CO.,LTD. -00-26-68 (hex) Nokia Danmark A/S -00-26-69 (hex) Nokia Danmark A/S -00-26-6A (hex) ESSENSIUM NV -00-26-6B (hex) SHINE UNION ENTERPRISE LIMITED -00-26-6C (hex) Inventec -00-26-6D (hex) MobileAccess Networks -00-26-6E (hex) Nissho-denki Co.,LTD. -00-26-6F (hex) Coordiwise Technology Corp. -00-26-70 (hex) Cinch Connectors -00-26-71 (hex) AUTOVISION Co., Ltd -00-26-72 (hex) AAMP of America -00-26-73 (hex) RICOH COMPANY LTD. -00-26-74 (hex) Electronic Solutions, Inc. -00-26-75 (hex) Aztech Electronics Pte Ltd -00-26-76 (hex) COMMidt AS -00-26-77 (hex) DEIF A/S -00-26-78 (hex) Logic Instrument SA -00-26-79 (hex) Euphonic Technologies, Inc. -00-26-7A (hex) wuhan hongxin telecommunication technologies co.,ltd -00-26-7B (hex) GSI Helmholtzzentrum fr Schwerionenforschung GmbH -00-26-7C (hex) Metz-Werke GmbH & Co KG -00-26-7D (hex) A-Max Technology Macao Commercial Offshore Company Limited -00-26-7E (hex) Parrot SA -00-26-7F (hex) Zenterio AB -00-26-80 (hex) Lockie Innovation Pty Ltd -00-26-81 (hex) Interspiro AB -00-26-82 (hex) Gemtek Technology Co., Ltd. -00-26-83 (hex) Ajoho Enterprise Co., Ltd. -00-26-84 (hex) KISAN SYSTEM -00-26-85 (hex) Digital Innovation -00-26-86 (hex) Quantenna Communcations, Inc. -00-26-87 (hex) ALLIED TELESIS, K.K corega division. -00-26-88 (hex) Juniper Networks -00-26-89 (hex) General Dynamics Robotic Systems -00-26-8A (hex) Terrier SC Ltd -00-26-8B (hex) Guangzhou Escene Computer Technology Limited -00-26-8C (hex) StarLeaf Ltd. -00-26-8D (hex) CellTel S.p.A. -00-26-8E (hex) Alta Solutions, Inc. -00-26-8F (hex) MTA SpA -00-26-90 (hex) I DO IT -00-26-91 (hex) SAGEM COMMUNICATION -00-26-92 (hex) Mitsubishi Electric Co. -00-26-93 (hex) QVidium Technologies, Inc. -00-26-94 (hex) Senscient Ltd -00-26-95 (hex) ZT Group Int'l Inc -00-26-96 (hex) NOOLIX Co., Ltd -00-26-97 (hex) Cheetah Technologies, L.P. -00-26-98 (hex) Cisco Systems -00-26-99 (hex) Cisco Systems -00-26-9A (hex) carina system co., ltd. -00-26-9B (hex) SOKRAT Ltd. -00-26-9C (hex) ITUS JAPAN CO. LTD -00-26-9D (hex) M2Mnet Co., Ltd. -00-26-9E (hex) Quanta Computer Inc -00-26-9F (hex) PRIVATE -00-26-A0 (hex) moblic -00-26-A1 (hex) Megger -00-26-A2 (hex) Instrumentation Technology Systems -00-26-A3 (hex) FQ Ingenieria Electronica S.A. -00-26-A4 (hex) Novus Produtos Eletronicos Ltda -00-26-A5 (hex) MICROROBOT.CO.,LTD -00-26-A6 (hex) TRIXELL -00-26-A7 (hex) CONNECT SRL -00-26-A8 (hex) DAEHAP HYPER-TECH -00-26-A9 (hex) Strong Technologies Pty Ltd -00-26-AA (hex) Kenmec Mechanical Engineering Co., Ltd. -00-26-AB (hex) SEIKO EPSON CORPORATION -00-26-AC (hex) Shanghai LUSTER Teraband photonic Co., Ltd. -00-26-AD (hex) Arada Systems, Inc. -00-26-AE (hex) Wireless Measurement Ltd -00-26-AF (hex) Duelco A/S -00-26-B0 (hex) Apple, Inc -00-26-B1 (hex) Harman/Navis -00-26-B2 (hex) Setrix AG -00-26-B3 (hex) Thales Communications Inc -00-26-B4 (hex) Ford Motor Company -00-26-B5 (hex) ICOMM Tele Ltd -00-26-B6 (hex) Askey Computer -00-26-B7 (hex) Kingston Technology Company, Inc. -00-26-B8 (hex) Actiontec Electronics, Inc -00-26-B9 (hex) Dell Inc -00-26-BA (hex) Motorola Mobile Devices -00-26-BB (hex) Apple, Inc -00-26-BC (hex) General Jack Technology Ltd. -00-26-BD (hex) JTEC Card & Communication Co., Ltd. -00-26-BE (hex) Schoonderbeek Elektronica Systemen B.V. -00-26-BF (hex) ShenZhen Temobi Science&Tech Development Co.,Ltd -00-26-C0 (hex) EnergyHub -00-26-C1 (hex) ARTRAY CO., LTD. -00-26-C2 (hex) SCDI Co. LTD -00-26-C3 (hex) Insightek Corp. -00-26-C4 (hex) Cadmos microsystems S.r.l. -00-26-C5 (hex) Guangdong Gosun Telecommunications Co.,Ltd -00-26-C6 (hex) Intel Corporate -00-26-C7 (hex) Intel Corporate -00-26-C8 (hex) System Sensor -00-26-C9 (hex) Proventix Systems, Inc. -00-26-CA (hex) Cisco Systems -00-26-CB (hex) Cisco Systems -00-26-CC (hex) Nokia Danmark A/S -00-26-CD (hex) PurpleComm, Inc. -00-26-CE (hex) Kozumi USA Corp. -00-26-CF (hex) DEKA R&D -00-26-D0 (hex) Semihalf -00-26-D1 (hex) S Squared Innovations Inc. -00-26-D2 (hex) Pcube Systems, Inc. -00-26-D3 (hex) Zeno Information System -00-26-D4 (hex) IRCA SpA -00-26-D5 (hex) Ory Solucoes em Comercio de Informatica Ltda. -00-26-D6 (hex) Ningbo Andy Optoelectronic Co., Ltd. -00-26-D7 (hex) Xiamen BB Electron & Technology Co., Ltd. -00-26-D8 (hex) Magic Point Inc. -00-26-D9 (hex) Pace Micro Technology plc -00-26-DA (hex) Universal Media Corporation /Slovakia/ s.r.o. -00-26-DB (hex) Ionics EMS Inc. -00-26-DC (hex) Optical Systems Design -00-26-DD (hex) Fival Corporation -00-26-DE (hex) FDI MATELEC -00-26-DF (hex) TaiDoc Technology Corp. -00-26-E0 (hex) ASITEQ -00-26-E1 (hex) Stanford University, OpenFlow Group -00-26-E2 (hex) LG Electronics -00-26-E3 (hex) DTI -00-26-E4 (hex) CANAL OVERSEAS -00-26-E5 (hex) AEG Power Solutions -00-26-E6 (hex) Visionhitech Co., Ltd. -00-26-E7 (hex) Shanghai ONLAN Communication Tech. Co., Ltd. -00-26-E8 (hex) Murata Manufacturing Co., Ltd. -00-26-E9 (hex) SP Corp -00-26-EA (hex) Cheerchip Electronic Technology (ShangHai) Co., Ltd. -00-26-EB (hex) Advanced Spectrum Technology Co., Ltd. -00-26-EC (hex) Legrand Home Systems, Inc -00-26-ED (hex) zte corporation -00-26-EE (hex) TKM GmbH -00-26-EF (hex) Technology Advancement Group, Inc. -00-26-F0 (hex) cTrixs International GmbH. -00-26-F1 (hex) ProCurve Networking by HP -00-26-F2 (hex) Netgear -00-26-F3 (hex) SMC Networks -00-26-F4 (hex) Nesslab -00-26-F5 (hex) XRPLUS Inc. -00-26-F6 (hex) Military Communication Institute -00-26-F7 (hex) Infosys Technologies Ltd. -00-26-F8 (hex) Golden Highway Industry Development Co., Ltd. -00-26-F9 (hex) S.E.M. srl -00-26-FA (hex) BandRich Inc. -00-26-FB (hex) AirDio Wireless, Inc. -00-26-FC (hex) AcSiP Technology Corp. -00-26-FD (hex) Interactive Intelligence -00-26-FE (hex) MKD Technology Inc. -00-26-FF (hex) Research In Motion -00-27-00 (hex) Shenzhen Siglent Technology Co., Ltd. -00-27-01 (hex) INCOstartec GmbH -00-27-02 (hex) SolarEdge Technologies -00-27-03 (hex) Testech Electronics Pte Ltd -00-27-04 (hex) Accelerated Concepts, LLC -00-27-05 (hex) Sectronic -00-27-06 (hex) YOISYS -00-27-07 (hex) Lift Complex DS, JSC -00-27-08 (hex) Nordiag ASA -00-27-09 (hex) Nintendo Co., Ltd. -00-27-0A (hex) IEE S.A. -00-27-0B (hex) Adura Technologies -00-27-0C (hex) Cisco Systems -00-27-0D (hex) Cisco Systems -00-27-0E (hex) Intel Corporate -00-27-0F (hex) Envisionnovation Inc -00-27-10 (hex) Intel Corporate -00-27-11 (hex) LanPro Inc -00-27-12 (hex) MaxVision LLC -00-27-13 (hex) USI -00-27-14 (hex) Grainmustards, Co,ltd. -00-27-15 (hex) Rebound Telecom. Co., Ltd -00-27-16 (hex) Adachi-Syokai Co., Ltd. -00-27-17 (hex) CE Digital(Zhenjiang)Co.,Ltd -00-27-18 (hex) Suzhou NEW SEAUNION Video Technology Co.,Ltd -00-27-19 (hex) TP-LINK TECHNOLOGIES CO., LTD. -00-27-1A (hex) Geenovo Technology Ltd. -00-27-1B (hex) Alec Sicherheitssysteme GmbH -00-27-1C (hex) MERCURY CORPORATION -00-27-1D (hex) Comba Telecom Systems (China) Ltd. -00-27-1E (hex) Xagyl Communications -00-27-1F (hex) MIPRO Electronics Co., Ltd -00-27-20 (hex) NEW-SOL COM -00-27-21 (hex) Shenzhen Baoan Fenda Industrial Co., Ltd -00-27-22 (hex) Ubiquiti Networks -00-30-00 (hex) ALLWELL TECHNOLOGY CORP. -00-30-01 (hex) SMP -00-30-02 (hex) Expand Networks -00-30-03 (hex) Phasys Ltd. -00-30-04 (hex) LEADTEK RESEARCH INC. -00-30-05 (hex) Fujitsu Siemens Computers -00-30-06 (hex) SUPERPOWER COMPUTER -00-30-07 (hex) OPTI, INC. -00-30-08 (hex) AVIO DIGITAL, INC. -00-30-09 (hex) Tachion Networks, Inc. -00-30-0A (hex) AZTECH Electronics Pte Ltd -00-30-0B (hex) mPHASE Technologies, Inc. -00-30-0C (hex) CONGRUENCY, LTD. -00-30-0D (hex) MMC Technology, Inc. -00-30-0E (hex) Klotz Digital AG -00-30-0F (hex) IMT - Information Management T -00-30-10 (hex) VISIONETICS INTERNATIONAL -00-30-11 (hex) HMS FIELDBUS SYSTEMS AB -00-30-12 (hex) DIGITAL ENGINEERING LTD. -00-30-13 (hex) NEC Corporation -00-30-14 (hex) DIVIO, INC. -00-30-15 (hex) CP CLARE CORP. -00-30-16 (hex) ISHIDA CO., LTD. -00-30-17 (hex) BlueArc UK Ltd -00-30-18 (hex) Jetway Information Co., Ltd. -00-30-19 (hex) CISCO SYSTEMS, INC. -00-30-1A (hex) SMARTBRIDGES PTE. LTD. -00-30-1B (hex) SHUTTLE, INC. -00-30-1C (hex) ALTVATER AIRDATA SYSTEMS -00-30-1D (hex) SKYSTREAM, INC. -00-30-1E (hex) 3COM Europe Ltd. -00-30-1F (hex) OPTICAL NETWORKS, INC. -00-30-20 (hex) TSI, Inc.. -00-30-21 (hex) HSING TECH. ENTERPRISE CO.,LTD -00-30-22 (hex) Fong Kai Industrial Co., Ltd. -00-30-23 (hex) COGENT COMPUTER SYSTEMS, INC. -00-30-24 (hex) CISCO SYSTEMS, INC. -00-30-25 (hex) CHECKOUT COMPUTER SYSTEMS, LTD -00-30-26 (hex) HeiTel Digital Video GmbH -00-30-27 (hex) KERBANGO, INC. -00-30-28 (hex) FASE Saldatura srl -00-30-29 (hex) OPICOM -00-30-2A (hex) SOUTHERN INFORMATION -00-30-2B (hex) INALP NETWORKS, INC. -00-30-2C (hex) SYLANTRO SYSTEMS CORPORATION -00-30-2D (hex) QUANTUM BRIDGE COMMUNICATIONS -00-30-2E (hex) Hoft & Wessel AG -00-30-2F (hex) GE Aviation System -00-30-30 (hex) HARMONIX CORPORATION -00-30-31 (hex) LIGHTWAVE COMMUNICATIONS, INC. -00-30-32 (hex) MagicRam, Inc. -00-30-33 (hex) ORIENT TELECOM CO., LTD. -00-30-34 (hex) SET ENGINEERING -00-30-35 (hex) Corning Incorporated -00-30-36 (hex) RMP ELEKTRONIKSYSTEME GMBH -00-30-37 (hex) Packard Bell Nec Services -00-30-38 (hex) XCP, INC. -00-30-39 (hex) SOFTBOOK PRESS -00-30-3A (hex) MAATEL -00-30-3B (hex) PowerCom Technology -00-30-3C (hex) ONNTO CORP. -00-30-3D (hex) IVA CORPORATION -00-30-3E (hex) Radcom Ltd. -00-30-3F (hex) TurboComm Tech Inc. -00-30-40 (hex) CISCO SYSTEMS, INC. -00-30-41 (hex) SAEJIN T & M CO., LTD. -00-30-42 (hex) DeTeWe-Deutsche Telephonwerke -00-30-43 (hex) IDREAM TECHNOLOGIES, PTE. LTD. -00-30-44 (hex) Portsmith LLC -00-30-45 (hex) Village Networks, Inc. (VNI) -00-30-46 (hex) Controlled Electronic Manageme -00-30-47 (hex) NISSEI ELECTRIC CO., LTD. -00-30-48 (hex) Supermicro Computer, Inc. -00-30-49 (hex) BRYANT TECHNOLOGY, LTD. -00-30-4A (hex) Fraunhofer IPMS -00-30-4B (hex) ORBACOM SYSTEMS, INC. -00-30-4C (hex) APPIAN COMMUNICATIONS, INC. -00-30-4D (hex) ESI -00-30-4E (hex) BUSTEC PRODUCTION LTD. -00-30-4F (hex) PLANET Technology Corporation -00-30-50 (hex) Versa Technology -00-30-51 (hex) ORBIT AVIONIC & COMMUNICATION -00-30-52 (hex) ELASTIC NETWORKS -00-30-53 (hex) Basler AG -00-30-54 (hex) CASTLENET TECHNOLOGY, INC. -00-30-55 (hex) Renesas Technology America, Inc. -00-30-56 (hex) Beck IPC GmbH -00-30-57 (hex) QTelNet, Inc. -00-30-58 (hex) API MOTION -00-30-59 (hex) DIGITAL-LOGIC AG -00-30-5A (hex) TELGEN CORPORATION -00-30-5B (hex) Toko Inc. -00-30-5C (hex) SMAR Laboratories Corp. -00-30-5D (hex) DIGITRA SYSTEMS, INC. -00-30-5E (hex) Abelko Innovation -00-30-5F (hex) Hasselblad -00-30-60 (hex) Powerfile, Inc. -00-30-61 (hex) MobyTEL -00-30-62 (hex) PATH 1 NETWORK TECHNOL'S INC. -00-30-63 (hex) SANTERA SYSTEMS, INC. -00-30-64 (hex) ADLINK TECHNOLOGY, INC. -00-30-65 (hex) APPLE COMPUTER, INC. -00-30-66 (hex) RFM -00-30-67 (hex) BIOSTAR MICROTECH INT'L CORP. -00-30-68 (hex) CYBERNETICS TECH. CO., LTD. -00-30-69 (hex) IMPACCT TECHNOLOGY CORP. -00-30-6A (hex) PENTA MEDIA CO., LTD. -00-30-6B (hex) CMOS SYSTEMS, INC. -00-30-6C (hex) Hitex Holding GmbH -00-30-6D (hex) LUCENT TECHNOLOGIES -00-30-6E (hex) HEWLETT PACKARD -00-30-6F (hex) SEYEON TECH. CO., LTD. -00-30-70 (hex) 1Net Corporation -00-30-71 (hex) Cisco Systems, Inc. -00-30-72 (hex) Intellibyte Inc. -00-30-73 (hex) International Microsystems, In -00-30-74 (hex) EQUIINET LTD. -00-30-75 (hex) ADTECH -00-30-76 (hex) Akamba Corporation -00-30-77 (hex) ONPREM NETWORKS -00-30-78 (hex) Cisco Systems, Inc. -00-30-79 (hex) CQOS, INC. -00-30-7A (hex) Advanced Technology & Systems -00-30-7B (hex) Cisco Systems, Inc. -00-30-7C (hex) ADID SA -00-30-7D (hex) GRE AMERICA, INC. -00-30-7E (hex) Redflex Communication Systems -00-30-7F (hex) IRLAN LTD. -00-30-80 (hex) CISCO SYSTEMS, INC. -00-30-81 (hex) ALTOS C&C -00-30-82 (hex) TAIHAN ELECTRIC WIRE CO., LTD. -00-30-83 (hex) Ivron Systems -00-30-84 (hex) ALLIED TELESYN INTERNAIONAL -00-30-85 (hex) CISCO SYSTEMS, INC. -00-30-86 (hex) Transistor Devices, Inc. -00-30-87 (hex) VEGA GRIESHABER KG -00-30-88 (hex) Siara Systems, Inc. -00-30-89 (hex) Spectrapoint Wireless, LLC -00-30-8A (hex) NICOTRA SISTEMI S.P.A -00-30-8B (hex) Brix Networks -00-30-8C (hex) Quantum Corporation -00-30-8D (hex) Pinnacle Systems, Inc. -00-30-8E (hex) CROSS MATCH TECHNOLOGIES, INC. -00-30-8F (hex) MICRILOR, Inc. -00-30-90 (hex) CYRA TECHNOLOGIES, INC. -00-30-91 (hex) TAIWAN FIRST LINE ELEC. CORP. -00-30-92 (hex) ModuNORM GmbH -00-30-93 (hex) Sonnet Technologies, Inc -00-30-94 (hex) Cisco Systems, Inc. -00-30-95 (hex) Procomp Informatics, Ltd. -00-30-96 (hex) CISCO SYSTEMS, INC. -00-30-97 (hex) AB Regin -00-30-98 (hex) Global Converging Technologies -00-30-99 (hex) BOENIG UND KALLENBACH OHG -00-30-9A (hex) ASTRO TERRA CORP. -00-30-9B (hex) Smartware -00-30-9C (hex) Timing Applications, Inc. -00-30-9D (hex) Nimble Microsystems, Inc. -00-30-9E (hex) WORKBIT CORPORATION. -00-30-9F (hex) AMBER NETWORKS -00-30-A0 (hex) TYCO SUBMARINE SYSTEMS, LTD. -00-30-A1 (hex) WEBGATE Inc. -00-30-A2 (hex) Lightner Engineering -00-30-A3 (hex) CISCO SYSTEMS, INC. -00-30-A4 (hex) Woodwind Communications System -00-30-A5 (hex) ACTIVE POWER -00-30-A6 (hex) VIANET TECHNOLOGIES, LTD. -00-30-A7 (hex) SCHWEITZER ENGINEERING -00-30-A8 (hex) OL'E COMMUNICATIONS, INC. -00-30-A9 (hex) Netiverse, Inc. -00-30-AA (hex) AXUS MICROSYSTEMS, INC. -00-30-AB (hex) DELTA NETWORKS, INC. -00-30-AC (hex) Systeme Lauer GmbH & Co., Ltd. -00-30-AD (hex) SHANGHAI COMMUNICATION -00-30-AE (hex) Times N System, Inc. -00-30-AF (hex) Honeywell GmbH -00-30-B0 (hex) Convergenet Technologies -00-30-B1 (hex) aXess-pro networks GmbH -00-30-B2 (hex) L-3 Sonoma EO -00-30-B3 (hex) San Valley Systems, Inc. -00-30-B4 (hex) INTERSIL CORP. -00-30-B5 (hex) Tadiran Microwave Networks -00-30-B6 (hex) CISCO SYSTEMS, INC. -00-30-B7 (hex) Teletrol Systems, Inc. -00-30-B8 (hex) RiverDelta Networks -00-30-B9 (hex) ECTEL -00-30-BA (hex) AC&T SYSTEM CO., LTD. -00-30-BB (hex) CacheFlow, Inc. -00-30-BC (hex) Optronic AG -00-30-BD (hex) BELKIN COMPONENTS -00-30-BE (hex) City-Net Technology, Inc. -00-30-BF (hex) MULTIDATA GMBH -00-30-C0 (hex) Lara Technology, Inc. -00-30-C1 (hex) HEWLETT-PACKARD -00-30-C2 (hex) COMONE -00-30-C3 (hex) FLUECKIGER ELEKTRONIK AG -00-30-C4 (hex) Canon Imaging Systems Inc. -00-30-C5 (hex) CADENCE DESIGN SYSTEMS -00-30-C6 (hex) CONTROL SOLUTIONS, INC. -00-30-C7 (hex) Macromate Corp. -00-30-C8 (hex) GAD LINE, LTD. -00-30-C9 (hex) LuxN, N -00-30-CA (hex) Discovery Com -00-30-CB (hex) OMNI FLOW COMPUTERS, INC. -00-30-CC (hex) Tenor Networks, Inc. -00-30-CD (hex) CONEXANT SYSTEMS, INC. -00-30-CE (hex) Zaffire -00-30-CF (hex) TWO TECHNOLOGIES, INC. -00-30-D0 (hex) Tellabs -00-30-D1 (hex) INOVA CORPORATION -00-30-D2 (hex) WIN TECHNOLOGIES, CO., LTD. -00-30-D3 (hex) Agilent Technologies -00-30-D4 (hex) AAE Systems, Inc -00-30-D5 (hex) DResearch GmbH -00-30-D6 (hex) MSC VERTRIEBS GMBH -00-30-D7 (hex) Innovative Systems, L.L.C. -00-30-D8 (hex) SITEK -00-30-D9 (hex) DATACORE SOFTWARE CORP. -00-30-DA (hex) COMTREND CO. -00-30-DB (hex) Mindready Solutions, Inc. -00-30-DC (hex) RIGHTECH CORPORATION -00-30-DD (hex) INDIGITA CORPORATION -00-30-DE (hex) WAGO Kontakttechnik GmbH -00-30-DF (hex) KB/TEL TELECOMUNICACIONES -00-30-E0 (hex) OXFORD SEMICONDUCTOR LTD. -00-30-E1 (hex) ACROTRON SYSTEMS, INC. -00-30-E2 (hex) GARNET SYSTEMS CO., LTD. -00-30-E3 (hex) SEDONA NETWORKS CORP. -00-30-E4 (hex) CHIYODA SYSTEM RIKEN -00-30-E5 (hex) Amper Datos S.A. -00-30-E6 (hex) Draeger Medical Systems, Inc. -00-30-E7 (hex) CNF MOBILE SOLUTIONS, INC. -00-30-E8 (hex) ENSIM CORP. -00-30-E9 (hex) GMA COMMUNICATION MANUFACT'G -00-30-EA (hex) TeraForce Technology Corporation -00-30-EB (hex) TURBONET COMMUNICATIONS, INC. -00-30-EC (hex) BORGARDT -00-30-ED (hex) Expert Magnetics Corp. -00-30-EE (hex) DSG Technology, Inc. -00-30-EF (hex) NEON TECHNOLOGY, INC. -00-30-F0 (hex) Uniform Industrial Corp. -00-30-F1 (hex) Accton Technology Corp. -00-30-F2 (hex) CISCO SYSTEMS, INC. -00-30-F3 (hex) At Work Computers -00-30-F4 (hex) STARDOT TECHNOLOGIES -00-30-F5 (hex) Wild Lab. Ltd. -00-30-F6 (hex) SECURELOGIX CORPORATION -00-30-F7 (hex) RAMIX INC. -00-30-F8 (hex) Dynapro Systems, Inc. -00-30-F9 (hex) Sollae Systems Co., Ltd. -00-30-FA (hex) TELICA, INC. -00-30-FB (hex) AZS Technology AG -00-30-FC (hex) Terawave Communications, Inc. -00-30-FD (hex) INTEGRATED SYSTEMS DESIGN -00-30-FE (hex) DSA GmbH -00-30-FF (hex) DATAFAB SYSTEMS, INC. -00-35-32 (hex) Electro-Metrics Corporation -00-3A-98 (hex) Cisco Systems -00-3A-99 (hex) Cisco Systems -00-3A-9A (hex) Cisco Systems -00-3A-9B (hex) Cisco Systems -00-3A-9C (hex) Cisco Systems -00-3A-AF (hex) BlueBit Ltd. -00-3C-C5 (hex) WONWOO Engineering Co., Ltd -00-3D-41 (hex) Hatteland Computer AS -00-40-00 (hex) PCI COMPONENTES DA AMZONIA LTD -00-40-01 (hex) ZYXEL COMMUNICATIONS, INC. -00-40-02 (hex) PERLE SYSTEMS LIMITED -00-40-03 (hex) Emerson Process Management Power & Water Solutions, Inc. -00-40-04 (hex) ICM CO. LTD. -00-40-05 (hex) ANI COMMUNICATIONS INC. -00-40-06 (hex) SAMPO TECHNOLOGY CORPORATION -00-40-07 (hex) TELMAT INFORMATIQUE -00-40-08 (hex) A PLUS INFO CORPORATION -00-40-09 (hex) TACHIBANA TECTRON CO., LTD. -00-40-0A (hex) PIVOTAL TECHNOLOGIES, INC. -00-40-0B (hex) CISCO SYSTEMS, INC. -00-40-0C (hex) GENERAL MICRO SYSTEMS, INC. -00-40-0D (hex) LANNET DATA COMMUNICATIONS,LTD -00-40-0E (hex) MEMOTEC, INC. -00-40-0F (hex) DATACOM TECHNOLOGIES -00-40-10 (hex) SONIC SYSTEMS, INC. -00-40-11 (hex) ANDOVER CONTROLS CORPORATION -00-40-12 (hex) WINDATA, INC. -00-40-13 (hex) NTT DATA COMM. SYSTEMS CORP. -00-40-14 (hex) COMSOFT GMBH -00-40-15 (hex) ASCOM INFRASYS AG -00-40-16 (hex) ADC - Global Connectivity Solutions Division -00-40-17 (hex) Silex Technology America -00-40-18 (hex) ADOBE SYSTEMS, INC. -00-40-19 (hex) AEON SYSTEMS, INC. -00-40-1A (hex) FUJI ELECTRIC CO., LTD. -00-40-1B (hex) PRINTER SYSTEMS CORP. -00-40-1C (hex) AST RESEARCH, INC. -00-40-1D (hex) INVISIBLE SOFTWARE, INC. -00-40-1E (hex) ICC -00-40-1F (hex) COLORGRAPH LTD -00-40-20 (hex) Tyco Electronics (UK) Ltd -00-40-21 (hex) RASTER GRAPHICS -00-40-22 (hex) KLEVER COMPUTERS, INC. -00-40-23 (hex) LOGIC CORPORATION -00-40-24 (hex) COMPAC INC. -00-40-25 (hex) MOLECULAR DYNAMICS -00-40-26 (hex) Buffalo, Inc -00-40-27 (hex) SMC MASSACHUSETTS, INC. -00-40-28 (hex) NETCOMM LIMITED -00-40-29 (hex) COMPEX -00-40-2A (hex) CANOGA-PERKINS -00-40-2B (hex) TRIGEM COMPUTER, INC. -00-40-2C (hex) ISIS DISTRIBUTED SYSTEMS, INC. -00-40-2D (hex) HARRIS ADACOM CORPORATION -00-40-2E (hex) PRECISION SOFTWARE, INC. -00-40-2F (hex) XLNT DESIGNS INC. -00-40-30 (hex) GK COMPUTER -00-40-31 (hex) KOKUSAI ELECTRIC CO., LTD -00-40-32 (hex) DIGITAL COMMUNICATIONS -00-40-33 (hex) ADDTRON TECHNOLOGY CO., LTD. -00-40-34 (hex) BUSTEK CORPORATION -00-40-35 (hex) OPCOM -00-40-36 (hex) TRIBE COMPUTER WORKS, INC. -00-40-37 (hex) SEA-ILAN, INC. -00-40-38 (hex) TALENT ELECTRIC INCORPORATED -00-40-39 (hex) OPTEC DAIICHI DENKO CO., LTD. -00-40-3A (hex) IMPACT TECHNOLOGIES -00-40-3B (hex) SYNERJET INTERNATIONAL CORP. -00-40-3C (hex) FORKS, INC. -00-40-3D (hex) TERADATA -00-40-3E (hex) RASTER OPS CORPORATION -00-40-3F (hex) SSANGYONG COMPUTER SYSTEMS -00-40-40 (hex) RING ACCESS, INC. -00-40-41 (hex) FUJIKURA LTD. -00-40-42 (hex) N.A.T. GMBH -00-40-43 (hex) Nokia Siemens Networks GmbH & Co. KG. -00-40-44 (hex) QNIX COMPUTER CO., LTD. -00-40-45 (hex) TWINHEAD CORPORATION -00-40-46 (hex) UDC RESEARCH LIMITED -00-40-47 (hex) WIND RIVER SYSTEMS -00-40-48 (hex) SMD INFORMATICA S.A. -00-40-49 (hex) TEGIMENTA AG -00-40-4A (hex) WEST AUSTRALIAN DEPARTMENT -00-40-4B (hex) MAPLE COMPUTER SYSTEMS -00-40-4C (hex) HYPERTEC PTY LTD. -00-40-4D (hex) TELECOMMUNICATIONS TECHNIQUES -00-40-4E (hex) FLUENT, INC. -00-40-4F (hex) SPACE & NAVAL WARFARE SYSTEMS -00-40-50 (hex) IRONICS, INCORPORATED -00-40-51 (hex) GRACILIS, INC. -00-40-52 (hex) STAR TECHNOLOGIES, INC. -00-40-53 (hex) AMPRO COMPUTERS -00-40-54 (hex) CONNECTION MACHINES SERVICES -00-40-55 (hex) METRONIX GMBH -00-40-56 (hex) MCM JAPAN LTD. -00-40-57 (hex) LOCKHEED - SANDERS -00-40-58 (hex) KRONOS, INC. -00-40-59 (hex) YOSHIDA KOGYO K. K. -00-40-5A (hex) GOLDSTAR INFORMATION & COMM. -00-40-5B (hex) FUNASSET LIMITED -00-40-5C (hex) FUTURE SYSTEMS, INC. -00-40-5D (hex) STAR-TEK, INC. -00-40-5E (hex) NORTH HILLS ISRAEL -00-40-5F (hex) AFE COMPUTERS LTD. -00-40-60 (hex) COMENDEC LTD -00-40-61 (hex) DATATECH ENTERPRISES CO., LTD. -00-40-62 (hex) E-SYSTEMS, INC./GARLAND DIV. -00-40-63 (hex) VIA TECHNOLOGIES, INC. -00-40-64 (hex) KLA INSTRUMENTS CORPORATION -00-40-65 (hex) GTE SPACENET -00-40-66 (hex) HITACHI CABLE, LTD. -00-40-67 (hex) OMNIBYTE CORPORATION -00-40-68 (hex) EXTENDED SYSTEMS -00-40-69 (hex) LEMCOM SYSTEMS, INC. -00-40-6A (hex) KENTEK INFORMATION SYSTEMS,INC -00-40-6B (hex) SYSGEN -00-40-6C (hex) COPERNIQUE -00-40-6D (hex) LANCO, INC. -00-40-6E (hex) COROLLARY, INC. -00-40-6F (hex) SYNC RESEARCH INC. -00-40-70 (hex) INTERWARE CO., LTD. -00-40-71 (hex) ATM COMPUTER GMBH -00-40-72 (hex) Applied Innovation Inc. -00-40-73 (hex) BASS ASSOCIATES -00-40-74 (hex) CABLE AND WIRELESS -00-40-75 (hex) M-TRADE (UK) LTD -00-40-76 (hex) Sun Conversion Technologies -00-40-77 (hex) MAXTON TECHNOLOGY CORPORATION -00-40-78 (hex) WEARNES AUTOMATION PTE LTD -00-40-79 (hex) JUKO MANUFACTURE COMPANY, LTD. -00-40-7A (hex) SOCIETE D'EXPLOITATION DU CNIT -00-40-7B (hex) SCIENTIFIC ATLANTA -00-40-7C (hex) QUME CORPORATION -00-40-7D (hex) EXTENSION TECHNOLOGY CORP. -00-40-7E (hex) EVERGREEN SYSTEMS, INC. -00-40-7F (hex) FLIR Systems -00-40-80 (hex) ATHENIX CORPORATION -00-40-81 (hex) MANNESMANN SCANGRAPHIC GMBH -00-40-82 (hex) LABORATORY EQUIPMENT CORP. -00-40-83 (hex) TDA INDUSTRIA DE PRODUTOS -00-40-84 (hex) HONEYWELL ACS -00-40-85 (hex) SAAB INSTRUMENTS AB -00-40-86 (hex) MICHELS & KLEBERHOFF COMPUTER -00-40-87 (hex) UBITREX CORPORATION -00-40-88 (hex) MOBIUS TECHNOLOGIES, INC. -00-40-89 (hex) MEIDENSHA CORPORATION -00-40-8A (hex) TPS TELEPROCESSING SYS. GMBH -00-40-8B (hex) RAYLAN CORPORATION -00-40-8C (hex) AXIS COMMUNICATIONS AB -00-40-8D (hex) THE GOODYEAR TIRE & RUBBER CO. -00-40-8E (hex) DIGILOG, INC. -00-40-8F (hex) WM-DATA MINFO AB -00-40-90 (hex) ANSEL COMMUNICATIONS -00-40-91 (hex) PROCOMP INDUSTRIA ELETRONICA -00-40-92 (hex) ASP COMPUTER PRODUCTS, INC. -00-40-93 (hex) PAXDATA NETWORKS LTD. -00-40-94 (hex) SHOGRAPHICS, INC. -00-40-95 (hex) R.P.T. INTERGROUPS INT'L LTD. -00-40-96 (hex) Cisco Systems, Inc. -00-40-97 (hex) DATEX DIVISION OF -00-40-98 (hex) DRESSLER GMBH & CO. -00-40-99 (hex) NEWGEN SYSTEMS CORP. -00-40-9A (hex) NETWORK EXPRESS, INC. -00-40-9B (hex) HAL COMPUTER SYSTEMS INC. -00-40-9C (hex) TRANSWARE -00-40-9D (hex) DIGIBOARD, INC. -00-40-9E (hex) CONCURRENT TECHNOLOGIES LTD. -00-40-9F (hex) LANCAST/CASAT TECHNOLOGY, INC. -00-40-A0 (hex) GOLDSTAR CO., LTD. -00-40-A1 (hex) ERGO COMPUTING -00-40-A2 (hex) KINGSTAR TECHNOLOGY INC. -00-40-A3 (hex) MICROUNITY SYSTEMS ENGINEERING -00-40-A4 (hex) ROSE ELECTRONICS -00-40-A5 (hex) CLINICOMP INTL. -00-40-A6 (hex) Cray, Inc. -00-40-A7 (hex) ITAUTEC PHILCO S.A. -00-40-A8 (hex) IMF INTERNATIONAL LTD. -00-40-A9 (hex) DATACOM INC. -00-40-AA (hex) VALMET AUTOMATION INC. -00-40-AB (hex) ROLAND DG CORPORATION -00-40-AC (hex) SUPER WORKSTATION, INC. -00-40-AD (hex) SMA REGELSYSTEME GMBH -00-40-AE (hex) DELTA CONTROLS, INC. -00-40-AF (hex) DIGITAL PRODUCTS, INC. -00-40-B0 (hex) BYTEX CORPORATION, ENGINEERING -00-40-B1 (hex) CODONICS INC. -00-40-B2 (hex) SYSTEMFORSCHUNG -00-40-B3 (hex) PAR MICROSYSTEMS CORPORATION -00-40-B4 (hex) NEXTCOM K.K. -00-40-B5 (hex) VIDEO TECHNOLOGY COMPUTERS LTD -00-40-B6 (hex) COMPUTERM CORPORATION -00-40-B7 (hex) STEALTH COMPUTER SYSTEMS -00-40-B8 (hex) IDEA ASSOCIATES -00-40-B9 (hex) MACQ ELECTRONIQUE SA -00-40-BA (hex) ALLIANT COMPUTER SYSTEMS CORP. -00-40-BB (hex) GOLDSTAR CABLE CO., LTD. -00-40-BC (hex) ALGORITHMICS LTD. -00-40-BD (hex) STARLIGHT NETWORKS, INC. -00-40-BE (hex) BOEING DEFENSE & SPACE -00-40-BF (hex) CHANNEL SYSTEMS INTERN'L INC. -00-40-C0 (hex) VISTA CONTROLS CORPORATION -00-40-C1 (hex) BIZERBA-WERKE WILHEIM KRAUT -00-40-C2 (hex) APPLIED COMPUTING DEVICES -00-40-C3 (hex) FISCHER AND PORTER CO. -00-40-C4 (hex) KINKEI SYSTEM CORPORATION -00-40-C5 (hex) MICOM COMMUNICATIONS INC. -00-40-C6 (hex) FIBERNET RESEARCH, INC. -00-40-C7 (hex) RUBY TECH CORPORATION -00-40-C8 (hex) MILAN TECHNOLOGY CORPORATION -00-40-C9 (hex) NCUBE -00-40-CA (hex) FIRST INTERNAT'L COMPUTER, INC -00-40-CB (hex) LANWAN TECHNOLOGIES -00-40-CC (hex) SILCOM MANUF'G TECHNOLOGY INC. -00-40-CD (hex) TERA MICROSYSTEMS, INC. -00-40-CE (hex) NET-SOURCE, INC. -00-40-CF (hex) STRAWBERRY TREE, INC. -00-40-D0 (hex) MITAC INTERNATIONAL CORP. -00-40-D1 (hex) FUKUDA DENSHI CO., LTD. -00-40-D2 (hex) PAGINE CORPORATION -00-40-D3 (hex) KIMPSION INTERNATIONAL CORP. -00-40-D4 (hex) GAGE TALKER CORP. -00-40-D5 (hex) Sartorius Mechatronics T&H GmbH -00-40-D6 (hex) LOCAMATION B.V. -00-40-D7 (hex) STUDIO GEN INC. -00-40-D8 (hex) OCEAN OFFICE AUTOMATION LTD. -00-40-D9 (hex) AMERICAN MEGATRENDS INC. -00-40-DA (hex) TELSPEC LTD -00-40-DB (hex) ADVANCED TECHNICAL SOLUTIONS -00-40-DC (hex) TRITEC ELECTRONIC GMBH -00-40-DD (hex) HONG TECHNOLOGIES -00-40-DE (hex) ELETTRONICA SAN GIORGIO -00-40-DF (hex) DIGALOG SYSTEMS, INC. -00-40-E0 (hex) ATOMWIDE LTD. -00-40-E1 (hex) MARNER INTERNATIONAL, INC. -00-40-E2 (hex) MESA RIDGE TECHNOLOGIES, INC. -00-40-E3 (hex) QUIN SYSTEMS LTD -00-40-E4 (hex) E-M TECHNOLOGY, INC. -00-40-E5 (hex) SYBUS CORPORATION -00-40-E6 (hex) C.A.E.N. -00-40-E7 (hex) ARNOS INSTRUMENTS & COMPUTER -00-40-E8 (hex) CHARLES RIVER DATA SYSTEMS,INC -00-40-E9 (hex) ACCORD SYSTEMS, INC. -00-40-EA (hex) PLAIN TREE SYSTEMS INC -00-40-EB (hex) MARTIN MARIETTA CORPORATION -00-40-EC (hex) MIKASA SYSTEM ENGINEERING -00-40-ED (hex) NETWORK CONTROLS INT'NATL INC. -00-40-EE (hex) OPTIMEM -00-40-EF (hex) HYPERCOM, INC. -00-40-F0 (hex) MICRO SYSTEMS, INC. -00-40-F1 (hex) CHUO ELECTRONICS CO., LTD. -00-40-F2 (hex) JANICH & KLASS COMPUTERTECHNIK -00-40-F3 (hex) NETCOR -00-40-F4 (hex) CAMEO COMMUNICATIONS, INC. -00-40-F5 (hex) OEM ENGINES -00-40-F6 (hex) KATRON COMPUTERS INC. -00-40-F7 (hex) Polaroid Corporation -00-40-F8 (hex) SYSTEMHAUS DISCOM -00-40-F9 (hex) COMBINET -00-40-FA (hex) MICROBOARDS, INC. -00-40-FB (hex) CASCADE COMMUNICATIONS CORP. -00-40-FC (hex) IBR COMPUTER TECHNIK GMBH -00-40-FD (hex) LXE -00-40-FE (hex) SYMPLEX COMMUNICATIONS -00-40-FF (hex) TELEBIT CORPORATION -00-42-52 (hex) RLX Technologies -00-45-01 (hex) Versus Technology, Inc. -00-50-00 (hex) NEXO COMMUNICATIONS, INC. -00-50-01 (hex) YAMASHITA SYSTEMS CORP. -00-50-02 (hex) OMNISEC AG -00-50-03 (hex) GRETAG MACBETH AG -00-50-04 (hex) 3COM CORPORATION -00-50-06 (hex) TAC AB -00-50-07 (hex) SIEMENS TELECOMMUNICATION SYSTEMS LIMITED -00-50-08 (hex) TIVA MICROCOMPUTER CORP. (TMC) -00-50-09 (hex) PHILIPS BROADBAND NETWORKS -00-50-0A (hex) IRIS TECHNOLOGIES, INC. -00-50-0B (hex) CISCO SYSTEMS, INC. -00-50-0C (hex) e-Tek Labs, Inc. -00-50-0D (hex) SATORI ELECTORIC CO., LTD. -00-50-0E (hex) CHROMATIS NETWORKS, INC. -00-50-0F (hex) CISCO SYSTEMS, INC. -00-50-10 (hex) NovaNET Learning, Inc. -00-50-12 (hex) CBL - GMBH -00-50-13 (hex) Chaparral Network Storage -00-50-14 (hex) CISCO SYSTEMS, INC. -00-50-15 (hex) BRIGHT STAR ENGINEERING -00-50-16 (hex) SST/WOODHEAD INDUSTRIES -00-50-17 (hex) RSR S.R.L. -00-50-18 (hex) AMIT, Inc. -00-50-19 (hex) SPRING TIDE NETWORKS, INC. -00-50-1A (hex) IQinVision -00-50-1B (hex) ABL CANADA, INC. -00-50-1C (hex) JATOM SYSTEMS, INC. -00-50-1E (hex) Miranda Technologies, Inc. -00-50-1F (hex) MRG SYSTEMS, LTD. -00-50-20 (hex) MEDIASTAR CO., LTD. -00-50-21 (hex) EIS INTERNATIONAL, INC. -00-50-22 (hex) ZONET TECHNOLOGY, INC. -00-50-23 (hex) PG DESIGN ELECTRONICS, INC. -00-50-24 (hex) NAVIC SYSTEMS, INC. -00-50-26 (hex) COSYSTEMS, INC. -00-50-27 (hex) GENICOM CORPORATION -00-50-28 (hex) AVAL COMMUNICATIONS -00-50-29 (hex) 1394 PRINTER WORKING GROUP -00-50-2A (hex) CISCO SYSTEMS, INC. -00-50-2B (hex) GENRAD LTD. -00-50-2C (hex) SOYO COMPUTER, INC. -00-50-2D (hex) ACCEL, INC. -00-50-2E (hex) CAMBEX CORPORATION -00-50-2F (hex) TollBridge Technologies, Inc. -00-50-30 (hex) FUTURE PLUS SYSTEMS -00-50-31 (hex) AEROFLEX LABORATORIES, INC. -00-50-32 (hex) PICAZO COMMUNICATIONS, INC. -00-50-33 (hex) MAYAN NETWORKS -00-50-36 (hex) NETCAM, LTD. -00-50-37 (hex) KOGA ELECTRONICS CO. -00-50-38 (hex) DAIN TELECOM CO., LTD. -00-50-39 (hex) MARINER NETWORKS -00-50-3A (hex) DATONG ELECTRONICS LTD. -00-50-3B (hex) MEDIAFIRE CORPORATION -00-50-3C (hex) TSINGHUA NOVEL ELECTRONICS -00-50-3E (hex) CISCO SYSTEMS, INC. -00-50-3F (hex) ANCHOR GAMES -00-50-40 (hex) Panasonic Electric Works Co., Ltd. -00-50-41 (hex) Coretronic Corporation -00-50-42 (hex) SCI MANUFACTURING SINGAPORE PTE, LTD. -00-50-43 (hex) MARVELL SEMICONDUCTOR, INC. -00-50-44 (hex) ASACA CORPORATION -00-50-45 (hex) RIOWORKS SOLUTIONS, INC. -00-50-46 (hex) MENICX INTERNATIONAL CO., LTD. -00-50-47 (hex) PRIVATE -00-50-48 (hex) INFOLIBRIA -00-50-49 (hex) Arbor Networks Inc -00-50-4A (hex) ELTECO A.S. -00-50-4B (hex) BARCONET N.V. -00-50-4C (hex) Galil Motion Control -00-50-4D (hex) Tokyo Electron Device Limited -00-50-4E (hex) SIERRA MONITOR CORP. -00-50-4F (hex) OLENCOM ELECTRONICS -00-50-50 (hex) CISCO SYSTEMS, INC. -00-50-51 (hex) IWATSU ELECTRIC CO., LTD. -00-50-52 (hex) TIARA NETWORKS, INC. -00-50-53 (hex) CISCO SYSTEMS, INC. -00-50-54 (hex) CISCO SYSTEMS, INC. -00-50-55 (hex) DOMS A/S -00-50-56 (hex) VMware, Inc. -00-50-57 (hex) BROADBAND ACCESS SYSTEMS -00-50-58 (hex) VegaStream Group Limted -00-50-59 (hex) iBAHN -00-50-5A (hex) NETWORK ALCHEMY, INC. -00-50-5B (hex) KAWASAKI LSI U.S.A., INC. -00-50-5C (hex) TUNDO CORPORATION -00-50-5E (hex) DIGITEK MICROLOGIC S.A. -00-50-5F (hex) BRAND INNOVATORS -00-50-60 (hex) TANDBERG TELECOM AS -00-50-62 (hex) KOUWELL ELECTRONICS CORP. ** -00-50-63 (hex) OY COMSEL SYSTEM AB -00-50-64 (hex) CAE ELECTRONICS -00-50-65 (hex) TDK-Lambda Corporation -00-50-66 (hex) AtecoM GmbH advanced telecomunication modules -00-50-67 (hex) AEROCOMM, INC. -00-50-68 (hex) ELECTRONIC INDUSTRIES ASSOCIATION -00-50-69 (hex) PixStream Incorporated -00-50-6A (hex) EDEVA, INC. -00-50-6B (hex) SPX-ATEG -00-50-6C (hex) Beijer Electronics Products AB -00-50-6D (hex) VIDEOJET SYSTEMS -00-50-6E (hex) CORDER ENGINEERING CORPORATION -00-50-6F (hex) G-CONNECT -00-50-70 (hex) CHAINTECH COMPUTER CO., LTD. -00-50-71 (hex) AIWA CO., LTD. -00-50-72 (hex) CORVIS CORPORATION -00-50-73 (hex) CISCO SYSTEMS, INC. -00-50-74 (hex) ADVANCED HI-TECH CORP. -00-50-75 (hex) KESTREL SOLUTIONS -00-50-76 (hex) IBM -00-50-77 (hex) PROLIFIC TECHNOLOGY, INC. -00-50-78 (hex) MEGATON HOUSE, LTD. -00-50-79 (hex) PRIVATE -00-50-7A (hex) XPEED, INC. -00-50-7B (hex) MERLOT COMMUNICATIONS -00-50-7C (hex) VIDEOCON AG -00-50-7D (hex) IFP -00-50-7E (hex) NEWER TECHNOLOGY -00-50-7F (hex) DrayTek Corp. -00-50-80 (hex) CISCO SYSTEMS, INC. -00-50-81 (hex) MURATA MACHINERY, LTD. -00-50-82 (hex) FORESSON CORPORATION -00-50-83 (hex) GILBARCO, INC. -00-50-84 (hex) ATL PRODUCTS -00-50-86 (hex) TELKOM SA, LTD. -00-50-87 (hex) TERASAKI ELECTRIC CO., LTD. -00-50-88 (hex) AMANO CORPORATION -00-50-89 (hex) SAFETY MANAGEMENT SYSTEMS -00-50-8B (hex) Hewlett Packard -00-50-8C (hex) RSI SYSTEMS -00-50-8D (hex) ABIT COMPUTER CORPORATION -00-50-8E (hex) OPTIMATION, INC. -00-50-8F (hex) ASITA TECHNOLOGIES INT'L LTD. -00-50-90 (hex) DCTRI -00-50-91 (hex) NETACCESS, INC. -00-50-92 (hex) RIGAKU INDUSTRIAL CORPORATION -00-50-93 (hex) BOEING -00-50-94 (hex) PACE MICRO TECHNOLOGY PLC -00-50-95 (hex) PERACOM NETWORKS -00-50-96 (hex) SALIX TECHNOLOGIES, INC. -00-50-97 (hex) MMC-EMBEDDED COMPUTERTECHNIK GmbH -00-50-98 (hex) GLOBALOOP, LTD. -00-50-99 (hex) 3COM EUROPE, LTD. -00-50-9A (hex) TAG ELECTRONIC SYSTEMS -00-50-9B (hex) SWITCHCORE AB -00-50-9C (hex) BETA RESEARCH -00-50-9D (hex) THE INDUSTREE B.V. -00-50-9E (hex) Les Technologies SoftAcoustik Inc. -00-50-9F (hex) HORIZON COMPUTER -00-50-A0 (hex) DELTA COMPUTER SYSTEMS, INC. -00-50-A1 (hex) CARLO GAVAZZI, INC. -00-50-A2 (hex) CISCO SYSTEMS, INC. -00-50-A3 (hex) TransMedia Communications, Inc. -00-50-A4 (hex) IO TECH, INC. -00-50-A5 (hex) CAPITOL BUSINESS SYSTEMS, LTD. -00-50-A6 (hex) OPTRONICS -00-50-A7 (hex) CISCO SYSTEMS, INC. -00-50-A8 (hex) OpenCon Systems, Inc. -00-50-A9 (hex) MOLDAT WIRELESS TECHNOLGIES -00-50-AA (hex) KONICA MINOLTA HOLDINGS, INC. -00-50-AB (hex) NALTEC, Inc. -00-50-AC (hex) MAPLE COMPUTER CORPORATION -00-50-AD (hex) CommUnique Wireless Corp. -00-50-AE (hex) IWAKI ELECTRONICS CO., LTD. -00-50-AF (hex) INTERGON, INC. -00-50-B0 (hex) TECHNOLOGY ATLANTA CORPORATION -00-50-B1 (hex) GIDDINGS & LEWIS -00-50-B2 (hex) BRODEL AUTOMATION -00-50-B3 (hex) VOICEBOARD CORPORATION -00-50-B4 (hex) SATCHWELL CONTROL SYSTEMS, LTD -00-50-B5 (hex) FICHET-BAUCHE -00-50-B6 (hex) GOOD WAY IND. CO., LTD. -00-50-B7 (hex) BOSER TECHNOLOGY CO., LTD. -00-50-B8 (hex) INOVA COMPUTERS GMBH & CO. KG -00-50-B9 (hex) XITRON TECHNOLOGIES, INC. -00-50-BA (hex) D-LINK -00-50-BB (hex) CMS TECHNOLOGIES -00-50-BC (hex) HAMMER STORAGE SOLUTIONS -00-50-BD (hex) CISCO SYSTEMS, INC. -00-50-BE (hex) FAST MULTIMEDIA AG -00-50-BF (hex) MOTOTECH INC. -00-50-C0 (hex) GATAN, INC. -00-50-C1 (hex) GEMFLEX NETWORKS, LTD. -00-50-C2 (hex) IEEE REGISTRATION AUTHORITY -00-50-C4 (hex) IMD -00-50-C5 (hex) ADS Technologies, Inc -00-50-C6 (hex) LOOP TELECOMMUNICATION INTERNATIONAL, INC. -00-50-C8 (hex) ADDONICS COMMUNICATIONS, INC. -00-50-C9 (hex) MASPRO DENKOH CORP. -00-50-CA (hex) NET TO NET TECHNOLOGIES -00-50-CB (hex) JETTER -00-50-CC (hex) XYRATEX -00-50-CD (hex) DIGIANSWER A/S -00-50-CE (hex) LG INTERNATIONAL CORP. -00-50-CF (hex) VANLINK COMMUNICATION TECHNOLOGY RESEARCH INSTITUTE -00-50-D0 (hex) MINERVA SYSTEMS -00-50-D1 (hex) CISCO SYSTEMS, INC. -00-50-D2 (hex) CMC Electronics Inc -00-50-D3 (hex) DIGITAL AUDIO PROCESSING PTY. LTD. -00-50-D4 (hex) JOOHONG INFORMATION & -00-50-D5 (hex) AD SYSTEMS CORP. -00-50-D6 (hex) ATLAS COPCO TOOLS AB -00-50-D7 (hex) TELSTRAT -00-50-D8 (hex) UNICORN COMPUTER CORP. -00-50-D9 (hex) ENGETRON-ENGENHARIA ELETRONICA IND. e COM. LTDA -00-50-DA (hex) 3COM CORPORATION -00-50-DB (hex) CONTEMPORARY CONTROL -00-50-DC (hex) TAS TELEFONBAU A. SCHWABE GMBH & CO. KG -00-50-DD (hex) SERRA SOLDADURA, S.A. -00-50-DE (hex) SIGNUM SYSTEMS CORP. -00-50-DF (hex) AirFiber, Inc. -00-50-E1 (hex) NS TECH ELECTRONICS SDN BHD -00-50-E2 (hex) CISCO SYSTEMS, INC. -00-50-E3 (hex) Motorola, Inc. -00-50-E4 (hex) APPLE COMPUTER, INC. -00-50-E6 (hex) HAKUSAN CORPORATION -00-50-E7 (hex) PARADISE INNOVATIONS (ASIA) -00-50-E8 (hex) NOMADIX INC. -00-50-EA (hex) XEL COMMUNICATIONS, INC. -00-50-EB (hex) ALPHA-TOP CORPORATION -00-50-EC (hex) OLICOM A/S -00-50-ED (hex) ANDA NETWORKS -00-50-EE (hex) TEK DIGITEL CORPORATION -00-50-EF (hex) SPE Systemhaus GmbH -00-50-F0 (hex) CISCO SYSTEMS, INC. -00-50-F1 (hex) LIBIT SIGNAL PROCESSING, LTD. -00-50-F2 (hex) MICROSOFT CORP. -00-50-F3 (hex) GLOBAL NET INFORMATION CO., Ltd. -00-50-F4 (hex) SIGMATEK GMBH & CO. KG -00-50-F6 (hex) PAN-INTERNATIONAL INDUSTRIAL CORP. -00-50-F7 (hex) VENTURE MANUFACTURING (SINGAPORE) LTD. -00-50-F8 (hex) ENTREGA TECHNOLOGIES, INC. -00-50-F9 (hex) SENSORMATIC ACD -00-50-FA (hex) OXTEL, LTD. -00-50-FB (hex) VSK ELECTRONICS -00-50-FC (hex) EDIMAX TECHNOLOGY CO., LTD. -00-50-FD (hex) VISIONCOMM CO., LTD. -00-50-FE (hex) PCTVnet ASA -00-50-FF (hex) HAKKO ELECTRONICS CO., LTD. -00-52-18 (hex) Wuxi Keboda Electron Co.Ltd -00-60-00 (hex) XYCOM INC. -00-60-01 (hex) InnoSys, Inc. -00-60-02 (hex) SCREEN SUBTITLING SYSTEMS, LTD -00-60-03 (hex) TERAOKA WEIGH SYSTEM PTE, LTD. -00-60-04 (hex) COMPUTADORES MODULARES SA -00-60-05 (hex) FEEDBACK DATA LTD. -00-60-06 (hex) SOTEC CO., LTD -00-60-07 (hex) ACRES GAMING, INC. -00-60-08 (hex) 3COM CORPORATION -00-60-09 (hex) CISCO SYSTEMS, INC. -00-60-0A (hex) SORD COMPUTER CORPORATION -00-60-0B (hex) LOGWARE GmbH -00-60-0C (hex) APPLIED DATA SYSTEMS, INC. -00-60-0D (hex) Digital Logic GmbH -00-60-0E (hex) WAVENET INTERNATIONAL, INC. -00-60-0F (hex) WESTELL, INC. -00-60-10 (hex) NETWORK MACHINES, INC. -00-60-11 (hex) CRYSTAL SEMICONDUCTOR CORP. -00-60-12 (hex) POWER COMPUTING CORPORATION -00-60-13 (hex) NETSTAL MASCHINEN AG -00-60-14 (hex) EDEC CO., LTD. -00-60-15 (hex) NET2NET CORPORATION -00-60-16 (hex) CLARIION -00-60-17 (hex) TOKIMEC INC. -00-60-18 (hex) STELLAR ONE CORPORATION -00-60-19 (hex) Roche Diagnostics -00-60-1A (hex) KEITHLEY INSTRUMENTS -00-60-1B (hex) MESA ELECTRONICS -00-60-1C (hex) TELXON CORPORATION -00-60-1D (hex) LUCENT TECHNOLOGIES -00-60-1E (hex) SOFTLAB, INC. -00-60-1F (hex) STALLION TECHNOLOGIES -00-60-20 (hex) PIVOTAL NETWORKING, INC. -00-60-21 (hex) DSC CORPORATION -00-60-22 (hex) VICOM SYSTEMS, INC. -00-60-23 (hex) PERICOM SEMICONDUCTOR CORP. -00-60-24 (hex) GRADIENT TECHNOLOGIES, INC. -00-60-25 (hex) ACTIVE IMAGING PLC -00-60-26 (hex) VIKING COMPONENTS, INC. -00-60-27 (hex) Superior Modular Products -00-60-28 (hex) MACROVISION CORPORATION -00-60-29 (hex) CARY PERIPHERALS INC. -00-60-2A (hex) SYMICRON COMPUTER COMMUNICATIONS, LTD. -00-60-2B (hex) PEAK AUDIO -00-60-2C (hex) LINX Data Terminals, Inc. -00-60-2D (hex) ALERTON TECHNOLOGIES, INC. -00-60-2E (hex) CYCLADES CORPORATION -00-60-2F (hex) CISCO SYSTEMS, INC. -00-60-30 (hex) VILLAGE TRONIC ENTWICKLUNG -00-60-31 (hex) HRK SYSTEMS -00-60-32 (hex) I-CUBE, INC. -00-60-33 (hex) ACUITY IMAGING, INC. -00-60-34 (hex) ROBERT BOSCH GmbH -00-60-35 (hex) DALLAS SEMICONDUCTOR, INC. -00-60-36 (hex) AIT Austrian Institute of Technology GmbH -00-60-37 (hex) NXP Semiconductors -00-60-38 (hex) Nortel Networks -00-60-39 (hex) SanCom Technology, Inc. -00-60-3A (hex) QUICK CONTROLS LTD. -00-60-3B (hex) AMTEC spa -00-60-3C (hex) HAGIWARA SYS-COM CO., LTD. -00-60-3D (hex) 3CX -00-60-3E (hex) CISCO SYSTEMS, INC. -00-60-3F (hex) PATAPSCO DESIGNS -00-60-40 (hex) NETRO CORP. -00-60-41 (hex) Yokogawa Electric Corporation -00-60-42 (hex) TKS (USA), INC. -00-60-43 (hex) iDirect, INC. -00-60-44 (hex) LITTON/POLY-SCIENTIFIC -00-60-45 (hex) PATHLIGHT TECHNOLOGIES -00-60-46 (hex) VMETRO, INC. -00-60-47 (hex) CISCO SYSTEMS, INC. -00-60-48 (hex) EMC CORPORATION -00-60-49 (hex) VINA TECHNOLOGIES -00-60-4A (hex) SAIC IDEAS GROUP -00-60-4B (hex) Safe-com GmbH & Co. KG -00-60-4C (hex) SAGEM COMMUNICATION -00-60-4D (hex) MMC NETWORKS, INC. -00-60-4E (hex) CYCLE COMPUTER CORPORATION, INC. -00-60-4F (hex) SUZUKI MFG. CO., LTD. -00-60-50 (hex) INTERNIX INC. -00-60-51 (hex) QUALITY SEMICONDUCTOR -00-60-52 (hex) PERIPHERALS ENTERPRISE CO., Ltd. -00-60-53 (hex) TOYODA MACHINE WORKS, LTD. -00-60-54 (hex) CONTROLWARE GMBH -00-60-55 (hex) CORNELL UNIVERSITY -00-60-56 (hex) NETWORK TOOLS, INC. -00-60-57 (hex) MURATA MANUFACTURING CO., LTD. -00-60-58 (hex) COPPER MOUNTAIN COMMUNICATIONS, INC. -00-60-59 (hex) TECHNICAL COMMUNICATIONS CORP. -00-60-5A (hex) CELCORE, INC. -00-60-5B (hex) IntraServer Technology, Inc. -00-60-5C (hex) CISCO SYSTEMS, INC. -00-60-5D (hex) SCANIVALVE CORP. -00-60-5E (hex) LIBERTY TECHNOLOGY NETWORKING -00-60-5F (hex) NIPPON UNISOFT CORPORATION -00-60-60 (hex) DAWNING TECHNOLOGIES, INC. -00-60-61 (hex) WHISTLE COMMUNICATIONS CORP. -00-60-62 (hex) TELESYNC, INC. -00-60-63 (hex) PSION DACOM PLC. -00-60-64 (hex) NETCOMM LIMITED -00-60-65 (hex) BERNECKER & RAINER INDUSTRIE-ELEKTRONIC GmbH -00-60-66 (hex) LACROIX TECHNOLGIE -00-60-67 (hex) ACER NETXUS INC. -00-60-68 (hex) Dialogic Corporation -00-60-69 (hex) Brocade Communications Systems, Inc. -00-60-6A (hex) MITSUBISHI WIRELESS COMMUNICATIONS. INC. -00-60-6B (hex) Synclayer Inc. -00-60-6C (hex) ARESCOM -00-60-6D (hex) DIGITAL EQUIPMENT CORP. -00-60-6E (hex) DAVICOM SEMICONDUCTOR, INC. -00-60-6F (hex) CLARION CORPORATION OF AMERICA -00-60-70 (hex) CISCO SYSTEMS, INC. -00-60-71 (hex) MIDAS LAB, INC. -00-60-72 (hex) VXL INSTRUMENTS, LIMITED -00-60-73 (hex) REDCREEK COMMUNICATIONS, INC. -00-60-74 (hex) QSC AUDIO PRODUCTS -00-60-75 (hex) PENTEK, INC. -00-60-76 (hex) SCHLUMBERGER TECHNOLOGIES RETAIL PETROLEUM SYSTEMS -00-60-77 (hex) PRISA NETWORKS -00-60-78 (hex) POWER MEASUREMENT LTD. -00-60-79 (hex) Mainstream Data, Inc. -00-60-7A (hex) DVS GmbH -00-60-7B (hex) FORE SYSTEMS, INC. -00-60-7C (hex) WaveAccess, Ltd. -00-60-7D (hex) SENTIENT NETWORKS INC. -00-60-7E (hex) GIGALABS, INC. -00-60-7F (hex) AURORA TECHNOLOGIES, INC. -00-60-80 (hex) MICROTRONIX DATACOM LTD. -00-60-81 (hex) TV/COM INTERNATIONAL -00-60-82 (hex) NOVALINK TECHNOLOGIES, INC. -00-60-83 (hex) CISCO SYSTEMS, INC. -00-60-84 (hex) DIGITAL VIDEO -00-60-85 (hex) Storage Concepts -00-60-86 (hex) LOGIC REPLACEMENT TECH. LTD. -00-60-87 (hex) KANSAI ELECTRIC CO., LTD. -00-60-88 (hex) WHITE MOUNTAIN DSP, INC. -00-60-89 (hex) XATA -00-60-8A (hex) CITADEL COMPUTER -00-60-8B (hex) ConferTech International -00-60-8C (hex) 3COM CORPORATION -00-60-8D (hex) UNIPULSE CORP. -00-60-8E (hex) HE ELECTRONICS, TECHNOLOGIE & SYSTEMTECHNIK GmbH -00-60-8F (hex) TEKRAM TECHNOLOGY CO., LTD. -00-60-90 (hex) ABLE COMMUNICATIONS, INC. -00-60-91 (hex) FIRST PACIFIC NETWORKS, INC. -00-60-92 (hex) MICRO/SYS, INC. -00-60-93 (hex) VARIAN -00-60-94 (hex) IBM CORP. -00-60-95 (hex) ACCU-TIME SYSTEMS, INC. -00-60-96 (hex) T.S. MICROTECH INC. -00-60-97 (hex) 3COM CORPORATION -00-60-98 (hex) HT COMMUNICATIONS -00-60-99 (hex) SBE, Inc. -00-60-9A (hex) NJK TECHNO CO. -00-60-9B (hex) ASTRO-MED, INC. -00-60-9C (hex) Perkin-Elmer Incorporated -00-60-9D (hex) PMI FOOD EQUIPMENT GROUP -00-60-9E (hex) ASC X3 - INFORMATION TECHNOLOGY STANDARDS SECRETARIATS -00-60-9F (hex) PHAST CORPORATION -00-60-A0 (hex) SWITCHED NETWORK TECHNOLOGIES, INC. -00-60-A1 (hex) VPNet, Inc. -00-60-A2 (hex) NIHON UNISYS LIMITED CO. -00-60-A3 (hex) CONTINUUM TECHNOLOGY CORP. -00-60-A4 (hex) GRINAKER SYSTEM TECHNOLOGIES -00-60-A5 (hex) PERFORMANCE TELECOM CORP. -00-60-A6 (hex) PARTICLE MEASURING SYSTEMS -00-60-A7 (hex) MICROSENS GmbH & CO. KG -00-60-A8 (hex) TIDOMAT AB -00-60-A9 (hex) GESYTEC MbH -00-60-AA (hex) INTELLIGENT DEVICES INC. (IDI) -00-60-AB (hex) LARSCOM INCORPORATED -00-60-AC (hex) RESILIENCE CORPORATION -00-60-AD (hex) MegaChips Corporation -00-60-AE (hex) TRIO INFORMATION SYSTEMS AB -00-60-AF (hex) PACIFIC MICRO DATA, INC. -00-60-B0 (hex) HEWLETT-PACKARD CO. -00-60-B1 (hex) INPUT/OUTPUT, INC. -00-60-B2 (hex) PROCESS CONTROL CORP. -00-60-B3 (hex) Z-COM, INC. -00-60-B4 (hex) GLENAYRE R&D INC. -00-60-B5 (hex) KEBA GmbH -00-60-B6 (hex) LAND COMPUTER CO., LTD. -00-60-B7 (hex) CHANNELMATIC, INC. -00-60-B8 (hex) CORELIS Inc. -00-60-B9 (hex) NEC Infrontia Corporation -00-60-BA (hex) SAHARA NETWORKS, INC. -00-60-BB (hex) CABLETRON - NETLINK, INC. -00-60-BC (hex) KeunYoung Electronics & Communication Co., Ltd. -00-60-BD (hex) HUBBELL-PULSECOM -00-60-BE (hex) WEBTRONICS -00-60-BF (hex) MACRAIGOR SYSTEMS, INC. -00-60-C0 (hex) Nera Networks AS -00-60-C1 (hex) WaveSpan Corporation -00-60-C2 (hex) MPL AG -00-60-C3 (hex) NETVISION CORPORATION -00-60-C4 (hex) SOLITON SYSTEMS K.K. -00-60-C5 (hex) ANCOT CORP. -00-60-C6 (hex) DCS AG -00-60-C7 (hex) AMATI COMMUNICATIONS CORP. -00-60-C8 (hex) KUKA WELDING SYSTEMS & ROBOTS -00-60-C9 (hex) ControlNet, Inc. -00-60-CA (hex) HARMONIC SYSTEMS INCORPORATED -00-60-CB (hex) HITACHI ZOSEN CORPORATION -00-60-CC (hex) EMTRAK, INCORPORATED -00-60-CD (hex) VideoServer, Inc. -00-60-CE (hex) ACCLAIM COMMUNICATIONS -00-60-CF (hex) ALTEON NETWORKS, INC. -00-60-D0 (hex) SNMP RESEARCH INCORPORATED -00-60-D1 (hex) CASCADE COMMUNICATIONS -00-60-D2 (hex) LUCENT TECHNOLOGIES TAIWAN TELECOMMUNICATIONS CO., LTD. -00-60-D3 (hex) AT&T -00-60-D4 (hex) ELDAT COMMUNICATION LTD. -00-60-D5 (hex) MIYACHI TECHNOS CORP. -00-60-D6 (hex) NovAtel Wireless Technologies Ltd. -00-60-D7 (hex) ECOLE POLYTECHNIQUE FEDERALE DE LAUSANNE (EPFL) -00-60-D8 (hex) ELMIC SYSTEMS, INC. -00-60-D9 (hex) TRANSYS NETWORKS INC. -00-60-DA (hex) JBM ELECTRONICS CO. -00-60-DB (hex) NTP ELEKTRONIK A/S -00-60-DC (hex) Toyo Network Systems & System Integration Co. LTD -00-60-DD (hex) MYRICOM, INC. -00-60-DE (hex) Kayser-Threde GmbH -00-60-DF (hex) Brocade Communications Systems, Inc. -00-60-E0 (hex) AXIOM TECHNOLOGY CO., LTD. -00-60-E1 (hex) ORCKIT COMMUNICATIONS LTD. -00-60-E2 (hex) QUEST ENGINEERING & DEVELOPMENT -00-60-E3 (hex) ARBIN INSTRUMENTS -00-60-E4 (hex) COMPUSERVE, INC. -00-60-E5 (hex) FUJI AUTOMATION CO., LTD. -00-60-E6 (hex) SHOMITI SYSTEMS INCORPORATED -00-60-E7 (hex) RANDATA -00-60-E8 (hex) HITACHI COMPUTER PRODUCTS (AMERICA), INC. -00-60-E9 (hex) ATOP TECHNOLOGIES, INC. -00-60-EA (hex) StreamLogic -00-60-EB (hex) FOURTHTRACK SYSTEMS -00-60-EC (hex) HERMARY OPTO ELECTRONICS INC. -00-60-ED (hex) RICARDO TEST AUTOMATION LTD. -00-60-EE (hex) APOLLO -00-60-EF (hex) FLYTECH TECHNOLOGY CO., LTD. -00-60-F0 (hex) JOHNSON & JOHNSON MEDICAL, INC -00-60-F1 (hex) EXP COMPUTER, INC. -00-60-F2 (hex) LASERGRAPHICS, INC. -00-60-F3 (hex) Performance Analysis Broadband, Spirent plc -00-60-F4 (hex) ADVANCED COMPUTER SOLUTIONS, Inc. -00-60-F5 (hex) ICON WEST, INC. -00-60-F6 (hex) NEXTEST COMMUNICATIONS PRODUCTS, INC. -00-60-F7 (hex) DATAFUSION SYSTEMS -00-60-F8 (hex) Loran International Technologies Inc. -00-60-F9 (hex) DIAMOND LANE COMMUNICATIONS -00-60-FA (hex) EDUCATIONAL TECHNOLOGY RESOURCES, INC. -00-60-FB (hex) PACKETEER, INC. -00-60-FC (hex) CONSERVATION THROUGH INNOVATION LTD. -00-60-FD (hex) NetICs, Inc. -00-60-FE (hex) LYNX SYSTEM DEVELOPERS, INC. -00-60-FF (hex) QuVis, Inc. -00-64-40 (hex) Cisco Systems -00-70-B0 (hex) M/A-COM INC. COMPANIES -00-70-B3 (hex) DATA RECALL LTD. -00-80-00 (hex) MULTITECH SYSTEMS, INC. -00-80-01 (hex) PERIPHONICS CORPORATION -00-80-02 (hex) SATELCOM (UK) LTD -00-80-03 (hex) HYTEC ELECTRONICS LTD. -00-80-04 (hex) ANTLOW COMMUNICATIONS, LTD. -00-80-05 (hex) CACTUS COMPUTER INC. -00-80-06 (hex) COMPUADD CORPORATION -00-80-07 (hex) DLOG NC-SYSTEME -00-80-08 (hex) DYNATECH COMPUTER SYSTEMS -00-80-09 (hex) JUPITER SYSTEMS, INC. -00-80-0A (hex) JAPAN COMPUTER CORP. -00-80-0B (hex) CSK CORPORATION -00-80-0C (hex) VIDECOM LIMITED -00-80-0D (hex) VOSSWINKEL F.U. -00-80-0E (hex) ATLANTIX CORPORATION -00-80-0F (hex) STANDARD MICROSYSTEMS -00-80-10 (hex) COMMODORE INTERNATIONAL -00-80-11 (hex) DIGITAL SYSTEMS INT'L. INC. -00-80-12 (hex) INTEGRATED MEASUREMENT SYSTEMS -00-80-13 (hex) THOMAS-CONRAD CORPORATION -00-80-14 (hex) ESPRIT SYSTEMS -00-80-15 (hex) SEIKO SYSTEMS, INC. -00-80-16 (hex) WANDEL AND GOLTERMANN -00-80-17 (hex) PFU LIMITED -00-80-18 (hex) KOBE STEEL, LTD. -00-80-19 (hex) DAYNA COMMUNICATIONS, INC. -00-80-1A (hex) BELL ATLANTIC -00-80-1B (hex) KODIAK TECHNOLOGY -00-80-1C (hex) NEWPORT SYSTEMS SOLUTIONS -00-80-1D (hex) INTEGRATED INFERENCE MACHINES -00-80-1E (hex) XINETRON, INC. -00-80-1F (hex) KRUPP ATLAS ELECTRONIK GMBH -00-80-20 (hex) NETWORK PRODUCTS -00-80-21 (hex) Alcatel Canada Inc. -00-80-22 (hex) SCAN-OPTICS -00-80-23 (hex) INTEGRATED BUSINESS NETWORKS -00-80-24 (hex) KALPANA, INC. -00-80-25 (hex) STOLLMANN GMBH -00-80-26 (hex) NETWORK PRODUCTS CORPORATION -00-80-27 (hex) ADAPTIVE SYSTEMS, INC. -00-80-28 (hex) TRADPOST (HK) LTD -00-80-29 (hex) EAGLE TECHNOLOGY, INC. -00-80-2A (hex) TEST SYSTEMS & SIMULATIONS INC -00-80-2B (hex) INTEGRATED MARKETING CO -00-80-2C (hex) THE SAGE GROUP PLC -00-80-2D (hex) XYLOGICS INC -00-80-2E (hex) CASTLE ROCK COMPUTING -00-80-2F (hex) NATIONAL INSTRUMENTS CORP. -00-80-30 (hex) NEXUS ELECTRONICS -00-80-31 (hex) BASYS, CORP. -00-80-32 (hex) ACCESS CO., LTD. -00-80-33 (hex) FORMATION, INC. -00-80-34 (hex) SMT GOUPIL -00-80-35 (hex) TECHNOLOGY WORKS, INC. -00-80-36 (hex) REFLEX MANUFACTURING SYSTEMS -00-80-37 (hex) Ericsson Group -00-80-38 (hex) DATA RESEARCH & APPLICATIONS -00-80-39 (hex) ALCATEL STC AUSTRALIA -00-80-3A (hex) VARITYPER, INC. -00-80-3B (hex) APT COMMUNICATIONS, INC. -00-80-3C (hex) TVS ELECTRONICS LTD -00-80-3D (hex) SURIGIKEN CO., LTD. -00-80-3E (hex) SYNERNETICS -00-80-3F (hex) TATUNG COMPANY -00-80-40 (hex) JOHN FLUKE MANUFACTURING CO. -00-80-41 (hex) VEB KOMBINAT ROBOTRON -00-80-42 (hex) Emerson Network Power -00-80-43 (hex) NETWORLD, INC. -00-80-44 (hex) SYSTECH COMPUTER CORP. -00-80-45 (hex) MATSUSHITA ELECTRIC IND. CO -00-80-46 (hex) UNIVERSITY OF TORONTO -00-80-47 (hex) IN-NET CORP. -00-80-48 (hex) COMPEX INCORPORATED -00-80-49 (hex) NISSIN ELECTRIC CO., LTD. -00-80-4A (hex) PRO-LOG -00-80-4B (hex) EAGLE TECHNOLOGIES PTY.LTD. -00-80-4C (hex) CONTEC CO., LTD. -00-80-4D (hex) CYCLONE MICROSYSTEMS, INC. -00-80-4E (hex) APEX COMPUTER COMPANY -00-80-4F (hex) DAIKIN INDUSTRIES, LTD. -00-80-50 (hex) ZIATECH CORPORATION -00-80-51 (hex) FIBERMUX -00-80-52 (hex) TECHNICALLY ELITE CONCEPTS -00-80-53 (hex) INTELLICOM, INC. -00-80-54 (hex) FRONTIER TECHNOLOGIES CORP. -00-80-55 (hex) FERMILAB -00-80-56 (hex) SPHINX ELEKTRONIK GMBH -00-80-57 (hex) ADSOFT, LTD. -00-80-58 (hex) PRINTER SYSTEMS CORPORATION -00-80-59 (hex) STANLEY ELECTRIC CO., LTD -00-80-5A (hex) TULIP COMPUTERS INTERNAT'L B.V -00-80-5B (hex) CONDOR SYSTEMS, INC. -00-80-5C (hex) AGILIS CORPORATION -00-80-5D (hex) CANSTAR -00-80-5E (hex) LSI LOGIC CORPORATION -00-80-5F (hex) Hewlett Packard -00-80-60 (hex) NETWORK INTERFACE CORPORATION -00-80-61 (hex) LITTON SYSTEMS, INC. -00-80-62 (hex) INTERFACE CO. -00-80-63 (hex) RICHARD HIRSCHMANN GMBH & CO. -00-80-64 (hex) WYSE TECHNOLOGY -00-80-65 (hex) CYBERGRAPHIC SYSTEMS PTY LTD. -00-80-66 (hex) ARCOM CONTROL SYSTEMS, LTD. -00-80-67 (hex) SQUARE D COMPANY -00-80-68 (hex) YAMATECH SCIENTIFIC LTD. -00-80-69 (hex) COMPUTONE SYSTEMS -00-80-6A (hex) ERI (EMPAC RESEARCH INC.) -00-80-6B (hex) SCHMID TELECOMMUNICATION -00-80-6C (hex) CEGELEC PROJECTS LTD -00-80-6D (hex) CENTURY SYSTEMS CORP. -00-80-6E (hex) NIPPON STEEL CORPORATION -00-80-6F (hex) ONELAN LTD. -00-80-70 (hex) COMPUTADORAS MICRON -00-80-71 (hex) SAI TECHNOLOGY -00-80-72 (hex) MICROPLEX SYSTEMS LTD. -00-80-73 (hex) DWB ASSOCIATES -00-80-74 (hex) FISHER CONTROLS -00-80-75 (hex) PARSYTEC GMBH -00-80-76 (hex) MCNC -00-80-77 (hex) BROTHER INDUSTRIES, LTD. -00-80-78 (hex) PRACTICAL PERIPHERALS, INC. -00-80-79 (hex) MICROBUS DESIGNS LTD. -00-80-7A (hex) AITECH SYSTEMS LTD. -00-80-7B (hex) ARTEL COMMUNICATIONS CORP. -00-80-7C (hex) FIBERCOM, INC. -00-80-7D (hex) EQUINOX SYSTEMS INC. -00-80-7E (hex) SOUTHERN PACIFIC LTD. -00-80-7F (hex) DY-4 INCORPORATED -00-80-80 (hex) DATAMEDIA CORPORATION -00-80-81 (hex) KENDALL SQUARE RESEARCH CORP. -00-80-82 (hex) PEP MODULAR COMPUTERS GMBH -00-80-83 (hex) AMDAHL -00-80-84 (hex) THE CLOUD INC. -00-80-85 (hex) H-THREE SYSTEMS CORPORATION -00-80-86 (hex) COMPUTER GENERATION INC. -00-80-87 (hex) OKI ELECTRIC INDUSTRY CO., LTD -00-80-88 (hex) VICTOR COMPANY OF JAPAN, LTD. -00-80-89 (hex) TECNETICS (PTY) LTD. -00-80-8A (hex) SUMMIT MICROSYSTEMS CORP. -00-80-8B (hex) DACOLL LIMITED -00-80-8C (hex) NetScout Systems, Inc. -00-80-8D (hex) WESTCOAST TECHNOLOGY B.V. -00-80-8E (hex) RADSTONE TECHNOLOGY -00-80-8F (hex) C. ITOH ELECTRONICS, INC. -00-80-90 (hex) MICROTEK INTERNATIONAL, INC. -00-80-91 (hex) TOKYO ELECTRIC CO.,LTD -00-80-92 (hex) Silex Technology, Inc. -00-80-93 (hex) XYRON CORPORATION -00-80-94 (hex) ALFA LAVAL AUTOMATION AB -00-80-95 (hex) BASIC MERTON HANDELSGES.M.B.H. -00-80-96 (hex) HUMAN DESIGNED SYSTEMS, INC. -00-80-97 (hex) CENTRALP AUTOMATISMES -00-80-98 (hex) TDK CORPORATION -00-80-99 (hex) KLOCKNER MOELLER IPC -00-80-9A (hex) NOVUS NETWORKS LTD -00-80-9B (hex) JUSTSYSTEM CORPORATION -00-80-9C (hex) LUXCOM, INC. -00-80-9D (hex) Commscraft Ltd. -00-80-9E (hex) DATUS GMBH -00-80-9F (hex) ALCATEL BUSINESS SYSTEMS -00-80-A0 (hex) EDISA HEWLETT PACKARD S/A -00-80-A1 (hex) MICROTEST, INC. -00-80-A2 (hex) CREATIVE ELECTRONIC SYSTEMS -00-80-A3 (hex) Lantronix -00-80-A4 (hex) LIBERTY ELECTRONICS -00-80-A5 (hex) SPEED INTERNATIONAL -00-80-A6 (hex) REPUBLIC TECHNOLOGY, INC. -00-80-A7 (hex) MEASUREX CORP. -00-80-A8 (hex) VITACOM CORPORATION -00-80-A9 (hex) CLEARPOINT RESEARCH -00-80-AA (hex) MAXPEED -00-80-AB (hex) DUKANE NETWORK INTEGRATION -00-80-AC (hex) IMLOGIX, DIVISION OF GENESYS -00-80-AD (hex) CNET TECHNOLOGY, INC. -00-80-AE (hex) HUGHES NETWORK SYSTEMS -00-80-AF (hex) ALLUMER CO., LTD. -00-80-B0 (hex) ADVANCED INFORMATION -00-80-B1 (hex) SOFTCOM A/S -00-80-B2 (hex) NETWORK EQUIPMENT TECHNOLOGIES -00-80-B3 (hex) AVAL DATA CORPORATION -00-80-B4 (hex) SOPHIA SYSTEMS -00-80-B5 (hex) UNITED NETWORKS INC. -00-80-B6 (hex) THEMIS COMPUTER -00-80-B7 (hex) STELLAR COMPUTER -00-80-B8 (hex) BUG, INCORPORATED -00-80-B9 (hex) ARCHE TECHNOLIGIES INC. -00-80-BA (hex) SPECIALIX (ASIA) PTE, LTD -00-80-BB (hex) HUGHES LAN SYSTEMS -00-80-BC (hex) HITACHI ENGINEERING CO., LTD -00-80-BD (hex) THE FURUKAWA ELECTRIC CO., LTD -00-80-BE (hex) ARIES RESEARCH -00-80-BF (hex) TAKAOKA ELECTRIC MFG. CO. LTD. -00-80-C0 (hex) PENRIL DATACOMM -00-80-C1 (hex) LANEX CORPORATION -00-80-C2 (hex) IEEE 802.1 COMMITTEE -00-80-C3 (hex) BICC INFORMATION SYSTEMS & SVC -00-80-C4 (hex) DOCUMENT TECHNOLOGIES, INC. -00-80-C5 (hex) NOVELLCO DE MEXICO -00-80-C6 (hex) NATIONAL DATACOMM CORPORATION -00-80-C7 (hex) XIRCOM -00-80-C8 (hex) D-LINK SYSTEMS, INC. -00-80-C9 (hex) ALBERTA MICROELECTRONIC CENTRE -00-80-CA (hex) NETCOM RESEARCH INCORPORATED -00-80-CB (hex) FALCO DATA PRODUCTS -00-80-CC (hex) MICROWAVE BYPASS SYSTEMS -00-80-CD (hex) MICRONICS COMPUTER, INC. -00-80-CE (hex) BROADCAST TELEVISION SYSTEMS -00-80-CF (hex) EMBEDDED PERFORMANCE INC. -00-80-D0 (hex) COMPUTER PERIPHERALS, INC. -00-80-D1 (hex) KIMTRON CORPORATION -00-80-D2 (hex) SHINNIHONDENKO CO., LTD. -00-80-D3 (hex) SHIVA CORP. -00-80-D4 (hex) CHASE RESEARCH LTD. -00-80-D5 (hex) CADRE TECHNOLOGIES -00-80-D6 (hex) NUVOTECH, INC. -00-80-D7 (hex) Fantum Engineering -00-80-D8 (hex) NETWORK PERIPHERALS INC. -00-80-D9 (hex) EMK Elektronik GmbH & Co. KG -00-80-DA (hex) BRUEL & KJAER -00-80-DB (hex) GRAPHON CORPORATION -00-80-DC (hex) PICKER INTERNATIONAL -00-80-DD (hex) GMX INC/GIMIX -00-80-DE (hex) GIPSI S.A. -00-80-DF (hex) ADC CODENOLL TECHNOLOGY CORP. -00-80-E0 (hex) XTP SYSTEMS, INC. -00-80-E1 (hex) STMICROELECTRONICS -00-80-E2 (hex) T.D.I. CO., LTD. -00-80-E3 (hex) CORAL NETWORK CORPORATION -00-80-E4 (hex) NORTHWEST DIGITAL SYSTEMS, INC -00-80-E5 (hex) LSI Logic Corporation -00-80-E6 (hex) PEER NETWORKS, INC. -00-80-E7 (hex) LYNWOOD SCIENTIFIC DEV. LTD. -00-80-E8 (hex) CUMULUS CORPORATIION -00-80-E9 (hex) Madge Ltd. -00-80-EA (hex) ADVA Optical Networking Ltd. -00-80-EB (hex) COMPCONTROL B.V. -00-80-EC (hex) SUPERCOMPUTING SOLUTIONS, INC. -00-80-ED (hex) IQ TECHNOLOGIES, INC. -00-80-EE (hex) THOMSON CSF -00-80-EF (hex) RATIONAL -00-80-F0 (hex) Panasonic Communications Co., Ltd. -00-80-F1 (hex) OPUS SYSTEMS -00-80-F2 (hex) RAYCOM SYSTEMS INC -00-80-F3 (hex) SUN ELECTRONICS CORP. -00-80-F4 (hex) TELEMECANIQUE ELECTRIQUE -00-80-F5 (hex) Quantel Ltd -00-80-F6 (hex) SYNERGY MICROSYSTEMS -00-80-F7 (hex) ZENITH ELECTRONICS -00-80-F8 (hex) MIZAR, INC. -00-80-F9 (hex) HEURIKON CORPORATION -00-80-FA (hex) RWT GMBH -00-80-FB (hex) BVM LIMITED -00-80-FC (hex) AVATAR CORPORATION -00-80-FD (hex) EXSCEED CORPRATION -00-80-FE (hex) AZURE TECHNOLOGIES, INC. -00-80-FF (hex) SOC. DE TELEINFORMATIQUE RTC -00-8C-FA (hex) Inventec Corporation -00-90-00 (hex) DIAMOND MULTIMEDIA -00-90-01 (hex) NISHIMU ELECTRONICS INDUSTRIES CO., LTD. -00-90-02 (hex) ALLGON AB -00-90-03 (hex) APLIO -00-90-04 (hex) 3COM EUROPE LTD. -00-90-05 (hex) PROTECH SYSTEMS CO., LTD. -00-90-06 (hex) HAMAMATSU PHOTONICS K.K. -00-90-07 (hex) DOMEX TECHNOLOGY CORP. -00-90-08 (hex) HanA Systems Inc. -00-90-09 (hex) i Controls, Inc. -00-90-0A (hex) PROTON ELECTRONIC INDUSTRIAL CO., LTD. -00-90-0B (hex) LANNER ELECTRONICS, INC. -00-90-0C (hex) CISCO SYSTEMS, INC. -00-90-0D (hex) Overland Storage Inc. -00-90-0E (hex) HANDLINK TECHNOLOGIES, INC. -00-90-0F (hex) KAWASAKI HEAVY INDUSTRIES, LTD -00-90-10 (hex) SIMULATION LABORATORIES, INC. -00-90-11 (hex) WAVTrace, Inc. -00-90-12 (hex) GLOBESPAN SEMICONDUCTOR, INC. -00-90-13 (hex) SAMSAN CORP. -00-90-14 (hex) ROTORK INSTRUMENTS, LTD. -00-90-15 (hex) CENTIGRAM COMMUNICATIONS CORP. -00-90-16 (hex) ZAC -00-90-17 (hex) Zypcom, Inc -00-90-18 (hex) ITO ELECTRIC INDUSTRY CO, LTD. -00-90-19 (hex) HERMES ELECTRONICS CO., LTD. -00-90-1A (hex) UNISPHERE SOLUTIONS -00-90-1B (hex) DIGITAL CONTROLS -00-90-1C (hex) mps Software Gmbh -00-90-1D (hex) PEC (NZ) LTD. -00-90-1E (hex) Selesta Ingegneria S.p.A. -00-90-1F (hex) ADTEC PRODUCTIONS, INC. -00-90-20 (hex) PHILIPS ANALYTICAL X-RAY B.V. -00-90-21 (hex) CISCO SYSTEMS, INC. -00-90-22 (hex) IVEX -00-90-23 (hex) ZILOG INC. -00-90-24 (hex) PIPELINKS, INC. -00-90-25 (hex) BAE Systems Australia (Electronic Systems) Pty Ltd -00-90-26 (hex) ADVANCED SWITCHING COMMUNICATIONS, INC. -00-90-27 (hex) INTEL CORPORATION -00-90-28 (hex) NIPPON SIGNAL CO., LTD. -00-90-29 (hex) CRYPTO AG -00-90-2A (hex) COMMUNICATION DEVICES, INC. -00-90-2B (hex) CISCO SYSTEMS, INC. -00-90-2C (hex) DATA & CONTROL EQUIPMENT LTD. -00-90-2D (hex) DATA ELECTRONICS (AUST.) PTY, LTD. -00-90-2E (hex) NAMCO LIMITED -00-90-2F (hex) NETCORE SYSTEMS, INC. -00-90-30 (hex) HONEYWELL-DATING -00-90-31 (hex) MYSTICOM, LTD. -00-90-32 (hex) PELCOMBE GROUP LTD. -00-90-33 (hex) INNOVAPHONE AG -00-90-34 (hex) IMAGIC, INC. -00-90-35 (hex) ALPHA TELECOM, INC. -00-90-36 (hex) ens, inc. -00-90-37 (hex) ACUCOMM, INC. -00-90-38 (hex) FOUNTAIN TECHNOLOGIES, INC. -00-90-39 (hex) SHASTA NETWORKS -00-90-3A (hex) NIHON MEDIA TOOL INC. -00-90-3B (hex) TriEMS Research Lab, Inc. -00-90-3C (hex) ATLANTIC NETWORK SYSTEMS -00-90-3D (hex) BIOPAC SYSTEMS, INC. -00-90-3E (hex) N.V. PHILIPS INDUSTRIAL ACTIVITIES -00-90-3F (hex) AZTEC RADIOMEDIA -00-90-40 (hex) Siemens Network Convergence LLC -00-90-41 (hex) APPLIED DIGITAL ACCESS -00-90-42 (hex) ECCS, Inc. -00-90-43 (hex) NICHIBEI DENSHI CO., LTD. -00-90-44 (hex) ASSURED DIGITAL, INC. -00-90-45 (hex) Marconi Communications -00-90-46 (hex) DEXDYNE, LTD. -00-90-47 (hex) GIGA FAST E. LTD. -00-90-48 (hex) ZEAL CORPORATION -00-90-49 (hex) ENTRIDIA CORPORATION -00-90-4A (hex) CONCUR SYSTEM TECHNOLOGIES -00-90-4B (hex) GemTek Technology Co., Ltd. -00-90-4C (hex) EPIGRAM, INC. -00-90-4D (hex) SPEC S.A. -00-90-4E (hex) DELEM BV -00-90-4F (hex) ABB POWER T&D COMPANY, INC. -00-90-50 (hex) TELESTE OY -00-90-51 (hex) ULTIMATE TECHNOLOGY CORP. -00-90-52 (hex) SELCOM ELETTRONICA S.R.L. -00-90-53 (hex) DAEWOO ELECTRONICS CO., LTD. -00-90-54 (hex) INNOVATIVE SEMICONDUCTORS, INC -00-90-55 (hex) PARKER HANNIFIN CORPORATION COMPUMOTOR DIVISION -00-90-56 (hex) TELESTREAM, INC. -00-90-57 (hex) AANetcom, Inc. -00-90-58 (hex) Ultra Electronics Ltd., Command and Control Systems -00-90-59 (hex) TELECOM DEVICE K.K. -00-90-5A (hex) DEARBORN GROUP, INC. -00-90-5B (hex) RAYMOND AND LAE ENGINEERING -00-90-5C (hex) EDMI -00-90-5D (hex) NETCOM SICHERHEITSTECHNIK GmbH -00-90-5E (hex) RAULAND-BORG CORPORATION -00-90-5F (hex) CISCO SYSTEMS, INC. -00-90-60 (hex) SYSTEM CREATE CORP. -00-90-61 (hex) PACIFIC RESEARCH & ENGINEERING CORPORATION -00-90-62 (hex) ICP VORTEX COMPUTERSYSTEME GmbH -00-90-63 (hex) COHERENT COMMUNICATIONS SYSTEMS CORPORATION -00-90-64 (hex) Thomson Inc. -00-90-65 (hex) FINISAR CORPORATION -00-90-66 (hex) Troika Networks, Inc. -00-90-67 (hex) WalkAbout Computers, Inc. -00-90-68 (hex) DVT CORP. -00-90-69 (hex) JUNIPER NETWORKS, INC. -00-90-6A (hex) TURNSTONE SYSTEMS, INC. -00-90-6B (hex) APPLIED RESOURCES, INC. -00-90-6C (hex) Sartorius Hamburg GmbH -00-90-6D (hex) CISCO SYSTEMS, INC. -00-90-6E (hex) PRAXON, INC. -00-90-6F (hex) CISCO SYSTEMS, INC. -00-90-70 (hex) NEO NETWORKS, INC. -00-90-71 (hex) Applied Innovation Inc. -00-90-72 (hex) SIMRAD AS -00-90-73 (hex) GAIO TECHNOLOGY -00-90-74 (hex) ARGON NETWORKS, INC. -00-90-75 (hex) NEC DO BRASIL S.A. -00-90-76 (hex) FMT AIRCRAFT GATE SUPPORT SYSTEMS AB -00-90-77 (hex) ADVANCED FIBRE COMMUNICATIONS -00-90-78 (hex) MER TELEMANAGEMENT SOLUTIONS, LTD. -00-90-79 (hex) ClearOne, Inc. -00-90-7A (hex) Polycom, Inc. -00-90-7B (hex) E-TECH, INC. -00-90-7C (hex) DIGITALCAST, INC. -00-90-7D (hex) Lake Communications -00-90-7E (hex) VETRONIX CORP. -00-90-7F (hex) WatchGuard Technologies, Inc. -00-90-80 (hex) NOT LIMITED, INC. -00-90-81 (hex) ALOHA NETWORKS, INC. -00-90-82 (hex) FORCE INSTITUTE -00-90-83 (hex) TURBO COMMUNICATION, INC. -00-90-84 (hex) ATECH SYSTEM -00-90-85 (hex) GOLDEN ENTERPRISES, INC. -00-90-86 (hex) CISCO SYSTEMS, INC. -00-90-87 (hex) ITIS -00-90-88 (hex) BAXALL SECURITY LTD. -00-90-89 (hex) SOFTCOM MICROSYSTEMS, INC. -00-90-8A (hex) BAYLY COMMUNICATIONS, INC. -00-90-8B (hex) PFU Systems, Inc. -00-90-8C (hex) ETREND ELECTRONICS, INC. -00-90-8D (hex) VICKERS ELECTRONICS SYSTEMS -00-90-8E (hex) Nortel Networks Broadband Access -00-90-8F (hex) AUDIO CODES LTD. -00-90-90 (hex) I-BUS -00-90-91 (hex) DigitalScape, Inc. -00-90-92 (hex) CISCO SYSTEMS, INC. -00-90-93 (hex) NANAO CORPORATION -00-90-94 (hex) OSPREY TECHNOLOGIES, INC. -00-90-95 (hex) UNIVERSAL AVIONICS -00-90-96 (hex) ASKEY COMPUTER CORP. -00-90-97 (hex) Sycamore Networks -00-90-98 (hex) SBC DESIGNS, INC. -00-90-99 (hex) ALLIED TELESIS, K.K. -00-90-9A (hex) ONE WORLD SYSTEMS, INC. -00-90-9B (hex) IMAJE -00-90-9C (hex) Motorola, Inc. -00-90-9D (hex) NovaTech Process Solutions, LLC -00-90-9E (hex) Critical IO, LLC -00-90-9F (hex) DIGI-DATA CORPORATION -00-90-A0 (hex) 8X8 INC. -00-90-A1 (hex) Flying Pig Systems/High End Systems Inc. -00-90-A2 (hex) CYBERTAN TECHNOLOGY, INC. -00-90-A3 (hex) Corecess Inc. -00-90-A4 (hex) ALTIGA NETWORKS -00-90-A5 (hex) SPECTRA LOGIC -00-90-A6 (hex) CISCO SYSTEMS, INC. -00-90-A7 (hex) CLIENTEC CORPORATION -00-90-A8 (hex) NineTiles Networks, Ltd. -00-90-A9 (hex) WESTERN DIGITAL -00-90-AA (hex) INDIGO ACTIVE VISION SYSTEMS LIMITED -00-90-AB (hex) CISCO SYSTEMS, INC. -00-90-AC (hex) OPTIVISION, INC. -00-90-AD (hex) ASPECT ELECTRONICS, INC. -00-90-AE (hex) ITALTEL S.p.A. -00-90-AF (hex) J. MORITA MFG. CORP. -00-90-B0 (hex) VADEM -00-90-B1 (hex) CISCO SYSTEMS, INC. -00-90-B2 (hex) AVICI SYSTEMS INC. -00-90-B3 (hex) AGRANAT SYSTEMS -00-90-B4 (hex) WILLOWBROOK TECHNOLOGIES -00-90-B5 (hex) NIKON CORPORATION -00-90-B6 (hex) FIBEX SYSTEMS -00-90-B7 (hex) DIGITAL LIGHTWAVE, INC. -00-90-B8 (hex) ROHDE & SCHWARZ GMBH & CO. KG -00-90-B9 (hex) BERAN INSTRUMENTS LTD. -00-90-BA (hex) VALID NETWORKS, INC. -00-90-BB (hex) TAINET COMMUNICATION SYSTEM Corp. -00-90-BC (hex) TELEMANN CO., LTD. -00-90-BD (hex) OMNIA COMMUNICATIONS, INC. -00-90-BE (hex) IBC/INTEGRATED BUSINESS COMPUTERS -00-90-BF (hex) CISCO SYSTEMS, INC. -00-90-C0 (hex) K.J. LAW ENGINEERS, INC. -00-90-C1 (hex) Peco II, Inc. -00-90-C2 (hex) JK microsystems, Inc. -00-90-C3 (hex) TOPIC SEMICONDUCTOR CORP. -00-90-C4 (hex) JAVELIN SYSTEMS, INC. -00-90-C5 (hex) INTERNET MAGIC, INC. -00-90-C6 (hex) OPTIM SYSTEMS, INC. -00-90-C7 (hex) ICOM INC. -00-90-C8 (hex) WAVERIDER COMMUNICATIONS (CANADA) INC. -00-90-C9 (hex) DPAC Technologies -00-90-CA (hex) ACCORD VIDEO TELECOMMUNICATIONS, LTD. -00-90-CB (hex) Wireless OnLine, Inc. -00-90-CC (hex) Planex Communications -00-90-CD (hex) ENT-EMPRESA NACIONAL DE TELECOMMUNICACOES, S.A. -00-90-CE (hex) TETRA GmbH -00-90-CF (hex) NORTEL -00-90-D0 (hex) Thomson Telecom Belgium -00-90-D1 (hex) LEICHU ENTERPRISE CO., LTD. -00-90-D2 (hex) ARTEL VIDEO SYSTEMS -00-90-D3 (hex) GIESECKE & DEVRIENT GmbH -00-90-D4 (hex) BindView Development Corp. -00-90-D5 (hex) EUPHONIX, INC. -00-90-D6 (hex) CRYSTAL GROUP -00-90-D7 (hex) NetBoost Corp. -00-90-D8 (hex) WHITECROSS SYSTEMS -00-90-D9 (hex) CISCO SYSTEMS, INC. -00-90-DA (hex) DYNARC, INC. -00-90-DB (hex) NEXT LEVEL COMMUNICATIONS -00-90-DC (hex) TECO INFORMATION SYSTEMS -00-90-DD (hex) THE MIHARU COMMUNICATIONS CO., LTD. -00-90-DE (hex) CARDKEY SYSTEMS, INC. -00-90-DF (hex) MITSUBISHI CHEMICAL AMERICA, INC. -00-90-E0 (hex) SYSTRAN CORP. -00-90-E1 (hex) TELENA S.P.A. -00-90-E2 (hex) DISTRIBUTED PROCESSING TECHNOLOGY -00-90-E3 (hex) AVEX ELECTRONICS INC. -00-90-E4 (hex) NEC AMERICA, INC. -00-90-E5 (hex) TEKNEMA, INC. -00-90-E6 (hex) ALi Corporation -00-90-E7 (hex) HORSCH ELEKTRONIK AG -00-90-E8 (hex) MOXA TECHNOLOGIES CORP., LTD. -00-90-E9 (hex) JANZ COMPUTER AG -00-90-EA (hex) ALPHA TECHNOLOGIES, INC. -00-90-EB (hex) SENTRY TELECOM SYSTEMS -00-90-EC (hex) PYRESCOM -00-90-ED (hex) CENTRAL SYSTEM RESEARCH CO., LTD. -00-90-EE (hex) PERSONAL COMMUNICATIONS TECHNOLOGIES -00-90-EF (hex) INTEGRIX, INC. -00-90-F0 (hex) Harmonic Video Systems Ltd. -00-90-F1 (hex) DOT HILL SYSTEMS CORPORATION -00-90-F2 (hex) CISCO SYSTEMS, INC. -00-90-F3 (hex) ASPECT COMMUNICATIONS -00-90-F4 (hex) LIGHTNING INSTRUMENTATION -00-90-F5 (hex) CLEVO CO. -00-90-F6 (hex) ESCALATE NETWORKS, INC. -00-90-F7 (hex) NBASE COMMUNICATIONS LTD. -00-90-F8 (hex) MEDIATRIX TELECOM -00-90-F9 (hex) LEITCH -00-90-FA (hex) EMULEX Corp -00-90-FB (hex) PORTWELL, INC. -00-90-FC (hex) NETWORK COMPUTING DEVICES -00-90-FD (hex) CopperCom, Inc. -00-90-FE (hex) ELECOM CO., LTD. (LANEED DIV.) -00-90-FF (hex) TELLUS TECHNOLOGY INC. -00-91-D6 (hex) Crystal Group, Inc. -00-93-63 (hex) Uni-Link Technology Co., Ltd. -00-9D-8E (hex) CARDIAC RECORDERS, INC. -00-A0-00 (hex) CENTILLION NETWORKS, INC. -00-A0-01 (hex) DRS Signal Solutions -00-A0-02 (hex) LEEDS & NORTHRUP AUSTRALIA PTY LTD -00-A0-03 (hex) Siemens Switzerland Ltd., I B T HVP -00-A0-04 (hex) NETPOWER, INC. -00-A0-05 (hex) DANIEL INSTRUMENTS, LTD. -00-A0-06 (hex) IMAGE DATA PROCESSING SYSTEM GROUP -00-A0-07 (hex) APEXX TECHNOLOGY, INC. -00-A0-08 (hex) NETCORP -00-A0-09 (hex) WHITETREE NETWORK -00-A0-0A (hex) Airspan -00-A0-0B (hex) COMPUTEX CO., LTD. -00-A0-0C (hex) KINGMAX TECHNOLOGY, INC. -00-A0-0D (hex) THE PANDA PROJECT -00-A0-0E (hex) VISUAL NETWORKS, INC. -00-A0-0F (hex) Broadband Technologies -00-A0-10 (hex) SYSLOGIC DATENTECHNIK AG -00-A0-11 (hex) MUTOH INDUSTRIES LTD. -00-A0-12 (hex) B.A.T.M. ADVANCED TECHNOLOGIES -00-A0-13 (hex) TELTREND LTD. -00-A0-14 (hex) CSIR -00-A0-15 (hex) WYLE -00-A0-16 (hex) MICROPOLIS CORP. -00-A0-17 (hex) J B M CORPORATION -00-A0-18 (hex) CREATIVE CONTROLLERS, INC. -00-A0-19 (hex) NEBULA CONSULTANTS, INC. -00-A0-1A (hex) BINAR ELEKTRONIK AB -00-A0-1B (hex) PREMISYS COMMUNICATIONS, INC. -00-A0-1C (hex) NASCENT NETWORKS CORPORATION -00-A0-1D (hex) SIXNET -00-A0-1E (hex) EST CORPORATION -00-A0-1F (hex) TRICORD SYSTEMS, INC. -00-A0-20 (hex) CITICORP/TTI -00-A0-21 (hex) General Dynamics -00-A0-22 (hex) CENTRE FOR DEVELOPMENT OF ADVANCED COMPUTING -00-A0-23 (hex) APPLIED CREATIVE TECHNOLOGY, INC. -00-A0-24 (hex) 3COM CORPORATION -00-A0-25 (hex) REDCOM LABS INC. -00-A0-26 (hex) TELDAT, S.A. -00-A0-27 (hex) FIREPOWER SYSTEMS, INC. -00-A0-28 (hex) CONNER PERIPHERALS -00-A0-29 (hex) COULTER CORPORATION -00-A0-2A (hex) TRANCELL SYSTEMS -00-A0-2B (hex) TRANSITIONS RESEARCH CORP. -00-A0-2C (hex) interWAVE Communications -00-A0-2D (hex) 1394 Trade Association -00-A0-2E (hex) BRAND COMMUNICATIONS, LTD. -00-A0-2F (hex) PIRELLI CAVI -00-A0-30 (hex) CAPTOR NV/SA -00-A0-31 (hex) HAZELTINE CORPORATION, MS 1-17 -00-A0-32 (hex) GES SINGAPORE PTE. LTD. -00-A0-33 (hex) imc MeBsysteme GmbH -00-A0-34 (hex) AXEL -00-A0-35 (hex) CYLINK CORPORATION -00-A0-36 (hex) APPLIED NETWORK TECHNOLOGY -00-A0-37 (hex) Mindray DS USA, Inc. -00-A0-38 (hex) EMAIL ELECTRONICS -00-A0-39 (hex) ROSS TECHNOLOGY, INC. -00-A0-3A (hex) KUBOTEK CORPORATION -00-A0-3B (hex) TOSHIN ELECTRIC CO., LTD. -00-A0-3C (hex) EG&G NUCLEAR INSTRUMENTS -00-A0-3D (hex) OPTO-22 -00-A0-3E (hex) ATM FORUM -00-A0-3F (hex) COMPUTER SOCIETY MICROPROCESSOR & MICROPROCESSOR STANDARDS C -00-A0-40 (hex) APPLE COMPUTER -00-A0-41 (hex) INFICON -00-A0-42 (hex) SPUR PRODUCTS CORP. -00-A0-43 (hex) AMERICAN TECHNOLOGY LABS, INC. -00-A0-44 (hex) NTT IT CO., LTD. -00-A0-45 (hex) PHOENIX CONTACT GMBH & CO. -00-A0-46 (hex) SCITEX CORP. LTD. -00-A0-47 (hex) INTEGRATED FITNESS CORP. -00-A0-48 (hex) QUESTECH, LTD. -00-A0-49 (hex) DIGITECH INDUSTRIES, INC. -00-A0-4A (hex) NISSHIN ELECTRIC CO., LTD. -00-A0-4B (hex) TFL LAN INC. -00-A0-4C (hex) INNOVATIVE SYSTEMS & TECHNOLOGIES, INC. -00-A0-4D (hex) EDA INSTRUMENTS, INC. -00-A0-4E (hex) VOELKER TECHNOLOGIES, INC. -00-A0-4F (hex) AMERITEC CORP. -00-A0-50 (hex) CYPRESS SEMICONDUCTOR -00-A0-51 (hex) ANGIA COMMUNICATIONS. INC. -00-A0-52 (hex) STANILITE ELECTRONICS PTY. LTD -00-A0-53 (hex) COMPACT DEVICES, INC. -00-A0-54 (hex) PRIVATE -00-A0-55 (hex) Data Device Corporation -00-A0-56 (hex) MICROPROSS -00-A0-57 (hex) LANCOM Systems GmbH -00-A0-58 (hex) GLORY, LTD. -00-A0-59 (hex) HAMILTON HALLMARK -00-A0-5A (hex) KOFAX IMAGE PRODUCTS -00-A0-5B (hex) MARQUIP, INC. -00-A0-5C (hex) INVENTORY CONVERSION, INC./ -00-A0-5D (hex) CS COMPUTER SYSTEME GmbH -00-A0-5E (hex) MYRIAD LOGIC INC. -00-A0-5F (hex) BTG Electronics Design BV -00-A0-60 (hex) ACER PERIPHERALS, INC. -00-A0-61 (hex) PURITAN BENNETT -00-A0-62 (hex) AES PRODATA -00-A0-63 (hex) JRL SYSTEMS, INC. -00-A0-64 (hex) KVB/ANALECT -00-A0-65 (hex) Symantec Corporation -00-A0-66 (hex) ISA CO., LTD. -00-A0-67 (hex) NETWORK SERVICES GROUP -00-A0-68 (hex) BHP LIMITED -00-A0-69 (hex) Symmetricom, Inc. -00-A0-6A (hex) Verilink Corporation -00-A0-6B (hex) DMS DORSCH MIKROSYSTEM GMBH -00-A0-6C (hex) SHINDENGEN ELECTRIC MFG. CO., LTD. -00-A0-6D (hex) MANNESMANN TALLY CORPORATION -00-A0-6E (hex) AUSTRON, INC. -00-A0-6F (hex) THE APPCON GROUP, INC. -00-A0-70 (hex) COASTCOM -00-A0-71 (hex) VIDEO LOTTERY TECHNOLOGIES,INC -00-A0-72 (hex) OVATION SYSTEMS LTD. -00-A0-73 (hex) COM21, INC. -00-A0-74 (hex) PERCEPTION TECHNOLOGY -00-A0-75 (hex) MICRON TECHNOLOGY, INC. -00-A0-76 (hex) CARDWARE LAB, INC. -00-A0-77 (hex) FUJITSU NEXION, INC. -00-A0-78 (hex) Marconi Communications -00-A0-79 (hex) ALPS ELECTRIC (USA), INC. -00-A0-7A (hex) ADVANCED PERIPHERALS TECHNOLOGIES, INC. -00-A0-7B (hex) DAWN COMPUTER INCORPORATION -00-A0-7C (hex) TONYANG NYLON CO., LTD. -00-A0-7D (hex) SEEQ TECHNOLOGY, INC. -00-A0-7E (hex) AVID TECHNOLOGY, INC. -00-A0-7F (hex) GSM-SYNTEL, LTD. -00-A0-80 (hex) SBE, Inc. -00-A0-81 (hex) ALCATEL DATA NETWORKS -00-A0-82 (hex) NKT ELEKTRONIK A/S -00-A0-83 (hex) ASIMMPHONY TURKEY -00-A0-84 (hex) Dataplex Pty Ltd -00-A0-85 (hex) PRIVATE -00-A0-86 (hex) AMBER WAVE SYSTEMS, INC. -00-A0-87 (hex) Zarlink Semiconductor Ltd. -00-A0-88 (hex) ESSENTIAL COMMUNICATIONS -00-A0-89 (hex) XPOINT TECHNOLOGIES, INC. -00-A0-8A (hex) BROOKTROUT TECHNOLOGY, INC. -00-A0-8B (hex) ASTON ELECTRONIC DESIGNS LTD. -00-A0-8C (hex) MultiMedia LANs, Inc. -00-A0-8D (hex) JACOMO CORPORATION -00-A0-8E (hex) Nokia Internet Communications -00-A0-8F (hex) DESKNET SYSTEMS, INC. -00-A0-90 (hex) TimeStep Corporation -00-A0-91 (hex) APPLICOM INTERNATIONAL -00-A0-92 (hex) H. BOLLMANN MANUFACTURERS, LTD -00-A0-93 (hex) B/E AEROSPACE, Inc. -00-A0-94 (hex) COMSAT CORPORATION -00-A0-95 (hex) ACACIA NETWORKS, INC. -00-A0-96 (hex) MITUMI ELECTRIC CO., LTD. -00-A0-97 (hex) JC INFORMATION SYSTEMS -00-A0-98 (hex) NetApp -00-A0-99 (hex) K-NET LTD. -00-A0-9A (hex) NIHON KOHDEN AMERICA -00-A0-9B (hex) QPSX COMMUNICATIONS, LTD. -00-A0-9C (hex) Xyplex, Inc. -00-A0-9D (hex) JOHNATHON FREEMAN TECHNOLOGIES -00-A0-9E (hex) ICTV -00-A0-9F (hex) COMMVISION CORP. -00-A0-A0 (hex) COMPACT DATA, LTD. -00-A0-A1 (hex) EPIC DATA INC. -00-A0-A2 (hex) DIGICOM S.P.A. -00-A0-A3 (hex) RELIABLE POWER METERS -00-A0-A4 (hex) MICROS SYSTEMS, INC. -00-A0-A5 (hex) TEKNOR MICROSYSTEME, INC. -00-A0-A6 (hex) M.I. SYSTEMS, K.K. -00-A0-A7 (hex) VORAX CORPORATION -00-A0-A8 (hex) RENEX CORPORATION -00-A0-A9 (hex) NAVTEL COMMUNICATIONS INC. -00-A0-AA (hex) SPACELABS MEDICAL -00-A0-AB (hex) NETCS INFORMATIONSTECHNIK GMBH -00-A0-AC (hex) GILAT SATELLITE NETWORKS, LTD. -00-A0-AD (hex) MARCONI SPA -00-A0-AE (hex) NUCOM SYSTEMS, INC. -00-A0-AF (hex) WMS INDUSTRIES -00-A0-B0 (hex) I-O DATA DEVICE, INC. -00-A0-B1 (hex) FIRST VIRTUAL CORPORATION -00-A0-B2 (hex) SHIMA SEIKI -00-A0-B3 (hex) ZYKRONIX -00-A0-B4 (hex) TEXAS MICROSYSTEMS, INC. -00-A0-B5 (hex) 3H TECHNOLOGY -00-A0-B6 (hex) SANRITZ AUTOMATION CO., LTD. -00-A0-B7 (hex) CORDANT, INC. -00-A0-B8 (hex) SYMBIOS LOGIC INC. -00-A0-B9 (hex) EAGLE TECHNOLOGY, INC. -00-A0-BA (hex) PATTON ELECTRONICS CO. -00-A0-BB (hex) HILAN GMBH -00-A0-BC (hex) VIASAT, INCORPORATED -00-A0-BD (hex) I-TECH CORP. -00-A0-BE (hex) INTEGRATED CIRCUIT SYSTEMS, INC. COMMUNICATIONS GROUP -00-A0-BF (hex) WIRELESS DATA GROUP MOTOROLA -00-A0-C0 (hex) DIGITAL LINK CORP. -00-A0-C1 (hex) ORTIVUS MEDICAL AB -00-A0-C2 (hex) R.A. SYSTEMS CO., LTD. -00-A0-C3 (hex) UNICOMPUTER GMBH -00-A0-C4 (hex) CRISTIE ELECTRONICS LTD. -00-A0-C5 (hex) ZYXEL COMMUNICATION -00-A0-C6 (hex) QUALCOMM INCORPORATED -00-A0-C7 (hex) TADIRAN TELECOMMUNICATIONS -00-A0-C8 (hex) ADTRAN INC. -00-A0-C9 (hex) INTEL CORPORATION - HF1-06 -00-A0-CA (hex) FUJITSU DENSO LTD. -00-A0-CB (hex) ARK TELECOMMUNICATIONS, INC. -00-A0-CC (hex) LITE-ON COMMUNICATIONS, INC. -00-A0-CD (hex) DR. JOHANNES HEIDENHAIN GmbH -00-A0-CE (hex) ASTROCOM CORPORATION -00-A0-CF (hex) SOTAS, INC. -00-A0-D0 (hex) TEN X TECHNOLOGY, INC. -00-A0-D1 (hex) INVENTEC CORPORATION -00-A0-D2 (hex) ALLIED TELESIS INTERNATIONAL CORPORATION -00-A0-D3 (hex) INSTEM COMPUTER SYSTEMS, LTD. -00-A0-D4 (hex) RADIOLAN, INC. -00-A0-D5 (hex) SIERRA WIRELESS INC. -00-A0-D6 (hex) SBE, INC. -00-A0-D7 (hex) KASTEN CHASE APPLIED RESEARCH -00-A0-D8 (hex) SPECTRA - TEK -00-A0-D9 (hex) CONVEX COMPUTER CORPORATION -00-A0-DA (hex) INTEGRATED SYSTEMS Technology, Inc. -00-A0-DB (hex) FISHER & PAYKEL PRODUCTION -00-A0-DC (hex) O.N. ELECTRONIC CO., LTD. -00-A0-DD (hex) AZONIX CORPORATION -00-A0-DE (hex) YAMAHA CORPORATION -00-A0-DF (hex) STS TECHNOLOGIES, INC. -00-A0-E0 (hex) TENNYSON TECHNOLOGIES PTY LTD -00-A0-E1 (hex) WESTPORT RESEARCH ASSOCIATES, INC. -00-A0-E2 (hex) Keisokugiken Corporation -00-A0-E3 (hex) XKL SYSTEMS CORP. -00-A0-E4 (hex) OPTIQUEST -00-A0-E5 (hex) NHC COMMUNICATIONS -00-A0-E6 (hex) DIALOGIC CORPORATION -00-A0-E7 (hex) CENTRAL DATA CORPORATION -00-A0-E8 (hex) REUTERS HOLDINGS PLC -00-A0-E9 (hex) ELECTRONIC RETAILING SYSTEMS INTERNATIONAL -00-A0-EA (hex) ETHERCOM CORP. -00-A0-EB (hex) Encore Networks, Inc. -00-A0-EC (hex) TRANSMITTON LTD. -00-A0-ED (hex) Brooks Automation, Inc. -00-A0-EE (hex) NASHOBA NETWORKS -00-A0-EF (hex) LUCIDATA LTD. -00-A0-F0 (hex) TORONTO MICROELECTRONICS INC. -00-A0-F1 (hex) MTI -00-A0-F2 (hex) INFOTEK COMMUNICATIONS, INC. -00-A0-F3 (hex) STAUBLI -00-A0-F4 (hex) GE -00-A0-F5 (hex) RADGUARD LTD. -00-A0-F6 (hex) AutoGas Systems Inc. -00-A0-F7 (hex) V.I COMPUTER CORP. -00-A0-F8 (hex) SYMBOL TECHNOLOGIES, INC. -00-A0-F9 (hex) BINTEC COMMUNICATIONS GMBH -00-A0-FA (hex) Marconi Communication GmbH -00-A0-FB (hex) TORAY ENGINEERING CO., LTD. -00-A0-FC (hex) IMAGE SCIENCES, INC. -00-A0-FD (hex) SCITEX DIGITAL PRINTING, INC. -00-A0-FE (hex) BOSTON TECHNOLOGY, INC. -00-A0-FF (hex) TELLABS OPERATIONS, INC. -00-AA-00 (hex) INTEL CORPORATION -00-AA-01 (hex) INTEL CORPORATION -00-AA-02 (hex) INTEL CORPORATION -00-AA-3C (hex) OLIVETTI TELECOM SPA (OLTECO) -00-B0-09 (hex) Grass Valley Group -00-B0-17 (hex) InfoGear Technology Corp. -00-B0-19 (hex) Casi-Rusco -00-B0-1C (hex) Westport Technologies -00-B0-1E (hex) Rantic Labs, Inc. -00-B0-2A (hex) ORSYS GmbH -00-B0-2D (hex) ViaGate Technologies, Inc. -00-B0-3B (hex) HiQ Networks -00-B0-48 (hex) Marconi Communications Inc. -00-B0-4A (hex) Cisco Systems, Inc. -00-B0-52 (hex) Intellon Corporation -00-B0-64 (hex) Cisco Systems, Inc. -00-B0-69 (hex) Honewell Oy -00-B0-6D (hex) Jones Futurex Inc. -00-B0-80 (hex) Mannesmann Ipulsys B.V. -00-B0-86 (hex) LocSoft Limited -00-B0-8E (hex) Cisco Systems, Inc. -00-B0-91 (hex) Transmeta Corp. -00-B0-94 (hex) Alaris, Inc. -00-B0-9A (hex) Morrow Technologies Corp. -00-B0-9D (hex) Point Grey Research Inc. -00-B0-AC (hex) SIAE-Microelettronica S.p.A. -00-B0-AE (hex) Symmetricom -00-B0-B3 (hex) Xstreamis PLC -00-B0-C2 (hex) Cisco Systems, Inc. -00-B0-C7 (hex) Tellabs Operations, Inc. -00-B0-CE (hex) TECHNOLOGY RESCUE -00-B0-D0 (hex) Dell Computer Corp. -00-B0-DB (hex) Nextcell, Inc. -00-B0-DF (hex) RELDATA Inc -00-B0-E7 (hex) British Federal Ltd. -00-B0-EC (hex) EACEM -00-B0-EE (hex) Ajile Systems, Inc. -00-B0-F0 (hex) CALY NETWORKS -00-B0-F5 (hex) NetWorth Technologies, Inc. -00-BA-C0 (hex) Biometric Access Company -00-BB-01 (hex) OCTOTHORPE CORP. -00-BB-F0 (hex) UNGERMANN-BASS INC. -00-BD-3A (hex) Nokia Corporation -00-C0-00 (hex) LANOPTICS, LTD. -00-C0-01 (hex) DIATEK PATIENT MANAGMENT -00-C0-02 (hex) SERCOMM CORPORATION -00-C0-03 (hex) GLOBALNET COMMUNICATIONS -00-C0-04 (hex) JAPAN BUSINESS COMPUTER CO.LTD -00-C0-05 (hex) LIVINGSTON ENTERPRISES, INC. -00-C0-06 (hex) NIPPON AVIONICS CO., LTD. -00-C0-07 (hex) PINNACLE DATA SYSTEMS, INC. -00-C0-08 (hex) SECO SRL -00-C0-09 (hex) KT TECHNOLOGY (S) PTE LTD -00-C0-0A (hex) MICRO CRAFT -00-C0-0B (hex) NORCONTROL A.S. -00-C0-0C (hex) RELIA TECHNOLGIES -00-C0-0D (hex) ADVANCED LOGIC RESEARCH, INC. -00-C0-0E (hex) PSITECH, INC. -00-C0-0F (hex) QUANTUM SOFTWARE SYSTEMS LTD. -00-C0-10 (hex) HIRAKAWA HEWTECH CORP. -00-C0-11 (hex) INTERACTIVE COMPUTING DEVICES -00-C0-12 (hex) NETSPAN CORPORATION -00-C0-13 (hex) NETRIX -00-C0-14 (hex) TELEMATICS CALABASAS INT'L,INC -00-C0-15 (hex) NEW MEDIA CORPORATION -00-C0-16 (hex) ELECTRONIC THEATRE CONTROLS -00-C0-17 (hex) Fluke Corporation -00-C0-18 (hex) LANART CORPORATION -00-C0-19 (hex) LEAP TECHNOLOGY, INC. -00-C0-1A (hex) COROMETRICS MEDICAL SYSTEMS -00-C0-1B (hex) SOCKET COMMUNICATIONS, INC. -00-C0-1C (hex) INTERLINK COMMUNICATIONS LTD. -00-C0-1D (hex) GRAND JUNCTION NETWORKS, INC. -00-C0-1E (hex) LA FRANCAISE DES JEUX -00-C0-1F (hex) S.E.R.C.E.L. -00-C0-20 (hex) ARCO ELECTRONIC, CONTROL LTD. -00-C0-21 (hex) NETEXPRESS -00-C0-22 (hex) LASERMASTER TECHNOLOGIES, INC. -00-C0-23 (hex) TUTANKHAMON ELECTRONICS -00-C0-24 (hex) EDEN SISTEMAS DE COMPUTACAO SA -00-C0-25 (hex) DATAPRODUCTS CORPORATION -00-C0-26 (hex) LANS TECHNOLOGY CO., LTD. -00-C0-27 (hex) CIPHER SYSTEMS, INC. -00-C0-28 (hex) JASCO CORPORATION -00-C0-29 (hex) Nexans Deutschland GmbH - ANS -00-C0-2A (hex) OHKURA ELECTRIC CO., LTD. -00-C0-2B (hex) GERLOFF GESELLSCHAFT FUR -00-C0-2C (hex) CENTRUM COMMUNICATIONS, INC. -00-C0-2D (hex) FUJI PHOTO FILM CO., LTD. -00-C0-2E (hex) NETWIZ -00-C0-2F (hex) OKUMA CORPORATION -00-C0-30 (hex) INTEGRATED ENGINEERING B. V. -00-C0-31 (hex) DESIGN RESEARCH SYSTEMS, INC. -00-C0-32 (hex) I-CUBED LIMITED -00-C0-33 (hex) TELEBIT COMMUNICATIONS APS -00-C0-34 (hex) TRANSACTION NETWORK -00-C0-35 (hex) QUINTAR COMPANY -00-C0-36 (hex) RAYTECH ELECTRONIC CORP. -00-C0-37 (hex) DYNATEM -00-C0-38 (hex) RASTER IMAGE PROCESSING SYSTEM -00-C0-39 (hex) Teridian Semiconductor Corporation -00-C0-3A (hex) MEN-MIKRO ELEKTRONIK GMBH -00-C0-3B (hex) MULTIACCESS COMPUTING CORP. -00-C0-3C (hex) TOWER TECH S.R.L. -00-C0-3D (hex) WIESEMANN & THEIS GMBH -00-C0-3E (hex) FA. GEBR. HELLER GMBH -00-C0-3F (hex) STORES AUTOMATED SYSTEMS, INC. -00-C0-40 (hex) ECCI -00-C0-41 (hex) DIGITAL TRANSMISSION SYSTEMS -00-C0-42 (hex) DATALUX CORP. -00-C0-43 (hex) STRATACOM -00-C0-44 (hex) EMCOM CORPORATION -00-C0-45 (hex) ISOLATION SYSTEMS, LTD. -00-C0-46 (hex) Blue Chip Technology Ltd -00-C0-47 (hex) UNIMICRO SYSTEMS, INC. -00-C0-48 (hex) BAY TECHNICAL ASSOCIATES -00-C0-49 (hex) U.S. ROBOTICS, INC. -00-C0-4A (hex) GROUP 2000 AG -00-C0-4B (hex) CREATIVE MICROSYSTEMS -00-C0-4C (hex) DEPARTMENT OF FOREIGN AFFAIRS -00-C0-4D (hex) MITEC, INC. -00-C0-4E (hex) COMTROL CORPORATION -00-C0-4F (hex) DELL COMPUTER CORPORATION -00-C0-50 (hex) TOYO DENKI SEIZO K.K. -00-C0-51 (hex) ADVANCED INTEGRATION RESEARCH -00-C0-52 (hex) BURR-BROWN -00-C0-53 (hex) Aspect Software Inc. -00-C0-54 (hex) NETWORK PERIPHERALS, LTD. -00-C0-55 (hex) MODULAR COMPUTING TECHNOLOGIES -00-C0-56 (hex) SOMELEC -00-C0-57 (hex) MYCO ELECTRONICS -00-C0-58 (hex) DATAEXPERT CORP. -00-C0-59 (hex) DENSO CORPORATION -00-C0-5A (hex) SEMAPHORE COMMUNICATIONS CORP. -00-C0-5B (hex) NETWORKS NORTHWEST, INC. -00-C0-5C (hex) ELONEX PLC -00-C0-5D (hex) L&N TECHNOLOGIES -00-C0-5E (hex) VARI-LITE, INC. -00-C0-5F (hex) FINE-PAL COMPANY LIMITED -00-C0-60 (hex) ID SCANDINAVIA AS -00-C0-61 (hex) SOLECTEK CORPORATION -00-C0-62 (hex) IMPULSE TECHNOLOGY -00-C0-63 (hex) MORNING STAR TECHNOLOGIES, INC -00-C0-64 (hex) GENERAL DATACOMM IND. INC. -00-C0-65 (hex) SCOPE COMMUNICATIONS, INC. -00-C0-66 (hex) DOCUPOINT, INC. -00-C0-67 (hex) UNITED BARCODE INDUSTRIES -00-C0-68 (hex) PHILIP DRAKE ELECTRONICS LTD. -00-C0-69 (hex) Axxcelera Broadband Wireless -00-C0-6A (hex) ZAHNER-ELEKTRIK GMBH & CO. KG -00-C0-6B (hex) OSI PLUS CORPORATION -00-C0-6C (hex) SVEC COMPUTER CORP. -00-C0-6D (hex) BOCA RESEARCH, INC. -00-C0-6E (hex) HAFT TECHNOLOGY, INC. -00-C0-6F (hex) KOMATSU LTD. -00-C0-70 (hex) SECTRA SECURE-TRANSMISSION AB -00-C0-71 (hex) AREANEX COMMUNICATIONS, INC. -00-C0-72 (hex) KNX LTD. -00-C0-73 (hex) XEDIA CORPORATION -00-C0-74 (hex) TOYODA AUTOMATIC LOOM -00-C0-75 (hex) XANTE CORPORATION -00-C0-76 (hex) I-DATA INTERNATIONAL A-S -00-C0-77 (hex) DAEWOO TELECOM LTD. -00-C0-78 (hex) COMPUTER SYSTEMS ENGINEERING -00-C0-79 (hex) FONSYS CO.,LTD. -00-C0-7A (hex) PRIVA B.V. -00-C0-7B (hex) ASCEND COMMUNICATIONS, INC. -00-C0-7C (hex) HIGHTECH INFORMATION -00-C0-7D (hex) RISC DEVELOPMENTS LTD. -00-C0-7E (hex) KUBOTA CORPORATION ELECTRONIC -00-C0-7F (hex) NUPON COMPUTING CORP. -00-C0-80 (hex) NETSTAR, INC. -00-C0-81 (hex) METRODATA LTD. -00-C0-82 (hex) MOORE PRODUCTS CO. -00-C0-83 (hex) TRACE MOUNTAIN PRODUCTS, INC. -00-C0-84 (hex) DATA LINK CORP. LTD. -00-C0-85 (hex) ELECTRONICS FOR IMAGING, INC. -00-C0-86 (hex) THE LYNK CORPORATION -00-C0-87 (hex) UUNET TECHNOLOGIES, INC. -00-C0-88 (hex) EKF ELEKTRONIK GMBH -00-C0-89 (hex) TELINDUS DISTRIBUTION -00-C0-8A (hex) Lauterbach GmbH -00-C0-8B (hex) RISQ MODULAR SYSTEMS, INC. -00-C0-8C (hex) PERFORMANCE TECHNOLOGIES, INC. -00-C0-8D (hex) TRONIX PRODUCT DEVELOPMENT -00-C0-8E (hex) NETWORK INFORMATION TECHNOLOGY -00-C0-8F (hex) Panasonic Electric Works Co., Ltd. -00-C0-90 (hex) PRAIM S.R.L. -00-C0-91 (hex) JABIL CIRCUIT, INC. -00-C0-92 (hex) MENNEN MEDICAL INC. -00-C0-93 (hex) ALTA RESEARCH CORP. -00-C0-94 (hex) VMX INC. -00-C0-95 (hex) ZNYX -00-C0-96 (hex) TAMURA CORPORATION -00-C0-97 (hex) ARCHIPEL SA -00-C0-98 (hex) CHUNTEX ELECTRONIC CO., LTD. -00-C0-99 (hex) YOSHIKI INDUSTRIAL CO.,LTD. -00-C0-9A (hex) PHOTONICS CORPORATION -00-C0-9B (hex) RELIANCE COMM/TEC, R-TEC -00-C0-9C (hex) HIOKI E.E. CORPORATION -00-C0-9D (hex) DISTRIBUTED SYSTEMS INT'L, INC -00-C0-9E (hex) CACHE COMPUTERS, INC. -00-C0-9F (hex) QUANTA COMPUTER, INC. -00-C0-A0 (hex) ADVANCE MICRO RESEARCH, INC. -00-C0-A1 (hex) TOKYO DENSHI SEKEI CO. -00-C0-A2 (hex) INTERMEDIUM A/S -00-C0-A3 (hex) DUAL ENTERPRISES CORPORATION -00-C0-A4 (hex) UNIGRAF OY -00-C0-A5 (hex) DICKENS DATA SYSTEMS -00-C0-A6 (hex) EXICOM AUSTRALIA PTY. LTD -00-C0-A7 (hex) SEEL LTD. -00-C0-A8 (hex) GVC CORPORATION -00-C0-A9 (hex) BARRON MCCANN LTD. -00-C0-AA (hex) SILICON VALLEY COMPUTER -00-C0-AB (hex) Telco Systems, Inc. -00-C0-AC (hex) GAMBIT COMPUTER COMMUNICATIONS -00-C0-AD (hex) MARBEN COMMUNICATION SYSTEMS -00-C0-AE (hex) TOWERCOM CO. INC. DBA PC HOUSE -00-C0-AF (hex) TEKLOGIX INC. -00-C0-B0 (hex) GCC TECHNOLOGIES,INC. -00-C0-B1 (hex) GENIUS NET CO. -00-C0-B2 (hex) NORAND CORPORATION -00-C0-B3 (hex) COMSTAT DATACOMM CORPORATION -00-C0-B4 (hex) MYSON TECHNOLOGY, INC. -00-C0-B5 (hex) CORPORATE NETWORK SYSTEMS,INC. -00-C0-B6 (hex) Overland Storage, Inc. -00-C0-B7 (hex) AMERICAN POWER CONVERSION CORP -00-C0-B8 (hex) FRASER'S HILL LTD. -00-C0-B9 (hex) FUNK SOFTWARE, INC. -00-C0-BA (hex) NETVANTAGE -00-C0-BB (hex) FORVAL CREATIVE, INC. -00-C0-BC (hex) TELECOM AUSTRALIA/CSSC -00-C0-BD (hex) INEX TECHNOLOGIES, INC. -00-C0-BE (hex) ALCATEL - SEL -00-C0-BF (hex) TECHNOLOGY CONCEPTS, LTD. -00-C0-C0 (hex) SHORE MICROSYSTEMS, INC. -00-C0-C1 (hex) QUAD/GRAPHICS, INC. -00-C0-C2 (hex) INFINITE NETWORKS LTD. -00-C0-C3 (hex) ACUSON COMPUTED SONOGRAPHY -00-C0-C4 (hex) COMPUTER OPERATIONAL -00-C0-C5 (hex) SID INFORMATICA -00-C0-C6 (hex) PERSONAL MEDIA CORP. -00-C0-C7 (hex) SPARKTRUM MICROSYSTEMS, INC. -00-C0-C8 (hex) MICRO BYTE PTY. LTD. -00-C0-C9 (hex) ELSAG BAILEY PROCESS -00-C0-CA (hex) ALFA, INC. -00-C0-CB (hex) CONTROL TECHNOLOGY CORPORATION -00-C0-CC (hex) TELESCIENCES CO SYSTEMS, INC. -00-C0-CD (hex) COMELTA, S.A. -00-C0-CE (hex) CEI SYSTEMS & ENGINEERING PTE -00-C0-CF (hex) IMATRAN VOIMA OY -00-C0-D0 (hex) RATOC SYSTEM INC. -00-C0-D1 (hex) COMTREE TECHNOLOGY CORPORATION -00-C0-D2 (hex) SYNTELLECT, INC. -00-C0-D3 (hex) OLYMPUS IMAGE SYSTEMS, INC. -00-C0-D4 (hex) AXON NETWORKS, INC. -00-C0-D5 (hex) Werbeagentur Jrgen Siebert -00-C0-D6 (hex) J1 SYSTEMS, INC. -00-C0-D7 (hex) TAIWAN TRADING CENTER DBA -00-C0-D8 (hex) UNIVERSAL DATA SYSTEMS -00-C0-D9 (hex) QUINTE NETWORK CONFIDENTIALITY -00-C0-DA (hex) NICE SYSTEMS LTD. -00-C0-DB (hex) IPC CORPORATION (PTE) LTD. -00-C0-DC (hex) EOS TECHNOLOGIES, INC. -00-C0-DD (hex) QLogic Corporation -00-C0-DE (hex) ZCOMM, INC. -00-C0-DF (hex) KYE Systems Corp. -00-C0-E0 (hex) DSC COMMUNICATION CORP. -00-C0-E1 (hex) SONIC SOLUTIONS -00-C0-E2 (hex) CALCOMP, INC. -00-C0-E3 (hex) OSITECH COMMUNICATIONS, INC. -00-C0-E4 (hex) SIEMENS BUILDING -00-C0-E5 (hex) GESPAC, S.A. -00-C0-E6 (hex) Verilink Corporation -00-C0-E7 (hex) FIBERDATA AB -00-C0-E8 (hex) PLEXCOM, INC. -00-C0-E9 (hex) OAK SOLUTIONS, LTD. -00-C0-EA (hex) ARRAY TECHNOLOGY LTD. -00-C0-EB (hex) SEH COMPUTERTECHNIK GMBH -00-C0-EC (hex) DAUPHIN TECHNOLOGY -00-C0-ED (hex) US ARMY ELECTRONIC -00-C0-EE (hex) KYOCERA CORPORATION -00-C0-EF (hex) ABIT CORPORATION -00-C0-F0 (hex) KINGSTON TECHNOLOGY CORP. -00-C0-F1 (hex) SHINKO ELECTRIC CO., LTD. -00-C0-F2 (hex) TRANSITION NETWORKS -00-C0-F3 (hex) NETWORK COMMUNICATIONS CORP. -00-C0-F4 (hex) INTERLINK SYSTEM CO., LTD. -00-C0-F5 (hex) METACOMP, INC. -00-C0-F6 (hex) CELAN TECHNOLOGY INC. -00-C0-F7 (hex) ENGAGE COMMUNICATION, INC. -00-C0-F8 (hex) ABOUT COMPUTING INC. -00-C0-F9 (hex) Motorola Embedded Computing Group -00-C0-FA (hex) CANARY COMMUNICATIONS, INC. -00-C0-FB (hex) ADVANCED TECHNOLOGY LABS -00-C0-FC (hex) ELASTIC REALITY, INC. -00-C0-FD (hex) PROSUM -00-C0-FE (hex) APTEC COMPUTER SYSTEMS, INC. -00-C0-FF (hex) DOT HILL SYSTEMS CORPORATION -00-CB-BD (hex) Cambridge Broadband Networks Ltd. -00-CF-1C (hex) COMMUNICATION MACHINERY CORP. -00-D0-00 (hex) FERRAN SCIENTIFIC, INC. -00-D0-01 (hex) VST TECHNOLOGIES, INC. -00-D0-02 (hex) DITECH CORPORATION -00-D0-03 (hex) COMDA ENTERPRISES CORP. -00-D0-04 (hex) PENTACOM LTD. -00-D0-05 (hex) ZHS ZEITMANAGEMENTSYSTEME -00-D0-06 (hex) CISCO SYSTEMS, INC. -00-D0-07 (hex) MIC ASSOCIATES, INC. -00-D0-08 (hex) MACTELL CORPORATION -00-D0-09 (hex) HSING TECH. ENTERPRISE CO. LTD -00-D0-0A (hex) LANACCESS TELECOM S.A. -00-D0-0B (hex) RHK TECHNOLOGY, INC. -00-D0-0C (hex) SNIJDER MICRO SYSTEMS -00-D0-0D (hex) MICROMERITICS INSTRUMENT -00-D0-0E (hex) PLURIS, INC. -00-D0-0F (hex) SPEECH DESIGN GMBH -00-D0-10 (hex) CONVERGENT NETWORKS, INC. -00-D0-11 (hex) PRISM VIDEO, INC. -00-D0-12 (hex) GATEWORKS CORP. -00-D0-13 (hex) PRIMEX AEROSPACE COMPANY -00-D0-14 (hex) ROOT, INC. -00-D0-15 (hex) UNIVEX MICROTECHNOLOGY CORP. -00-D0-16 (hex) SCM MICROSYSTEMS, INC. -00-D0-17 (hex) SYNTECH INFORMATION CO., LTD. -00-D0-18 (hex) QWES. COM, INC. -00-D0-19 (hex) DAINIPPON SCREEN CORPORATE -00-D0-1A (hex) URMET TLC S.P.A. -00-D0-1B (hex) MIMAKI ENGINEERING CO., LTD. -00-D0-1C (hex) SBS TECHNOLOGIES, -00-D0-1D (hex) FURUNO ELECTRIC CO., LTD. -00-D0-1E (hex) PINGTEL CORP. -00-D0-1F (hex) CTAM PTY. LTD. -00-D0-20 (hex) AIM SYSTEM, INC. -00-D0-21 (hex) REGENT ELECTRONICS CORP. -00-D0-22 (hex) INCREDIBLE TECHNOLOGIES, INC. -00-D0-23 (hex) INFORTREND TECHNOLOGY, INC. -00-D0-24 (hex) Cognex Corporation -00-D0-25 (hex) XROSSTECH, INC. -00-D0-26 (hex) HIRSCHMANN AUSTRIA GMBH -00-D0-27 (hex) APPLIED AUTOMATION, INC. -00-D0-28 (hex) OMNEON VIDEO NETWORKS -00-D0-29 (hex) WAKEFERN FOOD CORPORATION -00-D0-2A (hex) Voxent Systems Ltd. -00-D0-2B (hex) JETCELL, INC. -00-D0-2C (hex) CAMPBELL SCIENTIFIC, INC. -00-D0-2D (hex) ADEMCO -00-D0-2E (hex) COMMUNICATION AUTOMATION CORP. -00-D0-2F (hex) VLSI TECHNOLOGY INC. -00-D0-30 (hex) Safetran Systems Corp -00-D0-31 (hex) INDUSTRIAL LOGIC CORPORATION -00-D0-32 (hex) YANO ELECTRIC CO., LTD. -00-D0-33 (hex) DALIAN DAXIAN NETWORK -00-D0-34 (hex) ORMEC SYSTEMS CORP. -00-D0-35 (hex) BEHAVIOR TECH. COMPUTER CORP. -00-D0-36 (hex) TECHNOLOGY ATLANTA CORP. -00-D0-37 (hex) Pace France -00-D0-38 (hex) FIVEMERE, LTD. -00-D0-39 (hex) UTILICOM, INC. -00-D0-3A (hex) ZONEWORX, INC. -00-D0-3B (hex) VISION PRODUCTS PTY. LTD. -00-D0-3C (hex) Vieo, Inc. -00-D0-3D (hex) GALILEO TECHNOLOGY, LTD. -00-D0-3E (hex) ROCKETCHIPS, INC. -00-D0-3F (hex) AMERICAN COMMUNICATION -00-D0-40 (hex) SYSMATE CO., LTD. -00-D0-41 (hex) AMIGO TECHNOLOGY CO., LTD. -00-D0-42 (hex) MAHLO GMBH & CO. UG -00-D0-43 (hex) ZONAL RETAIL DATA SYSTEMS -00-D0-44 (hex) ALIDIAN NETWORKS, INC. -00-D0-45 (hex) KVASER AB -00-D0-46 (hex) DOLBY LABORATORIES, INC. -00-D0-47 (hex) XN TECHNOLOGIES -00-D0-48 (hex) ECTON, INC. -00-D0-49 (hex) IMPRESSTEK CO., LTD. -00-D0-4A (hex) PRESENCE TECHNOLOGY GMBH -00-D0-4B (hex) LA CIE GROUP S.A. -00-D0-4C (hex) EUROTEL TELECOM LTD. -00-D0-4D (hex) DIV OF RESEARCH & STATISTICS -00-D0-4E (hex) LOGIBAG -00-D0-4F (hex) BITRONICS, INC. -00-D0-50 (hex) ISKRATEL -00-D0-51 (hex) O2 MICRO, INC. -00-D0-52 (hex) ASCEND COMMUNICATIONS, INC. -00-D0-53 (hex) CONNECTED SYSTEMS -00-D0-54 (hex) SAS INSTITUTE INC. -00-D0-55 (hex) KATHREIN-WERKE KG -00-D0-56 (hex) SOMAT CORPORATION -00-D0-57 (hex) ULTRAK, INC. -00-D0-58 (hex) CISCO SYSTEMS, INC. -00-D0-59 (hex) AMBIT MICROSYSTEMS CORP. -00-D0-5A (hex) SYMBIONICS, LTD. -00-D0-5B (hex) ACROLOOP MOTION CONTROL -00-D0-5C (hex) TECHNOTREND SYSTEMTECHNIK GMBH -00-D0-5D (hex) INTELLIWORXX, INC. -00-D0-5E (hex) STRATABEAM TECHNOLOGY, INC. -00-D0-5F (hex) VALCOM, INC. -00-D0-60 (hex) Panasonic Europe Ltd. -00-D0-61 (hex) TREMON ENTERPRISES CO., LTD. -00-D0-62 (hex) DIGIGRAM -00-D0-63 (hex) CISCO SYSTEMS, INC. -00-D0-64 (hex) MULTITEL -00-D0-65 (hex) TOKO ELECTRIC -00-D0-66 (hex) WINTRISS ENGINEERING CORP. -00-D0-67 (hex) CAMPIO COMMUNICATIONS -00-D0-68 (hex) IWILL CORPORATION -00-D0-69 (hex) TECHNOLOGIC SYSTEMS -00-D0-6A (hex) LINKUP SYSTEMS CORPORATION -00-D0-6B (hex) SR TELECOM INC. -00-D0-6C (hex) SHAREWAVE, INC. -00-D0-6D (hex) ACRISON, INC. -00-D0-6E (hex) TRENDVIEW RECORDERS LTD. -00-D0-6F (hex) KMC CONTROLS -00-D0-70 (hex) LONG WELL ELECTRONICS CORP. -00-D0-71 (hex) ECHELON CORP. -00-D0-72 (hex) BROADLOGIC -00-D0-73 (hex) ACN ADVANCED COMMUNICATIONS -00-D0-74 (hex) TAQUA SYSTEMS, INC. -00-D0-75 (hex) ALARIS MEDICAL SYSTEMS, INC. -00-D0-76 (hex) Merrill Lynch & Co., Inc. -00-D0-77 (hex) LUCENT TECHNOLOGIES -00-D0-78 (hex) Eltex of Sweden AB -00-D0-79 (hex) CISCO SYSTEMS, INC. -00-D0-7A (hex) AMAQUEST COMPUTER CORP. -00-D0-7B (hex) COMCAM INTERNATIONAL INC -00-D0-7C (hex) KOYO ELECTRONICS INC. CO.,LTD. -00-D0-7D (hex) COSINE COMMUNICATIONS -00-D0-7E (hex) KEYCORP LTD. -00-D0-7F (hex) STRATEGY & TECHNOLOGY, LIMITED -00-D0-80 (hex) EXABYTE CORPORATION -00-D0-81 (hex) RTD Embedded Technologies, Inc. -00-D0-82 (hex) IOWAVE INC. -00-D0-83 (hex) INVERTEX, INC. -00-D0-84 (hex) NEXCOMM SYSTEMS, INC. -00-D0-85 (hex) OTIS ELEVATOR COMPANY -00-D0-86 (hex) FOVEON, INC. -00-D0-87 (hex) MICROFIRST INC. -00-D0-88 (hex) Motorola, Inc. -00-D0-89 (hex) DYNACOLOR, INC. -00-D0-8A (hex) PHOTRON USA -00-D0-8B (hex) ADVA Optical Networking Ltd -00-D0-8C (hex) GENOA TECHNOLOGY, INC. -00-D0-8D (hex) PHOENIX GROUP, INC. -00-D0-8E (hex) NVISION INC. -00-D0-8F (hex) ARDENT TECHNOLOGIES, INC. -00-D0-90 (hex) CISCO SYSTEMS, INC. -00-D0-91 (hex) SMARTSAN SYSTEMS, INC. -00-D0-92 (hex) GLENAYRE WESTERN MULTIPLEX -00-D0-93 (hex) TQ - COMPONENTS GMBH -00-D0-94 (hex) TIMELINE VISTA, INC. -00-D0-95 (hex) Alcatel-Lucent, Enterprise Business Group -00-D0-96 (hex) 3COM EUROPE LTD. -00-D0-97 (hex) CISCO SYSTEMS, INC. -00-D0-98 (hex) Photon Dynamics Canada Inc. -00-D0-99 (hex) ELCARD OY -00-D0-9A (hex) FILANET CORPORATION -00-D0-9B (hex) SPECTEL LTD. -00-D0-9C (hex) KAPADIA COMMUNICATIONS -00-D0-9D (hex) VERIS INDUSTRIES -00-D0-9E (hex) 2WIRE, INC. -00-D0-9F (hex) NOVTEK TEST SYSTEMS -00-D0-A0 (hex) MIPS DENMARK -00-D0-A1 (hex) OSKAR VIERLING GMBH + CO. KG -00-D0-A2 (hex) INTEGRATED DEVICE -00-D0-A3 (hex) VOCAL DATA, INC. -00-D0-A4 (hex) ALANTRO COMMUNICATIONS -00-D0-A5 (hex) AMERICAN ARIUM -00-D0-A6 (hex) LANBIRD TECHNOLOGY CO., LTD. -00-D0-A7 (hex) TOKYO SOKKI KENKYUJO CO., LTD. -00-D0-A8 (hex) NETWORK ENGINES, INC. -00-D0-A9 (hex) SHINANO KENSHI CO., LTD. -00-D0-AA (hex) CHASE COMMUNICATIONS -00-D0-AB (hex) DELTAKABEL TELECOM CV -00-D0-AC (hex) GRAYSON WIRELESS -00-D0-AD (hex) TL INDUSTRIES -00-D0-AE (hex) ORESIS COMMUNICATIONS, INC. -00-D0-AF (hex) CUTLER-HAMMER, INC. -00-D0-B0 (hex) BITSWITCH LTD. -00-D0-B1 (hex) OMEGA ELECTRONICS SA -00-D0-B2 (hex) XIOTECH CORPORATION -00-D0-B3 (hex) DRS FLIGHT SAFETY AND -00-D0-B4 (hex) KATSUJIMA CO., LTD. -00-D0-B5 (hex) IPricot formerly DotCom -00-D0-B6 (hex) CRESCENT NETWORKS, INC. -00-D0-B7 (hex) INTEL CORPORATION -00-D0-B8 (hex) Iomega Corporation -00-D0-B9 (hex) MICROTEK INTERNATIONAL, INC. -00-D0-BA (hex) CISCO SYSTEMS, INC. -00-D0-BB (hex) CISCO SYSTEMS, INC. -00-D0-BC (hex) CISCO SYSTEMS, INC. -00-D0-BD (hex) Silicon Image GmbH -00-D0-BE (hex) EMUTEC INC. -00-D0-BF (hex) PIVOTAL TECHNOLOGIES -00-D0-C0 (hex) CISCO SYSTEMS, INC. -00-D0-C1 (hex) HARMONIC DATA SYSTEMS, LTD. -00-D0-C2 (hex) BALTHAZAR TECHNOLOGY AB -00-D0-C3 (hex) VIVID TECHNOLOGY PTE, LTD. -00-D0-C4 (hex) TERATECH CORPORATION -00-D0-C5 (hex) COMPUTATIONAL SYSTEMS, INC. -00-D0-C6 (hex) THOMAS & BETTS CORP. -00-D0-C7 (hex) PATHWAY, INC. -00-D0-C8 (hex) Prevas A/S -00-D0-C9 (hex) ADVANTECH CO., LTD. -00-D0-CA (hex) Intrinsyc Software International Inc. -00-D0-CB (hex) DASAN CO., LTD. -00-D0-CC (hex) TECHNOLOGIES LYRE INC. -00-D0-CD (hex) ATAN TECHNOLOGY INC. -00-D0-CE (hex) ASYST ELECTRONIC -00-D0-CF (hex) MORETON BAY -00-D0-D0 (hex) ZHONGXING TELECOM LTD. -00-D0-D1 (hex) Sycamore Networks -00-D0-D2 (hex) EPILOG CORPORATION -00-D0-D3 (hex) CISCO SYSTEMS, INC. -00-D0-D4 (hex) V-BITS, INC. -00-D0-D5 (hex) GRUNDIG AG -00-D0-D6 (hex) AETHRA TELECOMUNICAZIONI -00-D0-D7 (hex) B2C2, INC. -00-D0-D8 (hex) 3Com Corporation -00-D0-D9 (hex) DEDICATED MICROCOMPUTERS -00-D0-DA (hex) TAICOM DATA SYSTEMS CO., LTD. -00-D0-DB (hex) MCQUAY INTERNATIONAL -00-D0-DC (hex) MODULAR MINING SYSTEMS, INC. -00-D0-DD (hex) SUNRISE TELECOM, INC. -00-D0-DE (hex) PHILIPS MULTIMEDIA NETWORK -00-D0-DF (hex) KUZUMI ELECTRONICS, INC. -00-D0-E0 (hex) DOOIN ELECTRONICS CO. -00-D0-E1 (hex) AVIONITEK ISRAEL INC. -00-D0-E2 (hex) MRT MICRO, INC. -00-D0-E3 (hex) ELE-CHEM ENGINEERING CO., LTD. -00-D0-E4 (hex) CISCO SYSTEMS, INC. -00-D0-E5 (hex) SOLIDUM SYSTEMS CORP. -00-D0-E6 (hex) IBOND INC. -00-D0-E7 (hex) VCON TELECOMMUNICATION LTD. -00-D0-E8 (hex) MAC SYSTEM CO., LTD. -00-D0-E9 (hex) Advantage Century Telecommunication Corp. -00-D0-EA (hex) NEXTONE COMMUNICATIONS, INC. -00-D0-EB (hex) LIGHTERA NETWORKS, INC. -00-D0-EC (hex) NAKAYO TELECOMMUNICATIONS, INC -00-D0-ED (hex) XIOX -00-D0-EE (hex) DICTAPHONE CORPORATION -00-D0-EF (hex) IGT -00-D0-F0 (hex) CONVISION TECHNOLOGY GMBH -00-D0-F1 (hex) SEGA ENTERPRISES, LTD. -00-D0-F2 (hex) MONTEREY NETWORKS -00-D0-F3 (hex) SOLARI DI UDINE SPA -00-D0-F4 (hex) CARINTHIAN TECH INSTITUTE -00-D0-F5 (hex) ORANGE MICRO, INC. -00-D0-F6 (hex) Alcatel Canada -00-D0-F7 (hex) NEXT NETS CORPORATION -00-D0-F8 (hex) FUJIAN STAR TERMINAL -00-D0-F9 (hex) ACUTE COMMUNICATIONS CORP. -00-D0-FA (hex) Thales e-Security Ltd. -00-D0-FB (hex) TEK MICROSYSTEMS, INCORPORATED -00-D0-FC (hex) GRANITE MICROSYSTEMS -00-D0-FD (hex) OPTIMA TELE.COM, INC. -00-D0-FE (hex) ASTRAL POINT -00-D0-FF (hex) CISCO SYSTEMS, INC. -00-D1-1C (hex) ACETEL -00-DB-45 (hex) THAMWAY CO.,LTD. -00-DD-00 (hex) UNGERMANN-BASS INC. -00-DD-01 (hex) UNGERMANN-BASS INC. -00-DD-02 (hex) UNGERMANN-BASS INC. -00-DD-03 (hex) UNGERMANN-BASS INC. -00-DD-04 (hex) UNGERMANN-BASS INC. -00-DD-05 (hex) UNGERMANN-BASS INC. -00-DD-06 (hex) UNGERMANN-BASS INC. -00-DD-07 (hex) UNGERMANN-BASS INC. -00-DD-08 (hex) UNGERMANN-BASS INC. -00-DD-09 (hex) UNGERMANN-BASS INC. -00-DD-0A (hex) UNGERMANN-BASS INC. -00-DD-0B (hex) UNGERMANN-BASS INC. -00-DD-0C (hex) UNGERMANN-BASS INC. -00-DD-0D (hex) UNGERMANN-BASS INC. -00-DD-0E (hex) UNGERMANN-BASS INC. -00-DD-0F (hex) UNGERMANN-BASS INC. -00-E0-00 (hex) Fujitsu Limited -00-E0-01 (hex) STRAND LIGHTING LIMITED -00-E0-02 (hex) CROSSROADS SYSTEMS, INC. -00-E0-03 (hex) NOKIA WIRELESS BUSINESS COMMUN -00-E0-04 (hex) PMC-SIERRA, INC. -00-E0-05 (hex) TECHNICAL CORP. -00-E0-06 (hex) SILICON INTEGRATED SYS. CORP. -00-E0-07 (hex) Avaya ECS Ltd -00-E0-08 (hex) AMAZING CONTROLS! INC. -00-E0-09 (hex) MARATHON TECHNOLOGIES CORP. -00-E0-0A (hex) DIBA, INC. -00-E0-0B (hex) ROOFTOP COMMUNICATIONS CORP. -00-E0-0C (hex) MOTOROLA -00-E0-0D (hex) RADIANT SYSTEMS -00-E0-0E (hex) AVALON IMAGING SYSTEMS, INC. -00-E0-0F (hex) SHANGHAI BAUD DATA -00-E0-10 (hex) HESS SB-AUTOMATENBAU GmbH -00-E0-11 (hex) Uniden Corporation -00-E0-12 (hex) PLUTO TECHNOLOGIES INTERNATIONAL INC. -00-E0-13 (hex) EASTERN ELECTRONIC CO., LTD. -00-E0-14 (hex) CISCO SYSTEMS, INC. -00-E0-15 (hex) HEIWA CORPORATION -00-E0-16 (hex) RAPID CITY COMMUNICATIONS -00-E0-17 (hex) EXXACT GmbH -00-E0-18 (hex) ASUSTEK COMPUTER INC. -00-E0-19 (hex) ING. GIORDANO ELETTRONICA -00-E0-1A (hex) COMTEC SYSTEMS. CO., LTD. -00-E0-1B (hex) SPHERE COMMUNICATIONS, INC. -00-E0-1C (hex) Cradlepoint, Inc -00-E0-1D (hex) WebTV NETWORKS, INC. -00-E0-1E (hex) CISCO SYSTEMS, INC. -00-E0-1F (hex) AVIDIA Systems, Inc. -00-E0-20 (hex) TECNOMEN OY -00-E0-21 (hex) FREEGATE CORP. -00-E0-22 (hex) Analog Devices Inc. -00-E0-23 (hex) TELRAD -00-E0-24 (hex) GADZOOX NETWORKS -00-E0-25 (hex) dit Co., Ltd. -00-E0-26 (hex) Redlake MASD LLC -00-E0-27 (hex) DUX, INC. -00-E0-28 (hex) APTIX CORPORATION -00-E0-29 (hex) STANDARD MICROSYSTEMS CORP. -00-E0-2A (hex) TANDBERG TELEVISION AS -00-E0-2B (hex) EXTREME NETWORKS -00-E0-2C (hex) AST COMPUTER -00-E0-2D (hex) InnoMediaLogic, Inc. -00-E0-2E (hex) SPC ELECTRONICS CORPORATION -00-E0-2F (hex) MCNS HOLDINGS, L.P. -00-E0-30 (hex) MELITA INTERNATIONAL CORP. -00-E0-31 (hex) HAGIWARA ELECTRIC CO., LTD. -00-E0-32 (hex) MISYS FINANCIAL SYSTEMS, LTD. -00-E0-33 (hex) E.E.P.D. GmbH -00-E0-34 (hex) CISCO SYSTEMS, INC. -00-E0-35 (hex) Emerson Network Power -00-E0-36 (hex) PIONEER CORPORATION -00-E0-37 (hex) CENTURY CORPORATION -00-E0-38 (hex) PROXIMA CORPORATION -00-E0-39 (hex) PARADYNE CORP. -00-E0-3A (hex) CABLETRON SYSTEMS, INC. -00-E0-3B (hex) PROMINET CORPORATION -00-E0-3C (hex) AdvanSys -00-E0-3D (hex) FOCON ELECTRONIC SYSTEMS A/S -00-E0-3E (hex) ALFATECH, INC. -00-E0-3F (hex) JATON CORPORATION -00-E0-40 (hex) DeskStation Technology, Inc. -00-E0-41 (hex) CSPI -00-E0-42 (hex) Pacom Systems Ltd. -00-E0-43 (hex) VitalCom -00-E0-44 (hex) LSICS CORPORATION -00-E0-45 (hex) TOUCHWAVE, INC. -00-E0-46 (hex) BENTLY NEVADA CORP. -00-E0-47 (hex) InFocus Corporation -00-E0-48 (hex) SDL COMMUNICATIONS, INC. -00-E0-49 (hex) MICROWI ELECTRONIC GmbH -00-E0-4A (hex) ENHANCED MESSAGING SYSTEMS, INC -00-E0-4B (hex) JUMP INDUSTRIELLE COMPUTERTECHNIK GmbH -00-E0-4C (hex) REALTEK SEMICONDUCTOR CORP. -00-E0-4D (hex) INTERNET INITIATIVE JAPAN, INC -00-E0-4E (hex) SANYO DENKI CO., LTD. -00-E0-4F (hex) CISCO SYSTEMS, INC. -00-E0-50 (hex) EXECUTONE INFORMATION SYSTEMS, INC. -00-E0-51 (hex) TALX CORPORATION -00-E0-52 (hex) Brocade Communications Systems, Inc -00-E0-53 (hex) CELLPORT LABS, INC. -00-E0-54 (hex) KODAI HITEC CO., LTD. -00-E0-55 (hex) INGENIERIA ELECTRONICA COMERCIAL INELCOM S.A. -00-E0-56 (hex) HOLONTECH CORPORATION -00-E0-57 (hex) HAN MICROTELECOM. CO., LTD. -00-E0-58 (hex) PHASE ONE DENMARK A/S -00-E0-59 (hex) CONTROLLED ENVIRONMENTS, LTD. -00-E0-5A (hex) GALEA NETWORK SECURITY -00-E0-5B (hex) WEST END SYSTEMS CORP. -00-E0-5C (hex) MATSUSHITA KOTOBUKI ELECTRONICS INDUSTRIES, LTD. -00-E0-5D (hex) UNITEC CO., LTD. -00-E0-5E (hex) JAPAN AVIATION ELECTRONICS INDUSTRY, LTD. -00-E0-5F (hex) e-Net, Inc. -00-E0-60 (hex) SHERWOOD -00-E0-61 (hex) EdgePoint Networks, Inc. -00-E0-62 (hex) HOST ENGINEERING -00-E0-63 (hex) CABLETRON - YAGO SYSTEMS, INC. -00-E0-64 (hex) SAMSUNG ELECTRONICS -00-E0-65 (hex) OPTICAL ACCESS INTERNATIONAL -00-E0-66 (hex) ProMax Systems, Inc. -00-E0-67 (hex) eac AUTOMATION-CONSULTING GmbH -00-E0-68 (hex) MERRIMAC SYSTEMS INC. -00-E0-69 (hex) JAYCOR -00-E0-6A (hex) KAPSCH AG -00-E0-6B (hex) W&G SPECIAL PRODUCTS -00-E0-6C (hex) AEP Systems International Ltd -00-E0-6D (hex) COMPUWARE CORPORATION -00-E0-6E (hex) FAR SYSTEMS S.p.A. -00-E0-6F (hex) Motorola, Inc. -00-E0-70 (hex) DH TECHNOLOGY -00-E0-71 (hex) EPIS MICROCOMPUTER -00-E0-72 (hex) LYNK -00-E0-73 (hex) NATIONAL AMUSEMENT NETWORK, INC. -00-E0-74 (hex) TIERNAN COMMUNICATIONS, INC. -00-E0-75 (hex) Verilink Corporation -00-E0-76 (hex) DEVELOPMENT CONCEPTS, INC. -00-E0-77 (hex) WEBGEAR, INC. -00-E0-78 (hex) BERKELEY NETWORKS -00-E0-79 (hex) A.T.N.R. -00-E0-7A (hex) MIKRODIDAKT AB -00-E0-7B (hex) BAY NETWORKS -00-E0-7C (hex) METTLER-TOLEDO, INC. -00-E0-7D (hex) NETRONIX, INC. -00-E0-7E (hex) WALT DISNEY IMAGINEERING -00-E0-7F (hex) LOGISTISTEM s.r.l. -00-E0-80 (hex) CONTROL RESOURCES CORPORATION -00-E0-81 (hex) TYAN COMPUTER CORP. -00-E0-82 (hex) ANERMA -00-E0-83 (hex) JATO TECHNOLOGIES, INC. -00-E0-84 (hex) COMPULITE R&D -00-E0-85 (hex) GLOBAL MAINTECH, INC. -00-E0-86 (hex) CYBEX COMPUTER PRODUCTS -00-E0-87 (hex) LeCroy - Networking Productions Division -00-E0-88 (hex) LTX CORPORATION -00-E0-89 (hex) ION Networks, Inc. -00-E0-8A (hex) GEC AVERY, LTD. -00-E0-8B (hex) QLogic Corp. -00-E0-8C (hex) NEOPARADIGM LABS, INC. -00-E0-8D (hex) PRESSURE SYSTEMS, INC. -00-E0-8E (hex) UTSTARCOM -00-E0-8F (hex) CISCO SYSTEMS, INC. -00-E0-90 (hex) BECKMAN LAB. AUTOMATION DIV. -00-E0-91 (hex) LG ELECTRONICS, INC. -00-E0-92 (hex) ADMTEK INCORPORATED -00-E0-93 (hex) ACKFIN NETWORKS -00-E0-94 (hex) OSAI SRL -00-E0-95 (hex) ADVANCED-VISION TECHNOLGIES CORP. -00-E0-96 (hex) SHIMADZU CORPORATION -00-E0-97 (hex) CARRIER ACCESS CORPORATION -00-E0-98 (hex) AboCom Systems, Inc. -00-E0-99 (hex) SAMSON AG -00-E0-9A (hex) Positron Inc. -00-E0-9B (hex) ENGAGE NETWORKS, INC. -00-E0-9C (hex) MII -00-E0-9D (hex) SARNOFF CORPORATION -00-E0-9E (hex) QUANTUM CORPORATION -00-E0-9F (hex) PIXEL VISION -00-E0-A0 (hex) WILTRON CO. -00-E0-A1 (hex) HIMA PAUL HILDEBRANDT GmbH Co. KG -00-E0-A2 (hex) MICROSLATE INC. -00-E0-A3 (hex) CISCO SYSTEMS, INC. -00-E0-A4 (hex) ESAOTE S.p.A. -00-E0-A5 (hex) ComCore Semiconductor, Inc. -00-E0-A6 (hex) TELOGY NETWORKS, INC. -00-E0-A7 (hex) IPC INFORMATION SYSTEMS, INC. -00-E0-A8 (hex) SAT GmbH & Co. -00-E0-A9 (hex) FUNAI ELECTRIC CO., LTD. -00-E0-AA (hex) ELECTROSONIC LTD. -00-E0-AB (hex) DIMAT S.A. -00-E0-AC (hex) MIDSCO, INC. -00-E0-AD (hex) EES TECHNOLOGY, LTD. -00-E0-AE (hex) XAQTI CORPORATION -00-E0-AF (hex) GENERAL DYNAMICS INFORMATION SYSTEMS -00-E0-B0 (hex) CISCO SYSTEMS, INC. -00-E0-B1 (hex) Alcatel-Lucent, Enterprise Business Group -00-E0-B2 (hex) TELMAX COMMUNICATIONS CORP. -00-E0-B3 (hex) EtherWAN Systems, Inc. -00-E0-B4 (hex) TECHNO SCOPE CO., LTD. -00-E0-B5 (hex) ARDENT COMMUNICATIONS CORP. -00-E0-B6 (hex) Entrada Networks -00-E0-B7 (hex) PI GROUP, LTD. -00-E0-B8 (hex) GATEWAY 2000 -00-E0-B9 (hex) BYAS SYSTEMS -00-E0-BA (hex) BERGHOF AUTOMATIONSTECHNIK GmbH -00-E0-BB (hex) NBX CORPORATION -00-E0-BC (hex) SYMON COMMUNICATIONS, INC. -00-E0-BD (hex) INTERFACE SYSTEMS, INC. -00-E0-BE (hex) GENROCO INTERNATIONAL, INC. -00-E0-BF (hex) TORRENT NETWORKING TECHNOLOGIES CORP. -00-E0-C0 (hex) SEIWA ELECTRIC MFG. CO., LTD. -00-E0-C1 (hex) MEMOREX TELEX JAPAN, LTD. -00-E0-C2 (hex) NECSY S.p.A. -00-E0-C3 (hex) SAKAI SYSTEM DEVELOPMENT CORP. -00-E0-C4 (hex) HORNER ELECTRIC, INC. -00-E0-C5 (hex) BCOM ELECTRONICS INC. -00-E0-C6 (hex) LINK2IT, L.L.C. -00-E0-C7 (hex) EUROTECH SRL -00-E0-C8 (hex) VIRTUAL ACCESS, LTD. -00-E0-C9 (hex) AutomatedLogic Corporation -00-E0-CA (hex) BEST DATA PRODUCTS -00-E0-CB (hex) RESON, INC. -00-E0-CC (hex) HERO SYSTEMS, LTD. -00-E0-CD (hex) SENSIS CORPORATION -00-E0-CE (hex) ARN -00-E0-CF (hex) INTEGRATED DEVICE TECHNOLOGY, INC. -00-E0-D0 (hex) NETSPEED, INC. -00-E0-D1 (hex) TELSIS LIMITED -00-E0-D2 (hex) VERSANET COMMUNICATIONS, INC. -00-E0-D3 (hex) DATENTECHNIK GmbH -00-E0-D4 (hex) EXCELLENT COMPUTER -00-E0-D5 (hex) Emulex Corporation -00-E0-D6 (hex) COMPUTER & COMMUNICATION RESEARCH LAB. -00-E0-D7 (hex) SUNSHINE ELECTRONICS, INC. -00-E0-D8 (hex) LANBit Computer, Inc. -00-E0-D9 (hex) TAZMO CO., LTD. -00-E0-DA (hex) Alcatel North America ESD -00-E0-DB (hex) ViaVideo Communications, Inc. -00-E0-DC (hex) NEXWARE CORP. -00-E0-DD (hex) ZENITH ELECTRONICS CORPORATION -00-E0-DE (hex) DATAX NV -00-E0-DF (hex) KEYMILE GmbH -00-E0-E0 (hex) SI ELECTRONICS, LTD. -00-E0-E1 (hex) G2 NETWORKS, INC. -00-E0-E2 (hex) INNOVA CORP. -00-E0-E3 (hex) SK-ELEKTRONIK GmbH -00-E0-E4 (hex) FANUC ROBOTICS NORTH AMERICA, Inc. -00-E0-E5 (hex) CINCO NETWORKS, INC. -00-E0-E6 (hex) INCAA DATACOM B.V. -00-E0-E7 (hex) RAYTHEON E-SYSTEMS, INC. -00-E0-E8 (hex) GRETACODER Data Systems AG -00-E0-E9 (hex) DATA LABS, INC. -00-E0-EA (hex) INNOVAT COMMUNICATIONS, INC. -00-E0-EB (hex) DIGICOM SYSTEMS, INCORPORATED -00-E0-EC (hex) CELESTICA INC. -00-E0-ED (hex) SILICOM, LTD. -00-E0-EE (hex) MAREL HF -00-E0-EF (hex) DIONEX -00-E0-F0 (hex) ABLER TECHNOLOGY, INC. -00-E0-F1 (hex) THAT CORPORATION -00-E0-F2 (hex) ARLOTTO COMNET, INC. -00-E0-F3 (hex) WebSprint Communications, Inc. -00-E0-F4 (hex) INSIDE Technology A/S -00-E0-F5 (hex) TELES AG -00-E0-F6 (hex) DECISION EUROPE -00-E0-F7 (hex) CISCO SYSTEMS, INC. -00-E0-F8 (hex) DICNA CONTROL AB -00-E0-F9 (hex) CISCO SYSTEMS, INC. -00-E0-FA (hex) TRL TECHNOLOGY, LTD. -00-E0-FB (hex) LEIGHTRONIX, INC. -00-E0-FC (hex) HUAWEI TECHNOLOGIES CO., LTD. -00-E0-FD (hex) A-TREND TECHNOLOGY CO., LTD. -00-E0-FE (hex) CISCO SYSTEMS, INC. -00-E0-FF (hex) SECURITY DYNAMICS TECHNOLOGIES, Inc. -00-E6-D3 (hex) NIXDORF COMPUTER CORP. -02-07-01 (hex) RACAL-DATACOM -02-1C-7C (hex) PERQ SYSTEMS CORPORATION -02-60-86 (hex) LOGIC REPLACEMENT TECH. LTD. -02-60-8C (hex) 3COM CORPORATION -02-70-01 (hex) RACAL-DATACOM -02-70-B0 (hex) M/A-COM INC. COMPANIES -02-70-B3 (hex) DATA RECALL LTD -02-9D-8E (hex) CARDIAC RECORDERS INC. -02-AA-3C (hex) OLIVETTI TELECOMM SPA (OLTECO) -02-BB-01 (hex) OCTOTHORPE CORP. -02-C0-8C (hex) 3COM CORPORATION -02-CF-1C (hex) COMMUNICATION MACHINERY CORP. -02-E6-D3 (hex) NIXDORF COMPUTER CORPORATION -04-0A-E0 (hex) XMIT AG COMPUTER NETWORKS -04-0E-C2 (hex) ViewSonic Mobile China Limited -04-1E-64 (hex) Apple, Inc -04-22-34 (hex) Wireless Standard Extensions -04-2B-BB (hex) PicoCELA, Inc. -04-2F-56 (hex) ATOCS (Shenzhen) LTD -04-4F-AA (hex) Ruckus Wireless -04-76-6E (hex) ALPS Co,. Ltd. -04-94-A1 (hex) CATCH THE WIND INC -04-9F-81 (hex) Simena, LLC -04-B3-B6 (hex) Seamap (UK) Ltd -04-B4-66 (hex) BSP Co., Ltd. -04-C0-5B (hex) Tigo Energy -04-C8-80 (hex) Samtec Inc -04-E0-C4 (hex) TRIUMPH-ADLER AG -04-FE-7F (hex) Cisco Systems -08-00-01 (hex) COMPUTERVISION CORPORATION -08-00-02 (hex) BRIDGE COMMUNICATIONS INC. -08-00-03 (hex) ADVANCED COMPUTER COMM. -08-00-04 (hex) CROMEMCO INCORPORATED -08-00-05 (hex) SYMBOLICS INC. -08-00-06 (hex) SIEMENS AG -08-00-07 (hex) APPLE COMPUTER INC. -08-00-08 (hex) BOLT BERANEK AND NEWMAN INC. -08-00-09 (hex) HEWLETT PACKARD -08-00-0A (hex) NESTAR SYSTEMS INCORPORATED -08-00-0B (hex) UNISYS CORPORATION -08-00-0C (hex) MIKLYN DEVELOPMENT CO. -08-00-0D (hex) INTERNATIONAL COMPUTERS LTD. -08-00-0E (hex) NCR CORPORATION -08-00-0F (hex) MITEL CORPORATION -08-00-11 (hex) TEKTRONIX INC. -08-00-12 (hex) BELL ATLANTIC INTEGRATED SYST. -08-00-13 (hex) EXXON -08-00-14 (hex) EXCELAN -08-00-15 (hex) STC BUSINESS SYSTEMS -08-00-16 (hex) BARRISTER INFO SYS CORP -08-00-17 (hex) NATIONAL SEMICONDUCTOR -08-00-18 (hex) PIRELLI FOCOM NETWORKS -08-00-19 (hex) GENERAL ELECTRIC CORPORATION -08-00-1A (hex) TIARA/ 10NET -08-00-1B (hex) EMC Corporation -08-00-1C (hex) KDD-KOKUSAI DEBNSIN DENWA CO. -08-00-1D (hex) ABLE COMMUNICATIONS INC. -08-00-1E (hex) APOLLO COMPUTER INC. -08-00-1F (hex) SHARP CORPORATION -08-00-20 (hex) SUN MICROSYSTEMS INC. -08-00-21 (hex) 3M COMPANY -08-00-22 (hex) NBI INC. -08-00-23 (hex) Panasonic Communications Co., Ltd. -08-00-24 (hex) 10NET COMMUNICATIONS/DCA -08-00-25 (hex) CONTROL DATA -08-00-26 (hex) NORSK DATA A.S. -08-00-27 (hex) CADMUS COMPUTER SYSTEMS -08-00-28 (hex) Texas Instruments -08-00-29 (hex) MEGATEK CORPORATION -08-00-2A (hex) MOSAIC TECHNOLOGIES INC. -08-00-2B (hex) DIGITAL EQUIPMENT CORPORATION -08-00-2C (hex) BRITTON LEE INC. -08-00-2D (hex) LAN-TEC INC. -08-00-2E (hex) METAPHOR COMPUTER SYSTEMS -08-00-2F (hex) PRIME COMPUTER INC. -08-00-30 (hex) NETWORK RESEARCH CORPORATION -08-00-30 (hex) CERN -08-00-30 (hex) ROYAL MELBOURNE INST OF TECH -08-00-31 (hex) LITTLE MACHINES INC. -08-00-32 (hex) TIGAN INCORPORATED -08-00-33 (hex) BAUSCH & LOMB -08-00-34 (hex) FILENET CORPORATION -08-00-35 (hex) MICROFIVE CORPORATION -08-00-36 (hex) INTERGRAPH CORPORATION -08-00-37 (hex) FUJI-XEROX CO. LTD. -08-00-38 (hex) CII HONEYWELL BULL -08-00-39 (hex) SPIDER SYSTEMS LIMITED -08-00-3A (hex) ORCATECH INC. -08-00-3B (hex) TORUS SYSTEMS LIMITED -08-00-3C (hex) SCHLUMBERGER WELL SERVICES -08-00-3D (hex) CADNETIX CORPORATIONS -08-00-3E (hex) CODEX CORPORATION -08-00-3F (hex) FRED KOSCHARA ENTERPRISES -08-00-40 (hex) FERRANTI COMPUTER SYS. LIMITED -08-00-41 (hex) RACAL-MILGO INFORMATION SYS.. -08-00-42 (hex) JAPAN MACNICS CORP. -08-00-43 (hex) PIXEL COMPUTER INC. -08-00-44 (hex) DAVID SYSTEMS INC. -08-00-45 (hex) CONCURRENT COMPUTER CORP. -08-00-46 (hex) Sony Corporation -08-00-47 (hex) SEQUENT COMPUTER SYSTEMS INC. -08-00-48 (hex) EUROTHERM GAUGING SYSTEMS -08-00-49 (hex) UNIVATION -08-00-4A (hex) BANYAN SYSTEMS INC. -08-00-4B (hex) PLANNING RESEARCH CORP. -08-00-4C (hex) HYDRA COMPUTER SYSTEMS INC. -08-00-4D (hex) CORVUS SYSTEMS INC. -08-00-4E (hex) 3COM EUROPE LTD. -08-00-4F (hex) CYGNET SYSTEMS -08-00-50 (hex) DAISY SYSTEMS CORP. -08-00-51 (hex) EXPERDATA -08-00-52 (hex) INSYSTEC -08-00-53 (hex) MIDDLE EAST TECH. UNIVERSITY -08-00-55 (hex) STANFORD TELECOMM. INC. -08-00-56 (hex) STANFORD LINEAR ACCEL. CENTER -08-00-57 (hex) EVANS & SUTHERLAND -08-00-58 (hex) SYSTEMS CONCEPTS -08-00-59 (hex) A/S MYCRON -08-00-5A (hex) IBM CORPORATION -08-00-5B (hex) VTA TECHNOLOGIES INC. -08-00-5C (hex) FOUR PHASE SYSTEMS -08-00-5D (hex) GOULD INC. -08-00-5E (hex) COUNTERPOINT COMPUTER INC. -08-00-5F (hex) SABER TECHNOLOGY CORP. -08-00-60 (hex) INDUSTRIAL NETWORKING INC. -08-00-61 (hex) JAROGATE LTD. -08-00-62 (hex) GENERAL DYNAMICS -08-00-63 (hex) PLESSEY -08-00-64 (hex) AUTOPHON AG -08-00-65 (hex) GENRAD INC. -08-00-66 (hex) AGFA CORPORATION -08-00-67 (hex) COMDESIGN -08-00-68 (hex) RIDGE COMPUTERS -08-00-69 (hex) SILICON GRAPHICS INC. -08-00-6A (hex) ATT BELL LABORATORIES -08-00-6B (hex) ACCEL TECHNOLOGIES INC. -08-00-6C (hex) SUNTEK TECHNOLOGY INT'L -08-00-6D (hex) WHITECHAPEL COMPUTER WORKS -08-00-6E (hex) MASSCOMP -08-00-6F (hex) PHILIPS APELDOORN B.V. -08-00-70 (hex) MITSUBISHI ELECTRIC CORP. -08-00-71 (hex) MATRA (DSIE) -08-00-72 (hex) XEROX CORP UNIV GRANT PROGRAM -08-00-73 (hex) TECMAR INC. -08-00-74 (hex) CASIO COMPUTER CO. LTD. -08-00-75 (hex) DANSK DATA ELECTRONIK -08-00-76 (hex) PC LAN TECHNOLOGIES -08-00-77 (hex) TSL COMMUNICATIONS LTD. -08-00-78 (hex) ACCELL CORPORATION -08-00-79 (hex) THE DROID WORKS -08-00-7A (hex) INDATA -08-00-7B (hex) SANYO ELECTRIC CO. LTD. -08-00-7C (hex) VITALINK COMMUNICATIONS CORP. -08-00-7E (hex) AMALGAMATED WIRELESS(AUS) LTD -08-00-7F (hex) CARNEGIE-MELLON UNIVERSITY -08-00-80 (hex) AES DATA INC. -08-00-81 (hex) ,ASTECH INC. -08-00-82 (hex) VERITAS SOFTWARE -08-00-83 (hex) Seiko Instruments Inc. -08-00-84 (hex) TOMEN ELECTRONICS CORP. -08-00-85 (hex) ELXSI -08-00-86 (hex) KONICA MINOLTA HOLDINGS, INC. -08-00-87 (hex) XYPLEX -08-00-88 (hex) Brocade Communications Systems, Inc. -08-00-89 (hex) KINETICS -08-00-8A (hex) PERFORMANCE TECHNOLOGY -08-00-8B (hex) PYRAMID TECHNOLOGY CORP. -08-00-8C (hex) NETWORK RESEARCH CORPORATION -08-00-8D (hex) XYVISION INC. -08-00-8E (hex) TANDEM COMPUTERS -08-00-8F (hex) CHIPCOM CORPORATION -08-00-90 (hex) SONOMA SYSTEMS -08-14-43 (hex) UNIBRAIN S.A. -08-16-51 (hex) Shenzhen Sea Star Technology Co.,Ltd -08-18-4C (hex) A. S. Thomas, Inc. -08-1F-F3 (hex) Cisco Systems -08-2A-D0 (hex) SRD Innovations Inc. -08-4E-1C (hex) H2A Systems, LLC -08-76-18 (hex) ViE Technologies Sdn. Bhd. -08-76-95 (hex) Auto Industrial Co., Ltd. -08-BB-CC (hex) AK-NORD EDV VERTRIEBSGES. mbH -08-F2-F4 (hex) Net One Partners Co.,Ltd. -0C-17-F1 (hex) TELECSYS -0C-27-55 (hex) Valuable Techologies Limited -0C-60-76 (hex) Hon Hai Precision Ind. Co.,Ltd. -0C-7D-7C (hex) Kexiang Information Technology Co, Ltd. -0C-82-30 (hex) SHENZHEN MAGNUS TECHNOLOGIES CO.,LTD -0C-84-11 (hex) A.O. Smith Water Products -0C-A4-2A (hex) OB Telecom Electronic Technology Co., Ltd -0C-C3-A7 (hex) Meritec -0C-C9-C6 (hex) Samwin Hong Kong Limited -0C-D5-02 (hex) Westell -0C-D7-C2 (hex) Axium Technologies, Inc. -0C-E7-09 (hex) Fox Crypto B.V. -0C-E9-36 (hex) ELIMOS srl -0C-EE-E6 (hex) Hon Hai Precision Ind. Co.,Ltd. -0C-EF-7C (hex) AnaCom Inc -10-00-00 (hex) PRIVATE -10-00-5A (hex) IBM CORPORATION -10-00-E8 (hex) NATIONAL SEMICONDUCTOR -10-10-B6 (hex) McCain Inc -10-18-9E (hex) Elmo Motion Control -10-2D-96 (hex) Looxcie Inc. -10-43-69 (hex) Soundmax Electronic Limited -10-44-5A (hex) Shaanxi Hitech Electronic Co., LTD -10-45-F8 (hex) LNT-Automation GmbH -10-56-CA (hex) Peplink International Ltd. -10-62-C9 (hex) Adatis GmbH & Co. KG -10-65-A3 (hex) Panamax Inc. -10-88-0F (hex) DARUMA TELECOMUNICAES E INFORMTICA S/A -10-B7-F6 (hex) Plastoform Industries Ltd. -10-BA-A5 (hex) GANA I&C CO., LTD -10-C7-3F (hex) Midas Klark Teknik Ltd -10-CA-81 (hex) PRECIA -10-E6-AE (hex) Source Technologies, LLC -11-00-AA (hex) PRIVATE -14-6E-0A (hex) PRIVATE -14-A6-2C (hex) S.M. Dezac S.A. -14-A8-6B (hex) ShenZhen Telacom Science&Technology Co., Ltd -18-01-E3 (hex) Elektrobit Wireless Communications Ltd -18-17-14 (hex) DAEWOOIS -18-3B-D2 (hex) BYD Precision Manufacture Company Ltd. -18-86-AC (hex) Nokia Danmark A/S -18-A9-05 (hex) Hewlett Packard -18-C0-86 (hex) Broadcom Corporation -18-FC-9F (hex) Changhe Electronics Co., Ltd. -1C-0F-CF (hex) Sypro Optics GmbH -1C-12-9D (hex) IEEE PES PSRC/SUB -1C-4B-D6 (hex) AzureWave -1C-8F-8A (hex) Phase Motion Control SpA -1C-AF-F7 (hex) D-LINK INTERNATIONAL PTE LIMITED -1C-BD-B9 (hex) D-LINK INTERNATIONAL PTE LIMITED -1C-F0-61 (hex) SCAPS GmbH -20-12-57 (hex) Most Lucky Trading Ltd -20-21-A5 (hex) LG Electronics Inc -20-2C-B7 (hex) Kong Yue Electronics & Information Industry (Xinhui) Ltd. -20-41-5A (hex) Smarteh d.o.o. -20-46-F9 (hex) Advanced Network Devices (dba:AND) -20-4E-6B (hex) Axxana(israel) ltd -20-59-A0 (hex) Paragon Technologies Inc. -20-7C-8F (hex) Quanta Microsystems,Inc. -20-BF-DB (hex) DVL -24-21-AB (hex) Sony Ericsson Mobile Communications -24-82-8A (hex) Prowave Technologies Ltd. -24-BF-74 (hex) PRIVATE -24-CF-21 (hex) Shenzhen State Micro Technology Co., Ltd -24-D2-CC (hex) SmartDrive Systems Inc. -24-DB-AD (hex) ShopperTrak RCT Corporation -28-6E-D4 (hex) HUAWEI TECHNOLOGIES CO.,LTD -28-72-C5 (hex) Smartmatic Corp -28-E7-94 (hex) Microtime Computer Inc. -28-EF-01 (hex) PRIVATE -28-FB-D3 (hex) Shanghai RagenTek Communication Technology Co.,Ltd. -2C-06-23 (hex) Win Leader Inc. -2C-19-84 (hex) IDN Telecom, Inc. -2C-34-27 (hex) ERCO & GENER -2C-3A-28 (hex) Fagor Electr�nica -2C-6B-F5 (hex) Juniper networks -2C-81-58 (hex) Hon Hai Precision Ind. Co.,Ltd -2C-91-27 (hex) Eintechno Corporation -2C-A7-80 (hex) True Technologies Inc. -2C-A8-35 (hex) RIM -2C-CD-27 (hex) Precor Inc -30-32-D4 (hex) Hanilstm Co., Ltd. -30-37-A6 (hex) Cisco Systems -30-41-74 (hex) ALTEC LANSING LLC -30-46-9A (hex) NETGEAR -30-52-5A (hex) NST Co., LTD -30-7C-30 (hex) RIM -30-EF-D1 (hex) Alstom Strongwish (Shenzhen) Co., Ltd. -34-15-9E (hex) Apple, Inc -34-7E-39 (hex) Nokia Danmark A/S -34-86-2A (hex) Heinz Lackmann GmbH & Co KG -34-C3-AC (hex) Samsung Electronics -34-C6-9A (hex) Enecsys Ltd -34-CE-94 (hex) Parsec (Pty) Ltd -34-EF-44 (hex) 2Wire -34-EF-8B (hex) NTT Communications Corporation -38-22-9D (hex) PIRELLI BROADBAND SOLUTIONS -38-63-F6 (hex) 3NOD MULTIMEDIA(SHENZHEN)CO.,LTD -38-BB-23 (hex) OzVision America LLC -38-E7-D8 (hex) HTC Corporation -38-E8-DF (hex) b gmbh medien + datenbanken -38-E9-8C (hex) Reco S.p.A. -3C-05-AB (hex) Product Creation Studio -3C-1C-BE (hex) JADAK LLC -3C-2D-B7 (hex) Texas Instruments -3C-39-C3 (hex) JW Electronics Co., Ltd. -3C-4C-69 (hex) Infinity System S.L. -3C-B1-7F (hex) Wattwatchers Pty Ld -3C-DF-1E (hex) Cisco Systems -3C-E5-A6 (hex) Hangzhou H3C Technologies Co., Ltd. -3C-F5-2C (hex) DSPECIALISTS GmbH -3C-F7-2A (hex) Nokia Corporation -40-01-C6 (hex) 3COM EUROPE LTD -40-12-E4 (hex) Compass-EOS -40-15-97 (hex) Protect America, Inc. -40-25-C2 (hex) Intel Corporate -40-2B-A1 (hex) Sony Ericsson Mobile Communications AB -40-4A-03 (hex) ZyXEL Communications Corporation -40-61-86 (hex) MICRO-STAR INT'L CO.,LTD -40-8A-9A (hex) TITENG CO., Ltd. -40-95-58 (hex) Aisino Corporation -40-97-D1 (hex) BK Electronics cc -40-A6-A4 (hex) PassivSystems Ltd -40-D3-2D (hex) Apple, Inc -40-EC-F8 (hex) Siemens AG -40-EF-4C (hex) Fihonest communication co.,Ltd -40-F5-2E (hex) Leica Microsystems (Schweiz) AG -44-37-6F (hex) Young Electric Sign Co -44-4E-1A (hex) Samsung Electronics Co.,Ltd -44-56-8D (hex) PNC Technologies Co., Ltd. -44-56-B7 (hex) Spawn Labs, Inc -44-6C-24 (hex) Reallin Electronic Co.,Ltd -44-83-12 (hex) Star-Net -44-87-FC (hex) ELITEGROUP COMPUTER SYSTEM CO., LTD. -44-8E-81 (hex) VIG -44-A4-2D (hex) TCT Mobile Limited -44-C2-33 (hex) Guangzhou Comet Technology Development Co.Ltd -44-C9-A2 (hex) Greenwald Industries -44-E4-9A (hex) OMNITRONICS PTY LTD -44-F4-59 (hex) Samsung Electronics -48-34-3D (hex) IEP GmbH -48-44-87 (hex) Cisco SPVTG -48-5B-39 (hex) ASUSTek COMPUTER INC. -48-6F-D2 (hex) StorSimple Inc -48-71-19 (hex) SGB GROUP LTD. -48-AA-5D (hex) Store Electronic Systems -48-EB-30 (hex) ETERNA TECHNOLOGY, INC. -4C-32-2D (hex) TELEDATA NETWORKS -4C-4B-68 (hex) Mobile Device, Inc. -4C-63-EB (hex) Application Solutions (Electronics and Vision) Ltd -4C-9E-E4 (hex) Hanyang Navicom Co.,Ltd. -4C-C4-52 (hex) Shang Hai Tyd. Electon Technology Ltd. -4C-C6-02 (hex) Radios, Inc. -50-25-2B (hex) Nethra Imaging Incorporated -50-2A-7E (hex) Smart electronic GmbH -50-2A-8B (hex) Telekom Research and Development Sdn Bhd -50-2D-A2 (hex) Intel Corporate -50-2D-F4 (hex) Phytec Messtechnik GmbH -50-63-13 (hex) Hon Hai Precision Ind. Co.,Ltd. -50-93-4F (hex) Gradual Tecnologia Ltda. -50-A6-E3 (hex) David Clark Company -50-F0-03 (hex) Open Stack, Inc. -54-03-F5 (hex) EBN Technology Corp. -54-42-49 (hex) Sony Corporation -54-5F-A9 (hex) Teracom Limited -54-7F-EE (hex) Cisco Systems -54-92-BE (hex) Samsung Electronics Co.,Ltd -54-9A-16 (hex) Uzushio Electric Co.,Ltd. -54-B6-20 (hex) SUHDOL E&C Co.Ltd. -54-D4-6F (hex) Cisco SPVTG -58-05-56 (hex) Elettronica GF S.r.L. -58-3C-C6 (hex) Omneality Ltd. -58-49-BA (hex) Chitai Electronic Corp. -58-4C-EE (hex) Digital One Technologies, Limited -58-50-E6 (hex) Best Buy Corporation -58-6E-D6 (hex) PRIVATE -58-B0-35 (hex) Apple, Inc -58-F6-7B (hex) Xia Men UnionCore Technology LTD. -5C-14-37 (hex) Thyssenkrupp Aufzugswerke GmbH -5C-33-8E (hex) Alpha Networkc Inc. -5C-35-DA (hex) There Corporation Oy -5C-57-C8 (hex) Nokia Corporation -5C-87-78 (hex) Cybertelbridge co.,ltd -5C-E2-23 (hex) Delphin Technology AG -5C-E2-86 (hex) Nortel Networks -5C-FF-35 (hex) Wistron Corporation -60-1D-0F (hex) Midnite Solar -60-38-0E (hex) Alps Electric Co., -60-39-1F (hex) ABB Ltd -60-89-B7 (hex) KAEL M�HENDISLIK ELEKTRONIK TICARET SANAYI Limited �irketi -60-8D-17 (hex) Sentrus Government Systems Division, Inc -60-9F-9D (hex) CloudSwitch -60-B3-C4 (hex) Elber Srl -60-D0-A9 (hex) Samsung Electronics Co.,Ltd -60-D3-0A (hex) Quatius Limited -60-F1-3D (hex) JABLOCOM s.r.o. -60-FB-42 (hex) Apple, Inc -64-16-8D (hex) Cisco Systems -64-16-F0 (hex) Shehzhen Huawei Communication Technologies Co., Ltd. -64-4B-C3 (hex) Shanghai WOASiS Telecommunications Ltd., Co. -64-4F-74 (hex) LENUS Co., Ltd. -64-65-C0 (hex) Nuvon, Inc -64-68-0C (hex) COMTREND -64-6E-6C (hex) Radio Datacom LLC -64-7D-81 (hex) YOKOTA INDUSTRIAL CO,.LTD -64-A8-37 (hex) Juni Korea Co., Ltd -64-B9-E8 (hex) Apple, Inc -64-BC-11 (hex) CombiQ AB -64-C6-AF (hex) AXERRA Networks Ltd -64-D4-DA (hex) Intel Corporate -64-DB-18 (hex) OpenPattern -64-ED-57 (hex) Motorola MDb/Broadband -64-F9-70 (hex) Kenade Electronics Technology Co.,LTD. -68-1F-D8 (hex) Advanced Telemetry -68-79-24 (hex) ELS-GmbH & Co. KG -68-7F-74 (hex) Cisco-Linksys, LLC -68-85-40 (hex) IGI Mobile, Inc. -68-A1-B7 (hex) Honghao Mingchuan Technology (Beijing) CO.,Ltd. -68-AA-D2 (hex) DATECS LTD., -68-CC-9C (hex) Mine Site Technologies -68-EF-BD (hex) Cisco Systems -6C-0E-0D (hex) Sony Ericsson Mobile Communications AB -6C-0F-6A (hex) JDC Tech Co., Ltd. -6C-18-11 (hex) Decatur Electronics -6C-32-DE (hex) Indieon Technologies Pvt. Ltd. -6C-5E-7A (hex) Ubiquitous Internet Telecom Co., Ltd -6C-6F-18 (hex) Stereotaxis, Inc. -6C-8C-DB (hex) Otus Technologies Ltd -6C-AC-60 (hex) Venetex Corp -6C-BE-E9 (hex) Alcatel-Lucent-IPD -6C-D6-8A (hex) LG Electronics Inc -6C-F0-49 (hex) GIGA-BYTE TECHNOLOGY CO.,LTD. -6C-FD-B9 (hex) Proware Technologies Co Ltd. -6C-FF-BE (hex) MPB Communications Inc. -70-1A-04 (hex) Liteon Tech Corp. -70-1A-ED (hex) ADVAS CO., LTD. -70-2B-1D (hex) E-Domus International Limited -70-2F-97 (hex) Aava Mobile Oy -70-3C-39 (hex) SEAWING Kft -70-58-12 (hex) Panasonic AVC Networks Company -70-5A-B6 (hex) COMPAL INFORMATION (KUNSHAN) CO., LTD. -70-72-CF (hex) EdgeCore Networks -70-82-8E (hex) OleumTech Corporation -70-D5-E7 (hex) Wellcore Corporation -70-D8-80 (hex) Upos System sp. z o.o. -70-F1-A1 (hex) Liteon Technology Corporation -74-32-56 (hex) NT-ware Systemprg GmbH -74-6B-82 (hex) MOVEK -74-7E-1A (hex) Red Embedded Design Limited -74-D8-50 (hex) Evrisko Systems -74-E5-37 (hex) RADSPIN -74-F0-7D (hex) BnCOM Co.,Ltd -74-F7-26 (hex) Neuron Robotics -78-12-B8 (hex) ORANTEK LIMITED -78-19-2E (hex) NASCENT Technology -78-1D-BA (hex) HUAWEI TECHNOLOGIES CO.,LTD -78-25-AD (hex) SAMSUNG ELECTRONICS CO., LTD. -78-30-E1 (hex) UltraClenz, LLC -78-44-76 (hex) Zioncom technology co.,ltd -78-5C-72 (hex) Hioso Technology Co., Ltd. -78-7F-62 (hex) GiK mbH -78-92-9C (hex) Intel Corporate -78-99-8F (hex) MEDILINE ITALIA SRL -78-B8-1A (hex) INTER SALES A/S -78-C4-0E (hex) H&D Wireless -78-DD-08 (hex) Hon Hai Precision Ind. Co.,Ltd. -78-E7-D1 (hex) Hewlett Packard -7C-08-D9 (hex) Shanghai Engineering Research Center for Broadband Technologies and Applications -7C-14-76 (hex) AE Partners S.a.s -7C-1E-B3 (hex) 2N TELEKOMUNIKACE a.s. -7C-20-64 (hex) Alcatel Lucent IPD -7C-2C-F3 (hex) Secure Electrans Ltd -7C-2F-80 (hex) Gigaset Communications GmbH -7C-3B-D5 (hex) Imago Group -7C-6C-8F (hex) AMS NEVE LTD -7C-6D-62 (hex) Apple, Inc -7C-6F-06 (hex) Caterpillar Trimble Control Technologies -7C-76-73 (hex) ENMAS GmbH -7C-7B-E4 (hex) Z'SEDAI KENKYUSHO CORPORATION -7C-CB-0D (hex) Aaxeon Technologies Inc. -7C-CF-CF (hex) Shanghai SEARI Intelligent System Co., Ltd -80-00-10 (hex) ATT BELL LABORATORIES -80-17-7D (hex) Nortel Networks -80-38-FD (hex) LeapFrog Enterprises, Inc. -80-3B-9A (hex) ghe-ces electronic ag -80-71-1F (hex) Juniper Networks -80-81-A5 (hex) TONGQING COMMUNICATION EQUIPMENT (SHENZHEN) Co.,Ltd -80-91-2A (hex) Lih Rong electronic Enterprise Co., Ltd. -80-B2-89 (hex) Forworld Electronics Ltd. -80-BA-AC (hex) TeleAdapt Ltd -80-C8-62 (hex) Openpeak, Inc -80-F5-93 (hex) IRCO Sistemas de Telecomunicaci�n S.A. -84-21-41 (hex) Shenzhen Ginwave Technologies Ltd. -84-48-23 (hex) WOXTER TECHNOLOGY Co. Ltd -84-90-00 (hex) Arnold & Richter Cine Technik -88-43-E1 (hex) Cisco Systems -88-4B-39 (hex) Siemens AG, Healthcare Sector -88-91-DD (hex) Racktivity -88-94-F9 (hex) Gemicom Technology, Inc. -88-98-21 (hex) TERAON -88-A5-BD (hex) QPCOM INC. -88-B6-27 (hex) Gembird Europe BV -88-BA-7F (hex) Qfiednet Co., Ltd. -88-ED-1C (hex) Cudo Communication Co., Ltd. -88-FD-15 (hex) LINEEYE CO., LTD -8C-56-C5 (hex) Nintendo Co., Ltd. -8C-59-8B (hex) C Technologies AB -8C-64-0B (hex) BS Storitve d.o.o. -8C-73-6E (hex) Fujitsu Limited -8C-92-36 (hex) Aus.Linx Technology Co., Ltd. -8C-A9-82 (hex) Intel Corporate -90-18-AE (hex) Shanghai Meridian Technologies, Co. Ltd. -90-3D-6B (hex) Zicon Technology Corp. -90-47-16 (hex) RORZE CORPORATION -90-4C-E5 (hex) Hon Hai Precision Ind. Co.,Ltd. -90-6D-C8 (hex) DLG Automao Industrial Ltda -90-7F-61 (hex) Chicony Electronics Co., Ltd. -90-84-0D (hex) Apple, Inc -90-A2-DA (hex) GHEO SA -90-A7-C1 (hex) Pakedge Device and Software Inc. -90-E6-BA (hex) ASUSTek COMPUTER INC. -90-FB-A6 (hex) Hon Hai Precision Ind.Co.Ltd -94-0C-6D (hex) TP-LINK Technologies Co.,Ltd. -94-23-6E (hex) Shenzhen Junlan Electronic Ltd -94-2E-63 (hex) Finscur -94-44-52 (hex) Belkin International, Inc. -94-59-2D (hex) EKE Building Technology Systems Ltd -94-BA-31 (hex) Visiontec da Amaznia Ltda. -94-C4-E9 (hex) PowerLayer Microsystems HongKong Limited -94-F6-92 (hex) Geminico co.,Ltd. -98-6D-C8 (hex) TOSHIBA MITSUBISHI-ELECTRIC INDUSTRIAL SYSTEMS CORPORATION -98-89-ED (hex) Anadem Information Inc. -98-8B-5D (hex) SAGEM COMMUNICATION -98-BC-99 (hex) Edeltech Co.,Ltd. -98-D8-8C (hex) Nortel Networks -9C-18-74 (hex) Nokia Danmark A/S -9C-4E-8E (hex) ALT Systems Ltd -9C-55-B4 (hex) I.S.E. S.r.l. -9C-5B-96 (hex) NMR Corporation -9C-5E-73 (hex) Calibre UK Ltd -9C-AF-CA (hex) Cisco Systems -9C-B2-06 (hex) PROCENTEC -9C-C0-77 (hex) PrintCounts, LLC -9C-CD-82 (hex) CHENG UEI PRECISION INDUSTRY CO.,LTD -9C-EB-E8 (hex) BizLink (Kunshan) Co.,Ltd -A0-07-98 (hex) Samsung Electronics -A0-18-59 (hex) Shenzhen Yidashi Electronics Co Ltd -A0-23-1B (hex) TeleComp R&D Corp. -A0-2E-F3 (hex) United Integrated Services Co., Led. -A0-3A-75 (hex) PSS Belgium N.V. -A0-40-25 (hex) Actioncable, Inc. -A0-59-3A (hex) V.D.S. Video Display Systems srl -A0-5D-C1 (hex) TMCT Co., LTD. -A0-5D-E7 (hex) DIRECTV, Inc. -A0-69-86 (hex) Wellav Technologies Ltd -A0-6A-00 (hex) Verilink Corporation -A0-73-32 (hex) Cashmaster International Limited -A0-98-05 (hex) OpenVox Communication Co Ltd -A0-98-ED (hex) Shandong Intelligent Optical Communication Development Co., Ltd. -A0-9A-5A (hex) Time Domain -A0-B9-ED (hex) Skytap -A0-BF-A5 (hex) CORESYS -A4-38-FC (hex) Plastic Logic -A4-79-E4 (hex) KLINFO Corp -A4-AD-00 (hex) Ragsdale Technology -A4-AD-B8 (hex) Vitec Group, Camera Dynamics Ltd -A4-B1-21 (hex) Arantia 2010 S.L. -A4-B1-EE (hex) H. ZANDER GmbH & Co. KG -A4-BA-DB (hex) Dell Inc. -A4-C2-AB (hex) Hangzhou LEAD-IT Information & Technology Co.,Ltd -A4-DA-3F (hex) Bionics Corp. -A4-DE-50 (hex) Total Walther GmbH -A4-E7-E4 (hex) Connex GmbH -A4-ED-4E (hex) Motorola Mobile Devices -A8-63-DF (hex) DISPL�AIRE CORPORATION -A8-70-A5 (hex) UniComm Inc. -A8-7B-39 (hex) Nokia Corporation -A8-7E-33 (hex) Nokia Danmark A/S -A8-93-E6 (hex) JIANGXI JINGGANGSHAN CKING COMMUNICATION TECHNOLOGY CO.,LTD -A8-99-5C (hex) aizo ag -A8-C2-22 (hex) TM-Research Inc. -A8-CB-95 (hex) EAST BEST CO., LTD. -A8-CE-90 (hex) CVC -A8-E3-EE (hex) Sony Computer Entertainment Inc. -A8-F2-74 (hex) Samsung Electronics -A8-F4-70 (hex) Fujian Newland Communication Science Technologies Co.,Ltd. -A8-F9-4B (hex) Eltex Enterprise Ltd. -AA-00-00 (hex) DIGITAL EQUIPMENT CORPORATION -AA-00-01 (hex) DIGITAL EQUIPMENT CORPORATION -AA-00-02 (hex) DIGITAL EQUIPMENT CORPORATION -AA-00-03 (hex) DIGITAL EQUIPMENT CORPORATION -AA-00-04 (hex) DIGITAL EQUIPMENT CORPORATION -AC-44-F2 (hex) Revolabs Inc -AC-58-3B (hex) Human Assembler, Inc. -AC-67-06 (hex) Ruckus Wireless -AC-83-17 (hex) Shenzhen Furtunetel Communication Co., Ltd -AC-86-7E (hex) Create New Technology (HK) Limited Company -AC-BE-B6 (hex) Visualedge Technology Co., Ltd. -AC-CE-8F (hex) HWA YAO TECHNOLOGIES CO., LTD -AC-D1-80 (hex) Crexendo Business Solutions, Inc. -AC-DE-48 (hex) PRIVATE -AC-E3-48 (hex) MadgeTech, Inc -AC-E9-AA (hex) Hay Systems Ltd -AC-EA-6A (hex) GENIX INFOCOMM CO., LTD. -B0-5B-1F (hex) THERMO FISHER SCIENTIFIC S.P.A. -B0-90-74 (hex) Fulan Electronics Limited -B0-91-34 (hex) Taleo -B0-97-3A (hex) E-Fuel Corporation -B0-AA-36 (hex) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD. -B0-C6-9A (hex) Juniper Networks -B0-C8-AD (hex) People Power Company -B0-E7-54 (hex) 2Wire -B0-E9-7E (hex) Advanced Micro Peripherals -B4-08-32 (hex) TC Communications -B4-2C-BE (hex) Direct Payment Solutions Limited -B4-41-7A (hex) ShenZhen Gongjin Electronics Co.,Ltd -B4-58-61 (hex) CRemote, LLC -B4-82-FE (hex) Askey Computer Corp -B4-B5-AF (hex) Minsung Electronics -B4-ED-54 (hex) Wohler Technologies -B8-64-91 (hex) CK Telecom Ltd -B8-65-3B (hex) Bolymin, Inc. -B8-94-D2 (hex) Retail Innovation HTT AB -B8-A3-E0 (hex) BenRui Technology Co.,Ltd -B8-AC-6F (hex) Dell Inc -B8-B1-C7 (hex) BT&COM CO.,LTD -B8-F7-32 (hex) Aryaka Networks Inc -BC-05-43 (hex) AVM GmbH -BC-4E-3C (hex) CORE STAFF CO., LTD. -BC-9D-A5 (hex) DASCOM Europe GmbH -BC-B1-81 (hex) SHARP CORPORATION -BC-D5-B6 (hex) d2d technologies -C0-1E-9B (hex) Pixavi AS -C0-22-50 (hex) PRIVATE -C0-38-F9 (hex) Nokia Danmark A/S -C0-3F-0E (hex) NETGEAR -C0-6C-0F (hex) Dobbs Stanford -C0-91-34 (hex) ProCurve Networking by HP -C0-9C-92 (hex) COBY -C0-BA-E6 (hex) Application Solutions (Safety and Security) Ltd -C0-E4-22 (hex) Texas Instruments -C4-17-FE (hex) Hon Hai Precision Ind. Co.,Ltd. -C4-19-8B (hex) Dominion Voting Systems Corporation -C4-1E-CE (hex) HMI Sources Ltd. -C4-59-76 (hex) Fugoo -C4-7D-4F (hex) Cisco Systems -C4-AA-A1 (hex) SUMMIT DEVELOPMENT, spol.s r.o. -C4-E1-7C (hex) U2S co. -C4-FC-E4 (hex) DishTV NZ Ltd -C8-0A-A9 (hex) Quanta Computer Inc. -C8-2E-94 (hex) Halfa Enterprise Co., Ltd. -C8-3A-35 (hex) Tenda Technology Co., Ltd. -C8-6C-1E (hex) Display Systems Ltd -C8-6C-B6 (hex) Optcom Co., Ltd. -C8-72-48 (hex) Aplicom Oy -C8-7E-75 (hex) Samsung Electronics Co.,Ltd -C8-87-3B (hex) Net Optics -C8-97-9F (hex) Nokia Corporation -C8-AA-CC (hex) PRIVATE -C8-C1-3C (hex) RuggedTek Hangzhou Co., Ltd -C8-D1-D1 (hex) AGAiT Technology Corporation -C8-D2-C1 (hex) Jetlun (Shenzhen) Corporation -CC-00-80 (hex) TRUST SYSTEM Co., -CC-22-18 (hex) InnoDigital Co., Ltd. -CC-50-76 (hex) Ocom Communications, Inc. -CC-54-59 (hex) OnTime Networks AS -CC-69-B0 (hex) Global Traffic Technologies, LLC -CC-B8-88 (hex) AnB Securite s.a. -CC-CC-4E (hex) Sun Fountainhead USA. Corp -CC-EA-1C (hex) DCONWORKS Co., Ltd -D0-37-61 (hex) Texas Instruments -D0-58-75 (hex) Active Control Technology Inc. -D0-B3-3F (hex) SHENZHEN TINNO MOBILE TECHNOLOGY CO.,LTD. -D0-D2-86 (hex) Beckman Coulter Biomedical K.K. -D0-E4-0B (hex) Wearable Inc. -D0-F0-DB (hex) Ericsson -D4-11-D6 (hex) ShotSpotter, Inc. -D4-1F-0C (hex) TVI Vision Oy -D4-4C-A7 (hex) Informtekhnika & Communication, LLC -D4-6C-BF (hex) Goodrich ISR -D4-82-3E (hex) Argosy Technologies, Ltd. -D4-9A-20 (hex) Apple, Inc -D4-9C-28 (hex) JayBird Gear LLC -D4-AA-FF (hex) MICRO WORLD -D4-C7-66 (hex) Acentic GmbH -D4-F1-43 (hex) IPROAD.,Inc -D8-1B-FE (hex) TWINLINX CORPORATION -D8-28-C9 (hex) General Electric Consumer and Industrial -D8-42-AC (hex) FreeComm Data Communication Co.,Ltd. -D8-54-3A (hex) Texas Instruments -D8-AE-90 (hex) Itibia Technologies -D8-C3-FB (hex) DETRACOM -D8-D3-85 (hex) Hewlett Packard -D8-D6-7E (hex) GSK CNC EQUIPMENT CO.,LTD -D8-E7-2B (hex) OnPATH Technologies -DC-02-65 (hex) Meditech Kft -DC-1D-9F (hex) U & B tech -DC-2C-26 (hex) Iton Technology Limited -DC-33-50 (hex) TechSAT GmbH -DC-49-C9 (hex) CASCO SIGNAL LTD -DC-A9-71 (hex) Intel Corporate -DC-E2-AC (hex) Lumens Digital Optics Inc. -DC-E7-1C (hex) AUG Elektronik GmbH -E0-26-30 (hex) Intrigue Technologies, Inc. -E0-26-36 (hex) Nortel Networks -E0-64-BB (hex) DigiView S.r.l. -E0-8F-EC (hex) REPOTEC CO., LTD. -E0-91-53 (hex) XAVi Technologies Corp. -E0-AB-FE (hex) Orb Networks, Inc. -E0-BC-43 (hex) C2 Microsystems, Inc. -E0-CA-4D (hex) Shenzhen Unistar Communication Co.,LTD -E0-CB-4E (hex) ASUSTek COMPUTER INC. -E0-E7-51 (hex) Nintendo Co., Ltd. -E4-1F-13 (hex) IBM -E4-35-93 (hex) Hangzhou GoTo technology Co.Ltd -E4-75-1E (hex) Getinge Sterilization AB -E4-97-F0 (hex) Shanghai VLC Technologies Ltd. Co. -E4-FF-DD (hex) ELECTRON INDIA -E8-05-6D (hex) Nortel Networks -E8-0B-13 (hex) Akib Systems Taiwan, INC -E8-3A-97 (hex) OCZ Technology Group -E8-4E-CE (hex) Nintendo Co., Ltd. -E8-5E-53 (hex) Infratec Datentechnik GmbH -E8-9D-87 (hex) Toshiba -E8-A4-C1 (hex) Deep Sea Electronics PLC -E8-DA-AA (hex) VideoHome Technology Corp. -E8-DF-F2 (hex) PRF Co., Ltd. -E8-E1-E2 (hex) Energotest -E8-E7-76 (hex) Shenzhen Kootion Technology Co., Ltd -E8-F2-01 (hex) NEC AccessTechnica, Ltd. -EC-30-91 (hex) Cisco Systems -EC-43-E6 (hex) AWCER Ltd. -EC-44-76 (hex) Cisco Systems -EC-6C-9F (hex) Chengdu Volans Technology CO.,LTD -EC-8E-AD (hex) DLX -EC-9B-5B (hex) Nokia Corporation -EC-C8-82 (hex) Cisco Systems -EC-CD-6D (hex) Allied Telesis, Inc. -EC-D0-0E (hex) MiraeRecognition Co., Ltd. -EC-DE-3D (hex) Lamprey Networks, Inc. -EC-E9-F8 (hex) Guang Zhou TRI-SUN Electronics Technology Co., Ltd -F0-24-08 (hex) Talaris (Sweden) AB -F0-26-4C (hex) Dr. Sigrist AG -F0-2F-D8 (hex) Bi2-Vision -F0-43-35 (hex) DVN(Shanghai)Ltd. -F0-4B-F2 (hex) JTECH Communications, Inc. -F0-62-81 (hex) ProCurve Networking by HP -F0-68-53 (hex) Integrated Corporation -F0-77-D0 (hex) Xcellen -F0-7B-CB (hex) Hon Hai Precision Ind. Co.,Ltd. -F0-B6-EB (hex) Poslab Technology Co., Ltd. -F0-BC-C8 (hex) MaxID (Pty) Ltd -F0-C2-4C (hex) Zhejiang FeiYue Digital Technology Co., Ltd -F0-DE-71 (hex) Shanghai EDO Technologies Co.,Ltd. -F0-EC-39 (hex) Essec -F4-0B-93 (hex) Research In Motion -F4-45-ED (hex) Portable Innovation Technology Ltd. -F4-5F-F7 (hex) DQ Technology Inc. -F4-63-49 (hex) Diffon Corporation -F4-76-26 (hex) Viltechmeda UAB -F4-AC-C1 (hex) Cisco Systems -F4-CE-46 (hex) Hewlett Packard -F4-FC-32 (hex) Texas Instruments -F8-1E-DF (hex) Apple, Inc -F8-47-2D (hex) X2gen Digital Corp. Ltd -F8-52-DF (hex) VNL Europe AB -F8-71-FE (hex) The Goldman Sachs Group, Inc. -F8-81-1A (hex) S2IH -F8-8D-EF (hex) Tenebraex -F8-91-2A (hex) GLP German Light Products GmbH -F8-DC-7A (hex) Variscite LTD -F8-E9-68 (hex) Egker Kft. -FC-08-77 (hex) Prentke Romich Company -FC-44-63 (hex) Universal Audio -FC-61-98 (hex) NEC Personal Products, Ltd -FC-68-3E (hex) Directed Perception, Inc -FC-CC-E4 (hex) Ascon Ltd. -FC-CF-62 (hex) BLADE Network Technology -FC-E1-92 (hex) Sichuan Jinwangtong Electronic Science&Technology Co,.Ltd -FC-FA-F7 (hex) Shanghai Baud Data Communication Co.,Ltd. -FC-FB-FB (hex) Cisco Systems diff --git a/database/.gitignore b/database/.gitignore new file mode 100644 index 000000000..9b1dffd90 --- /dev/null +++ b/database/.gitignore @@ -0,0 +1 @@ +*.sqlite diff --git a/database/IXP-Manager.ormdesigner2 b/database/IXP-Manager.ormdesigner2 new file mode 100755 index 000000000..1b7fe21af --- /dev/null +++ b/database/IXP-Manager.ormdesigner2 @@ -0,0 +1,1726 @@ + + + + + + + + + + + + + + + + + Repositories\IrrdbAsn + irrdb_asn + irrdb_asn + + + + + + CASCADE + + + + + + + apiKey + + + + + + + allowedIPs + + + + + + lastseenAt + + + + + lastseenFrom + + + + + Repositories\ApiKey + api_keys + api_keys + + + + + + CASCADE + + + + + + + + + + + + + + + + irrdb_prefix + irrdb_prefix + Repositories\IrrdbPrefix + + + + + + CASCADE + + + + + + + + + + + + + + + + user_logins + user_logins + Repositories\UserLoginHistory + + + + + + + + + + + + + + + + + + + netinfo + netinfo + Repositories\NetInfo + + + + + + + + + + registeredName + + + + + companyNumber + + + + + + + + + townCity + + + + + + company_registration_detail + company_registration_detail + Repositories\CompanyRegisteredDetail + + + + + + + + + isPrimary + + + + + + + + + + + + infrastructure + infrastructure + Repositories\Infrastructure + + + + + + + + + + + + + + + + ixp + ixp + Repositories\IXP + + + + + + + + Repositories\CustomerToIXP + customer_to_ixp + customer_to_ixp + + + + + + + + + + + + + + + billingContactName + + + + + billingAddress1 + + + + + billingAddress2 + + + + + billingAddress3 + + + + + billingTownCity + + + + + billingPostcode + + + + + billingCountry + + + + + billingEmail + + + + + billingTelephone + + + + + vatNumber + + + + + vatRate + + + + + purchaseOrderRequired + + + + + invoiceMethod + + + + + invoiceEmail + + + + + billingFrequency + + + + Repositories\CompanyBillingDetail + company_billing_detail + company_billing_detail + + + + + + + + + + + + Repositories\ContactGroup + contact_group + contact_group + + + + + + + + Repositories\ContactToGroup + contact_to_group + contact_to_group + + + + + + + + + + + + + + + + + + + + cust_notes + cust_notes + Repositories\CustomerNote + + + + + + CASCADE + + + + + + + + + + + + + + + + Repositories\BGPSessionData + bgpsessiondata + bgpsessiondata + + + + + + + + + + + + + + + + + + + + Repositories\Location + location + location + + + + + + + + + + + + + Repositories\Cabinet + cabinet + cabinet + + + + + + + + + + + + + + + + + + + + + + + + + osDate + + + + + osVersion + + + + + serialNumber + + + + + mauSupported + + + + + lastPolled + + + + + + + + switch + switch + Repositories\Switcher + + + + + + + + + + + + + + + + + + + Repositories\Vendor + vendor + vendor + + + + + + + + + + + ifIndex + + + + + ifName + + + + + ifAlias + + + + + ifHighSpeed + + + + + ifMtu + + + + + ifPhysAddress + + + + + ifAdminStatus + + + + + ifOperStatus + + + + + ifLastChange + + + + + lastSnmpPoll + + + + + lagIfIndex + + + + + mauType + + + + + mauState + + + + + mauAvailability + + + + + mauJacktype + + + + + mauAutoNegSupported + + + + + mauAutoNegAdminState + + + + Repositories\SwitchPort + switchport + switchport + + + + + + + + + + + + + + + + + Repositories\PhysicalInterface + physicalinterface + physicalinterface + + + + + + + + + CASCADE + + + + + + RESTRICT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Repositories\VlanInterface + vlaninterface + vlaninterface + + + + + + CASCADE + + + + + + RESTRICT + + + + + + RESTRICT + + + + + + RESTRICT + + + + + + + + + + + + + + + + + + + Repositories\Vlan + vlan + vlan + + + + + + + + + + + + + + + + + Repositories\VirtualInterface + virtualinterface + virtualinterface + + + + + + CASCADE + + + + + + + + + + + + Repositories\IPv4Address + ipv4address + ipv4address + + + + + + + + + + + + + + + Repositories\IPv6Address + ipv6address + ipv6address + + + + + + + + + + + + + abbreviatedName + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MD5Support + + + + + isReseller + + + + + + + + Repositories\Customer + cust + cust + + + + + + RESTRICT + + + + + + + + + + + + + + + + + + + + + + + + + + + + Repositories\Contact + contact + contact + + + + + + CASCADE + + + + + + + + + + + + + + + + + + + + + Repositories\ConsoleServerConnection + consoleserverconnection + consoleserverconnection + + + + + + CASCADE + + + + + + + + + + + + + Repositories\CustomerEquipment + custkit + custkit + + + + + + CASCADE + + + + + + + + + + + + + Repositories\IRRDBConfig + irrdbconfig + irrdbconfig + + + + + + + + + + Repositories\MACAddress + macaddress + macaddress + + + + + + CASCADE + + + + + + + + + + + + + Repositories\NetworkInfo + networkinfo + networkinfo + + + + + + + + + + + + + + + + + + Repositories\PeeringManager + peering_manager + peering_manager + + + + + + CASCADE + + + + + + CASCADE + + + + + + + + + + + + + Repositories\PeeringMatrix + peering_matrix + peering_matrix + + + + + + CASCADE + + + + + + CASCADE + + + + + + + + + + + + Repositories\RSPrefix + rs_prefixes + rs_prefixes + + + + + + CASCADE + + + + + + + + + + + + authorisedMobile + + + + + + + + + + + + + + Repositories\User + user + user + + + + + + SET NULL + + + + + + + + + + + + + + + + + + user_pref + user_pref + Repositories\UserPreference + + + + + + CASCADE + + + + + + + + + + Repositories\Traffic95th + traffic_95th + traffic_95th + + + + + + CASCADE + + + + + + + + + Repositories\Traffic95thMonthly + traffic_95th_monthly + traffic_95th_monthly + + + + + + CASCADE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + traffic_daily + traffic_daily + Repositories\TrafficDaily + + + + + + CASCADE + + + + + + + + + + + oui + oui + Repositories\OUI + + + + + + + + + + + + + + Repositories\Logo + logos + logos + + + + + + RESTRICT + + + + + + + + + Repositories\SflowReceiver + sflow_receiver + sflow_receiver + + + + + + CASCADE + + + + + + + + + + + + + + + + + + patch_panel + Repositories\PatchPanel + patch_panel + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + patch_panel_port + patch_panel_port + Repositories\PatchPanelPort + + + + + + + + + RESTRICT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + patch_panel_port_history + patch_panel_port_history + Repositories\PatchPanelPortHistory + + + + + + + + + + + + + + + + + + + + + Repositories\Layer2Address + l2address + l2address + + + + + + CASCADE + + + + + + + + + + + + + + patch_panel_port_file + patch_panel_port_file + + + + + + + + + + + + + + + + + patch_panel_port_history_file + patch_panel_port_history_file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Repositories\Router + routers + routers + + + + + + + + + + coreinterfaces + coreinterfaces + Repository\CoreInterface + + + + + + + + + + + + + + + + corelinks + corelinks + Repositories\CoreLink + + + + + + + + + + + + + + + + + + + + + + + + + corebundles + corebundles + Repositories\CoreBundle + + + + + + + + + + + + serialNumber + + + + + + console_server + console_server + Repositories\ConsoleServer + + + + + + + + + + + + + + + + + + Repositories\CustomerTag + cust_tag + cust_tag + + + + + + + cust_to_cust_tag + cust_to_cust_tag + + + + + + + + + + + + + + + + + + + + + + + + + Repositories\BgpSession + bgp_sessions + bgp_sessions + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + route_server_filters + route_server_filters + route_server_filters + Repositories\RouteServerFilter + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/database/migrations/2014_10_12_100000_create_password_resets_table.php b/database/migrations/2014_10_12_100000_create_password_resets_table.php new file mode 100644 index 000000000..75f0458f3 --- /dev/null +++ b/database/migrations/2014_10_12_100000_create_password_resets_table.php @@ -0,0 +1,48 @@ +getPdo()->exec( file_get_contents( database_path('schema/2021-as-at-end-v5.sql') ) ); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + echo "*** There is no downgrade possible for the base schema. Drop your database manually...\n"; + } +} diff --git a/database/migrations/2020_07_21_094354_create_route_server_filters.php b/database/migrations/2020_07_21_094354_create_route_server_filters.php new file mode 100644 index 000000000..958f18fa4 --- /dev/null +++ b/database/migrations/2020_07_21_094354_create_route_server_filters.php @@ -0,0 +1,49 @@ +bigIncrements('id'); + $table->integer( 'customer_id' )->nullable(); + $table->integer( 'peer_id' )->nullable(); + $table->integer( 'vlan_id' )->nullable(); + $table->string( 'received_prefix',43 )->nullable( true ); + $table->string( 'advertised_prefix',43 )->nullable( true ); + $table->smallInteger('protocol' )->nullable( true ); + $table->string( 'action_advertise',255 )->nullable( true ); + $table->string( 'action_receive',255 )->nullable( true ); + $table->boolean( 'enabled' )->default( true ); + $table->integer('order_by' )->nullable( false ); + $table->string( 'live',255 )->nullable( false ); + + $table->foreign('customer_id' )->references('id' )->on('cust' ); + $table->foreign('peer_id' )->references('id' )->on('cust' ); + $table->foreign('vlan_id' )->references('id' )->on('vlan' ); + + $table->unique( [ 'customer_id', 'order_by' ] ); + + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('route_server_filters'); + } +} \ No newline at end of file diff --git a/database/migrations/2020_09_03_153723_add_timestamps.php b/database/migrations/2020_09_03_153723_add_timestamps.php new file mode 100644 index 000000000..16ea3b5a6 --- /dev/null +++ b/database/migrations/2020_09_03_153723_add_timestamps.php @@ -0,0 +1,488 @@ +renameColumn( 'created', 'created_at' ); + $table->timestamp( 'updated_at' )->nullable(); + }); + + Schema::table('bgp_sessions', function (Blueprint $table) { + $table->timestamps(); + }); + + Schema::table('cabinet', function (Blueprint $table) { + $table->timestamps(); + }); + + Schema::table('company_billing_detail', function (Blueprint $table) { + $table->timestamps(); + }); + + Schema::table('company_registration_detail', function (Blueprint $table) { + $table->timestamps(); + }); + + Schema::table('console_server', function (Blueprint $table) { + $table->timestamps(); + }); + + Schema::table('consoleserverconnection', function (Blueprint $table) { + $table->timestamps(); + }); + + Schema::table('contact', function (Blueprint $table) { + $table->renameColumn( 'created', 'created_at' ); + $table->renameColumn( 'lastupdated', 'updated_at' ); + }); + + Schema::table('contact_group', function (Blueprint $table) { + $table->renameColumn( 'created', 'created_at' ); + $table->timestamp( 'updated_at' )->nullable(); + }); + + Schema::table('contact_to_group', function (Blueprint $table) { + $table->timestamps(); + }); + + Schema::table('corebundles', function (Blueprint $table) { + $table->timestamps(); + }); + + Schema::table('coreinterfaces', function (Blueprint $table) { + $table->timestamps(); + }); + + Schema::table('corelinks', function (Blueprint $table) { + $table->timestamps(); + }); + + Schema::table('cust', function (Blueprint $table) { + $table->renameColumn( 'created', 'created_at' ); + $table->renameColumn( 'lastupdated', 'updated_at' ); + }); + + Schema::table('cust', function (Blueprint $table) { + $table->dateTime( 'created_at' )->nullable()->change(); + $table->dateTime( 'updated_at' )->nullable()->change(); + }); + + Schema::table('cust_notes', function (Blueprint $table) { + $table->renameColumn( 'created', 'created_at' ); + $table->renameColumn( 'updated', 'updated_at' ); + }); + + Schema::table('customer_to_users', function (Blueprint $table) { + $table->timestamp( 'updated_at' )->nullable(); + }); + + Schema::table('custkit', function (Blueprint $table) { + $table->timestamps(); + }); + + Schema::table('cust_tag', function (Blueprint $table) { + $table->renameColumn( 'created', 'created_at' ); + $table->renameColumn( 'updated', 'updated_at' ); + }); + + Schema::table('cust_to_cust_tag', function (Blueprint $table) { + $table->timestamps(); + }); + + Schema::table('infrastructure', function (Blueprint $table) { + $table->timestamps(); + }); + + Schema::table('ipv4address', function (Blueprint $table) { + $table->timestamps(); + }); + + Schema::table('ipv6address', function (Blueprint $table) { + $table->timestamps(); + }); + + Schema::table('irrdb_asn', function (Blueprint $table) { + $table->timestamps(); + }); + + Schema::table('irrdbconfig', function (Blueprint $table) { + $table->timestamps(); + }); + + Schema::table('irrdb_prefix', function (Blueprint $table) { + $table->timestamps(); + }); + + Schema::table('l2address', function (Blueprint $table) { + $table->renameColumn( 'created', 'created_at' ); + $table->timestamp( 'updated_at' )->nullable(); + }); + + Schema::table('location', function (Blueprint $table) { + $table->timestamps(); + }); + + Schema::table('logos', function (Blueprint $table) { + $table->renameColumn( 'uploaded_at', 'created_at' ); + $table->timestamp( 'updated_at' )->nullable(); + }); + + Schema::table('macaddress', function (Blueprint $table) { + $table->timestamps(); + }); + + Schema::table('networkinfo', function (Blueprint $table) { + $table->timestamps(); + }); + + Schema::table('oui', function (Blueprint $table) { + $table->timestamps(); + }); + + Schema::table('patch_panel', function (Blueprint $table) { + $table->timestamps(); + $table->date( 'installation_date' )->nullable()->change(); + }); + + Schema::table('patch_panel_port', function (Blueprint $table) { + $table->timestamps(); + }); + + Schema::table('patch_panel_port_file', function (Blueprint $table) { + $table->timestamps(); + }); + + Schema::table('patch_panel_port_history', function (Blueprint $table) { + $table->timestamps(); + }); + + Schema::table('patch_panel_port_history_file', function (Blueprint $table) { + $table->timestamps(); + }); + + Schema::table('peering_manager', function (Blueprint $table) { + $table->renameColumn( 'updated', 'updated_at' ); + $table->renameColumn( 'created', 'created_at' ); + }); + + Schema::table('physicalinterface', function (Blueprint $table) { + $table->timestamps(); + }); + + Schema::table('routers', function (Blueprint $table) { + $table->timestamps(); + }); + + Schema::table('sflow_receiver', function (Blueprint $table) { + $table->timestamps(); + }); + + Schema::table('switch', function (Blueprint $table) { + $table->timestamps(); + }); + + Schema::table('switchport', function (Blueprint $table) { + $table->timestamps(); + }); + + Schema::table('traffic_daily', function (Blueprint $table) { + $table->timestamps(); + }); + + Schema::table('traffic_daily_phys_ints', function (Blueprint $table) { + $table->timestamps(); + }); + + Schema::table('user', function (Blueprint $table) { + $table->renameColumn( 'lastupdated', 'updated_at' ); + $table->renameColumn( 'created', 'created_at' ); + }); + + Schema::table('user_logins', function (Blueprint $table) { + $table->timestamps(); + }); + + Schema::table('user_remember_tokens', function (Blueprint $table) { + $table->renameColumn( 'created', 'created_at' ); + $table->timestamp( 'updated_at' )->nullable(); + }); + + Schema::table('vendor', function (Blueprint $table) { + $table->timestamps(); + }); + + Schema::table('virtualinterface', function (Blueprint $table) { + $table->timestamps(); + }); + + Schema::table('vlaninterface', function (Blueprint $table) { + $table->timestamps(); + }); + + + Schema::table('vlan', function (Blueprint $table) { + $table->timestamps(); + }); + + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('api_keys', function (Blueprint $table) { + $table->renameColumn( 'created_at', 'created' ); + $table->dropColumn( 'updated_at' ); + }); + + Schema::table('bgp_sessions', function (Blueprint $table) { + $table->timestamps(); + }); + + Schema::table('cabinet', function (Blueprint $table) { + $table->dropTimestamps(); + }); + + Schema::table('company_billing_detail', function (Blueprint $table) { + $table->dropTimestamps(); + }); + + Schema::table('company_registration_detail', function (Blueprint $table) { + $table->dropTimestamps(); + }); + + Schema::table('console_server', function (Blueprint $table) { + $table->dropTimestamps(); + }); + + Schema::table('consoleserverconnection', function (Blueprint $table) { + $table->dropTimestamps(); + }); + + Schema::table('contact', function (Blueprint $table) { + $table->renameColumn( 'created_at', 'created' ); + $table->renameColumn( 'updated_at', 'lastupdated' ); + }); + + Schema::table('contact_group', function (Blueprint $table) { + $table->renameColumn( 'created_at', 'created' ); + $table->dropColumn( 'updated_at' ); + }); + + Schema::table('contact_to_group', function (Blueprint $table) { + $table->dropTimestamps(); + }); + + Schema::table('corebundles', function (Blueprint $table) { + $table->dropTimestamps(); + }); + + Schema::table('coreinterfaces', function (Blueprint $table) { + $table->dropTimestamps(); + }); + + Schema::table('corelinks', function (Blueprint $table) { + $table->dropTimestamps(); + }); + + Schema::table('cust', function (Blueprint $table) { + $table->renameColumn( 'created_at', 'created' ); + $table->renameColumn( 'updated_at', 'lastupdated' ); + }); + + Schema::table('cust', function (Blueprint $table) { + $table->date( 'created' )->nullable()->change(); + $table->date( 'lastupdated' )->nullable()->change(); + }); + + Schema::table('cust_notes', function (Blueprint $table) { + $table->renameColumn( 'created_at', 'created' ); + $table->renameColumn( 'updated_at', 'updated' ); + }); + + Schema::table('customer_to_users', function (Blueprint $table) { + $table->dropColumn( 'updated_at' ); + }); + + Schema::table('custkit', function (Blueprint $table) { + $table->dropTimestamps(); + }); + + Schema::table('cust_tag', function (Blueprint $table) { + $table->renameColumn( 'created_at', 'created' ); + $table->renameColumn( 'updated_at', 'updated' ); + }); + + Schema::table('cust_to_cust_tag', function (Blueprint $table) { + $table->dropTimestamps(); + }); + + Schema::table('infrastructure', function (Blueprint $table) { + $table->dropTimestamps(); + }); + + Schema::table('ipv4address', function (Blueprint $table) { + $table->dropTimestamps(); + }); + + Schema::table('ipv6address', function (Blueprint $table) { + $table->dropTimestamps(); + }); + + Schema::table('irrdb_asn', function (Blueprint $table) { + $table->dropTimestamps(); + }); + Schema::table('irrdbconfig', function (Blueprint $table) { + $table->dropTimestamps(); + }); + + Schema::table('irrdb_prefix', function (Blueprint $table) { + $table->dropTimestamps(); + }); + + Schema::table('l2address', function (Blueprint $table) { + $table->renameColumn( 'created_at', 'created' ); + $table->dropColumn( 'updated_at' ); + }); + + Schema::table('location', function (Blueprint $table) { + $table->dropTimestamps(); + }); + + Schema::table('logos', function (Blueprint $table) { + $table->renameColumn( 'created_at', 'uploaded_at' ); + $table->dropColumn( 'updated_at' ); + }); + + Schema::table('macaddress', function (Blueprint $table) { + $table->dropTimestamps(); + }); + + Schema::table('networkinfo', function (Blueprint $table) { + $table->dropTimestamps(); + }); + + Schema::table('oui', function (Blueprint $table) { + $table->dropTimestamps(); + }); + + Schema::table('patch_panel', function (Blueprint $table) { + $table->dropTimestamps(); + }); + + Schema::table('patch_panel_port', function (Blueprint $table) { + $table->dropTimestamps(); + }); + + Schema::table('patch_panel_port_file', function (Blueprint $table) { + $table->dropTimestamps(); + }); + + Schema::table('patch_panel_port_history', function (Blueprint $table) { + $table->dropTimestamps(); + }); + + Schema::table('patch_panel_port_history_file', function (Blueprint $table) { + $table->dropTimestamps(); + }); + + Schema::table('peering_manager', function (Blueprint $table) { + $table->renameColumn( 'updated_at', 'updated' ); + $table->renameColumn( 'created_at', 'created' ); + }); + + Schema::table('physicalinterface', function (Blueprint $table) { + $table->dropTimestamps(); + }); + + Schema::table('routers', function (Blueprint $table) { + $table->renameColumn( 'updated_at', 'last_updated' ); + $table->dropColumn( 'created_at' ); + }); + + Schema::table('sflow_receiver', function (Blueprint $table) { + $table->dropTimestamps(); + }); + + Schema::table('switch', function (Blueprint $table) { + $table->dropTimestamps(); + }); + + Schema::table('switchport', function (Blueprint $table) { + $table->dropTimestamps(); + }); + + Schema::table('traffic_daily', function (Blueprint $table) { + $table->dropTimestamps(); + }); + + Schema::table('traffic_daily_phys_ints', function (Blueprint $table) { + $table->dropTimestamps(); + }); + + Schema::table('user', function (Blueprint $table) { + $table->renameColumn( 'updated_at', 'lastupdated' ); + $table->renameColumn( 'created_at', 'created' ); + }); + + Schema::table('user_logins', function (Blueprint $table) { + $table->dropTimestamps(); + }); + + Schema::table('user_remember_tokens', function (Blueprint $table) { + $table->renameColumn( 'created_at', 'created' ); + $table->removeColumn( 'updated_at' ); + }); + + Schema::table('vendor', function (Blueprint $table) { + $table->dropTimestamps(); + }); + + Schema::table('virtualinterface', function (Blueprint $table) { + $table->dropTimestamps(); + }); + + Schema::table('vlaninterface', function (Blueprint $table) { + $table->dropTimestamps(); + }); + + Schema::table('vlan', function (Blueprint $table) { + $table->dropTimestamps(); + }); + } +} diff --git a/database/migrations/2020_09_18_095136_delete_ixp_table.php b/database/migrations/2020_09_18_095136_delete_ixp_table.php new file mode 100644 index 000000000..4a9c94363 --- /dev/null +++ b/database/migrations/2020_09_18_095136_delete_ixp_table.php @@ -0,0 +1,83 @@ +drop(); + }); + + $infrastructure_fks = array_column( + Schema::getConnection()->select('SELECT CONSTRAINT_NAME FROM information_schema.KEY_COLUMN_USAGE ' + . 'WHERE TABLE_SCHEMA = "' . ( config('database.connections.mysql.database') ?? '' ) . '" AND TABLE_NAME = "infrastructure" AND ' + . 'REFERENCED_TABLE_SCHEMA = "' . ( config('database.connections.mysql.database') ?? '' ) . '" AND REFERENCED_TABLE_NAME = "ixp"' + ), 'CONSTRAINT_NAME' + ); + + foreach( $infrastructure_fks as $fk ) { + Schema::table( 'infrastructure', function( Blueprint $table ) use ( $fk ) { + $table->dropForeign( $fk ); + $table->dropColumn( 'ixp_id' ); + } ); + } + + $traffic_daily_fks = array_column( + Schema::getConnection()->select('SELECT CONSTRAINT_NAME FROM information_schema.KEY_COLUMN_USAGE ' + . 'WHERE TABLE_SCHEMA = "' . ( config('database.connections.mysql.database') ?? '' ) . '" AND TABLE_NAME = "traffic_daily" AND ' + . 'REFERENCED_TABLE_SCHEMA = "' . ( config('database.connections.mysql.database') ?? '' ) . '" AND REFERENCED_TABLE_NAME = "ixp"' + ), 'CONSTRAINT_NAME' + ); + + foreach( $traffic_daily_fks as $fk ) { + Schema::table( 'traffic_daily', function( Blueprint $table ) use ( $fk ) { + $table->dropForeign( $fk ); + $table->dropColumn( 'ixp_id' ); + } ); + } + + Schema::table('ixp', function (Blueprint $table) { + $table->drop(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} diff --git a/database/migrations/2020_11_16_102415_database_fixes.php b/database/migrations/2020_11_16_102415_database_fixes.php new file mode 100644 index 000000000..c8d8c48f4 --- /dev/null +++ b/database/migrations/2020_11_16_102415_database_fixes.php @@ -0,0 +1,57 @@ +integer( 'chargeable' )->nullable(false)->default( PatchPanelPort::CHARGEABLE_NO )->change(); + }); + + // Update Patch panel ports that have chargeable set to 0 to 2 (CHARGEABLE_NO) + DB::table('patch_panel_port')->where('chargeable' , 0 ) + ->update( [ 'chargeable' => PatchPanelPort::CHARGEABLE_NO ] ); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} diff --git a/database/migrations/2021_03_12_150418_create_log_table.php b/database/migrations/2021_03_12_150418_create_log_table.php new file mode 100644 index 000000000..ec1cf18d3 --- /dev/null +++ b/database/migrations/2021_03_12_150418_create_log_table.php @@ -0,0 +1,63 @@ +id(); + $table->integer('user_id' )->nullable(); + $table->string('model',100 ); + $table->unsignedBigInteger('model_id')->nullable(true )->default(null); + $table->string('action',7 ); + $table->text('message' ); + $table->json('models' ); + $table->timestamps(); + + $table->foreign('user_id' )->references('id')->on('user' ); + + $table->index('action' ); + $table->index( ['model', 'model_id'] ); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('log'); + } +} diff --git a/database/migrations/2021_03_30_124916_create_atlas_probes.php b/database/migrations/2021_03_30_124916_create_atlas_probes.php new file mode 100644 index 000000000..33a6813c0 --- /dev/null +++ b/database/migrations/2021_03_30_124916_create_atlas_probes.php @@ -0,0 +1,44 @@ +bigIncrements('id')->unsigned( false ); + $table->integer('cust_id'); + $table->string('address_v4', 15)->nullable(); + $table->string('address_v6', 39)->nullable(); + $table->tinyInteger('v4_enabled' )->nullable(); + $table->tinyInteger('v6_enabled' )->nullable(); + $table->integer('asn' )->nullable(); + $table->integer('atlas_id'); + $table->tinyInteger('is_anchor' ); + $table->tinyInteger('is_public' ); + $table->dateTime('last_connected' )->nullable(); + $table->string('status', 255 )->nullable(); + $table->json('api_data' )->nullable(); + $table->timestamps(); + $table->foreign('cust_id')->references('id')->on('cust'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('atlas_probes'); + } +} diff --git a/database/migrations/2021_03_30_125238_create_atlas_runs.php b/database/migrations/2021_03_30_125238_create_atlas_runs.php new file mode 100644 index 000000000..7bcb3fae3 --- /dev/null +++ b/database/migrations/2021_03_30_125238_create_atlas_runs.php @@ -0,0 +1,38 @@ +increments('id')->unsigned( false ); + $table->integer('vlan_id' )->nullable(); + $table->integer('protocol' )->nullable(); + $table->dateTime('scheduled_at' )->nullable(); + $table->dateTime('started_at' )->nullable(); + $table->dateTime('completed_at' )->nullable(); + $table->timestamps(); + $table->foreign('vlan_id' )->references('id' )->on('vlan' ); + + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('atlas_runs'); + } +} diff --git a/database/migrations/2021_03_30_125422_create_atlas_measurements.php b/database/migrations/2021_03_30_125422_create_atlas_measurements.php new file mode 100644 index 000000000..0d775cd08 --- /dev/null +++ b/database/migrations/2021_03_30_125422_create_atlas_measurements.php @@ -0,0 +1,44 @@ +increments('id')->unsigned( false ); + $table->integer('run_id' ); + $table->integer('cust_source' )->nullable(); + $table->integer('cust_dest' )->nullable(); + $table->integer('atlas_id' )->nullable(); + $table->dateTime('atlas_create' )->nullable(); + $table->dateTime('atlas_start' )->nullable(); + $table->dateTime('atlas_stop' )->nullable(); + $table->json('atlas_data' )->nullable(); + $table->json('atlas_request' )->nullable(); + $table->string('atlas_state', 255)->nullable(); + $table->timestamps(); + $table->foreign('cust_source' )->references('id' )->on('cust' ); + $table->foreign('cust_dest' )->references('id' )->on('cust' ); + $table->foreign('run_id' )->references('id' )->on('atlas_runs' ); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('atlas_measurements'); + } +} diff --git a/database/migrations/2021_03_30_125723_create_atlas_results.php b/database/migrations/2021_03_30_125723_create_atlas_results.php new file mode 100644 index 000000000..0c44d0960 --- /dev/null +++ b/database/migrations/2021_03_30_125723_create_atlas_results.php @@ -0,0 +1,35 @@ +increments('id')->unsigned( false ); + $table->integer('measurement_id' )->nullable()->unique(); + $table->string('routing', 255)->nullable(); + $table->longText('path' )->nullable(); + $table->timestamps(); + $table->foreign('measurement_id' )->references('id' )->on('atlas_measurements' )->onDelete( 'cascade' ); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('atlas_results'); + } +} \ No newline at end of file diff --git a/database/migrations/2021_04_14_101948_update_timestamps.php b/database/migrations/2021_04_14_101948_update_timestamps.php new file mode 100644 index 000000000..ef61e4c5f --- /dev/null +++ b/database/migrations/2021_04_14_101948_update_timestamps.php @@ -0,0 +1,173 @@ +timestamp( 'created_at' )->change(); + }); + + Schema::table('contact_group', function (Blueprint $table) { + DB::statement("ALTER TABLE contact_group CHANGE created_at created_at TIMESTAMP NULL"); + }); + + Schema::table('cust', function (Blueprint $table) { + DB::statement("ALTER TABLE cust MODIFY COLUMN created_at DATETIME AFTER peeringdb_oauth"); + DB::statement("ALTER TABLE cust MODIFY COLUMN updated_at DATETIME AFTER created_at"); + $table->timestamp( 'created_at' )->change(); + $table->timestamp( 'updated_at' )->nullable()->change(); + }); + + Schema::table('cust_notes', function (Blueprint $table) { + DB::statement("ALTER TABLE cust_notes CHANGE created_at created_at TIMESTAMP NULL"); + DB::statement("ALTER TABLE cust_notes CHANGE updated_at updated_at TIMESTAMP NULL"); + }); + + Schema::table('cust_tag', function (Blueprint $table) { + DB::statement("ALTER TABLE cust_tag CHANGE created_at created_at TIMESTAMP NULL"); + DB::statement("ALTER TABLE cust_tag CHANGE updated_at updated_at TIMESTAMP NULL"); + }); + + Schema::table('customer_to_users', function (Blueprint $table) { + DB::statement("ALTER TABLE customer_to_users MODIFY COLUMN created_at DATETIME AFTER last_login_via"); + $table->timestamp( 'created_at' )->change(); + }); + + Schema::table('l2address', function (Blueprint $table) { + $table->timestamp( 'created_at' )->change(); + }); + + Schema::table('logos', function (Blueprint $table) { + DB::statement("ALTER TABLE logos MODIFY COLUMN created_at DATETIME AFTER height"); + $table->timestamp( 'created_at' )->change(); + }); + + Schema::table('peering_manager', function (Blueprint $table) { + $table->timestamp( 'created_at' )->change(); + $table->timestamp( 'updated_at' )->nullable()->change(); + }); + + Schema::table('routers', function (Blueprint $table) { + DB::statement("ALTER TABLE routers MODIFY COLUMN updated_at DATETIME AFTER created_at"); + $table->timestamp( 'updated_at' )->nullable()->change(); + }); + + Schema::table('user', function (Blueprint $table) { + DB::statement("ALTER TABLE user MODIFY COLUMN created_at DATETIME AFTER extra_attributes"); + DB::statement("ALTER TABLE user MODIFY COLUMN updated_at DATETIME AFTER created_at"); + $table->timestamp( 'created_at' )->change(); + $table->timestamp( 'updated_at' )->nullable()->change(); + }); + + Schema::table('user_2fa', function (Blueprint $table) { + $table->timestamp( 'created_at' )->change(); + $table->timestamp( 'updated_at' )->nullable()->change(); + }); + + Schema::table('user_remember_tokens', function (Blueprint $table) { + DB::statement("ALTER TABLE user_remember_tokens MODIFY COLUMN created_at DATETIME AFTER is_2fa_complete"); + $table->timestamp( 'created_at' )->change(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('api_keys', function (Blueprint $table) { + DB::statement("ALTER TABLE api_keys MODIFY COLUMN created_at DATETIME AFTER description"); + $table->timestamp( 'created_at' )->change(); + }); + + Schema::table('contact_group', function (Blueprint $table) { + DB::statement("ALTER TABLE contact_group CHANGE created_at created_at DATETIME NULL"); + }); + + Schema::table('cust', function (Blueprint $table) { + DB::statement("ALTER TABLE cust MODIFY COLUMN created_at DATETIME AFTER peeringdb_oauth"); + DB::statement("ALTER TABLE cust MODIFY COLUMN updated_at DATETIME AFTER created_at"); + $table->dateTime( 'created_at' )->change(); + $table->dateTime( 'updated_at' )->change(); + }); + + Schema::table('cust_notes', function (Blueprint $table) { + DB::statement("ALTER TABLE cust_notes CHANGE created_at created_at DATETIME NULL"); + DB::statement("ALTER TABLE cust_notes CHANGE updated_at updated_at DATETIME NULL"); + }); + + Schema::table('cust_tag', function (Blueprint $table) { + DB::statement("ALTER TABLE cust_tag CHANGE created_at created_at DATETIME NULL"); + DB::statement("ALTER TABLE cust_tag CHANGE updated_at updated_at DATETIME NULL"); + }); + + Schema::table('customer_to_users', function (Blueprint $table) { + $table->dateTime( 'created_at' )->change(); + }); + + Schema::table('l2address', function (Blueprint $table) { + $table->dateTime( 'created_at' )->change(); + }); + + Schema::table('logos', function (Blueprint $table) { + $table->dateTime( 'created_at' )->change(); + }); + + Schema::table('peering_manager', function (Blueprint $table) { + $table->dateTime( 'created_at' )->change(); + $table->dateTime( 'updated_at' )->change(); + }); + + Schema::table('routers', function (Blueprint $table) { + $table->dateTime( 'updated_at' )->change(); + }); + + Schema::table('user', function (Blueprint $table) { + $table->dateTime( 'created_at' )->change(); + $table->dateTime( 'updated_at' )->change(); + }); + + Schema::table('user_2fa', function (Blueprint $table) { + $table->dateTime( 'created_at' )->change(); + $table->dateTime( 'updated_at' )->change(); + }); + + Schema::table('user_remember_tokens', function (Blueprint $table) { + $table->dateTime( 'created_at' )->change(); + }); + } +} diff --git a/database/migrations/2021_04_14_125742_user_pref.php b/database/migrations/2021_04_14_125742_user_pref.php new file mode 100644 index 000000000..d951074a5 --- /dev/null +++ b/database/migrations/2021_04_14_125742_user_pref.php @@ -0,0 +1,177 @@ +json('prefs' )->nullable()->after( 'extra_attributes' ); + }); + + + // Import the last read note data + $notesRead = DB::table( 'user_pref' ) + ->select( [ 'user_id', 'attribute', 'value' ] ) + ->where( 'attribute', 'like', 'customer-notes.%.last_read' ) + ->get()->toArray(); + + $userNotes = []; + + foreach( $notesRead as $read ){ + $cust_id = explode( '.', $read->attribute )[1]; + $userNotes[ $read->user_id ][ $cust_id ] = \Carbon\Carbon::parse( (int)$read->value )->format( 'Y-m-d H:i:s' ); + } + + foreach( $userNotes as $user_id => $note ){ + DB::table('user') + ->where('id', $user_id ) + ->update( [ 'prefs' => [ 'notes' => [ 'last_read' => $note ] ] ] ); + } + + // Import the watching note data + $notesWatching = DB::table( 'user_pref' ) + ->select( [ 'user_id', 'attribute', 'value' ] ) + ->where( 'attribute', 'like', 'customer-notes.watching.%' ) + ->get()->toArray(); + + $userWatching = []; + + foreach( $notesWatching as $watching ){ + $note_id = explode( '.', $watching->attribute )[2]; + $userWatching[ $watching->user_id ][ $note_id ] = \Carbon\Carbon::parse( (int)$read->value )->format( 'Y-m-d H:i:s' ); + } + + foreach( $userWatching as $user_id => $watching ){ + $u = User::find( $user_id ); + $prefs = $u->prefs; + $prefs[ 'notes' ][ 'note_watching' ] = $watching; + $u->prefs = $prefs ; + $u->save(); + } + + // Import the global notifications data + $notifications = DB::table( 'user_pref' ) + ->select( [ 'user_id', 'attribute', 'value' ] ) + ->where( 'attribute', 'customer-notes.notify' ) + ->get()->toArray(); + + $notifs = []; + + foreach( $notifications as $notif ){ + $notifs[ $notif->user_id ] = $notif->value; + } + + foreach( $notifs as $user_id => $global_notifs ){ + $u = User::find( $user_id ); + $prefs = $u->prefs; + $prefs[ 'notes' ][ 'global_notifs' ] = $global_notifs; + $u->prefs = $prefs ; + $u->save(); + } + + // Import the individual notes notifications data + $notesNotify = DB::table( 'user_pref' ) + ->select( [ 'user_id', 'attribute', 'value' ] ) + ->where( 'attribute', 'like','customer-notes.%.notify' ) + ->get()->toArray(); + + $notify = []; + + foreach( $notesNotify as $not ){ + $note_id = explode( '.', $not->attribute )[1]; + $notify[ $not->user_id ][ $note_id ] = $not->value; + } + + foreach( $notify as $user_id => $individual_notifs ){ + $u = User::find( $user_id ); + $prefs = $u->prefs; + $prefs[ 'notes' ][ 'customer_watching' ] = $individual_notifs; + $u->prefs = $prefs ; + $u->save(); + } + + // Import read_upto notes data + $notesReadupto = DB::table( 'user_pref' ) + ->select( [ 'user_id', 'attribute', 'value' ] ) + ->where( 'attribute','customer-notes.read_upto' ) + ->get()->toArray(); + + $readupto = []; + + foreach( $notesReadupto as $upto ){ + $readupto[ $upto->user_id ] = \Carbon\Carbon::parse( (int)$upto->value )->format( 'Y-m-d H:i:s' );; + } + + foreach( $readupto as $user_id => $read ){ + $u = User::find( $user_id ); + $prefs = $u->prefs; + $prefs[ 'notes' ][ 'read_upto' ] = $read; + $u->prefs = $prefs ; + $u->save(); + } + + // Import the mailing list data + $mailingList = DB::table( 'user_pref' ) + ->select( [ 'user_id', 'attribute', 'value' ]) + ->where( 'attribute', 'like', 'mailinglist%' ) + ->get()->toArray(); + + $userMailling = []; + + foreach( $mailingList as $mailling ){ + $list = explode( '.', $mailling->attribute )[1]; + $userMailling[ $mailling->user_id ][ $list ] = $mailling->value; + } + + foreach( $userMailling as $user_id => $mail ){ + $u = User::find( $user_id ); + $prefs = $u->prefs; + $prefs[ 'mailinglist' ] = $mail; + $u->prefs = $prefs ; + $u->save(); + } + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('user', function (Blueprint $table) { + $table->dropColumn( 'prefs' ); + }); + } +} diff --git a/database/migrations/2021_05_18_085721_add_note_infrastructure.php b/database/migrations/2021_05_18_085721_add_note_infrastructure.php new file mode 100644 index 000000000..fbdb3a966 --- /dev/null +++ b/database/migrations/2021_05_18_085721_add_note_infrastructure.php @@ -0,0 +1,33 @@ +longText( 'notes' )->after( 'country' )->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('infrastructure', function (Blueprint $table) { + $table->dropColumn('notes'); + }); + + } +} diff --git a/database/migrations/2021_05_18_114206_update_pp_prefix_size.php b/database/migrations/2021_05_18_114206_update_pp_prefix_size.php new file mode 100644 index 000000000..e3fefe615 --- /dev/null +++ b/database/migrations/2021_05_18_114206_update_pp_prefix_size.php @@ -0,0 +1,53 @@ +string('port_prefix', 20)->change(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('patch_panel', function (Blueprint $table) { + $table->string('port_prefix', 10)->change(); + }); + } +} diff --git a/database/migrations/2021_06_11_141137_update_db_doctrine2eloquent.php b/database/migrations/2021_06_11_141137_update_db_doctrine2eloquent.php new file mode 100644 index 000000000..9684281c3 --- /dev/null +++ b/database/migrations/2021_06_11_141137_update_db_doctrine2eloquent.php @@ -0,0 +1,61 @@ +dropColumn('switchid'); + }); + + Schema::table('cabinet', function (Blueprint $table) { + $table->renameColumn('cololocation', 'colocation'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('consoleserverconnection', function (Blueprint $table) { + $table->integer('switchid'); + }); + + Schema::table('cabinet', function (Blueprint $table) { + $table->renameColumn('colocation', 'cololocation'); + }); + } +} diff --git a/database/migrations/2021_07_20_134716_fix_last_updated_and_timestamps.php b/database/migrations/2021_07_20_134716_fix_last_updated_and_timestamps.php new file mode 100644 index 000000000..2d128f43b --- /dev/null +++ b/database/migrations/2021_07_20_134716_fix_last_updated_and_timestamps.php @@ -0,0 +1,49 @@ +dateTime( 'last_updated' )->nullable(); + } else { + $table->dateTime( 'last_updated' )->nullable()->change(); + } + + if( !Schema::hasColumn( 'routers', 'created_at' ) ) { + $table->timestamp( 'created_at' )->nullable(); + } else { + $table->timestamp( 'created_at' )->nullable()->change(); + } + + if( !Schema::hasColumn( 'routers', 'updated_at' ) ) { + $table->timestamp( 'updated_at' )->nullable(); + } else { + $table->timestamp( 'updated_at' )->nullable()->change(); + } + + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('routers', function (Blueprint $table) { + // + }); + } +} diff --git a/database/migrations/2021_09_16_195333_add_rate_limit_col_to_physint.php b/database/migrations/2021_09_16_195333_add_rate_limit_col_to_physint.php new file mode 100644 index 000000000..5e7ceee46 --- /dev/null +++ b/database/migrations/2021_09_16_195333_add_rate_limit_col_to_physint.php @@ -0,0 +1,32 @@ +integer( 'rate_limit' )->after( 'duplex' )->unsigned()->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('physicalinterface', function (Blueprint $table) { + $table->dropColumn('rate_limit'); + }); + } +} diff --git a/database/migrations/2021_09_17_144421_modernise_irrdb_conf_table.php b/database/migrations/2021_09_17_144421_modernise_irrdb_conf_table.php new file mode 100644 index 000000000..4e660d4ee --- /dev/null +++ b/database/migrations/2021_09_17_144421_modernise_irrdb_conf_table.php @@ -0,0 +1,33 @@ +dropColumn('protocol'); + \Illuminate\Support\Facades\DB::table('irrdbconfig')->update(['host' => 'whois.radb.net']); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('irrdbconfig', function (Blueprint $table) { + $table->string('protocol')->nullable()->after('host'); + }); + } +} diff --git a/database/migrations/2021_09_21_100354_create_route_server_filters_prod.php b/database/migrations/2021_09_21_100354_create_route_server_filters_prod.php new file mode 100644 index 000000000..638569bd1 --- /dev/null +++ b/database/migrations/2021_09_21_100354_create_route_server_filters_prod.php @@ -0,0 +1,49 @@ +bigIncrements('id'); + $table->integer( 'customer_id' )->nullable(); + $table->integer( 'peer_id' )->nullable(); + $table->integer( 'vlan_id' )->nullable(); + $table->string( 'received_prefix',43 )->nullable( true ); + $table->string( 'advertised_prefix',43 )->nullable( true ); + $table->smallInteger('protocol' )->nullable( true ); + $table->string( 'action_advertise',255 )->nullable( true ); + $table->string( 'action_receive',255 )->nullable( true ); + $table->boolean( 'enabled' )->default( true ); + $table->integer('order_by' )->nullable( false ); + $table->string( 'live',255 )->nullable( false ); + + $table->foreign('customer_id' )->references('id' )->on('cust' ); + $table->foreign('peer_id' )->references('id' )->on('cust' ); + $table->foreign('vlan_id' )->references('id' )->on('vlan' ); + + $table->unique( [ 'customer_id', 'order_by' ] ); + + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('route_server_filters_prod'); + } +} \ No newline at end of file diff --git a/database/migrations/2021_09_21_162700_rs_pairing.php b/database/migrations/2021_09_21_162700_rs_pairing.php new file mode 100644 index 000000000..c85d04ea9 --- /dev/null +++ b/database/migrations/2021_09_21_162700_rs_pairing.php @@ -0,0 +1,42 @@ +datetime( 'last_update_started' )->nullable()->after('skip_md5'); + $table->boolean( 'pause_updates' )->default(false)->after('last_updated'); + $table->integer('pair_id')->nullable()->after('id'); + $table->foreign('pair_id')->references('id')->on('routers')->nullOnDelete(); + + }); + + DB::update('update routers set last_update_started = last_updated' ); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('routers', function (Blueprint $table) { + $table->dropColumn('last_update_started'); + $table->dropColumn('pause_updates'); + + $table->dropForeign('routers_pair_id_foreign'); + $table->dropColumn('pair_id'); + }); + } +} diff --git a/database/migrations/2022_02_12_183121_add_colo_pp_type_patch_panel.php b/database/migrations/2022_02_12_183121_add_colo_pp_type_patch_panel.php new file mode 100644 index 000000000..70beb54cd --- /dev/null +++ b/database/migrations/2022_02_12_183121_add_colo_pp_type_patch_panel.php @@ -0,0 +1,33 @@ +tinyInteger( 'colo_pp_type' )->after( 'active' )->nullable( false )->default(1); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('patch_panel', function (Blueprint $table) { + $table->dropColumn('colo_pp_type'); + }); + + } +} diff --git a/database/migrations/2023_09_26_191150_add_registration_details.php b/database/migrations/2023_09_26_191150_add_registration_details.php new file mode 100644 index 000000000..0dc567ac5 --- /dev/null +++ b/database/migrations/2023_09_26_191150_add_registration_details.php @@ -0,0 +1,31 @@ +longText( 'notes' )->after( 'country' )->nullable(); + } ); + + Schema::table( 'company_billing_detail', function( Blueprint $table ) { + $table->string( 'purchaseOrderNumber', 50 )->after( 'purchaseOrderRequired' )->nullable(); + $table->longText( 'notes' )->after( 'billingFrequency' )->nullable(); + } ); + } + + public function down(): void + { + Schema::table( 'company_registration_detail', function( Blueprint $table ) { + $table->dropColumn('notes'); + } ); + + Schema::table( 'company_billing_detail', function( Blueprint $table ) { + $table->dropColumn( 'purchaseOrderNumber' ); + $table->dropColumn( 'notes' ); + } ); + } +}; diff --git a/database/migrations/2024_03_18_191322_add_export_to_ixf_vlan.php b/database/migrations/2024_03_18_191322_add_export_to_ixf_vlan.php new file mode 100644 index 000000000..08931d4f7 --- /dev/null +++ b/database/migrations/2024_03_18_191322_add_export_to_ixf_vlan.php @@ -0,0 +1,34 @@ +tinyInteger( 'export_to_ixf' )->after( 'peering_manager' )->nullable( false )->default(1); + }); + + \IXP\Models\Vlan::wherePrivate(1)->update(['export_to_ixf' => false]); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('vlan', function (Blueprint $table) { + $table->dropColumn('export_to_ixf'); + }); + } +} diff --git a/database/migrations/2024_05_29_102028_reset-views.php b/database/migrations/2024_05_29_102028_reset-views.php new file mode 100644 index 000000000..0caab0f2f --- /dev/null +++ b/database/migrations/2024_05_29_102028_reset-views.php @@ -0,0 +1,28 @@ +id(); + + $table->integer('cust_id')->unique(true); + + $table->dateTime( 'prefix_v4' )->nullable()->default(null); + $table->dateTime( 'prefix_v6' )->nullable()->default(null); + $table->dateTime( 'asn_v4' )->nullable()->default(null); + $table->dateTime( 'asn_v6' )->nullable()->default(null); + + $table->foreign('cust_id')->references('id')->on('cust'); + + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('irrdb_update_logs'); + } +}; diff --git a/database/migrations/2024_09_05_111855_create_p2p_daily_stats_table.php b/database/migrations/2024_09_05_111855_create_p2p_daily_stats_table.php new file mode 100644 index 000000000..558dc66a1 --- /dev/null +++ b/database/migrations/2024_09_05_111855_create_p2p_daily_stats_table.php @@ -0,0 +1,42 @@ +id(); + $table->date('day'); + $table->integer('cust_id'); + $table->foreign('cust_id')->references('id')->on('cust')->onDelete('cascade'); + $table->integer('peer_id'); + $table->bigInteger('ipv4_total_in')->nullable()->unsigned(); + $table->bigInteger('ipv4_total_out')->nullable()->unsigned(); + $table->bigInteger('ipv6_total_in')->nullable()->unsigned(); + $table->bigInteger('ipv6_total_out')->nullable()->unsigned(); + $table->bigInteger('ipv4_max_in')->nullable()->unsigned(); + $table->bigInteger('ipv4_max_out')->nullable()->unsigned(); + $table->bigInteger('ipv6_max_in')->nullable()->unsigned(); + $table->bigInteger('ipv6_max_out')->nullable()->unsigned(); + + $table->unique(['day', 'cust_id', 'peer_id']); + + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('p2p_daily_stats'); + } +}; diff --git a/database/schema/2021-as-at-end-v5.sql b/database/schema/2021-as-at-end-v5.sql new file mode 100644 index 000000000..1cff16a48 --- /dev/null +++ b/database/schema/2021-as-at-end-v5.sql @@ -0,0 +1,1818 @@ +-- MySQL dump 10.13 Distrib 5.7.32, for osx10.16 (x86_64) +-- +-- Host: localhost Database: inex +-- ------------------------------------------------------ +-- Server version 5.7.32 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `api_keys` +-- + +DROP TABLE IF EXISTS `api_keys`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `api_keys` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `user_id` int(11) NOT NULL, + `apiKey` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `expires` datetime DEFAULT NULL, + `allowedIPs` mediumtext COLLATE utf8_unicode_ci, + `created` datetime NOT NULL, + `lastseenAt` datetime DEFAULT NULL, + `lastseenFrom` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `description` longtext COLLATE utf8_unicode_ci, + PRIMARY KEY (`id`), + UNIQUE KEY `UNIQ_9579321F800A1141` (`apiKey`), + KEY `IDX_9579321FA76ED395` (`user_id`), + CONSTRAINT `FK_9579321FA76ED395` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `bgp_sessions` +-- + +DROP TABLE IF EXISTS `bgp_sessions`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `bgp_sessions` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `srcipaddressid` int(11) NOT NULL, + `protocol` int(11) NOT NULL, + `dstipaddressid` int(11) NOT NULL, + `packetcount` int(11) NOT NULL DEFAULT '0', + `last_seen` datetime NOT NULL, + `source` varchar(40) COLLATE utf8_unicode_ci DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `src_protocol_dst` (`srcipaddressid`,`protocol`,`dstipaddressid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `bgpsessiondata` +-- + +DROP TABLE IF EXISTS `bgpsessiondata`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `bgpsessiondata` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `srcipaddressid` int(11) DEFAULT NULL, + `dstipaddressid` int(11) DEFAULT NULL, + `protocol` int(11) DEFAULT NULL, + `vlan` int(11) DEFAULT NULL, + `packetcount` int(11) DEFAULT '0', + `timestamp` datetime DEFAULT NULL, + `source` varchar(40) COLLATE utf8_unicode_ci DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `idx_timestamp` (`timestamp`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +/*!50003 SET @saved_cs_client = @@character_set_client */ ; +/*!50003 SET @saved_cs_results = @@character_set_results */ ; +/*!50003 SET @saved_col_connection = @@collation_connection */ ; +/*!50003 SET character_set_client = utf8 */ ; +/*!50003 SET character_set_results = utf8 */ ; +/*!50003 SET collation_connection = utf8_general_ci */ ; +/*!50003 SET @saved_sql_mode = @@sql_mode */ ; +/*!50003 SET sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION' */ ; +/*!50003 SET sql_mode = @saved_sql_mode */ ; +/*!50003 SET character_set_client = @saved_cs_client */ ; +/*!50003 SET character_set_results = @saved_cs_results */ ; +/*!50003 SET collation_connection = @saved_col_connection */ ; + +-- +-- Table structure for table `cabinet` +-- + +DROP TABLE IF EXISTS `cabinet`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `cabinet` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `locationid` int(11) DEFAULT NULL, + `name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `cololocation` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `height` int(11) DEFAULT NULL, + `type` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `notes` longtext COLLATE utf8_unicode_ci, + `u_counts_from` smallint(6) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `UNIQ_4CED05B05E237E06` (`name`), + KEY `IDX_4CED05B03530CCF` (`locationid`), + CONSTRAINT `FK_4CED05B03530CCF` FOREIGN KEY (`locationid`) REFERENCES `location` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `company_billing_detail` +-- + +DROP TABLE IF EXISTS `company_billing_detail`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `company_billing_detail` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `billingContactName` varchar(255) DEFAULT NULL, + `billingAddress1` varchar(255) DEFAULT NULL, + `billingAddress2` varchar(255) DEFAULT NULL, + `billingAddress3` varchar(255) DEFAULT NULL, + `billingTownCity` varchar(255) DEFAULT NULL, + `billingPostcode` varchar(255) DEFAULT NULL, + `billingCountry` varchar(255) DEFAULT NULL, + `billingEmail` varchar(255) DEFAULT NULL, + `billingTelephone` varchar(255) DEFAULT NULL, + `vatNumber` varchar(255) DEFAULT NULL, + `vatRate` varchar(255) DEFAULT NULL, + `purchaseOrderRequired` tinyint(1) NOT NULL DEFAULT '0', + `invoiceMethod` varchar(255) DEFAULT NULL, + `invoiceEmail` varchar(255) DEFAULT NULL, + `billingFrequency` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `company_registration_detail` +-- + +DROP TABLE IF EXISTS `company_registration_detail`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `company_registration_detail` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `registeredName` varchar(255) DEFAULT NULL, + `companyNumber` varchar(255) DEFAULT NULL, + `jurisdiction` varchar(255) DEFAULT NULL, + `address1` varchar(255) DEFAULT NULL, + `address2` varchar(255) DEFAULT NULL, + `address3` varchar(255) DEFAULT NULL, + `townCity` varchar(255) DEFAULT NULL, + `postcode` varchar(255) DEFAULT NULL, + `country` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `console_server` +-- + +DROP TABLE IF EXISTS `console_server`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `console_server` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `vendor_id` int(11) DEFAULT NULL, + `cabinet_id` int(11) DEFAULT NULL, + `name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `hostname` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `model` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `serialNumber` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `active` tinyint(1) DEFAULT '1', + `notes` longtext COLLATE utf8_unicode_ci, + PRIMARY KEY (`id`), + UNIQUE KEY `UNIQ_92A539235E237E06` (`name`), + KEY `IDX_92A53923F603EE73` (`vendor_id`), + KEY `IDX_92A53923D351EC` (`cabinet_id`), + CONSTRAINT `FK_92A53923D351EC` FOREIGN KEY (`cabinet_id`) REFERENCES `cabinet` (`id`), + CONSTRAINT `FK_92A53923F603EE73` FOREIGN KEY (`vendor_id`) REFERENCES `vendor` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `consoleserverconnection` +-- + +DROP TABLE IF EXISTS `consoleserverconnection`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `consoleserverconnection` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `custid` int(11) DEFAULT NULL, + `switchid` int(11) DEFAULT NULL, + `description` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `port` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `speed` int(11) DEFAULT NULL, + `parity` int(11) DEFAULT NULL, + `stopbits` int(11) DEFAULT NULL, + `flowcontrol` int(11) DEFAULT NULL, + `autobaud` tinyint(1) DEFAULT NULL, + `notes` longtext COLLATE utf8_unicode_ci, + `console_server_id` int(11) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `console_server_port_uniq` (`console_server_id`,`port`), + KEY `IDX_530316DCDA0209B9` (`custid`), + KEY `IDX_530316DCF472E7C6` (`console_server_id`), + CONSTRAINT `FK_530316DCDA0209B9` FOREIGN KEY (`custid`) REFERENCES `cust` (`id`) ON DELETE CASCADE, + CONSTRAINT `FK_530316DCF472E7C6` FOREIGN KEY (`console_server_id`) REFERENCES `console_server` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `contact` +-- + +DROP TABLE IF EXISTS `contact`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `contact` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `custid` int(11) DEFAULT NULL, + `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `email` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `phone` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, + `mobile` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, + `facilityaccess` tinyint(1) NOT NULL DEFAULT '0', + `mayauthorize` tinyint(1) NOT NULL DEFAULT '0', + `lastupdated` datetime DEFAULT NULL, + `lastupdatedby` int(11) DEFAULT NULL, + `creator` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, + `created` datetime DEFAULT NULL, + `position` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, + `notes` longtext COLLATE utf8_unicode_ci, + PRIMARY KEY (`id`), + KEY `IDX_4C62E638DA0209B9` (`custid`), + CONSTRAINT `FK_4C62E638DA0209B9` FOREIGN KEY (`custid`) REFERENCES `cust` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `contact_group` +-- + +DROP TABLE IF EXISTS `contact_group`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `contact_group` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `name` varchar(20) COLLATE utf8_unicode_ci NOT NULL, + `description` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `type` varchar(20) COLLATE utf8_unicode_ci NOT NULL, + `active` tinyint(1) NOT NULL DEFAULT '1', + `limited_to` int(11) NOT NULL DEFAULT '0', + `created` datetime NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `UNIQ_40EA54CA5E237E06` (`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `contact_to_group` +-- + +DROP TABLE IF EXISTS `contact_to_group`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `contact_to_group` ( + `contact_id` int(11) NOT NULL, + `contact_group_id` bigint(20) NOT NULL, + PRIMARY KEY (`contact_id`,`contact_group_id`), + KEY `IDX_FCD9E962E7A1254A` (`contact_id`), + KEY `IDX_FCD9E962647145D0` (`contact_group_id`), + CONSTRAINT `FK_FCD9E962647145D0` FOREIGN KEY (`contact_group_id`) REFERENCES `contact_group` (`id`), + CONSTRAINT `FK_FCD9E962E7A1254A` FOREIGN KEY (`contact_id`) REFERENCES `contact` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `corebundles` +-- + +DROP TABLE IF EXISTS `corebundles`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `corebundles` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `description` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `type` int(11) NOT NULL, + `graph_title` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `bfd` tinyint(1) NOT NULL DEFAULT '0', + `ipv4_subnet` varchar(18) COLLATE utf8_unicode_ci DEFAULT NULL, + `ipv6_subnet` varchar(43) COLLATE utf8_unicode_ci DEFAULT NULL, + `stp` tinyint(1) NOT NULL DEFAULT '0', + `cost` int(10) unsigned DEFAULT NULL, + `preference` int(10) unsigned DEFAULT NULL, + `enabled` tinyint(1) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `coreinterfaces` +-- + +DROP TABLE IF EXISTS `coreinterfaces`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `coreinterfaces` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `physical_interface_id` int(11) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `UNIQ_E1A404B7FF664B20` (`physical_interface_id`), + CONSTRAINT `FK_E1A404B7FF664B20` FOREIGN KEY (`physical_interface_id`) REFERENCES `physicalinterface` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `corelinks` +-- + +DROP TABLE IF EXISTS `corelinks`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `corelinks` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `core_interface_sidea_id` int(11) NOT NULL, + `core_interface_sideb_id` int(11) NOT NULL, + `core_bundle_id` int(11) NOT NULL, + `bfd` tinyint(1) NOT NULL DEFAULT '0', + `ipv4_subnet` varchar(18) COLLATE utf8_unicode_ci DEFAULT NULL, + `ipv6_subnet` varchar(43) COLLATE utf8_unicode_ci DEFAULT NULL, + `enabled` tinyint(1) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + UNIQUE KEY `UNIQ_BE421236BEBB85C6` (`core_interface_sidea_id`), + UNIQUE KEY `UNIQ_BE421236AC0E2A28` (`core_interface_sideb_id`), + KEY `IDX_BE421236BE9AE9F7` (`core_bundle_id`), + CONSTRAINT `FK_BE421236AC0E2A28` FOREIGN KEY (`core_interface_sideb_id`) REFERENCES `coreinterfaces` (`id`), + CONSTRAINT `FK_BE421236BE9AE9F7` FOREIGN KEY (`core_bundle_id`) REFERENCES `corebundles` (`id`), + CONSTRAINT `FK_BE421236BEBB85C6` FOREIGN KEY (`core_interface_sidea_id`) REFERENCES `coreinterfaces` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `cust` +-- + +DROP TABLE IF EXISTS `cust`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `cust` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `type` int(11) DEFAULT NULL, + `shortname` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `autsys` int(11) DEFAULT NULL, + `maxprefixes` int(11) DEFAULT NULL, + `peeringemail` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `nocphone` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `noc24hphone` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `nocfax` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `nocemail` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `nochours` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `nocwww` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `irrdb` int(11) DEFAULT NULL, + `peeringmacro` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `peeringpolicy` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `corpwww` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `datejoin` date DEFAULT NULL, + `dateleave` date DEFAULT NULL, + `status` smallint(6) DEFAULT NULL, + `activepeeringmatrix` tinyint(1) DEFAULT NULL, + `lastupdated` date DEFAULT NULL, + `lastupdatedby` int(11) DEFAULT NULL, + `creator` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `created` date DEFAULT NULL, + `company_registered_detail_id` int(11) DEFAULT NULL, + `company_billing_details_id` int(11) DEFAULT NULL, + `peeringmacrov6` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `abbreviatedName` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL, + `MD5Support` varchar(255) COLLATE utf8_unicode_ci DEFAULT 'UNKNOWN', + `reseller` int(11) DEFAULT NULL, + `isReseller` tinyint(1) NOT NULL DEFAULT '0', + `in_manrs` tinyint(1) NOT NULL DEFAULT '0', + `in_peeringdb` tinyint(1) NOT NULL DEFAULT '0', + `peeringdb_oauth` tinyint(1) NOT NULL DEFAULT '1', + PRIMARY KEY (`id`), + UNIQUE KEY `UNIQ_997B25A64082763` (`shortname`), + UNIQUE KEY `UNIQ_997B25A98386213` (`company_registered_detail_id`), + UNIQUE KEY `UNIQ_997B25A84478F0C` (`company_billing_details_id`), + KEY `IDX_997B25A666E98DF` (`irrdb`), + KEY `IDX_997B25A18015899` (`reseller`), + CONSTRAINT `FK_997B25A18015899` FOREIGN KEY (`reseller`) REFERENCES `cust` (`id`), + CONSTRAINT `FK_997B25A666E98DF` FOREIGN KEY (`irrdb`) REFERENCES `irrdbconfig` (`id`), + CONSTRAINT `FK_997B25A84478F0C` FOREIGN KEY (`company_billing_details_id`) REFERENCES `company_billing_detail` (`id`), + CONSTRAINT `FK_997B25A98386213` FOREIGN KEY (`company_registered_detail_id`) REFERENCES `company_registration_detail` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `cust_notes` +-- + +DROP TABLE IF EXISTS `cust_notes`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `cust_notes` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `customer_id` int(11) NOT NULL, + `private` tinyint(1) NOT NULL DEFAULT '1', + `title` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `note` longtext COLLATE utf8_unicode_ci NOT NULL, + `created` datetime NOT NULL, + `updated` datetime NOT NULL, + PRIMARY KEY (`id`), + KEY `IDX_6377D8679395C3F3` (`customer_id`), + CONSTRAINT `FK_6377D8679395C3F3` FOREIGN KEY (`customer_id`) REFERENCES `cust` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `cust_tag` +-- + +DROP TABLE IF EXISTS `cust_tag`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `cust_tag` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `tag` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `display_as` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `description` longtext COLLATE utf8_unicode_ci, + `internal_only` tinyint(1) NOT NULL DEFAULT '0', + `created` datetime NOT NULL, + `updated` datetime NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `UNIQ_6B54CFB8389B783` (`tag`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `cust_to_cust_tag` +-- + +DROP TABLE IF EXISTS `cust_to_cust_tag`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `cust_to_cust_tag` ( + `customer_tag_id` int(11) NOT NULL, + `customer_id` int(11) NOT NULL, + PRIMARY KEY (`customer_tag_id`,`customer_id`), + KEY `IDX_A6CFB30CB17BF40` (`customer_tag_id`), + KEY `IDX_A6CFB30C9395C3F3` (`customer_id`), + CONSTRAINT `FK_A6CFB30C9395C3F3` FOREIGN KEY (`customer_id`) REFERENCES `cust` (`id`), + CONSTRAINT `FK_A6CFB30CB17BF40` FOREIGN KEY (`customer_tag_id`) REFERENCES `cust_tag` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `custkit` +-- + +DROP TABLE IF EXISTS `custkit`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `custkit` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `custid` int(11) DEFAULT NULL, + `cabinetid` int(11) DEFAULT NULL, + `name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `descr` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `IDX_8127F9AADA0209B9` (`custid`), + KEY `IDX_8127F9AA2B96718A` (`cabinetid`), + CONSTRAINT `FK_8127F9AA2B96718A` FOREIGN KEY (`cabinetid`) REFERENCES `cabinet` (`id`), + CONSTRAINT `FK_8127F9AADA0209B9` FOREIGN KEY (`custid`) REFERENCES `cust` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `customer_to_ixp` +-- + +DROP TABLE IF EXISTS `customer_to_ixp`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `customer_to_ixp` ( + `customer_id` int(11) NOT NULL, + `ixp_id` int(11) NOT NULL, + PRIMARY KEY (`customer_id`,`ixp_id`), + KEY `IDX_E85DBF209395C3F3` (`customer_id`), + KEY `IDX_E85DBF20A5A4E881` (`ixp_id`), + CONSTRAINT `FK_E85DBF209395C3F3` FOREIGN KEY (`customer_id`) REFERENCES `cust` (`id`), + CONSTRAINT `FK_E85DBF20A5A4E881` FOREIGN KEY (`ixp_id`) REFERENCES `ixp` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `customer_to_users` +-- + +DROP TABLE IF EXISTS `customer_to_users`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `customer_to_users` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `customer_id` int(11) NOT NULL, + `user_id` int(11) NOT NULL, + `privs` int(11) NOT NULL, + `extra_attributes` longtext COLLATE utf8mb4_unicode_ci COMMENT '(DC2Type:json)', + `last_login_date` datetime DEFAULT NULL, + `last_login_from` tinytext COLLATE utf8mb4_unicode_ci, + `created_at` datetime NOT NULL, + `last_login_via` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `customer_user` (`customer_id`,`user_id`), + KEY `IDX_337AD7F69395C3F3` (`customer_id`), + KEY `IDX_337AD7F6A76ED395` (`user_id`), + CONSTRAINT `FK_337AD7F69395C3F3` FOREIGN KEY (`customer_id`) REFERENCES `cust` (`id`), + CONSTRAINT `FK_337AD7F6A76ED395` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `docstore_customer_directories` +-- + +DROP TABLE IF EXISTS `docstore_customer_directories`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `docstore_customer_directories` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `cust_id` int(11) NOT NULL, + `parent_dir_id` bigint(20) unsigned DEFAULT NULL, + `name` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL, + `description` text COLLATE utf8mb4_unicode_ci, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `docstore_customer_directories_cust_id_foreign` (`cust_id`), + KEY `docstore_customer_directories_parent_dir_id_index` (`parent_dir_id`), + CONSTRAINT `docstore_customer_directories_cust_id_foreign` FOREIGN KEY (`cust_id`) REFERENCES `cust` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `docstore_customer_files` +-- + +DROP TABLE IF EXISTS `docstore_customer_files`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `docstore_customer_files` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `cust_id` int(11) NOT NULL, + `docstore_customer_directory_id` bigint(20) unsigned DEFAULT NULL, + `name` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL, + `disk` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'docstore_customers', + `path` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '', + `sha256` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `description` text COLLATE utf8mb4_unicode_ci, + `min_privs` smallint(6) NOT NULL, + `file_last_updated` datetime NOT NULL, + `created_by` int(11) DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `docstore_customer_files_cust_id_foreign` (`cust_id`), + KEY `docstore_customer_files_docstore_customer_directory_id_foreign` (`docstore_customer_directory_id`), + CONSTRAINT `docstore_customer_files_cust_id_foreign` FOREIGN KEY (`cust_id`) REFERENCES `cust` (`id`), + CONSTRAINT `docstore_customer_files_docstore_customer_directory_id_foreign` FOREIGN KEY (`docstore_customer_directory_id`) REFERENCES `docstore_customer_directories` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `docstore_directories` +-- + +DROP TABLE IF EXISTS `docstore_directories`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `docstore_directories` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `parent_dir_id` bigint(20) unsigned DEFAULT NULL, + `name` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL, + `description` text COLLATE utf8mb4_unicode_ci, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `docstore_directories_parent_dir_id_index` (`parent_dir_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `docstore_files` +-- + +DROP TABLE IF EXISTS `docstore_files`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `docstore_files` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `docstore_directory_id` bigint(20) unsigned DEFAULT NULL, + `name` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL, + `disk` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'docstore', + `path` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '', + `sha256` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `description` text COLLATE utf8mb4_unicode_ci, + `min_privs` smallint(6) NOT NULL, + `file_last_updated` datetime NOT NULL, + `created_by` int(11) DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `docstore_files_docstore_directory_id_foreign` (`docstore_directory_id`), + CONSTRAINT `docstore_files_docstore_directory_id_foreign` FOREIGN KEY (`docstore_directory_id`) REFERENCES `docstore_directories` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `docstore_logs` +-- + +DROP TABLE IF EXISTS `docstore_logs`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `docstore_logs` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `docstore_file_id` bigint(20) unsigned NOT NULL, + `downloaded_by` int(11) DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `docstore_logs_docstore_file_id_foreign` (`docstore_file_id`), + KEY `docstore_logs_created_at_index` (`created_at`), + CONSTRAINT `docstore_logs_docstore_file_id_foreign` FOREIGN KEY (`docstore_file_id`) REFERENCES `docstore_files` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `failed_jobs` +-- + +DROP TABLE IF EXISTS `failed_jobs`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `failed_jobs` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `connection` text COLLATE utf8mb4_unicode_ci NOT NULL, + `queue` text COLLATE utf8mb4_unicode_ci NOT NULL, + `payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL, + `exception` longtext COLLATE utf8mb4_unicode_ci NOT NULL, + `failed_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `infrastructure` +-- + +DROP TABLE IF EXISTS `infrastructure`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `infrastructure` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `ixp_id` int(11) NOT NULL, + `name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `shortname` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `isPrimary` tinyint(1) NOT NULL DEFAULT '0', + `peeringdb_ix_id` bigint(20) DEFAULT NULL, + `ixf_ix_id` bigint(20) DEFAULT NULL, + `country` varchar(2) COLLATE utf8_unicode_ci DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `IXPSN` (`shortname`,`ixp_id`), + KEY `IDX_D129B190A5A4E881` (`ixp_id`), + CONSTRAINT `FK_D129B190A5A4E881` FOREIGN KEY (`ixp_id`) REFERENCES `ixp` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `ipv4address` +-- + +DROP TABLE IF EXISTS `ipv4address`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `ipv4address` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `vlanid` int(11) DEFAULT NULL, + `address` varchar(16) COLLATE utf8_unicode_ci DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `vlan_address` (`vlanid`,`address`), + KEY `IDX_A44BCBEEF48D6D0` (`vlanid`), + CONSTRAINT `FK_A44BCBEEF48D6D0` FOREIGN KEY (`vlanid`) REFERENCES `vlan` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `ipv6address` +-- + +DROP TABLE IF EXISTS `ipv6address`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `ipv6address` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `vlanid` int(11) DEFAULT NULL, + `address` varchar(40) COLLATE utf8_unicode_ci DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `vlan_address` (`vlanid`,`address`), + KEY `IDX_E66ECC93F48D6D0` (`vlanid`), + CONSTRAINT `FK_E66ECC93F48D6D0` FOREIGN KEY (`vlanid`) REFERENCES `vlan` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `irrdb_asn` +-- + +DROP TABLE IF EXISTS `irrdb_asn`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `irrdb_asn` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `customer_id` int(11) NOT NULL, + `asn` int(10) unsigned NOT NULL, + `protocol` int(11) NOT NULL, + `first_seen` datetime DEFAULT NULL, + `last_seen` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `custasn` (`asn`,`protocol`,`customer_id`), + KEY `IDX_87BFC5569395C3F3` (`customer_id`), + CONSTRAINT `FK_87BFC5569395C3F3` FOREIGN KEY (`customer_id`) REFERENCES `cust` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `irrdb_prefix` +-- + +DROP TABLE IF EXISTS `irrdb_prefix`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `irrdb_prefix` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `customer_id` int(11) NOT NULL, + `prefix` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `protocol` int(11) NOT NULL, + `first_seen` datetime DEFAULT NULL, + `last_seen` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `custprefix` (`prefix`,`protocol`,`customer_id`), + KEY `IDX_FE73E77C9395C3F3` (`customer_id`), + CONSTRAINT `FK_FE73E77C9395C3F3` FOREIGN KEY (`customer_id`) REFERENCES `cust` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `irrdbconfig` +-- + +DROP TABLE IF EXISTS `irrdbconfig`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `irrdbconfig` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `host` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `protocol` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `source` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `notes` longtext COLLATE utf8_unicode_ci, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `ixp` +-- + +DROP TABLE IF EXISTS `ixp`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `ixp` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `shortname` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `address1` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `address2` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `address3` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `address4` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `country` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `UNIQ_FA4AB7F64082763` (`shortname`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `l2address` +-- + +DROP TABLE IF EXISTS `l2address`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `l2address` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `vlan_interface_id` int(11) NOT NULL, + `mac` varchar(12) COLLATE utf8_unicode_ci DEFAULT NULL, + `firstseen` datetime DEFAULT NULL, + `lastseen` datetime DEFAULT NULL, + `created` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `mac_vlanint` (`mac`,`vlan_interface_id`), + KEY `IDX_B9482E1D6AB5F82` (`vlan_interface_id`), + CONSTRAINT `FK_B9482E1D6AB5F82` FOREIGN KEY (`vlan_interface_id`) REFERENCES `vlaninterface` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `location` +-- + +DROP TABLE IF EXISTS `location`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `location` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `shortname` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `tag` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `address` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `nocphone` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `nocfax` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `nocemail` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `officephone` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `officefax` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `officeemail` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `notes` longtext COLLATE utf8_unicode_ci, + `pdb_facility_id` bigint(20) DEFAULT NULL, + `city` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, + `country` varchar(2) COLLATE utf8_unicode_ci DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `UNIQ_5E9E89CB64082763` (`shortname`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `logos` +-- + +DROP TABLE IF EXISTS `logos`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `logos` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `customer_id` int(11) DEFAULT NULL, + `type` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `original_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `stored_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `uploaded_by` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `uploaded_at` datetime NOT NULL, + `width` int(11) NOT NULL, + `height` int(11) NOT NULL, + PRIMARY KEY (`id`), + KEY `IDX_9F54004F9395C3F3` (`customer_id`), + CONSTRAINT `FK_9F54004F9395C3F3` FOREIGN KEY (`customer_id`) REFERENCES `cust` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `macaddress` +-- + +DROP TABLE IF EXISTS `macaddress`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `macaddress` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `virtualinterfaceid` int(11) DEFAULT NULL, + `firstseen` datetime DEFAULT NULL, + `lastseen` datetime DEFAULT NULL, + `mac` varchar(12) COLLATE utf8_unicode_ci DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `IDX_42CD65F6BFDF15D5` (`virtualinterfaceid`), + CONSTRAINT `FK_42CD65F6BFDF15D5` FOREIGN KEY (`virtualinterfaceid`) REFERENCES `virtualinterface` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `migrations` +-- + +DROP TABLE IF EXISTS `migrations`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `migrations` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `migration` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + `batch` int(11) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `netinfo` +-- + +DROP TABLE IF EXISTS `netinfo`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `netinfo` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `vlan_id` int(11) NOT NULL, + `protocol` int(11) NOT NULL, + `property` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `ix` int(11) NOT NULL DEFAULT '0', + `value` longtext COLLATE utf8_unicode_ci NOT NULL, + PRIMARY KEY (`id`), + KEY `IDX_F843DE6B8B4937A1` (`vlan_id`), + KEY `VlanProtoProp` (`protocol`,`property`,`vlan_id`), + CONSTRAINT `FK_F843DE6B8B4937A1` FOREIGN KEY (`vlan_id`) REFERENCES `vlan` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `networkinfo` +-- + +DROP TABLE IF EXISTS `networkinfo`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `networkinfo` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `vlanid` int(11) DEFAULT NULL, + `protocol` int(11) DEFAULT NULL, + `network` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `masklen` int(11) DEFAULT NULL, + `rs1address` varchar(40) COLLATE utf8_unicode_ci DEFAULT NULL, + `rs2address` varchar(40) COLLATE utf8_unicode_ci DEFAULT NULL, + `dnsfile` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `IDX_6A0AF167F48D6D0` (`vlanid`), + CONSTRAINT `FK_6A0AF167F48D6D0` FOREIGN KEY (`vlanid`) REFERENCES `vlan` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `oui` +-- + +DROP TABLE IF EXISTS `oui`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `oui` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `oui` varchar(6) COLLATE utf8_unicode_ci NOT NULL, + `organisation` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `UNIQ_DAEC0140DAEC0140` (`oui`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `password_resets` +-- + +DROP TABLE IF EXISTS `password_resets`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `password_resets` ( + `email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + `token` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + `created_at` timestamp NULL DEFAULT NULL, + KEY `password_resets_email_index` (`email`(191)) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `patch_panel` +-- + +DROP TABLE IF EXISTS `patch_panel`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `patch_panel` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `cabinet_id` int(11) DEFAULT NULL, + `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `colo_reference` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `cable_type` int(11) NOT NULL, + `connector_type` int(11) NOT NULL, + `installation_date` datetime DEFAULT NULL, + `port_prefix` varchar(10) COLLATE utf8_unicode_ci NOT NULL, + `active` tinyint(1) NOT NULL DEFAULT '1', + `chargeable` int(11) NOT NULL DEFAULT '0', + `location_notes` longtext COLLATE utf8_unicode_ci NOT NULL, + `u_position` int(11) DEFAULT NULL, + `mounted_at` smallint(6) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `IDX_79A52562D351EC` (`cabinet_id`), + CONSTRAINT `FK_79A52562D351EC` FOREIGN KEY (`cabinet_id`) REFERENCES `cabinet` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `patch_panel_port` +-- + +DROP TABLE IF EXISTS `patch_panel_port`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `patch_panel_port` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `switch_port_id` int(11) DEFAULT NULL, + `patch_panel_id` int(11) DEFAULT NULL, + `customer_id` int(11) DEFAULT NULL, + `state` int(11) NOT NULL, + `notes` longtext COLLATE utf8_unicode_ci, + `assigned_at` date DEFAULT NULL, + `connected_at` date DEFAULT NULL, + `cease_requested_at` date DEFAULT NULL, + `ceased_at` date DEFAULT NULL, + `last_state_change` date DEFAULT NULL, + `internal_use` tinyint(1) NOT NULL DEFAULT '0', + `chargeable` int(11) NOT NULL DEFAULT '0', + `duplex_master_id` int(11) DEFAULT NULL, + `number` smallint(6) NOT NULL, + `colo_circuit_ref` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `ticket_ref` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `private_notes` longtext COLLATE utf8_unicode_ci, + `owned_by` int(11) NOT NULL DEFAULT '0', + `loa_code` varchar(25) COLLATE utf8_unicode_ci DEFAULT NULL, + `description` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `colo_billing_ref` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `UNIQ_4BE40BC2C1DA6A2A` (`switch_port_id`), + KEY `IDX_4BE40BC2635D5D87` (`patch_panel_id`), + KEY `IDX_4BE40BC29395C3F3` (`customer_id`), + KEY `IDX_4BE40BC23838446` (`duplex_master_id`), + CONSTRAINT `FK_4BE40BC23838446` FOREIGN KEY (`duplex_master_id`) REFERENCES `patch_panel_port` (`id`), + CONSTRAINT `FK_4BE40BC2635D5D87` FOREIGN KEY (`patch_panel_id`) REFERENCES `patch_panel` (`id`), + CONSTRAINT `FK_4BE40BC29395C3F3` FOREIGN KEY (`customer_id`) REFERENCES `cust` (`id`), + CONSTRAINT `FK_4BE40BC2C1DA6A2A` FOREIGN KEY (`switch_port_id`) REFERENCES `switchport` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `patch_panel_port_file` +-- + +DROP TABLE IF EXISTS `patch_panel_port_file`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `patch_panel_port_file` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `patch_panel_port_id` int(11) DEFAULT NULL, + `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `type` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `uploaded_at` datetime NOT NULL, + `uploaded_by` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `size` int(11) NOT NULL, + `is_private` tinyint(1) NOT NULL DEFAULT '0', + `storage_location` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + PRIMARY KEY (`id`), + KEY `IDX_28089403B0F978FF` (`patch_panel_port_id`), + CONSTRAINT `FK_28089403B0F978FF` FOREIGN KEY (`patch_panel_port_id`) REFERENCES `patch_panel_port` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `patch_panel_port_history` +-- + +DROP TABLE IF EXISTS `patch_panel_port_history`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `patch_panel_port_history` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `patch_panel_port_id` int(11) DEFAULT NULL, + `state` int(11) NOT NULL, + `notes` longtext COLLATE utf8_unicode_ci, + `assigned_at` date DEFAULT NULL, + `connected_at` date DEFAULT NULL, + `cease_requested_at` date DEFAULT NULL, + `ceased_at` date DEFAULT NULL, + `internal_use` tinyint(1) NOT NULL DEFAULT '0', + `chargeable` int(11) NOT NULL DEFAULT '0', + `customer` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `switchport` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `duplex_master_id` int(11) DEFAULT NULL, + `number` smallint(6) NOT NULL, + `colo_circuit_ref` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `ticket_ref` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `private_notes` longtext COLLATE utf8_unicode_ci, + `owned_by` int(11) NOT NULL DEFAULT '0', + `description` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `colo_billing_ref` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `cust_id` int(11) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `IDX_CB80B54AB0F978FF` (`patch_panel_port_id`), + KEY `IDX_CB80B54A3838446` (`duplex_master_id`), + CONSTRAINT `FK_CB80B54A3838446` FOREIGN KEY (`duplex_master_id`) REFERENCES `patch_panel_port_history` (`id`), + CONSTRAINT `FK_CB80B54AB0F978FF` FOREIGN KEY (`patch_panel_port_id`) REFERENCES `patch_panel_port` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `patch_panel_port_history_file` +-- + +DROP TABLE IF EXISTS `patch_panel_port_history_file`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `patch_panel_port_history_file` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `patch_panel_port_history_id` int(11) DEFAULT NULL, + `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `type` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `uploaded_at` datetime NOT NULL, + `uploaded_by` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `size` int(11) NOT NULL, + `is_private` tinyint(1) NOT NULL DEFAULT '0', + `storage_location` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + PRIMARY KEY (`id`), + KEY `IDX_206EAD4E6F461430` (`patch_panel_port_history_id`), + CONSTRAINT `FK_206EAD4E6F461430` FOREIGN KEY (`patch_panel_port_history_id`) REFERENCES `patch_panel_port_history` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `peering_manager` +-- + +DROP TABLE IF EXISTS `peering_manager`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `peering_manager` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `custid` int(11) DEFAULT NULL, + `peerid` int(11) DEFAULT NULL, + `email_last_sent` datetime DEFAULT NULL, + `emails_sent` int(11) DEFAULT NULL, + `peered` tinyint(1) DEFAULT NULL, + `rejected` tinyint(1) DEFAULT NULL, + `notes` longtext COLLATE utf8_unicode_ci, + `created` datetime DEFAULT NULL, + `updated` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `IDX_35A72597DA0209B9` (`custid`), + KEY `IDX_35A725974E5F9AFF` (`peerid`), + CONSTRAINT `FK_35A725974E5F9AFF` FOREIGN KEY (`peerid`) REFERENCES `cust` (`id`) ON DELETE CASCADE, + CONSTRAINT `FK_35A72597DA0209B9` FOREIGN KEY (`custid`) REFERENCES `cust` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `peering_matrix` +-- + +DROP TABLE IF EXISTS `peering_matrix`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `peering_matrix` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `x_custid` int(11) DEFAULT NULL, + `y_custid` int(11) DEFAULT NULL, + `vlan` int(11) DEFAULT NULL, + `x_as` int(11) DEFAULT NULL, + `y_as` int(11) DEFAULT NULL, + `peering_status` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `updated` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `IDX_C1A6F6F9A4CA6408` (`x_custid`), + KEY `IDX_C1A6F6F968606496` (`y_custid`), + CONSTRAINT `FK_C1A6F6F968606496` FOREIGN KEY (`y_custid`) REFERENCES `cust` (`id`) ON DELETE CASCADE, + CONSTRAINT `FK_C1A6F6F9A4CA6408` FOREIGN KEY (`x_custid`) REFERENCES `cust` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `physicalinterface` +-- + +DROP TABLE IF EXISTS `physicalinterface`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `physicalinterface` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `switchportid` int(11) DEFAULT NULL, + `virtualinterfaceid` int(11) DEFAULT NULL, + `status` int(11) DEFAULT NULL, + `speed` int(11) DEFAULT NULL, + `duplex` varchar(16) COLLATE utf8_unicode_ci DEFAULT NULL, + `notes` longtext COLLATE utf8_unicode_ci, + `fanout_physical_interface_id` int(11) DEFAULT NULL, + `autoneg` tinyint(1) NOT NULL DEFAULT '1', + PRIMARY KEY (`id`), + UNIQUE KEY `UNIQ_5FFF4D60E5F6FACB` (`switchportid`), + UNIQUE KEY `UNIQ_5FFF4D602E68AB8C` (`fanout_physical_interface_id`), + KEY `IDX_5FFF4D60BFDF15D5` (`virtualinterfaceid`), + CONSTRAINT `FK_5FFF4D602E68AB8C` FOREIGN KEY (`fanout_physical_interface_id`) REFERENCES `physicalinterface` (`id`), + CONSTRAINT `FK_5FFF4D60BFDF15D5` FOREIGN KEY (`virtualinterfaceid`) REFERENCES `virtualinterface` (`id`) ON DELETE CASCADE, + CONSTRAINT `FK_5FFF4D60E5F6FACB` FOREIGN KEY (`switchportid`) REFERENCES `switchport` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `routers` +-- + +DROP TABLE IF EXISTS `routers`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `routers` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `vlan_id` int(11) NOT NULL, + `handle` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `protocol` smallint(5) unsigned NOT NULL, + `type` smallint(5) unsigned NOT NULL, + `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `shortname` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `router_id` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `peering_ip` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `asn` int(10) unsigned NOT NULL, + `software` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `mgmt_host` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `api` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `api_type` smallint(5) unsigned NOT NULL, + `lg_access` smallint(5) unsigned DEFAULT NULL, + `quarantine` tinyint(1) NOT NULL, + `bgp_lc` tinyint(1) NOT NULL, + `template` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `skip_md5` tinyint(1) NOT NULL, + `last_updated` datetime DEFAULT NULL, + `rpki` tinyint(1) NOT NULL DEFAULT '0', + `software_version` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `operating_system` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `operating_system_version` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `rfc1997_passthru` tinyint(1) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + UNIQUE KEY `UNIQ_504FC9BE918020D9` (`handle`), + KEY `IDX_504FC9BE8B4937A1` (`vlan_id`), + CONSTRAINT `FK_504FC9BE8B4937A1` FOREIGN KEY (`vlan_id`) REFERENCES `vlan` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `rs_prefixes` +-- + +DROP TABLE IF EXISTS `rs_prefixes`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `rs_prefixes` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `custid` int(11) DEFAULT NULL, + `timestamp` datetime DEFAULT NULL, + `prefix` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL, + `protocol` int(11) DEFAULT NULL, + `irrdb` int(11) DEFAULT NULL, + `rs_origin` int(11) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `IDX_29FA9871DA0209B9` (`custid`), + CONSTRAINT `FK_29FA9871DA0209B9` FOREIGN KEY (`custid`) REFERENCES `cust` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `sessions` +-- + +DROP TABLE IF EXISTS `sessions`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `sessions` ( + `id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + `user_id` bigint(20) DEFAULT NULL, + `ip_address` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `user_agent` longtext COLLATE utf8mb4_unicode_ci, + `payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL, + `last_activity` int(11) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `sessions_id_unique` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `sflow_receiver` +-- + +DROP TABLE IF EXISTS `sflow_receiver`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `sflow_receiver` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `virtual_interface_id` int(11) DEFAULT NULL, + `dst_ip` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `dst_port` int(11) NOT NULL, + PRIMARY KEY (`id`), + KEY `IDX_E633EA142C0D6F5F` (`virtual_interface_id`), + CONSTRAINT `FK_E633EA142C0D6F5F` FOREIGN KEY (`virtual_interface_id`) REFERENCES `virtualinterface` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `switch` +-- + +DROP TABLE IF EXISTS `switch`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `switch` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `cabinetid` int(11) DEFAULT NULL, + `vendorid` int(11) DEFAULT NULL, + `name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `ipv4addr` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `ipv6addr` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `snmppasswd` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `infrastructure` int(11) DEFAULT NULL, + `model` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `active` tinyint(1) DEFAULT '1', + `notes` longtext COLLATE utf8_unicode_ci, + `hostname` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `os` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `osDate` datetime DEFAULT NULL, + `osVersion` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `serialNumber` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `lastPolled` datetime DEFAULT NULL, + `mauSupported` tinyint(1) DEFAULT NULL, + `asn` int(10) unsigned DEFAULT NULL, + `loopback_ip` varchar(39) COLLATE utf8_unicode_ci DEFAULT NULL, + `loopback_name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `mgmt_mac_address` varchar(12) COLLATE utf8_unicode_ci DEFAULT NULL, + `snmp_engine_time` bigint(20) DEFAULT NULL, + `snmp_system_uptime` bigint(20) DEFAULT NULL, + `snmp_engine_boots` bigint(20) DEFAULT NULL, + `poll` tinyint(1) NOT NULL DEFAULT '1', + PRIMARY KEY (`id`), + UNIQUE KEY `UNIQ_6FE94B185E237E06` (`name`), + UNIQUE KEY `UNIQ_6FE94B1850C101F8` (`loopback_ip`), + KEY `IDX_6FE94B182B96718A` (`cabinetid`), + KEY `IDX_6FE94B18420FB55F` (`vendorid`), + KEY `IDX_6FE94B18D129B190` (`infrastructure`), + CONSTRAINT `FK_6FE94B182B96718A` FOREIGN KEY (`cabinetid`) REFERENCES `cabinet` (`id`), + CONSTRAINT `FK_6FE94B18420FB55F` FOREIGN KEY (`vendorid`) REFERENCES `vendor` (`id`), + CONSTRAINT `FK_6FE94B18D129B190` FOREIGN KEY (`infrastructure`) REFERENCES `infrastructure` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `switchport` +-- + +DROP TABLE IF EXISTS `switchport`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `switchport` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `switchid` int(11) DEFAULT NULL, + `type` int(11) DEFAULT NULL, + `name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `ifName` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `ifAlias` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `ifHighSpeed` int(11) DEFAULT NULL, + `ifMtu` int(11) DEFAULT NULL, + `ifPhysAddress` varchar(17) COLLATE utf8_unicode_ci DEFAULT NULL, + `ifAdminStatus` int(11) DEFAULT NULL, + `ifOperStatus` int(11) DEFAULT NULL, + `ifLastChange` int(11) DEFAULT NULL, + `lastSnmpPoll` datetime DEFAULT NULL, + `ifIndex` int(11) DEFAULT NULL, + `active` tinyint(1) NOT NULL DEFAULT '1', + `lagIfIndex` int(11) DEFAULT NULL, + `mauType` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `mauState` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `mauAvailability` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `mauJacktype` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `mauAutoNegSupported` tinyint(1) DEFAULT NULL, + `mauAutoNegAdminState` tinyint(1) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `IDX_F84274F1DC2C08F8` (`switchid`), + CONSTRAINT `FK_F84274F1DC2C08F8` FOREIGN KEY (`switchid`) REFERENCES `switch` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `telescope_entries` +-- + +DROP TABLE IF EXISTS `telescope_entries`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `telescope_entries` ( + `sequence` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `uuid` char(36) COLLATE utf8mb4_unicode_ci NOT NULL, + `batch_id` char(36) COLLATE utf8mb4_unicode_ci NOT NULL, + `family_hash` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `should_display_on_index` tinyint(1) NOT NULL DEFAULT '1', + `type` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL, + `content` longtext COLLATE utf8mb4_unicode_ci NOT NULL, + `created_at` datetime DEFAULT NULL, + PRIMARY KEY (`sequence`), + UNIQUE KEY `telescope_entries_uuid_unique` (`uuid`), + KEY `telescope_entries_batch_id_index` (`batch_id`), + KEY `telescope_entries_type_should_display_on_index_index` (`type`,`should_display_on_index`), + KEY `telescope_entries_family_hash_index` (`family_hash`(191)) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `telescope_entries_tags` +-- + +DROP TABLE IF EXISTS `telescope_entries_tags`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `telescope_entries_tags` ( + `entry_uuid` char(36) COLLATE utf8mb4_unicode_ci NOT NULL, + `tag` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + KEY `telescope_entries_tags_entry_uuid_tag_index` (`entry_uuid`,`tag`(191)), + KEY `telescope_entries_tags_tag_index` (`tag`(191)), + CONSTRAINT `telescope_entries_tags_entry_uuid_foreign` FOREIGN KEY (`entry_uuid`) REFERENCES `telescope_entries` (`uuid`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `telescope_monitoring` +-- + +DROP TABLE IF EXISTS `telescope_monitoring`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `telescope_monitoring` ( + `tag` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `traffic_95th` +-- + +DROP TABLE IF EXISTS `traffic_95th`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `traffic_95th` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `cust_id` int(11) DEFAULT NULL, + `datetime` datetime DEFAULT NULL, + `average` bigint(20) DEFAULT NULL, + `max` bigint(20) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `IDX_70BB409ABFF2A482` (`cust_id`), + CONSTRAINT `FK_70BB409ABFF2A482` FOREIGN KEY (`cust_id`) REFERENCES `cust` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `traffic_95th_monthly` +-- + +DROP TABLE IF EXISTS `traffic_95th_monthly`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `traffic_95th_monthly` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `cust_id` int(11) DEFAULT NULL, + `month` date DEFAULT NULL, + `max_95th` bigint(20) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `IDX_ED79F9DCBFF2A482` (`cust_id`), + CONSTRAINT `FK_ED79F9DCBFF2A482` FOREIGN KEY (`cust_id`) REFERENCES `cust` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `traffic_daily` +-- + +DROP TABLE IF EXISTS `traffic_daily`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `traffic_daily` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `cust_id` int(11) NOT NULL, + `day` date DEFAULT NULL, + `category` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL, + `day_avg_in` bigint(20) DEFAULT NULL, + `day_avg_out` bigint(20) DEFAULT NULL, + `day_max_in` bigint(20) DEFAULT NULL, + `day_max_out` bigint(20) DEFAULT NULL, + `day_tot_in` bigint(20) DEFAULT NULL, + `day_tot_out` bigint(20) DEFAULT NULL, + `week_avg_in` bigint(20) DEFAULT NULL, + `week_avg_out` bigint(20) DEFAULT NULL, + `week_max_in` bigint(20) DEFAULT NULL, + `week_max_out` bigint(20) DEFAULT NULL, + `week_tot_in` bigint(20) DEFAULT NULL, + `week_tot_out` bigint(20) DEFAULT NULL, + `month_avg_in` bigint(20) DEFAULT NULL, + `month_avg_out` bigint(20) DEFAULT NULL, + `month_max_in` bigint(20) DEFAULT NULL, + `month_max_out` bigint(20) DEFAULT NULL, + `month_tot_in` bigint(20) DEFAULT NULL, + `month_tot_out` bigint(20) DEFAULT NULL, + `year_avg_in` bigint(20) DEFAULT NULL, + `year_avg_out` bigint(20) DEFAULT NULL, + `year_max_in` bigint(20) DEFAULT NULL, + `year_max_out` bigint(20) DEFAULT NULL, + `year_tot_in` bigint(20) DEFAULT NULL, + `year_tot_out` bigint(20) DEFAULT NULL, + `ixp_id` int(11) NOT NULL, + PRIMARY KEY (`id`), + KEY `IDX_1F0F81A7BFF2A482` (`cust_id`), + KEY `IDX_1F0F81A7A5A4E881` (`ixp_id`), + CONSTRAINT `FK_1F0F81A7A5A4E881` FOREIGN KEY (`ixp_id`) REFERENCES `ixp` (`id`), + CONSTRAINT `FK_1F0F81A7BFF2A482` FOREIGN KEY (`cust_id`) REFERENCES `cust` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `traffic_daily_phys_ints` +-- + +DROP TABLE IF EXISTS `traffic_daily_phys_ints`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `traffic_daily_phys_ints` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `physicalinterface_id` int(11) NOT NULL, + `day` date DEFAULT NULL, + `category` varchar(10) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `day_avg_in` bigint(20) DEFAULT NULL, + `day_avg_out` bigint(20) DEFAULT NULL, + `day_max_in` bigint(20) DEFAULT NULL, + `day_max_out` bigint(20) DEFAULT NULL, + `day_max_in_at` datetime DEFAULT NULL, + `day_max_out_at` datetime DEFAULT NULL, + `day_tot_in` bigint(20) DEFAULT NULL, + `day_tot_out` bigint(20) DEFAULT NULL, + `week_avg_in` bigint(20) DEFAULT NULL, + `week_avg_out` bigint(20) DEFAULT NULL, + `week_max_in` bigint(20) DEFAULT NULL, + `week_max_out` bigint(20) DEFAULT NULL, + `week_max_in_at` datetime DEFAULT NULL, + `week_max_out_at` datetime DEFAULT NULL, + `week_tot_in` bigint(20) DEFAULT NULL, + `week_tot_out` bigint(20) DEFAULT NULL, + `month_avg_in` bigint(20) DEFAULT NULL, + `month_avg_out` bigint(20) DEFAULT NULL, + `month_max_in` bigint(20) DEFAULT NULL, + `month_max_out` bigint(20) DEFAULT NULL, + `month_max_in_at` datetime DEFAULT NULL, + `month_max_out_at` datetime DEFAULT NULL, + `month_tot_in` bigint(20) DEFAULT NULL, + `month_tot_out` bigint(20) DEFAULT NULL, + `year_avg_in` bigint(20) DEFAULT NULL, + `year_avg_out` bigint(20) DEFAULT NULL, + `year_max_in` bigint(20) DEFAULT NULL, + `year_max_out` bigint(20) DEFAULT NULL, + `year_max_in_at` datetime DEFAULT NULL, + `year_max_out_at` datetime DEFAULT NULL, + `year_tot_in` bigint(20) DEFAULT NULL, + `year_tot_out` bigint(20) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `IDX_E219461D4643D08A` (`physicalinterface_id`), + CONSTRAINT `FK_E219461D4643D08A` FOREIGN KEY (`physicalinterface_id`) REFERENCES `physicalinterface` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `user` +-- + +DROP TABLE IF EXISTS `user`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `user` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `custid` int(11) DEFAULT NULL, + `username` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `password` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `email` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `authorisedMobile` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL, + `uid` int(11) DEFAULT NULL, + `privs` int(11) DEFAULT NULL, + `disabled` tinyint(1) DEFAULT NULL, + `lastupdated` datetime DEFAULT NULL, + `lastupdatedby` int(11) DEFAULT NULL, + `creator` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `created` datetime DEFAULT NULL, + `name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `peeringdb_id` bigint(20) DEFAULT NULL, + `extra_attributes` json DEFAULT NULL COMMENT '(DC2Type:json)', + PRIMARY KEY (`id`), + UNIQUE KEY `UNIQ_8D93D649F85E0677` (`username`), + UNIQUE KEY `UNIQ_8D93D649F2C6186B` (`peeringdb_id`), + KEY `IDX_8D93D649DA0209B9` (`custid`), + CONSTRAINT `FK_8D93D649DA0209B9` FOREIGN KEY (`custid`) REFERENCES `cust` (`id`) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `user_2fa` +-- + +DROP TABLE IF EXISTS `user_2fa`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `user_2fa` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `user_id` int(11) NOT NULL, + `enabled` tinyint(1) NOT NULL DEFAULT '0', + `secret` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `created_at` datetime DEFAULT NULL, + `updated_at` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `UNIQ_3AAA1488A76ED395` (`user_id`), + CONSTRAINT `FK_3AAA1488A76ED395` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `user_logins` +-- + +DROP TABLE IF EXISTS `user_logins`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `user_logins` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `user_id` int(11) DEFAULT NULL, + `ip` varchar(39) COLLATE utf8_unicode_ci NOT NULL, + `at` datetime NOT NULL, + `customer_to_user_id` int(11) DEFAULT NULL, + `via` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `IDX_6341CC99D43FEAE2` (`customer_to_user_id`), + KEY `at_idx` (`at`), + KEY `user_id_idx` (`user_id`), + CONSTRAINT `FK_6341CC99D43FEAE2` FOREIGN KEY (`customer_to_user_id`) REFERENCES `customer_to_users` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `user_pref` +-- + +DROP TABLE IF EXISTS `user_pref`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `user_pref` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `user_id` int(11) DEFAULT NULL, + `attribute` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `ix` int(11) NOT NULL DEFAULT '0', + `op` varchar(2) COLLATE utf8_unicode_ci DEFAULT NULL, + `value` longtext COLLATE utf8_unicode_ci, + `expire` bigint(20) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + UNIQUE KEY `IX_UserPreference_1` (`user_id`,`attribute`,`op`,`ix`), + KEY `IDX_DBD4D4F8A76ED395` (`user_id`), + CONSTRAINT `FK_DBD4D4F8A76ED395` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `user_remember_tokens` +-- + +DROP TABLE IF EXISTS `user_remember_tokens`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `user_remember_tokens` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `user_id` int(11) NOT NULL, + `token` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + `device` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + `ip` varchar(39) COLLATE utf8mb4_unicode_ci NOT NULL, + `created` datetime NOT NULL, + `expires` datetime NOT NULL, + `is_2fa_complete` tinyint(1) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + UNIQUE KEY `user_token` (`user_id`,`token`), + KEY `IDX_E253302EA76ED395` (`user_id`), + CONSTRAINT `FK_E253302EA76ED395` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `vendor` +-- + +DROP TABLE IF EXISTS `vendor`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `vendor` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `shortname` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `nagios_name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `bundle_name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + + +-- +-- Table structure for table `virtualinterface` +-- + +DROP TABLE IF EXISTS `virtualinterface`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `virtualinterface` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `custid` int(11) DEFAULT NULL, + `name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `description` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `mtu` int(11) DEFAULT NULL, + `trunk` tinyint(1) DEFAULT NULL, + `channelgroup` int(11) DEFAULT NULL, + `lag_framing` tinyint(1) NOT NULL DEFAULT '0', + `fastlacp` tinyint(1) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `IDX_11D9014FDA0209B9` (`custid`), + CONSTRAINT `FK_11D9014FDA0209B9` FOREIGN KEY (`custid`) REFERENCES `cust` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `vlan` +-- + +DROP TABLE IF EXISTS `vlan`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `vlan` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `number` int(11) DEFAULT NULL, + `notes` longtext COLLATE utf8_unicode_ci, + `private` tinyint(1) NOT NULL DEFAULT '0', + `infrastructureid` int(11) NOT NULL, + `peering_matrix` tinyint(1) NOT NULL DEFAULT '0', + `peering_manager` tinyint(1) NOT NULL DEFAULT '0', + `config_name` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `infra_config_name` (`infrastructureid`,`config_name`), + KEY `IDX_F83104A1721EBF79` (`infrastructureid`), + CONSTRAINT `FK_F83104A1721EBF79` FOREIGN KEY (`infrastructureid`) REFERENCES `infrastructure` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `vlaninterface` +-- + +DROP TABLE IF EXISTS `vlaninterface`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `vlaninterface` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `ipv4addressid` int(11) DEFAULT NULL, + `ipv6addressid` int(11) DEFAULT NULL, + `virtualinterfaceid` int(11) DEFAULT NULL, + `vlanid` int(11) DEFAULT NULL, + `ipv4enabled` tinyint(1) DEFAULT '0', + `ipv4hostname` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `ipv6enabled` tinyint(1) DEFAULT '0', + `ipv6hostname` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `mcastenabled` tinyint(1) DEFAULT '0', + `irrdbfilter` tinyint(1) DEFAULT '1', + `bgpmd5secret` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `ipv4bgpmd5secret` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `ipv6bgpmd5secret` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `maxbgpprefix` int(11) DEFAULT NULL, + `rsclient` tinyint(1) DEFAULT NULL, + `ipv4canping` tinyint(1) DEFAULT NULL, + `ipv6canping` tinyint(1) DEFAULT NULL, + `ipv4monitorrcbgp` tinyint(1) DEFAULT NULL, + `ipv6monitorrcbgp` tinyint(1) DEFAULT NULL, + `as112client` tinyint(1) DEFAULT NULL, + `busyhost` tinyint(1) DEFAULT NULL, + `notes` longtext COLLATE utf8_unicode_ci, + `rsmorespecifics` tinyint(1) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + UNIQUE KEY `UNIQ_B4B4411A73720641` (`ipv4addressid`), + UNIQUE KEY `UNIQ_B4B4411A7787D67C` (`ipv6addressid`), + KEY `IDX_B4B4411ABFDF15D5` (`virtualinterfaceid`), + KEY `IDX_B4B4411AF48D6D0` (`vlanid`), + CONSTRAINT `FK_B4B4411A73720641` FOREIGN KEY (`ipv4addressid`) REFERENCES `ipv4address` (`id`), + CONSTRAINT `FK_B4B4411A7787D67C` FOREIGN KEY (`ipv6addressid`) REFERENCES `ipv6address` (`id`), + CONSTRAINT `FK_B4B4411ABFDF15D5` FOREIGN KEY (`virtualinterfaceid`) REFERENCES `virtualinterface` (`id`) ON DELETE CASCADE, + CONSTRAINT `FK_B4B4411AF48D6D0` FOREIGN KEY (`vlanid`) REFERENCES `vlan` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2021-03-16 14:38:53 diff --git a/data/schemas/IXP-Manager-Schema-20121212.pdf b/database/schema/IXP-Manager-Schema-20121212.pdf similarity index 100% rename from data/schemas/IXP-Manager-Schema-20121212.pdf rename to database/schema/IXP-Manager-Schema-20121212.pdf diff --git a/database/schema/IXP-Manager-Schema-20171027.pdf b/database/schema/IXP-Manager-Schema-20171027.pdf new file mode 100644 index 000000000..525efc971 Binary files /dev/null and b/database/schema/IXP-Manager-Schema-20171027.pdf differ diff --git a/application/views/_skins/.hellogit b/database/seeders/.hellogit similarity index 100% rename from application/views/_skins/.hellogit rename to database/seeders/.hellogit diff --git a/database/seeders/ContactGroups.php b/database/seeders/ContactGroups.php new file mode 100644 index 000000000..57b16b752 --- /dev/null +++ b/database/seeders/ContactGroups.php @@ -0,0 +1,62 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Database\Seeds + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class ContactGroups extends Seeder +{ + /** + * Run the database seeds. + * + * @return void + */ + public function run(): void + { + $contactRoles = [ 'Billing', 'Technical', 'Admin', 'Marketing' ]; + + foreach( $contactRoles as $name ) + { + ContactGroup::create([ + 'name' => $name, + 'description' => sprintf( "Contact role for %s matters", strtolower( $name ) ), + 'type' => ContactGroup::TYPE_ROLE, + 'active' => true, + 'limited_to' => 0, + ]); + } + } +} diff --git a/database/seeders/IRRDBs.php b/database/seeders/IRRDBs.php new file mode 100644 index 000000000..537b70204 --- /dev/null +++ b/database/seeders/IRRDBs.php @@ -0,0 +1,125 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Database\Seeds + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class IRRDBs extends Seeder +{ + /** + * Run the database seeds. + * + * @return void + */ + public function run(): void + { + $irrdbs = [ + [ + 'host' => 'whois.radb.net', + 'source' => 'RIPE', + 'notes' => 'RIPE Query from RIPE Database' + ], + [ + 'host' => 'whois.radb.net', + 'source' => 'RIPE,RIPE-NONAUTH', + 'notes' => 'RIPE+RIPE-NONAUTH Query from RIPE Database' + ], + [ + 'host' => 'whois.radb.net', + 'source' => 'RADB', + 'notes' => 'RADB Query from RADB Database' + ], + [ + 'host' => 'whois.radb.net', + 'source' => 'LACNIC', + 'notes' => 'LACNIC Query from LACNIC Database' + ], + [ + 'host' => 'whois.radb.net', + 'source' => 'AFRINIC', + 'notes' => 'AFRINIC Query from AFRINIC Database' + ], + [ + 'host' => 'whois.radb.net', + 'source' => 'APNIC', + 'notes' => 'APNIC Query from APNIC Database' + ], + [ + 'host' => 'whois.radb.net', + 'source' => 'LEVEL3', + 'notes' => 'Level3 Query from Level3 Database' + ], + [ + 'host' => 'whois.radb.net', + 'source' => 'ARIN', + 'notes' => 'ARIN Query from RADB Database' + ], + [ + 'host' => 'whois.radb.net', + 'source' => 'RADB,ARIN', + 'notes' => 'RADB+ARIN Query from RADB Database' + ], + [ + 'host' => 'whois.radb.net', + 'source' => 'ALTDB', + 'notes' => 'ALTDB Query from RADB Database' + ], + [ + 'host' => 'whois.radb.net', + 'source' => 'RADB,RIPE', + 'notes' => 'RADB+RIPE Query from RADB Database' + ], + [ + 'host' => 'whois.radb.net', + 'source' => 'RADB,APNIC,ARIN', + 'notes' => 'RADB+APNIC+ARIN Query from RADB Database' + ], + [ + 'host' => 'whois.radb.net', + 'source' => 'RIPE,ARIN', + 'notes' => 'RIPE+ARIN Query from RADB Database' + ] + ]; + + foreach( $irrdbs as $irrdb ) { + IrrdbConfig::create([ + 'host' => $irrdb['host'], + 'source' => $irrdb['source'], + 'notes' => $irrdb['notes'], + ]); + } + } +} \ No newline at end of file diff --git a/database/seeders/Vendors.php b/database/seeders/Vendors.php new file mode 100644 index 000000000..5a7e82fc5 --- /dev/null +++ b/database/seeders/Vendors.php @@ -0,0 +1,78 @@ + + * @author Yann Robin + * @category IXP + * @package IXP\Database\Seeds + * @copyright Copyright (C) 2009 - 2021 Internet Neutral Exchange Association Company Limited By Guarantee + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 + */ +class Vendors extends Seeder +{ + /** + * Run the database seeds. + * + * @return void + */ + public function run(): void + { + $vendors = [ + [ "Allied Telesyn", 'AlliedTel', 'alliedtel' ], + [ "Allied Telesis", 'AlliedTelesis', 'alliedtelesis' ], + [ "Arista", 'Arista', 'arista', 'Port-channel' ], + [ "Brocade", 'Brocade', 'brocade' ], + [ "Cisco Systems", 'Cisco', 'cisco', 'Port-channel' ], + [ "Cumulus Networks", 'Cumulus', 'cumulus', 'bond' ], + [ "Dell", 'Dell', 'dell' ], + [ "Enterasys", 'Enterasys', 'enterasys' ], + [ "Extreme Networks", 'Extreme', 'extreme' ], + [ "Force10 Networks", 'Force10', 'force10' ], + [ "Foundry Networks", 'Brocade', 'brocade' ], + [ "Glimmerglass", 'Glimmerglass', 'glimmerglass' ], + [ "Hewlett-Packard", 'HP', 'hp' ], + [ "Hitachi Cable", 'Hitachi', 'hitachi' ], + [ "Juniper Networks", 'Juniper', 'juniper' ], + [ "Linux", 'Linux', 'linux' ], + [ "MRV", 'MRV', 'mrv' ], + [ "Transmode", 'Transmode', 'transmode' ], + ]; + + foreach( $vendors as $vendor ) { + Vendor::create([ + 'name' => $vendor[0], + 'shortname' => $vendor[1], + 'nagios_name' => $vendor[2], + 'bundle_name' => $vendor[3] ?? null, + ]); + } + } +} diff --git a/doc/barry.txt b/doc/barry.txt deleted file mode 100644 index de5f15e4a..000000000 --- a/doc/barry.txt +++ /dev/null @@ -1,4 +0,0 @@ - -mysqldump --single-transaction --no-create-info --complete-insert ---skip-comments --disable-keys -u root inex | mysql -u root inex2 - diff --git a/doctrine/schema/Entities.BGPSessionData.dcm.xml b/doctrine/schema/Entities.BGPSessionData.dcm.xml deleted file mode 100644 index 688e1c59c..000000000 --- a/doctrine/schema/Entities.BGPSessionData.dcm.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/doctrine/schema/Entities.Cabinet.dcm.xml b/doctrine/schema/Entities.Cabinet.dcm.xml deleted file mode 100644 index 0e7596ea5..000000000 --- a/doctrine/schema/Entities.Cabinet.dcm.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/doctrine/schema/Entities.ChangeLog.dcm.xml b/doctrine/schema/Entities.ChangeLog.dcm.xml deleted file mode 100644 index ab4474da7..000000000 --- a/doctrine/schema/Entities.ChangeLog.dcm.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/doctrine/schema/Entities.ConsoleServerConnection.dcm.xml b/doctrine/schema/Entities.ConsoleServerConnection.dcm.xml deleted file mode 100644 index 29405a0ba..000000000 --- a/doctrine/schema/Entities.ConsoleServerConnection.dcm.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doctrine/schema/Entities.Contact.dcm.xml b/doctrine/schema/Entities.Contact.dcm.xml deleted file mode 100644 index 15577788c..000000000 --- a/doctrine/schema/Entities.Contact.dcm.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doctrine/schema/Entities.Customer.dcm.xml b/doctrine/schema/Entities.Customer.dcm.xml deleted file mode 100644 index 270281c11..000000000 --- a/doctrine/schema/Entities.Customer.dcm.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doctrine/schema/Entities.CustomerEquipment.dcm.xml b/doctrine/schema/Entities.CustomerEquipment.dcm.xml deleted file mode 100644 index f1e01c8cf..000000000 --- a/doctrine/schema/Entities.CustomerEquipment.dcm.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/doctrine/schema/Entities.IPv4Address.dcm.xml b/doctrine/schema/Entities.IPv4Address.dcm.xml deleted file mode 100644 index a2bc6bd01..000000000 --- a/doctrine/schema/Entities.IPv4Address.dcm.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/doctrine/schema/Entities.IPv6Address.dcm.xml b/doctrine/schema/Entities.IPv6Address.dcm.xml deleted file mode 100644 index 92066de3f..000000000 --- a/doctrine/schema/Entities.IPv6Address.dcm.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/doctrine/schema/Entities.IRRDBConfig.dcm.xml b/doctrine/schema/Entities.IRRDBConfig.dcm.xml deleted file mode 100644 index e53cadde1..000000000 --- a/doctrine/schema/Entities.IRRDBConfig.dcm.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/doctrine/schema/Entities.Location.dcm.xml b/doctrine/schema/Entities.Location.dcm.xml deleted file mode 100644 index da09f2fd2..000000000 --- a/doctrine/schema/Entities.Location.dcm.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/doctrine/schema/Entities.MACAddress.dcm.xml b/doctrine/schema/Entities.MACAddress.dcm.xml deleted file mode 100644 index ba3720e89..000000000 --- a/doctrine/schema/Entities.MACAddress.dcm.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/doctrine/schema/Entities.Meeting.dcm.xml b/doctrine/schema/Entities.Meeting.dcm.xml deleted file mode 100644 index 547dcf378..000000000 --- a/doctrine/schema/Entities.Meeting.dcm.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doctrine/schema/Entities.MeetingItem.dcm.xml b/doctrine/schema/Entities.MeetingItem.dcm.xml deleted file mode 100644 index 39c138ce4..000000000 --- a/doctrine/schema/Entities.MeetingItem.dcm.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doctrine/schema/Entities.NetworkInfo.dcm.xml b/doctrine/schema/Entities.NetworkInfo.dcm.xml deleted file mode 100644 index 80313c315..000000000 --- a/doctrine/schema/Entities.NetworkInfo.dcm.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/doctrine/schema/Entities.PeeringManager.dcm.xml b/doctrine/schema/Entities.PeeringManager.dcm.xml deleted file mode 100644 index 147714c4d..000000000 --- a/doctrine/schema/Entities.PeeringManager.dcm.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doctrine/schema/Entities.PeeringMatrix.dcm.xml b/doctrine/schema/Entities.PeeringMatrix.dcm.xml deleted file mode 100644 index 0ad9cb54b..000000000 --- a/doctrine/schema/Entities.PeeringMatrix.dcm.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doctrine/schema/Entities.PhysicalInterface.dcm.xml b/doctrine/schema/Entities.PhysicalInterface.dcm.xml deleted file mode 100644 index 835c154ed..000000000 --- a/doctrine/schema/Entities.PhysicalInterface.dcm.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doctrine/schema/Entities.RSDroppedPrefix.dcm.xml b/doctrine/schema/Entities.RSDroppedPrefix.dcm.xml deleted file mode 100644 index 7be0699ca..000000000 --- a/doctrine/schema/Entities.RSDroppedPrefix.dcm.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/doctrine/schema/Entities.SecEvent.dcm.xml b/doctrine/schema/Entities.SecEvent.dcm.xml deleted file mode 100644 index f584b899e..000000000 --- a/doctrine/schema/Entities.SecEvent.dcm.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doctrine/schema/Entities.SwitchPort.dcm.xml b/doctrine/schema/Entities.SwitchPort.dcm.xml deleted file mode 100644 index 361746088..000000000 --- a/doctrine/schema/Entities.SwitchPort.dcm.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/doctrine/schema/Entities.Switcher.dcm.xml b/doctrine/schema/Entities.Switcher.dcm.xml deleted file mode 100644 index 2bd963e0f..000000000 --- a/doctrine/schema/Entities.Switcher.dcm.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doctrine/schema/Entities.Traffic95th.dcm.xml b/doctrine/schema/Entities.Traffic95th.dcm.xml deleted file mode 100644 index 686a2e779..000000000 --- a/doctrine/schema/Entities.Traffic95th.dcm.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/doctrine/schema/Entities.Traffic95thMonthly.dcm.xml b/doctrine/schema/Entities.Traffic95thMonthly.dcm.xml deleted file mode 100644 index 18295f87d..000000000 --- a/doctrine/schema/Entities.Traffic95thMonthly.dcm.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/doctrine/schema/Entities.TrafficDaily.dcm.xml b/doctrine/schema/Entities.TrafficDaily.dcm.xml deleted file mode 100644 index 7e9e450d0..000000000 --- a/doctrine/schema/Entities.TrafficDaily.dcm.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doctrine/schema/Entities.User.dcm.xml b/doctrine/schema/Entities.User.dcm.xml deleted file mode 100644 index 4f375cd96..000000000 --- a/doctrine/schema/Entities.User.dcm.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doctrine/schema/Entities.UserPreference.dcm.xml b/doctrine/schema/Entities.UserPreference.dcm.xml deleted file mode 100644 index b8beea8b5..000000000 --- a/doctrine/schema/Entities.UserPreference.dcm.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/doctrine/schema/Entities.Vendor.dcm.xml b/doctrine/schema/Entities.Vendor.dcm.xml deleted file mode 100644 index 95548cc07..000000000 --- a/doctrine/schema/Entities.Vendor.dcm.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/doctrine/schema/Entities.VirtualInterface.dcm.xml b/doctrine/schema/Entities.VirtualInterface.dcm.xml deleted file mode 100644 index e1379f4f6..000000000 --- a/doctrine/schema/Entities.VirtualInterface.dcm.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/doctrine/schema/Entities.Vlan.dcm.xml b/doctrine/schema/Entities.Vlan.dcm.xml deleted file mode 100644 index efea68440..000000000 --- a/doctrine/schema/Entities.Vlan.dcm.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/doctrine/schema/Entities.VlanInterface.dcm.xml b/doctrine/schema/Entities.VlanInterface.dcm.xml deleted file mode 100644 index 19419a48e..000000000 --- a/doctrine/schema/Entities.VlanInterface.dcm.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lang/en/auth.php b/lang/en/auth.php new file mode 100644 index 000000000..6598e2c06 --- /dev/null +++ b/lang/en/auth.php @@ -0,0 +1,20 @@ + 'These credentials do not match our records.', + 'password' => 'The provided password is incorrect.', + 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.', + +]; diff --git a/lang/en/pagination.php b/lang/en/pagination.php new file mode 100644 index 000000000..d48141187 --- /dev/null +++ b/lang/en/pagination.php @@ -0,0 +1,19 @@ + '« Previous', + 'next' => 'Next »', + +]; diff --git a/lang/en/passwords.php b/lang/en/passwords.php new file mode 100644 index 000000000..2345a56b5 --- /dev/null +++ b/lang/en/passwords.php @@ -0,0 +1,22 @@ + 'Your password has been reset!', + 'sent' => 'We have emailed your password reset link!', + 'throttled' => 'Please wait before retrying.', + 'token' => 'This password reset token is invalid.', + 'user' => "We can't find a user with that email address.", + +]; diff --git a/lang/en/validation.php b/lang/en/validation.php new file mode 100644 index 000000000..70407c9d9 --- /dev/null +++ b/lang/en/validation.php @@ -0,0 +1,184 @@ + 'The :attribute must be accepted.', + 'accepted_if' => 'The :attribute must be accepted when :other is :value.', + 'active_url' => 'The :attribute is not a valid URL.', + 'after' => 'The :attribute must be a date after :date.', + 'after_or_equal' => 'The :attribute must be a date after or equal to :date.', + 'alpha' => 'The :attribute must only contain letters.', + 'alpha_dash' => 'The :attribute must only contain letters, numbers, dashes and underscores.', + 'alpha_num' => 'The :attribute must only contain letters and numbers.', + 'array' => 'The :attribute must be an array.', + 'ascii' => 'The :attribute must only contain single-byte alphanumeric characters and symbols.', + 'before' => 'The :attribute must be a date before :date.', + 'before_or_equal' => 'The :attribute must be a date before or equal to :date.', + 'between' => [ + 'array' => 'The :attribute must have between :min and :max items.', + 'file' => 'The :attribute must be between :min and :max kilobytes.', + 'numeric' => 'The :attribute must be between :min and :max.', + 'string' => 'The :attribute must be between :min and :max characters.', + ], + 'boolean' => 'The :attribute field must be true or false.', + 'confirmed' => 'The :attribute confirmation does not match.', + 'current_password' => 'The password is incorrect.', + 'date' => 'The :attribute is not a valid date.', + 'date_equals' => 'The :attribute must be a date equal to :date.', + 'date_format' => 'The :attribute does not match the format :format.', + 'decimal' => 'The :attribute must have :decimal decimal places.', + 'declined' => 'The :attribute must be declined.', + 'declined_if' => 'The :attribute must be declined when :other is :value.', + 'different' => 'The :attribute and :other must be different.', + 'digits' => 'The :attribute must be :digits digits.', + 'digits_between' => 'The :attribute must be between :min and :max digits.', + 'dimensions' => 'The :attribute has invalid image dimensions.', + 'distinct' => 'The :attribute field has a duplicate value.', + 'doesnt_end_with' => 'The :attribute may not end with one of the following: :values.', + 'doesnt_start_with' => 'The :attribute may not start with one of the following: :values.', + 'email' => 'The :attribute must be a valid email address.', + 'ends_with' => 'The :attribute must end with one of the following: :values.', + 'enum' => 'The selected :attribute is invalid.', + 'exists' => 'The selected :attribute is invalid.', + 'file' => 'The :attribute must be a file.', + 'filled' => 'The :attribute field must have a value.', + 'gt' => [ + 'array' => 'The :attribute must have more than :value items.', + 'file' => 'The :attribute must be greater than :value kilobytes.', + 'numeric' => 'The :attribute must be greater than :value.', + 'string' => 'The :attribute must be greater than :value characters.', + ], + 'gte' => [ + 'array' => 'The :attribute must have :value items or more.', + 'file' => 'The :attribute must be greater than or equal to :value kilobytes.', + 'numeric' => 'The :attribute must be greater than or equal to :value.', + 'string' => 'The :attribute must be greater than or equal to :value characters.', + ], + 'image' => 'The :attribute must be an image.', + 'in' => 'The selected :attribute is invalid.', + 'in_array' => 'The :attribute field does not exist in :other.', + 'integer' => 'The :attribute must be an integer.', + 'ip' => 'The :attribute must be a valid IP address.', + 'ipv4' => 'The :attribute must be a valid IPv4 address.', + 'ipv6' => 'The :attribute must be a valid IPv6 address.', + 'json' => 'The :attribute must be a valid JSON string.', + 'lowercase' => 'The :attribute must be lowercase.', + 'lt' => [ + 'array' => 'The :attribute must have less than :value items.', + 'file' => 'The :attribute must be less than :value kilobytes.', + 'numeric' => 'The :attribute must be less than :value.', + 'string' => 'The :attribute must be less than :value characters.', + ], + 'lte' => [ + 'array' => 'The :attribute must not have more than :value items.', + 'file' => 'The :attribute must be less than or equal to :value kilobytes.', + 'numeric' => 'The :attribute must be less than or equal to :value.', + 'string' => 'The :attribute must be less than or equal to :value characters.', + ], + 'mac_address' => 'The :attribute must be a valid MAC address.', + 'max' => [ + 'array' => 'The :attribute must not have more than :max items.', + 'file' => 'The :attribute must not be greater than :max kilobytes.', + 'numeric' => 'The :attribute must not be greater than :max.', + 'string' => 'The :attribute must not be greater than :max characters.', + ], + 'max_digits' => 'The :attribute must not have more than :max digits.', + 'mimes' => 'The :attribute must be a file of type: :values.', + 'mimetypes' => 'The :attribute must be a file of type: :values.', + 'min' => [ + 'array' => 'The :attribute must have at least :min items.', + 'file' => 'The :attribute must be at least :min kilobytes.', + 'numeric' => 'The :attribute must be at least :min.', + 'string' => 'The :attribute must be at least :min characters.', + ], + 'min_digits' => 'The :attribute must have at least :min digits.', + 'missing' => 'The :attribute field must be missing.', + 'missing_if' => 'The :attribute field must be missing when :other is :value.', + 'missing_unless' => 'The :attribute field must be missing unless :other is :value.', + 'missing_with' => 'The :attribute field must be missing when :values is present.', + 'missing_with_all' => 'The :attribute field must be missing when :values are present.', + 'multiple_of' => 'The :attribute must be a multiple of :value.', + 'not_in' => 'The selected :attribute is invalid.', + 'not_regex' => 'The :attribute format is invalid.', + 'numeric' => 'The :attribute must be a number.', + 'password' => [ + 'letters' => 'The :attribute must contain at least one letter.', + 'mixed' => 'The :attribute must contain at least one uppercase and one lowercase letter.', + 'numbers' => 'The :attribute must contain at least one number.', + 'symbols' => 'The :attribute must contain at least one symbol.', + 'uncompromised' => 'The given :attribute has appeared in a data leak. Please choose a different :attribute.', + ], + 'present' => 'The :attribute field must be present.', + 'prohibited' => 'The :attribute field is prohibited.', + 'prohibited_if' => 'The :attribute field is prohibited when :other is :value.', + 'prohibited_unless' => 'The :attribute field is prohibited unless :other is in :values.', + 'prohibits' => 'The :attribute field prohibits :other from being present.', + 'regex' => 'The :attribute format is invalid.', + 'required' => 'The :attribute field is required.', + 'required_array_keys' => 'The :attribute field must contain entries for: :values.', + 'required_if' => 'The :attribute field is required when :other is :value.', + 'required_if_accepted' => 'The :attribute field is required when :other is accepted.', + 'required_unless' => 'The :attribute field is required unless :other is in :values.', + 'required_with' => 'The :attribute field is required when :values is present.', + 'required_with_all' => 'The :attribute field is required when :values are present.', + 'required_without' => 'The :attribute field is required when :values is not present.', + 'required_without_all' => 'The :attribute field is required when none of :values are present.', + 'same' => 'The :attribute and :other must match.', + 'size' => [ + 'array' => 'The :attribute must contain :size items.', + 'file' => 'The :attribute must be :size kilobytes.', + 'numeric' => 'The :attribute must be :size.', + 'string' => 'The :attribute must be :size characters.', + ], + 'starts_with' => 'The :attribute must start with one of the following: :values.', + 'string' => 'The :attribute must be a string.', + 'timezone' => 'The :attribute must be a valid timezone.', + 'unique' => 'The :attribute has already been taken.', + 'uploaded' => 'The :attribute failed to upload.', + 'uppercase' => 'The :attribute must be uppercase.', + 'url' => 'The :attribute must be a valid URL.', + 'ulid' => 'The :attribute must be a valid ULID.', + 'uuid' => 'The :attribute must be a valid UUID.', + + /* + |-------------------------------------------------------------------------- + | Custom Validation Language Lines + |-------------------------------------------------------------------------- + | + | Here you may specify custom validation messages for attributes using the + | convention "attribute.rule" to name the lines. This makes it quick to + | specify a specific custom language line for a given attribute rule. + | + */ + + 'custom' => [ + 'attribute-name' => [ + 'rule-name' => 'custom-message', + ], + ], + + /* + |-------------------------------------------------------------------------- + | Custom Validation Attributes + |-------------------------------------------------------------------------- + | + | The following language lines are used to swap our attribute placeholder + | with something more reader friendly such as "E-Mail Address" instead + | of "email". This simply helps us make our message more expressive. + | + */ + + 'attributes' => [], + +]; diff --git a/library/INEX/Controller/Action.php b/library/INEX/Controller/Action.php deleted file mode 100644 index b6d2276ef..000000000 --- a/library/INEX/Controller/Action.php +++ /dev/null @@ -1,144 +0,0 @@ - cust.name for super users - * @var array - */ - protected $_customers = null; - - /** - * Override the Zend_Controller_Action's constructor (which is called - * at the very beginning of this function anyway). - * - * @param object $request See Parent class constructer - * @param object $response See Parent class constructer - * @param object $invokeArgs See Parent class constructer - */ - public function __construct( - Zend_Controller_Request_Abstract $request, - Zend_Controller_Response_Abstract $response, - array $invokeArgs = null ) - { - // call the parent's version where all the Zend magic happens - parent::__construct( $request, $response, $invokeArgs ); - - // we need this for access to class constants in the template - $this->view->registerClass( 'USER', '\\Entities\\User' ); - $this->view->registerClass( 'CUSTOMER', '\\Entities\\Customer' ); - - if( $this->getAuth()->hasIdentity() && $this->getUser()->getPrivs() == Entities\User::AUTH_SUPERUSER ) - $this->superUserSetup(); - } - - /** - * Get the customer object - * - * @return Entities\Customer The customer object for the current user - */ - protected function getCustomer() - { - return $this->getUser()->getCustomer(); - } - - - /** - * Assertion function for ensuring user permissions. - * - * This redirects to an insufficient perms page and issues a log if the - * assertion fails. - * - * @param $priv int The \Entities\User::AUTH_XXX permission to ensure the user has - * @param $exact bool If true, match the permission exactly rather than 'at least' - * @return bool True if okay, redirects on insufficient permissions - */ - protected function assertPrivilege( $priv, $exact = true ) - { - if( !$this->getAuth()->hasIdentity() ) - $this->redirectAndEnsureDie( 'auth/login' ); - - if( ( $exact && $this->getUser()->getPrivs() != $priv ) || ( !$exact && $this->getUser()->getPrivs() < $priv ) ) - { - $this->getLogger()->notice( "{$this->getUser()->getUsername()} illegally tried to access {$this->getRequest()->getRequestUri()}" ); - $this->redirectAndEnsureDie( 'error/insufficient-permissions' ); - } - - return true; - } - - /** - * Perform some setup functions for super users - * - */ - private function superUserSetup() - { - // get an array of customer id => names - if( !( $this->_customers = $this->getD2Cache()->fetch( 'admin_home_customers' ) ) ) - { - $this->_customers = $this->getD2EM()->getRepository( 'Entities\\Customer' )->getNames(); - $this->getD2Cache()->save( 'admin_home_customers', $this->_customers, 3600 ); - } - - $this->view->customers = $this->_customers; - } - -} - diff --git a/library/INEX/Controller/AuthRequiredAction.php b/library/INEX/Controller/AuthRequiredAction.php deleted file mode 100644 index 20b5fb398..000000000 --- a/library/INEX/Controller/AuthRequiredAction.php +++ /dev/null @@ -1,66 +0,0 @@ -getParam( 'shortname', false ); - - if( $shortname ) - $c = $this->getD2EM()->getRepository( '\\Entities\\Customer' )->findOneBy( [ 'shortname' => $shortname ] ); - - if( !$shortname || !$c ) - { - $this->addMessage( 'Invalid customer', OSS_Message::ERROR ); - $this->redirect( $redirect === null ? 'error/error' : $redirect ); - } - - return $c; - } - -} - diff --git a/library/INEX/Controller/FrontEnd.php b/library/INEX/Controller/FrontEnd.php deleted file mode 100644 index 2899c23f9..000000000 --- a/library/INEX/Controller/FrontEnd.php +++ /dev/null @@ -1,38 +0,0 @@ - \ No newline at end of file diff --git a/library/INEX/Controller/Router/Cli.php b/library/INEX/Controller/Router/Cli.php deleted file mode 100644 index 6cdfe5827..000000000 --- a/library/INEX/Controller/Router/Cli.php +++ /dev/null @@ -1,42 +0,0 @@ -setAttrib( 'accept-charset', 'UTF-8' ); - $this->setMethod( 'post' ); - $this->setAttrib( "horizontal", true ); - - $this->onEditSkipIfBlank = array(); - - $this->addElementPrefixPath( 'OSS_Filter', 'OSS/Filter/', 'filter' ); - $this->addElementPrefixPath( 'OSS_Validate', 'OSS/Validate/', 'validate' ); - - if( method_exists( $this, 'initialiseTraits' ) ) - $this->initialiseTraits( $options ); - - parent::__construct( $options ); - } - -} - diff --git a/library/INEX/Form/AddAddresses.php b/library/INEX/Form/AddAddresses.php deleted file mode 100644 index 2948fcb3c..000000000 --- a/library/INEX/Form/AddAddresses.php +++ /dev/null @@ -1,52 +0,0 @@ - - * @category INEX - * @package INEX_Form - * @copyright Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd - * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 - */ -class INEX_Form_AddAddresses extends INEX_Form -{ - public function init() - { - $this->setDecorators( [ [ 'ViewScript', [ 'viewScript' => 'ipv4-address/forms/add-addresses.phtml' ] ] ] ); - - $this->addElement( INEX_Form_Vlan::getPopulatedSelect( 'vlanid' ) ); - - - $type = $this->createElement( 'select', 'type' ); - $type->setMultiOptions( [ 'IPv4' => 'IPv4', 'IPv6' => 'IPv6' ] ) - ->setRegisterInArrayValidator( true ) - ->setValue( 'IPv6' ) - ->setAttrib( 'class', 'span3 chzn-select' ) - ->setLabel( 'Address Family' ); - $this->addElement( $type ); - } -} - diff --git a/library/INEX/Form/Auth/Login.php b/library/INEX/Form/Auth/Login.php deleted file mode 100644 index 9437b8a35..000000000 --- a/library/INEX/Form/Auth/Login.php +++ /dev/null @@ -1,48 +0,0 @@ - - * @category INEX - * @package INEX_Form - * @copyright Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Limited - * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2 - */ -class INEX_Form_Auth_Login extends INEX_Form -{ - - public function init() - { - $this->setAttrib( 'id', 'auth_login' ) - ->setAttrib( 'name', 'auth_login' ); - - $this->addElement( OSS_Form_Auth::createUsernameElement() ); - $this->addElement( OSS_Form_Auth::createPasswordElement() ); - //$this->addElement( OSS_Form_Auth::createRememberMeElement() ); - $this->addElement( OSS_Form::createSubmitElement( 'submit', _( 'Login' ) ) ); - $this->addElement( OSS_Form_Auth::createLostPasswordElement() ); - $this->addElement( OSS_Form_Auth::createLostUsernameElement() ); - } - -} diff --git a/library/INEX/Form/Auth/LostPassword.php b/library/INEX/Form/Auth/LostPassword.php deleted file mode 100644 index a35b43d64..000000000 --- a/library/INEX/Form/Auth/LostPassword.php +++ /dev/null @@ -1,45 +0,0 @@ - - * @category INEX - * @package INEX_Form - * @copyright Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Limited - * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2 - */ -class INEX_Form_Auth_LostPassword extends INEX_Form -{ - - public function init() - { - $this->setAttrib( 'id', 'auth_lost_password' ) - ->setAttrib( 'name', 'auth_lost_password' ); - - $this->addElement( OSS_Form_Auth::createUsernameElement() ); - $this->addElement( OSS_Form::createSubmitElement( 'submit', _( 'Reset Password' ) ) ); - $this->addElement( OSS_Form_Auth::createReturnToLoginElement() ); - } - -} diff --git a/library/INEX/Form/Auth/LostUsername.php b/library/INEX/Form/Auth/LostUsername.php deleted file mode 100644 index 8ef1000b6..000000000 --- a/library/INEX/Form/Auth/LostUsername.php +++ /dev/null @@ -1,45 +0,0 @@ - - * @category INEX - * @package INEX_Form - * @copyright Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Limited - * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2 - */ -class INEX_Form_Auth_LostUsername extends INEX_Form -{ - - public function init() - { - $this->setAttrib( 'id', 'auth_lost_username' ) - ->setAttrib( 'name', 'auth_lost_username' ); - - $this->addElement( OSS_Form_User::createEmailElement() ); - $this->addElement( OSS_Form::createSubmitElement( 'submit', _( 'Find Username(s)' ) ) ); - $this->addElement( OSS_Form_Auth::createReturnToLoginElement() ); - } - -} diff --git a/library/INEX/Form/Auth/ResetPassword.php b/library/INEX/Form/Auth/ResetPassword.php deleted file mode 100644 index e55d9e3f1..000000000 --- a/library/INEX/Form/Auth/ResetPassword.php +++ /dev/null @@ -1,48 +0,0 @@ - - * @category INEX - * @package INEX_Form - * @copyright Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Limited - * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2 - */ -class INEX_Form_Auth_ResetPassword extends INEX_Form -{ - - public function init() - { - $this->setAttrib( 'id', 'auth_reset_password' ) - ->setAttrib( 'name', 'auth_reset_password' ); - - $this->addElement( OSS_Form_Auth::createUsernameElement() ); - $this->addElement( OSS_Form_Auth::createPasswordResetTokenElement() ); - $this->addElement( OSS_Form_Auth::createPasswordElement() ); - $this->addElement( OSS_Form_Auth::createPasswordConfirmElement() ); - $this->addElement( OSS_Form::createSubmitElement( 'submit', _( 'Reset Password' ) ) ); - $this->addElement( OSS_Form_Auth::createReturnToLoginElement() ); - } - -} diff --git a/library/INEX/Form/Cabinet.php b/library/INEX/Form/Cabinet.php deleted file mode 100644 index 64a9cc5ba..000000000 --- a/library/INEX/Form/Cabinet.php +++ /dev/null @@ -1,114 +0,0 @@ - - * @category INEX - * @package INEX_Form - * @copyright Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd - * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 - */ -class INEX_Form_Cabinet extends INEX_Form -{ - public function init() - { - $name = $this->createElement( 'text', 'name' ); - $name->addValidator( 'stringLength', false, array( 1, 255 ) ) - ->setRequired( true ) - ->setAttrib( 'class', 'span3' ) - ->setLabel( 'Name' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $name ); - - - $this->addElement( INEX_Form_Location::getPopulatedSelect( 'locationid' ) ); - - - $cololocation = $this->createElement( 'text', 'cololocation' ); - $cololocation->addValidator( 'stringLength', false, array( 1, 255 ) ) - ->setAttrib( 'class', 'span3' ) - ->setRequired( true ) - ->setLabel( 'Colo Location' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $cololocation ); - - - $type = $this->createElement( 'text', 'type' ); - $type->addValidator( 'stringLength', false, array( 1, 255 ) ) - ->setAttrib( 'class', 'span3' ) - ->setRequired( true ) - ->setLabel( 'Type' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $type ); - - $height = $this->createElement( 'text', 'height' ); - $height->addValidator( 'between', false, array( 0, 100 ) ) - ->setAttrib( 'class', 'span3' ) - ->setLabel( 'Height (U)' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $height ); - - $notes = $this->createElement( 'textarea', 'notes' ); - $notes->setLabel( 'Notes' ) - ->setRequired( false ) - ->addFilter( new OSS_Filter_StripSlashes() ) - ->setAttrib( 'cols', 60 ) - ->setAttrib( 'class', 'span3' ) - ->setAttrib( 'rows', 5 ); - $this->addElement( $notes ); - - - $this->addElement( self::createSubmitElement( 'submit', _( 'Add' ) ) ); - $this->addElement( $this->createCancelElement() ); - } - - - /** - * Create a SELECT / dropdown element of all cabinet names indexed by their id. - * - * @param string $name The element name - * @return Zend_Form_Element_Select The select element - */ - public static function getPopulatedSelect( $name = 'cabinetid' ) - { - $cab = new Zend_Form_Element_Select( $name ); - - $maxId = self::populateSelectFromDatabase( $cab, '\\Entities\\Cabinet', 'id', 'name', 'name', 'ASC' ); - - $cab->setRegisterInArrayValidator( true ) - ->setRequired( true ) - ->setLabel( _( 'Cabinet' ) ) - ->setAttrib( 'class', 'span3 chzn-select' ) - ->addValidator( 'between', false, array( 1, $maxId ) ) - ->setErrorMessages( array( _( 'Please select a cabinet' ) ) ); - - return $cab; - } -} - diff --git a/library/INEX/Form/ChangePassword.php b/library/INEX/Form/ChangePassword.php deleted file mode 100644 index e0c04b479..000000000 --- a/library/INEX/Form/ChangePassword.php +++ /dev/null @@ -1,62 +0,0 @@ - - * @category INEX - * @package INEX_Form - * @copyright Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd - * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 - */ -class INEX_Form_ChangePassword extends INEX_Form -{ - public function init() - { - $this->setAttrib( 'id', 'change_password' ) - ->setAttrib( 'name', 'change_password' ) - ->setAction( OSS_Utils::genUrl( 'profile', 'change-password' ) ); - - $this->addElement( - OSS_Form_Auth::createPasswordElement( 'current_password' ) - ->setLabel( _( 'Current Password' ) ) - ->setAttrib( 'class', 'span6' ) - ); - - $this->addElement( - OSS_Form_Auth::createPasswordElement( 'new_password' ) - ->setLabel( _( 'New Password' ) ) - ->setAttrib( 'class', 'span6' ) - ); - - $this->addElement( - OSS_Form_Auth::createPasswordConfirmElement( 'confirm_password', 'new_password' ) - ->setAttrib( 'class', 'span6' ) - ); - - $this->addElement( OSS_Form::createSubmitElement( 'submit', _( 'Change Password' ) ) ); - } -} - diff --git a/library/INEX/Form/ConsoleServerConnection.php b/library/INEX/Form/ConsoleServerConnection.php deleted file mode 100644 index ea8ae8ebd..000000000 --- a/library/INEX/Form/ConsoleServerConnection.php +++ /dev/null @@ -1,114 +0,0 @@ - - * @category INEX - * @package INEX_Form - * @copyright Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd - * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 - */ -class INEX_Form_ConsoleServerConnection extends INEX_Form -{ - public function init() - { - $description = $this->createElement( 'text', 'description' ); - $description->addValidator( 'stringLength', false, array( 1, 255 ) ) - ->setRequired( true ) - ->setAttrib( 'class', 'span3' ) - ->setLabel( 'Description' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $description ); - - - $this->addElement( INEX_Form_Customer::getPopulatedSelect( 'custid' ) ); - $this->addElement( INEX_Form_Switch::getPopulatedSelect( 'switchid', \Entities\Switcher::TYPE_CONSOLESERVER ) ); - - $port = $this->createElement( 'text', 'port' ); - $port->addValidator( 'stringLength', false, array( 1, 255 ) ) - ->setRequired( true ) - ->setLabel( 'Port' ) - ->setAttrib( 'class', 'span3' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $port ); - - $speed = $this->createElement( 'text', 'speed' ); - $speed->addValidator( 'int' ) - ->setLabel( 'Speed' ) - ->addFilter( 'StringTrim' ) - ->setAttrib( 'class', 'span3' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $speed ); - - $parity = $this->createElement( 'text', 'parity' ); - $parity->addValidator( 'int' ) - ->setLabel( 'Parity' ) - ->setAttrib( 'class', 'span3' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $parity ); - - $stopbits = $this->createElement( 'text', 'stopbits' ); - $stopbits->addValidator( 'int' ) - ->setLabel( 'Stopbits' ) - ->setAttrib( 'class', 'span3' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $stopbits ); - - $flowcontrol = $this->createElement( 'text', 'flowcontrol' ); - $flowcontrol->addValidator( 'int' ) - ->setLabel( 'Flow Control' ) - ->setAttrib( 'class', 'span3' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $flowcontrol ); - - $autobaud = $this->createElement( 'text', 'autobaud' ); - $autobaud->addValidator( 'int' ) - ->setAttrib( 'class', 'span3' ) - ->setLabel( 'Autobaud' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $autobaud ); - - $notes = $this->createElement( 'textarea', 'notes' ); - $notes->setLabel( 'Notes' ) - ->setRequired( false ) - ->addFilter( new OSS_Filter_StripSlashes() ) - ->setAttrib( 'cols', 60 ) - ->setAttrib( 'class', 'span3' ) - ->setAttrib( 'rows', 5 ); - $this->addElement( $notes ); - - - $this->addElement( self::createSubmitElement( 'submit', _( 'Add' ) ) ); - $this->addElement( $this->createCancelElement() ); - } - -} - diff --git a/library/INEX/Form/Contact.php b/library/INEX/Form/Contact.php deleted file mode 100644 index ada5a4631..000000000 --- a/library/INEX/Form/Contact.php +++ /dev/null @@ -1,93 +0,0 @@ - - * @category INEX - * @package INEX_Form - * @copyright Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd - * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 - */ -class INEX_Form_Contact extends INEX_Form -{ - public function init() - { - - $name = $this->createElement( 'text', 'name' ); - $name->addValidator( 'stringLength', false, array( 1, 255 ) ) - ->setRequired( true ) - ->setLabel( 'Name' ) - ->setAttrib( 'class', 'span3' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - - $this->addElement( $name ); - - $this->addElement( INEX_Form_Customer::getPopulatedSelect( 'custid' ) ); - - $this->addElement( OSS_Form_User::createEmailElement( 'email' ) ); - - $phone = $this->createElement( 'text', 'phone' ); - $phone->addValidator( 'stringLength', false, array( 1, 32 ) ) - ->setLabel( _( 'Phone' ) ) - ->setAttrib( 'class', 'span3' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $phone ); - - - - $mobile = $this->createElement( 'text', 'mobile' ); - $mobile->addValidator( 'stringLength', false, array( 1, 32 ) ) - ->setLabel( _( 'Mobile' ) ) - ->addFilter( 'StringTrim' ) - ->setAttrib( 'class', 'span3' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - - $this->addElement( $mobile ); - - - - - - $facilityaccess = $this->createElement( 'checkbox', 'facilityaccess' ); - $facilityaccess->setLabel( 'Facility Access' ) - ->setCheckedValue( '1' ); - $this->addElement( $facilityaccess ); - - - - - $mayauthorize = $this->createElement( 'checkbox', 'mayauthorize' ); - $mayauthorize->setLabel( 'May Authorize' ) - ->setCheckedValue( '1' ); - $this->addElement( $mayauthorize ); - - - $this->addElement( self::createSubmitElement( 'submit', _( 'Add' ) ) ); - $this->addElement( $this->createCancelElement() ); - } - -} diff --git a/library/INEX/Form/CustKit.php b/library/INEX/Form/CustKit.php deleted file mode 100644 index d7c094fda..000000000 --- a/library/INEX/Form/CustKit.php +++ /dev/null @@ -1,67 +0,0 @@ - - * @category INEX - * @package INEX_Form - * @copyright Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd - * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 - */ -class INEX_Form_CustKit extends INEX_Form -{ - public function init() - { - $name = $this->createElement( 'text', 'name' ); - $name->addValidator( 'stringLength', false, array( 1, 255 ) ) - ->setRequired( true ) - ->setLabel( 'Name' ) - ->setAttrib( 'class', 'span3' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $name ); - - - $this->addElement( INEX_Form_Customer::getPopulatedSelect( 'custid' ) ); - $this->addElement( INEX_Form_Cabinet::getPopulatedSelect( 'cabinetid' ) ); - - - $descr = $this->createElement( 'textarea', 'descr' ); - $descr->setLabel( _( 'Description' ) ) - ->setRequired( false ) - ->addFilter( new OSS_Filter_StripSlashes() ) - ->setAttrib( 'cols', 60 ) - ->setAttrib( 'class', 'span3' ) - ->setAttrib( 'rows', 5 ); - $this->addElement( $descr ); - - - $this->addElement( self::createSubmitElement( 'submit', _( 'Add' ) ) ); - $this->addElement( $this->createCancelElement() ); - } - -} - diff --git a/library/INEX/Form/Customer.php b/library/INEX/Form/Customer.php deleted file mode 100644 index a9737707b..000000000 --- a/library/INEX/Form/Customer.php +++ /dev/null @@ -1,337 +0,0 @@ - - * @category INEX - * @package INEX_Form - * @copyright Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd - * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 - */ -class INEX_Form_Customer extends INEX_Form -{ - public function init() - { - $this->setDecorators( [ [ 'ViewScript', [ 'viewScript' => 'customer/forms/edit.phtml' ] ] ] ); - - $name = $this->createElement( 'text', 'name' ); - $name->addValidator( 'stringLength', false, array( 1, 255 ) ) - ->setRequired( true ) - ->setLabel( 'Name' ) - ->setAttrib( 'class', 'span6' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $name ); - - $type = $this->createElement( 'select', 'type' ); - $type->setMultiOptions( [ '0' => '' ] + \Entities\Customer::$CUST_TYPES_TEXT ) - ->setRegisterInArrayValidator( true ) - ->setLabel( 'Type' ) - ->setAttrib( 'class', 'chzn-select span6' ) - ->setErrorMessages( array( 'Please select a customer type' ) ); - $this->addElement( $type ); - - $shortname = $this->createElement( 'text', 'shortname' ); - $shortname->addValidator( 'stringLength', false, array( 1, 255 ) ) - ->addValidator( 'alnum' ) - ->addValidator( 'regex', false, array('/^[a-z0-9]+/' ) ) - ->setRequired( true ) - ->setAttrib( 'class', 'span4' ) - ->setLabel( 'Short Name' ) - ->addFilter( 'StringToLower' ) - ->addFilter( 'StringTrim' ); - $this->addElement( $shortname ); - - - $corpwww = $this->createElement( 'text', 'corpwww' ); - $corpwww->addValidator( 'stringLength', false, array( 0, 255 ) ) - ->setRequired( false ) - ->setAttrib( 'placeholder', 'http://www.example.com/' ) - ->setAttrib( 'class', 'span6' ) - ->setLabel( 'Corporate Website' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $corpwww ); - - $datejoin = $this->createElement( 'text', 'datejoin' ); - $datejoin->addValidator( 'stringLength', false, array( 10, 10 ) ) - ->addValidator( 'regex', false, array('/^\d\d\d\d-\d\d-\d\d/' ) ) - ->setRequired( false ) - ->setLabel( 'Date Joined' ) - ->setAttrib( 'placeholder', 'YYYY-MM-DD' ) - ->setAttrib( 'class', 'span4' ) - ->addFilter( 'StringTrim' ) - ->setAttrib( 'id', 'datejoin' ); - $this->addElement( $datejoin ); - - $dateleave = $this->createElement( 'text', 'dateleave' ); - $dateleave->addValidator( 'stringLength', false, array( 10, 10 ) ) - ->addValidator( 'regex', false, array('/^\d\d\d\d-\d\d-\d\d/' ) ) - ->setRequired( false ) - ->setAttrib( 'placeholder', 'YYYY-MM-DD' ) - ->setAttrib( 'class', 'span4' ) - ->setLabel( 'Date Left' ) - ->addFilter( 'StringTrim' ); - $this->addElement( $dateleave ); - - $status = $this->createElement( 'select', 'status' ); - $status->setMultiOptions( [ '0' => '' ] + \Entities\Customer::$CUST_STATUS_TEXT ) - ->setRegisterInArrayValidator( true ) - ->setLabel( 'Status' ) - ->setRequired( true ) - ->setAttrib( 'class', 'chzn-select span6' ) - ->setErrorMessages( array( 'Please set the customer\'s status' ) ); - $this->addElement( $status ); - - - $autsys = $this->createElement( 'text', 'autsys' ); - $autsys->addValidator('int') - ->addValidator( 'greaterThan', false, array( -1 ) ) - ->setRequired( false ) - ->setAttrib( 'class', 'span4' ) - ->setLabel( 'AS Number' ); - $this->addElement( $autsys ); - - $maxprefixes = $this->createElement( 'text', 'maxprefixes' ); - $maxprefixes->addValidator('int') - ->addValidator( 'greaterThan', false, array( -1 ) ) - ->setRequired( false ) - ->setAttrib( 'class', 'span2' ) - ->setLabel( 'Max Prefixes' ); - $this->addElement( $maxprefixes ); - - $peeringemail = $this->createElement( 'text', 'peeringemail' ); - $peeringemail->addValidator('emailAddress' ) - ->addValidator( 'stringLength', false, array( 0, 64 ) ) - ->setRequired( false ) - ->setAttrib( 'class', 'span6' ) - ->setAttrib( 'placeholder', 'peering@example.com' ) - ->setLabel( 'Email' ); - $this->addElement( $peeringemail ); - - $peeringpolicy = $this->createElement( 'select', 'peeringpolicy' ); - $peeringpolicy->setMultiOptions( [ 0 => '' ] + \Entities\Customer::$PEERING_POLICIES ) - ->setRegisterInArrayValidator( true ) - ->setLabel( 'Peering Policy' ) - ->setRequired( false ) - ->setAttrib( 'class', 'chzn-select span6' ); - - $this->addElement( $peeringpolicy ); - - - $peeringmacro = $this->createElement( 'text', 'peeringmacro' ); - $peeringmacro->addValidator( 'stringLength', false, array( 0, 255 ) ) - ->setRequired( false ) - ->setLabel( 'Peering Macro' ) - ->addFilter( 'StringTrim' ) - ->setAttrib( 'class', 'span4' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $peeringmacro ); - - - $this->addElement( INEX_Form_IrrdbConfig::getPopulatedSelect() ); - - - - $activepeeringmatrix = $this->createElement( 'checkbox', 'activepeeringmatrix' ); - $activepeeringmatrix->setLabel( 'Active Peering Matrix' ) - ->setCheckedValue( '1' ); - $this->addElement( $activepeeringmatrix ); - - - $this->addDisplayGroup( - [ 'autsys', 'maxprefixes', 'peeringemail', 'peeringmacro', 'peeringpolicy', 'irrdb', 'activepeeringmatrix' ], - 'peeringDisplayGroup' - ); - $this->getDisplayGroup( 'peeringDisplayGroup' )->setLegend( 'Peering Details' ); - - - - $nocphone = $this->createElement( 'text', 'nocphone' ); - $nocphone->addValidator( 'stringLength', false, array( 0, 255 ) ) - ->setRequired( false ) - ->setLabel( 'Phone' ) - ->setAttrib( 'placeholder', '+353 1 123 4567' ) - ->setAttrib( 'class', 'span4' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $nocphone ); - - $noc24hphone = $this->createElement( 'text', 'noc24hphone' ); - $noc24hphone->addValidator( 'stringLength', false, array( 0, 255 ) ) - ->setRequired( false ) - ->setAttrib( 'placeholder', '+353 86 876 5432' ) - ->setAttrib( 'class', 'span4' ) - ->setLabel( '24h Phone' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $noc24hphone ); - - $nocfax = $this->createElement( 'text', 'nocfax' ); - $nocfax->addValidator( 'stringLength', false, array( 0, 40 ) ) - ->setRequired( false ) - ->setLabel( 'Fax' ) - ->setAttrib( 'placeholder', '+353 1 765 4321' ) - ->setAttrib( 'class', 'span4' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $nocfax ); - - $nocemail = $this->createElement( 'text', 'nocemail' ); - $nocemail->addValidator('emailAddress' ) - ->addValidator( 'stringLength', false, array( 0, 40 ) ) - ->setRequired( false ) - ->setAttrib( 'class', 'span6' ) - ->setAttrib( 'placeholder', 'noc@example.com' ) - ->setLabel( 'E-Mail' ); - $this->addElement( $nocemail ); - - $nochours = $this->createElement( 'select', 'nochours' ); - $nochours->setMultiOptions( [ '0' => '' ] + \Entities\Customer::$NOC_HOURS ) - ->setRegisterInArrayValidator( true ) - ->setLabel( 'Hours' ) - ->setRequired( false ) - ->setAttrib( 'class', 'chzn-select span6' ); - $this->addElement( $nochours ); - - - $nocwww = $this->createElement( 'text', 'nocwww' ); - $nocwww->addValidator( 'stringLength', false, array( 0, 255 ) ) - ->setRequired( false ) - ->setLabel( 'Website' ) - ->setAttrib( 'placeholder', 'http://www.noc.example.com/' ) - ->setAttrib( 'class', 'span6' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $nocwww ); - - $this->addDisplayGroup( - array( 'nocphone', 'noc24hphone', 'nocfax', 'nocemail', 'nochours', 'nocwww' ), - 'nocDisplayGroup' - ); - $this->getDisplayGroup( 'nocDisplayGroup' )->setLegend( 'NOC Details' ); - - - - - $billingContact = $this->createElement( 'text', 'billingContact' ); - $billingContact->addValidator( 'stringLength', false, array( 0, 64 ) ) - ->setRequired( false ) - ->setLabel( 'Contact' ) - ->addFilter( 'StringTrim' ) - ->setAttrib( 'class', 'span6' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $billingContact ); - - $billingAddress1 = $this->createElement( 'text', 'billingAddress1' ); - $billingAddress1->addValidator( 'stringLength', false, array( 0, 64 ) ) - ->setRequired( false ) - ->setLabel( 'Address' ) - ->setAttrib( 'class', 'span6' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $billingAddress1 ); - - $billingAddress2 = $this->createElement( 'text', 'billingAddress2' ); - $billingAddress2->addValidator( 'stringLength', false, array( 0, 64 ) ) - ->setRequired( false ) - ->setAttrib( 'class', 'span6' ) - ->setLabel( '' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $billingAddress2 ); - - $billingCity = $this->createElement( 'text', 'billingCity' ); - $billingCity->addValidator( 'stringLength', false, array( 0, 64 ) ) - ->setRequired( false ) - ->setAttrib( 'class', 'span4' ) - ->setLabel( 'City' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $billingCity ); - - $billingCountry = $this->createElement( 'select', 'billingCountry' ); - $billingCountry->setMultiOptions( OSS_Countries::getCountriesArray() ) - ->setRegisterInArrayValidator( true ) - ->setValue( 'IE' ) - ->setLabel( 'Country' ) - ->setRequired( false ) - ->setAttrib( 'class', 'chzn-select span6' ); - - $this->addElement( $billingCountry ); - - - - - - - $this->addDisplayGroup( - [ 'billingContact', 'billingAddress1', 'billingAddress2', 'billingCity', 'billingCountry' ], - 'billingDisplayGroup' - ); - $this->getDisplayGroup( 'billingDisplayGroup' )->setLegend( 'Billing Details' ); - - - - $notes = $this->createElement( 'textarea', 'notes' ); - $notes->setLabel( 'Notes' ) - ->setAttrib( 'class', 'span3' ) - ->setRequired( false ) - ->addFilter( new OSS_Filter_StripSlashes() ) - ->setAttrib( 'cols', 60 ) - ->setAttrib( 'rows', 5 ); - $this->addElement( $notes ); - - $this->addElement( self::createSubmitElement( 'submit', _( 'Add' ) ) ); - $this->addElement( $this->createCancelElement() ); - } - - - - /** - * Create a SELECT / dropdown element of all customer names indexed by their id. - * - * @param string $name The element name - * @return Zend_Form_Element_Select The select element - */ - public static function getPopulatedSelect( $name = 'custid' ) - { - $cust = new Zend_Form_Element_Select( $name ); - - $maxId = self::populateSelectFromDatabase( $cust, '\\Entities\\Customer', 'id', 'name', 'name', 'ASC' ); - - $cust->setRegisterInArrayValidator( true ) - ->setRequired( true ) - ->setLabel( _( 'Customer' ) ) - ->setAttrib( 'class', 'span3 chzn-select' ) - ->addValidator( 'between', false, array( 1, $maxId ) ) - ->setErrorMessages( array( _( 'Please select a customer' ) ) ); - - return $cust; - } - -} - diff --git a/library/INEX/Form/Customer/BillingDetails.php b/library/INEX/Form/Customer/BillingDetails.php deleted file mode 100644 index 89889c9de..000000000 --- a/library/INEX/Form/Customer/BillingDetails.php +++ /dev/null @@ -1,95 +0,0 @@ - - * @category INEX - * @package INEX_Form - * @copyright Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd - * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 - */ -class INEX_Form_Customer_BillingDetails extends INEX_Form -{ - public function init() - { - $billingContact = $this->createElement( 'text', 'billingContact' ); - $billingContact->addValidator( 'stringLength', false, array( 0, 64 ) ) - ->setRequired( false ) - ->setLabel( 'Contact' ) - ->addFilter( 'StringTrim' ) - ->setAttrib( 'class', 'span6' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $billingContact ); - - $billingAddress1 = $this->createElement( 'text', 'billingAddress1' ); - $billingAddress1->addValidator( 'stringLength', false, array( 0, 64 ) ) - ->setRequired( false ) - ->setLabel( 'Address' ) - ->setAttrib( 'class', 'span6' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $billingAddress1 ); - - $billingAddress2 = $this->createElement( 'text', 'billingAddress2' ); - $billingAddress2->addValidator( 'stringLength', false, array( 0, 64 ) ) - ->setRequired( false ) - ->setAttrib( 'class', 'span6' ) - ->setLabel( '' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $billingAddress2 ); - - $billingCity = $this->createElement( 'text', 'billingCity' ); - $billingCity->addValidator( 'stringLength', false, array( 0, 64 ) ) - ->setRequired( false ) - ->setAttrib( 'class', 'span4' ) - ->setLabel( 'City' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $billingCity ); - - $billingCountry = $this->createElement( 'select', 'billingCountry' ); - $billingCountry->setMultiOptions( [ '' => '' ] + OSS_Countries::getCountriesArray() ) - ->setRegisterInArrayValidator( true ) - ->setValue( 'IE' ) - ->setLabel( 'Country' ) - ->setRequired( false ) - ->setAttrib( 'style', 'width: 150px;' ) - ->setAttrib( 'class', 'chzn-select' ); - - $this->addElement( $billingCountry ); - - - $this->addDisplayGroup( - [ 'billingContact', 'billingAddress1', 'billingAddress2', 'billingCity', 'billingCountry' ], - 'billingDisplayGroup' - ); - $this->getDisplayGroup( 'billingDisplayGroup' )->setLegend( 'Billing Details' ); - - $this->addElement( self::createSubmitElement( 'submit', _( 'Update' ) ) ); - } - -} - diff --git a/library/INEX/Form/Customer/NocDetails.php b/library/INEX/Form/Customer/NocDetails.php deleted file mode 100644 index 250df336c..000000000 --- a/library/INEX/Form/Customer/NocDetails.php +++ /dev/null @@ -1,107 +0,0 @@ - - * @category INEX - * @package INEX_Form - * @copyright Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd - * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 - */ -class INEX_Form_Customer_NocDetails extends INEX_Form -{ - - public function init() - { - $nocphone = $this->createElement( 'text', 'nocphone' ); - $nocphone->addValidator( 'stringLength', false, array( 0, 255 ) ) - ->setRequired( false ) - ->setLabel( 'Phone' ) - ->setAttrib( 'placeholder', '+353 1 123 4567' ) - ->setAttrib( 'class', 'span4' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $nocphone ); - - $noc24hphone = $this->createElement( 'text', 'noc24hphone' ); - $noc24hphone->addValidator( 'stringLength', false, array( 0, 255 ) ) - ->setRequired( false ) - ->setAttrib( 'placeholder', '+353 86 876 5432' ) - ->setAttrib( 'class', 'span4' ) - ->setLabel( '24h Phone' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $noc24hphone ); - - $nocfax = $this->createElement( 'text', 'nocfax' ); - $nocfax->addValidator( 'stringLength', false, array( 0, 40 ) ) - ->setRequired( false ) - ->setLabel( 'Fax' ) - ->setAttrib( 'placeholder', '+353 1 765 4321' ) - ->setAttrib( 'class', 'span4' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $nocfax ); - - $nocemail = $this->createElement( 'text', 'nocemail' ); - $nocemail->addValidator('emailAddress' ) - ->addValidator( 'stringLength', false, array( 0, 40 ) ) - ->setRequired( false ) - ->setAttrib( 'class', 'span6' ) - ->setAttrib( 'placeholder', 'noc@example.com' ) - ->setLabel( 'E-Mail' ); - $this->addElement( $nocemail ); - - $nochours = $this->createElement( 'select', 'nochours' ); - $nochours->setMultiOptions( [ '0' => '' ] + \Entities\Customer::$NOC_HOURS ) - ->setRegisterInArrayValidator( true ) - ->setLabel( 'Hours' ) - ->setRequired( false ) - ->setAttrib( 'class', 'chzn-select span6' ); - $this->addElement( $nochours ); - - - $nocwww = $this->createElement( 'text', 'nocwww' ); - $nocwww->addValidator( 'stringLength', false, array( 0, 255 ) ) - ->setRequired( false ) - ->setLabel( 'Website' ) - ->setAttrib( 'placeholder', 'http://www.noc.example.com/' ) - ->setAttrib( 'class', 'span6' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $nocwww ); - - $this->addDisplayGroup( - array( 'nocphone', 'noc24hphone', 'nocfax', 'nocemail', 'nochours', 'nocwww' ), - 'nocDisplayGroup' - ); - $this->getDisplayGroup( 'nocDisplayGroup' )->setLegend( 'NOC Details' ); - - $this->addElement( self::createSubmitElement( 'submit', _( 'Update' ) ) ); - } - -} - diff --git a/library/INEX/Form/Customer/SendEmail.php b/library/INEX/Form/Customer/SendEmail.php deleted file mode 100644 index 475251459..000000000 --- a/library/INEX/Form/Customer/SendEmail.php +++ /dev/null @@ -1,93 +0,0 @@ - - * @category INEX - * @package INEX_Form - * @copyright Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd - * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 - * - */ -class INEX_Form_Customer_SendEmail extends INEX_Form -{ - - public function init() - { - $this->setDecorators( [ [ 'ViewScript', [ 'viewScript' => 'customer/forms/send-email.phtml' ] ] ] ); - - $to = $this->createElement( 'text', 'to', [ 'size' => 100 ] ); - $to->addValidator( 'stringLength', false, array( 1, 4096 ) ) - ->setRequired( true ) - ->setLabel( 'To' ) - ->setAttrib( 'class', 'span9' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $to ); - - $cc = $this->createElement( 'text', 'cc', [ 'size' => 100 ] ); - $cc->addValidator( 'stringLength', false, array( 1, 4096 ) ) - ->setRequired( false ) - ->setLabel( 'CC' ) - ->setAttrib( 'class', 'span9' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $cc ); - - $bcc = $this->createElement( 'text', 'bcc', [ 'size' => 100 ] ); - $bcc->addValidator( 'stringLength', false, array( 1, 4096 ) ) - ->setRequired( false ) - ->setLabel( 'BCC' ) - ->setAttrib( 'class', 'span9' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $bcc ); - - $subject = $this->createElement( 'text', 'subject', [ 'size' => 100 ] ); - $subject->addValidator( 'stringLength', false, array( 1, 4096 ) ) - ->setRequired( true ) - ->setLabel( 'Subject' ) - ->setAttrib( 'class', 'span9' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $subject ); - - - $message = $this->createElement( 'textarea', 'message', [ 'cols' => 80, 'rows' => 20 ] ); - $message->addValidator( 'stringLength', false, array( 1, 40960 ) ) - ->setRequired( true ) - ->setLabel( 'Message' ) - ->setAttrib( 'class', 'span9' ) - ->setAttrib( 'style', 'font-family: Menlo, Monaco, "Courier New", monospace;' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $message ); - - $this->addElement( self::createSubmitElement( 'submit', _( 'Add' ) ) ); - $this->addElement( $this->createCancelElement() ); - } -} diff --git a/library/INEX/Form/Interface/AddWizard.php b/library/INEX/Form/Interface/AddWizard.php deleted file mode 100644 index ef726671c..000000000 --- a/library/INEX/Form/Interface/AddWizard.php +++ /dev/null @@ -1,304 +0,0 @@ - - * @category INEX - * @package INEX_Form - * @copyright Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd - * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 - */ -class INEX_Form_Interface_AddWizard extends INEX_Form -{ - public function init() - { - $this->setDecorators( [ [ 'ViewScript', [ 'viewScript' => 'virtual-interface/forms/add-wizard.phtml' ] ] ] ); - - $this->addElement( INEX_Form_Customer::getPopulatedSelect( 'custid' ) ); - $this->getElement( 'custid' )->setAttrib( 'class', 'chzn-select span12' ); - - //////////////////////////////////////////////////////////////////////////////////////////////////// - // VIRTUAL INTERFACE DETAILS - - $name = $this->createElement( 'text', 'name' ); - $name->addValidator( 'stringLength', false, array( 0, 255 ) ) - ->setRequired( false ) - ->setLabel( 'Virtual Interface Name' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $name ); - - - $descr = $this->createElement( 'text', 'description' ); - $descr->setLabel( 'Description' ) - ->addValidator( 'stringLength', false, array( 0, 255 ) ) - ->setRequired( false ) - ->addFilter( new OSS_Filter_StripSlashes() ) - ->addFilter( 'StringTrim' ); - $this->addElement( $descr ); - - - $channel = $this->createElement( 'text', 'channelgroup' ); - $channel->addValidator( 'int' ) - ->setLabel( 'Channel Group Number' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $channel ); - - $mtu = $this->createElement( 'text', 'mtu' ); - $mtu->addValidator( 'int' ) - ->setLabel( 'MTU' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $mtu ); - - - $trunk = $this->createElement( 'checkbox', 'trunk' ); - $trunk->setLabel( 'Is 802.1q Trunk?' ) - ->setCheckedValue( '1' ); - $this->addElement( $trunk ); - - $this->addDisplayGroup( - array( 'name', 'description', 'channelgroup', 'mtu', 'trunk' ), - 'virtualInterfaceDisplayGroup' - ); - - $this->getDisplayGroup( 'virtualInterfaceDisplayGroup' )->setLegend( 'Virtual Interface Details' ); - - - ///////////////////////////////////////////////////////////////////////////////////////////////////// - // PHYSICAL INTERFACE DETAILS - - $this->addElement( INEX_Form_Switch::getPopulatedSelect( 'switchid' ) ); - $this->getElement( 'switchid' )->setAttrib( 'class', 'chzn-select span12' ); - - $switchPorts = $this->createElement( 'select', 'switchportid' ); - - $switchPorts->setRequired( true ) - ->setRegisterInArrayValidator( false ) - ->setLabel( 'Port' ) - ->setAttrib( 'class', 'chzn-select span12' ) - ->addValidator( 'greaterThan', false, array( 'min' => 1 ) ) - ->setErrorMessages( array( 'Please select a switch port' ) ); - $this->addElement( $switchPorts ); - - - $status = $this->createElement( 'select', 'status' ); - $status->setMultiOptions( \Entities\PhysicalInterface::$STATES ) - ->setRegisterInArrayValidator( true ) - ->setAttrib( 'class', 'chzn-select span12' ) - ->setLabel( 'Status' ) - ->setErrorMessages( array( 'Please set the status' ) ); - $this->addElement( $status ); - - - $speed = $this->createElement( 'select', 'speed' ); - $speed->setMultiOptions( \Entities\PhysicalInterface::$SPEED ) - ->setRegisterInArrayValidator( true ) - ->setAttrib( 'class', 'chzn-select span12' ) - ->setLabel( 'Speed' ) - ->setErrorMessages( array( 'Please set the speed' ) ); - $this->addElement( $speed ); - - - $duplex = $this->createElement( 'select', 'duplex' ); - $duplex->setMultiOptions( \Entities\PhysicalInterface::$DUPLEX ) - ->setRegisterInArrayValidator( true ) - ->setAttrib( 'class', 'chzn-select span12' ) - ->setLabel( 'Duplex' ) - ->setErrorMessages( array( 'Please set the duplex' ) ); - $this->addElement( $duplex ); - - $this->addDisplayGroup( - [ 'switchid', 'switchportid', 'status', 'speed', 'duplex' ], - 'physicalInterfaceDisplayGroup' - ); - - $this->getDisplayGroup( 'physicalInterfaceDisplayGroup' )->setLegend( 'Physical Interface Details' ); - - - ////////////////////////////////////////////////////////////////////////// - // VLAN INTERFACE DETAILS - - $this->addElement( INEX_Form_Vlan::getPopulatedSelect( 'vlanid' ) ); - $this->getElement( 'vlanid' )->setAttrib( 'class', 'chzn-select span12' ); - - $ipv4enabled = $this->createElement( 'checkbox', 'ipv4enabled' ); - $ipv4enabled->setLabel( 'IPv4 Enabled' ) - ->setCheckedValue( '1' ); - $this->addElement( $ipv4enabled ); - - $ipv4addressid = $this->createElement( 'select', 'ipv4addressid' ); - $ipv4addressid->setRegisterInArrayValidator( false ) - ->setAttrib( 'class', 'chzn-select' ) - ->setLabel( 'IPv4 Address' ) - ->addValidator( 'greaterThan', false, array( 'min' => 1 ) ) - ->setErrorMessages( array( 'Please select a IPv4 address' ) ); - $this->addElement( $ipv4addressid ); - - $ipv4hostname = $this->createElement( 'text', 'ipv4hostname' ); - $ipv4hostname->addValidator( 'stringLength', false, array( 1, 64 ) ) - ->setLabel( 'IPv4 Hostname' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $ipv4hostname ); - - $ipv4bgpmd5secret = $this->createElement( 'text', 'ipv4bgpmd5secret' ); - $ipv4bgpmd5secret->addValidator( 'stringLength', false, array( 1, 64 ) ) - ->setLabel( 'IPv4 BGP MD5 Secret' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $ipv4bgpmd5secret ); - - $ipv4canping = $this->createElement( 'checkbox', 'ipv4canping' ); - $ipv4canping->setLabel( 'IPv4 Can Ping' ) - ->setCheckedValue( '1' ); - $this->addElement( $ipv4canping ); - - $ipv4monitorrcbgp = $this->createElement( 'checkbox', 'ipv4monitorrcbgp' ); - $ipv4monitorrcbgp->setLabel( 'IPv4 Monitor RC BGP' ) - ->setCheckedValue( '1' ); - $this->addElement( $ipv4monitorrcbgp ); - - $this->addDisplayGroup( - [ 'ipv4addressid', 'ipv4hostname', 'ipv4bgpmd5secret', 'ipv4canping', 'ipv4monitorrcbgp' ], - 'ipv4DisplayGroup' - ); - $this->getDisplayGroup( 'ipv4DisplayGroup' )->setLegend( 'IPv4 Details' ); - - - - - - $ipv6enabled = $this->createElement( 'checkbox', 'ipv6enabled' ); - $ipv6enabled->setLabel( 'IPv6 Enabled' ) - ->setCheckedValue( '1' ); - $this->addElement( $ipv6enabled ); - - $ipv6addressid = $this->createElement( 'select', 'ipv6addressid' ); - $ipv6addressid->setRegisterInArrayValidator( false ) - ->setLabel( 'IPv6 Address' ) - ->setAttrib( 'class', 'chzn-select' ) - ->addValidator( 'greaterThan', false, array( 'min' => 1 ) ) - ->setErrorMessages( array( 'Please select a IPv6 address' ) ); - $this->addElement( $ipv6addressid ); - - $ipv6hostname = $this->createElement( 'text', 'ipv6hostname' ); - $ipv6hostname->addValidator( 'stringLength', false, array( 1, 64 ) ) - ->setLabel( 'IPv6 Hostname' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $ipv6hostname ); - - $ipv6bgpmd5secret = $this->createElement( 'text', 'ipv6bgpmd5secret' ); - $ipv6bgpmd5secret->addValidator( 'stringLength', false, array( 1, 64 ) ) - ->setLabel( 'IPv6 BGP MD5 Secret' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $ipv6bgpmd5secret ); - - - $ipv6canping = $this->createElement( 'checkbox', 'ipv6canping' ); - $ipv6canping->setLabel( 'IPv6 Can Ping' ) - ->setCheckedValue( '1' ); - $this->addElement( $ipv6canping ); - - $ipv6monitorrcbgp = $this->createElement( 'checkbox', 'ipv6monitorrcbgp' ); - $ipv6monitorrcbgp->setLabel( 'IPv6 Monitor RC BGP' ) - ->setCheckedValue( '1' ); - $this->addElement( $ipv6monitorrcbgp ); - - $this->addDisplayGroup( - [ 'ipv6addressid', 'ipv6hostname', 'ipv6bgpmd5secret', 'ipv6canping', 'ipv6monitorrcbgp' ], - 'ipv6DisplayGroup' - ); - - $this->getDisplayGroup( 'ipv6DisplayGroup' )->setLegend( 'IPv6 Details' ); - - - - $irrdbfilter = $this->createElement( 'checkbox', 'irrdbfilter' ); - $irrdbfilter->setLabel( 'Apply IRRDB Filtering' ) - ->setCheckedValue( '1' ); - $this->addElement( $irrdbfilter ); - - $mcastenabled = $this->createElement( 'checkbox', 'mcastenabled' ); - $mcastenabled->setLabel( 'Multicast Enabled' ) - ->setCheckedValue( '1' ); - $this->addElement( $mcastenabled ); - - $maxbgpprefix = $this->createElement( 'text', 'maxbgpprefix' ); - $maxbgpprefix->addValidator('int') - ->addValidator( 'greaterThan', false, array( -1 ) ) - ->setRequired( false ) - ->setLabel( 'Max BGP Prefixes' ); - $this->addElement( $maxbgpprefix ); - - $rsclient = $this->createElement( 'checkbox', 'rsclient' ); - $rsclient->setLabel( 'Route Server Client' ) - ->setCheckedValue( '1' ); - $this->addElement( $rsclient ); - - $as112client = $this->createElement( 'checkbox', 'as112client' ); - $as112client->setLabel( 'AS112 Client' ) - ->setCheckedValue( '1' ); - $this->addElement( $as112client ); - - $busyhost = $this->createElement( 'checkbox', 'busyhost' ); - $busyhost->setLabel( 'Busy host' ) - ->setCheckedValue( '1' ); - $this->addElement( $busyhost ); - - - $this->addDisplayGroup( - [ 'irrdbfilter', 'mcastenabled', 'maxbgpprefix', 'rsclient', 'as112client', 'busyhost' ], - 'vlanInterfaceDisplayGroup' - ); - - $this->getDisplayGroup( 'vlanInterfaceDisplayGroup' )->setLegend( 'Other VLAN Interface Settings' ); - - - $this->addElement( self::createSubmitElement( 'submit', _( 'Add' ) ) ); - $this->addElement( $this->createCancelElement() ); - - $preselectIPv4Address = $this->createElement( 'hidden', 'preselectIPv4Address' ); - $this->addElement( $preselectIPv4Address ); - - $preselectIPv6Address = $this->createElement( 'hidden', 'preselectIPv6Address' ); - $this->addElement( $preselectIPv6Address ); - - $preselectVlanInterface = $this->createElement( 'hidden', 'preselectVlanInterface' ); - $this->addElement( $preselectVlanInterface ); - - $preselectSwitchPort = $this->createElement( 'hidden', 'preselectSwitchPort' ); - $this->addElement( $preselectSwitchPort ); - - $preselectPhysicalInterface = $this->createElement( 'hidden', 'preselectPhysicalInterface' ); - $this->addElement( $preselectPhysicalInterface ); - - } - -} diff --git a/library/INEX/Form/Interface/Physical.php b/library/INEX/Form/Interface/Physical.php deleted file mode 100644 index 1134904d1..000000000 --- a/library/INEX/Form/Interface/Physical.php +++ /dev/null @@ -1,109 +0,0 @@ - - * @category INEX - * @package INEX_Form - * @copyright Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd - * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 - */ -class INEX_Form_Interface_Physical extends INEX_Form -{ - - public function init() - { - $this->addElement( INEX_Form_Switch::getPopulatedSelect( 'switchid' ) ); - - $switchPorts = $this->createElement( 'select', 'switchportid' ); - - $switchPorts->setRequired( true ) - ->setRegisterInArrayValidator( false ) - ->setLabel( 'Port' ) - ->setAttrib( 'class', 'chzn-select span3' ) - ->addValidator( 'greaterThan', false, array( 'min' => 1 ) ) - ->setErrorMessages( array( 'Please select a switch port' ) ); - $this->addElement( $switchPorts ); - - $virtualInterface = $this->createElement( 'hidden', 'virtualinterfaceid' ); - $this->addElement( $virtualInterface ); - - $status = $this->createElement( 'select', 'status' ); - $status->setMultiOptions( \Entities\PhysicalInterface::$STATES ) - ->setRegisterInArrayValidator( true ) - ->setAttrib( 'class', 'chzn-select span3' ) - ->setLabel( 'Status' ) - ->setErrorMessages( array( 'Please set the status' ) ); - $this->addElement( $status ); - - - $speed = $this->createElement( 'select', 'speed' ); - $speed->setMultiOptions( \Entities\PhysicalInterface::$SPEED ) - ->setRegisterInArrayValidator( true ) - ->setAttrib( 'class', 'chzn-select span3' ) - ->setLabel( 'Speed' ) - ->setErrorMessages( array( 'Please set the speed' ) ); - $this->addElement( $speed ); - - - $duplex = $this->createElement( 'select', 'duplex' ); - $duplex->setMultiOptions( \Entities\PhysicalInterface::$DUPLEX ) - ->setRegisterInArrayValidator( true ) - ->setAttrib( 'class', 'chzn-select span3' ) - ->setLabel( 'Duplex' ) - ->setErrorMessages( array( 'Please set the duplex' ) ); - $this->addElement( $duplex ); - - - $monitorindex = $this->createElement( 'text', 'monitorindex' ); - $monitorindex->addValidator( 'int' ) - ->setLabel( 'Monitor Index' ) - ->setAttrib( 'class', 'span3' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $monitorindex ); - - - $notes = $this->createElement( 'textarea', 'notes' ); - $notes->setLabel( 'Notes' ) - ->setRequired( false ) - ->setAttrib( 'class', 'span3' ) - ->addFilter( new OSS_Filter_StripSlashes() ) - ->setAttrib( 'cols', 60 ) - ->setAttrib( 'rows', 5 ); - $this->addElement( $notes ); - - $this->addElement( self::createSubmitElement( 'submit', _( 'Add' ) ) ); - $this->addElement( $this->createCancelElement() ); - - $preselectSwitchPort = $this->createElement( 'hidden', 'preselectSwitchPort' ); - $this->addElement( $preselectSwitchPort ); - - $preselectPhysicalInterface = $this->createElement( 'hidden', 'preselectPhysicalInterface' ); - $this->addElement( $preselectPhysicalInterface ); - - } -} diff --git a/library/INEX/Form/Interface/Virtual.php b/library/INEX/Form/Interface/Virtual.php deleted file mode 100644 index e824cff80..000000000 --- a/library/INEX/Form/Interface/Virtual.php +++ /dev/null @@ -1,96 +0,0 @@ - - * @category INEX - * @package INEX_Form - * @copyright Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd - * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 - */ -class INEX_Form_Interface_Virtual extends INEX_Form -{ - - public function init() - { - $this->setDecorators( [ [ 'ViewScript', [ 'viewScript' => 'virtual-interface/forms/virtual-interface.phtml' ] ] ] ); - - $this->addElement( INEX_Form_Customer::getPopulatedSelect( 'custid' ) ); - $this->getElement( 'custid' )->setAttrib( 'class', 'chzn-select span6' ); - - $name = $this->createElement( 'text', 'name' ); - $name->addValidator( 'stringLength', false, array( 0, 255 ) ) - ->setRequired( false ) - ->setLabel( 'Virtual Interface Name' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $name ); - - - $descr = $this->createElement( 'text', 'description' ); - $descr->setLabel( 'Description' ) - ->addValidator( 'stringLength', false, array( 0, 255 ) ) - ->setRequired( false ) - ->addFilter( new OSS_Filter_StripSlashes() ) - ->addFilter( 'StringTrim' ); - $this->addElement( $descr ); - - - $channel = $this->createElement( 'text', 'channelgroup' ); - $channel->addValidator( 'int' ) - ->setLabel( 'Channel Group Number' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $channel ); - - $mtu = $this->createElement( 'text', 'mtu' ); - $mtu->addValidator( 'int' ) - ->setLabel( 'MTU' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $mtu ); - - - $trunk = $this->createElement( 'checkbox', 'trunk' ); - $trunk->setLabel( 'Is 802.1q Trunk?' ) - ->setCheckedValue( '1' ); - $this->addElement( $trunk ); - - - $this->addDisplayGroup( - [ 'custid', 'name', 'description', 'channelgroup', 'mtu', 'trunk' ], - 'virtualInterfaceDisplayGroup' - ); - - $this->getDisplayGroup( 'virtualInterfaceDisplayGroup' )->setLegend( 'Customer Connection Details' ); - - - $this->addElement( self::createSubmitElement( 'submit', _( 'Add' ) ) ); - $this->addElement( $this->createCancelElement() ); - } - -} - diff --git a/library/INEX/Form/Interface/Vlan.php b/library/INEX/Form/Interface/Vlan.php deleted file mode 100644 index 6505c44dd..000000000 --- a/library/INEX/Form/Interface/Vlan.php +++ /dev/null @@ -1,194 +0,0 @@ - - * @category INEX - * @package INEX_Form - * @copyright Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd - * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 - */ -class INEX_Form_Interface_Vlan extends INEX_Form -{ - - public function init() - { - $this->setDecorators( [ [ 'ViewScript', [ 'viewScript' => 'vlan-interface/forms/vlan-interface.phtml' ] ] ] ); - - $virtualInterface = $this->createElement( 'hidden', 'virtualinterfaceid' ); - $this->addElement( $virtualInterface ); - - $this->addElement( INEX_Form_Vlan::getPopulatedSelect( 'vlanid' ) ); - $this->getElement( 'vlanid' )->setAttrib( 'class', 'chzn-select span6' ); - - $ipv4enabled = $this->createElement( 'checkbox', 'ipv4enabled' ); - $ipv4enabled->setLabel( 'IPv4 Enabled' ) - ->setCheckedValue( '1' ); - $this->addElement( $ipv4enabled ); - - $ipv4addressid = $this->createElement( 'select', 'ipv4addressid' ); - $ipv4addressid->setRegisterInArrayValidator( false ) - ->setAttrib( 'class', 'chzn-select' ) - ->setLabel( 'IPv4 Address' ) - ->addValidator( 'greaterThan', false, array( 'min' => 1 ) ) - ->setErrorMessages( array( 'Please select a IPv4 address' ) ); - $this->addElement( $ipv4addressid ); - - $ipv4hostname = $this->createElement( 'text', 'ipv4hostname' ); - $ipv4hostname->addValidator( 'stringLength', false, array( 1, 64 ) ) - ->setLabel( 'IPv4 Hostname' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $ipv4hostname ); - - $ipv4bgpmd5secret = $this->createElement( 'text', 'ipv4bgpmd5secret' ); - $ipv4bgpmd5secret->addValidator( 'stringLength', false, array( 1, 64 ) ) - ->setLabel( 'IPv4 BGP MD5 Secret' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $ipv4bgpmd5secret ); - - $ipv4canping = $this->createElement( 'checkbox', 'ipv4canping' ); - $ipv4canping->setLabel( 'IPv4 Can Ping' ) - ->setCheckedValue( '1' ); - $this->addElement( $ipv4canping ); - - $ipv4monitorrcbgp = $this->createElement( 'checkbox', 'ipv4monitorrcbgp' ); - $ipv4monitorrcbgp->setLabel( 'IPv4 Monitor RC BGP' ) - ->setCheckedValue( '1' ); - $this->addElement( $ipv4monitorrcbgp ); - - $this->addDisplayGroup( - [ 'ipv4addressid', 'ipv4hostname', 'ipv4bgpmd5secret', 'ipv4canping', 'ipv4monitorrcbgp' ], - 'ipv4DisplayGroup' - ); - $this->getDisplayGroup( 'ipv4DisplayGroup' )->setLegend( 'IPv4 Details' ); - - - - $ipv6enabled = $this->createElement( 'checkbox', 'ipv6enabled' ); - $ipv6enabled->setLabel( 'IPv6 Enabled' ) - ->setCheckedValue( '1' ); - $this->addElement( $ipv6enabled ); - - $ipv6addressid = $this->createElement( 'select', 'ipv6addressid' ); - $ipv6addressid->setRegisterInArrayValidator( false ) - ->setLabel( 'IPv6 Address' ) - ->setAttrib( 'class', 'chzn-select' ) - ->addValidator( 'greaterThan', false, array( 'min' => 1 ) ) - ->setErrorMessages( array( 'Please select a IPv6 address' ) ); - $this->addElement( $ipv6addressid ); - - $ipv6hostname = $this->createElement( 'text', 'ipv6hostname' ); - $ipv6hostname->addValidator( 'stringLength', false, array( 1, 64 ) ) - ->setLabel( 'IPv6 Hostname' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $ipv6hostname ); - - $ipv6bgpmd5secret = $this->createElement( 'text', 'ipv6bgpmd5secret' ); - $ipv6bgpmd5secret->addValidator( 'stringLength', false, array( 1, 64 ) ) - ->setLabel( 'IPv6 BGP MD5 Secret' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $ipv6bgpmd5secret ); - - - $ipv6canping = $this->createElement( 'checkbox', 'ipv6canping' ); - $ipv6canping->setLabel( 'IPv6 Can Ping' ) - ->setCheckedValue( '1' ); - $this->addElement( $ipv6canping ); - - $ipv6monitorrcbgp = $this->createElement( 'checkbox', 'ipv6monitorrcbgp' ); - $ipv6monitorrcbgp->setLabel( 'IPv6 Monitor RC BGP' ) - ->setCheckedValue( '1' ); - $this->addElement( $ipv6monitorrcbgp ); - - $this->addDisplayGroup( - array( 'ipv6addressid', 'ipv6hostname', 'ipv6bgpmd5secret', 'ipv6canping', 'ipv6monitorrcbgp' ), - 'ipv6DisplayGroup' - ); - $this->getDisplayGroup( 'ipv6DisplayGroup' )->setLegend( 'IPv6 Details' ); - - - - $irrdbfilter = $this->createElement( 'checkbox', 'irrdbfilter' ); - $irrdbfilter->setLabel( 'Apply IRRDB Filtering' ) - ->setCheckedValue( '1' ); - $this->addElement( $irrdbfilter ); - - $mcastenabled = $this->createElement( 'checkbox', 'mcastenabled' ); - $mcastenabled->setLabel( 'Multicast Enabled' ) - ->setCheckedValue( '1' ); - $this->addElement( $mcastenabled ); - - $maxbgpprefix = $this->createElement( 'text', 'maxbgpprefix' ); - $maxbgpprefix->addValidator('int') - ->addValidator( 'greaterThan', false, array( -1 ) ) - ->setRequired( false ) - ->setLabel( 'Max BGP Prefixes' ); - $this->addElement( $maxbgpprefix ); - - $rsclient = $this->createElement( 'checkbox', 'rsclient' ); - $rsclient->setLabel( 'Route Server Client' ) - ->setCheckedValue( '1' ); - $this->addElement( $rsclient ); - - $as112client = $this->createElement( 'checkbox', 'as112client' ); - $as112client->setLabel( 'AS112 Client' ) - ->setCheckedValue( '1' ); - $this->addElement( $as112client ); - - $busyhost = $this->createElement( 'checkbox', 'busyhost' ); - $busyhost->setLabel( 'Busy host' ) - ->setCheckedValue( '1' ); - $this->addElement( $busyhost ); - - $notes = $this->createElement( 'textarea', 'notes' ); - $notes->setLabel( 'Notes' ) - ->setRequired( false ) - ->setAttrib( 'class', 'span3' ) - ->addFilter( new OSS_Filter_StripSlashes() ) - ->setAttrib( 'cols', 60 ) - ->setAttrib( 'rows', 5 ); - $this->addElement( $notes ); - - $this->addElement( self::createSubmitElement( 'submit', _( 'Add' ) ) ); - $this->addElement( $this->createCancelElement() ); - - $preselectIPv4Address = $this->createElement( 'hidden', 'preselectIPv4Address' ); - $this->addElement( $preselectIPv4Address ); - - $preselectIPv6Address = $this->createElement( 'hidden', 'preselectIPv6Address' ); - $this->addElement( $preselectIPv6Address ); - - $preselectVlanInterface = $this->createElement( 'hidden', 'preselectVlanInterface' ); - $this->addElement( $preselectVlanInterface ); - - } - -} - diff --git a/library/INEX/Form/IrrdbConfig.php b/library/INEX/Form/IrrdbConfig.php deleted file mode 100644 index c6472eb05..000000000 --- a/library/INEX/Form/IrrdbConfig.php +++ /dev/null @@ -1,100 +0,0 @@ - - * @category INEX - * @package INEX_Form - * @copyright Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd - * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 - */ -class INEX_Form_IrrdbConfig extends INEX_Form -{ - public function init() - { - $host = $this->createElement( 'text', 'host' ); - $host->addValidator( 'stringLength', false, array( 1, 255 ) ) - ->setRequired( true ) - ->setLabel( 'Host' ) - ->setAttrib( 'class', 'span3' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $host ); - - $protocol = $this->createElement( 'text', 'protocol' ); - $protocol->addValidator( 'stringLength', false, array( 1, 10 ) ) - ->setRequired( true ) - ->setLabel( 'Protocol' ) - ->setAttrib( 'class', 'span3' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $protocol ); - - $source = $this->createElement( 'text', 'source' ); - $source->addValidator( 'stringLength', false, array( 1, 50 ) ) - ->setRequired( true ) - ->setLabel( 'Source' ) - ->setAttrib( 'class', 'span3' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $source ); - - $notes = $this->createElement( 'textarea', 'notes' ); - $notes->setLabel( 'Notes' ) - ->setRequired( false ) - ->addFilter( new OSS_Filter_StripSlashes() ) - ->setAttrib( 'cols', 60 ) - ->setAttrib( 'class', 'span3' ) - ->setAttrib( 'rows', 5 ); - $this->addElement( $notes ); - - $this->addElement( self::createSubmitElement( 'submit', _( 'Add' ) ) ); - $this->addElement( $this->createCancelElement() ); - } - - /** - * Create a SELECT / dropdown element of all IRRDB names indexed by their id. - * - * @param string $name The element name - * @return Zend_Form_Element_Select The select element - */ - public static function getPopulatedSelect( $name = 'irrdb' ) - { - $e = new Zend_Form_Element_Select( $name ); - - $maxId = self::populateSelectFromDatabase( $e, '\\Entities\\IRRDBConfig', 'id', 'source', 'source', 'ASC' ); - - $e->setRegisterInArrayValidator( true ) - ->setRequired( true ) - ->setLabel( _( 'IRRDB Source' ) ) - ->setAttrib( 'class', 'span3 chzn-select' ) - ->addValidator( 'between', false, array( 1, $maxId ) ) - ->setErrorMessages( array( _( 'Please select an IRRDB source' ) ) ); - - return $e; - } - -} diff --git a/library/INEX/Form/Location.php b/library/INEX/Form/Location.php deleted file mode 100644 index 5ef0f4fa9..000000000 --- a/library/INEX/Form/Location.php +++ /dev/null @@ -1,169 +0,0 @@ - - * @category INEX - * @package INEX_Form - * @copyright Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd - * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 - */ -class INEX_Form_Location extends INEX_Form -{ - public function init() - { - $name = $this->createElement( 'text', 'name' ); - $name->addValidator( 'stringLength', false, array( 1, 255 ) ) - ->setAttrib( 'class', 'span3' ) - ->setRequired( true ) - ->setLabel( 'Name' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $name ); - - - $shortname = $this->createElement( 'text', 'shortname' ); - $shortname->addValidator( 'stringLength', false, array( 1, 255 ) ) - ->setAttrib( 'class', 'span3' ) - ->setRequired( true ) - ->setLabel( 'Short Name' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $shortname ); - - $address = $this->createElement( 'textarea', 'address' ); - $address->addValidator( 'stringLength', false, array( 1, 255 ) ) - ->setLabel( 'Address' ) - ->addFilter( new OSS_Filter_StripSlashes() ) - ->setAttrib( 'cols', 60 ) - ->setAttrib( 'class', 'span3' ) - ->setAttrib( 'rows', 5 ); - $this->addElement( $address ); - - $nocphone = $this->createElement( 'text', 'nocphone' ); - $nocphone->addValidator( 'stringLength', false, array( 0, 255 ) ) - ->setAttrib( 'class', 'span3' ) - ->setRequired( false ) - ->setLabel( 'Phone' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $nocphone ); - - $nocfax = $this->createElement( 'text', 'nocfax' ); - $nocfax->addValidator( 'stringLength', false, array( 0, 255 ) ) - ->setAttrib( 'class', 'span3' ) - ->setRequired( false ) - ->setLabel( 'Fax' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $nocfax ); - - $nocemail = $this->createElement( 'text', 'nocemail' ); - $nocemail->addValidator('emailAddress' ) - ->setAttrib( 'class', 'span3' ) - ->addValidator( 'stringLength', false, array( 0, 255 ) ) - ->setRequired( false ) - ->setLabel( 'E-Mail' ); - $this->addElement( $nocemail ); - - - $this->addDisplayGroup( - array( 'nocphone', 'nocfax', 'nocemail' ), - 'nocDisplayGroup' - ); - $this->getDisplayGroup( 'nocDisplayGroup' )->setLegend( 'NOC Details' ); - - $officephone = $this->createElement( 'text', 'officephone' ); - $officephone->addValidator( 'stringLength', false, array( 0, 255 ) ) - ->setAttrib( 'class', 'span3' ) - ->setRequired( false ) - ->setLabel( 'Phone' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $officephone ); - - $officefax = $this->createElement( 'text', 'officefax' ); - $officefax->addValidator( 'stringLength', false, array( 0, 255 ) ) - ->setAttrib( 'class', 'span3' ) - ->setRequired( false ) - ->setLabel( 'Fax' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $officefax ); - - $officeemail = $this->createElement( 'text', 'officeemail' ); - $officeemail->addValidator('emailAddress' ) - ->setAttrib( 'class', 'span3' ) - ->addValidator( 'stringLength', false, array( 0, 255 ) ) - ->setRequired( false ) - ->setLabel( 'E-Mail' ); - $this->addElement( $officeemail ); - - $this->addDisplayGroup( - array( 'officephone', 'officefax', 'officeemail' ), - 'officeDisplayGroup' - ); - $this->getDisplayGroup( 'officeDisplayGroup' )->setLegend( 'Office Details' ); - - - $notes = $this->createElement( 'textarea', 'notes' ); - $notes->setLabel( 'Notes' ) - ->setRequired( false ) - ->setAttrib( 'class', 'span3' ) - ->addFilter( new OSS_Filter_StripSlashes() ) - ->setAttrib( 'cols', 60 ) - ->setAttrib( 'rows', 5 ); - $this->addElement( $notes ); - - $this->addElement( self::createSubmitElement( 'submit', _( 'Add' ) ) ); - $this->addElement( $this->createCancelElement() ); - } - - - - /** - * Create a SELECT / dropdown element of all location names indexed by their id. - * - * @param string $name The element name - * @return Zend_Form_Element_Select The select element - */ - public static function getPopulatedSelect( $name = 'locationid' ) - { - $loc = new Zend_Form_Element_Select( $name ); - - $maxId = self::populateSelectFromDatabase( $loc, '\\Entities\\Location', 'id', 'name', 'name', 'ASC' ); - - $loc->setRegisterInArrayValidator( true ) - ->setRequired( true ) - ->setLabel( _( 'Location' ) ) - ->setAttrib( 'class', 'span3 chzn-select' ) - ->addValidator( 'between', false, array( 1, $maxId ) ) - ->setErrorMessages( array( _( 'Please select a location' ) ) ); - - return $loc; - } -} - diff --git a/library/INEX/Form/Meeting.php b/library/INEX/Form/Meeting.php deleted file mode 100644 index 3ccf2d87e..000000000 --- a/library/INEX/Form/Meeting.php +++ /dev/null @@ -1,132 +0,0 @@ - - * @category INEX - * @package INEX_Form - * @copyright Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd - * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 - */ -class INEX_Form_Meeting extends INEX_Form -{ - public function init() - { - - $title = $this->createElement( 'text', 'title', array( 'size' => '100' ) ); - $title->addValidator( 'stringLength', false, array( 1, 255 ) ) - ->setRequired( true ) - ->setLabel( 'Title' ) - ->setValue( 'Members\' Meeting' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $title ); - - $before_text = $this->createElement( 'textarea', 'before_text' ); - $before_text->setLabel( 'Preamble' ) - ->setRequired( false ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ) - ->setAttrib( 'cols', 120 ) - ->setAttrib( 'class', 'span9' ) - ->setAttrib( 'rows', 10 ); - $this->addElement( $before_text ); - - $after_text = $this->createElement( 'textarea', 'after_text' ); - $after_text->setLabel( 'Postamble' ) - ->setRequired( false ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ) - ->setAttrib( 'cols', 120 ) - ->setAttrib( 'class', 'span9' ) - ->setAttrib( 'rows', 10 ); - $this->addElement( $after_text ); - - $date = $this->createElement( 'text', 'date' ); - $date->addValidator( 'stringLength', false, array( 10, 10 ) ) - ->addValidator( 'regex', false, array('/^\d\d\d\d-\d\d-\d\d/' ) ) - ->setRequired( true ) - ->setLabel( 'Date' ) - ->setValue( 'YYYY-MM-DD' ) - ->addFilter( 'StringTrim' ); - $this->addElement( $date ); - - $time = $this->createElement( 'text', 'time' ); - $time->addValidator( 'stringLength', false, array( 5, 8 ) ) - ->addValidator( 'regex', false, array('/^\d\d:\d\d(:\d\d){0,1}/' ) ) - ->setRequired( true ) - ->setValue( 'HH:MM' ) - ->setLabel( 'Time' ) - ->addFilter( 'StringTrim' ); - $this->addElement( $time ); - - $venue = $this->createElement( 'text', 'venue', array( 'size' => '100' ) ); - $venue->addValidator( 'stringLength', false, array( 1, 255 ) ) - ->setRequired( true ) - ->setLabel( 'Venue' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - - $this->addElement( $venue ); - - $venue_url = $this->createElement( 'text', 'venue_url', array( 'size' => '100' ) ); - $venue_url->addValidator( 'stringLength', false, array( 0, 255 ) ) - ->setRequired( false ) - ->setLabel( 'Venue URL' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ) - ->setValue( '' ); - - $this->addElement( $venue_url ); - - - $this->addElement( self::createSubmitElement( 'submit', _( 'Add' ) ) ); - $this->addElement( $this->createCancelElement() ); - } - - /** - * Create a SELECT / dropdown element of all meetings indexed by their id. - * - * @param string $name The element name - * @return Zend_Form_Element_Select The select element - */ - public static function getPopulatedSelect( $name = 'meeting_id' ) - { - $e = new Zend_Form_Element_Select( $name ); - - $maxId = self::populateSelectFromDatabase( $e, '\\Entities\\Meeting', 'id', [ 'title' => [ 'type' => 'STRING' ], 'date' => [ 'type' => 'DATE', 'format' => 'Y-m-d' ] ], 'title', 'ASC' ); - - $e->setRegisterInArrayValidator( true ) - ->setRequired( true ) - ->setLabel( _( 'Meeting' ) ) - ->setAttrib( 'class', 'span3 chzn-select' ) - ->addValidator( 'between', false, array( 1, $maxId ) ) - ->setErrorMessages( array( _( 'Please select a meeting' ) ) ); - - return $e; - } - -} diff --git a/library/INEX/Form/Meeting/Item.php b/library/INEX/Form/Meeting/Item.php deleted file mode 100644 index b7288aa6f..000000000 --- a/library/INEX/Form/Meeting/Item.php +++ /dev/null @@ -1,135 +0,0 @@ - - * @category INEX - * @package INEX_Form - * @copyright Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd - * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 - */ -class INEX_Form_Meeting_Item extends INEX_Form -{ - public function init() - { - $this->addElement( INEX_Form_Meeting::getPopulatedSelect( 'meeting_id' ) ); - $this->getElement( 'meeting_id' )->setAttrib( 'class', 'chzn-select span6' ); - - $title = $this->createElement( 'text', 'title', array( 'size' => '100' ) ); - $title->addValidator( 'stringLength', false, array( 1, 255 ) ) - ->setRequired( true ) - ->setLabel( 'Title' ) - ->setAttrib( 'class', 'span6' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $title ); - - $name = $this->createElement( 'text', 'name', array( 'size' => '100' ) ); - $name->addValidator( 'stringLength', false, array( 1, 255 ) ) - ->setRequired( true ) - ->setAttrib( 'class', 'span6' ) - ->setLabel( 'Name' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $name ); - - $role = $this->createElement( 'text', 'role', array( 'size' => '100' ) ); - $role->addValidator( 'stringLength', false, array( 1, 255 ) ) - ->setRequired( false ) - ->setAttrib( 'class', 'span6' ) - ->setLabel( 'Role' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $role ); - - $email = $this->createElement( 'text', 'email', array( 'size' => '100' ) ); - $email->addValidator( 'stringLength', false, array( 1, 255 ) ) - ->setRequired( false ) - ->setLabel( 'E-Mail' ) - ->setAttrib( 'class', 'span6' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $email ); - - $company = $this->createElement( 'text', 'company', array( 'size' => '100' ) ); - $company->addValidator( 'stringLength', false, array( 1, 255 ) ) - ->setRequired( true ) - ->setLabel( 'Company' ) - ->setAttrib( 'class', 'span6' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $company ); - - $company_url = $this->createElement( 'text', 'company_url', array( 'size' => '100' ) ); - $company_url->addValidator( 'stringLength', false, array( 1, 255 ) ) - ->setRequired( false ) - ->setLabel( 'Company URL' ) - ->setAttrib( 'class', 'span6' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $company_url ); - - - $summary = $this->createElement( 'textarea', 'summary' ); - $summary->setLabel( 'Summary' ) - ->setRequired( false ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ) - ->setAttrib( 'class', 'span6' ) - ->setAttrib( 'rows', 10 ); - $this->addElement( $summary ); - - $presentation = $this->createElement( 'file', 'presentation' ); - $presentation->setLabel( 'Attach Presentation' ) - ->setAttrib( 'class', 'span6' ) - ->setRequired( false ); - $this->addElement( $presentation ); - - $video_url = $this->createElement( 'text', 'video_url', array( 'size' => '100' ) ); - $video_url->addValidator( 'stringLength', false, array( 1, 255 ) ) - ->setRequired( false ) - ->setLabel( 'Video' ) - ->setAttrib( 'class', 'span6' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $video_url ); - - - $other_content = $this->createElement( 'checkbox', 'other_content' ); - $other_content->setLabel( 'Other Content?' ) - ->setRequired( false ); - $this->addElement( $other_content ); - - $this->addElement( self::createSubmitElement( 'submit', _( 'Add' ) ) ); - $this->addElement( $this->createCancelElement() ); - - // we shouldn't update the presentation file on an edit if it's blank - $this->onEditSkipIfBlank = array( 'presentation' ); - - } - -} - diff --git a/library/INEX/Form/PeeringRequest.php b/library/INEX/Form/PeeringRequest.php deleted file mode 100644 index e4f863168..000000000 --- a/library/INEX/Form/PeeringRequest.php +++ /dev/null @@ -1,118 +0,0 @@ - - * @category INEX - * @package INEX_Form - * @copyright Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd - * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 - */ -class INEX_Form_PeeringRequest extends INEX_Form -{ - - public function init() - { - $this->setAttrib( 'id', 'peering-request-form' ) - ->setAttrib( 'name', 'peering-request-form' ); - - $this->setDecorators( [ [ 'ViewScript', [ 'viewScript' => 'peering-manager/peering-request-form.phtml' ] ] ] ); - - $custid = $this->createElement( 'hidden', 'custid' ) - ->setAttrib( 'id', 'peering-request-form-custid' ); - $this->addElement( $custid ); - - $marksent = $this->createElement( 'hidden', 'marksent' ) - ->setValue( '0' ) - ->setAttrib( 'id', 'peering-request-form-marksent' ); - $this->addElement( $marksent ); - - $sendtome = $this->createElement( 'hidden', 'sendtome' ) - ->setValue( '0' ) - ->setAttrib( 'id', 'peering-request-form-sendtome' ); - $this->addElement( $sendtome ); - - - $to = $this->createElement( 'text', 'to' ); - $to->addValidator( 'stringLength', false, array( 1, 4096 ) ) - ->setRequired( true ) - ->setLabel( 'To' ) - ->setAttrib( 'class', 'span5' ) - ->setAttrib( 'readonly', 'readonly' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - - $this->addElement( $to ); - - $cc = $this->createElement( 'text', 'cc' ); - $cc->addValidator( 'stringLength', false, array( 1, 4096 ) ) - ->setRequired( false ) - ->setLabel( 'CC' ) - ->setAttrib( 'class', 'span5' ) - ->setAttrib( 'readonly', 'readonly' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - - $this->addElement( $cc ); - - $bcc = $this->createElement( 'text', 'bcc' ); - $bcc->addValidator( 'stringLength', false, array( 1, 4096 ) ) - ->setRequired( false ) - ->setLabel( 'BCC' ) - ->setAttrib( 'class', 'span5' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - - $this->addElement( $bcc ); - - $subject = $this->createElement( 'text', 'subject' ); - $subject->addValidator( 'stringLength', false, array( 1, 4096 ) ) - ->setRequired( true ) - ->setLabel( 'Subject' ) - ->setAttrib( 'class', 'span5' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - - $this->addElement( $subject ); - - - $message = $this->createElement( 'textarea', 'message', - [ 'cols' => 50, 'rows' => 8 ] - ); - - $message->addValidator( 'stringLength', false, array( 1, 40960 ) ) - ->setRequired( true ) - ->setLabel( 'Message' ) - ->setAttrib( 'class', 'span5 mono' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - - $this->addElement( $message ); - - $this->addElement( 'submit', 'commit', array( 'label' => 'Send' ) ); - } - -} diff --git a/library/INEX/Form/Profile.php b/library/INEX/Form/Profile.php deleted file mode 100644 index de4bf52ce..000000000 --- a/library/INEX/Form/Profile.php +++ /dev/null @@ -1,62 +0,0 @@ - - * @category INEX - * @package INEX_Form - * @copyright Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd - * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 - */ -class INEX_Form_Profile extends INEX_Form -{ - public function init() - { - $this->setAttrib( 'id', 'profile' ) - ->setAttrib( 'name', 'profile' ) - ->setAction( OSS_Utils::genUrl( 'profile', 'change-profile' ) ); - - $username = OSS_Form_Auth::createUsernameElement(); - $username->setAttrib( 'readonly', 'readonly' ) - ->setAttrib( 'class', 'span6' ); - $this->addElement( $username ); - - - $mobile = INEX_Form_User::createMobileElement() - ->setAttrib( 'class', 'span6' ); - $this->addElement( $mobile ); - - - $email = OSS_Form_User::createEmailElement(); - $email->setAttrib( 'readonly', 'readonly' ) - ->setAttrib( 'class', 'span9' ); - $this->addElement( $email ); - - $this->addElement( self::createSubmitElement( 'submit', _( 'Update' ) ) ); - } - -} - diff --git a/library/INEX/Form/Provision/InterfaceEmail.php b/library/INEX/Form/Provision/InterfaceEmail.php deleted file mode 100644 index 1cf91c76e..000000000 --- a/library/INEX/Form/Provision/InterfaceEmail.php +++ /dev/null @@ -1,133 +0,0 @@ -setAttrib( 'accept-charset', 'UTF-8' ); - $this->setAttrib( 'class', 'form' ); - - $this->setElementDecorators( - array( - 'ViewHelper', - 'Errors', - array( 'HtmlTag', array( 'tag' => 'dd' ) ), - array( 'Label', array( 'tag' => 'dt' ) ), - ) - ); - - - $to = $this->createElement( 'text', 'to', - array( - 'size' => 100 - ) - ); - $to->addValidator( 'stringLength', false, array( 1, 4096 ) ) - ->setRequired( true ) - ->setLabel( 'To' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new INEX_Filter_StripSlashes() ); - - $this->addElement( $to ); - - $cc = $this->createElement( 'text', 'cc', - array( - 'size' => 100 - ) - ); - - $cc->addValidator( 'stringLength', false, array( 1, 4096 ) ) - ->setRequired( false ) - ->setLabel( 'CC' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new INEX_Filter_StripSlashes() ); - - $this->addElement( $cc ); - - $bcc = $this->createElement( 'text', 'bcc', - array( - 'size' => 100 - ) - ); - - $bcc->addValidator( 'stringLength', false, array( 1, 4096 ) ) - ->setRequired( false ) - ->setLabel( 'BCC' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new INEX_Filter_StripSlashes() ); - - $this->addElement( $bcc ); - - $subject = $this->createElement( 'text', 'subject', - array( - 'size' => 100 - ) - ); - - $subject->addValidator( 'stringLength', false, array( 1, 4096 ) ) - ->setRequired( true ) - ->setLabel( 'Subject' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new INEX_Filter_StripSlashes() ); - - $this->addElement( $subject ); - - - $message = $this->createElement( 'textarea', 'message', - array( - 'cols' => 100, - 'rows' => 12, - 'style' => 'font-family:monospace;' - ) - ); - - $message->addValidator( 'stringLength', false, array( 1, 40960 ) ) - ->setRequired( true ) - ->setLabel( 'Message' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new INEX_Filter_StripSlashes() ); - - $this->addElement( $message ); - - - - - $this->addElement( 'button', 'cancel', array( 'label' => 'Cancel', 'onClick' => "parent.location='$cancelLocation'" ) ); - $this->addElement( 'submit', 'commit', array( 'label' => 'Send' ) ); - } - -} diff --git a/library/INEX/Form/SubForm.php b/library/INEX/Form/SubForm.php deleted file mode 100644 index e0df35bd5..000000000 --- a/library/INEX/Form/SubForm.php +++ /dev/null @@ -1,75 +0,0 @@ -hasIdentity() ) - // $identity = $auth->getIdentity(); - // else - // return false; - - //$identity['user']['privs'] - - - $columns = Doctrine::getTable( $controller->getModelName() )->getFieldNames(); - - foreach( $this->getElements() as $elementName => $elementConfig ) - if( in_array( $elementName, $columns ) ) - $model->$elementName = $this->getValue( $elementName ); - - return $model; - } - - public function assignModelToForm( $model, $controller ) - { - $columns = Doctrine::getTable( $controller->getModelName() )->getFieldNames(); - - foreach( $this->getElements() as $elementName => $elementConfig ) - if( in_array( $elementName, $columns ) ) - $this->getElement( $elementName )->setValue( $model->$elementName ); - - return $this; - } - -} - -?> \ No newline at end of file diff --git a/library/INEX/Form/SubForm/AddCancel.php b/library/INEX/Form/SubForm/AddCancel.php deleted file mode 100644 index 8a937cd23..000000000 --- a/library/INEX/Form/SubForm/AddCancel.php +++ /dev/null @@ -1,66 +0,0 @@ -setDecorators( - array( - array( 'ViewScript', array( 'viewScript' => 'form/add-cancel.tpl' ) ) - ) - ); - - - $this->setElementDecorators( - array( - 'ViewHelper' - ) - ); - - } - -} - -?> \ No newline at end of file diff --git a/library/INEX/Form/SubForm/AddEditCancel.php b/library/INEX/Form/SubForm/AddEditCancel.php deleted file mode 100644 index ff9ff8f69..000000000 --- a/library/INEX/Form/SubForm/AddEditCancel.php +++ /dev/null @@ -1,65 +0,0 @@ -setDecorators( - array( - array( 'ViewScript', array( 'viewScript' => 'form/add-edit-cancel.tpl' ) ) - ) - ); - - - $this->setElementDecorators( - array( - 'ViewHelper' - ) - ); - - } - -} - -?> \ No newline at end of file diff --git a/library/INEX/Form/SubForm/PatchPanel.php b/library/INEX/Form/SubForm/PatchPanel.php deleted file mode 100644 index 49297e3b6..000000000 --- a/library/INEX/Form/SubForm/PatchPanel.php +++ /dev/null @@ -1,151 +0,0 @@ -setDecorators( - array( - array( 'ViewScript', array( 'viewScript' => 'patch-panel/form/patch-panel.tpl' ) ) - ) - ); - - //////////////////////////////////////////////// - // Create and configure elements - //////////////////////////////////////////////// - - $name = $this->createElement( 'text', 'name' ); - $name->addValidator( 'stringLength', false, array( 1, 255 ) ) - ->setAttrib('size', 50 ) - ->setAttrib('maxlength', 255) - ->setRequired( true ) - ->setLabel( 'Name' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new INEX_Filter_StripSlashes() ) - ->setErrorMessages( array( 'Name is required and cannot be empty' ) ); - - $this->addElement( $name ); - - - $dbCabinets = Doctrine_Query::create() - ->from( 'Cabinet' ) - ->orderBy( 'name ASC' ) - ->execute(); - - $cabinets = array( '0' => '' ); - $maxId = 0; - - foreach( $dbCabinets as $c ) - { - $cabinets[ $c['id'] ] = "{$c->Location->name} :: {$c['name']}"; - if( $c['id'] > $maxId ) $maxId = $c['id']; - } - - $cabinet = $this->createElement( 'select', 'cabinetid' ); - $cabinet->setMultiOptions( $cabinets ); - $cabinet->setRegisterInArrayValidator( true ) - ->setLabel( 'Cabinet' ) - ->addValidator( 'between', false, array( 1, $maxId ) ) - ->setErrorMessages( array( 'Please select a cabinet' ) ); - - $this->addElement( $cabinet ); - - - $colo_ref = $this->createElement( 'text', 'colo_ref' ); - $colo_ref->addValidator( 'stringLength', false, array( 1, 255 ) ) - ->setAttrib('size', 40 ) - ->setAttrib('maxlength', 255) - ->setRequired( true ) - ->setLabel( 'Colo Provider\'s Ref' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new INEX_Filter_StripSlashes() ); - - $this->addElement( $colo_ref ); - - - $cable_type = $this->createElement( 'select', 'cable_type' ); - $cable_type->setMultiOptions( PatchPanelPort::$CABLES_TYPES ) - ->setRegisterInArrayValidator( true ) - ->addValidator( 'greaterThan', true, array( 0 ) ) - ->setLabel( 'Cable Type' ) - ->setErrorMessages( array( 'Please set the cable type' ) ); - - $this->addElement( $cable_type ); - - $interface_type = $this->createElement( 'select', 'interface_type' ); - $interface_type->setMultiOptions( PatchPanelPort::$INTERFACE_TYPES ) - ->setRegisterInArrayValidator( true ) - ->addValidator( 'greaterThan', true, array( 0 ) ) - ->setLabel( 'Interface Type' ) - ->setErrorMessages( array( 'Please set the interface type' ) ); - - $this->addElement( $interface_type ); - - - $allow_duplex = $this->createElement( 'checkbox', 'allow_duplex' ); - $allow_duplex->setLabel( 'Allow Duplex?' ); - - $this->addElement( $allow_duplex ); - - - $notes = $this->createElement( 'textarea', 'notes' ); - $notes->setLabel( 'Notes' ) - ->setRequired( false ) - ->addFilter( new INEX_Filter_StripSlashes() ) - ->setAttrib( 'cols', 80 ) - ->setAttrib( 'rows', 6 ); - $this->addElement( $notes ); - - - $this->setElementDecorators( - array( - 'ViewHelper' - ) - ); - - } - -} - -?> \ No newline at end of file diff --git a/library/INEX/Form/SubForm/PatchPanelAutoGen.php b/library/INEX/Form/SubForm/PatchPanelAutoGen.php deleted file mode 100644 index 4c973bead..000000000 --- a/library/INEX/Form/SubForm/PatchPanelAutoGen.php +++ /dev/null @@ -1,87 +0,0 @@ -setDecorators( - array( - array( 'ViewScript', array( 'viewScript' => 'patch-panel/form/autogen.tpl' ) ) - ) - ); - - //////////////////////////////////////////////// - // Create and configure elements - //////////////////////////////////////////////// - - $autogen = $this->createElement( 'checkbox', 'cb_autogen' ); - $autogen->setLabel( 'Autogenerate ports for this patch panel?' ) - ->setAttrib( 'id', 'auto_gen_ports_cb' ); - - $this->addElement( $autogen ); - - $num_ports = $this->createElement( 'text', 'num_ports' ); - $num_ports->setAttrib('size', 4 ) - ->setAttrib( 'maxlength', 3 ) - ->setLabel( 'Number of Ports' ) - ->setErrorMessages( array( 'You must provide a numeric number of ports between 1 and 48' ) ); - - $this->addElement( $num_ports ); - - $edit = $this->createElement( 'checkbox', 'edit' ); - $edit->setLabel( 'Edit before committing to database?' ); - - $this->addElement( $edit ); - - $this->setElementDecorators( - array( - 'ViewHelper' - ) - ); - - } - -} - -?> \ No newline at end of file diff --git a/library/INEX/Form/SubForm/PatchPanelPort.php b/library/INEX/Form/SubForm/PatchPanelPort.php deleted file mode 100644 index 329ded660..000000000 --- a/library/INEX/Form/SubForm/PatchPanelPort.php +++ /dev/null @@ -1,112 +0,0 @@ -setDecorators( - array( - array( 'ViewScript', array( 'viewScript' => 'patch-panel/form/patch-panel-port.tpl' ) ) - ) - ); - - //////////////////////////////////////////////// - // Create and configure elements - //////////////////////////////////////////////// - - $port = $this->createElement( 'hidden', 'port' ); - $this->addElement( $port ); - - - $side = $this->createElement( 'hidden', 'side' ); - $this->addElement( $side ); - - $colo_ref = $this->createElement( 'text', 'colo_ref' ); - $colo_ref->addValidator( 'stringLength', false, array( 1, 255 ) ) - ->setAttrib('size', 40 ) - ->setAttrib('maxlength', 255) - ->setRequired( true ) - ->setLabel( 'Colo Provider\'s Ref' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new INEX_Filter_StripSlashes() ) - ->setErrorMessages( array( 'Colocation reference is required' ) ); - - $this->addElement( $colo_ref ); - - $type = $this->createElement( 'select', 'type' ); - $type->setMultiOptions( PatchPanelPort::$INTERFACE_TYPES ) - ->setRegisterInArrayValidator( true ) - ->addValidator( 'greaterThan', true, array( 0 ) ) - ->setLabel( 'Interface Type' ) - ->setErrorMessages( array( 'Please set the interface type' ) ); - - $this->addElement( $type ); - - - - $cable_type = $this->createElement( 'select', 'cable_type' ); - $cable_type->setMultiOptions( PatchPanelPort::$CABLES_TYPES ) - ->setRegisterInArrayValidator( true ) - ->addValidator( 'greaterThan', true, array( 0 ) ) - ->setLabel( 'Cable Type' ) - ->setErrorMessages( array( 'Please set the cable type' ) ); - - $this->addElement( $cable_type ); - - - $this->addElement( 'button', 'cancel', array( 'label' => 'Cancel', 'onClick' => "parent.location='" - . $cancelLocation . "'" ) ); - $this->addElement( 'submit', 'commit', array( 'label' => 'Add' ) ); - - $this->setElementDecorators( - array( - 'ViewHelper' - ) - ); - - } - -} - -?> \ No newline at end of file diff --git a/library/INEX/Form/Switch.php b/library/INEX/Form/Switch.php deleted file mode 100644 index dc43427ef..000000000 --- a/library/INEX/Form/Switch.php +++ /dev/null @@ -1,156 +0,0 @@ - - * @category INEX - * @package INEX_Form - * @copyright Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd - * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 - */ -class INEX_Form_Switch extends INEX_Form -{ - public function init() - { - - $name = $this->createElement( 'text', 'name' ); - $name->addValidator( 'stringLength', false, array( 1, 255 ) ) - ->setAttrib( 'class', 'span3' ) - ->setRequired( true ) - ->setLabel( 'Name' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $name ); - - $switchtype = $this->createElement( 'select', 'switchtype' ); - $switchtype->setMultiOptions( \Entities\Switcher::$TYPES ) - ->setAttrib( 'class', 'span3 chzn-select' ) - ->setRegisterInArrayValidator( true ) - ->addValidator( 'greaterThan', true, array( 0 ) ) - ->setLabel( 'Type' ) - ->setErrorMessages( array( 'Please set the switch type' ) ); - $this->addElement( $switchtype ); - - $this->addElement( INEX_Form_Cabinet::getPopulatedSelect( 'cabinetid' ) ); - - $infrastructre = $this->createElement( 'text', 'infrastructure' ); - $infrastructre->setLabel( 'Infrastructure' ) - ->setAttrib( 'class', 'span3' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $infrastructre ); - - - $ipv4addr = $this->createElement( 'text', 'ipv4addr' ); - $ipv4addr->addValidator( 'stringLength', false, array( 1, 255 ) ) - ->setAttrib( 'class', 'span3' ) - ->setRequired( true ) - ->setLabel( 'IPv4 Address' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $ipv4addr ); - - $ipv6addr = $this->createElement( 'text', 'ipv6addr' ); - $ipv6addr->addValidator( 'stringLength', false, array( 1, 255 ) ) - ->setAttrib( 'class', 'span3' ) - ->setLabel( 'IPv6 Address' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $ipv6addr ); - - $snmppasswd = $this->createElement( 'text', 'snmppasswd' ); - $snmppasswd->addValidator( 'stringLength', false, array( 1, 255 ) ) - ->setAttrib( 'class', 'span3' ) - ->setLabel( 'SNMP Community' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $snmppasswd ); - - $this->addElement( INEX_Form_Vendor::getPopulatedSelect( 'vendorid' ) ); - - - $model = $this->createElement( 'text', 'model' ); - $model->addValidator( 'stringLength', false, array( 1, 255 ) ) - ->setLabel( 'Model' ) - ->setAttrib( 'class', 'span3' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $model ); - - - $notes = $this->createElement( 'textarea', 'notes' ); - $notes->setLabel( 'Notes' ) - ->setAttrib( 'class', 'span3' ) - ->setRequired( false ) - ->addFilter( new OSS_Filter_StripSlashes() ) - ->setAttrib( 'cols', 60 ) - ->setAttrib( 'rows', 5 ); - $this->addElement( $notes ); - - $active = $this->createElement( 'checkbox', 'active' ); - $active->setLabel( 'Active?' ) - ->setCheckedValue( '1' ) - ->setUncheckedValue( '0' ) - ->setValue( '1' ); - $this->addElement( $active ); - - - $this->addElement( self::createSubmitElement( 'submit', _( 'Add' ) ) ); - $this->addElement( $this->createCancelElement() ); - } - - /** - * Create a SELECT / dropdown element of all switch names indexed by their id. - * - * @param string $name The element name - * @return Zend_Form_Element_Select The select element - */ - public static function getPopulatedSelect( $name = 'switchid', $type = null ) - { - $sw = new Zend_Form_Element_Select( $name ); - - $qb = Zend_Registry::get( 'd2em' )['default']->createQueryBuilder() - ->select( 'e.id AS id, e.name AS name' ) - ->from( '\\Entities\\Switcher', 'e' ) - ->orderBy( "e.name", 'ASC' ); - - if( $type !== null ) - $qb->where( 'e.switchtype = ?1' )->setParameter( 1, $type ); - - $maxId = self::populateSelectFromDatabaseQuery( $qb->getQuery(), $sw, '\\Entities\\Switcher', 'id', 'name', 'name', 'ASC' ); - - $sw->setRegisterInArrayValidator( true ) - ->setRequired( true ) - ->setLabel( _( 'Switch' ) ) - ->setAttrib( 'class', 'span3 chzn-select' ) - ->addValidator( 'between', false, array( 1, $maxId ) ) - ->setErrorMessages( array( _( 'Please select a switch' ) ) ); - - return $sw; - } - - -} diff --git a/library/INEX/Form/Switch/AddPorts.php b/library/INEX/Form/Switch/AddPorts.php deleted file mode 100644 index 3f74acbac..000000000 --- a/library/INEX/Form/Switch/AddPorts.php +++ /dev/null @@ -1,68 +0,0 @@ - - * @category INEX - * @package INEX_Form - * @copyright Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd - * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 - */ -class INEX_Form_Switch_AddPorts extends INEX_Form -{ - public function init() - { - $this->setDecorators( [ [ 'ViewScript', [ 'viewScript' => 'switch-port/forms/add-ports.phtml' ] ] ] ); - - $this->addElement( INEX_Form_Switch::getPopulatedSelect( 'switchid' ) ); - - $deftype = $this->createElement( 'select', 'deftype' ); - $deftype->setMultiOptions( \Entities\SwitchPort::$TYPES ) - ->setAttrib( 'class', 'chzn-select span3' ) - ->setRegisterInArrayValidator( true ) - ->setLabel( 'Default Type' ); - $this->addElement( $deftype ); - - $numfirst = $this->createElement( 'text', 'numfirst' ); - $numfirst->addValidator('int') - ->addValidator( 'greaterThan', false, array( 0 ) ) - ->setRequired( true ) - ->setAttrib( 'class', 'span3' ) - ->setLabel( 'Number of First Port' ); - $this->addElement( $numfirst ); - - $numports = $this->createElement( 'text', 'numports' ); - $numports->addValidator('int') - ->addValidator( 'greaterThan', false, array( 0 ) ) - ->setRequired( true ) - ->setAttrib( 'class', 'span3' ) - ->setLabel( 'Number of Ports' ); - $this->addElement( $numports ); - - $this->addElement( self::createSubmitElement( 'submit', _( 'Add Ports' ) ) ); - $this->addElement( $this->createCancelElement() ); - } -} diff --git a/library/INEX/Form/Switch/Port.php b/library/INEX/Form/Switch/Port.php deleted file mode 100644 index 7d631a375..000000000 --- a/library/INEX/Form/Switch/Port.php +++ /dev/null @@ -1,58 +0,0 @@ - - * @category INEX - * @package INEX_Form - * @copyright Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd - * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 - */ -class INEX_Form_Switch_Port extends INEX_Form -{ - public function init() - { - $this->addElement( INEX_Form_Switch::getPopulatedSelect( 'switchid' ) ); - - $name = $this->createElement( 'text', 'name' ); - $name->setLabel( 'Name' ) - ->setAttrib( 'class', 'span3' ); - $this->addElement( $name ); - - $type = $this->createElement( 'select', 'type' ); - $type->setMultiOptions( \Entities\SwitchPort::$TYPES ) - ->setRegisterInArrayValidator( true ) - ->addValidator( 'greaterThan', true, array( 0 ) ) - ->setLabel( 'Type' ) - ->setAttrib( 'class', 'span3 chzn-select' ) - ->setErrorMessages( array( 'Please set the port type' ) ); - $this->addElement( $type ); - - $this->addElement( self::createSubmitElement( 'submit', _( 'Add' ) ) ); - $this->addElement( $this->createCancelElement() ); - } -} - diff --git a/library/INEX/Form/User.php b/library/INEX/Form/User.php deleted file mode 100644 index 7d2c967ca..000000000 --- a/library/INEX/Form/User.php +++ /dev/null @@ -1,125 +0,0 @@ - - * @category INEX - * @package INEX_Form - * @copyright Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd - * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 - */ -class INEX_Form_User extends INEX_Form -{ - public function init() - { - //////////////////////////////////////////////// - // Create and configure elements - //////////////////////////////////////////////// - - // let's capture the user's name and add them to the contact table also - $name = $this->createElement( 'text', 'name' ); - $name->addValidator( 'stringLength', false, array( 2, 64 ) ) - ->setRequired( true ) - ->setAttrib( 'size', 50 ) - ->setAttrib( 'class', 'span3' ) - ->setLabel( 'Name' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $name ); - - - - - $username = OSS_Form_Auth::createUsernameElement(); - - $username->addValidator( 'stringLength', false, array( 2, 30 ) ) - ->addValidator( 'regex', true, array( '/^[a-zA-Z0-9\-_\.]+$/' ) ); - - $this->addElement( $username ); - - - - - $password = $this->createElement( 'text', 'password' ); - $password->addValidator( 'stringLength', false, array( 8, 30 ) ) - ->setRequired( true ) - ->setAttrib( 'class', 'span3' ) - ->setLabel( 'Password' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $password ); - - - - - - $privileges = $this->createElement( 'select', 'privs' ); - $privileges->setMultiOptions( \Entities\User::$PRIVILEGES_TEXT ) - ->setRegisterInArrayValidator( true ) - ->setLabel( 'Privileges' ) - ->setAttrib( 'class', 'span3 chzn-select' ) - ->setErrorMessages( array( 'Please select the users privilege level' ) ); - - $this->addElement( $privileges ); - - - - $this->addElement( OSS_Form_User::createEmailElement() ); - - - - $this->addElement( self::createMobileElement( 'authorisedMobile' ) ); - - $this->addElement( INEX_Form_Customer::getPopulatedSelect( 'custid' ) ); - - - $disabled = $this->createElement( 'checkbox', 'disabled' ); - $disabled->setLabel( 'Disabled?' ) - ->setCheckedValue( '1' ); - $this->addElement( $disabled ); - - - $this->addElement( self::createSubmitElement( 'submit', _( 'Add' ) ) ); - $this->addElement( $this->createCancelElement() ); - - } - - - public static function createMobileElement( $name = 'mobile' ) - { - $mobile = new Zend_Form_Element_Text( $name ); - - $mobile->addValidator( 'stringLength', false, array( 0, 30 ) ) - ->setRequired( false ) - ->setLabel( 'Mobile' ) - ->setAttrib( 'placeholder', '+353 86 123 4567' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - - return $mobile; - } -} - diff --git a/library/INEX/Form/Vendor.php b/library/INEX/Form/Vendor.php deleted file mode 100644 index ada592136..000000000 --- a/library/INEX/Form/Vendor.php +++ /dev/null @@ -1,74 +0,0 @@ - - * @category INEX - * @package INEX_Form - * @copyright Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd - * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 - */ -class INEX_Form_Vendor extends INEX_Form -{ - public function init() - { - $name = $this->createElement( 'text', 'name' ); - $name->addValidator( 'stringLength', false, array( 1, 255 ) ) - ->setRequired( true ) - ->setAttrib( 'class', 'span3' ) - ->setLabel( 'Name' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - - $this->addElement( $name ); - - $this->addElement( self::createSubmitElement( 'submit', _( 'Add' ) ) ); - $this->addElement( $this->createCancelElement() ); - } - - /** - * Create a SELECT / dropdown element of all vendor names indexed by their id. - * - * @param string $name The element name - * @return Zend_Form_Element_Select The select element - */ - public static function getPopulatedSelect( $name = 'vendorid' ) - { - $v = new Zend_Form_Element_Select( $name ); - - $maxId = self::populateSelectFromDatabase( $v, '\\Entities\\Vendor', 'id', 'name', 'name', 'ASC' ); - - $v->setRegisterInArrayValidator( true ) - ->setRequired( true ) - ->setLabel( _( 'Vendor' ) ) - ->setAttrib( 'class', 'span3 chzn-select' ) - ->addValidator( 'between', false, array( 1, $maxId ) ) - ->setErrorMessages( array( _( 'Please select a vendor' ) ) ); - - return $v; - } -} - diff --git a/library/INEX/Form/Vlan.php b/library/INEX/Form/Vlan.php deleted file mode 100644 index 1fe5b62af..000000000 --- a/library/INEX/Form/Vlan.php +++ /dev/null @@ -1,100 +0,0 @@ - - * @category INEX - * @package INEX_Form - * @copyright Copyright (c) 2009 - 2012, Internet Neutral Exchange Association Ltd - * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL V2.0 - */ -class INEX_Form_VLAN extends INEX_Form -{ - public function init() - { - - $name = $this->createElement( 'text', 'name' ); - $name->addValidator( 'stringLength', false, array( 1, 255 ) ) - ->setRequired( true ) - ->setLabel( 'Name' ) - ->setAttrib( 'class', 'span3' ) - ->addFilter( 'StringTrim' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $name ); - - $number = $this->createElement( 'text', 'number' ); - $number->addValidator( 'int' ) - ->addValidator( 'between', false, array( 1, 4096 ) ) - ->setRequired( true ) - ->setLabel( 'Tag' ) - ->setAttrib( 'class', 'span3' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $number ); - - $rcvrfname = $this->createElement( 'text', 'rcvrfname' ); - $rcvrfname->addValidator( 'stringLength', false, array( 1, 255 ) ) - ->setLabel( 'RC VRF Name' ) - ->addFilter( 'StringTrim' ) - ->setAttrib( 'class', 'span3' ) - ->addFilter( new OSS_Filter_StripSlashes() ); - $this->addElement( $rcvrfname ); - - $notes = $this->createElement( 'textarea', 'notes' ); - $notes->setLabel( 'Notes' ) - ->setRequired( false ) - ->addFilter( new OSS_Filter_StripSlashes() ) - ->setAttrib( 'cols', 60 ) - ->setAttrib( 'class', 'span3' ) - ->setAttrib( 'rows', 5 ); - $this->addElement( $notes ); - - $this->addElement( self::createSubmitElement( 'submit', _( 'Add' ) ) ); - $this->addElement( $this->createCancelElement() ); - } - - /** - * Create a SELECT / dropdown element of all VLAN names indexed by their id. - * - * @param string $name The element name - * @return Zend_Form_Element_Select The select element - */ - public static function getPopulatedSelect( $name = 'vlanid' ) - { - $vlan = new Zend_Form_Element_Select( $name ); - - $maxId = self::populateSelectFromDatabase( $vlan, '\\Entities\\Vlan', 'id', 'name', 'name', 'ASC' ); - - $vlan->setRegisterInArrayValidator( true ) - ->setRequired( true ) - ->setLabel( _( 'VLAN' ) ) - ->setAttrib( 'class', 'span3 chzn-select' ) - ->addValidator( 'between', false, array( 1, $maxId ) ) - ->setErrorMessages( array( _( 'Please select a VLAN' ) ) ); - - return $vlan; - } - -} diff --git a/library/INEX/Mrtg.php b/library/INEX/Mrtg.php deleted file mode 100644 index 8085085ff..000000000 --- a/library/INEX/Mrtg.php +++ /dev/null @@ -1,563 +0,0 @@ - INEX_Mrtg::PERIOD_DAY, - 'Week' => INEX_Mrtg::PERIOD_WEEK, - 'Month' => INEX_Mrtg::PERIOD_MONTH, - 'Year' => INEX_Mrtg::PERIOD_YEAR - ); - - - /** - * Period times. - * - * these values are taken from mrtg/src/rateup.c - */ - public static $PERIOD_TIME = array( - 'day' => 119988.0, //( 33.33 * 3600 ), - 'week' => 719712.0, // ( 8.33 * 24 * 3600 ), - 'month' => 2879712.0, // ( 33.33 * 24 * 3600 ), - 'year' => 31622400.0 // ( 366 * 24 * 3600 ) - ); - - - /** - * 'Bits' category for MRTG data and graphs - */ - const CATEGORY_BITS = 'bits'; - - /** - * 'Packets' category for MRTG data and graphs - */ - const CATEGORY_PACKETS = 'pkts'; - - /** - * 'Errors' category for MRTG data and graphs - */ - const CATEGORY_ERRORS = 'errs'; - - /** - * 'Discards' category for MRTG data and graphs - */ - const CATEGORY_DISCARDS = 'discs'; - - /** - * Array of valid categories for graphs - */ - public static $CATEGORIES = array( - 'Bits' => INEX_Mrtg::CATEGORY_BITS, - 'Packets' => INEX_Mrtg::CATEGORY_PACKETS, - 'Errors' => INEX_Mrtg::CATEGORY_ERRORS, - 'Discards' => INEX_Mrtg::CATEGORY_DISCARDS - ); - - /** - * Array of valid categories for aggregate graphs - */ - public static $CATEGORIES_AGGREGATE = array( - 'Bits' => INEX_Mrtg::CATEGORY_BITS, - 'Packets' => INEX_Mrtg::CATEGORY_PACKETS - ); - - /** - * Protocols for MRTG data and graphs - */ - const PROTOCOL_IPV4 = 4; - - /** - * Protocols for MRTG data and graphs - */ - const PROTOCOL_IPV6 = 6; - - /** - * Array of valid protocols - */ - public static $PROTOCOLS = array( - 'IPv4' => INEX_Mrtg::PROTOCOL_IPV4, - 'IPv6' => INEX_Mrtg::PROTOCOL_IPV6 - ); - - - /** - * Infrastructures for MRTG data and graphs - */ - const INFRASTRUCTURE_PRIMARY = 1; - - /** - * Infrastructures for MRTG data and graphs - */ - const INFRASTRUCTURE_SECONDARY = 2; - - - /** - * Array of valid infrastructures - */ - public static $INFRASTRUCTURES = array( - 'Primary' => INEX_Mrtg::INFRASTRUCTURE_PRIMARY, - 'Secondary' => INEX_Mrtg::INFRASTRUCTURE_SECONDARY - ); - - /** - * Infrastructures - */ - public static $INFRASTRUCTURES_TEXT = array( - self::INFRASTRUCTURE_PRIMARY => 'Primary Infrastructure', - self::INFRASTRUCTURE_SECONDARY => 'Secondary Infrastructure' - ); - - - - /** - * Class constructor. - * - * @param $file The MRTG log file to load for analysis - */ - function __construct( $file = null ) - { - $this->file = $file; - $this->loadmrtgfile(); - } - - - /** - * Returns the full absolute path to an MRTG graphing file. - * - * This function will sanitise and correct with defaults and inappropriate - * values for $monitorindex, $category, and $period. $shortname is not sanitised - * and it is expected that you have already done this. - * - * @param $mrtgPath The base path on the filesystem to the MRTG files. - * @param $type The file type to return. options are 'PNG' for an image file, 'LOG' for an MRTG log file. - * @param $monitorindex Integer representing the interface number or 'aggregate'. - * @param $category A value from INEX_Mrtg::$CATEGORIES - * @param $shortname The customer's shortname - * @param $period A value from INEX_Mrtg::$PERIODS - * @return string The full absolute path and filename - */ - static function getMrtgFilePath( $mrtgPath, - $type = 'PNG', - $monitorindex = 'aggregate', - $category = 'bits', - $shortname, - $period = 'day' - ) - { - // sanitise these things carefully - if( is_numeric( $monitorindex ) && $monitorindex > 10 ) - $monitorindex = '1'; - - if( !is_numeric( $monitorindex ) && $monitorindex != 'aggregate' ) - $monitorindex = 'aggregate'; - - if( !in_array( $period, INEX_Mrtg::$PERIODS ) ) - $period = INEX_Mrtg::$PERIODS['Day']; - - if( !in_array( $category, INEX_Mrtg::$CATEGORIES ) ) - $category = INEX_Mrtg::$CATEGORIES['Bits']; - - $memberdir = $mrtgPath . '/' . $shortname; - - switch( $type ) - { - case 'PNG': - return $memberdir . '/' - . $shortname .'-'.$monitorindex .'-'.$category.'-'. $period .'.png'; - break; - - case 'LOG': - return $memberdir . '/' - . $shortname .'-'.$monitorindex .'-'.$category . '.log'; - break; - - default: - return ''; - } - } - - /** - * Returns the full absolute path to an MRTG P2P graph file. - * - * This function assumes appropriate values and it is expected that - * you have already sanitised these. - * - * @param $mrtgPath The base path on the filesystem to the MRTG files. - * @param $svid Integer representing the virtual interface of the source virtual interface - * @param $dvid Integer representing the virtual interface of the dest virtual interface - * @param $category A value from INEX_Mrtg::$CATEGORIES - * @param $period A value from INEX_Mrtg::$PERIODS - * @param $proto A value from INEX_Mrtg::$PROTOCOLS - * @return string The full absolute path and filename - */ - static function getMrtgP2pFilePath( $mrtgPath, - $svid, - $dvid, - $category = 'bits', - $period = 'day', - $proto = 4 - ) - { - // sanitise these things carefully - return $mrtgPath - . "?srcvid={$svid}" - . "&dstvid={$dvid}" - . "&protocol={$proto}" - . "&type={$category}" - . "&period={$period}"; - } - - /** - * Utility function to generate URLs for grabbing graph images. - * - * FIXME This isn't the right place for this but I'm not sure what is - * right now. - * - * @param array $params Array of parameters to make up the URL - * @return string The URL - */ - public static function generateZendFrontendUrl( $params ) - { - $url = Zend_Controller_Front::getInstance()->getBaseUrl(); - - if( isset( $params['p2p'] ) && $params['p2p'] ) - $url .= '/mrtg/retrieve-p2p-image'; - else - $url .= '/mrtg/retrieve-image'; - - if( isset( $params['shortname'] ) ) - $url .= "/shortname/{$params['shortname']}"; - - if( isset( $params['monitorindex'] ) ) - $url .= "/monitorindex/{$params['monitorindex']}"; - else if( !isset( $params['p2p'] ) || !$params['p2p'] ) - $url .= "/monitorindex/aggregate"; - - if( isset( $params['period'] ) ) - $url .= "/period/{$params['period']}"; - else - $url .= "/period/day"; - - if( isset( $params['category'] ) ) - $url .= "/category/{$params['category']}"; - else - $url .= "/category/bits"; - - if( isset( $params['p2p'] ) && $params['p2p'] ) - { - if( isset( $params['svid'] ) ) - $url .= "/svid/{$params['svid']}"; - else - die(); - - if( isset( $params['dvid'] ) ) - $url .= "/dvid/{$params['dvid']}"; - else - die(); - - if( isset( $params['proto'] ) ) - $url .= "/proto/{$params['proto']}"; - else - $url .= "/proto/4"; - - if( isset( $params['infra'] ) ) - $url .= "/infra/{$params['infra']}"; - else - $url .= "/infra/1"; - } - - if( isset( $params['graph'] ) ) - $url .= "/graph/{$params['graph']}"; - - return $url; - } - - function getPeriod( $period ) - { - if( isset( INEX_Mrtg::$PERIOD_TIME[ $period ] ) ) - return INEX_Mrtg::$PERIOD_TIME[ $period ]; - else - return null; - } - - /** - * Scale function - * - * This function will scale a number to (for example for traffic - * measured in bits/second) to Kbps, Mbps, Gbps or Tbps. - * - * Valid string formats ($strFormats) and what they return are: - * bytes => Bytes, KBytes, MBytes, GBytes, TBytes - * pkts / errs / discs => pps, Kpps, Mpps, Gpps, Tpps - * bits / * => bits, Kbits, Mbits, Gbits, Tbits - * - * Valid return types ($intReturn) are: - * 0 => fully formatted and scaled value. E.g. 12,354.235 Tbits - * 1 => scaled value without string. E.g. 12,354.235 - * 2 => just the string. E.g. Tbits - * - * @param $intNumber The integer value to scale - * @param $strFormat The string format to use. Valid values are listed above. Defaults to 'bits'. - * @param $intDem Number of decimals after the decimal point. Defaults to 3. - * @param $intReturn Type of string to return. Valid values are listed above. Defaults to 0. - * @return string Scaled / formatted number / type. - */ - public static function scale( $intNumber, $strFormat = 'bits', $intDem = 3, $intReturn = 0 ) - { - if( $strFormat == "bytes" ) - { - $arrFormat = array( - "Bytes", "KBytes", "MBytes", "GBytes", "TBytes" - ); - } - else if( in_array( $strFormat, array( 'pkts', 'errs', 'discs' ) ) ) - { - $arrFormat = array( - "pps", "Kpps", "Mpps", "Gpps", "Tpps" - ); - } - else - { - $intNumber = $intNumber * 8; - $arrFormat = array( - "bits", "Kbits", "Mbits", "Gbits", "Tbits" - ); - } - - for( $i = 0; $i < sizeof( $arrFormat ); $i++ ) - { - if( ( $intNumber / 1000 < 1 ) || ( sizeof( $arrFormat ) == $i + 1 ) ) - { - if( !$intReturn ) - return number_format( $intNumber, $intDem ) . " $arrFormat[$i]"; - elseif( $intReturn == 1 ) - return number_format( $intNumber, $intDem ); - elseif( $intReturn == 2 ) - return "$arrFormat[$i]"; - break; - } - else - { - $intNumber = $intNumber / 1000; - } - } - } - - - function loadmrtgfile() - { - # Create the array and define other expressions i want to use - $arrValues = array(); - - // Log files are available over HTTP from the monitoring server but - // are sometimes unavailable during a log update / rebuild / etc. - // As such, try a reasonable number of times for this infrequent - // occurance to get a good chance of getting the file. - for( $i = 0; $i <10; $i++ ) - { - if( $fd = @fopen( $this->file, "r" ) ) - break; - - sleep( 2 ); - } - - if( !$fd ) - { - $this->array = array(); - return; - } - - - # Take the values inside the time frame and load them into the array - $intCounter = 0; - while( !feof( $fd ) ) - { - $strLina = fgets( $fd, 4096 ); - $arrLine = explode( " ", $strLina ); - - // need to convert $arrLine[4] from string to float, - // because it contains a trailing - if( isset( $arrLine[4] ) ) - $arrLine[ 4 ] = floor( $arrLine[ 4 ] ); - else - $arrLine[ 4 ] = 0; - - if( $arrLine[ 0 ] ) { - $arrValues[ $intCounter ] = array( - $arrLine[ 0 ], - $arrLine[ 1 ], - $arrLine[ 2 ], - isset( $arrLine[ 3 ] ) ? $arrLine[ 3 ] : 0, - isset( $arrLine[ 4 ] ) ? $arrLine[ 4 ] : 0 - ); - $intCounter++; - } - } - - fclose( $fd ); - - $this->array = $arrValues; - } - - function getValues( $period = null, $category = 'bits', $doScale = true ) - { - $periodsecs = $this->getPeriod( $period ); - - if( !isset( $periodsecs ) ) - return null; - - $starttime = time() - $periodsecs; - $endtime = time(); - - $maxIn = 0; - $maxOut = 0; - $totalIn = 0; - $totalOut = 0; - $intLastTime = 0; - $intTime = 0; - - $gotrealstartdate = false; - $curenddate = false; - - $arrValues = $this->array; - - # Run through the array and start calculating - for( $i = sizeof( $arrValues ) - 1; $i > 0; $i-- ) { - # *** get start and end time - if( ($arrValues[ $i ][ 0 ] < $starttime) || ($arrValues[ $i ][ 0 ] > $endtime) ) { - continue; - } - - $curenddate = $arrValues[ $i ][ 0 ]; - - # we depend on array index monotonically increasing - if( !$gotrealstartdate && $arrValues[ $i ][ 0 ] ) { - $starttime = $arrValues[ $i ][ 0 ]; - $gotrealstartdate = true; - } - - list( $intTime, $avgratein, $avgrateout, $peakratein, $peakrateout ) = $arrValues[ $i ]; - - if( $peakratein > $maxIn ) { - $maxIn = $peakratein; - } - if( $peakrateout > $maxOut ) { - $maxOut = $peakrateout; - } - - if( $intLastTime == 0 ) { - $intLastTime = $intTime; - } - else { - $intRange = $intTime - $intLastTime; - $totalIn += $intRange * $avgratein; - $totalOut += $intRange * $avgrateout; - $intLastTime = $intTime; - } - } - - $endtime = $curenddate; - $totalTime = $endtime - $starttime; - - $curIn = isset( $avgratein ) ? $avgratein : 0.0; - $curOut = isset( $avgrateout ) ? $avgrateout : 0.0; - - // we use floor() to convert all values to float and round them suitably - return( - array( - 'totalin' => $totalIn, - 'totalout' => $totalOut, - 'curin' => $doScale ? $this->scale( $curIn, $category, 3, 0 ) : $curIn, - 'curout' => $doScale ? $this->scale( $curOut, $category, 3, 0 ) : $curOut, - 'averagein' => $doScale ? $this->scale( $totalIn / $totalTime, $category, 3, 0 ) : $totalIn / $totalTime, - 'averageout' => $doScale ? $this->scale( $totalOut / $totalTime, $category, 3, 0 ) : $totalOut / $totalTime, - 'maxin' => $doScale ? $this->scale( $maxIn, $category, 3, 0 ) : $maxIn, - 'maxout' => $doScale ? $this->scale( $maxOut, $category, 3, 0 ) : $maxOut - ) - ); - } - - /** - * Accessor method for $array - the data from the MRTG file. - * - * @return array The data from the MRTG file - */ - public function getArray() - { - return $this->array; - } - -} - diff --git a/library/INEX/Version.php b/library/INEX/Version.php deleted file mode 100644 index d480fd00e..000000000 --- a/library/INEX/Version.php +++ /dev/null @@ -1,8 +0,0 @@ - | - | Rasmus Lerdorf | - | Ilia Alshanetsky | - +----------------------------------------------------------------------+ - - All other licensing and usage conditions are those of the PHP Group. - - */ - -$VERSION='$Id: apc.php,v 3.68.2.2 2008/05/11 18:57:00 rasmus Exp $'; - -////////// READ OPTIONAL CONFIGURATION FILE //////////// -if (file_exists("apc.conf.php")) include("apc.conf.php"); -//////////////////////////////////////////////////////// - -////////// BEGIN OF DEFAULT CONFIG AREA /////////////////////////////////////////////////////////// - -defaults('USE_AUTHENTICATION',0); // Use (internal) authentication - best choice if - // no other authentication is available - // If set to 0: - // There will be no further authentication. You - // will have to handle this by yourself! - // If set to 1: - // You need to change ADMIN_PASSWORD to make - // this work! -defaults('ADMIN_USERNAME','apc'); // Admin Username -defaults('ADMIN_PASSWORD','password'); // Admin Password - CHANGE THIS TO ENABLE!!! - -// (beckerr) I'm using a clear text password here, because I've no good idea how to let -// users generate a md5 or crypt password in a easy way to fill it in above - -//defaults('DATE_FORMAT', "d.m.Y H:i:s"); // German -defaults('DATE_FORMAT', 'Y/m/d H:i:s'); // US - -defaults('GRAPH_SIZE',200); // Image size - -////////// END OF DEFAULT CONFIG AREA ///////////////////////////////////////////////////////////// - - -// "define if not defined" -function defaults($d,$v) { - if (!defined($d)) define($d,$v); // or just @define(...) -} - -// rewrite $PHP_SELF to block XSS attacks -// -$PHP_SELF= $BU; //isset($_SERVER['PHP_SELF']) ? htmlentities(strip_tags($_SERVER['PHP_SELF'],''), ENT_QUOTES) : ''; -$time = time(); -$host = getenv('HOSTNAME'); -if($host) { $host = '('.$host.')'; } - -// operation constants -define('OB_HOST_STATS',1); -define('OB_SYS_CACHE',2); -define('OB_USER_CACHE',3); -define('OB_SYS_CACHE_DIR',4); -define('OB_VERSION_CHECK',9); - -// check validity of input variables -$vardom=array( - 'OB' => '/^\d+$/', // operational mode switch - 'CC' => '/^[01]$/', // clear cache requested - 'DU' => '/^.*$/', // Delete User Key - 'SH' => '/^[a-z0-9]+$/', // shared object description - - 'IMG' => '/^[123]$/', // image to generate - 'LO' => '/^1$/', // login requested - - 'COUNT' => '/^\d+$/', // number of line displayed in list - 'SCOPE' => '/^[AD]$/', // list view scope - 'SORT1' => '/^[AHSMCDTZ]$/', // first sort key - 'SORT2' => '/^[DA]$/', // second sort key - 'AGGR' => '/^\d+$/', // aggregation by dir level - 'SEARCH' => '~^[a-zA-Z0-1/_.-]*$~' // aggregation by dir level -); - -// default cache mode -$cache_mode='opcode'; - -// cache scope -$scope_list=array( - 'A' => 'cache_list', - 'D' => 'deleted_list' -); - -// handle POST and GET requests -if (empty($_REQUEST)) { - if (!empty($_GET) && !empty($_POST)) { - $_REQUEST = array_merge($_GET, $_POST); - } else if (!empty($_GET)) { - $_REQUEST = $_GET; - } else if (!empty($_POST)) { - $_REQUEST = $_POST; - } else { - $_REQUEST = array(); - } -} - -// check parameter syntax -foreach($vardom as $var => $dom) { - if (!isset($_REQUEST[$var])) { - $MYREQUEST[$var]=NULL; - } else if (!is_array($_REQUEST[$var]) && preg_match($dom.'D',$_REQUEST[$var])) { - $MYREQUEST[$var]=$_REQUEST[$var]; - } else { - $MYREQUEST[$var]=$_REQUEST[$var]=NULL; - } -} - -// check parameter sematics -if (empty($MYREQUEST['SCOPE'])) $MYREQUEST['SCOPE']="A"; -if (empty($MYREQUEST['SORT1'])) $MYREQUEST['SORT1']="H"; -if (empty($MYREQUEST['SORT2'])) $MYREQUEST['SORT2']="D"; -if (empty($MYREQUEST['OB'])) $MYREQUEST['OB']=OB_HOST_STATS; -if (!isset($MYREQUEST['COUNT'])) $MYREQUEST['COUNT']=20; -if (!isset($scope_list[$MYREQUEST['SCOPE']])) $MYREQUEST['SCOPE']='A'; - -$MY_SELF= - "$PHP_SELF". - "?SCOPE=".$MYREQUEST['SCOPE']. - "&SORT1=".$MYREQUEST['SORT1']. - "&SORT2=".$MYREQUEST['SORT2']. - "&COUNT=".$MYREQUEST['COUNT']; -$MY_SELF_WO_SORT= - "$PHP_SELF". - "?SCOPE=".$MYREQUEST['SCOPE']. - "&COUNT=".$MYREQUEST['COUNT']; - -$GLOBALS['MY_SELF'] = $MY_SELF; -$GLOBALS['MY_SELF_WO_SORT'] = $MY_SELF_WO_SORT; - -// authentication needed? -// -if (!USE_AUTHENTICATION) { - $AUTHENTICATED=1; -} else { - $AUTHENTICATED=0; - if (ADMIN_PASSWORD!='password' && ($MYREQUEST['LO'] == 1 || isset($_SERVER['PHP_AUTH_USER']))) { - - if (!isset($_SERVER['PHP_AUTH_USER']) || - !isset($_SERVER['PHP_AUTH_PW']) || - $_SERVER['PHP_AUTH_USER'] != ADMIN_USERNAME || - $_SERVER['PHP_AUTH_PW'] != ADMIN_PASSWORD) { - Header("WWW-Authenticate: Basic realm=\"APC Login\""); - Header("HTTP/1.0 401 Unauthorized"); - - echo << -

    Rejected!

    - Wrong Username or Password!
     
      - Continue... - -EOB; - exit; - - } else { - $AUTHENTICATED=1; - } - } -} - -$GLOBALS['AUTHENTICATED'] = $AUTHENTICATED; - -// select cache mode -if ($AUTHENTICATED && $MYREQUEST['OB'] == OB_USER_CACHE) { - $cache_mode='user'; -} -// clear cache -if ($AUTHENTICATED && isset($MYREQUEST['CC']) && $MYREQUEST['CC']) { - apc_clear_cache($cache_mode); -} - -if ($AUTHENTICATED && !empty($MYREQUEST['DU'])) { - apc_delete($MYREQUEST['DU']); -} - -if(!function_exists('apc_cache_info') || !($cache=@apc_cache_info($cache_mode))) { - echo "No cache info available. APC does not appear to be running."; - exit; -} - -$cache_user = apc_cache_info('user', 1); -$mem=apc_sma_info(); -if(!$cache['num_hits']) { $cache['num_hits']=1; $time++; } // Avoid division by 0 errors on a cache clear - -// don't cache this page -// -header("Cache-Control: no-store, no-cache, must-revalidate"); // HTTP/1.1 -header("Cache-Control: post-check=0, pre-check=0", false); -header("Pragma: no-cache"); // HTTP/1.0 - -function duration($ts) { - global $time; - $years = (int)((($time - $ts)/(7*86400))/52.177457); - $rem = (int)(($time-$ts)-($years * 52.177457 * 7 * 86400)); - $weeks = (int)(($rem)/(7*86400)); - $days = (int)(($rem)/86400) - $weeks*7; - $hours = (int)(($rem)/3600) - $days*24 - $weeks*7*24; - $mins = (int)(($rem)/60) - $hours*60 - $days*24*60 - $weeks*7*24*60; - $str = ''; - if($years==1) $str .= "$years year, "; - if($years>1) $str .= "$years years, "; - if($weeks==1) $str .= "$weeks week, "; - if($weeks>1) $str .= "$weeks weeks, "; - if($days==1) $str .= "$days day,"; - if($days>1) $str .= "$days days,"; - if($hours == 1) $str .= " $hours hour and"; - if($hours>1) $str .= " $hours hours and"; - if($mins == 1) $str .= " 1 minute"; - else $str .= " $mins minutes"; - return $str; -} - -// create graphics -// -function graphics_avail() { - return extension_loaded('gd'); -} -if (isset($MYREQUEST['IMG'])) -{ - if (!graphics_avail()) { - exit(0); - } - - function fill_arc($im, $centerX, $centerY, $diameter, $start, $end, $color1,$color2,$text='',$placeindex=0) { - $r=$diameter/2; - $w=deg2rad((360+$start+($end-$start)/2)%360); - - - if (function_exists("imagefilledarc")) { - // exists only if GD 2.0.1 is avaliable - imagefilledarc($im, $centerX+1, $centerY+1, $diameter, $diameter, $start, $end, $color1, IMG_ARC_PIE); - imagefilledarc($im, $centerX, $centerY, $diameter, $diameter, $start, $end, $color2, IMG_ARC_PIE); - imagefilledarc($im, $centerX, $centerY, $diameter, $diameter, $start, $end, $color1, IMG_ARC_NOFILL|IMG_ARC_EDGED); - } else { - imagearc($im, $centerX, $centerY, $diameter, $diameter, $start, $end, $color2); - imageline($im, $centerX, $centerY, $centerX + cos(deg2rad($start)) * $r, $centerY + sin(deg2rad($start)) * $r, $color2); - imageline($im, $centerX, $centerY, $centerX + cos(deg2rad($start+1)) * $r, $centerY + sin(deg2rad($start)) * $r, $color2); - imageline($im, $centerX, $centerY, $centerX + cos(deg2rad($end-1)) * $r, $centerY + sin(deg2rad($end)) * $r, $color2); - imageline($im, $centerX, $centerY, $centerX + cos(deg2rad($end)) * $r, $centerY + sin(deg2rad($end)) * $r, $color2); - imagefill($im,$centerX + $r*cos($w)/2, $centerY + $r*sin($w)/2, $color2); - } - if ($text) { - if ($placeindex>0) { - imageline($im,$centerX + $r*cos($w)/2, $centerY + $r*sin($w)/2,$diameter, $placeindex*12,$color1); - imagestring($im,4,$diameter, $placeindex*12,$text,$color1); - - } else { - imagestring($im,4,$centerX + $r*cos($w)/2, $centerY + $r*sin($w)/2,$text,$color1); - } - } - } - - function text_arc($im, $centerX, $centerY, $diameter, $start, $end, $color1,$text,$placeindex=0) { - $r=$diameter/2; - $w=deg2rad((360+$start+($end-$start)/2)%360); - - if ($placeindex>0) { - imageline($im,$centerX + $r*cos($w)/2, $centerY + $r*sin($w)/2,$diameter, $placeindex*12,$color1); - imagestring($im,4,$diameter, $placeindex*12,$text,$color1); - - } else { - imagestring($im,4,$centerX + $r*cos($w)/2, $centerY + $r*sin($w)/2,$text,$color1); - } - } - - function fill_box($im, $x, $y, $w, $h, $color1, $color2,$text='',$placeindex='') { - global $col_black; - $x1=$x+$w-1; - $y1=$y+$h-1; - - imagerectangle($im, $x, $y1, $x1+1, $y+1, $col_black); - if($y1>$y) imagefilledrectangle($im, $x, $y, $x1, $y1, $color2); - else imagefilledrectangle($im, $x, $y1, $x1, $y, $color2); - imagerectangle($im, $x, $y1, $x1, $y, $color1); - if ($text) { - if ($placeindex>0) { - - if ($placeindex<16) - { - $px=5; - $py=$placeindex*12+6; - imagefilledrectangle($im, $px+90, $py+3, $px+90-4, $py-3, $color2); - imageline($im,$x,$y+$h/2,$px+90,$py,$color2); - imagestring($im,2,$px,$py-6,$text,$color1); - - } else { - if ($placeindex<31) { - $px=$x+40*2; - $py=($placeindex-15)*12+6; - } else { - $px=$x+40*2+100*intval(($placeindex-15)/15); - $py=($placeindex%15)*12+6; - } - imagefilledrectangle($im, $px, $py+3, $px-4, $py-3, $color2); - imageline($im,$x+$w,$y+$h/2,$px,$py,$color2); - imagestring($im,2,$px+2,$py-6,$text,$color1); - } - } else { - imagestring($im,4,$x+5,$y1-16,$text,$color1); - } - } - } - - - $size = GRAPH_SIZE; // image size - if ($MYREQUEST['IMG']==3) - $image = imagecreate(2*$size+150, $size+10); - else - $image = imagecreate($size+50, $size+10); - - $col_white = imagecolorallocate($image, 0xFF, 0xFF, 0xFF); - $col_red = imagecolorallocate($image, 0xD0, 0x60, 0x30); - $col_green = imagecolorallocate($image, 0x60, 0xF0, 0x60); - $col_black = imagecolorallocate($image, 0, 0, 0); - imagecolortransparent($image,$col_white); - - switch ($MYREQUEST['IMG']) { - - case 1: - $s=$mem['num_seg']*$mem['seg_size']; - $a=$mem['avail_mem']; - $x=$y=$size/2; - $fuzz = 0.000001; - - // This block of code creates the pie chart. It is a lot more complex than you - // would expect because we try to visualize any memory fragmentation as well. - $angle_from = 0; - $string_placement=array(); - for($i=0; $i<$mem['num_seg']; $i++) { - $ptr = 0; - $free = $mem['block_lists'][$i]; - foreach($free as $block) { - if($block['offset']!=$ptr) { // Used block - $angle_to = $angle_from+($block['offset']-$ptr)/$s; - if(($angle_to+$fuzz)>1) $angle_to = 1; - if( ($angle_to*360) - ($angle_from*360) >= 1) { - fill_arc($image,$x,$y,$size,$angle_from*360,$angle_to*360,$col_black,$col_red); - if (($angle_to-$angle_from)>0.05) { - array_push($string_placement, array($angle_from,$angle_to)); - } - } - $angle_from = $angle_to; - } - $angle_to = $angle_from+($block['size'])/$s; - if(($angle_to+$fuzz)>1) $angle_to = 1; - if( ($angle_to*360) - ($angle_from*360) >= 1) { - fill_arc($image,$x,$y,$size,$angle_from*360,$angle_to*360,$col_black,$col_green); - if (($angle_to-$angle_from)>0.05) { - array_push($string_placement, array($angle_from,$angle_to)); - } - } - $angle_from = $angle_to; - $ptr = $block['offset']+$block['size']; - } - if ($ptr < $mem['seg_size']) { // memory at the end - $angle_to = $angle_from + ($mem['seg_size'] - $ptr)/$s; - if(($angle_to+$fuzz)>1) $angle_to = 1; - fill_arc($image,$x,$y,$size,$angle_from*360,$angle_to*360,$col_black,$col_red); - if (($angle_to-$angle_from)>0.05) { - array_push($string_placement, array($angle_from,$angle_to)); - } - } - } - foreach ($string_placement as $angle) { - text_arc($image,$x,$y,$size,$angle[0]*360,$angle[1]*360,$col_black,bsize($s*($angle[1]-$angle[0]))); - } - break; - - case 2: - $s=$cache['num_hits']+$cache['num_misses']; - $a=$cache['num_hits']; - - fill_box($image, 30,$size,50,-$a*($size-21)/$s,$col_black,$col_green,sprintf("%.1f%%",$cache['num_hits']*100/$s)); - fill_box($image,130,$size,50,-max(4,($s-$a)*($size-21)/$s),$col_black,$col_red,sprintf("%.1f%%",$cache['num_misses']*100/$s)); - break; - - case 3: - $s=$mem['num_seg']*$mem['seg_size']; - $a=$mem['avail_mem']; - $x=130; - $y=1; - $j=1; - - // This block of code creates the bar chart. It is a lot more complex than you - // would expect because we try to visualize any memory fragmentation as well. - for($i=0; $i<$mem['num_seg']; $i++) { - $ptr = 0; - $free = $mem['block_lists'][$i]; - foreach($free as $block) { - if($block['offset']!=$ptr) { // Used block - $h=(GRAPH_SIZE-5)*($block['offset']-$ptr)/$s; - if ($h>0) { - $j++; - if($j<75) fill_box($image,$x,$y,50,$h,$col_black,$col_red,bsize($block['offset']-$ptr),$j); - else fill_box($image,$x,$y,50,$h,$col_black,$col_red); - } - $y+=$h; - } - $h=(GRAPH_SIZE-5)*($block['size'])/$s; - if ($h>0) { - $j++; - if($j<75) fill_box($image,$x,$y,50,$h,$col_black,$col_green,bsize($block['size']),$j); - else fill_box($image,$x,$y,50,$h,$col_black,$col_green); - } - $y+=$h; - $ptr = $block['offset']+$block['size']; - } - if ($ptr < $mem['seg_size']) { // memory at the end - $h = (GRAPH_SIZE-5) * ($mem['seg_size'] - $ptr) / $s; - if ($h > 0) { - fill_box($image,$x,$y,50,$h,$col_black,$col_red,bsize($mem['seg_size']-$ptr),$j++); - } - } - } - break; - case 4: - $s=$cache['num_hits']+$cache['num_misses']; - $a=$cache['num_hits']; - - fill_box($image, 30,$size,50,-$a*($size-21)/$s,$col_black,$col_green,sprintf("%.1f%%",$cache['num_hits']*100/$s)); - fill_box($image,130,$size,50,-max(4,($s-$a)*($size-21)/$s),$col_black,$col_red,sprintf("%.1f%%",$cache['num_misses']*100/$s)); - break; - - } - header("Content-type: image/png"); - imagepng($image); - exit; -} - -// pretty printer for byte values -// -function bsize($s) { - foreach (array('','K','M','G') as $i => $k) { - if ($s < 1024) break; - $s/=1024; - } - return sprintf("%5.1f %sBytes",$s,$k); -} - -// sortable table header in "scripts for this host" view -function sortheader($key,$name,$extra='') { - global $MYREQUEST, $MY_SELF_WO_SORT; - - if ($MYREQUEST['SORT1']==$key) { - $MYREQUEST['SORT2'] = $MYREQUEST['SORT2']=='A' ? 'D' : 'A'; - } - return "$name"; - -} - -// create menu entry -function menu_entry($ob,$title) { - global $MYREQUEST,$MY_SELF; - if ($MYREQUEST['OB']!=$ob) { - return "
  • $title
  • "; - } else if (empty($MYREQUEST['SH'])) { - return "
  • $title
  • "; - } else { - return "
  • $title
  • "; - } -} - -function put_login_link($s="Login") -{ - global $MY_SELF,$MYREQUEST,$AUTHENTICATED; - // needs ADMIN_PASSWORD to be changed! - // - if (!USE_AUTHENTICATION) { - return; - } else if (ADMIN_PASSWORD=='password') - { - print <<$s -EOB; - } else if ($AUTHENTICATED) { - print <<$s -EOB; - } -} - - -?> - - -APC INFO <?php echo $host ?> - - - -
    -

    - -
    Opcode Cache
    -

    - -
    -
    - -
  • Refresh Data
  • -EOB; -echo - menu_entry(1,'View Host Stats'), - menu_entry(2,'System Cache Entries'); -if ($AUTHENTICATED) { - echo menu_entry(4,'Per-Directory Entries'); -} -echo - menu_entry(3,'User Cache Entries'), - menu_entry(9,'Version Check'); - -if ($AUTHENTICATED) { - echo <<Clear $cache_mode Cache -EOB; -} -echo << -EOB; - - -// CONTENT -echo << -EOB; - -// MAIN SWITCH STATEMENT - -switch ($MYREQUEST['OB']) { - - - - - -// ----------------------------------------------- -// Host Stats -// ----------------------------------------------- -case OB_HOST_STATS: - $mem_size = $mem['num_seg']*$mem['seg_size']; - $mem_avail= $mem['avail_mem']; - $mem_used = $mem_size-$mem_avail; - $seg_size = bsize($mem['seg_size']); - $req_rate = sprintf("%.2f",($cache['num_hits']+$cache['num_misses'])/($time-$cache['start_time'])); - $hit_rate = sprintf("%.2f",($cache['num_hits'])/($time-$cache['start_time'])); - $miss_rate = sprintf("%.2f",($cache['num_misses'])/($time-$cache['start_time'])); - $insert_rate = sprintf("%.2f",($cache['num_inserts'])/($time-$cache['start_time'])); - $req_rate_user = sprintf("%.2f",($cache_user['num_hits']+$cache_user['num_misses'])/($time-$cache_user['start_time'])); - $hit_rate_user = sprintf("%.2f",($cache_user['num_hits'])/($time-$cache_user['start_time'])); - $miss_rate_user = sprintf("%.2f",($cache_user['num_misses'])/($time-$cache_user['start_time'])); - $insert_rate_user = sprintf("%.2f",($cache_user['num_inserts'])/($time-$cache_user['start_time'])); - $apcversion = phpversion('apc'); - $phpversion = phpversion(); - $number_files = $cache['num_entries']; - $size_files = bsize($cache['mem_size']); - $number_vars = $cache_user['num_entries']; - $size_vars = bsize($cache_user['mem_size']); - $i=0; - echo <<< EOB -

    General Cache Information

    - - - -EOB; - - if(!empty($_SERVER['SERVER_NAME'])) - echo "\n"; - if(!empty($_SERVER['SERVER_SOFTWARE'])) - echo "\n"; - - echo << -EOB; - echo ''; - echo ''; - echo ''; - echo <<
    APC Version$apcversion
    PHP Version$phpversion
    APC Host{$_SERVER['SERVER_NAME']} $host
    Server Software{$_SERVER['SERVER_SOFTWARE']}
    Shared Memory{$mem['num_seg']} Segment(s) with $seg_size -
    ({$cache['memory_type']} memory, {$cache['locking_type']} locking) -
    Start Time',date(DATE_FORMAT,$cache['start_time']),'
    Uptime',duration($cache['start_time']),'
    File Upload Support',$cache['file_upload_progress'],'
    -
    - -

    File Cache Information

    - - - - - - - - - -
    Cached Files$number_files ($size_files)
    Hits{$cache['num_hits']}
    Misses{$cache['num_misses']}
    Request Rate (hits, misses)$req_rate cache requests/second
    Hit Rate$hit_rate cache requests/second
    Miss Rate$miss_rate cache requests/second
    Insert Rate$insert_rate cache requests/second
    Cache full count{$cache['expunges']}
    -
    - -

    User Cache Information

    - - - - - - - - - - -
    Cached Variables$number_vars ($size_vars)
    Hits{$cache_user['num_hits']}
    Misses{$cache_user['num_misses']}
    Request Rate (hits, misses)$req_rate_user cache requests/second
    Hit Rate$hit_rate_user cache requests/second
    Miss Rate$miss_rate_user cache requests/second
    Insert Rate$insert_rate_user cache requests/second
    Cache full count{$cache_user['expunges']}
    -
    - -

    Runtime Settings

    -EOB; - - $j = 0; - foreach (ini_get_all('apc') as $k => $v) { - echo "\n"; - $j = 1 - $j; - } - - if($mem['num_seg']>1 || $mem['num_seg']==1 && count($mem['block_lists'][0])>1) - $mem_note = "Memory Usage
    (multiple slices indicate fragments)"; - else - $mem_note = "Memory Usage"; - - echo <<< EOB -
    ",$k,"",str_replace(',',',
    ',$v['local_value']),"
    -
    - -

    Host Status Diagrams

    - -EOB; - $size='width='.(GRAPH_SIZE+50).' height='.(GRAPH_SIZE+10); - echo << - - - -EOB; - - echo - graphics_avail() ? - ''. - "". - "\n" - : "", - '', - '\n", - '\n", - '', - '', - '\n", - '\n"; - echo <<< EOB - -
    $mem_noteHits & Misses
    \"\"\"\"
     Free: ',bsize($mem_avail).sprintf(" (%.1f%%)",$mem_avail*100/$mem_size)," Hits: ',$cache['num_hits'].sprintf(" (%.1f%%)",$cache['num_hits']*100/($cache['num_hits']+$cache['num_misses'])),"
     Used: ',bsize($mem_used ).sprintf(" (%.1f%%)",$mem_used *100/$mem_size)," Misses: ',$cache['num_misses'].sprintf(" (%.1f%%)",$cache['num_misses']*100/($cache['num_hits']+$cache['num_misses'])),"
    - -
    -

    Detailed Memory Usage and Fragmentation

    - - - - -EOB; - if(isset($mem['adist'])) { - foreach($mem['adist'] as $i=>$v) { - $cur = pow(2,$i); $nxt = pow(2,$i+1)-1; - if($i==0) $range = "1"; - else $range = "$cur - $nxt"; - echo "\n"; - } - } - echo <<

    -EOB; - - // Fragementation: (freeseg - 1) / total_seg - $nseg = $freeseg = $fragsize = $freetotal = 0; - for($i=0; $i<$mem['num_seg']; $i++) { - $ptr = 0; - foreach($mem['block_lists'][$i] as $block) { - if ($block['offset'] != $ptr) { - ++$nseg; - } - $ptr = $block['offset'] + $block['size']; - /* Only consider blocks <5M for the fragmentation % */ - if($block['size']<(5*1024*1024)) $fragsize+=$block['size']; - $freetotal+=$block['size']; - } - $freeseg += count($mem['block_lists'][$i]); - } - - if ($freeseg > 1) { - $frag = sprintf("%.2f%% (%s out of %s in %d fragments)", ($fragsize/$freetotal)*100,bsize($fragsize),bsize($freetotal),$freeseg); - } else { - $frag = "0%"; - } - - if (graphics_avail()) { - $size='width='.(2*GRAPH_SIZE+150).' height='.(GRAPH_SIZE+10); - echo << -EOB; - } - echo <<Fragmentation: $frag -
    $range$v
    -
    -EOB; - - break; - - -// ----------------------------------------------- -// User Cache Entries -// ----------------------------------------------- -case OB_USER_CACHE: - if (!$AUTHENTICATED) { - echo '
    You need to login to see the user values here!
     
    '; - put_login_link("Login now!"); - echo '
    '; - break; - } - $fieldname='info'; - $fieldheading='User Entry Label'; - $fieldkey='info'; - -// ----------------------------------------------- -// System Cache Entries -// ----------------------------------------------- -case OB_SYS_CACHE: - if (!isset($fieldname)) - { - $fieldname='filename'; - $fieldheading='Script Filename'; - if(ini_get("apc.stat")) $fieldkey='inode'; - else $fieldkey='filename'; - } - if (!empty($MYREQUEST['SH'])) - { - echo <<< EOB -
    - -EOB; - - $m=0; - foreach($scope_list as $j => $list) { - foreach($cache[$list] as $i => $entry) { - if (md5($entry[$fieldkey])!=$MYREQUEST['SH']) continue; - foreach($entry as $k => $value) { - if (!$AUTHENTICATED) { - // hide all path entries if not logged in - $value=preg_replace('/^.*(\\/|\\\\)/','<hidden>/',$value); - } - - if ($k == "num_hits") { - $value=sprintf("%s (%.2f%%)",$value,$value*100/$cache['num_hits']); - } - if ($k == 'deletion_time') { - if(!$entry['deletion_time']) $value = "None"; - } - echo - "", - "", - "", - ""; - $m=1-$m; - } - if($fieldkey=='info') { - echo "\n"; - } - break; - } - } - - echo <<
    AttributeValue
    ",ucwords(preg_replace("/_/"," ",$k)),"",(preg_match("/time/",$k) && $value!='None') ? date(DATE_FORMAT,$value) : $value,"
    Stored Value
    ";
    -					$output = var_export(apc_fetch($entry[$fieldkey]),true);
    -					echo htmlspecialchars($output);
    -					echo "
    -
    -EOB; - break; - } - - $cols=6; - echo <<
    Scope: - - ", - ", Sorting:', - '', - '', - '  Search: ', - ' ', - '
    '; - - if (isset($MYREQUEST['SEARCH'])) { - // Don't use preg_quote because we want the user to be able to specify a - // regular expression subpattern. - $MYREQUEST['SEARCH'] = '/'.str_replace('/', '\\/', $MYREQUEST['SEARCH']).'/i'; - if (preg_match($MYREQUEST['SEARCH'], 'test') === false) { - echo '
    Error: enter a valid regular expression as a search query.
    '; - break; - } - } - - echo - '
    ', - '', - '', - '', - '', - '', - '', - ''; - - if($fieldname=='info') { - $cols+=2; - echo ''; - } - echo ''; - - // builds list with alpha numeric sortable keys - // - $list = array(); - foreach($cache[$scope_list[$MYREQUEST['SCOPE']]] as $i => $entry) { - switch($MYREQUEST['SORT1']) { - case 'A': $k=sprintf('%015d-',$entry['access_time']); break; - case 'H': $k=sprintf('%015d-',$entry['num_hits']); break; - case 'Z': $k=sprintf('%015d-',$entry['mem_size']); break; - case 'M': $k=sprintf('%015d-',$entry['mtime']); break; - case 'C': $k=sprintf('%015d-',$entry['creation_time']); break; - case 'T': $k=sprintf('%015d-',$entry['ttl']); break; - case 'D': $k=sprintf('%015d-',$entry['deletion_time']); break; - case 'S': $k=''; break; - } - if (!$AUTHENTICATED) { - // hide all path entries if not logged in - $list[$k.$entry[$fieldname]]=preg_replace('/^.*(\\/|\\\\)/','<hidden>/',$entry); - } else { - $list[$k.$entry[$fieldname]]=$entry; - } - } - - if ($list) { - - // sort list - // - switch ($MYREQUEST['SORT2']) { - case "A": krsort($list); break; - case "D": ksort($list); break; - } - - // output list - $i=0; - foreach($list as $k => $entry) { - if(!$MYREQUEST['SEARCH'] || preg_match($MYREQUEST['SEARCH'], $entry[$fieldname]) != 0) { - echo - '', - "', - '', - '', - '', - '', - ''; - - if($fieldname=='info') { - if($entry['ttl']) - echo ''; - else - echo ''; - } - if ($entry['deletion_time']) { - - echo ''; - } else if ($MYREQUEST['OB'] == OB_USER_CACHE) { - - echo ''; - } else { - echo ''; - } - echo ''; - $i++; - if ($i == $MYREQUEST['COUNT']) - break; - } - } - - } else { - echo ''; - } - echo <<< EOB -
    ',sortheader('S',$fieldheading, "&OB=".$MYREQUEST['OB']),'',sortheader('H','Hits', "&OB=".$MYREQUEST['OB']),'',sortheader('Z','Size', "&OB=".$MYREQUEST['OB']),'',sortheader('A','Last accessed',"&OB=".$MYREQUEST['OB']),'',sortheader('M','Last modified',"&OB=".$MYREQUEST['OB']),'',sortheader('C','Created at', "&OB=".$MYREQUEST['OB']),'',sortheader('T','Timeout',"&OB=".$MYREQUEST['OB']),'',sortheader('D','Deleted at',"&OB=".$MYREQUEST['OB']),'
    ",$entry[$fieldname],'',$entry['num_hits'],'',$entry['mem_size'],'',date(DATE_FORMAT,$entry['access_time']),'',date(DATE_FORMAT,$entry['mtime']),'',date(DATE_FORMAT,$entry['creation_time']),''.$entry['ttl'].' secondsNone', date(DATE_FORMAT,$entry['deletion_time']), ''; - echo '[Delete Now]'; - echo '  
    No data
    -EOB; - - if ($list && $i < count($list)) { - echo "",count($list)-$i,' more available...'; - } - - echo <<< EOB -
    -EOB; - break; - - -// ----------------------------------------------- -// Per-Directory System Cache Entries -// ----------------------------------------------- -case OB_SYS_CACHE_DIR: - if (!$AUTHENTICATED) { - break; - } - - echo <<
    Scope: - - ", - ", Sorting:', - '', - '', - ", Group By Dir Level:', - ' ', - '
    ', - - '
    ', - '', - '', - '', - '', - '', - '', - '', - ''; - - // builds list with alpha numeric sortable keys - // - $tmp = $list = array(); - foreach($cache[$scope_list[$MYREQUEST['SCOPE']]] as $entry) { - $n = dirname($entry['filename']); - if ($MYREQUEST['AGGR'] > 0) { - $n = preg_replace("!^(/?(?:[^/\\\\]+[/\\\\]){".($MYREQUEST['AGGR']-1)."}[^/\\\\]*).*!", "$1", $n); - } - if (!isset($tmp[$n])) { - $tmp[$n] = array('hits'=>0,'size'=>0,'ents'=>0); - } - $tmp[$n]['hits'] += $entry['num_hits']; - $tmp[$n]['size'] += $entry['mem_size']; - ++$tmp[$n]['ents']; - } - - foreach ($tmp as $k => $v) { - switch($MYREQUEST['SORT1']) { - case 'A': $kn=sprintf('%015d-',$v['size'] / $v['ents']);break; - case 'T': $kn=sprintf('%015d-',$v['ents']); break; - case 'H': $kn=sprintf('%015d-',$v['hits']); break; - case 'Z': $kn=sprintf('%015d-',$v['size']); break; - case 'C': $kn=sprintf('%015d-',$v['hits'] / $v['ents']);break; - case 'S': $kn = $k; break; - } - $list[$kn.$k] = array($k, $v['ents'], $v['hits'], $v['size']); - } - - if ($list) { - - // sort list - // - switch ($MYREQUEST['SORT2']) { - case "A": krsort($list); break; - case "D": ksort($list); break; - } - - // output list - $i = 0; - foreach($list as $entry) { - echo - '', - "', - '', - '', - '', - '', - '', - ''; - - if (++$i == $MYREQUEST['COUNT']) break; - } - - } else { - echo ''; - } - echo <<< EOB -
    ',sortheader('S','Directory Name', "&OB=".$MYREQUEST['OB']),'',sortheader('T','Number of Files',"&OB=".$MYREQUEST['OB']),'',sortheader('H','Total Hits', "&OB=".$MYREQUEST['OB']),'',sortheader('Z','Total Size', "&OB=".$MYREQUEST['OB']),'',sortheader('C','Avg. Hits', "&OB=".$MYREQUEST['OB']),'',sortheader('A','Avg. Size', "&OB=".$MYREQUEST['OB']),'
    ",$entry[0],'',$entry[1],'',$entry[2],'',$entry[3],'',round($entry[2] / $entry[1]),'',round($entry[3] / $entry[1]),'
    No data
    -EOB; - - if ($list && $i < count($list)) { - echo "",count($list)-$i,' more available...'; - } - - echo <<< EOB -
    -EOB; - break; - -// ----------------------------------------------- -// Version check -// ----------------------------------------------- -case OB_VERSION_CHECK: - echo <<

    APC Version Information

    - - - - -EOB; - - $rss = @file_get_contents("http://pecl.php.net/feeds/pkg_apc.rss"); - if (!$rss) { - echo ''; - } else { - $apcversion = phpversion('apc'); - - preg_match('!APC ([0-9.]+)!', $rss, $match); - echo ''; - echo ''; - } - echo <<< EOB -
    Unable to fetch version information.
    '; - if (version_compare($apcversion, $match[1], '>=')) { - echo '
    You are running the latest version of APC ('.$apcversion.')
    '; - $i = 3; - } else { - echo '
    You are running an older version of APC ('.$apcversion.'), - newer version '.$match[1].' is available at - http://pecl.php.net/package/APC/'.$match[1].' -
    '; - $i = -1; - } - echo '

    Change Log:


    '; - - preg_match_all('!<(title|description)>([^<]+)!', $rss, $match); - next($match[2]); next($match[2]); - - while (list(,$v) = each($match[2])) { - list(,$ver) = explode(' ', $v, 2); - if ($i < 0 && version_compare($apcversion, $ver, '>=')) { - break; - } else if (!$i--) { - break; - } - echo "".htmlspecialchars($v)."
    "; - echo nl2br(htmlspecialchars(current($match[2])))."
    "; - next($match[2]); - } - echo '
    -
    -EOB; - break; - -} - -echo <<< EOB -
    -EOB; - -?> - - - - diff --git a/library/inex-smarty/functions/dead-for-deletion/compiler.include_if_exists.php b/library/inex-smarty/functions/dead-for-deletion/compiler.include_if_exists.php deleted file mode 100644 index 95a3d81c2..000000000 --- a/library/inex-smarty/functions/dead-for-deletion/compiler.include_if_exists.php +++ /dev/null @@ -1,64 +0,0 @@ - $value ) - { - if( is_bool( $value ) ) - $params[ $arg ] = $value ? 'true' : 'false'; - - if( !in_array( $arg, array( 'file', 'assign', 'else' ) ) ) - { - $original_values[ $arg ] = $value; - $smarty->assign( $arg, $value ); - } - } - - $params['file'] = str_replace( array( '\'', '"' ), '', $params['file'] ); - $params['else'] = str_replace( array( '\'', '"' ), '', $params['file'] ); - - if( $smarty->getTemplateVars( '___SKIN' ) ) - $skin = $smarty->getTemplateVars( '___SKIN' ); - else - $skin = false; - - if( $skin && $smarty->templateExists( 'skins/' . $skin . '/' . $params['file'] ) ) - $params['file'] = 'skins/' . $skin . '/' . $params['file']; - elseif( $skin && $smarty->templateExists( 'skins/' . $skin . '/' . $params['else'] ) ) - $params['file'] = 'skins/' . $skin . '/' . $params['else']; - elseif( $smarty->templateExists( $params['file'] ) ) - $params['file'] = $params['file']; - elseif( $smarty->templateExists( $params['else'] ) ) - $params['file'] = $params['else']; - else - throw new SmartyCompilerException( "Template file nor alternative does not exist for all skins - [{$params['file']}]" ); - - $output = ''; - - if( isset( $params['assign'] ) ) - $smarty->assign( $params['assign'], $smarty->fetch( $params['file'] ) ); - else - $output = $smarty->fetch( $params['file'] ); - - foreach( $original_values as $arg => $value ) - { - $smarty->assign( $arg, $value ); - } - - return $output; -} - diff --git a/library/inex-smarty/functions/dead-for-deletion/compiler.tmplinclude.php b/library/inex-smarty/functions/dead-for-deletion/compiler.tmplinclude.php deleted file mode 100644 index 50ed01797..000000000 --- a/library/inex-smarty/functions/dead-for-deletion/compiler.tmplinclude.php +++ /dev/null @@ -1,67 +0,0 @@ - $value ) - { - if( is_bool( $value ) ) - $params[ $arg ] = $value ? 'true' : 'false'; - - if( !in_array( $arg, array( 'file', 'assign' ) ) ) - { - $original_values[ $arg ] = $value; - $smarty->assign( $arg, $value ); - } - } - - if( substr( $params['file'], 0, 24 ) == '$_smarty_tpl->tpl_vars[\'' ) - { - $params['file'] = substr( $params['file'], 24 ); - $params['file'] = substr( $params['file'], 0, strpos( $params['file'], '\'' ) ); - $params['file'] = $smarty->getTemplateVars( $params['file'] ); - } - elseif( substr( $params['file'], 0, 24 ) == '($_smarty_tpl->tpl_vars[' ) - { - $params['file'] = substr( $params['file'], 24 ); - $params['file'] = substr( $params['file'], 0, strpos( $params['file'], ']' ) ); - $params['file'] = $smarty->getTemplateVars( $params['file'] ); - } - elseif( substr( $params['file'], 0, 23 ) == '$_smarty_tpl->tpl_vars[' ) - { - $params['file'] = substr( $params['file'], 23 ); - $params['file'] = substr( $params['file'], 0, strpos( $params['file'], ']' ) ); - $params['file'] = $smarty->getTemplateVars( $params['file'] ); - } - else - $params['file'] = str_replace( array( '\'', '"' ), '', $params['file'] ); - - if( $smarty->getTemplateVars( '___SKIN' ) ) - $skin = $smarty->getTemplateVars( '___SKIN' ); - else - $skin = false; - - if( $skin && $smarty->templateExists( 'skins/' . $skin . '/' . $params['file'] ) ) - $params['file'] = 'skins/' . $skin . '/' . $params['file']; - elseif( !$smarty->templateExists( $params['file'] ) ) - throw new SmartyCompilerException( "Template file does not exist - [{$params['file']}]" ); - - if( isset( $params['assign'] ) ) - $smarty->assign( $params['assign'], $smarty->fetch( $params['file'] ) ); - else - $output = $smarty->fetch( $params['file'] ); - - foreach( $original_values as $arg => $value ) - { - $smarty->assign( $arg, $value ); - } - - return $output; -} diff --git a/library/inex-smarty/functions/function.genDoctrinePagerLinks.php b/library/inex-smarty/functions/function.genDoctrinePagerLinks.php deleted file mode 100644 index a1b4f8ad4..000000000 --- a/library/inex-smarty/functions/function.genDoctrinePagerLinks.php +++ /dev/null @@ -1,117 +0,0 @@ - the Doctrine pagination object - * showPageCount => show an extra row of: Page x of y - * - * @param array $params The attributes used when calling the function - * @param object $smarty The Smarty object - */ -function smarty_function_genDoctrinePagerLinks( $params, &$smarty ) -{ - // remove non-URL parameters from the parameters array - $pager = $params['pager']; - unset( $params['pager'] ); - - $showPageCount = isset( $params['showPageCount'] ) && $params['showPageCount'] ? true : false; - - $baseUrl = smarty_function_genUrl( array(), $smarty ); - - $links = ''; - - // number of columns in the pagination table - $numCols = 4; // always first, prev, next, last - - $params['p'] = $pager->getFirstPage(); - if( $pager->getPage() == $params['p'] ) - $links .= ''; - else - $links .= ''; - - $params['p'] = $pager->getPreviousPage(); - if( $pager->getPage() == $params['p'] ) - $links .= ''; - else - $links .= ''; - - - $pagerRange = new Doctrine_Pager_Range_Sliding( - array( - 'chunk' => 5 // Chunk length - ), - $pager - ); - - foreach( $pagerRange->rangeAroundPage() as $p ) - { - if( $p == $pager->getPage() ) - $links .= ""; - else - { - $params['p'] = $p; - $links .= ''; - } - - $numCols++; - } - - $params['p'] = $pager->getNextPage(); - if( $pager->getPage() == $params['p'] ) - $links .= ''; - else - $links .= ''; - - $params['p'] = $pager->getLastPage(); - if( $pager->getPage() == $params['p'] ) - $links .= ''; - else - $links .= ''; - - $links .= ''; - - if( $showPageCount ) - { - $links .= "\n\n"; - } - - $links .= '
    |<|<<<$p' . $p . '>>|>>|
    Page " . $pager->getPage() . " of " . $pager->getLastPage() . "
    '; - - return $links; -} - - diff --git a/library/inex-smarty/functions/function.genMrtgGraphBox.php b/library/inex-smarty/functions/function.genMrtgGraphBox.php deleted file mode 100644 index 48fbb1283..000000000 --- a/library/inex-smarty/functions/function.genMrtgGraphBox.php +++ /dev/null @@ -1,91 +0,0 @@ - - - - - - - - - - - Max     - - - Average     - - - Current     - - - - - - In - - - {$params['values']['maxin']} - - - {$params['values']['averagein']} - - - {$params['values']['curin']} - - - - - - Out - - - {$params['values']['maxout']} - - - {$params['values']['averageout']} - - - {$params['values']['curout']} - - - - -END_BOX; - - return $box; -} - -?> \ No newline at end of file diff --git a/library/inex-smarty/functions/function.genMrtgImgUrl.php b/library/inex-smarty/functions/function.genMrtgImgUrl.php deleted file mode 100644 index 4827a5144..000000000 --- a/library/inex-smarty/functions/function.genMrtgImgUrl.php +++ /dev/null @@ -1,35 +0,0 @@ - \ No newline at end of file diff --git a/library/inex-smarty/functions/function.genMrtgImgUrlTag.php b/library/inex-smarty/functions/function.genMrtgImgUrlTag.php deleted file mode 100644 index bde246252..000000000 --- a/library/inex-smarty/functions/function.genMrtgImgUrlTag.php +++ /dev/null @@ -1,37 +0,0 @@ -'; -} - -?> \ No newline at end of file diff --git a/library/inex-smarty/functions/function.genMrtgP2pImgUrl.php b/library/inex-smarty/functions/function.genMrtgP2pImgUrl.php deleted file mode 100644 index 5b4bd9964..000000000 --- a/library/inex-smarty/functions/function.genMrtgP2pImgUrl.php +++ /dev/null @@ -1,36 +0,0 @@ - \ No newline at end of file diff --git a/library/inex-smarty/functions/function.getMrtgStatistics.php b/library/inex-smarty/functions/function.getMrtgStatistics.php deleted file mode 100644 index 430e8bfea..000000000 --- a/library/inex-smarty/functions/function.getMrtgStatistics.php +++ /dev/null @@ -1,58 +0,0 @@ -'; -} - -?> \ No newline at end of file diff --git a/library/inex-smarty/functions/function.mrtgScale.php b/library/inex-smarty/functions/function.mrtgScale.php deleted file mode 100644 index 693a8b757..000000000 --- a/library/inex-smarty/functions/function.mrtgScale.php +++ /dev/null @@ -1,38 +0,0 @@ - \ No newline at end of file diff --git a/library/inex-smarty/functions/modifier.asnumber.php b/library/inex-smarty/functions/modifier.asnumber.php deleted file mode 100644 index 3e5f3912d..000000000 --- a/library/inex-smarty/functions/modifier.asnumber.php +++ /dev/null @@ -1,38 +0,0 @@ -' . $value . ''; -} - diff --git a/library/inex-smarty/functions/modifier.pmasn.php b/library/inex-smarty/functions/modifier.pmasn.php deleted file mode 100644 index 942f0a6dd..000000000 --- a/library/inex-smarty/functions/modifier.pmasn.php +++ /dev/null @@ -1,33 +0,0 @@ -'; - - return $r; -} - diff --git a/library/inex-smarty/functions/modifier.underline.php b/library/inex-smarty/functions/modifier.underline.php deleted file mode 100644 index 50437d358..000000000 --- a/library/inex-smarty/functions/modifier.underline.php +++ /dev/null @@ -1,47 +0,0 @@ -=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", + "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.3.tgz", + "integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.3", + "@babel/parser": "^7.28.3", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.3", + "@babel/types": "^7.28.2", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", + "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.3", + "@babel/types": "^7.28.2", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.3.tgz", + "integrity": "sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/traverse": "^7.28.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.1.tgz", + "integrity": "sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "regexpu-core": "^6.2.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.5.tgz", + "integrity": "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-plugin-utils": "^7.27.1", + "debug": "^4.4.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.22.10" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz", + "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", + "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", + "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-wrap-function": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", + "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", + "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.28.3.tgz", + "integrity": "sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.3", + "@babel/types": "^7.28.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.3.tgz", + "integrity": "sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz", + "integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.27.1.tgz", + "integrity": "sha512-QPG3C9cCVRQLxAVwmefEmwdTanECuUBMQZ/ym5kiw3XKCGA7qkuQLcjWWHcrD/GKbn/WmJwaezfuuAOcyKlRPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz", + "integrity": "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz", + "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", + "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-transform-optional-chaining": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.3.tgz", + "integrity": "sha512-b6YTX108evsvE4YgWyQ921ZAFFQm3Bn+CA3+ZXlNVnPhx+UfsVURoPjfGAPCjBgrqo30yX/C2nZGX96DxvR9Iw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-proposal-object-rest-spread": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", + "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-object-rest-spread instead.", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.20.5", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.20.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz", + "integrity": "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", + "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.28.0.tgz", + "integrity": "sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-remap-async-to-generator": "^7.27.1", + "@babel/traverse": "^7.28.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz", + "integrity": "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-remap-async-to-generator": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz", + "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.0.tgz", + "integrity": "sha512-gKKnwjpdx5sER/wl0WN0efUBFzF/56YZO0RJrSYP4CljXnP31ByY7fol89AzomdlLNzI36AvOTmYHsnZTCkq8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz", + "integrity": "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.3.tgz", + "integrity": "sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.28.3", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.3.tgz", + "integrity": "sha512-DoEWC5SuxuARF2KdKmGUq3ghfPMO6ZzR12Dnp5gubwbeWJo4dbNWXJPVlwvh4Zlq6Z7YVvL8VFxeSOJgjsx4Sg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-globals": "^7.28.0", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz", + "integrity": "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/template": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.0.tgz", + "integrity": "sha512-v1nrSMBiKcodhsyJ4Gf+Z0U/yawmJDBOTpEB3mcQY52r9RIyPneGyAS/yM6seP/8I+mWI3elOMtT5dB8GJVs+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.28.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.27.1.tgz", + "integrity": "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz", + "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.27.1.tgz", + "integrity": "sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz", + "integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-explicit-resource-management": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.0.tgz", + "integrity": "sha512-K8nhUcn3f6iB+P3gwCv/no7OdzOZQcKchW6N389V6PD8NUWKZHzndOd9sPDVbMoBsbmjMqlB4L9fm+fEFNVlwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.28.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.27.1.tgz", + "integrity": "sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz", + "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", + "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", + "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.27.1.tgz", + "integrity": "sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", + "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.27.1.tgz", + "integrity": "sha512-SJvDs5dXxiae4FbSL1aBJlG4wvl594N6YEVVn9e3JGulwioy6z3oPjx/sQBO3Y4NwUu5HNix6KJ3wBZoewcdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz", + "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz", + "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz", + "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.27.1.tgz", + "integrity": "sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz", + "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.27.1.tgz", + "integrity": "sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz", + "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.27.1.tgz", + "integrity": "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.27.1.tgz", + "integrity": "sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.0.tgz", + "integrity": "sha512-9VNGikXxzu5eCiQjdE4IZn8sb9q7Xsk5EXLDBKUYg1e/Tve8/05+KJEtcxGxAgCY5t/BpKQM+JEL/yT4tvgiUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.28.0", + "@babel/plugin-transform-parameters": "^7.27.7", + "@babel/traverse": "^7.28.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz", + "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.27.1.tgz", + "integrity": "sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz", + "integrity": "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.27.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", + "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.27.1.tgz", + "integrity": "sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.27.1.tgz", + "integrity": "sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz", + "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.3.tgz", + "integrity": "sha512-K3/M/a4+ESb5LEldjQb+XSrpY0nF+ZBFlTCbSnKaYAMfD8v33O6PMs4uYnOk19HlcsI8WMu3McdFPTiQHF/1/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regexp-modifiers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.27.1.tgz", + "integrity": "sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz", + "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.28.3.tgz", + "integrity": "sha512-Y6ab1kGqZ0u42Zv/4a7l0l72n9DKP/MKoKWaUSBylrhNZO2prYuqFOLbn5aW5SIFXwSH93yfjbgllL8lxuGKLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "babel-plugin-polyfill-corejs2": "^0.4.14", + "babel-plugin-polyfill-corejs3": "^0.13.0", + "babel-plugin-polyfill-regenerator": "^0.6.5", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz", + "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz", + "integrity": "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", + "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", + "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz", + "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", + "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.27.1.tgz", + "integrity": "sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", + "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.27.1.tgz", + "integrity": "sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.28.3.tgz", + "integrity": "sha512-ROiDcM+GbYVPYBOeCR6uBXKkQpBExLl8k9HO1ygXEyds39j+vCCsjmj7S8GOniZQlEs81QlkdJZe76IpLSiqpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.0", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.27.1", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.3", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-import-assertions": "^7.27.1", + "@babel/plugin-syntax-import-attributes": "^7.27.1", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.27.1", + "@babel/plugin-transform-async-generator-functions": "^7.28.0", + "@babel/plugin-transform-async-to-generator": "^7.27.1", + "@babel/plugin-transform-block-scoped-functions": "^7.27.1", + "@babel/plugin-transform-block-scoping": "^7.28.0", + "@babel/plugin-transform-class-properties": "^7.27.1", + "@babel/plugin-transform-class-static-block": "^7.28.3", + "@babel/plugin-transform-classes": "^7.28.3", + "@babel/plugin-transform-computed-properties": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.28.0", + "@babel/plugin-transform-dotall-regex": "^7.27.1", + "@babel/plugin-transform-duplicate-keys": "^7.27.1", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.27.1", + "@babel/plugin-transform-dynamic-import": "^7.27.1", + "@babel/plugin-transform-explicit-resource-management": "^7.28.0", + "@babel/plugin-transform-exponentiation-operator": "^7.27.1", + "@babel/plugin-transform-export-namespace-from": "^7.27.1", + "@babel/plugin-transform-for-of": "^7.27.1", + "@babel/plugin-transform-function-name": "^7.27.1", + "@babel/plugin-transform-json-strings": "^7.27.1", + "@babel/plugin-transform-literals": "^7.27.1", + "@babel/plugin-transform-logical-assignment-operators": "^7.27.1", + "@babel/plugin-transform-member-expression-literals": "^7.27.1", + "@babel/plugin-transform-modules-amd": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.27.1", + "@babel/plugin-transform-modules-systemjs": "^7.27.1", + "@babel/plugin-transform-modules-umd": "^7.27.1", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.27.1", + "@babel/plugin-transform-new-target": "^7.27.1", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1", + "@babel/plugin-transform-numeric-separator": "^7.27.1", + "@babel/plugin-transform-object-rest-spread": "^7.28.0", + "@babel/plugin-transform-object-super": "^7.27.1", + "@babel/plugin-transform-optional-catch-binding": "^7.27.1", + "@babel/plugin-transform-optional-chaining": "^7.27.1", + "@babel/plugin-transform-parameters": "^7.27.7", + "@babel/plugin-transform-private-methods": "^7.27.1", + "@babel/plugin-transform-private-property-in-object": "^7.27.1", + "@babel/plugin-transform-property-literals": "^7.27.1", + "@babel/plugin-transform-regenerator": "^7.28.3", + "@babel/plugin-transform-regexp-modifiers": "^7.27.1", + "@babel/plugin-transform-reserved-words": "^7.27.1", + "@babel/plugin-transform-shorthand-properties": "^7.27.1", + "@babel/plugin-transform-spread": "^7.27.1", + "@babel/plugin-transform-sticky-regex": "^7.27.1", + "@babel/plugin-transform-template-literals": "^7.27.1", + "@babel/plugin-transform-typeof-symbol": "^7.27.1", + "@babel/plugin-transform-unicode-escapes": "^7.27.1", + "@babel/plugin-transform-unicode-property-regex": "^7.27.1", + "@babel/plugin-transform-unicode-regex": "^7.27.1", + "@babel/plugin-transform-unicode-sets-regex": "^7.27.1", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.14", + "babel-plugin-polyfill-corejs3": "^0.13.0", + "babel-plugin-polyfill-regenerator": "^0.6.5", + "core-js-compat": "^3.43.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.3.tgz", + "integrity": "sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.3.tgz", + "integrity": "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.3", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.2", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", + "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz", + "integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.30", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", + "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", + "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.1", + "@parcel/watcher-darwin-arm64": "2.5.1", + "@parcel/watcher-darwin-x64": "2.5.1", + "@parcel/watcher-freebsd-x64": "2.5.1", + "@parcel/watcher-linux-arm-glibc": "2.5.1", + "@parcel/watcher-linux-arm-musl": "2.5.1", + "@parcel/watcher-linux-arm64-glibc": "2.5.1", + "@parcel/watcher-linux-arm64-musl": "2.5.1", + "@parcel/watcher-linux-x64-glibc": "2.5.1", + "@parcel/watcher-linux-x64-musl": "2.5.1", + "@parcel/watcher-win32-arm64": "2.5.1", + "@parcel/watcher-win32-ia32": "2.5.1", + "@parcel/watcher-win32-x64": "2.5.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", + "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", + "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", + "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", + "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", + "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", + "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", + "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", + "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", + "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", + "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", + "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", + "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bonjour": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", + "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/clean-css": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@types/clean-css/-/clean-css-4.2.11.tgz", + "integrity": "sha512-Y8n81lQVTAfP2TOdtJJEsCoYl1AnOkqDqMvXb9/7pfgZZ7r8YrEyurrAvAoAjHOGXKRybay+5CsExqIH6liccw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "source-map": "^0.6.0" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect-history-api-fallback": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", + "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "license": "MIT", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "license": "MIT", + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "license": "MIT" + }, + "node_modules/@types/express": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.23.tgz", + "integrity": "sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.7.tgz", + "integrity": "sha512-R+33OsgWw7rOhD1emjU7dzCDHucJrgJXMA5PYCzJxVil0dsyx5iBEPHqpPfiKNJQb7lZ1vxwoLR4Z87bBUpeGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/express/node_modules/@types/express-serve-static-core": { + "version": "4.19.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", + "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/http-proxy": { + "version": "1.17.16", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.16.tgz", + "integrity": "sha512-sdWoUajOB1cd0A8cRRQ1cfyWNbmFKLAqBB89Y8x5iYyG/mkJHc0YUH8pdWBy2omi9qtCpiIgGjuwO0dQST2l5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/imagemin": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@types/imagemin/-/imagemin-9.0.1.tgz", + "integrity": "sha512-xMWpvrUhtYxl6EeW+UhVH3rwUKhCRx21XddcoWByjDAasXZT5pQaCn0YVnXoTijX5hlTrGqV4TGQL/Htpp00+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/imagemin-gifsicle": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/@types/imagemin-gifsicle/-/imagemin-gifsicle-7.0.4.tgz", + "integrity": "sha512-ZghMBd/Jgqg5utTJNPmvf6DkuHzMhscJ8vgf/7MUGCpO+G+cLrhYltL+5d+h3A1B4W73S2SrmJZ1jS5LACpX+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/imagemin": "*" + } + }, + "node_modules/@types/imagemin-mozjpeg": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/@types/imagemin-mozjpeg/-/imagemin-mozjpeg-8.0.4.tgz", + "integrity": "sha512-ZCAxV8SYJB8ehwHpnbRpHjg5Wc4HcyuAMiDhXbkgC7gujDoOTyHO3dhDkUtZ1oK1DLBRZapqG9etdLVhUml7yQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/imagemin": "*" + } + }, + "node_modules/@types/imagemin-optipng": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@types/imagemin-optipng/-/imagemin-optipng-5.2.4.tgz", + "integrity": "sha512-mvKnDMC8eCYZetAQudjs1DbgpR84WhsTx1wgvdiXnpuUEti3oJ+MaMYBRWPY0JlQ4+y4TXKOfa7+LOuT8daegQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/imagemin": "*" + } + }, + "node_modules/@types/imagemin-svgo": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@types/imagemin-svgo/-/imagemin-svgo-8.0.1.tgz", + "integrity": "sha512-YafkdrVAcr38U0Ln1C+L1n4SIZqC47VBHTyxCq7gTUSd1R9MdIvMcrljWlgU1M9O68WZDeQWUrKipKYfEOCOvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/imagemin": "*", + "@types/svgo": "^1" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "license": "MIT" + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.0.tgz", + "integrity": "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.10.0" + } + }, + "node_modules/@types/node-forge": { + "version": "1.3.14", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.14.tgz", + "integrity": "sha512-mhVF2BnD4BO+jtOp7z1CdzaK4mbuK0LLQYAvdOLqHTavxFNq4zA1EmYkpnFjP8HOUzedfQkRnp0E2ulSAYSzAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "0.17.5", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz", + "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-index": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", + "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.8.tgz", + "integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, + "node_modules/@types/sockjs": { + "version": "0.3.36", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", + "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/svgo": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@types/svgo/-/svgo-1.3.6.tgz", + "integrity": "sha512-AZU7vQcy/4WFEuwnwsNsJnFwupIpbllH1++LXScN6uxT1Z4zPzdrWG97w4/I7eFKFTvfy/bHFStWjdBAg2Vjug==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.1.5.tgz", + "integrity": "sha512-1tdfLmNjWG6t/CsPldh+foumYFo3cpyCHgBYQ34ylaMsJ+SNHQ1kApMIa8jN+i593zQuaw3AdWH0nJTARzCFhg==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.1.5" + } + }, + "node_modules/@vue/shared": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.1.5.tgz", + "integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "license": "MIT", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "license": "Apache-2.0", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webpack-cli/configtest": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz", + "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "webpack": "4.x.x || 5.x.x", + "webpack-cli": "4.x.x" + } + }, + "node_modules/@webpack-cli/info": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz", + "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "envinfo": "^7.7.3" + }, + "peerDependencies": { + "webpack-cli": "4.x.x" + } + }, + "node_modules/@webpack-cli/serve": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz", + "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "webpack-cli": "4.x.x" + }, + "peerDependenciesMeta": { + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "license": "BSD-3-Clause" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "license": "Apache-2.0" + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-phases": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", + "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", + "license": "MIT", + "engines": { + "node": ">=10.13.0" + }, + "peerDependencies": { + "acorn": "^8.14.0" + } + }, + "node_modules/adjust-sourcemap-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", + "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "loader-utils": "^2.0.0", + "regex-parser": "^2.2.11" + }, + "engines": { + "node": ">=8.9" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/alpinejs": { + "version": "3.14.9", + "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.14.9.tgz", + "integrity": "sha512-gqSOhTEyryU9FhviNqiHBHzgjkvtukq9tevew29fTj+ofZtfsYriw4zPirHHOAy9bw8QoL3WGhyk7QqCh5AYlw==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "~3.1.1" + } + }, + "node_modules/ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "dev": true, + "engines": [ + "node >= 0.8.0" + ], + "license": "Apache-2.0", + "bin": { + "ansi-html": "bin/ansi-html" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/asn1.js/node_modules/bn.js": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/assert": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.1.tgz", + "integrity": "sha512-zzw1uCAgLbsKwBfFc8CX78DDg+xZeBksSO3vwVIDDN5i94eOrPsSSyiVhmsSABFDM/OcpE2aagCat9dnWQLG1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "object.assign": "^4.1.4", + "util": "^0.10.4" + } + }, + "node_modules/assert/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true, + "license": "ISC" + }, + "node_modules/assert/node_modules/util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "2.0.3" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/autoprefixer": { + "version": "10.4.21", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", + "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.24.4", + "caniuse-lite": "^1.0.30001702", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axios": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz", + "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/babel-loader": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.4.1.tgz", + "integrity": "sha512-nXzRChX+Z1GoE6yWavBQg6jDslyFF3SDjl2paADuoQtQW10JqShJt62R6eJQ5m/pjJFDT8xgKIWSP85OY8eXeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^2.0.4", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + }, + "engines": { + "node": ">= 8.9" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "webpack": ">=2" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.14.tgz", + "integrity": "sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.7", + "@babel/helper-define-polyfill-provider": "^0.6.5", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.13.0.tgz", + "integrity": "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.5", + "core-js-compat": "^3.43.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.5.tgz", + "integrity": "sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.5" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", + "dev": true, + "license": "MIT" + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/blueimp-canvas-to-blob": { + "version": "3.29.0", + "resolved": "https://registry.npmjs.org/blueimp-canvas-to-blob/-/blueimp-canvas-to-blob-3.29.0.tgz", + "integrity": "sha512-0pcSSGxC0QxT+yVkivxIqW0Y4VlO2XSDPofBAqoJ1qJxgH9eiUDLv50Rixij2cDuEfx4M6DpD9UGZpRhT5Q8qg==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/blueimp-file-upload": { + "version": "10.32.0", + "resolved": "https://registry.npmjs.org/blueimp-file-upload/-/blueimp-file-upload-10.32.0.tgz", + "integrity": "sha512-3WMJw5Cbfz94Adl1OeyH+rRpGwHiNHzja+CR6aRWPoAtwrUwvP5gXKo0XdX+sdPE+iCU63Xmba88hoHQmzY8RQ==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "blueimp-canvas-to-blob": "3", + "blueimp-load-image": "5", + "blueimp-tmpl": "3" + }, + "peerDependencies": { + "jquery": ">=1.7" + } + }, + "node_modules/blueimp-load-image": { + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/blueimp-load-image/-/blueimp-load-image-5.16.0.tgz", + "integrity": "sha512-3DUSVdOtlfNRk7moRZuTwDmA3NnG8KIJuLcq3c0J7/BIr6X3Vb/EpX3kUH1joxUhmoVF4uCpDfz7wHkz8pQajA==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/blueimp-tmpl": { + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/blueimp-tmpl/-/blueimp-tmpl-3.20.0.tgz", + "integrity": "sha512-g6ln9L+VX8ZA4WA8mgKMethYH+5teroJ2uOkCvcthy9Y9d9LrQ42OAMn+r3ECKu9CB+xe9GOChlIUJBSxwkI6g==", + "dev": true, + "license": "MIT", + "optional": true, + "bin": { + "tmpl.js": "js/compile.js" + } + }, + "node_modules/bn.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.2.tgz", + "integrity": "sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/body-parser/node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/bonjour-service": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.3.0.tgz", + "integrity": "sha512-3YuAUiSkWykd+2Azjgyxei8OWf8thdn8AITIog2M4UICzoqfjlqr64WIjEXZllf/W6vK1goqleSR6brGomxQqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "license": "ISC" + }, + "node_modules/bootbox": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/bootbox/-/bootbox-6.0.4.tgz", + "integrity": "sha512-LoOT8WbiH6YjlhIxzJ3nZHK1p9tlcoa6QNILSGJMx9ihydzFk+DVzNVWNpHk8MZLOkizey1XhFu1dhoqk77xVg==", + "license": "MIT", + "peerDependencies": { + "@popperjs/core": "^2.0.0", + "bootstrap": "^4.4.0 || ^5.0.0", + "jquery": "^3.5.1" + } + }, + "node_modules/bootstrap": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.2.tgz", + "integrity": "sha512-51Bbp/Uxr9aTuy6ca/8FbFloBUJZLHwnhTcnjIeRn2suQWsWzcuJhGjKDB5eppVte/8oCdOL3VuwxvZDUggwGQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "license": "MIT", + "peerDependencies": { + "jquery": "1.9.1 - 3", + "popper.js": "^1.16.1" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", + "dev": true, + "license": "MIT" + }, + "node_modules/browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "node_modules/browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/browserify-rsa": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.1.tgz", + "integrity": "sha512-YBjSAiTqM04ZVei6sXighu679a3SqWORA3qZTEqZImnlkDIFtKc6pNutpjyZ8RJTjQtuYfeetkxM11GwoYXMIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^5.2.1", + "randombytes": "^2.1.0", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/browserify-sign": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.3.tgz", + "integrity": "sha512-JWCZW6SKhfhjJxO8Tyiiy+XYB7cqd2S5/+WeYHsKdNKFlCBhKbblba1A/HN/90YwtxKc8tCErjffZl++UNmGiw==", + "dev": true, + "license": "ISC", + "dependencies": { + "bn.js": "^5.2.1", + "browserify-rsa": "^4.1.0", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.5", + "hash-base": "~3.0", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.7", + "readable-stream": "^2.3.8", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pako": "~1.0.5" + } + }, + "node_modules/browserslist": { + "version": "4.25.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.3.tgz", + "integrity": "sha512-cDGv1kkDI4/0e5yON9yM5G/0A5u8sf5TnmdX5C9qHzI9PPu++sQ9zjm1k9NiOrf3riY4OkK0zSGqfvJyJsgCBQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001735", + "electron-to-chromium": "^1.5.204", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001737", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001737.tgz", + "integrity": "sha512-BiloLiXtQNrY5UyF0+1nSJLXUENuhka2pzy2Fx5pGxqavdrxSCW4U6Pn/PoG3Efspi2frRbHpBV2XsrPE6EDlw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "license": "MIT", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/cipher-base": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.6.tgz", + "integrity": "sha512-3Ek9H3X6pj5TgenXYtNWdaBon1tgYCaebd+XPg0keyjEbEfkD4KkmAxkQ/i1vYvxdcT5nscLBfq9VJRmCBcFSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/clean-css": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", + "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 10.0" + } + }, + "node_modules/cli-table3": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/clipboard": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.11.tgz", + "integrity": "sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==", + "dev": true, + "license": "MIT", + "dependencies": { + "good-listener": "^1.2.2", + "select": "^1.1.2", + "tiny-emitter": "^2.0.0" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/collect.js": { + "version": "4.36.1", + "resolved": "https://registry.npmjs.org/collect.js/-/collect.js-4.36.1.tgz", + "integrity": "sha512-jd97xWPKgHn6uvK31V6zcyPd40lUJd7gpYxbN2VOVxGWO4tyvS9Li4EpsFjXepGTo2tYcOTC4a8YsbQXMJ4XUw==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true, + "license": "MIT" + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "compressible": "~2.0.18", + "debug": "2.6.9", + "negotiator": "~0.6.4", + "on-headers": "~1.1.0", + "safe-buffer": "5.2.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/concat/-/concat-1.0.3.tgz", + "integrity": "sha512-f/ZaH1aLe64qHgTILdldbvyfGiGF4uzeo9IuXUloIOLQzFmIPloy9QbZadNsuVv0j5qbKQvQb/H/UYf2UsKTpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^2.9.0" + }, + "bin": { + "concat": "bin/concat" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/consola": { + "version": "2.15.3", + "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", + "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", + "dev": true + }, + "node_modules/constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/core-js-compat": { + "version": "3.45.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.45.1.tgz", + "integrity": "sha512-tqTt5T4PzsMIZ430XGviK4vzYSoeNJ6CXODi6c/voxOT6IZqBht5/EKaSNnYiEjjRYxjVz7DQIsOsY0XNi8PIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.25.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cosmiconfig": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + } + }, + "node_modules/create-ecdh/node_modules/bn.js": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, + "node_modules/crypto-browserify": { + "version": "3.12.1", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.1.tgz", + "integrity": "sha512-r4ESw/IlusD17lgQi1O20Fa3qNnsckR126TdUuBgAu7GBYSIPvdNyONd3Zrxh0xCwA4+6w/TDArBPsMvhur+KQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserify-cipher": "^1.0.1", + "browserify-sign": "^4.2.3", + "create-ecdh": "^4.0.4", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "diffie-hellman": "^5.0.3", + "hash-base": "~3.0.4", + "inherits": "^2.0.4", + "pbkdf2": "^3.1.2", + "public-encrypt": "^4.0.3", + "randombytes": "^2.1.0", + "randomfill": "^1.0.4" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/css-declaration-sorter": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz", + "integrity": "sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.0.9" + } + }, + "node_modules/css-loader": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.2.tgz", + "integrity": "sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA==", + "license": "MIT", + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.33", + "postcss-modules-extract-imports": "^3.1.0", + "postcss-modules-local-by-default": "^4.0.5", + "postcss-modules-scope": "^3.2.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "webpack": "^5.27.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-select/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano": { + "version": "5.1.15", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.15.tgz", + "integrity": "sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssnano-preset-default": "^5.2.14", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/cssnano" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/cssnano-preset-default": { + "version": "5.2.14", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.14.tgz", + "integrity": "sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-declaration-sorter": "^6.3.1", + "cssnano-utils": "^3.1.0", + "postcss-calc": "^8.2.3", + "postcss-colormin": "^5.3.1", + "postcss-convert-values": "^5.1.3", + "postcss-discard-comments": "^5.1.2", + "postcss-discard-duplicates": "^5.1.0", + "postcss-discard-empty": "^5.1.1", + "postcss-discard-overridden": "^5.1.0", + "postcss-merge-longhand": "^5.1.7", + "postcss-merge-rules": "^5.1.4", + "postcss-minify-font-values": "^5.1.0", + "postcss-minify-gradients": "^5.1.1", + "postcss-minify-params": "^5.1.4", + "postcss-minify-selectors": "^5.2.1", + "postcss-normalize-charset": "^5.1.0", + "postcss-normalize-display-values": "^5.1.0", + "postcss-normalize-positions": "^5.1.1", + "postcss-normalize-repeat-style": "^5.1.1", + "postcss-normalize-string": "^5.1.0", + "postcss-normalize-timing-functions": "^5.1.0", + "postcss-normalize-unicode": "^5.1.1", + "postcss-normalize-url": "^5.1.0", + "postcss-normalize-whitespace": "^5.1.1", + "postcss-ordered-values": "^5.1.3", + "postcss-reduce-initial": "^5.1.2", + "postcss-reduce-transforms": "^5.1.0", + "postcss-svgo": "^5.1.0", + "postcss-unique-selectors": "^5.1.1" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/cssnano-utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", + "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-tree": "^1.1.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/datatables": { + "version": "1.10.18", + "resolved": "https://registry.npmjs.org/datatables/-/datatables-1.10.18.tgz", + "integrity": "sha512-ntatMgS9NN6UMpwbmO+QkYJuKlVeMA2Mi0Gu/QxyIh+dW7ZjLSDhPT2tWlzjpIWEkDYgieDzS9Nu7bdQCW0sbQ==", + "license": "MIT", + "dependencies": { + "jquery": ">=1.7" + } + }, + "node_modules/datatables-responsive": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/datatables-responsive/-/datatables-responsive-1.0.7.tgz", + "integrity": "sha512-xp9jKu7QRkaJl/E50T2v5/U40vjyXLMa0PUjkb4wH9VjHUApG57i7HwCSdx7FMA9rTjIiDTPK3r3/mh1+fGdOw==", + "dependencies": { + "datatables": ">=1.10.1", + "jquery": ">=1.7.0" + } + }, + "node_modules/datatables.net": { + "version": "1.13.11", + "resolved": "https://registry.npmjs.org/datatables.net/-/datatables.net-1.13.11.tgz", + "integrity": "sha512-AE6RkMXziRaqzPcu/pl3SJXeRa6fmXQG/fVjuRESujvkzqDCYEeKTTpPMuVJSGYJpPi32WGSphVNNY1G4nSN/g==", + "license": "MIT", + "dependencies": { + "jquery": "1.8 - 4" + } + }, + "node_modules/datatables.net-bs4": { + "version": "1.13.11", + "resolved": "https://registry.npmjs.org/datatables.net-bs4/-/datatables.net-bs4-1.13.11.tgz", + "integrity": "sha512-1LnxzQDFKpwvBETc8wtUtQ+pUXhs6NJomNST5pRzzHAccckkj9rZeOp3mevKDnDJKuNhBM1Y0rIeZGylJnqh9A==", + "license": "MIT", + "dependencies": { + "datatables.net": "1.13.11", + "jquery": "1.8 - 4" + } + }, + "node_modules/datatables.net-responsive": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/datatables.net-responsive/-/datatables.net-responsive-2.5.1.tgz", + "integrity": "sha512-hyJb2faIzEWUX5Yn4HOSq/6NNB9SXDVbI4OU9ny+XU/2ghZEz4676spOgzpDHTdWvCfM+t1mbUsT70fDiTTr9w==", + "license": "MIT", + "dependencies": { + "datatables.net": "^1.13.0", + "jquery": ">=1.7" + } + }, + "node_modules/datatables.net-responsive-bs4": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/datatables.net-responsive-bs4/-/datatables.net-responsive-bs4-2.5.1.tgz", + "integrity": "sha512-cOVCG9zRioqJiqZUPXel5/vxKGt8EFhxgzVafDNy2hY3ZO+UMMuRKcs2br/QMoojbXzpKNf2rL/lM7NoXKVKZA==", + "license": "MIT", + "dependencies": { + "datatables.net-bs4": "^1.13.0", + "datatables.net-responsive": "2.5.1", + "jquery": ">=1.7" + } + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegate": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", + "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/des.js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", + "integrity": "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true, + "license": "MIT" + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "node_modules/diffie-hellman/node_modules/bn.js": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, + "license": "MIT" + }, + "node_modules/dns-packet": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/dom-serializer/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4", + "npm": ">=1.2" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.3.0.tgz", + "integrity": "sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.0.1" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/domutils/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=10" + } + }, + "node_modules/dotenv-expand": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", + "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true, + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.208", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.208.tgz", + "integrity": "sha512-ozZyibehoe7tOhNaf16lKmljVf+3npZcJIEbJRVftVsmAg5TeA1mGS9dVCZzOwr2xT7xK15V0p7+GZqSPgkuPg==", + "license": "ISC" + }, + "node_modules/elliptic": { + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.1.tgz", + "integrity": "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.18.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", + "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "license": "BSD-2-Clause", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/envinfo": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.14.0.tgz", + "integrity": "sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg==", + "dev": true, + "license": "MIT", + "bin": { + "envinfo": "dist/cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true, + "license": "MIT" + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/express": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/express/node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/file-loader/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/file-type": { + "version": "12.4.2", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-12.4.2.tgz", + "integrity": "sha512-UssQP5ZgIOKelfsaB5CuGAL+Y+q7EmONuiwF3N5HAH0t27rvrttgi6Ra9k/+DVaY9UF6+ybxu5pOXLUdA8N7Vg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "license": "MIT", + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/font-awesome": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz", + "integrity": "sha512-U6kGnykA/6bFmg1M/oT9EkFeIYv7JlX3bozwQJWiiLz6L0w3F5vBVPxHlwyX/vtNq1ckcpRKOB9f2Qal/VtFpg==", + "dev": true, + "license": "(OFL-1.1 AND MIT)", + "engines": { + "node": ">=0.10.3" + } + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs-monkey": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.1.0.tgz", + "integrity": "sha512-QMUezzXWII9EV5aTFXW1UBVUO77wYPpjqIF8/AviUCThNeSYZykpoTixUeaNNBwmCev0AMDWMAni+f8Hxb1IFw==", + "dev": true, + "license": "Unlicense" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "license": "BSD-2-Clause" + }, + "node_modules/globby": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz", + "integrity": "sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/glob": "^7.1.1", + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.0.3", + "glob": "^7.1.3", + "ignore": "^5.1.1", + "merge2": "^1.2.3", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/good-listener": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz", + "integrity": "sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==", + "dev": true, + "license": "MIT", + "dependencies": { + "delegate": "^3.1.2" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw==", + "dev": true, + "license": "MIT" + }, + "node_modules/handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hash-base": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.5.tgz", + "integrity": "sha512-vXm0l45VbcHEVlTCzs8M+s0VeYsB2lnlAaThoLKGXr3bE/VWDOelNUnycUPEhKEaXARL2TEFjBOyUiM6+55KBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/hash-sum": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz", + "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==", + "dev": true, + "license": "MIT" + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/html-entities": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz", + "integrity": "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ], + "license": "MIT" + }, + "node_modules/html-loader": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/html-loader/-/html-loader-1.3.2.tgz", + "integrity": "sha512-DEkUwSd0sijK5PF3kRWspYi56XP7bTNkyg5YWSzBdjaSDmvCufep5c4Vpb3PBf6lUL0YPtLwBfy9fL0t5hBAGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "html-minifier-terser": "^5.1.1", + "htmlparser2": "^4.1.0", + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/html-loader/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/html-minifier-terser": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz", + "integrity": "sha512-ZPr5MNObqnV/T9akshPKbVgyOqLmy+Bxo7juKCfTfnjNniTAMdy4hz21YQqoofMBJD2kdREaqPPdThoR78Tgxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "camel-case": "^4.1.1", + "clean-css": "^4.2.3", + "commander": "^4.1.1", + "he": "^1.2.0", + "param-case": "^3.0.3", + "relateurl": "^0.2.7", + "terser": "^4.6.3" + }, + "bin": { + "html-minifier-terser": "cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/html-minifier-terser/node_modules/clean-css": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.4.tgz", + "integrity": "sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==", + "dev": true, + "license": "MIT", + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/html-minifier-terser/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/html-minifier-terser/node_modules/terser": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.1.tgz", + "integrity": "sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/html-minifier-terser/node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/htmlparser2": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-4.1.0.tgz", + "integrity": "sha512-4zDq1a1zhE4gQso/c5LP1OtrhYTncXNSpvJYtWJBtXAETPlMfi3IFNjGuQbYLuVY4ZR0QMqRVvo4Pdy9KLyP8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^3.0.0", + "domutils": "^2.0.0", + "entities": "^2.0.0" + } + }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", + "dev": true, + "license": "MIT" + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz", + "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-middleware": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz", + "integrity": "sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } + } + }, + "node_modules/https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/imagemin": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/imagemin/-/imagemin-7.0.1.tgz", + "integrity": "sha512-33AmZ+xjZhg2JMCe+vDf6a9mzWukE7l+wAtesjE7KyteqqKjzxv7aVQeWnul1Ve26mWvEQqyPwl0OctNBfSR9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "file-type": "^12.0.0", + "globby": "^10.0.0", + "graceful-fs": "^4.2.2", + "junk": "^3.1.0", + "make-dir": "^3.0.0", + "p-pipe": "^3.0.0", + "replace-ext": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/img-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/img-loader/-/img-loader-4.0.0.tgz", + "integrity": "sha512-UwRcPQdwdOyEHyCxe1V9s9YFwInwEWCpoO+kJGfIqDrBDqA8jZUsEZTxQ0JteNPGw/Gupmwesk2OhLTcnw6tnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "loader-utils": "^1.1.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "imagemin": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/img-loader/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/img-loader/node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/immutable": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.3.tgz", + "integrity": "sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==", + "dev": true, + "license": "MIT" + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/interpret": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/ip-address": { + "version": "5.9.4", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-5.9.4.tgz", + "integrity": "sha512-dHkI3/YNJq4b/qQaz+c8LuarD3pY24JqZWfjB8aZx1gtpc2MDILu9L9jpZe1sHpzo/yWFweQVn+U//FhazUxmw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "lodash": "^4.17.15", + "sprintf-js": "1.1.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/ipaddr.js": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", + "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/jquery": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", + "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==", + "license": "MIT" + }, + "node_modules/jquery-knob": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/jquery-knob/-/jquery-knob-1.2.11.tgz", + "integrity": "sha512-gyeWDmtr2x8XJLSNS2JGNYDUNmfmMrwmFcGPDJ074jnPPvMYf3PJFud4/6AubCT8ahOY7UL4YvALtSwCrxtBBQ==", + "license": "MIT" + }, + "node_modules/jquery-ui-dist": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/jquery-ui-dist/-/jquery-ui-dist-1.13.3.tgz", + "integrity": "sha512-qeTR3SOSQ0jgxaNXSFU6+JtxdzNUSJKgp8LCzVrVKntM25+2YBJW1Ea8B2AwjmmSHfPLy2dSlZxJQN06OfVFhg==", + "license": "MIT", + "dependencies": { + "jquery": ">=1.8.0 <4.0.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/junk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/junk/-/junk-3.1.0.tgz", + "integrity": "sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/klona": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/klona/-/klona-1.1.2.tgz", + "integrity": "sha512-xf88rTeHiXk+XE2Vhi6yj8Wm3gMZrygGdKjJqN8HkV+PwF/t50/LdAKHoHpPcxFAlmQszTZ1CugrK25S7qDRLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/laravel-mix": { + "version": "6.0.49", + "resolved": "https://registry.npmjs.org/laravel-mix/-/laravel-mix-6.0.49.tgz", + "integrity": "sha512-bBMFpFjp26XfijPvY5y9zGKud7VqlyOE0OWUcPo3vTBY5asw8LTjafAbee1dhfLz6PWNqDziz69CP78ELSpfKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.15.8", + "@babel/plugin-proposal-object-rest-spread": "^7.15.6", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-transform-runtime": "^7.15.8", + "@babel/preset-env": "^7.15.8", + "@babel/runtime": "^7.15.4", + "@types/babel__core": "^7.1.16", + "@types/clean-css": "^4.2.5", + "@types/imagemin-gifsicle": "^7.0.1", + "@types/imagemin-mozjpeg": "^8.0.1", + "@types/imagemin-optipng": "^5.2.1", + "@types/imagemin-svgo": "^8.0.0", + "autoprefixer": "^10.4.0", + "babel-loader": "^8.2.3", + "chalk": "^4.1.2", + "chokidar": "^3.5.2", + "clean-css": "^5.2.4", + "cli-table3": "^0.6.0", + "collect.js": "^4.28.5", + "commander": "^7.2.0", + "concat": "^1.0.3", + "css-loader": "^5.2.6", + "cssnano": "^5.0.8", + "dotenv": "^10.0.0", + "dotenv-expand": "^5.1.0", + "file-loader": "^6.2.0", + "fs-extra": "^10.0.0", + "glob": "^7.2.0", + "html-loader": "^1.3.2", + "imagemin": "^7.0.1", + "img-loader": "^4.0.0", + "lodash": "^4.17.21", + "md5": "^2.3.0", + "mini-css-extract-plugin": "^1.6.2", + "node-libs-browser": "^2.2.1", + "postcss-load-config": "^3.1.0", + "postcss-loader": "^6.2.0", + "semver": "^7.3.5", + "strip-ansi": "^6.0.0", + "style-loader": "^2.0.0", + "terser": "^5.9.0", + "terser-webpack-plugin": "^5.2.4", + "vue-style-loader": "^4.1.3", + "webpack": "^5.60.0", + "webpack-cli": "^4.9.1", + "webpack-dev-server": "^4.7.3", + "webpack-merge": "^5.8.0", + "webpack-notifier": "^1.14.1", + "webpackbar": "^5.0.0-3", + "yargs": "^17.2.1" + }, + "bin": { + "laravel-mix": "bin/cli.js", + "mix": "bin/cli.js" + }, + "engines": { + "node": ">=12.14.0" + }, + "peerDependencies": { + "@babel/core": "^7.15.8", + "@babel/plugin-proposal-object-rest-spread": "^7.15.6", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-transform-runtime": "^7.15.8", + "@babel/preset-env": "^7.15.8", + "postcss": "^8.3.11", + "webpack": "^5.60.0", + "webpack-cli": "^4.9.1" + } + }, + "node_modules/laravel-mix/node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/laravel-mix/node_modules/css-loader": { + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-5.2.7.tgz", + "integrity": "sha512-Q7mOvpBNBG7YrVGMxRxcBJZFL75o+cH2abNASdibkj/fffYD8qWbInZrD0S9ccI6vZclF3DsHE7njGlLtaHbhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "icss-utils": "^5.1.0", + "loader-utils": "^2.0.0", + "postcss": "^8.2.15", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.1.0", + "schema-utils": "^3.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.27.0 || ^5.0.0" + } + }, + "node_modules/laravel-mix/node_modules/klona": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/laravel-mix/node_modules/postcss-loader": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz", + "integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.5", + "semver": "^7.3.5" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^5.0.0" + } + }, + "node_modules/laravel-mix/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/launch-editor": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.11.1.tgz", + "integrity": "sha512-SEET7oNfgSaB6Ym0jufAdCeo3meJVeCaaDyzRygy0xsp2BFKCprcfHljTq4QkzTLUxEKkFK6OK4811YM2oSrRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "picocolors": "^1.1.1", + "shell-quote": "^1.8.3" + } + }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "license": "MIT", + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memfs": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", + "dev": true, + "license": "Unlicense", + "dependencies": { + "fs-monkey": "^1.0.4" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "bin": { + "miller-rabin": "bin/miller-rabin" + } + }, + "node_modules/miller-rabin/node_modules/bn.js": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/mini-css-extract-plugin": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-1.6.2.tgz", + "integrity": "sha512-WhDvO3SjGm40oV5y26GjMJYjd2UMqrLAGKy5YS2/3QKJy2F7jgynuHTir/tgUUOiNQu5saXHdc8reo7YuhhT4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0", + "webpack-sources": "^1.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.4.0 || ^5.0.0" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true, + "license": "ISC" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", + "dev": true, + "license": "MIT" + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/multicast-dns": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "dev": true, + "license": "MIT", + "dependencies": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "license": "MIT" + }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "dev": true, + "license": "(BSD-3-Clause OR GPL-2.0)", + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.1", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "^1.0.1" + } + }, + "node_modules/node-notifier": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-9.0.1.tgz", + "integrity": "sha512-fPNFIp2hF/Dq7qLDzSg4vZ0J4e9v60gJR+Qx7RbjbWqzPDdEqeVpEx5CFeDAELIl+A/woaaNn1fQ5nEVerMxJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "growly": "^1.3.0", + "is-wsl": "^2.2.0", + "semver": "^7.3.2", + "shellwords": "^0.1.1", + "uuid": "^8.3.0", + "which": "^2.0.2" + } + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true, + "license": "MIT" + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==", + "dev": true, + "license": "MIT" + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-pipe": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-pipe/-/p-pipe-3.1.0.tgz", + "integrity": "sha512-08pj8ATpzMR0Y80x50yJHn37NF6vjrqHutASaX5LiH5npS9XPvrUmscd9MF5R4fuYRHOxQR1FfMIlF7AzwoPqw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true, + "license": "(MIT AND Zlib)" + }, + "node_modules/param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-asn1": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.7.tgz", + "integrity": "sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==", + "dev": true, + "license": "ISC", + "dependencies": { + "asn1.js": "^4.10.1", + "browserify-aes": "^1.2.0", + "evp_bytestokey": "^1.0.3", + "hash-base": "~3.0", + "pbkdf2": "^3.1.2", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pbkdf2": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.3.tgz", + "integrity": "sha512-wfRLBZ0feWRhCIkoMB6ete7czJcnNnqRpcoWQBLqatqXXmelSRqfdDK4F3u9T2s2cXas/hQJcryI/4lAL+XTlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "create-hash": "~1.1.3", + "create-hmac": "^1.1.7", + "ripemd160": "=2.0.1", + "safe-buffer": "^5.2.1", + "sha.js": "^2.4.11", + "to-buffer": "^1.2.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/pbkdf2/node_modules/create-hash": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz", + "integrity": "sha512-snRpch/kwQhcdlnZKYanNF1m0RDlrCdSKQaH87w1FCFPVPNCQ/Il9QJKAX2jVBZddRdaHBMC+zXa9Gw9tmkNUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "sha.js": "^2.4.0" + } + }, + "node_modules/pbkdf2/node_modules/hash-base": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz", + "integrity": "sha512-0TROgQ1/SxE6KmxWSvXHvRj90/Xo1JvZShofnYF+f6ZsGtR4eES7WfrQzPalmyagfKZCXpVnitiRebZulWsbiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1" + } + }, + "node_modules/pbkdf2/node_modules/ripemd160": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz", + "integrity": "sha512-J7f4wutN8mdbV08MJnXibYpCOPHR+yzy+iQ/AsjMv2j8cLavQ8VGagDFUwwTAdF8FmRKVeNpbTTEwNHCW1g94w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hash-base": "^2.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/popper.js": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", + "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==", + "deprecated": "You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1", + "license": "MIT", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-calc": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz", + "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.0.9", + "postcss-value-parser": "^4.2.0" + }, + "peerDependencies": { + "postcss": "^8.2.2" + } + }, + "node_modules/postcss-colormin": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.1.tgz", + "integrity": "sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.21.4", + "caniuse-api": "^3.0.0", + "colord": "^2.9.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-convert-values": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz", + "integrity": "sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.21.4", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-comments": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz", + "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-duplicates": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", + "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-empty": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", + "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-overridden": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", + "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", + "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "lilconfig": "^2.0.5", + "yaml": "^1.10.2" + }, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-loader": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-8.1.1.tgz", + "integrity": "sha512-0IeqyAsG6tYiDRCYKQJLAmgQr47DX6N7sFSWvQxt6AcupX8DIdmykuk/o/tx0Lze3ErGHJEp5OSRxrelC6+NdQ==", + "license": "MIT", + "dependencies": { + "cosmiconfig": "^9.0.0", + "jiti": "^1.20.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/postcss-merge-longhand": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz", + "integrity": "sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0", + "stylehacks": "^5.1.1" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-merge-rules": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.4.tgz", + "integrity": "sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.21.4", + "caniuse-api": "^3.0.0", + "cssnano-utils": "^3.1.0", + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-font-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz", + "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-gradients": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz", + "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "colord": "^2.9.1", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-params": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz", + "integrity": "sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.21.4", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-selectors": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz", + "integrity": "sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", + "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz", + "integrity": "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==", + "license": "MIT", + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^7.0.0", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default/node_modules/postcss-selector-parser": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", + "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz", + "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==", + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope/node_modules/postcss-selector-parser": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", + "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "license": "ISC", + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-normalize-charset": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", + "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-display-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz", + "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-positions": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz", + "integrity": "sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-repeat-style": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz", + "integrity": "sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-string": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz", + "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-timing-functions": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz", + "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-unicode": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz", + "integrity": "sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.21.4", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz", + "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==", + "dev": true, + "license": "MIT", + "dependencies": { + "normalize-url": "^6.0.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-whitespace": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz", + "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-ordered-values": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz", + "integrity": "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-reduce-initial": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.2.tgz", + "integrity": "sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.21.4", + "caniuse-api": "^3.0.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-reduce-transforms": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz", + "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-svgo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz", + "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0", + "svgo": "^2.7.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-unique-selectors": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz", + "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "license": "MIT" + }, + "node_modules/pretty-time": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pretty-time/-/pretty-time-1.1.0.tgz", + "integrity": "sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-addr/node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true, + "license": "MIT" + }, + "node_modules/public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/public-encrypt/node_modules/bn.js": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/readable-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/rechoir": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", + "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve": "^1.9.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true, + "license": "MIT" + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", + "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", + "dev": true, + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regex-parser": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.3.1.tgz", + "integrity": "sha512-yXLRqatcCuKtVHsWrNg0JL3l1zGfdXeEvDa0bdu4tCDQw0RpMDZsqbkyRTUnKMR0tXF627V2oEWjBEaEdqTwtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/regexpu-core": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", + "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", + "dev": true, + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.2.0", + "regjsgen": "^0.8.0", + "regjsparser": "^0.12.0", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/regjsparser": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", + "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "jsesc": "~3.0.2" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/replace-ext": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", + "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-url-loader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz", + "integrity": "sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "adjust-sourcemap-loader": "^4.0.0", + "convert-source-map": "^1.7.0", + "loader-utils": "^2.0.0", + "postcss": "^8.2.14", + "source-map": "0.6.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/resolve-url-loader/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true, + "license": "MIT" + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/sass": { + "version": "1.90.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.90.0.tgz", + "integrity": "sha512-9GUyuksjw70uNpb1MTYWsH9MQHOHY6kwfnkafC24+7aOMZn9+rVMBxRbLvw756mrBFbIsFg6Xw9IkR2Fnn3k+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.0", + "immutable": "^5.0.2", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, + "node_modules/sass-loader": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-9.0.3.tgz", + "integrity": "sha512-fOwsP98ac1VMme+V3+o0HaaMHp8Q/C9P+MUazLFVi3Jl7ORGHQXL1XeRZt3zLSGZQQPC8xE42Y2WptItvGjDQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "klona": "^1.1.2", + "loader-utils": "^2.0.0", + "neo-async": "^2.6.2", + "schema-utils": "^2.7.0", + "semver": "^7.3.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "fibers": ">= 3.1.0", + "node-sass": "^4.0.0", + "sass": "^1.3.0", + "webpack": "^4.36.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "fibers": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/sass/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/sass/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/select": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz", + "integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA==", + "dev": true, + "license": "MIT" + }, + "node_modules/select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", + "dev": true, + "license": "MIT" + }, + "node_modules/select2": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/select2/-/select2-4.0.13.tgz", + "integrity": "sha512-1JeB87s6oN/TDxQQYCvS5EFoQyvV6eYMZZ0AeA4tdFDYWN3BAGZ8npr17UBFddU0lgAt3H0yjX3X6/ekOj1yjw==", + "dev": true, + "license": "MIT" + }, + "node_modules/selfsigned": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", + "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node-forge": "^1.3.0", + "node-forge": "^1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-index/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true, + "license": "ISC" + }, + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/serve-index/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/serve-index/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true, + "license": "MIT" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true, + "license": "ISC" + }, + "node_modules/sha.js": { + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz", + "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==", + "dev": true, + "license": "(MIT AND BSD-3-Clause)", + "dependencies": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1", + "to-buffer": "^1.2.0" + }, + "bin": { + "sha.js": "bin.js" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/shellwords": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", + "dev": true, + "license": "MIT" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "node_modules/source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "dev": true, + "license": "MIT" + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "node_modules/spdy-transport/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility", + "dev": true, + "license": "MIT" + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/std-env": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz", + "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==", + "dev": true, + "license": "MIT" + }, + "node_modules/stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "node_modules/stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/style-loader": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-2.0.0.tgz", + "integrity": "sha512-Z0gYUJmzZ6ZdRUqpg1r8GsaFKypE+3xAzuFeMuoHgjc9KZv3wMyCRjQIWEbhoFSq7+7yoHXySDJyyWQaPajeiQ==", + "license": "MIT", + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/style-loader/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/stylehacks": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", + "integrity": "sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.21.4", + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/sucrase/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sucrase/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svgo": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", + "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^4.1.3", + "css-tree": "^1.1.3", + "csso": "^4.2.0", + "picocolors": "^1.0.0", + "stable": "^0.1.8" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", + "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.6", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/tailwindcss/node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/tailwindcss/node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/tailwindcss/node_modules/yaml": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", + "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + } + }, + "node_modules/tapable": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.3.tgz", + "integrity": "sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser": { + "version": "5.43.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.43.1.tgz", + "integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==", + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.14.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.14", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz", + "integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==", + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "serialize-javascript": "^6.0.2", + "terser": "^5.31.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", + "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "license": "MIT" + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true, + "license": "MIT" + }, + "node_modules/timers-browserify": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", + "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "setimmediate": "^1.0.4" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/tiny-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", + "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/to-buffer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.1.tgz", + "integrity": "sha512-tB82LpAIWjhLYbqjx3X4zEeHN6M8CiuOEy2JY8SEQVdYRe3CCHOFaqrBW1doLDrfpWhplcW7BL+bO3/6S3pcDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "isarray": "^2.0.5", + "safe-buffer": "^5.2.1", + "typed-array-buffer": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/to-buffer/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha512-JVa5ijo+j/sOoHGjw0sxw734b1LhBkQ3bvUGNdxnVXDCX81Yx7TFgnZygxrIIWn23hbfTaMYLwRmAxFyDuFmIw==", + "dev": true, + "license": "MIT" + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/undici-types": { + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", + "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==", + "license": "MIT" + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", + "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/uri-js/node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/url": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.4.tgz", + "integrity": "sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^1.4.1", + "qs": "^6.12.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "2.0.3" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/util/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true, + "license": "ISC" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/vue-style-loader": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz", + "integrity": "sha512-sFuh0xfbtpRlKfm39ss/ikqs9AbKCoXZBpHeVZ8Tx650o0k0q/YCM7FRvigtxpACezfq6af+a7JeqVTWvncqDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "hash-sum": "^1.0.2", + "loader-utils": "^1.0.2" + } + }, + "node_modules/vue-style-loader/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/vue-style-loader/node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/watchpack": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", + "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==", + "license": "MIT", + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/webpack": { + "version": "5.101.3", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.101.3.tgz", + "integrity": "sha512-7b0dTKR3Ed//AD/6kkx/o7duS8H3f1a4w3BYpIriX4BzIhjkn4teo05cptsxvLesHFKK5KObnadmCHBwGc+51A==", + "license": "MIT", + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.8", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.15.0", + "acorn-import-phases": "^1.0.3", + "browserslist": "^4.24.0", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.17.3", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^4.3.2", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.11", + "watchpack": "^2.4.1", + "webpack-sources": "^3.3.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-cli": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz", + "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@discoveryjs/json-ext": "^0.5.0", + "@webpack-cli/configtest": "^1.2.0", + "@webpack-cli/info": "^1.5.0", + "@webpack-cli/serve": "^1.7.0", + "colorette": "^2.0.14", + "commander": "^7.0.0", + "cross-spawn": "^7.0.3", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^2.2.0", + "rechoir": "^0.7.0", + "webpack-merge": "^5.7.3" + }, + "bin": { + "webpack-cli": "bin/cli.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "4.x.x || 5.x.x" + }, + "peerDependenciesMeta": { + "@webpack-cli/generators": { + "optional": true + }, + "@webpack-cli/migrate": { + "optional": true + }, + "webpack-bundle-analyzer": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/webpack-dev-middleware": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz", + "integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "colorette": "^2.0.10", + "memfs": "^3.4.3", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/webpack-dev-middleware/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack-dev-middleware/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack-dev-middleware/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/webpack-dev-middleware/node_modules/schema-utils": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", + "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-dev-server": { + "version": "4.15.2", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.2.tgz", + "integrity": "sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", + "@types/serve-index": "^1.9.1", + "@types/serve-static": "^1.13.10", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.5.5", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.0.11", + "chokidar": "^3.5.3", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "default-gateway": "^6.0.3", + "express": "^4.17.3", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.0.1", + "launch-editor": "^2.6.0", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "rimraf": "^3.0.2", + "schema-utils": "^4.0.0", + "selfsigned": "^2.1.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^5.3.4", + "ws": "^8.13.0" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.37.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack-dev-server/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack-dev-server/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/webpack-dev-server/node_modules/schema-utils": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", + "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-merge": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", + "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/webpack-notifier": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/webpack-notifier/-/webpack-notifier-1.15.0.tgz", + "integrity": "sha512-N2V8UMgRB5komdXQRavBsRpw0hPhJq2/SWNOGuhrXpIgRhcMexzkGQysUyGStHLV5hkUlgpRiF7IUXoBqyMmzQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "node-notifier": "^9.0.0", + "strip-ansi": "^6.0.0" + }, + "peerDependencies": { + "@types/webpack": ">4.41.31" + }, + "peerDependenciesMeta": { + "@types/webpack": { + "optional": true + } + } + }, + "node_modules/webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + }, + "node_modules/webpack/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", + "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack/node_modules/webpack-sources": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", + "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpackbar": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/webpackbar/-/webpackbar-5.0.2.tgz", + "integrity": "sha512-BmFJo7veBDgQzfWXl/wwYXr/VFus0614qZ8i9znqcl9fnEdiVkdbi0TedLQ6xAK92HZHDJ0QmyQ0fmuZPAgCYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "consola": "^2.15.3", + "pretty-time": "^1.1.0", + "std-env": "^3.0.1" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "webpack": "3 || 4 || 5" + } + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wildcard": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 000000000..423064a20 --- /dev/null +++ b/package.json @@ -0,0 +1,45 @@ +{ + "private": true, + "scripts": { + "dev": "npm run development", + "development": "NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --config=node_modules/laravel-mix/setup/webpack.config.js", + "watch": "npm run development -- --watch", + "watch-poll": "npm run watch -- --watch-poll", + "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js", + "prod": "npm run production", + "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --config=node_modules/laravel-mix/setup/webpack.config.js" + }, + "devDependencies": { + "autoprefixer": "^10.4.19", + "axios": "^1.6.4", + "blueimp-file-upload": "^10.32.0", + "clipboard": "^2.0.11", + "font-awesome": "^4.7.0", + "ip-address": "^5.9.4", + "laravel-mix": "^6.0.49", + "moment": "^2.30.1", + "postcss": "^8.4.38", + "resolve-url-loader": "^5.0.0", + "sass": "^1.26.10", + "sass-loader": "^9.0.3", + "select2": "^4.0.13", + "tailwindcss": "^3.4.7" + }, + "dependencies": { + "alpinejs": "^3.14.1", + "bootbox": "^6.0.0", + "bootstrap": "^4.6.2", + "braces": "^3.0.3", + "cross-env": "^7.0.3", + "css-loader": "^7.1.2", + "datatables-responsive": "^1.0.7", + "datatables.net-bs4": "^1.10.21", + "datatables.net-responsive-bs4": "^2.2.5", + "jquery": "^3.5.1", + "jquery-knob": "^1.2.11", + "jquery-ui-dist": "^1.12.1", + "postcss-loader": "^8.1.1", + "style-loader": "^2.0.0" + }, + "version": "1.7.1" +} diff --git a/phpunit.dusk.xml b/phpunit.dusk.xml new file mode 100644 index 000000000..1e7c3099e --- /dev/null +++ b/phpunit.dusk.xml @@ -0,0 +1,16 @@ + + + + + ./tests/Browser + + + + + + + + ./app + + + diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 000000000..236fe235e --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,38 @@ + + + + + ./tests/Browser + + + ./tests/Docstore + ./tests/DocstoreCustomer + + + ./tests/API + ./tests/IXP + ./tests/Services + ./tests/Tasks + ./tests/Utils + + + + + + + + + + ./app + + + diff --git a/psalm-baseline.xml b/psalm-baseline.xml new file mode 100644 index 000000000..6a06c8efb --- /dev/null +++ b/psalm-baseline.xml @@ -0,0 +1,2465 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + custid]]> + customer]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + customer]]> + customer]]> + id]]> + privs]]> + username]]> + username]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + enabled]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + withInput()]]> + + + + + + + + + + + + + + + + + + + + + + email]]> + + + + + + + + + + + + + + + user]]> + + + + + + + + + + + + + + + + + + + + + + + name]]> + name]]> + name]]> + name]]> + username]]> + username]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + withInput()]]> + + $cs->id ] )]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $this->object, + 'groupsForContact' => $this->object->contactGroupsAll()->get()->keyBy( 'id' )->toArray(), + 'custs' => Customer::orderBy( 'name' )->get(), + 'roles' => $data[ 'roles' ], + 'allGroups' => $data[ 'allGroups' ], + ]]]> + $this->object, + 'groupsForContact' => [], + 'custs' => Customer::orderBy( 'name' )->get(), + 'roles' => $data[ 'roles' ], + 'allGroups' => $data[ 'allGroups' ], + ]]]> + $this->object->id ] ) )]]> + $this->object->id ] ) )]]> + + + + + + + + + + + + + + + + + + custid]]> + + + + + + + + + + + + + + + + $this->object, + 'types' => config( "contact_group.types" ) + ]]]> + $this->object, + 'types' => config( 'contact_group.types' ) + ]]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + purchaseOrderRequired]]> + status]]> + type]]> + + + + + + + + + + + + + billingNotes]]> + isResold]]> + isResold]]> + name]]> + reseller]]> + reseller]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + custid]]> + + + id]]> + + + + + + + + + + customer]]> + customer]]> + customer]]> + + + billingAddress1]]> + billingAddress2]]> + billingAddress3]]> + billingContactName]]> + billingCountry]]> + billingEmail]]> + billingPostcode]]> + billingTelephone]]> + billingTownCity]]> + invoiceEmail]]> + noc24hphone]]> + nocemail]]> + nochours]]> + nocphone]]> + nocwww]]> + + + + + privs]]> + username]]> + username]]> + + + + + disk . '.root', '*** UNKNOWN LOCATION ***' )]]> + + + username]]> + username]]> + username]]> + + + + + + + + + + + customer->id]]> + + + disk . '.root', '*** UNKNOWN LOCATION ***' )]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fanout]]> + + + + + + + + + custid]]> + custid]]> + mtu]]> + type]]> + + + + + speed]]> + status]]> + switchPort->switchid]]> + + + + + + + + + resellerMode() && $vi]]> + + + + + + + cb]]> + cb]]> + duplex]]> + duplex]]> + speed]]> + speed]]> + status]]> + status]]> + switchportid]]> + switchportid]]> + virtualinterfaceid]]> + virtualinterfaceid]]> + + + + + orderBy('name')->get()]]> + + + + + + + + + + + channelgroup]]> + channelgroup]]> + lag_framing]]> + name]]> + switchportid]]> + vlanid]]> + + + + + customer->maxprefixes]]> + + + + + + + + + + + + + redirect2vi]]> + virtualinterfaceid]]> + vlanid]]> + vlanid]]> + vlanid]]> + + + + + + + + + + + + + + + + getFirstIP()->version]]> + getFirstIP()->version]]> + getFirstIP()->version]]> + network]]> + network]]> + vlan]]> + + + + + + + + + + + + + + + + + + + + reset_cache]]> + reset_cache]]> + + + + + + + + + + + + + + + + + + + + + + + + + + }]]> + }]]> + + + + withInput()]]> + withInput()]]> + + + + + + + + + + + + + + + + location_notes]]> + location_notes]]> + numberOfPorts]]> + numberOfPorts]]> + + + + + username]]> + username]]> + username]]> + username]]> + + + port_id]]> + slave_id]]> + + + + + loa]]> + + + + + username]]> + + + + + patch_panel_port_history_id]]> + + + + + + switchPort->switchid ?? null]]> + orderBy( 'name' )->get()]]> + + + + + + + + allocated]]> + allocated]]> + assigned_at]]> + assigned_at]]> + cease_requested_at]]> + cease_requested_at]]> + ceased_at]]> + ceased_at]]> + chargeable]]> + colo_billing_ref]]> + colo_circuit_ref]]> + connected_at]]> + connected_at]]> + customer_id]]> + customer_id]]> + description]]> + duplex]]> + internal_use]]> + notes]]> + owned_by]]> + partner_port]]> + pi_status]]> + private_notes]]> + state]]> + switch]]> + switch_port_id]]> + ticket_ref]]> + + + + + + + + + + + + + + + + + + + custid]]> + custid]]> + custid]]> + custid]]> + custid]]> + custid]]> + + + marksent]]> + peerid]]> + sendtome]]> + + + + + + + + + + + + + + + + + + + + + + + + + prefs]]> + + + prefs[ 'notes' ][ 'global_notifs' ]]]> + + + authorisedMobile]]> + email]]> + name]]> + new_password]]> + notify]]> + username]]> + + + + + atlas_id );]]> + + + + + + , preAddForm: true}]]> + + + + + + + + + + protocol]]> + type]]> + + + + + + + + + + + template]]> + + + + + protocol ?? null]]> + + + + + + username]]> + username]]> + username]]> + username]]> + username]]> + + + peer_id]]> + vlan_id]]> + + + + + + + custid]]> + + + + + >}]]> + , interfaces: array>}]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + custid]]> + custid]]> + + + + + + + + + + + + + + + + + + + + + + + + + + day]]> + infra]]> + vlan]]> + vlan]]> + + + + + , infra: mixed, vendors: mixed}]]> + , infra: mixed, vendors: mixed}]]> + + + + + + + + + + + + + + + + + + + + + + + + + hostname]]> + snmppasswd]]> + + + + + + + + + + + + + + + + + + + + + + + + username]]> + username]]> + customer]]> + + + customers()]]> + + + privs]]> + user_id]]> + + + + + user2FA]]> + + + + + + + + customerToUser()->where( 'customer_id', Auth::user()->custid )->first() ) ) { + abort(404, 'UserToCustomer not found'); + }]]> + + + custid]]> + custid]]> + custid]]> + username]]> + username]]> + + + + + + authorisedMobile]]> + authorisedMobile]]> + custid]]> + custid]]> + disabled]]> + disabled]]> + email]]> + email]]> + email]]> + name]]> + name]]> + privs]]> + privs]]> + username]]> + username]]> + + + + + + + + object->user_id === Auth::id()]]> + + + + + + + + + + + + sourcea_dd ]]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + user2FA]]> + user2FA]]> + + + + + + + + + + + + + + + + + + + + + + + + cb]]> + type]]> + + + + + cust]]> + cust]]> + isReseller]]> + isResold]]> + reseller]]> + type]]> + type]]> + + + + + getFirstIP()]]> + + + version]]> + + + network]]> + + + + + getFirstIP()]]> + + + version]]> + + + network]]> + + + + + + + + cust]]> + cust]]> + protocol]]> + type]]> + + + + + port_id]]> + + + + + password]]> + + + current_password]]> + + + + + password]]> + + + actual_password]]> + + + + + advertised_prefix]]> + peer_id]]> + protocol]]> + protocol]]> + received_prefix]]> + vlan_id]]> + + + + + allocated]]> + duplex]]> + prewired]]> + + + + + fanout]]> + + + + + api]]> + api_type]]> + db_id]]> + protocol]]> + router]]> + + + + + ipv4enabled]]> + ipv6enabled]]> + + + + + ipv4enabled]]> + ipv6enabled]]> + + + + + id]]> + + + + + email]]> + + + + + c2u]]> + + + + + customer_id]]> + privs]]> + user_id]]> + + + + + username]]> + + + u]]> + + + + + + + + custid]]> + id]]> + privs]]> + + + + + + + customer]]> + + + + + + + custid]]> + privs]]> + u]]> + u]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + prefs[ 'notes' ][ 'global_notifs' ] ) && $user->prefs[ 'notes' ][ 'global_notifs' ] === 'none' ){ + continue; + }]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + event->note()]]> + + + + + message]]> + subject]]> + + + + + email_subject]]> + email_text]]> + + + + + + + + + + + message]]> + sendtome]]> + subject]]> + + + + + + + + + + + + + + + + + + + + + + + deleteObject();]]> + + + + + + + + + + + id ]]]> + + + + + + + + + + + + + + + + + + , removed_from: list}]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + physicalinterface]]> + + + + + autsys]]> + + + + + + , members?: array<''|int, self>}>]]> + + + + + where('id', null ); + } elseif( !$us->isSuperUser() ) { + // if not superuser make sure only records from the same customer are returned + $builder->where('cust_id', $us->custid ); + } + }]]> + + + + + + + + + where('id', null ); + } elseif( !$us->isSuperUser() ) { + // If not super user make sure only allowed files are returned + $builder->where('min_privs', '<=', $us->privs() ); + } + }]]> + + + + + + + + + + + + + + + + + + + + + asn_v4]]> + asn_v4]]> + asn_v6]]> + asn_v6]]> + prefix_v4]]> + prefix_v4]]> + prefix_v6]]> + prefix_v6]]> + + + + + + + + + + + + + + number % 2 ? ( floor( $this->number / 2 ) ) + 1 : $this->number / 2]]> + number / 2 ) ) + 1]]> + + + hasMany(PatchPanelPortFile::class, 'patch_panel_port_id' ) + ->where( 'is_private', 0 )]]> + + + + + + remove();]]> + + + + + + + username]]> + username]]> + username]]> + + + archive() )]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + coreLink()->coreBundle;]]> + + + ]]> + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + user()->userRememberTokens]]> + + + + + + + + + pi->switchPort->ifMtu]]> + + + pi->switchPort->ifMtu]]> + switchPort]]> + switchPort]]> + + + + + + + pi->switchPort->ifSpeed]]> + + + + + virtualInterface->customer->autsys]]> + virtualInterface->customer->autsys]]> + + + + + + + + + + + switchPort]]> + switchPort]]> + + + + + + + + + physicalInterfaces]]> + + + + + arping( $this->vli )]]> + + + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + id ] += ( ( $pi->detectedSpeed() > 0 ) ? $pi->detectedSpeed() : 1 ) * 1000000 / 8]]> + infrastructureModel->id ] += $maxbytes]]> + + id ] += $maxbytes]]> + id ] += $maxbytes]]> + + + + + + + + corelinks]]> + id]]> + + + + + + + + + + + + + + + + + + vli()->vlan->id ]]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + coreBundle()]]> + + + + + + + + customer()]]> + + + + + + + + + + + + + + + + + + + + + + + + + infrastructure()]]> + + + + + + + + + + + + + + + + + + + + location()]]> + + + + + + + + + + + + + + dvli()]]> + svli()]]> + + + + + + + + physicalInterface()]]> + + + + + + + + + + + + switch()]]> + + + + + + + + + + + + + + + + + + + + + + + + + + virtualInterface()]]> + + + + + + + + + + + vlan()]]> + + + + + + + + + + + + + + vlanInterface()]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + helpdesk_id]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + autsys ]]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + graph()->period() ]]]> + graph()->period() ]]]> + + + graph()->periodEnd()->timestamp]]> + graph()->periodStart()->timestamp]]> + graph()->period() ]]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + postDeleteRedirect()]]> + + + + + + + + + + getBody()]]> + + + + + + + + + + + + + + $name, + 'description' => sprintf( "Contact role for %s matters", strtolower( $name ) ), + 'type' => ContactGroup::TYPE_ROLE, + 'active' => true, + 'limited_to' => 0, + ])]]> + + + + + $irrdb['host'], + 'source' => $irrdb['source'], + 'notes' => $irrdb['notes'], + ])]]> + + + + + $vendor[0], + 'shortname' => $vendor[1], + 'nagios_name' => $vendor[2], + 'bundle_name' => $vendor[3] ?? null, + ])]]> + + + diff --git a/psalm.xml b/psalm.xml new file mode 100644 index 000000000..d14973841 --- /dev/null +++ b/psalm.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/.htaccess.dist b/public/.htaccess.dist deleted file mode 100644 index 28d5aa6cd..000000000 --- a/public/.htaccess.dist +++ /dev/null @@ -1,8 +0,0 @@ -SetEnv APPLICATION_ENV development - -RewriteEngine On -RewriteCond %{REQUEST_FILENAME} -s [OR] -RewriteCond %{REQUEST_FILENAME} -l [OR] -RewriteCond %{REQUEST_FILENAME} -d -RewriteRule ^.*$ - [NC,L] -RewriteRule ^.*$ /ixp/index.php [NC,L] diff --git a/public/css/200-jquery-ui-1.8.23.custom.css b/public/css/200-jquery-ui-1.8.23.custom.css deleted file mode 100644 index ebede8fb4..000000000 --- a/public/css/200-jquery-ui-1.8.23.custom.css +++ /dev/null @@ -1,563 +0,0 @@ -/*! - * jQuery UI CSS Framework 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Theming/API - */ - -/* Layout helpers -----------------------------------*/ -.ui-helper-hidden { display: none; } -.ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); } -.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; } -.ui-helper-clearfix:before, .ui-helper-clearfix:after { content: ""; display: table; } -.ui-helper-clearfix:after { clear: both; } -.ui-helper-clearfix { zoom: 1; } -.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); } - - -/* Interaction Cues -----------------------------------*/ -.ui-state-disabled { cursor: default !important; } - - -/* Icons -----------------------------------*/ - -/* states and images */ -.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; } - - -/* Misc visuals -----------------------------------*/ - -/* Overlays */ -.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } - - -/*! - * jQuery UI CSS Framework 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Theming/API - * - * To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Trebuchet%20MS,%20Tahoma,%20Verdana,%20Arial,%20sans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=f6a828&bgTextureHeader=12_gloss_wave.png&bgImgOpacityHeader=35&borderColorHeader=e78f08&fcHeader=ffffff&iconColorHeader=ffffff&bgColorContent=eeeeee&bgTextureContent=03_highlight_soft.png&bgImgOpacityContent=100&borderColorContent=dddddd&fcContent=333333&iconColorContent=222222&bgColorDefault=f6f6f6&bgTextureDefault=02_glass.png&bgImgOpacityDefault=100&borderColorDefault=cccccc&fcDefault=1c94c4&iconColorDefault=ef8c08&bgColorHover=fdf5ce&bgTextureHover=02_glass.png&bgImgOpacityHover=100&borderColorHover=fbcb09&fcHover=c77405&iconColorHover=ef8c08&bgColorActive=ffffff&bgTextureActive=02_glass.png&bgImgOpacityActive=65&borderColorActive=fbd850&fcActive=eb8f00&iconColorActive=ef8c08&bgColorHighlight=ffe45c&bgTextureHighlight=03_highlight_soft.png&bgImgOpacityHighlight=75&borderColorHighlight=fed22f&fcHighlight=363636&iconColorHighlight=228ef1&bgColorError=b81900&bgTextureError=08_diagonals_thick.png&bgImgOpacityError=18&borderColorError=cd0a0a&fcError=ffffff&iconColorError=ffd27a&bgColorOverlay=666666&bgTextureOverlay=08_diagonals_thick.png&bgImgOpacityOverlay=20&opacityOverlay=50&bgColorShadow=000000&bgTextureShadow=01_flat.png&bgImgOpacityShadow=10&opacityShadow=20&thicknessShadow=5px&offsetTopShadow=-5px&offsetLeftShadow=-5px&cornerRadiusShadow=5px - */ - - -/* Component containers -----------------------------------*/ -.ui-widget { font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif; font-size: 1.1em; } -.ui-widget .ui-widget { font-size: 1em; } -.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif; font-size: 1em; } -.ui-widget-content { border: 1px solid #dddddd; background: #eeeeee url(images/ui-bg_highlight-soft_100_eeeeee_1x100.png) 50% top repeat-x; color: #333333; } -.ui-widget-content a { color: #333333; } -.ui-widget-header { border: 1px solid #e78f08; background: #f6a828 url(images/ui-bg_gloss-wave_35_f6a828_500x100.png) 50% 50% repeat-x; color: #ffffff; font-weight: bold; } -.ui-widget-header a { color: #ffffff; } - -/* Interaction states -----------------------------------*/ -.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #cccccc; background: #f6f6f6 url(images/ui-bg_glass_100_f6f6f6_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #1c94c4; } -.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #1c94c4; text-decoration: none; } -.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #fbcb09; background: #fdf5ce url(images/ui-bg_glass_100_fdf5ce_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #c77405; } -.ui-state-hover a, .ui-state-hover a:hover { color: #c77405; text-decoration: none; } -.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #fbd850; background: #ffffff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #eb8f00; } -.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #eb8f00; text-decoration: none; } -.ui-widget :active { outline: none; } - -/* Interaction Cues -----------------------------------*/ -.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #fed22f; background: #ffe45c url(images/ui-bg_highlight-soft_75_ffe45c_1x100.png) 50% top repeat-x; color: #363636; } -.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636; } -.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a; background: #b81900 url(images/ui-bg_diagonals-thick_18_b81900_40x40.png) 50% 50% repeat; color: #ffffff; } -.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #ffffff; } -.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #ffffff; } -.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; } -.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; } -.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; } - -/* Icons -----------------------------------*/ - -/* states and images */ -.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png); } -.ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); } -.ui-widget-header .ui-icon {background-image: url(images/ui-icons_ffffff_256x240.png); } -.ui-state-default .ui-icon { background-image: url(images/ui-icons_ef8c08_256x240.png); } -.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_ef8c08_256x240.png); } -.ui-state-active .ui-icon {background-image: url(images/ui-icons_ef8c08_256x240.png); } -.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_228ef1_256x240.png); } -.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_ffd27a_256x240.png); } - -/* positioning */ -.ui-icon-carat-1-n { background-position: 0 0; } -.ui-icon-carat-1-ne { background-position: -16px 0; } -.ui-icon-carat-1-e { background-position: -32px 0; } -.ui-icon-carat-1-se { background-position: -48px 0; } -.ui-icon-carat-1-s { background-position: -64px 0; } -.ui-icon-carat-1-sw { background-position: -80px 0; } -.ui-icon-carat-1-w { background-position: -96px 0; } -.ui-icon-carat-1-nw { background-position: -112px 0; } -.ui-icon-carat-2-n-s { background-position: -128px 0; } -.ui-icon-carat-2-e-w { background-position: -144px 0; } -.ui-icon-triangle-1-n { background-position: 0 -16px; } -.ui-icon-triangle-1-ne { background-position: -16px -16px; } -.ui-icon-triangle-1-e { background-position: -32px -16px; } -.ui-icon-triangle-1-se { background-position: -48px -16px; } -.ui-icon-triangle-1-s { background-position: -64px -16px; } -.ui-icon-triangle-1-sw { background-position: -80px -16px; } -.ui-icon-triangle-1-w { background-position: -96px -16px; } -.ui-icon-triangle-1-nw { background-position: -112px -16px; } -.ui-icon-triangle-2-n-s { background-position: -128px -16px; } -.ui-icon-triangle-2-e-w { background-position: -144px -16px; } -.ui-icon-arrow-1-n { background-position: 0 -32px; } -.ui-icon-arrow-1-ne { background-position: -16px -32px; } -.ui-icon-arrow-1-e { background-position: -32px -32px; } -.ui-icon-arrow-1-se { background-position: -48px -32px; } -.ui-icon-arrow-1-s { background-position: -64px -32px; } -.ui-icon-arrow-1-sw { background-position: -80px -32px; } -.ui-icon-arrow-1-w { background-position: -96px -32px; } -.ui-icon-arrow-1-nw { background-position: -112px -32px; } -.ui-icon-arrow-2-n-s { background-position: -128px -32px; } -.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } -.ui-icon-arrow-2-e-w { background-position: -160px -32px; } -.ui-icon-arrow-2-se-nw { background-position: -176px -32px; } -.ui-icon-arrowstop-1-n { background-position: -192px -32px; } -.ui-icon-arrowstop-1-e { background-position: -208px -32px; } -.ui-icon-arrowstop-1-s { background-position: -224px -32px; } -.ui-icon-arrowstop-1-w { background-position: -240px -32px; } -.ui-icon-arrowthick-1-n { background-position: 0 -48px; } -.ui-icon-arrowthick-1-ne { background-position: -16px -48px; } -.ui-icon-arrowthick-1-e { background-position: -32px -48px; } -.ui-icon-arrowthick-1-se { background-position: -48px -48px; } -.ui-icon-arrowthick-1-s { background-position: -64px -48px; } -.ui-icon-arrowthick-1-sw { background-position: -80px -48px; } -.ui-icon-arrowthick-1-w { background-position: -96px -48px; } -.ui-icon-arrowthick-1-nw { background-position: -112px -48px; } -.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } -.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } -.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } -.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } -.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } -.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } -.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } -.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } -.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } -.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } -.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } -.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } -.ui-icon-arrowreturn-1-w { background-position: -64px -64px; } -.ui-icon-arrowreturn-1-n { background-position: -80px -64px; } -.ui-icon-arrowreturn-1-e { background-position: -96px -64px; } -.ui-icon-arrowreturn-1-s { background-position: -112px -64px; } -.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } -.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } -.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } -.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } -.ui-icon-arrow-4 { background-position: 0 -80px; } -.ui-icon-arrow-4-diag { background-position: -16px -80px; } -.ui-icon-extlink { background-position: -32px -80px; } -.ui-icon-newwin { background-position: -48px -80px; } -.ui-icon-refresh { background-position: -64px -80px; } -.ui-icon-shuffle { background-position: -80px -80px; } -.ui-icon-transfer-e-w { background-position: -96px -80px; } -.ui-icon-transferthick-e-w { background-position: -112px -80px; } -.ui-icon-folder-collapsed { background-position: 0 -96px; } -.ui-icon-folder-open { background-position: -16px -96px; } -.ui-icon-document { background-position: -32px -96px; } -.ui-icon-document-b { background-position: -48px -96px; } -.ui-icon-note { background-position: -64px -96px; } -.ui-icon-mail-closed { background-position: -80px -96px; } -.ui-icon-mail-open { background-position: -96px -96px; } -.ui-icon-suitcase { background-position: -112px -96px; } -.ui-icon-comment { background-position: -128px -96px; } -.ui-icon-person { background-position: -144px -96px; } -.ui-icon-print { background-position: -160px -96px; } -.ui-icon-trash { background-position: -176px -96px; } -.ui-icon-locked { background-position: -192px -96px; } -.ui-icon-unlocked { background-position: -208px -96px; } -.ui-icon-bookmark { background-position: -224px -96px; } -.ui-icon-tag { background-position: -240px -96px; } -.ui-icon-home { background-position: 0 -112px; } -.ui-icon-flag { background-position: -16px -112px; } -.ui-icon-calendar { background-position: -32px -112px; } -.ui-icon-cart { background-position: -48px -112px; } -.ui-icon-pencil { background-position: -64px -112px; } -.ui-icon-clock { background-position: -80px -112px; } -.ui-icon-disk { background-position: -96px -112px; } -.ui-icon-calculator { background-position: -112px -112px; } -.ui-icon-zoomin { background-position: -128px -112px; } -.ui-icon-zoomout { background-position: -144px -112px; } -.ui-icon-search { background-position: -160px -112px; } -.ui-icon-wrench { background-position: -176px -112px; } -.ui-icon-gear { background-position: -192px -112px; } -.ui-icon-heart { background-position: -208px -112px; } -.ui-icon-star { background-position: -224px -112px; } -.ui-icon-link { background-position: -240px -112px; } -.ui-icon-cancel { background-position: 0 -128px; } -.ui-icon-plus { background-position: -16px -128px; } -.ui-icon-plusthick { background-position: -32px -128px; } -.ui-icon-minus { background-position: -48px -128px; } -.ui-icon-minusthick { background-position: -64px -128px; } -.ui-icon-close { background-position: -80px -128px; } -.ui-icon-closethick { background-position: -96px -128px; } -.ui-icon-key { background-position: -112px -128px; } -.ui-icon-lightbulb { background-position: -128px -128px; } -.ui-icon-scissors { background-position: -144px -128px; } -.ui-icon-clipboard { background-position: -160px -128px; } -.ui-icon-copy { background-position: -176px -128px; } -.ui-icon-contact { background-position: -192px -128px; } -.ui-icon-image { background-position: -208px -128px; } -.ui-icon-video { background-position: -224px -128px; } -.ui-icon-script { background-position: -240px -128px; } -.ui-icon-alert { background-position: 0 -144px; } -.ui-icon-info { background-position: -16px -144px; } -.ui-icon-notice { background-position: -32px -144px; } -.ui-icon-help { background-position: -48px -144px; } -.ui-icon-check { background-position: -64px -144px; } -.ui-icon-bullet { background-position: -80px -144px; } -.ui-icon-radio-off { background-position: -96px -144px; } -.ui-icon-radio-on { background-position: -112px -144px; } -.ui-icon-pin-w { background-position: -128px -144px; } -.ui-icon-pin-s { background-position: -144px -144px; } -.ui-icon-play { background-position: 0 -160px; } -.ui-icon-pause { background-position: -16px -160px; } -.ui-icon-seek-next { background-position: -32px -160px; } -.ui-icon-seek-prev { background-position: -48px -160px; } -.ui-icon-seek-end { background-position: -64px -160px; } -.ui-icon-seek-start { background-position: -80px -160px; } -/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ -.ui-icon-seek-first { background-position: -80px -160px; } -.ui-icon-stop { background-position: -96px -160px; } -.ui-icon-eject { background-position: -112px -160px; } -.ui-icon-volume-off { background-position: -128px -160px; } -.ui-icon-volume-on { background-position: -144px -160px; } -.ui-icon-power { background-position: 0 -176px; } -.ui-icon-signal-diag { background-position: -16px -176px; } -.ui-icon-signal { background-position: -32px -176px; } -.ui-icon-battery-0 { background-position: -48px -176px; } -.ui-icon-battery-1 { background-position: -64px -176px; } -.ui-icon-battery-2 { background-position: -80px -176px; } -.ui-icon-battery-3 { background-position: -96px -176px; } -.ui-icon-circle-plus { background-position: 0 -192px; } -.ui-icon-circle-minus { background-position: -16px -192px; } -.ui-icon-circle-close { background-position: -32px -192px; } -.ui-icon-circle-triangle-e { background-position: -48px -192px; } -.ui-icon-circle-triangle-s { background-position: -64px -192px; } -.ui-icon-circle-triangle-w { background-position: -80px -192px; } -.ui-icon-circle-triangle-n { background-position: -96px -192px; } -.ui-icon-circle-arrow-e { background-position: -112px -192px; } -.ui-icon-circle-arrow-s { background-position: -128px -192px; } -.ui-icon-circle-arrow-w { background-position: -144px -192px; } -.ui-icon-circle-arrow-n { background-position: -160px -192px; } -.ui-icon-circle-zoomin { background-position: -176px -192px; } -.ui-icon-circle-zoomout { background-position: -192px -192px; } -.ui-icon-circle-check { background-position: -208px -192px; } -.ui-icon-circlesmall-plus { background-position: 0 -208px; } -.ui-icon-circlesmall-minus { background-position: -16px -208px; } -.ui-icon-circlesmall-close { background-position: -32px -208px; } -.ui-icon-squaresmall-plus { background-position: -48px -208px; } -.ui-icon-squaresmall-minus { background-position: -64px -208px; } -.ui-icon-squaresmall-close { background-position: -80px -208px; } -.ui-icon-grip-dotted-vertical { background-position: 0 -224px; } -.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } -.ui-icon-grip-solid-vertical { background-position: -32px -224px; } -.ui-icon-grip-solid-horizontal { background-position: -48px -224px; } -.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } -.ui-icon-grip-diagonal-se { background-position: -80px -224px; } - - -/* Misc visuals -----------------------------------*/ - -/* Corner radius */ -.ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; -khtml-border-top-left-radius: 4px; border-top-left-radius: 4px; } -.ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; -khtml-border-top-right-radius: 4px; border-top-right-radius: 4px; } -.ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; -khtml-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; } -.ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br { -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; -khtml-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; } - -/* Overlays */ -.ui-widget-overlay { background: #666666 url(images/ui-bg_diagonals-thick_20_666666_40x40.png) 50% 50% repeat; opacity: .50;filter:Alpha(Opacity=50); } -.ui-widget-shadow { margin: -5px 0 0 -5px; padding: 5px; background: #000000 url(images/ui-bg_flat_10_000000_40x100.png) 50% 50% repeat-x; opacity: .20;filter:Alpha(Opacity=20); -moz-border-radius: 5px; -khtml-border-radius: 5px; -webkit-border-radius: 5px; border-radius: 5px; }/*! - * jQuery UI Resizable 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Resizable#theming - */ -.ui-resizable { position: relative;} -.ui-resizable-handle { position: absolute;font-size: 0.1px; display: block; } -.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; } -.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; } -.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; } -.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; } -.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; } -.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; } -.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; } -.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; } -.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/*! - * jQuery UI Selectable 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Selectable#theming - */ -.ui-selectable-helper { position: absolute; z-index: 100; border:1px dotted black; } -/*! - * jQuery UI Accordion 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Accordion#theming - */ -/* IE/Win - Fix animation bug - #4615 */ -.ui-accordion { width: 100%; } -.ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; } -.ui-accordion .ui-accordion-li-fix { display: inline; } -.ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; } -.ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em .7em; } -.ui-accordion-icons .ui-accordion-header a { padding-left: 2.2em; } -.ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; } -.ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; } -.ui-accordion .ui-accordion-content-active { display: block; } -/*! - * jQuery UI Autocomplete 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Autocomplete#theming - */ -.ui-autocomplete { position: absolute; cursor: default; } - -/* workarounds */ -* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */ - -/* - * jQuery UI Menu 1.8.23 - * - * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Menu#theming - */ -.ui-menu { - list-style:none; - padding: 2px; - margin: 0; - display:block; - float: left; -} -.ui-menu .ui-menu { - margin-top: -3px; -} -.ui-menu .ui-menu-item { - margin:0; - padding: 0; - zoom: 1; - float: left; - clear: left; - width: 100%; -} -.ui-menu .ui-menu-item a { - text-decoration:none; - display:block; - padding:.2em .4em; - line-height:1.5; - zoom:1; -} -.ui-menu .ui-menu-item a.ui-state-hover, -.ui-menu .ui-menu-item a.ui-state-active { - font-weight: normal; - margin: -1px; -} -/*! - * jQuery UI Button 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Button#theming - */ -.ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: visible; } /* the overflow property removes extra width in IE */ -.ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */ -button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */ -.ui-button-icons-only { width: 3.4em; } -button.ui-button-icons-only { width: 3.7em; } - -/*button text element */ -.ui-button .ui-button-text { display: block; line-height: 1.4; } -.ui-button-text-only .ui-button-text { padding: .4em 1em; } -.ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; } -.ui-button-text-icon-primary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 1em .4em 2.1em; } -.ui-button-text-icon-secondary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 2.1em .4em 1em; } -.ui-button-text-icons .ui-button-text { padding-left: 2.1em; padding-right: 2.1em; } -/* no icon support for input elements, provide padding by default */ -input.ui-button { padding: .4em 1em; } - -/*button icon element(s) */ -.ui-button-icon-only .ui-icon, .ui-button-text-icon-primary .ui-icon, .ui-button-text-icon-secondary .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; } -.ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; } -.ui-button-text-icon-primary .ui-button-icon-primary, .ui-button-text-icons .ui-button-icon-primary, .ui-button-icons-only .ui-button-icon-primary { left: .5em; } -.ui-button-text-icon-secondary .ui-button-icon-secondary, .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; } -.ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; } - -/*button sets*/ -.ui-buttonset { margin-right: 7px; } -.ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; } - -/* workarounds */ -button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */ -/*! - * jQuery UI Dialog 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Dialog#theming - */ -.ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; } -.ui-dialog .ui-dialog-titlebar { padding: .4em 1em; position: relative; } -.ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .1em 0; } -.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; } -.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; } -.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; } -.ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; } -.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; } -.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { float: right; } -.ui-dialog .ui-dialog-buttonpane button { margin: .5em .4em .5em 0; cursor: pointer; } -.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; } -.ui-draggable .ui-dialog-titlebar { cursor: move; } -/*! - * jQuery UI Slider 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Slider#theming - */ -.ui-slider { position: relative; text-align: left; } -.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; } -.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; } - -.ui-slider-horizontal { height: .8em; } -.ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; } -.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; } -.ui-slider-horizontal .ui-slider-range-min { left: 0; } -.ui-slider-horizontal .ui-slider-range-max { right: 0; } - -.ui-slider-vertical { width: .8em; height: 100px; } -.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; } -.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; } -.ui-slider-vertical .ui-slider-range-min { bottom: 0; } -.ui-slider-vertical .ui-slider-range-max { top: 0; }/*! - * jQuery UI Tabs 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Tabs#theming - */ -.ui-tabs { position: relative; padding: .2em; zoom: 1; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */ -.ui-tabs .ui-tabs-nav { margin: 0; padding: .2em .2em 0; } -.ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap; } -.ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; } -.ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 1px; } -.ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; } -.ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */ -.ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.4em; background: none; } -.ui-tabs .ui-tabs-hide { display: none !important; } -/*! - * jQuery UI Datepicker 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Datepicker#theming - */ -.ui-datepicker { width: 17em; padding: .2em .2em 0; display: none; } -.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; } -.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; } -.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; } -.ui-datepicker .ui-datepicker-prev { left:2px; } -.ui-datepicker .ui-datepicker-next { right:2px; } -.ui-datepicker .ui-datepicker-prev-hover { left:1px; } -.ui-datepicker .ui-datepicker-next-hover { right:1px; } -.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; } -.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; } -.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; } -.ui-datepicker select.ui-datepicker-month-year {width: 100%;} -.ui-datepicker select.ui-datepicker-month, -.ui-datepicker select.ui-datepicker-year { width: 49%;} -.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; } -.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; } -.ui-datepicker td { border: 0; padding: 1px; } -.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; } -.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; } -.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; } -.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; } - -/* with multiple calendars */ -.ui-datepicker.ui-datepicker-multi { width:auto; } -.ui-datepicker-multi .ui-datepicker-group { float:left; } -.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; } -.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; } -.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; } -.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; } -.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; } -.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; } -.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; } -.ui-datepicker-row-break { clear:both; width:100%; font-size:0em; } - -/* RTL support */ -.ui-datepicker-rtl { direction: rtl; } -.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; } -.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; } -.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; } -.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; } -.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; } -.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; } -.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; } -.ui-datepicker-rtl .ui-datepicker-group { float:right; } -.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; } -.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; } - -/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */ -.ui-datepicker-cover { - position: absolute; /*must have*/ - z-index: -1; /*must have*/ - filter: mask(); /*must have*/ - top: -4px; /*must have*/ - left: -4px; /*must have*/ - width: 200px; /*must have*/ - height: 200px; /*must have*/ -}/*! - * jQuery UI Progressbar 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Progressbar#theming - */ -.ui-progressbar { height:2em; text-align: left; overflow: hidden; } -.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; } \ No newline at end of file diff --git a/public/css/230-jquery.contextMenu.css b/public/css/230-jquery.contextMenu.css deleted file mode 100644 index d9de1e82a..000000000 --- a/public/css/230-jquery.contextMenu.css +++ /dev/null @@ -1,142 +0,0 @@ -/*! - * jQuery contextMenu - Plugin for simple contextMenu handling - * - * Version: 1.5.24 - * - * Authors: Rodney Rehm, Addy Osmani (patches for FF) - * Web: http://medialize.github.com/jQuery-contextMenu/ - * - * Licensed under - * MIT License http://www.opensource.org/licenses/mit-license - * GPL v3 http://opensource.org/licenses/GPL-3.0 - * - */ - -.context-menu-list { - margin:0; - padding:0; - - min-width: 120px; - max-width: 250px; - display: inline-block; - position: absolute; - list-style-type: none; - - border: 1px solid #DDD; - background: #EEE; - - -webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5); - -moz-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5); - -ms-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5); - -o-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5); - box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5); - - font-family: Verdana, Arial, Helvetica, sans-serif; - font-size: 11px; -} - -.context-menu-item { - padding: 2px 2px 2px 24px; - background-color: #EEE; - position: relative; - -webkit-user-select: none; - -moz-user-select: -moz-none; - -ms-user-select: none; - user-select: none; -} - -.context-menu-separator { - padding-bottom:0; - border-bottom: 1px solid #DDD; -} - -.context-menu-item > label > input, -.context-menu-item > label > textarea { - -webkit-user-select: text; - -moz-user-select: text; - -ms-user-select: text; - user-select: text; -} - -.context-menu-item.hover { - cursor: pointer; - background-color: #39F; -} - -.context-menu-item.disabled { - color: #666; -} - -.context-menu-input.hover, -.context-menu-item.disabled.hover { - cursor: default; - background-color: #EEE; -} - -.context-menu-submenu:after { - content: ">"; - color: #666; - position: absolute; - top: 0; - right: 3px; - z-index: 1; -} - -/* icons - #protip: - In case you want to use sprites for icons (which I would suggest you do) have a look at - http://css-tricks.com/13224-pseudo-spriting/ to get an idea of how to implement - .context-menu-item.icon:before {} - */ -.context-menu-item.icon { min-height: 18px; background-repeat: no-repeat; background-position: 4px 2px; } -.context-menu-item.icon-edit { background-image: url(images/page_white_edit.png); } -.context-menu-item.icon-cut { background-image: url(images/cut.png); } -.context-menu-item.icon-copy { background-image: url(images/page_white_copy.png); } -.context-menu-item.icon-paste { background-image: url(images/page_white_paste.png); } -.context-menu-item.icon-delete { background-image: url(images/page_white_delete.png); } -.context-menu-item.icon-add { background-image: url(images/page_white_add.png); } -.context-menu-item.icon-quit { background-image: url(images/door.png); } - -/* vertically align inside labels */ -.context-menu-input > label > * { vertical-align: top; } - -/* position checkboxes and radios as icons */ -.context-menu-input > label > input[type="checkbox"], -.context-menu-input > label > input[type="radio"] { - margin-left: -17px; -} -.context-menu-input > label > span { - margin-left: 5px; -} - -.context-menu-input > label, -.context-menu-input > label > input[type="text"], -.context-menu-input > label > textarea, -.context-menu-input > label > select { - display: block; - width: 100%; - - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - -ms-box-sizing: border-box; - -o-box-sizing: border-box; - box-sizing: border-box; -} - -.context-menu-input > label > textarea { - height: 100px; -} -.context-menu-item > .context-menu-list { - display: none; - /* re-positioned by js */ - right: -5px; - top: 5px; -} - -.context-menu-item.hover > .context-menu-list { - display: block; -} - -.context-menu-accesskey { - text-decoration: underline; -} diff --git a/public/css/250-jquery-colorbox.css b/public/css/250-jquery-colorbox.css deleted file mode 100644 index 397fe3dca..000000000 --- a/public/css/250-jquery-colorbox.css +++ /dev/null @@ -1,86 +0,0 @@ -/* - ColorBox Core Style: - The following CSS is consistent between example themes and should not be altered. -*/ -#colorbox, #cboxOverlay, #cboxWrapper{position:absolute; top:0; left:0; z-index:9999; overflow:hidden;} -#cboxOverlay{position:fixed; width:100%; height:100%;} -#cboxMiddleLeft, #cboxBottomLeft{clear:left;} -#cboxContent{position:relative;} -#cboxLoadedContent{overflow:auto;} -#cboxTitle{margin:0;} -#cboxLoadingOverlay, #cboxLoadingGraphic{position:absolute; top:0; left:0; width:100%; height:100%;} -#cboxPrevious, #cboxNext, #cboxClose, #cboxSlideshow{cursor:pointer;} -.cboxPhoto{float:left; margin:auto; border:0; display:block; max-width:none;} -.cboxIframe{width:100%; height:100%; display:block; border:0;} -#colorbox, #cboxContent, #cboxLoadedContent{box-sizing:content-box;} - -/* - User Style: - Change the following styles to modify the appearance of ColorBox. They are - ordered & tabbed in a way that represents the nesting of the generated HTML. -*/ -#cboxOverlay{background:url(images/overlay.png) repeat 0 0;} -#colorbox{} - #cboxTopLeft{width:21px; height:21px; background:url(images/controls.png) no-repeat -101px 0;} - #cboxTopRight{width:21px; height:21px; background:url(images/controls.png) no-repeat -130px 0;} - #cboxBottomLeft{width:21px; height:21px; background:url(images/controls.png) no-repeat -101px -29px;} - #cboxBottomRight{width:21px; height:21px; background:url(images/controls.png) no-repeat -130px -29px;} - #cboxMiddleLeft{width:21px; background:url(images/controls.png) left top repeat-y;} - #cboxMiddleRight{width:21px; background:url(images/controls.png) right top repeat-y;} - #cboxTopCenter{height:21px; background:url(images/border.png) 0 0 repeat-x;} - #cboxBottomCenter{height:21px; background:url(images/border.png) 0 -29px repeat-x;} - #cboxContent{background:#fff; overflow:hidden;} - .cboxIframe{background:#fff;} - #cboxError{padding:50px; border:1px solid #ccc;} - #cboxLoadedContent{margin-bottom:28px;} - #cboxTitle{position:absolute; bottom:4px; left:0; text-align:center; width:100%; color:#949494;} - #cboxCurrent{position:absolute; bottom:4px; left:58px; color:#949494;} - #cboxSlideshow{position:absolute; bottom:4px; right:30px; color:#0092ef;} - #cboxPrevious{position:absolute; bottom:0; left:0; background:url(images/controls.png) no-repeat -75px 0; width:25px; height:25px; text-indent:-9999px;} - #cboxPrevious:hover{background-position:-75px -25px;} - #cboxNext{position:absolute; bottom:0; left:27px; background:url(images/controls.png) no-repeat -50px 0; width:25px; height:25px; text-indent:-9999px;} - #cboxNext:hover{background-position:-50px -25px;} - #cboxLoadingOverlay{background:url(images/loading_background.png) no-repeat center center;} - #cboxLoadingGraphic{background:url(images/loading.gif) no-repeat center center;} - #cboxClose{position:absolute; bottom:0; right:0; background:url(images/controls.png) no-repeat -25px 0; width:25px; height:25px; text-indent:-9999px;} - #cboxClose:hover{background-position:-25px -25px;} - -/* - The following fixes a problem where IE7 and IE8 replace a PNG's alpha transparency with a black fill - when an alpha filter (opacity change) is set on the element or ancestor element. This style is not applied to or needed in IE9. - See: http://jacklmoore.com/notes/ie-transparency-problems/ -*/ -.cboxIE #cboxTopLeft, -.cboxIE #cboxTopCenter, -.cboxIE #cboxTopRight, -.cboxIE #cboxBottomLeft, -.cboxIE #cboxBottomCenter, -.cboxIE #cboxBottomRight, -.cboxIE #cboxMiddleLeft, -.cboxIE #cboxMiddleRight { - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#00FFFFFF,endColorstr=#00FFFFFF); -} - -/* - The following provides PNG transparency support for IE6 - Feel free to remove this and the /ie6/ directory if you have dropped IE6 support. -*/ -.cboxIE6 #cboxTopLeft{background:url(images/ie6/borderTopLeft.png);} -.cboxIE6 #cboxTopCenter{background:url(images/ie6/borderTopCenter.png);} -.cboxIE6 #cboxTopRight{background:url(images/ie6/borderTopRight.png);} -.cboxIE6 #cboxBottomLeft{background:url(images/ie6/borderBottomLeft.png);} -.cboxIE6 #cboxBottomCenter{background:url(images/ie6/borderBottomCenter.png);} -.cboxIE6 #cboxBottomRight{background:url(images/ie6/borderBottomRight.png);} -.cboxIE6 #cboxMiddleLeft{background:url(images/ie6/borderMiddleLeft.png);} -.cboxIE6 #cboxMiddleRight{background:url(images/ie6/borderMiddleRight.png);} - -.cboxIE6 #cboxTopLeft, -.cboxIE6 #cboxTopCenter, -.cboxIE6 #cboxTopRight, -.cboxIE6 #cboxBottomLeft, -.cboxIE6 #cboxBottomCenter, -.cboxIE6 #cboxBottomRight, -.cboxIE6 #cboxMiddleLeft, -.cboxIE6 #cboxMiddleRight { - _behavior: expression(this.src = this.src ? this.src : this.currentStyle.backgroundImage.split('"')[1], this.style.background = "none", this.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src=" + this.src + ", sizingMethod='scale')"); -} diff --git a/public/css/300-chosen.css b/public/css/300-chosen.css deleted file mode 100644 index a4322c4aa..000000000 --- a/public/css/300-chosen.css +++ /dev/null @@ -1,396 +0,0 @@ -/* @group Base */ -.chzn-container { - font-size: 13px; - position: relative; - display: inline-block; - zoom: 1; - *display: inline; -} -.chzn-container .chzn-drop { - background: #fff; - border: 1px solid #aaa; - border-top: 0; - position: absolute; - top: 29px; - left: 0; - -webkit-box-shadow: 0 4px 5px rgba(0,0,0,.15); - -moz-box-shadow : 0 4px 5px rgba(0,0,0,.15); - -o-box-shadow : 0 4px 5px rgba(0,0,0,.15); - box-shadow : 0 4px 5px rgba(0,0,0,.15); - z-index: 1010; -} -/* @end */ - -/* @group Single Chosen */ -.chzn-container-single .chzn-single { - background-color: #ffffff; - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#eeeeee', GradientType=0 ); - background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(20%, #ffffff), color-stop(50%, #f6f6f6), color-stop(52%, #eeeeee), color-stop(100%, #f4f4f4)); - background-image: -webkit-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%); - background-image: -moz-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%); - background-image: -o-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%); - background-image: -ms-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%); - background-image: linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%); - -webkit-border-radius: 5px; - -moz-border-radius : 5px; - border-radius : 5px; - -moz-background-clip : padding; - -webkit-background-clip: padding-box; - background-clip : padding-box; - border: 1px solid #aaaaaa; - -webkit-box-shadow: 0 0 3px #ffffff inset, 0 1px 1px rgba(0,0,0,0.1); - -moz-box-shadow : 0 0 3px #ffffff inset, 0 1px 1px rgba(0,0,0,0.1); - box-shadow : 0 0 3px #ffffff inset, 0 1px 1px rgba(0,0,0,0.1); - display: block; - overflow: hidden; - white-space: nowrap; - position: relative; - height: 23px; - line-height: 24px; - padding: 0 0 0 8px; - color: #444444; - text-decoration: none; -} -.chzn-container-single .chzn-default { - color: #999; -} -.chzn-container-single .chzn-single span { - margin-right: 26px; - display: block; - overflow: hidden; - white-space: nowrap; - -o-text-overflow: ellipsis; - -ms-text-overflow: ellipsis; - text-overflow: ellipsis; -} -.chzn-container-single .chzn-single abbr { - display: block; - position: absolute; - right: 26px; - top: 6px; - width: 12px; - height: 13px; - font-size: 1px; - background: url('chosen-sprite.png') right top no-repeat; -} -.chzn-container-single .chzn-single abbr:hover { - background-position: right -11px; -} -.chzn-container-single.chzn-disabled .chzn-single abbr:hover { - background-position: right top; -} -.chzn-container-single .chzn-single div { - position: absolute; - right: 0; - top: 0; - display: block; - height: 100%; - width: 18px; -} -.chzn-container-single .chzn-single div b { - background: url('chosen-sprite.png') no-repeat 0 0; - display: block; - width: 100%; - height: 100%; -} -.chzn-container-single .chzn-search { - padding: 3px 4px; - position: relative; - margin: 0; - white-space: nowrap; - z-index: 1010; -} -.chzn-container-single .chzn-search input { - background: #fff url('chosen-sprite.png') no-repeat 100% -22px; - background: url('chosen-sprite.png') no-repeat 100% -22px, -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff)); - background: url('chosen-sprite.png') no-repeat 100% -22px, -webkit-linear-gradient(top, #eeeeee 1%, #ffffff 15%); - background: url('chosen-sprite.png') no-repeat 100% -22px, -moz-linear-gradient(top, #eeeeee 1%, #ffffff 15%); - background: url('chosen-sprite.png') no-repeat 100% -22px, -o-linear-gradient(top, #eeeeee 1%, #ffffff 15%); - background: url('chosen-sprite.png') no-repeat 100% -22px, -ms-linear-gradient(top, #eeeeee 1%, #ffffff 15%); - background: url('chosen-sprite.png') no-repeat 100% -22px, linear-gradient(top, #eeeeee 1%, #ffffff 15%); - margin: 1px 0; - padding: 4px 20px 4px 5px; - outline: 0; - border: 1px solid #aaa; - font-family: sans-serif; - font-size: 1em; -} -.chzn-container-single .chzn-drop { - -webkit-border-radius: 0 0 4px 4px; - -moz-border-radius : 0 0 4px 4px; - border-radius : 0 0 4px 4px; - -moz-background-clip : padding; - -webkit-background-clip: padding-box; - background-clip : padding-box; -} -/* @end */ - -.chzn-container-single-nosearch .chzn-search input { - position: absolute; - left: -9000px; -} - -/* @group Multi Chosen */ -.chzn-container-multi .chzn-choices { - background-color: #fff; - background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff)); - background-image: -webkit-linear-gradient(top, #eeeeee 1%, #ffffff 15%); - background-image: -moz-linear-gradient(top, #eeeeee 1%, #ffffff 15%); - background-image: -o-linear-gradient(top, #eeeeee 1%, #ffffff 15%); - background-image: -ms-linear-gradient(top, #eeeeee 1%, #ffffff 15%); - background-image: linear-gradient(top, #eeeeee 1%, #ffffff 15%); - border: 1px solid #aaa; - margin: 0; - padding: 0; - cursor: text; - overflow: hidden; - height: auto !important; - height: 1%; - position: relative; -} -.chzn-container-multi .chzn-choices li { - float: left; - list-style: none; -} -.chzn-container-multi .chzn-choices .search-field { - white-space: nowrap; - margin: 0; - padding: 0; -} -.chzn-container-multi .chzn-choices .search-field input { - color: #666; - background: transparent !important; - border: 0 !important; - font-family: sans-serif; - font-size: 100%; - height: 15px; - padding: 5px; - margin: 1px 0; - outline: 0; - -webkit-box-shadow: none; - -moz-box-shadow : none; - -o-box-shadow : none; - box-shadow : none; -} -.chzn-container-multi .chzn-choices .search-field .default { - color: #999; -} -.chzn-container-multi .chzn-choices .search-choice { - -webkit-border-radius: 3px; - -moz-border-radius : 3px; - border-radius : 3px; - -moz-background-clip : padding; - -webkit-background-clip: padding-box; - background-clip : padding-box; - background-color: #e4e4e4; - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f4f4f4', endColorstr='#eeeeee', GradientType=0 ); - background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eeeeee)); - background-image: -webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); - background-image: -moz-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); - background-image: -o-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); - background-image: -ms-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); - background-image: linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); - -webkit-box-shadow: 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05); - -moz-box-shadow : 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05); - box-shadow : 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05); - color: #333; - border: 1px solid #aaaaaa; - line-height: 13px; - padding: 3px 20px 3px 5px; - margin: 3px 0 3px 5px; - position: relative; - cursor: default; -} -.chzn-container-multi .chzn-choices .search-choice-focus { - background: #d4d4d4; -} -.chzn-container-multi .chzn-choices .search-choice .search-choice-close { - display: block; - position: absolute; - right: 3px; - top: 4px; - width: 12px; - height: 13px; - font-size: 1px; - background: url('chosen-sprite.png') right top no-repeat; -} -.chzn-container-multi .chzn-choices .search-choice .search-choice-close:hover { - background-position: right -11px; -} -.chzn-container-multi .chzn-choices .search-choice-focus .search-choice-close { - background-position: right -11px; -} -/* @end */ - -/* @group Results */ -.chzn-container .chzn-results { - margin: 0 4px 4px 0; - max-height: 240px; - padding: 0 0 0 4px; - position: relative; - overflow-x: hidden; - overflow-y: auto; - -webkit-overflow-scrolling: touch; -} -.chzn-container-multi .chzn-results { - margin: -1px 0 0; - padding: 0; -} -.chzn-container .chzn-results li { - display: none; - line-height: 15px; - padding: 5px 6px; - margin: 0; - list-style: none; -} -.chzn-container .chzn-results .active-result { - cursor: pointer; - display: list-item; -} -.chzn-container .chzn-results .highlighted { - background-color: #3875d7; - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#3875d7', endColorstr='#2a62bc', GradientType=0 ); - background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(20%, #3875d7), color-stop(90%, #2a62bc)); - background-image: -webkit-linear-gradient(top, #3875d7 20%, #2a62bc 90%); - background-image: -moz-linear-gradient(top, #3875d7 20%, #2a62bc 90%); - background-image: -o-linear-gradient(top, #3875d7 20%, #2a62bc 90%); - background-image: -ms-linear-gradient(top, #3875d7 20%, #2a62bc 90%); - background-image: linear-gradient(top, #3875d7 20%, #2a62bc 90%); - color: #fff; -} -.chzn-container .chzn-results li em { - background: #feffde; - font-style: normal; -} -.chzn-container .chzn-results .highlighted em { - background: transparent; -} -.chzn-container .chzn-results .no-results { - background: #f4f4f4; - display: list-item; -} -.chzn-container .chzn-results .group-result { - cursor: default; - color: #999; - font-weight: bold; -} -.chzn-container .chzn-results .group-option { - padding-left: 15px; -} -.chzn-container-multi .chzn-drop .result-selected { - display: none; -} -.chzn-container .chzn-results-scroll { - background: white; - margin: 0 4px; - position: absolute; - text-align: center; - width: 321px; /* This should by dynamic with js */ - z-index: 1; -} -.chzn-container .chzn-results-scroll span { - display: inline-block; - height: 17px; - text-indent: -5000px; - width: 9px; -} -.chzn-container .chzn-results-scroll-down { - bottom: 0; -} -.chzn-container .chzn-results-scroll-down span { - background: url('chosen-sprite.png') no-repeat -4px -3px; -} -.chzn-container .chzn-results-scroll-up span { - background: url('chosen-sprite.png') no-repeat -22px -3px; -} -/* @end */ - -/* @group Active */ -.chzn-container-active .chzn-single { - -webkit-box-shadow: 0 0 5px rgba(0,0,0,.3); - -moz-box-shadow : 0 0 5px rgba(0,0,0,.3); - -o-box-shadow : 0 0 5px rgba(0,0,0,.3); - box-shadow : 0 0 5px rgba(0,0,0,.3); - border: 1px solid #5897fb; -} -.chzn-container-active .chzn-single-with-drop { - border: 1px solid #aaa; - -webkit-box-shadow: 0 1px 0 #fff inset; - -moz-box-shadow : 0 1px 0 #fff inset; - -o-box-shadow : 0 1px 0 #fff inset; - box-shadow : 0 1px 0 #fff inset; - background-color: #eee; - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#eeeeee', endColorstr='#ffffff', GradientType=0 ); - background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(20%, #eeeeee), color-stop(80%, #ffffff)); - background-image: -webkit-linear-gradient(top, #eeeeee 20%, #ffffff 80%); - background-image: -moz-linear-gradient(top, #eeeeee 20%, #ffffff 80%); - background-image: -o-linear-gradient(top, #eeeeee 20%, #ffffff 80%); - background-image: -ms-linear-gradient(top, #eeeeee 20%, #ffffff 80%); - background-image: linear-gradient(top, #eeeeee 20%, #ffffff 80%); - -webkit-border-bottom-left-radius : 0; - -webkit-border-bottom-right-radius: 0; - -moz-border-radius-bottomleft : 0; - -moz-border-radius-bottomright: 0; - border-bottom-left-radius : 0; - border-bottom-right-radius: 0; -} -.chzn-container-active .chzn-single-with-drop div { - background: transparent; - border-left: none; -} -.chzn-container-active .chzn-single-with-drop div b { - background-position: -18px 1px; -} -.chzn-container-active .chzn-choices { - -webkit-box-shadow: 0 0 5px rgba(0,0,0,.3); - -moz-box-shadow : 0 0 5px rgba(0,0,0,.3); - -o-box-shadow : 0 0 5px rgba(0,0,0,.3); - box-shadow : 0 0 5px rgba(0,0,0,.3); - border: 1px solid #5897fb; -} -.chzn-container-active .chzn-choices .search-field input { - color: #111 !important; -} -/* @end */ - -/* @group Disabled Support */ -.chzn-disabled { - cursor: default; - opacity:0.5 !important; -} -.chzn-disabled .chzn-single { - cursor: default; -} -.chzn-disabled .chzn-choices .search-choice .search-choice-close { - cursor: default; -} - -/* @group Right to Left */ -.chzn-rtl { text-align: right; } -.chzn-rtl .chzn-single { padding: 0 8px 0 0; overflow: visible; } -.chzn-rtl .chzn-single span { margin-left: 26px; margin-right: 0; direction: rtl; } - -.chzn-rtl .chzn-single div { left: 3px; right: auto; } -.chzn-rtl .chzn-single abbr { - left: 26px; - right: auto; -} -.chzn-rtl .chzn-choices .search-field input { direction: rtl; } -.chzn-rtl .chzn-choices li { float: right; } -.chzn-rtl .chzn-choices .search-choice { padding: 3px 5px 3px 19px; margin: 3px 5px 3px 0; } -.chzn-rtl .chzn-choices .search-choice .search-choice-close { left: 4px; right: auto; background-position: right top;} -.chzn-rtl.chzn-container-single .chzn-results { margin: 0 0 4px 4px; padding: 0 4px 0 0; } -.chzn-rtl .chzn-results .group-option { padding-left: 0; padding-right: 15px; } -.chzn-rtl.chzn-container-active .chzn-single-with-drop div { border-right: none; } -.chzn-rtl .chzn-search input { - background: #fff url('chosen-sprite.png') no-repeat -38px -22px; - background: url('chosen-sprite.png') no-repeat -38px -22px, -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff)); - background: url('chosen-sprite.png') no-repeat -38px -22px, -webkit-linear-gradient(top, #eeeeee 1%, #ffffff 15%); - background: url('chosen-sprite.png') no-repeat -38px -22px, -moz-linear-gradient(top, #eeeeee 1%, #ffffff 15%); - background: url('chosen-sprite.png') no-repeat -38px -22px, -o-linear-gradient(top, #eeeeee 1%, #ffffff 15%); - background: url('chosen-sprite.png') no-repeat -38px -22px, -ms-linear-gradient(top, #eeeeee 1%, #ffffff 15%); - background: url('chosen-sprite.png') no-repeat -38px -22px, linear-gradient(top, #eeeeee 1%, #ffffff 15%); - padding: 4px 5px 4px 20px; - direction: rtl; -} -/* @end */ diff --git a/public/css/800-bootstrap.css b/public/css/800-bootstrap.css deleted file mode 100644 index 8ab3cefcf..000000000 --- a/public/css/800-bootstrap.css +++ /dev/null @@ -1,6039 +0,0 @@ -/*! - * Bootstrap v2.2.2 - * - * Copyright 2012 Twitter, Inc - * Licensed under the Apache License v2.0 - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Designed and built with all the love in the world @twitter by @mdo and @fat. - */ - -article, -aside, -details, -figcaption, -figure, -footer, -header, -hgroup, -nav, -section { - display: block; -} - -audio, -canvas, -video { - display: inline-block; - *display: inline; - *zoom: 1; -} - -audio:not([controls]) { - display: none; -} - -html { - font-size: 100%; - -webkit-text-size-adjust: 100%; - -ms-text-size-adjust: 100%; -} - -a:focus { - outline: thin dotted #333; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} - -a:hover, -a:active { - outline: 0; -} - -sub, -sup { - position: relative; - font-size: 75%; - line-height: 0; - vertical-align: baseline; -} - -sup { - top: -0.5em; -} - -sub { - bottom: -0.25em; -} - -img { - width: auto\9; - height: auto; - max-width: 100%; - vertical-align: middle; - border: 0; - -ms-interpolation-mode: bicubic; -} - -#map_canvas img, -.google-maps img { - max-width: none; -} - -button, -input, -select, -textarea { - margin: 0; - font-size: 100%; - vertical-align: middle; -} - -button, -input { - *overflow: visible; - line-height: normal; -} - -button::-moz-focus-inner, -input::-moz-focus-inner { - padding: 0; - border: 0; -} - -button, -html input[type="button"], -input[type="reset"], -input[type="submit"] { - cursor: pointer; - -webkit-appearance: button; -} - -label, -select, -button, -input[type="button"], -input[type="reset"], -input[type="submit"], -input[type="radio"], -input[type="checkbox"] { - cursor: pointer; -} - -input[type="search"] { - -webkit-box-sizing: content-box; - -moz-box-sizing: content-box; - box-sizing: content-box; - -webkit-appearance: textfield; -} - -input[type="search"]::-webkit-search-decoration, -input[type="search"]::-webkit-search-cancel-button { - -webkit-appearance: none; -} - -textarea { - overflow: auto; - vertical-align: top; -} - -@media print { - * { - color: #000 !important; - text-shadow: none !important; - background: transparent !important; - box-shadow: none !important; - } - a, - a:visited { - text-decoration: underline; - } - a[href]:after { - content: " (" attr(href) ")"; - } - abbr[title]:after { - content: " (" attr(title) ")"; - } - .ir a:after, - a[href^="javascript:"]:after, - a[href^="#"]:after { - content: ""; - } - pre, - blockquote { - border: 1px solid #999; - page-break-inside: avoid; - } - thead { - display: table-header-group; - } - tr, - img { - page-break-inside: avoid; - } - img { - max-width: 100% !important; - } - @page { - margin: 0.5cm; - } - p, - h2, - h3 { - orphans: 3; - widows: 3; - } - h2, - h3 { - page-break-after: avoid; - } -} - -.clearfix { - *zoom: 1; -} - -.clearfix:before, -.clearfix:after { - display: table; - line-height: 0; - content: ""; -} - -.clearfix:after { - clear: both; -} - -.hide-text { - font: 0/0 a; - color: transparent; - text-shadow: none; - background-color: transparent; - border: 0; -} - -.input-block-level { - display: block; - width: 100%; - min-height: 30px; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} - -body { - margin: 0; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 14px; - line-height: 20px; - color: #333333; - background-color: #ffffff; -} - -a { - color: #0088cc; - text-decoration: none; -} - -a:hover { - color: #005580; - text-decoration: underline; -} - -.img-rounded { - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; -} - -.img-polaroid { - padding: 4px; - background-color: #fff; - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, 0.2); - -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); - -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); -} - -.img-circle { - -webkit-border-radius: 500px; - -moz-border-radius: 500px; - border-radius: 500px; -} - -.row { - margin-left: -20px; - *zoom: 1; -} - -.row:before, -.row:after { - display: table; - line-height: 0; - content: ""; -} - -.row:after { - clear: both; -} - -[class*="span"] { - float: left; - min-height: 1px; - margin-left: 20px; -} - -.container, -.navbar-static-top .container, -.navbar-fixed-top .container, -.navbar-fixed-bottom .container { - width: 940px; -} - -.span12 { - width: 940px; -} - -.span11 { - width: 860px; -} - -.span10 { - width: 780px; -} - -.span9 { - width: 700px; -} - -.span8 { - width: 620px; -} - -.span7 { - width: 540px; -} - -.span6 { - width: 460px; -} - -.span5 { - width: 380px; -} - -.span4 { - width: 300px; -} - -.span3 { - width: 220px; -} - -.span2 { - width: 140px; -} - -.span1 { - width: 60px; -} - -.offset12 { - margin-left: 980px; -} - -.offset11 { - margin-left: 900px; -} - -.offset10 { - margin-left: 820px; -} - -.offset9 { - margin-left: 740px; -} - -.offset8 { - margin-left: 660px; -} - -.offset7 { - margin-left: 580px; -} - -.offset6 { - margin-left: 500px; -} - -.offset5 { - margin-left: 420px; -} - -.offset4 { - margin-left: 340px; -} - -.offset3 { - margin-left: 260px; -} - -.offset2 { - margin-left: 180px; -} - -.offset1 { - margin-left: 100px; -} - -.row-fluid { - width: 100%; - *zoom: 1; -} - -.row-fluid:before, -.row-fluid:after { - display: table; - line-height: 0; - content: ""; -} - -.row-fluid:after { - clear: both; -} - -.row-fluid [class*="span"] { - display: block; - float: left; - width: 100%; - min-height: 30px; - margin-left: 2.127659574468085%; - *margin-left: 2.074468085106383%; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} - -.row-fluid [class*="span"]:first-child { - margin-left: 0; -} - -.row-fluid .controls-row [class*="span"] + [class*="span"] { - margin-left: 2.127659574468085%; -} - -.row-fluid .span12 { - width: 100%; - *width: 99.94680851063829%; -} - -.row-fluid .span11 { - width: 91.48936170212765%; - *width: 91.43617021276594%; -} - -.row-fluid .span10 { - width: 82.97872340425532%; - *width: 82.92553191489361%; -} - -.row-fluid .span9 { - width: 74.46808510638297%; - *width: 74.41489361702126%; -} - -.row-fluid .span8 { - width: 65.95744680851064%; - *width: 65.90425531914893%; -} - -.row-fluid .span7 { - width: 57.44680851063829%; - *width: 57.39361702127659%; -} - -.row-fluid .span6 { - width: 48.93617021276595%; - *width: 48.88297872340425%; -} - -.row-fluid .span5 { - width: 40.42553191489362%; - *width: 40.37234042553192%; -} - -.row-fluid .span4 { - width: 31.914893617021278%; - *width: 31.861702127659576%; -} - -.row-fluid .span3 { - width: 23.404255319148934%; - *width: 23.351063829787233%; -} - -.row-fluid .span2 { - width: 14.893617021276595%; - *width: 14.840425531914894%; -} - -.row-fluid .span1 { - width: 6.382978723404255%; - *width: 6.329787234042553%; -} - -.row-fluid .offset12 { - margin-left: 104.25531914893617%; - *margin-left: 104.14893617021275%; -} - -.row-fluid .offset12:first-child { - margin-left: 102.12765957446808%; - *margin-left: 102.02127659574467%; -} - -.row-fluid .offset11 { - margin-left: 95.74468085106382%; - *margin-left: 95.6382978723404%; -} - -.row-fluid .offset11:first-child { - margin-left: 93.61702127659574%; - *margin-left: 93.51063829787232%; -} - -.row-fluid .offset10 { - margin-left: 87.23404255319149%; - *margin-left: 87.12765957446807%; -} - -.row-fluid .offset10:first-child { - margin-left: 85.1063829787234%; - *margin-left: 84.99999999999999%; -} - -.row-fluid .offset9 { - margin-left: 78.72340425531914%; - *margin-left: 78.61702127659572%; -} - -.row-fluid .offset9:first-child { - margin-left: 76.59574468085106%; - *margin-left: 76.48936170212764%; -} - -.row-fluid .offset8 { - margin-left: 70.2127659574468%; - *margin-left: 70.10638297872339%; -} - -.row-fluid .offset8:first-child { - margin-left: 68.08510638297872%; - *margin-left: 67.9787234042553%; -} - -.row-fluid .offset7 { - margin-left: 61.70212765957446%; - *margin-left: 61.59574468085106%; -} - -.row-fluid .offset7:first-child { - margin-left: 59.574468085106375%; - *margin-left: 59.46808510638297%; -} - -.row-fluid .offset6 { - margin-left: 53.191489361702125%; - *margin-left: 53.085106382978715%; -} - -.row-fluid .offset6:first-child { - margin-left: 51.063829787234035%; - *margin-left: 50.95744680851063%; -} - -.row-fluid .offset5 { - margin-left: 44.68085106382979%; - *margin-left: 44.57446808510638%; -} - -.row-fluid .offset5:first-child { - margin-left: 42.5531914893617%; - *margin-left: 42.4468085106383%; -} - -.row-fluid .offset4 { - margin-left: 36.170212765957444%; - *margin-left: 36.06382978723405%; -} - -.row-fluid .offset4:first-child { - margin-left: 34.04255319148936%; - *margin-left: 33.93617021276596%; -} - -.row-fluid .offset3 { - margin-left: 27.659574468085104%; - *margin-left: 27.5531914893617%; -} - -.row-fluid .offset3:first-child { - margin-left: 25.53191489361702%; - *margin-left: 25.425531914893618%; -} - -.row-fluid .offset2 { - margin-left: 19.148936170212764%; - *margin-left: 19.04255319148936%; -} - -.row-fluid .offset2:first-child { - margin-left: 17.02127659574468%; - *margin-left: 16.914893617021278%; -} - -.row-fluid .offset1 { - margin-left: 10.638297872340425%; - *margin-left: 10.53191489361702%; -} - -.row-fluid .offset1:first-child { - margin-left: 8.51063829787234%; - *margin-left: 8.404255319148938%; -} - -[class*="span"].hide, -.row-fluid [class*="span"].hide { - display: none; -} - -[class*="span"].pull-right, -.row-fluid [class*="span"].pull-right { - float: right; -} - -.container { - margin-right: auto; - margin-left: auto; - *zoom: 1; -} - -.container:before, -.container:after { - display: table; - line-height: 0; - content: ""; -} - -.container:after { - clear: both; -} - -.container-fluid { - padding-right: 20px; - padding-left: 20px; - *zoom: 1; -} - -.container-fluid:before, -.container-fluid:after { - display: table; - line-height: 0; - content: ""; -} - -.container-fluid:after { - clear: both; -} - -p { - margin: 0 0 10px; -} - -.lead { - margin-bottom: 20px; - font-size: 21px; - font-weight: 200; - line-height: 30px; -} - -small { - font-size: 85%; -} - -strong { - font-weight: bold; -} - -em { - font-style: italic; -} - -cite { - font-style: normal; -} - -.muted { - color: #999999; -} - -a.muted:hover { - color: #808080; -} - -.text-warning { - color: #c09853; -} - -a.text-warning:hover { - color: #a47e3c; -} - -.text-error { - color: #b94a48; -} - -a.text-error:hover { - color: #953b39; -} - -.text-info { - color: #3a87ad; -} - -a.text-info:hover { - color: #2d6987; -} - -.text-success { - color: #468847; -} - -a.text-success:hover { - color: #356635; -} - -h1, -h2, -h3, -h4, -h5, -h6 { - margin: 10px 0; - font-family: inherit; - font-weight: bold; - line-height: 20px; - color: inherit; - text-rendering: optimizelegibility; -} - -h1 small, -h2 small, -h3 small, -h4 small, -h5 small, -h6 small { - font-weight: normal; - line-height: 1; - color: #999999; -} - -h1, -h2, -h3 { - line-height: 40px; -} - -h1 { - font-size: 38.5px; -} - -h2 { - font-size: 31.5px; -} - -h3 { - font-size: 24.5px; -} - -h4 { - font-size: 17.5px; -} - -h5 { - font-size: 14px; -} - -h6 { - font-size: 11.9px; -} - -h1 small { - font-size: 24.5px; -} - -h2 small { - font-size: 17.5px; -} - -h3 small { - font-size: 14px; -} - -h4 small { - font-size: 14px; -} - -.page-header { - padding-bottom: 9px; - margin: 20px 0 30px; - border-bottom: 1px solid #eeeeee; -} - -ul, -ol { - padding: 0; - margin: 0 0 10px 25px; -} - -ul ul, -ul ol, -ol ol, -ol ul { - margin-bottom: 0; -} - -li { - line-height: 20px; -} - -ul.unstyled, -ol.unstyled { - margin-left: 0; - list-style: none; -} - -ul.inline, -ol.inline { - margin-left: 0; - list-style: none; -} - -ul.inline > li, -ol.inline > li { - display: inline-block; - padding-right: 5px; - padding-left: 5px; -} - -dl { - margin-bottom: 20px; -} - -dt, -dd { - line-height: 20px; -} - -dt { - font-weight: bold; -} - -dd { - margin-left: 10px; -} - -.dl-horizontal { - *zoom: 1; -} - -.dl-horizontal:before, -.dl-horizontal:after { - display: table; - line-height: 0; - content: ""; -} - -.dl-horizontal:after { - clear: both; -} - -.dl-horizontal dt { - float: left; - width: 160px; - overflow: hidden; - clear: left; - text-align: right; - text-overflow: ellipsis; - white-space: nowrap; -} - -.dl-horizontal dd { - margin-left: 180px; -} - -hr { - margin: 20px 0; - border: 0; - border-top: 1px solid #eeeeee; - border-bottom: 1px solid #ffffff; -} - -abbr[title], -abbr[data-original-title] { - cursor: help; - border-bottom: 1px dotted #999999; -} - -abbr.initialism { - font-size: 90%; - text-transform: uppercase; -} - -blockquote { - padding: 0 0 0 15px; - margin: 0 0 20px; - border-left: 5px solid #eeeeee; -} - -blockquote p { - margin-bottom: 0; - font-size: 16px; - font-weight: 300; - line-height: 25px; -} - -blockquote small { - display: block; - line-height: 20px; - color: #999999; -} - -blockquote small:before { - content: '\2014 \00A0'; -} - -blockquote.pull-right { - float: right; - padding-right: 15px; - padding-left: 0; - border-right: 5px solid #eeeeee; - border-left: 0; -} - -blockquote.pull-right p, -blockquote.pull-right small { - text-align: right; -} - -blockquote.pull-right small:before { - content: ''; -} - -blockquote.pull-right small:after { - content: '\00A0 \2014'; -} - -q:before, -q:after, -blockquote:before, -blockquote:after { - content: ""; -} - -address { - display: block; - margin-bottom: 20px; - font-style: normal; - line-height: 20px; -} - -code, -pre { - padding: 0 3px 2px; - font-family: Monaco, Menlo, Consolas, "Courier New", monospace; - font-size: 12px; - color: #333333; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; -} - -code { - padding: 2px 4px; - color: #d14; - white-space: nowrap; - background-color: #f7f7f9; - border: 1px solid #e1e1e8; -} - -pre { - display: block; - padding: 9.5px; - margin: 0 0 10px; - font-size: 13px; - line-height: 20px; - word-break: break-all; - word-wrap: break-word; - white-space: pre; - white-space: pre-wrap; - background-color: #f5f5f5; - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, 0.15); - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; -} - -pre.prettyprint { - margin-bottom: 20px; -} - -pre code { - padding: 0; - color: inherit; - white-space: pre; - white-space: pre-wrap; - background-color: transparent; - border: 0; -} - -.pre-scrollable { - max-height: 340px; - overflow-y: scroll; -} - -form { - margin: 0 0 20px; -} - -fieldset { - padding: 0; - margin: 0; - border: 0; -} - -legend { - display: block; - width: 100%; - padding: 0; - margin-bottom: 20px; - font-size: 21px; - line-height: 40px; - color: #333333; - border: 0; - border-bottom: 1px solid #e5e5e5; -} - -legend small { - font-size: 15px; - color: #999999; -} - -label, -input, -button, -select, -textarea { - font-size: 14px; - font-weight: normal; - line-height: 20px; -} - -input, -button, -select, -textarea { - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; -} - -label { - display: block; - margin-bottom: 5px; -} - -select, -textarea, -input[type="text"], -input[type="password"], -input[type="datetime"], -input[type="datetime-local"], -input[type="date"], -input[type="month"], -input[type="time"], -input[type="week"], -input[type="number"], -input[type="email"], -input[type="url"], -input[type="search"], -input[type="tel"], -input[type="color"], -.uneditable-input { - display: inline-block; - height: 20px; - padding: 4px 6px; - margin-bottom: 10px; - font-size: 14px; - line-height: 20px; - color: #555555; - vertical-align: middle; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; -} - -input, -textarea, -.uneditable-input { - width: 206px; -} - -textarea { - height: auto; -} - -textarea, -input[type="text"], -input[type="password"], -input[type="datetime"], -input[type="datetime-local"], -input[type="date"], -input[type="month"], -input[type="time"], -input[type="week"], -input[type="number"], -input[type="email"], -input[type="url"], -input[type="search"], -input[type="tel"], -input[type="color"], -.uneditable-input { - background-color: #ffffff; - border: 1px solid #cccccc; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - -webkit-transition: border linear 0.2s, box-shadow linear 0.2s; - -moz-transition: border linear 0.2s, box-shadow linear 0.2s; - -o-transition: border linear 0.2s, box-shadow linear 0.2s; - transition: border linear 0.2s, box-shadow linear 0.2s; -} - -textarea:focus, -input[type="text"]:focus, -input[type="password"]:focus, -input[type="datetime"]:focus, -input[type="datetime-local"]:focus, -input[type="date"]:focus, -input[type="month"]:focus, -input[type="time"]:focus, -input[type="week"]:focus, -input[type="number"]:focus, -input[type="email"]:focus, -input[type="url"]:focus, -input[type="search"]:focus, -input[type="tel"]:focus, -input[type="color"]:focus, -.uneditable-input:focus { - border-color: rgba(82, 168, 236, 0.8); - outline: 0; - outline: thin dotted \9; - /* IE6-9 */ - - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); -} - -input[type="radio"], -input[type="checkbox"] { - margin: 4px 0 0; - margin-top: 1px \9; - *margin-top: 0; - line-height: normal; -} - -input[type="file"], -input[type="image"], -input[type="submit"], -input[type="reset"], -input[type="button"], -input[type="radio"], -input[type="checkbox"] { - width: auto; -} - -select, -input[type="file"] { - height: 30px; - /* In IE7, the height of the select element cannot be changed by height, only font-size */ - - *margin-top: 4px; - /* For IE7, add top margin to align select with labels */ - - line-height: 30px; -} - -select { - width: 220px; - background-color: #ffffff; - border: 1px solid #cccccc; -} - -select[multiple], -select[size] { - height: auto; -} - -select:focus, -input[type="file"]:focus, -input[type="radio"]:focus, -input[type="checkbox"]:focus { - outline: thin dotted #333; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} - -.uneditable-input, -.uneditable-textarea { - color: #999999; - cursor: not-allowed; - background-color: #fcfcfc; - border-color: #cccccc; - -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); - -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); - box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); -} - -.uneditable-input { - overflow: hidden; - white-space: nowrap; -} - -.uneditable-textarea { - width: auto; - height: auto; -} - -input:-moz-placeholder, -textarea:-moz-placeholder { - color: #999999; -} - -input:-ms-input-placeholder, -textarea:-ms-input-placeholder { - color: #999999; -} - -input::-webkit-input-placeholder, -textarea::-webkit-input-placeholder { - color: #999999; -} - -.radio, -.checkbox { - min-height: 20px; - padding-left: 20px; -} - -.radio input[type="radio"], -.checkbox input[type="checkbox"] { - float: left; - margin-left: -20px; -} - -.controls > .radio:first-child, -.controls > .checkbox:first-child { - padding-top: 5px; -} - -.radio.inline, -.checkbox.inline { - display: inline-block; - padding-top: 5px; - margin-bottom: 0; - vertical-align: middle; -} - -.radio.inline + .radio.inline, -.checkbox.inline + .checkbox.inline { - margin-left: 10px; -} - -.input-mini { - width: 60px; -} - -.input-small { - width: 90px; -} - -.input-medium { - width: 150px; -} - -.input-large { - width: 210px; -} - -.input-xlarge { - width: 270px; -} - -.input-xxlarge { - width: 530px; -} - -input[class*="span"], -select[class*="span"], -textarea[class*="span"], -.uneditable-input[class*="span"], -.row-fluid input[class*="span"], -.row-fluid select[class*="span"], -.row-fluid textarea[class*="span"], -.row-fluid .uneditable-input[class*="span"] { - float: none; - margin-left: 0; -} - -.input-append input[class*="span"], -.input-append .uneditable-input[class*="span"], -.input-prepend input[class*="span"], -.input-prepend .uneditable-input[class*="span"], -.row-fluid input[class*="span"], -.row-fluid select[class*="span"], -.row-fluid textarea[class*="span"], -.row-fluid .uneditable-input[class*="span"], -.row-fluid .input-prepend [class*="span"], -.row-fluid .input-append [class*="span"] { - display: inline-block; -} - -input, -textarea, -.uneditable-input { - margin-left: 0; -} - -.controls-row [class*="span"] + [class*="span"] { - margin-left: 20px; -} - -input.span12, -textarea.span12, -.uneditable-input.span12 { - width: 926px; -} - -input.span11, -textarea.span11, -.uneditable-input.span11 { - width: 846px; -} - -input.span10, -textarea.span10, -.uneditable-input.span10 { - width: 766px; -} - -input.span9, -textarea.span9, -.uneditable-input.span9 { - width: 686px; -} - -input.span8, -textarea.span8, -.uneditable-input.span8 { - width: 606px; -} - -input.span7, -textarea.span7, -.uneditable-input.span7 { - width: 526px; -} - -input.span6, -textarea.span6, -.uneditable-input.span6 { - width: 446px; -} - -input.span5, -textarea.span5, -.uneditable-input.span5 { - width: 366px; -} - -input.span4, -textarea.span4, -.uneditable-input.span4 { - width: 286px; -} - -input.span3, -textarea.span3, -.uneditable-input.span3 { - width: 206px; -} - -input.span2, -textarea.span2, -.uneditable-input.span2 { - width: 126px; -} - -input.span1, -textarea.span1, -.uneditable-input.span1 { - width: 46px; -} - -.controls-row { - *zoom: 1; -} - -.controls-row:before, -.controls-row:after { - display: table; - line-height: 0; - content: ""; -} - -.controls-row:after { - clear: both; -} - -.controls-row [class*="span"], -.row-fluid .controls-row [class*="span"] { - float: left; -} - -.controls-row .checkbox[class*="span"], -.controls-row .radio[class*="span"] { - padding-top: 5px; -} - -input[disabled], -select[disabled], -textarea[disabled], -input[readonly], -select[readonly], -textarea[readonly] { - cursor: not-allowed; - background-color: #eeeeee; -} - -input[type="radio"][disabled], -input[type="checkbox"][disabled], -input[type="radio"][readonly], -input[type="checkbox"][readonly] { - background-color: transparent; -} - -.control-group.warning .control-label, -.control-group.warning .help-block, -.control-group.warning .help-inline { - color: #c09853; -} - -.control-group.warning .checkbox, -.control-group.warning .radio, -.control-group.warning input, -.control-group.warning select, -.control-group.warning textarea { - color: #c09853; -} - -.control-group.warning input, -.control-group.warning select, -.control-group.warning textarea { - border-color: #c09853; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); -} - -.control-group.warning input:focus, -.control-group.warning select:focus, -.control-group.warning textarea:focus { - border-color: #a47e3c; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; -} - -.control-group.warning .input-prepend .add-on, -.control-group.warning .input-append .add-on { - color: #c09853; - background-color: #fcf8e3; - border-color: #c09853; -} - -.control-group.error .control-label, -.control-group.error .help-block, -.control-group.error .help-inline { - color: #b94a48; -} - -.control-group.error .checkbox, -.control-group.error .radio, -.control-group.error input, -.control-group.error select, -.control-group.error textarea { - color: #b94a48; -} - -.control-group.error input, -.control-group.error select, -.control-group.error textarea { - border-color: #b94a48; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); -} - -.control-group.error input:focus, -.control-group.error select:focus, -.control-group.error textarea:focus { - border-color: #953b39; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; -} - -.control-group.error .input-prepend .add-on, -.control-group.error .input-append .add-on { - color: #b94a48; - background-color: #f2dede; - border-color: #b94a48; -} - -.control-group.success .control-label, -.control-group.success .help-block, -.control-group.success .help-inline { - color: #468847; -} - -.control-group.success .checkbox, -.control-group.success .radio, -.control-group.success input, -.control-group.success select, -.control-group.success textarea { - color: #468847; -} - -.control-group.success input, -.control-group.success select, -.control-group.success textarea { - border-color: #468847; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); -} - -.control-group.success input:focus, -.control-group.success select:focus, -.control-group.success textarea:focus { - border-color: #356635; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; -} - -.control-group.success .input-prepend .add-on, -.control-group.success .input-append .add-on { - color: #468847; - background-color: #dff0d8; - border-color: #468847; -} - -.control-group.info .control-label, -.control-group.info .help-block, -.control-group.info .help-inline { - color: #3a87ad; -} - -.control-group.info .checkbox, -.control-group.info .radio, -.control-group.info input, -.control-group.info select, -.control-group.info textarea { - color: #3a87ad; -} - -.control-group.info input, -.control-group.info select, -.control-group.info textarea { - border-color: #3a87ad; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); -} - -.control-group.info input:focus, -.control-group.info select:focus, -.control-group.info textarea:focus { - border-color: #2d6987; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; -} - -.control-group.info .input-prepend .add-on, -.control-group.info .input-append .add-on { - color: #3a87ad; - background-color: #d9edf7; - border-color: #3a87ad; -} - -input:focus:invalid, -textarea:focus:invalid, -select:focus:invalid { - color: #b94a48; - border-color: #ee5f5b; -} - -input:focus:invalid:focus, -textarea:focus:invalid:focus, -select:focus:invalid:focus { - border-color: #e9322d; - -webkit-box-shadow: 0 0 6px #f8b9b7; - -moz-box-shadow: 0 0 6px #f8b9b7; - box-shadow: 0 0 6px #f8b9b7; -} - -.form-actions { - padding: 19px 20px 20px; - margin-top: 20px; - margin-bottom: 20px; - background-color: #f5f5f5; - border-top: 1px solid #e5e5e5; - *zoom: 1; -} - -.form-actions:before, -.form-actions:after { - display: table; - line-height: 0; - content: ""; -} - -.form-actions:after { - clear: both; -} - -.help-block, -.help-inline { - color: #595959; -} - -.help-block { - display: block; - margin-bottom: 10px; -} - -.help-inline { - display: inline-block; - *display: inline; - padding-left: 5px; - vertical-align: middle; - *zoom: 1; -} - -.input-append, -.input-prepend { - margin-bottom: 5px; - font-size: 0; - white-space: nowrap; -} - -.input-append input, -.input-prepend input, -.input-append select, -.input-prepend select, -.input-append .uneditable-input, -.input-prepend .uneditable-input, -.input-append .dropdown-menu, -.input-prepend .dropdown-menu { - font-size: 14px; -} - -.input-append input, -.input-prepend input, -.input-append select, -.input-prepend select, -.input-append .uneditable-input, -.input-prepend .uneditable-input { - position: relative; - margin-bottom: 0; - *margin-left: 0; - vertical-align: top; - -webkit-border-radius: 0 4px 4px 0; - -moz-border-radius: 0 4px 4px 0; - border-radius: 0 4px 4px 0; -} - -.input-append input:focus, -.input-prepend input:focus, -.input-append select:focus, -.input-prepend select:focus, -.input-append .uneditable-input:focus, -.input-prepend .uneditable-input:focus { - z-index: 2; -} - -.input-append .add-on, -.input-prepend .add-on { - display: inline-block; - width: auto; - height: 20px; - min-width: 16px; - padding: 4px 5px; - font-size: 14px; - font-weight: normal; - line-height: 20px; - text-align: center; - text-shadow: 0 1px 0 #ffffff; - background-color: #eeeeee; - border: 1px solid #ccc; -} - -.input-append .add-on, -.input-prepend .add-on, -.input-append .btn, -.input-prepend .btn, -.input-append .btn-group > .dropdown-toggle, -.input-prepend .btn-group > .dropdown-toggle { - vertical-align: top; - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; -} - -.input-append .active, -.input-prepend .active { - background-color: #a9dba9; - border-color: #46a546; -} - -.input-prepend .add-on, -.input-prepend .btn { - margin-right: -1px; -} - -.input-prepend .add-on:first-child, -.input-prepend .btn:first-child { - -webkit-border-radius: 4px 0 0 4px; - -moz-border-radius: 4px 0 0 4px; - border-radius: 4px 0 0 4px; -} - -.input-append input, -.input-append select, -.input-append .uneditable-input { - -webkit-border-radius: 4px 0 0 4px; - -moz-border-radius: 4px 0 0 4px; - border-radius: 4px 0 0 4px; -} - -.input-append input + .btn-group .btn:last-child, -.input-append select + .btn-group .btn:last-child, -.input-append .uneditable-input + .btn-group .btn:last-child { - -webkit-border-radius: 0 4px 4px 0; - -moz-border-radius: 0 4px 4px 0; - border-radius: 0 4px 4px 0; -} - -.input-append .add-on, -.input-append .btn, -.input-append .btn-group { - margin-left: -1px; -} - -.input-append .add-on:last-child, -.input-append .btn:last-child, -.input-append .btn-group:last-child > .dropdown-toggle { - -webkit-border-radius: 0 4px 4px 0; - -moz-border-radius: 0 4px 4px 0; - border-radius: 0 4px 4px 0; -} - -.input-prepend.input-append input, -.input-prepend.input-append select, -.input-prepend.input-append .uneditable-input { - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; -} - -.input-prepend.input-append input + .btn-group .btn, -.input-prepend.input-append select + .btn-group .btn, -.input-prepend.input-append .uneditable-input + .btn-group .btn { - -webkit-border-radius: 0 4px 4px 0; - -moz-border-radius: 0 4px 4px 0; - border-radius: 0 4px 4px 0; -} - -.input-prepend.input-append .add-on:first-child, -.input-prepend.input-append .btn:first-child { - margin-right: -1px; - -webkit-border-radius: 4px 0 0 4px; - -moz-border-radius: 4px 0 0 4px; - border-radius: 4px 0 0 4px; -} - -.input-prepend.input-append .add-on:last-child, -.input-prepend.input-append .btn:last-child { - margin-left: -1px; - -webkit-border-radius: 0 4px 4px 0; - -moz-border-radius: 0 4px 4px 0; - border-radius: 0 4px 4px 0; -} - -.input-prepend.input-append .btn-group:first-child { - margin-left: 0; -} - -input.search-query { - padding-right: 14px; - padding-right: 4px \9; - padding-left: 14px; - padding-left: 4px \9; - /* IE7-8 doesn't have border-radius, so don't indent the padding */ - - margin-bottom: 0; - -webkit-border-radius: 15px; - -moz-border-radius: 15px; - border-radius: 15px; -} - -/* Allow for input prepend/append in search forms */ - -.form-search .input-append .search-query, -.form-search .input-prepend .search-query { - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; -} - -.form-search .input-append .search-query { - -webkit-border-radius: 14px 0 0 14px; - -moz-border-radius: 14px 0 0 14px; - border-radius: 14px 0 0 14px; -} - -.form-search .input-append .btn { - -webkit-border-radius: 0 14px 14px 0; - -moz-border-radius: 0 14px 14px 0; - border-radius: 0 14px 14px 0; -} - -.form-search .input-prepend .search-query { - -webkit-border-radius: 0 14px 14px 0; - -moz-border-radius: 0 14px 14px 0; - border-radius: 0 14px 14px 0; -} - -.form-search .input-prepend .btn { - -webkit-border-radius: 14px 0 0 14px; - -moz-border-radius: 14px 0 0 14px; - border-radius: 14px 0 0 14px; -} - -.form-search input, -.form-inline input, -.form-horizontal input, -.form-search textarea, -.form-inline textarea, -.form-horizontal textarea, -.form-search select, -.form-inline select, -.form-horizontal select, -.form-search .help-inline, -.form-inline .help-inline, -.form-horizontal .help-inline, -.form-search .uneditable-input, -.form-inline .uneditable-input, -.form-horizontal .uneditable-input, -.form-search .input-prepend, -.form-inline .input-prepend, -.form-horizontal .input-prepend, -.form-search .input-append, -.form-inline .input-append, -.form-horizontal .input-append { - display: inline-block; - *display: inline; - margin-bottom: 0; - vertical-align: middle; - *zoom: 1; -} - -.form-search .hide, -.form-inline .hide, -.form-horizontal .hide { - display: none; -} - -.form-search label, -.form-inline label, -.form-search .btn-group, -.form-inline .btn-group { - display: inline-block; -} - -.form-search .input-append, -.form-inline .input-append, -.form-search .input-prepend, -.form-inline .input-prepend { - margin-bottom: 0; -} - -.form-search .radio, -.form-search .checkbox, -.form-inline .radio, -.form-inline .checkbox { - padding-left: 0; - margin-bottom: 0; - vertical-align: middle; -} - -.form-search .radio input[type="radio"], -.form-search .checkbox input[type="checkbox"], -.form-inline .radio input[type="radio"], -.form-inline .checkbox input[type="checkbox"] { - float: left; - margin-right: 3px; - margin-left: 0; -} - -.control-group { - margin-bottom: 10px; -} - -legend + .control-group { - margin-top: 20px; - -webkit-margin-top-collapse: separate; -} - -.form-horizontal .control-group { - margin-bottom: 20px; - *zoom: 1; -} - -.form-horizontal .control-group:before, -.form-horizontal .control-group:after { - display: table; - line-height: 0; - content: ""; -} - -.form-horizontal .control-group:after { - clear: both; -} - -.form-horizontal .control-label { - float: left; - width: 160px; - padding-top: 5px; - text-align: right; -} - -.form-horizontal .controls { - *display: inline-block; - *padding-left: 20px; - margin-left: 180px; - *margin-left: 0; -} - -.form-horizontal .controls:first-child { - *padding-left: 180px; -} - -.form-horizontal .help-block { - margin-bottom: 0; -} - -.form-horizontal input + .help-block, -.form-horizontal select + .help-block, -.form-horizontal textarea + .help-block, -.form-horizontal .uneditable-input + .help-block, -.form-horizontal .input-prepend + .help-block, -.form-horizontal .input-append + .help-block { - margin-top: 10px; -} - -.form-horizontal .form-actions { - padding-left: 180px; -} - -table { - max-width: 100%; - background-color: transparent; - border-collapse: collapse; - border-spacing: 0; -} - -.table { - width: 100%; - margin-bottom: 20px; -} - -.table th, -.table td { - padding: 8px; - line-height: 20px; - text-align: left; - vertical-align: top; - border-top: 1px solid #dddddd; -} - -.table th { - font-weight: bold; -} - -.table thead th { - vertical-align: bottom; -} - -.table caption + thead tr:first-child th, -.table caption + thead tr:first-child td, -.table colgroup + thead tr:first-child th, -.table colgroup + thead tr:first-child td, -.table thead:first-child tr:first-child th, -.table thead:first-child tr:first-child td { - border-top: 0; -} - -.table tbody + tbody { - border-top: 2px solid #dddddd; -} - -.table .table { - background-color: #ffffff; -} - -.table-condensed th, -.table-condensed td { - padding: 4px 5px; -} - -.table-bordered { - border: 1px solid #dddddd; - border-collapse: separate; - *border-collapse: collapse; - border-left: 0; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; -} - -.table-bordered th, -.table-bordered td { - border-left: 1px solid #dddddd; -} - -.table-bordered caption + thead tr:first-child th, -.table-bordered caption + tbody tr:first-child th, -.table-bordered caption + tbody tr:first-child td, -.table-bordered colgroup + thead tr:first-child th, -.table-bordered colgroup + tbody tr:first-child th, -.table-bordered colgroup + tbody tr:first-child td, -.table-bordered thead:first-child tr:first-child th, -.table-bordered tbody:first-child tr:first-child th, -.table-bordered tbody:first-child tr:first-child td { - border-top: 0; -} - -.table-bordered thead:first-child tr:first-child > th:first-child, -.table-bordered tbody:first-child tr:first-child > td:first-child { - -webkit-border-top-left-radius: 4px; - border-top-left-radius: 4px; - -moz-border-radius-topleft: 4px; -} - -.table-bordered thead:first-child tr:first-child > th:last-child, -.table-bordered tbody:first-child tr:first-child > td:last-child { - -webkit-border-top-right-radius: 4px; - border-top-right-radius: 4px; - -moz-border-radius-topright: 4px; -} - -.table-bordered thead:last-child tr:last-child > th:first-child, -.table-bordered tbody:last-child tr:last-child > td:first-child, -.table-bordered tfoot:last-child tr:last-child > td:first-child { - -webkit-border-bottom-left-radius: 4px; - border-bottom-left-radius: 4px; - -moz-border-radius-bottomleft: 4px; -} - -.table-bordered thead:last-child tr:last-child > th:last-child, -.table-bordered tbody:last-child tr:last-child > td:last-child, -.table-bordered tfoot:last-child tr:last-child > td:last-child { - -webkit-border-bottom-right-radius: 4px; - border-bottom-right-radius: 4px; - -moz-border-radius-bottomright: 4px; -} - -.table-bordered tfoot + tbody:last-child tr:last-child td:first-child { - -webkit-border-bottom-left-radius: 0; - border-bottom-left-radius: 0; - -moz-border-radius-bottomleft: 0; -} - -.table-bordered tfoot + tbody:last-child tr:last-child td:last-child { - -webkit-border-bottom-right-radius: 0; - border-bottom-right-radius: 0; - -moz-border-radius-bottomright: 0; -} - -.table-bordered caption + thead tr:first-child th:first-child, -.table-bordered caption + tbody tr:first-child td:first-child, -.table-bordered colgroup + thead tr:first-child th:first-child, -.table-bordered colgroup + tbody tr:first-child td:first-child { - -webkit-border-top-left-radius: 4px; - border-top-left-radius: 4px; - -moz-border-radius-topleft: 4px; -} - -.table-bordered caption + thead tr:first-child th:last-child, -.table-bordered caption + tbody tr:first-child td:last-child, -.table-bordered colgroup + thead tr:first-child th:last-child, -.table-bordered colgroup + tbody tr:first-child td:last-child { - -webkit-border-top-right-radius: 4px; - border-top-right-radius: 4px; - -moz-border-radius-topright: 4px; -} - -.table-striped tbody > tr:nth-child(odd) > td, -.table-striped tbody > tr:nth-child(odd) > th { - background-color: #f9f9f9; -} - -.table-hover tbody tr:hover td, -.table-hover tbody tr:hover th { - background-color: #f5f5f5; -} - -table td[class*="span"], -table th[class*="span"], -.row-fluid table td[class*="span"], -.row-fluid table th[class*="span"] { - display: table-cell; - float: none; - margin-left: 0; -} - -.table td.span1, -.table th.span1 { - float: none; - width: 44px; - margin-left: 0; -} - -.table td.span2, -.table th.span2 { - float: none; - width: 124px; - margin-left: 0; -} - -.table td.span3, -.table th.span3 { - float: none; - width: 204px; - margin-left: 0; -} - -.table td.span4, -.table th.span4 { - float: none; - width: 284px; - margin-left: 0; -} - -.table td.span5, -.table th.span5 { - float: none; - width: 364px; - margin-left: 0; -} - -.table td.span6, -.table th.span6 { - float: none; - width: 444px; - margin-left: 0; -} - -.table td.span7, -.table th.span7 { - float: none; - width: 524px; - margin-left: 0; -} - -.table td.span8, -.table th.span8 { - float: none; - width: 604px; - margin-left: 0; -} - -.table td.span9, -.table th.span9 { - float: none; - width: 684px; - margin-left: 0; -} - -.table td.span10, -.table th.span10 { - float: none; - width: 764px; - margin-left: 0; -} - -.table td.span11, -.table th.span11 { - float: none; - width: 844px; - margin-left: 0; -} - -.table td.span12, -.table th.span12 { - float: none; - width: 924px; - margin-left: 0; -} - -.table tbody tr.success td { - background-color: #dff0d8; -} - -.table tbody tr.error td { - background-color: #f2dede; -} - -.table tbody tr.warning td { - background-color: #fcf8e3; -} - -.table tbody tr.info td { - background-color: #d9edf7; -} - -.table-hover tbody tr.success:hover td { - background-color: #d0e9c6; -} - -.table-hover tbody tr.error:hover td { - background-color: #ebcccc; -} - -.table-hover tbody tr.warning:hover td { - background-color: #faf2cc; -} - -.table-hover tbody tr.info:hover td { - background-color: #c4e3f3; -} - -[class^="icon-"], -[class*=" icon-"] { - display: inline-block; - width: 14px; - height: 14px; - margin-top: 1px; - *margin-right: .3em; - line-height: 14px; - vertical-align: text-top; - background-image: url("../img/glyphicons-halflings.png"); - background-position: 14px 14px; - background-repeat: no-repeat; -} - -/* White icons with optional class, or on hover/active states of certain elements */ - -.icon-white, -.nav-pills > .active > a > [class^="icon-"], -.nav-pills > .active > a > [class*=" icon-"], -.nav-list > .active > a > [class^="icon-"], -.nav-list > .active > a > [class*=" icon-"], -.navbar-inverse .nav > .active > a > [class^="icon-"], -.navbar-inverse .nav > .active > a > [class*=" icon-"], -.dropdown-menu > li > a:hover > [class^="icon-"], -.dropdown-menu > li > a:hover > [class*=" icon-"], -.dropdown-menu > .active > a > [class^="icon-"], -.dropdown-menu > .active > a > [class*=" icon-"], -.dropdown-submenu:hover > a > [class^="icon-"], -.dropdown-submenu:hover > a > [class*=" icon-"] { - background-image: url("../img/glyphicons-halflings-white.png"); -} - -.icon-glass { - background-position: 0 0; -} - -.icon-music { - background-position: -24px 0; -} - -.icon-search { - background-position: -48px 0; -} - -.icon-envelope { - background-position: -72px 0; -} - -.icon-heart { - background-position: -96px 0; -} - -.icon-star { - background-position: -120px 0; -} - -.icon-star-empty { - background-position: -144px 0; -} - -.icon-user { - background-position: -168px 0; -} - -.icon-film { - background-position: -192px 0; -} - -.icon-th-large { - background-position: -216px 0; -} - -.icon-th { - background-position: -240px 0; -} - -.icon-th-list { - background-position: -264px 0; -} - -.icon-ok { - background-position: -288px 0; -} - -.icon-remove { - background-position: -312px 0; -} - -.icon-zoom-in { - background-position: -336px 0; -} - -.icon-zoom-out { - background-position: -360px 0; -} - -.icon-off { - background-position: -384px 0; -} - -.icon-signal { - background-position: -408px 0; -} - -.icon-cog { - background-position: -432px 0; -} - -.icon-trash { - background-position: -456px 0; -} - -.icon-home { - background-position: 0 -24px; -} - -.icon-file { - background-position: -24px -24px; -} - -.icon-time { - background-position: -48px -24px; -} - -.icon-road { - background-position: -72px -24px; -} - -.icon-download-alt { - background-position: -96px -24px; -} - -.icon-download { - background-position: -120px -24px; -} - -.icon-upload { - background-position: -144px -24px; -} - -.icon-inbox { - background-position: -168px -24px; -} - -.icon-play-circle { - background-position: -192px -24px; -} - -.icon-repeat { - background-position: -216px -24px; -} - -.icon-refresh { - background-position: -240px -24px; -} - -.icon-list-alt { - background-position: -264px -24px; -} - -.icon-lock { - background-position: -287px -24px; -} - -.icon-flag { - background-position: -312px -24px; -} - -.icon-headphones { - background-position: -336px -24px; -} - -.icon-volume-off { - background-position: -360px -24px; -} - -.icon-volume-down { - background-position: -384px -24px; -} - -.icon-volume-up { - background-position: -408px -24px; -} - -.icon-qrcode { - background-position: -432px -24px; -} - -.icon-barcode { - background-position: -456px -24px; -} - -.icon-tag { - background-position: 0 -48px; -} - -.icon-tags { - background-position: -25px -48px; -} - -.icon-book { - background-position: -48px -48px; -} - -.icon-bookmark { - background-position: -72px -48px; -} - -.icon-print { - background-position: -96px -48px; -} - -.icon-camera { - background-position: -120px -48px; -} - -.icon-font { - background-position: -144px -48px; -} - -.icon-bold { - background-position: -167px -48px; -} - -.icon-italic { - background-position: -192px -48px; -} - -.icon-text-height { - background-position: -216px -48px; -} - -.icon-text-width { - background-position: -240px -48px; -} - -.icon-align-left { - background-position: -264px -48px; -} - -.icon-align-center { - background-position: -288px -48px; -} - -.icon-align-right { - background-position: -312px -48px; -} - -.icon-align-justify { - background-position: -336px -48px; -} - -.icon-list { - background-position: -360px -48px; -} - -.icon-indent-left { - background-position: -384px -48px; -} - -.icon-indent-right { - background-position: -408px -48px; -} - -.icon-facetime-video { - background-position: -432px -48px; -} - -.icon-picture { - background-position: -456px -48px; -} - -.icon-pencil { - background-position: 0 -72px; -} - -.icon-map-marker { - background-position: -24px -72px; -} - -.icon-adjust { - background-position: -48px -72px; -} - -.icon-tint { - background-position: -72px -72px; -} - -.icon-edit { - background-position: -96px -72px; -} - -.icon-share { - background-position: -120px -72px; -} - -.icon-check { - background-position: -144px -72px; -} - -.icon-move { - background-position: -168px -72px; -} - -.icon-step-backward { - background-position: -192px -72px; -} - -.icon-fast-backward { - background-position: -216px -72px; -} - -.icon-backward { - background-position: -240px -72px; -} - -.icon-play { - background-position: -264px -72px; -} - -.icon-pause { - background-position: -288px -72px; -} - -.icon-stop { - background-position: -312px -72px; -} - -.icon-forward { - background-position: -336px -72px; -} - -.icon-fast-forward { - background-position: -360px -72px; -} - -.icon-step-forward { - background-position: -384px -72px; -} - -.icon-eject { - background-position: -408px -72px; -} - -.icon-chevron-left { - background-position: -432px -72px; -} - -.icon-chevron-right { - background-position: -456px -72px; -} - -.icon-plus-sign { - background-position: 0 -96px; -} - -.icon-minus-sign { - background-position: -24px -96px; -} - -.icon-remove-sign { - background-position: -48px -96px; -} - -.icon-ok-sign { - background-position: -72px -96px; -} - -.icon-question-sign { - background-position: -96px -96px; -} - -.icon-info-sign { - background-position: -120px -96px; -} - -.icon-screenshot { - background-position: -144px -96px; -} - -.icon-remove-circle { - background-position: -168px -96px; -} - -.icon-ok-circle { - background-position: -192px -96px; -} - -.icon-ban-circle { - background-position: -216px -96px; -} - -.icon-arrow-left { - background-position: -240px -96px; -} - -.icon-arrow-right { - background-position: -264px -96px; -} - -.icon-arrow-up { - background-position: -289px -96px; -} - -.icon-arrow-down { - background-position: -312px -96px; -} - -.icon-share-alt { - background-position: -336px -96px; -} - -.icon-resize-full { - background-position: -360px -96px; -} - -.icon-resize-small { - background-position: -384px -96px; -} - -.icon-plus { - background-position: -408px -96px; -} - -.icon-minus { - background-position: -433px -96px; -} - -.icon-asterisk { - background-position: -456px -96px; -} - -.icon-exclamation-sign { - background-position: 0 -120px; -} - -.icon-gift { - background-position: -24px -120px; -} - -.icon-leaf { - background-position: -48px -120px; -} - -.icon-fire { - background-position: -72px -120px; -} - -.icon-eye-open { - background-position: -96px -120px; -} - -.icon-eye-close { - background-position: -120px -120px; -} - -.icon-warning-sign { - background-position: -144px -120px; -} - -.icon-plane { - background-position: -168px -120px; -} - -.icon-calendar { - background-position: -192px -120px; -} - -.icon-random { - width: 16px; - background-position: -216px -120px; -} - -.icon-comment { - background-position: -240px -120px; -} - -.icon-magnet { - background-position: -264px -120px; -} - -.icon-chevron-up { - background-position: -288px -120px; -} - -.icon-chevron-down { - background-position: -313px -119px; -} - -.icon-retweet { - background-position: -336px -120px; -} - -.icon-shopping-cart { - background-position: -360px -120px; -} - -.icon-folder-close { - background-position: -384px -120px; -} - -.icon-folder-open { - width: 16px; - background-position: -408px -120px; -} - -.icon-resize-vertical { - background-position: -432px -119px; -} - -.icon-resize-horizontal { - background-position: -456px -118px; -} - -.icon-hdd { - background-position: 0 -144px; -} - -.icon-bullhorn { - background-position: -24px -144px; -} - -.icon-bell { - background-position: -48px -144px; -} - -.icon-certificate { - background-position: -72px -144px; -} - -.icon-thumbs-up { - background-position: -96px -144px; -} - -.icon-thumbs-down { - background-position: -120px -144px; -} - -.icon-hand-right { - background-position: -144px -144px; -} - -.icon-hand-left { - background-position: -168px -144px; -} - -.icon-hand-up { - background-position: -192px -144px; -} - -.icon-hand-down { - background-position: -216px -144px; -} - -.icon-circle-arrow-right { - background-position: -240px -144px; -} - -.icon-circle-arrow-left { - background-position: -264px -144px; -} - -.icon-circle-arrow-up { - background-position: -288px -144px; -} - -.icon-circle-arrow-down { - background-position: -312px -144px; -} - -.icon-globe { - background-position: -336px -144px; -} - -.icon-wrench { - background-position: -360px -144px; -} - -.icon-tasks { - background-position: -384px -144px; -} - -.icon-filter { - background-position: -408px -144px; -} - -.icon-briefcase { - background-position: -432px -144px; -} - -.icon-fullscreen { - background-position: -456px -144px; -} - -.dropup, -.dropdown { - position: relative; -} - -.dropdown-toggle { - *margin-bottom: -3px; -} - -.dropdown-toggle:active, -.open .dropdown-toggle { - outline: 0; -} - -.caret { - display: inline-block; - width: 0; - height: 0; - vertical-align: top; - border-top: 4px solid #000000; - border-right: 4px solid transparent; - border-left: 4px solid transparent; - content: ""; -} - -.dropdown .caret { - margin-top: 8px; - margin-left: 2px; -} - -.dropdown-menu { - position: absolute; - top: 100%; - left: 0; - z-index: 1000; - display: none; - float: left; - min-width: 160px; - padding: 5px 0; - margin: 2px 0 0; - list-style: none; - background-color: #ffffff; - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, 0.2); - *border-right-width: 2px; - *border-bottom-width: 2px; - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; - -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); - -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); - box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); - -webkit-background-clip: padding-box; - -moz-background-clip: padding; - background-clip: padding-box; -} - -.dropdown-menu.pull-right { - right: 0; - left: auto; -} - -.dropdown-menu .divider { - *width: 100%; - height: 1px; - margin: 9px 1px; - *margin: -5px 0 5px; - overflow: hidden; - background-color: #e5e5e5; - border-bottom: 1px solid #ffffff; -} - -.dropdown-menu li > a { - display: block; - padding: 3px 20px; - clear: both; - font-weight: normal; - line-height: 20px; - color: #333333; - white-space: nowrap; -} - -.dropdown-menu li > a:hover, -.dropdown-menu li > a:focus, -.dropdown-submenu:hover > a { - color: #ffffff; - text-decoration: none; - background-color: #0081c2; - background-image: -moz-linear-gradient(top, #0088cc, #0077b3); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3)); - background-image: -webkit-linear-gradient(top, #0088cc, #0077b3); - background-image: -o-linear-gradient(top, #0088cc, #0077b3); - background-image: linear-gradient(to bottom, #0088cc, #0077b3); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0); -} - -.dropdown-menu .active > a, -.dropdown-menu .active > a:hover { - color: #ffffff; - text-decoration: none; - background-color: #0081c2; - background-image: -moz-linear-gradient(top, #0088cc, #0077b3); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3)); - background-image: -webkit-linear-gradient(top, #0088cc, #0077b3); - background-image: -o-linear-gradient(top, #0088cc, #0077b3); - background-image: linear-gradient(to bottom, #0088cc, #0077b3); - background-repeat: repeat-x; - outline: 0; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0); -} - -.dropdown-menu .disabled > a, -.dropdown-menu .disabled > a:hover { - color: #999999; -} - -.dropdown-menu .disabled > a:hover { - text-decoration: none; - cursor: default; - background-color: transparent; - background-image: none; - filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); -} - -.open { - *z-index: 1000; -} - -.open > .dropdown-menu { - display: block; -} - -.pull-right > .dropdown-menu { - right: 0; - left: auto; -} - -.dropup .caret, -.navbar-fixed-bottom .dropdown .caret { - border-top: 0; - border-bottom: 4px solid #000000; - content: ""; -} - -.dropup .dropdown-menu, -.navbar-fixed-bottom .dropdown .dropdown-menu { - top: auto; - bottom: 100%; - margin-bottom: 1px; -} - -.dropdown-submenu { - position: relative; -} - -.dropdown-submenu > .dropdown-menu { - top: 0; - left: 100%; - margin-top: -6px; - margin-left: -1px; - -webkit-border-radius: 0 6px 6px 6px; - -moz-border-radius: 0 6px 6px 6px; - border-radius: 0 6px 6px 6px; -} - -.dropdown-submenu:hover > .dropdown-menu { - display: block; -} - -.dropup .dropdown-submenu > .dropdown-menu { - top: auto; - bottom: 0; - margin-top: 0; - margin-bottom: -2px; - -webkit-border-radius: 5px 5px 5px 0; - -moz-border-radius: 5px 5px 5px 0; - border-radius: 5px 5px 5px 0; -} - -.dropdown-submenu > a:after { - display: block; - float: right; - width: 0; - height: 0; - margin-top: 5px; - margin-right: -10px; - border-color: transparent; - border-left-color: #cccccc; - border-style: solid; - border-width: 5px 0 5px 5px; - content: " "; -} - -.dropdown-submenu:hover > a:after { - border-left-color: #ffffff; -} - -.dropdown-submenu.pull-left { - float: none; -} - -.dropdown-submenu.pull-left > .dropdown-menu { - left: -100%; - margin-left: 10px; - -webkit-border-radius: 6px 0 6px 6px; - -moz-border-radius: 6px 0 6px 6px; - border-radius: 6px 0 6px 6px; -} - -.dropdown .dropdown-menu .nav-header { - padding-right: 20px; - padding-left: 20px; -} - -.typeahead { - z-index: 1051; - margin-top: 2px; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; -} - -.well { - min-height: 20px; - padding: 19px; - margin-bottom: 20px; - background-color: #f5f5f5; - border: 1px solid #e3e3e3; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); -} - -.well blockquote { - border-color: #ddd; - border-color: rgba(0, 0, 0, 0.15); -} - -.well-large { - padding: 24px; - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; -} - -.well-small { - padding: 9px; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; -} - -.fade { - opacity: 0; - -webkit-transition: opacity 0.15s linear; - -moz-transition: opacity 0.15s linear; - -o-transition: opacity 0.15s linear; - transition: opacity 0.15s linear; -} - -.fade.in { - opacity: 1; -} - -.collapse { - position: relative; - height: 0; - overflow: hidden; - -webkit-transition: height 0.35s ease; - -moz-transition: height 0.35s ease; - -o-transition: height 0.35s ease; - transition: height 0.35s ease; -} - -.collapse.in { - height: auto; -} - -.close { - float: right; - font-size: 20px; - font-weight: bold; - line-height: 20px; - color: #000000; - text-shadow: 0 1px 0 #ffffff; - opacity: 0.2; - filter: alpha(opacity=20); -} - -.close:hover { - color: #000000; - text-decoration: none; - cursor: pointer; - opacity: 0.4; - filter: alpha(opacity=40); -} - -button.close { - padding: 0; - cursor: pointer; - background: transparent; - border: 0; - -webkit-appearance: none; -} - -.btn { - display: inline-block; - *display: inline; - padding: 4px 12px; - margin-bottom: 0; - *margin-left: .3em; - font-size: 14px; - line-height: 20px; - color: #333333; - text-align: center; - text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); - vertical-align: middle; - cursor: pointer; - background-color: #f5f5f5; - *background-color: #e6e6e6; - background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6)); - background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); - background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); - background-image: linear-gradient(to bottom, #ffffff, #e6e6e6); - background-repeat: repeat-x; - border: 1px solid #bbbbbb; - *border: 0; - border-color: #e6e6e6 #e6e6e6 #bfbfbf; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - border-bottom-color: #a2a2a2; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0); - filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); - *zoom: 1; - -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); - -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); -} - -.btn:hover, -.btn:active, -.btn.active, -.btn.disabled, -.btn[disabled] { - color: #333333; - background-color: #e6e6e6; - *background-color: #d9d9d9; -} - -.btn:active, -.btn.active { - background-color: #cccccc \9; -} - -.btn:first-child { - *margin-left: 0; -} - -.btn:hover { - color: #333333; - text-decoration: none; - background-position: 0 -15px; - -webkit-transition: background-position 0.1s linear; - -moz-transition: background-position 0.1s linear; - -o-transition: background-position 0.1s linear; - transition: background-position 0.1s linear; -} - -.btn:focus { - outline: thin dotted #333; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} - -.btn.active, -.btn:active { - background-image: none; - outline: 0; - -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); - -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); - box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); -} - -.btn.disabled, -.btn[disabled] { - cursor: default; - background-image: none; - opacity: 0.65; - filter: alpha(opacity=65); - -webkit-box-shadow: none; - -moz-box-shadow: none; - box-shadow: none; -} - -.btn-large { - padding: 11px 19px; - font-size: 17.5px; - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; -} - -.btn-large [class^="icon-"], -.btn-large [class*=" icon-"] { - margin-top: 4px; -} - -.btn-small { - padding: 2px 10px; - font-size: 11.9px; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; -} - -.btn-small [class^="icon-"], -.btn-small [class*=" icon-"] { - margin-top: 0; -} - -.btn-mini [class^="icon-"], -.btn-mini [class*=" icon-"] { - margin-top: -1px; -} - -.btn-mini { - padding: 0 6px; - font-size: 10.5px; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; -} - -.btn-block { - display: block; - width: 100%; - padding-right: 0; - padding-left: 0; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} - -.btn-block + .btn-block { - margin-top: 5px; -} - -input[type="submit"].btn-block, -input[type="reset"].btn-block, -input[type="button"].btn-block { - width: 100%; -} - -.btn-primary.active, -.btn-warning.active, -.btn-danger.active, -.btn-success.active, -.btn-info.active, -.btn-inverse.active { - color: rgba(255, 255, 255, 0.75); -} - -.btn { - border-color: #c5c5c5; - border-color: rgba(0, 0, 0, 0.15) rgba(0, 0, 0, 0.15) rgba(0, 0, 0, 0.25); -} - -.btn-primary { - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #006dcc; - *background-color: #0044cc; - background-image: -moz-linear-gradient(top, #0088cc, #0044cc); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc)); - background-image: -webkit-linear-gradient(top, #0088cc, #0044cc); - background-image: -o-linear-gradient(top, #0088cc, #0044cc); - background-image: linear-gradient(to bottom, #0088cc, #0044cc); - background-repeat: repeat-x; - border-color: #0044cc #0044cc #002a80; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0044cc', GradientType=0); - filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); -} - -.btn-primary:hover, -.btn-primary:active, -.btn-primary.active, -.btn-primary.disabled, -.btn-primary[disabled] { - color: #ffffff; - background-color: #0044cc; - *background-color: #003bb3; -} - -.btn-primary:active, -.btn-primary.active { - background-color: #003399 \9; -} - -.btn-warning { - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #faa732; - *background-color: #f89406; - background-image: -moz-linear-gradient(top, #fbb450, #f89406); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406)); - background-image: -webkit-linear-gradient(top, #fbb450, #f89406); - background-image: -o-linear-gradient(top, #fbb450, #f89406); - background-image: linear-gradient(to bottom, #fbb450, #f89406); - background-repeat: repeat-x; - border-color: #f89406 #f89406 #ad6704; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0); - filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); -} - -.btn-warning:hover, -.btn-warning:active, -.btn-warning.active, -.btn-warning.disabled, -.btn-warning[disabled] { - color: #ffffff; - background-color: #f89406; - *background-color: #df8505; -} - -.btn-warning:active, -.btn-warning.active { - background-color: #c67605 \9; -} - -.btn-danger { - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #da4f49; - *background-color: #bd362f; - background-image: -moz-linear-gradient(top, #ee5f5b, #bd362f); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f)); - background-image: -webkit-linear-gradient(top, #ee5f5b, #bd362f); - background-image: -o-linear-gradient(top, #ee5f5b, #bd362f); - background-image: linear-gradient(to bottom, #ee5f5b, #bd362f); - background-repeat: repeat-x; - border-color: #bd362f #bd362f #802420; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffbd362f', GradientType=0); - filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); -} - -.btn-danger:hover, -.btn-danger:active, -.btn-danger.active, -.btn-danger.disabled, -.btn-danger[disabled] { - color: #ffffff; - background-color: #bd362f; - *background-color: #a9302a; -} - -.btn-danger:active, -.btn-danger.active { - background-color: #942a25 \9; -} - -.btn-success { - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #5bb75b; - *background-color: #51a351; - background-image: -moz-linear-gradient(top, #62c462, #51a351); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351)); - background-image: -webkit-linear-gradient(top, #62c462, #51a351); - background-image: -o-linear-gradient(top, #62c462, #51a351); - background-image: linear-gradient(to bottom, #62c462, #51a351); - background-repeat: repeat-x; - border-color: #51a351 #51a351 #387038; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff51a351', GradientType=0); - filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); -} - -.btn-success:hover, -.btn-success:active, -.btn-success.active, -.btn-success.disabled, -.btn-success[disabled] { - color: #ffffff; - background-color: #51a351; - *background-color: #499249; -} - -.btn-success:active, -.btn-success.active { - background-color: #408140 \9; -} - -.btn-info { - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #49afcd; - *background-color: #2f96b4; - background-image: -moz-linear-gradient(top, #5bc0de, #2f96b4); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4)); - background-image: -webkit-linear-gradient(top, #5bc0de, #2f96b4); - background-image: -o-linear-gradient(top, #5bc0de, #2f96b4); - background-image: linear-gradient(to bottom, #5bc0de, #2f96b4); - background-repeat: repeat-x; - border-color: #2f96b4 #2f96b4 #1f6377; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2f96b4', GradientType=0); - filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); -} - -.btn-info:hover, -.btn-info:active, -.btn-info.active, -.btn-info.disabled, -.btn-info[disabled] { - color: #ffffff; - background-color: #2f96b4; - *background-color: #2a85a0; -} - -.btn-info:active, -.btn-info.active { - background-color: #24748c \9; -} - -.btn-inverse { - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #363636; - *background-color: #222222; - background-image: -moz-linear-gradient(top, #444444, #222222); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#444444), to(#222222)); - background-image: -webkit-linear-gradient(top, #444444, #222222); - background-image: -o-linear-gradient(top, #444444, #222222); - background-image: linear-gradient(to bottom, #444444, #222222); - background-repeat: repeat-x; - border-color: #222222 #222222 #000000; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff444444', endColorstr='#ff222222', GradientType=0); - filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); -} - -.btn-inverse:hover, -.btn-inverse:active, -.btn-inverse.active, -.btn-inverse.disabled, -.btn-inverse[disabled] { - color: #ffffff; - background-color: #222222; - *background-color: #151515; -} - -.btn-inverse:active, -.btn-inverse.active { - background-color: #080808 \9; -} - -button.btn, -input[type="submit"].btn { - *padding-top: 3px; - *padding-bottom: 3px; -} - -button.btn::-moz-focus-inner, -input[type="submit"].btn::-moz-focus-inner { - padding: 0; - border: 0; -} - -button.btn.btn-large, -input[type="submit"].btn.btn-large { - *padding-top: 7px; - *padding-bottom: 7px; -} - -button.btn.btn-small, -input[type="submit"].btn.btn-small { - *padding-top: 3px; - *padding-bottom: 3px; -} - -button.btn.btn-mini, -input[type="submit"].btn.btn-mini { - *padding-top: 1px; - *padding-bottom: 1px; -} - -.btn-link, -.btn-link:active, -.btn-link[disabled] { - background-color: transparent; - background-image: none; - -webkit-box-shadow: none; - -moz-box-shadow: none; - box-shadow: none; -} - -.btn-link { - color: #0088cc; - cursor: pointer; - border-color: transparent; - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; -} - -.btn-link:hover { - color: #005580; - text-decoration: underline; - background-color: transparent; -} - -.btn-link[disabled]:hover { - color: #333333; - text-decoration: none; -} - -.btn-group { - position: relative; - display: inline-block; - *display: inline; - *margin-left: .3em; - font-size: 0; - white-space: nowrap; - vertical-align: middle; - *zoom: 1; -} - -.btn-group:first-child { - *margin-left: 0; -} - -.btn-group + .btn-group { - margin-left: 5px; -} - -.btn-toolbar { - margin-top: 10px; - margin-bottom: 10px; - font-size: 0; -} - -.btn-toolbar > .btn + .btn, -.btn-toolbar > .btn-group + .btn, -.btn-toolbar > .btn + .btn-group { - margin-left: 5px; -} - -.btn-group > .btn { - position: relative; - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; -} - -.btn-group > .btn + .btn { - margin-left: -1px; -} - -.btn-group > .btn, -.btn-group > .dropdown-menu, -.btn-group > .popover { - font-size: 14px; -} - -.btn-group > .btn-mini { - font-size: 10.5px; -} - -.btn-group > .btn-small { - font-size: 11.9px; -} - -.btn-group > .btn-large { - font-size: 17.5px; -} - -.btn-group > .btn:first-child { - margin-left: 0; - -webkit-border-bottom-left-radius: 4px; - border-bottom-left-radius: 4px; - -webkit-border-top-left-radius: 4px; - border-top-left-radius: 4px; - -moz-border-radius-bottomleft: 4px; - -moz-border-radius-topleft: 4px; -} - -.btn-group > .btn:last-child, -.btn-group > .dropdown-toggle { - -webkit-border-top-right-radius: 4px; - border-top-right-radius: 4px; - -webkit-border-bottom-right-radius: 4px; - border-bottom-right-radius: 4px; - -moz-border-radius-topright: 4px; - -moz-border-radius-bottomright: 4px; -} - -.btn-group > .btn.large:first-child { - margin-left: 0; - -webkit-border-bottom-left-radius: 6px; - border-bottom-left-radius: 6px; - -webkit-border-top-left-radius: 6px; - border-top-left-radius: 6px; - -moz-border-radius-bottomleft: 6px; - -moz-border-radius-topleft: 6px; -} - -.btn-group > .btn.large:last-child, -.btn-group > .large.dropdown-toggle { - -webkit-border-top-right-radius: 6px; - border-top-right-radius: 6px; - -webkit-border-bottom-right-radius: 6px; - border-bottom-right-radius: 6px; - -moz-border-radius-topright: 6px; - -moz-border-radius-bottomright: 6px; -} - -.btn-group > .btn:hover, -.btn-group > .btn:focus, -.btn-group > .btn:active, -.btn-group > .btn.active { - z-index: 2; -} - -.btn-group .dropdown-toggle:active, -.btn-group.open .dropdown-toggle { - outline: 0; -} - -.btn-group > .btn + .dropdown-toggle { - *padding-top: 5px; - padding-right: 8px; - *padding-bottom: 5px; - padding-left: 8px; - -webkit-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); - -moz-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); - box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); -} - -.btn-group > .btn-mini + .dropdown-toggle { - *padding-top: 2px; - padding-right: 5px; - *padding-bottom: 2px; - padding-left: 5px; -} - -.btn-group > .btn-small + .dropdown-toggle { - *padding-top: 5px; - *padding-bottom: 4px; -} - -.btn-group > .btn-large + .dropdown-toggle { - *padding-top: 7px; - padding-right: 12px; - *padding-bottom: 7px; - padding-left: 12px; -} - -.btn-group.open .dropdown-toggle { - background-image: none; - -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); - -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); - box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); -} - -.btn-group.open .btn.dropdown-toggle { - background-color: #e6e6e6; -} - -.btn-group.open .btn-primary.dropdown-toggle { - background-color: #0044cc; -} - -.btn-group.open .btn-warning.dropdown-toggle { - background-color: #f89406; -} - -.btn-group.open .btn-danger.dropdown-toggle { - background-color: #bd362f; -} - -.btn-group.open .btn-success.dropdown-toggle { - background-color: #51a351; -} - -.btn-group.open .btn-info.dropdown-toggle { - background-color: #2f96b4; -} - -.btn-group.open .btn-inverse.dropdown-toggle { - background-color: #222222; -} - -.btn .caret { - margin-top: 8px; - margin-left: 0; -} - -.btn-mini .caret, -.btn-small .caret, -.btn-large .caret { - margin-top: 6px; -} - -.btn-large .caret { - border-top-width: 5px; - border-right-width: 5px; - border-left-width: 5px; -} - -.dropup .btn-large .caret { - border-bottom-width: 5px; -} - -.btn-primary .caret, -.btn-warning .caret, -.btn-danger .caret, -.btn-info .caret, -.btn-success .caret, -.btn-inverse .caret { - border-top-color: #ffffff; - border-bottom-color: #ffffff; -} - -.btn-group-vertical { - display: inline-block; - *display: inline; - /* IE7 inline-block hack */ - - *zoom: 1; -} - -.btn-group-vertical > .btn { - display: block; - float: none; - max-width: 100%; - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; -} - -.btn-group-vertical > .btn + .btn { - margin-top: -1px; - margin-left: 0; -} - -.btn-group-vertical > .btn:first-child { - -webkit-border-radius: 4px 4px 0 0; - -moz-border-radius: 4px 4px 0 0; - border-radius: 4px 4px 0 0; -} - -.btn-group-vertical > .btn:last-child { - -webkit-border-radius: 0 0 4px 4px; - -moz-border-radius: 0 0 4px 4px; - border-radius: 0 0 4px 4px; -} - -.btn-group-vertical > .btn-large:first-child { - -webkit-border-radius: 6px 6px 0 0; - -moz-border-radius: 6px 6px 0 0; - border-radius: 6px 6px 0 0; -} - -.btn-group-vertical > .btn-large:last-child { - -webkit-border-radius: 0 0 6px 6px; - -moz-border-radius: 0 0 6px 6px; - border-radius: 0 0 6px 6px; -} - -.alert { - padding: 8px 35px 8px 14px; - margin-bottom: 20px; - text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); - background-color: #fcf8e3; - border: 1px solid #fbeed5; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; -} - -.alert, -.alert h4 { - color: #c09853; -} - -.alert h4 { - margin: 0; -} - -.alert .close { - position: relative; - top: -2px; - right: -21px; - line-height: 20px; -} - -.alert-success { - color: #468847; - background-color: #dff0d8; - border-color: #d6e9c6; -} - -.alert-success h4 { - color: #468847; -} - -.alert-danger, -.alert-error { - color: #b94a48; - background-color: #f2dede; - border-color: #eed3d7; -} - -.alert-danger h4, -.alert-error h4 { - color: #b94a48; -} - -.alert-info { - color: #3a87ad; - background-color: #d9edf7; - border-color: #bce8f1; -} - -.alert-info h4 { - color: #3a87ad; -} - -.alert-block { - padding-top: 14px; - padding-bottom: 14px; -} - -.alert-block > p, -.alert-block > ul { - margin-bottom: 0; -} - -.alert-block p + p { - margin-top: 5px; -} - -.nav { - margin-bottom: 20px; - margin-left: 0; - list-style: none; -} - -.nav > li > a { - display: block; -} - -.nav > li > a:hover { - text-decoration: none; - background-color: #eeeeee; -} - -.nav > li > a > img { - max-width: none; -} - -.nav > .pull-right { - float: right; -} - -.nav-header { - display: block; - padding: 3px 15px; - font-size: 11px; - font-weight: bold; - line-height: 20px; - color: #999999; - text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); - text-transform: uppercase; -} - -.nav li + .nav-header { - margin-top: 9px; -} - -.nav-list { - padding-right: 15px; - padding-left: 15px; - margin-bottom: 0; -} - -.nav-list > li > a, -.nav-list .nav-header { - margin-right: -15px; - margin-left: -15px; - text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); -} - -.nav-list > li > a { - padding: 3px 15px; -} - -.nav-list > .active > a, -.nav-list > .active > a:hover { - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2); - background-color: #0088cc; -} - -.nav-list [class^="icon-"], -.nav-list [class*=" icon-"] { - margin-right: 2px; -} - -.nav-list .divider { - *width: 100%; - height: 1px; - margin: 9px 1px; - *margin: -5px 0 5px; - overflow: hidden; - background-color: #e5e5e5; - border-bottom: 1px solid #ffffff; -} - -.nav-tabs, -.nav-pills { - *zoom: 1; -} - -.nav-tabs:before, -.nav-pills:before, -.nav-tabs:after, -.nav-pills:after { - display: table; - line-height: 0; - content: ""; -} - -.nav-tabs:after, -.nav-pills:after { - clear: both; -} - -.nav-tabs > li, -.nav-pills > li { - float: left; -} - -.nav-tabs > li > a, -.nav-pills > li > a { - padding-right: 12px; - padding-left: 12px; - margin-right: 2px; - line-height: 14px; -} - -.nav-tabs { - border-bottom: 1px solid #ddd; -} - -.nav-tabs > li { - margin-bottom: -1px; -} - -.nav-tabs > li > a { - padding-top: 8px; - padding-bottom: 8px; - line-height: 20px; - border: 1px solid transparent; - -webkit-border-radius: 4px 4px 0 0; - -moz-border-radius: 4px 4px 0 0; - border-radius: 4px 4px 0 0; -} - -.nav-tabs > li > a:hover { - border-color: #eeeeee #eeeeee #dddddd; -} - -.nav-tabs > .active > a, -.nav-tabs > .active > a:hover { - color: #555555; - cursor: default; - background-color: #ffffff; - border: 1px solid #ddd; - border-bottom-color: transparent; -} - -.nav-pills > li > a { - padding-top: 8px; - padding-bottom: 8px; - margin-top: 2px; - margin-bottom: 2px; - -webkit-border-radius: 5px; - -moz-border-radius: 5px; - border-radius: 5px; -} - -.nav-pills > .active > a, -.nav-pills > .active > a:hover { - color: #ffffff; - background-color: #0088cc; -} - -.nav-stacked > li { - float: none; -} - -.nav-stacked > li > a { - margin-right: 0; -} - -.nav-tabs.nav-stacked { - border-bottom: 0; -} - -.nav-tabs.nav-stacked > li > a { - border: 1px solid #ddd; - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; -} - -.nav-tabs.nav-stacked > li:first-child > a { - -webkit-border-top-right-radius: 4px; - border-top-right-radius: 4px; - -webkit-border-top-left-radius: 4px; - border-top-left-radius: 4px; - -moz-border-radius-topright: 4px; - -moz-border-radius-topleft: 4px; -} - -.nav-tabs.nav-stacked > li:last-child > a { - -webkit-border-bottom-right-radius: 4px; - border-bottom-right-radius: 4px; - -webkit-border-bottom-left-radius: 4px; - border-bottom-left-radius: 4px; - -moz-border-radius-bottomright: 4px; - -moz-border-radius-bottomleft: 4px; -} - -.nav-tabs.nav-stacked > li > a:hover { - z-index: 2; - border-color: #ddd; -} - -.nav-pills.nav-stacked > li > a { - margin-bottom: 3px; -} - -.nav-pills.nav-stacked > li:last-child > a { - margin-bottom: 1px; -} - -.nav-tabs .dropdown-menu { - -webkit-border-radius: 0 0 6px 6px; - -moz-border-radius: 0 0 6px 6px; - border-radius: 0 0 6px 6px; -} - -.nav-pills .dropdown-menu { - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; -} - -.nav .dropdown-toggle .caret { - margin-top: 6px; - border-top-color: #0088cc; - border-bottom-color: #0088cc; -} - -.nav .dropdown-toggle:hover .caret { - border-top-color: #005580; - border-bottom-color: #005580; -} - -/* move down carets for tabs */ - -.nav-tabs .dropdown-toggle .caret { - margin-top: 8px; -} - -.nav .active .dropdown-toggle .caret { - border-top-color: #fff; - border-bottom-color: #fff; -} - -.nav-tabs .active .dropdown-toggle .caret { - border-top-color: #555555; - border-bottom-color: #555555; -} - -.nav > .dropdown.active > a:hover { - cursor: pointer; -} - -.nav-tabs .open .dropdown-toggle, -.nav-pills .open .dropdown-toggle, -.nav > li.dropdown.open.active > a:hover { - color: #ffffff; - background-color: #999999; - border-color: #999999; -} - -.nav li.dropdown.open .caret, -.nav li.dropdown.open.active .caret, -.nav li.dropdown.open a:hover .caret { - border-top-color: #ffffff; - border-bottom-color: #ffffff; - opacity: 1; - filter: alpha(opacity=100); -} - -.tabs-stacked .open > a:hover { - border-color: #999999; -} - -.tabbable { - *zoom: 1; -} - -.tabbable:before, -.tabbable:after { - display: table; - line-height: 0; - content: ""; -} - -.tabbable:after { - clear: both; -} - -.tab-content { - overflow: auto; -} - -.tabs-below > .nav-tabs, -.tabs-right > .nav-tabs, -.tabs-left > .nav-tabs { - border-bottom: 0; -} - -.tab-content > .tab-pane, -.pill-content > .pill-pane { - display: none; -} - -.tab-content > .active, -.pill-content > .active { - display: block; -} - -.tabs-below > .nav-tabs { - border-top: 1px solid #ddd; -} - -.tabs-below > .nav-tabs > li { - margin-top: -1px; - margin-bottom: 0; -} - -.tabs-below > .nav-tabs > li > a { - -webkit-border-radius: 0 0 4px 4px; - -moz-border-radius: 0 0 4px 4px; - border-radius: 0 0 4px 4px; -} - -.tabs-below > .nav-tabs > li > a:hover { - border-top-color: #ddd; - border-bottom-color: transparent; -} - -.tabs-below > .nav-tabs > .active > a, -.tabs-below > .nav-tabs > .active > a:hover { - border-color: transparent #ddd #ddd #ddd; -} - -.tabs-left > .nav-tabs > li, -.tabs-right > .nav-tabs > li { - float: none; -} - -.tabs-left > .nav-tabs > li > a, -.tabs-right > .nav-tabs > li > a { - min-width: 74px; - margin-right: 0; - margin-bottom: 3px; -} - -.tabs-left > .nav-tabs { - float: left; - margin-right: 19px; - border-right: 1px solid #ddd; -} - -.tabs-left > .nav-tabs > li > a { - margin-right: -1px; - -webkit-border-radius: 4px 0 0 4px; - -moz-border-radius: 4px 0 0 4px; - border-radius: 4px 0 0 4px; -} - -.tabs-left > .nav-tabs > li > a:hover { - border-color: #eeeeee #dddddd #eeeeee #eeeeee; -} - -.tabs-left > .nav-tabs .active > a, -.tabs-left > .nav-tabs .active > a:hover { - border-color: #ddd transparent #ddd #ddd; - *border-right-color: #ffffff; -} - -.tabs-right > .nav-tabs { - float: right; - margin-left: 19px; - border-left: 1px solid #ddd; -} - -.tabs-right > .nav-tabs > li > a { - margin-left: -1px; - -webkit-border-radius: 0 4px 4px 0; - -moz-border-radius: 0 4px 4px 0; - border-radius: 0 4px 4px 0; -} - -.tabs-right > .nav-tabs > li > a:hover { - border-color: #eeeeee #eeeeee #eeeeee #dddddd; -} - -.tabs-right > .nav-tabs .active > a, -.tabs-right > .nav-tabs .active > a:hover { - border-color: #ddd #ddd #ddd transparent; - *border-left-color: #ffffff; -} - -.nav > .disabled > a { - color: #999999; -} - -.nav > .disabled > a:hover { - text-decoration: none; - cursor: default; - background-color: transparent; -} - -.navbar { - *position: relative; - *z-index: 2; - margin-bottom: 20px; - overflow: visible; -} - -.navbar-inner { - min-height: 40px; - padding-right: 20px; - padding-left: 20px; - background-color: #fafafa; - background-image: -moz-linear-gradient(top, #ffffff, #f2f2f2); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f2f2f2)); - background-image: -webkit-linear-gradient(top, #ffffff, #f2f2f2); - background-image: -o-linear-gradient(top, #ffffff, #f2f2f2); - background-image: linear-gradient(to bottom, #ffffff, #f2f2f2); - background-repeat: repeat-x; - border: 1px solid #d4d4d4; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff2f2f2', GradientType=0); - *zoom: 1; - -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); - -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); - box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); -} - -.navbar-inner:before, -.navbar-inner:after { - display: table; - line-height: 0; - content: ""; -} - -.navbar-inner:after { - clear: both; -} - -.navbar .container { - width: auto; -} - -.nav-collapse.collapse { - height: auto; - overflow: visible; -} - -.navbar .brand { - display: block; - float: left; - padding: 10px 20px 10px; - margin-left: -20px; - font-size: 20px; - font-weight: 200; - color: #777777; - text-shadow: 0 1px 0 #ffffff; -} - -.navbar .brand:hover { - text-decoration: none; -} - -.navbar-text { - margin-bottom: 0; - line-height: 40px; - color: #777777; -} - -.navbar-link { - color: #777777; -} - -.navbar-link:hover { - color: #333333; -} - -.navbar .divider-vertical { - height: 40px; - margin: 0 9px; - border-right: 1px solid #ffffff; - border-left: 1px solid #f2f2f2; -} - -.navbar .btn, -.navbar .btn-group { - margin-top: 5px; -} - -.navbar .btn-group .btn, -.navbar .input-prepend .btn, -.navbar .input-append .btn { - margin-top: 0; -} - -.navbar-form { - margin-bottom: 0; - *zoom: 1; -} - -.navbar-form:before, -.navbar-form:after { - display: table; - line-height: 0; - content: ""; -} - -.navbar-form:after { - clear: both; -} - -.navbar-form input, -.navbar-form select, -.navbar-form .radio, -.navbar-form .checkbox { - margin-top: 5px; -} - -.navbar-form input, -.navbar-form select, -.navbar-form .btn { - display: inline-block; - margin-bottom: 0; -} - -.navbar-form input[type="image"], -.navbar-form input[type="checkbox"], -.navbar-form input[type="radio"] { - margin-top: 3px; -} - -.navbar-form .input-append, -.navbar-form .input-prepend { - margin-top: 5px; - white-space: nowrap; -} - -.navbar-form .input-append input, -.navbar-form .input-prepend input { - margin-top: 0; -} - -.navbar-search { - position: relative; - float: left; - margin-top: 5px; - margin-bottom: 0; -} - -.navbar-search .search-query { - padding: 4px 14px; - margin-bottom: 0; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 13px; - font-weight: normal; - line-height: 1; - -webkit-border-radius: 15px; - -moz-border-radius: 15px; - border-radius: 15px; -} - -.navbar-static-top { - position: static; - margin-bottom: 0; -} - -.navbar-static-top .navbar-inner { - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; -} - -.navbar-fixed-top, -.navbar-fixed-bottom { - position: fixed; - right: 0; - left: 0; - z-index: 1030; - margin-bottom: 0; -} - -.navbar-fixed-top .navbar-inner, -.navbar-static-top .navbar-inner { - border-width: 0 0 1px; -} - -.navbar-fixed-bottom .navbar-inner { - border-width: 1px 0 0; -} - -.navbar-fixed-top .navbar-inner, -.navbar-fixed-bottom .navbar-inner { - padding-right: 0; - padding-left: 0; - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; -} - -.navbar-static-top .container, -.navbar-fixed-top .container, -.navbar-fixed-bottom .container { - width: 940px; -} - -.navbar-fixed-top { - top: 0; -} - -.navbar-fixed-top .navbar-inner, -.navbar-static-top .navbar-inner { - -webkit-box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1); - -moz-box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1); - box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1); -} - -.navbar-fixed-bottom { - bottom: 0; -} - -.navbar-fixed-bottom .navbar-inner { - -webkit-box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1); - -moz-box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1); - box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1); -} - -.navbar .nav { - position: relative; - left: 0; - display: block; - float: left; - margin: 0 10px 0 0; -} - -.navbar .nav.pull-right { - float: right; - margin-right: 0; -} - -.navbar .nav > li { - float: left; -} - -.navbar .nav > li > a { - float: none; - padding: 10px 15px 10px; - color: #777777; - text-decoration: none; - text-shadow: 0 1px 0 #ffffff; -} - -.navbar .nav .dropdown-toggle .caret { - margin-top: 8px; -} - -.navbar .nav > li > a:focus, -.navbar .nav > li > a:hover { - color: #333333; - text-decoration: none; - background-color: transparent; -} - -.navbar .nav > .active > a, -.navbar .nav > .active > a:hover, -.navbar .nav > .active > a:focus { - color: #555555; - text-decoration: none; - background-color: #e5e5e5; - -webkit-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125); - -moz-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125); - box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125); -} - -.navbar .btn-navbar { - display: none; - float: right; - padding: 7px 10px; - margin-right: 5px; - margin-left: 5px; - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #ededed; - *background-color: #e5e5e5; - background-image: -moz-linear-gradient(top, #f2f2f2, #e5e5e5); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f2f2f2), to(#e5e5e5)); - background-image: -webkit-linear-gradient(top, #f2f2f2, #e5e5e5); - background-image: -o-linear-gradient(top, #f2f2f2, #e5e5e5); - background-image: linear-gradient(to bottom, #f2f2f2, #e5e5e5); - background-repeat: repeat-x; - border-color: #e5e5e5 #e5e5e5 #bfbfbf; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffe5e5e5', GradientType=0); - filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); - -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075); - -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075); -} - -.navbar .btn-navbar:hover, -.navbar .btn-navbar:active, -.navbar .btn-navbar.active, -.navbar .btn-navbar.disabled, -.navbar .btn-navbar[disabled] { - color: #ffffff; - background-color: #e5e5e5; - *background-color: #d9d9d9; -} - -.navbar .btn-navbar:active, -.navbar .btn-navbar.active { - background-color: #cccccc \9; -} - -.navbar .btn-navbar .icon-bar { - display: block; - width: 18px; - height: 2px; - background-color: #f5f5f5; - -webkit-border-radius: 1px; - -moz-border-radius: 1px; - border-radius: 1px; - -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); - -moz-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); - box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); -} - -.btn-navbar .icon-bar + .icon-bar { - margin-top: 3px; -} - -.navbar .nav > li > .dropdown-menu:before { - position: absolute; - top: -7px; - left: 9px; - display: inline-block; - border-right: 7px solid transparent; - border-bottom: 7px solid #ccc; - border-left: 7px solid transparent; - border-bottom-color: rgba(0, 0, 0, 0.2); - content: ''; -} - -.navbar .nav > li > .dropdown-menu:after { - position: absolute; - top: -6px; - left: 10px; - display: inline-block; - border-right: 6px solid transparent; - border-bottom: 6px solid #ffffff; - border-left: 6px solid transparent; - content: ''; -} - -.navbar-fixed-bottom .nav > li > .dropdown-menu:before { - top: auto; - bottom: -7px; - border-top: 7px solid #ccc; - border-bottom: 0; - border-top-color: rgba(0, 0, 0, 0.2); -} - -.navbar-fixed-bottom .nav > li > .dropdown-menu:after { - top: auto; - bottom: -6px; - border-top: 6px solid #ffffff; - border-bottom: 0; -} - -.navbar .nav li.dropdown > a:hover .caret { - border-top-color: #555555; - border-bottom-color: #555555; -} - -.navbar .nav li.dropdown.open > .dropdown-toggle, -.navbar .nav li.dropdown.active > .dropdown-toggle, -.navbar .nav li.dropdown.open.active > .dropdown-toggle { - color: #555555; - background-color: #e5e5e5; -} - -.navbar .nav li.dropdown > .dropdown-toggle .caret { - border-top-color: #777777; - border-bottom-color: #777777; -} - -.navbar .nav li.dropdown.open > .dropdown-toggle .caret, -.navbar .nav li.dropdown.active > .dropdown-toggle .caret, -.navbar .nav li.dropdown.open.active > .dropdown-toggle .caret { - border-top-color: #555555; - border-bottom-color: #555555; -} - -.navbar .pull-right > li > .dropdown-menu, -.navbar .nav > li > .dropdown-menu.pull-right { - right: 0; - left: auto; -} - -.navbar .pull-right > li > .dropdown-menu:before, -.navbar .nav > li > .dropdown-menu.pull-right:before { - right: 12px; - left: auto; -} - -.navbar .pull-right > li > .dropdown-menu:after, -.navbar .nav > li > .dropdown-menu.pull-right:after { - right: 13px; - left: auto; -} - -.navbar .pull-right > li > .dropdown-menu .dropdown-menu, -.navbar .nav > li > .dropdown-menu.pull-right .dropdown-menu { - right: 100%; - left: auto; - margin-right: -1px; - margin-left: 0; - -webkit-border-radius: 6px 0 6px 6px; - -moz-border-radius: 6px 0 6px 6px; - border-radius: 6px 0 6px 6px; -} - -.navbar-inverse .navbar-inner { - background-color: #1b1b1b; - background-image: -moz-linear-gradient(top, #222222, #111111); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#222222), to(#111111)); - background-image: -webkit-linear-gradient(top, #222222, #111111); - background-image: -o-linear-gradient(top, #222222, #111111); - background-image: linear-gradient(to bottom, #222222, #111111); - background-repeat: repeat-x; - border-color: #252525; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff111111', GradientType=0); -} - -.navbar-inverse .brand, -.navbar-inverse .nav > li > a { - color: #999999; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); -} - -.navbar-inverse .brand:hover, -.navbar-inverse .nav > li > a:hover { - color: #ffffff; -} - -.navbar-inverse .brand { - color: #999999; -} - -.navbar-inverse .navbar-text { - color: #999999; -} - -.navbar-inverse .nav > li > a:focus, -.navbar-inverse .nav > li > a:hover { - color: #ffffff; - background-color: transparent; -} - -.navbar-inverse .nav .active > a, -.navbar-inverse .nav .active > a:hover, -.navbar-inverse .nav .active > a:focus { - color: #ffffff; - background-color: #111111; -} - -.navbar-inverse .navbar-link { - color: #999999; -} - -.navbar-inverse .navbar-link:hover { - color: #ffffff; -} - -.navbar-inverse .divider-vertical { - border-right-color: #222222; - border-left-color: #111111; -} - -.navbar-inverse .nav li.dropdown.open > .dropdown-toggle, -.navbar-inverse .nav li.dropdown.active > .dropdown-toggle, -.navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle { - color: #ffffff; - background-color: #111111; -} - -.navbar-inverse .nav li.dropdown > a:hover .caret { - border-top-color: #ffffff; - border-bottom-color: #ffffff; -} - -.navbar-inverse .nav li.dropdown > .dropdown-toggle .caret { - border-top-color: #999999; - border-bottom-color: #999999; -} - -.navbar-inverse .nav li.dropdown.open > .dropdown-toggle .caret, -.navbar-inverse .nav li.dropdown.active > .dropdown-toggle .caret, -.navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle .caret { - border-top-color: #ffffff; - border-bottom-color: #ffffff; -} - -.navbar-inverse .navbar-search .search-query { - color: #ffffff; - background-color: #515151; - border-color: #111111; - -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15); - -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15); - box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15); - -webkit-transition: none; - -moz-transition: none; - -o-transition: none; - transition: none; -} - -.navbar-inverse .navbar-search .search-query:-moz-placeholder { - color: #cccccc; -} - -.navbar-inverse .navbar-search .search-query:-ms-input-placeholder { - color: #cccccc; -} - -.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder { - color: #cccccc; -} - -.navbar-inverse .navbar-search .search-query:focus, -.navbar-inverse .navbar-search .search-query.focused { - padding: 5px 15px; - color: #333333; - text-shadow: 0 1px 0 #ffffff; - background-color: #ffffff; - border: 0; - outline: 0; - -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); - -moz-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); - box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); -} - -.navbar-inverse .btn-navbar { - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #0e0e0e; - *background-color: #040404; - background-image: -moz-linear-gradient(top, #151515, #040404); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#151515), to(#040404)); - background-image: -webkit-linear-gradient(top, #151515, #040404); - background-image: -o-linear-gradient(top, #151515, #040404); - background-image: linear-gradient(to bottom, #151515, #040404); - background-repeat: repeat-x; - border-color: #040404 #040404 #000000; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff151515', endColorstr='#ff040404', GradientType=0); - filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); -} - -.navbar-inverse .btn-navbar:hover, -.navbar-inverse .btn-navbar:active, -.navbar-inverse .btn-navbar.active, -.navbar-inverse .btn-navbar.disabled, -.navbar-inverse .btn-navbar[disabled] { - color: #ffffff; - background-color: #040404; - *background-color: #000000; -} - -.navbar-inverse .btn-navbar:active, -.navbar-inverse .btn-navbar.active { - background-color: #000000 \9; -} - -.breadcrumb { - padding: 8px 15px; - margin: 0 0 20px; - list-style: none; - background-color: #f5f5f5; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; -} - -.breadcrumb > li { - display: inline-block; - *display: inline; - text-shadow: 0 1px 0 #ffffff; - *zoom: 1; -} - -.breadcrumb > li > .divider { - padding: 0 5px; - color: #ccc; -} - -.breadcrumb > .active { - color: #999999; -} - -.pagination { - margin: 20px 0; -} - -.pagination ul { - display: inline-block; - *display: inline; - margin-bottom: 0; - margin-left: 0; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - *zoom: 1; - -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); - -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); -} - -.pagination ul > li { - display: inline; -} - -.pagination ul > li > a, -.pagination ul > li > span { - float: left; - padding: 4px 12px; - line-height: 20px; - text-decoration: none; - background-color: #ffffff; - border: 1px solid #dddddd; - border-left-width: 0; -} - -.pagination ul > li > a:hover, -.pagination ul > .active > a, -.pagination ul > .active > span { - background-color: #f5f5f5; -} - -.pagination ul > .active > a, -.pagination ul > .active > span { - color: #999999; - cursor: default; -} - -.pagination ul > .disabled > span, -.pagination ul > .disabled > a, -.pagination ul > .disabled > a:hover { - color: #999999; - cursor: default; - background-color: transparent; -} - -.pagination ul > li:first-child > a, -.pagination ul > li:first-child > span { - border-left-width: 1px; - -webkit-border-bottom-left-radius: 4px; - border-bottom-left-radius: 4px; - -webkit-border-top-left-radius: 4px; - border-top-left-radius: 4px; - -moz-border-radius-bottomleft: 4px; - -moz-border-radius-topleft: 4px; -} - -.pagination ul > li:last-child > a, -.pagination ul > li:last-child > span { - -webkit-border-top-right-radius: 4px; - border-top-right-radius: 4px; - -webkit-border-bottom-right-radius: 4px; - border-bottom-right-radius: 4px; - -moz-border-radius-topright: 4px; - -moz-border-radius-bottomright: 4px; -} - -.pagination-centered { - text-align: center; -} - -.pagination-right { - text-align: right; -} - -.pagination-large ul > li > a, -.pagination-large ul > li > span { - padding: 11px 19px; - font-size: 17.5px; -} - -.pagination-large ul > li:first-child > a, -.pagination-large ul > li:first-child > span { - -webkit-border-bottom-left-radius: 6px; - border-bottom-left-radius: 6px; - -webkit-border-top-left-radius: 6px; - border-top-left-radius: 6px; - -moz-border-radius-bottomleft: 6px; - -moz-border-radius-topleft: 6px; -} - -.pagination-large ul > li:last-child > a, -.pagination-large ul > li:last-child > span { - -webkit-border-top-right-radius: 6px; - border-top-right-radius: 6px; - -webkit-border-bottom-right-radius: 6px; - border-bottom-right-radius: 6px; - -moz-border-radius-topright: 6px; - -moz-border-radius-bottomright: 6px; -} - -.pagination-mini ul > li:first-child > a, -.pagination-small ul > li:first-child > a, -.pagination-mini ul > li:first-child > span, -.pagination-small ul > li:first-child > span { - -webkit-border-bottom-left-radius: 3px; - border-bottom-left-radius: 3px; - -webkit-border-top-left-radius: 3px; - border-top-left-radius: 3px; - -moz-border-radius-bottomleft: 3px; - -moz-border-radius-topleft: 3px; -} - -.pagination-mini ul > li:last-child > a, -.pagination-small ul > li:last-child > a, -.pagination-mini ul > li:last-child > span, -.pagination-small ul > li:last-child > span { - -webkit-border-top-right-radius: 3px; - border-top-right-radius: 3px; - -webkit-border-bottom-right-radius: 3px; - border-bottom-right-radius: 3px; - -moz-border-radius-topright: 3px; - -moz-border-radius-bottomright: 3px; -} - -.pagination-small ul > li > a, -.pagination-small ul > li > span { - padding: 2px 10px; - font-size: 11.9px; -} - -.pagination-mini ul > li > a, -.pagination-mini ul > li > span { - padding: 0 6px; - font-size: 10.5px; -} - -.pager { - margin: 20px 0; - text-align: center; - list-style: none; - *zoom: 1; -} - -.pager:before, -.pager:after { - display: table; - line-height: 0; - content: ""; -} - -.pager:after { - clear: both; -} - -.pager li { - display: inline; -} - -.pager li > a, -.pager li > span { - display: inline-block; - padding: 5px 14px; - background-color: #fff; - border: 1px solid #ddd; - -webkit-border-radius: 15px; - -moz-border-radius: 15px; - border-radius: 15px; -} - -.pager li > a:hover { - text-decoration: none; - background-color: #f5f5f5; -} - -.pager .next > a, -.pager .next > span { - float: right; -} - -.pager .previous > a, -.pager .previous > span { - float: left; -} - -.pager .disabled > a, -.pager .disabled > a:hover, -.pager .disabled > span { - color: #999999; - cursor: default; - background-color: #fff; -} - -.modal-backdrop { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 1040; - background-color: #000000; -} - -.modal-backdrop.fade { - opacity: 0; -} - -.modal-backdrop, -.modal-backdrop.fade.in { - opacity: 0.8; - filter: alpha(opacity=80); -} - -.modal { - position: fixed; - top: 10%; - left: 50%; - z-index: 1050; - width: 560px; - margin-left: -280px; - background-color: #ffffff; - border: 1px solid #999; - border: 1px solid rgba(0, 0, 0, 0.3); - *border: 1px solid #999; - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; - outline: none; - -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); - -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); - box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); - -webkit-background-clip: padding-box; - -moz-background-clip: padding-box; - background-clip: padding-box; -} - -.modal.fade { - top: -25%; - -webkit-transition: opacity 0.3s linear, top 0.3s ease-out; - -moz-transition: opacity 0.3s linear, top 0.3s ease-out; - -o-transition: opacity 0.3s linear, top 0.3s ease-out; - transition: opacity 0.3s linear, top 0.3s ease-out; -} - -.modal.fade.in { - top: 10%; -} - -.modal-header { - padding: 9px 15px; - border-bottom: 1px solid #eee; -} - -.modal-header .close { - margin-top: 2px; -} - -.modal-header h3 { - margin: 0; - line-height: 30px; -} - -.modal-body { - position: relative; - max-height: 400px; - padding: 15px; - overflow-y: auto; -} - -.modal-form { - margin-bottom: 0; -} - -.modal-footer { - padding: 14px 15px 15px; - margin-bottom: 0; - text-align: right; - background-color: #f5f5f5; - border-top: 1px solid #ddd; - -webkit-border-radius: 0 0 6px 6px; - -moz-border-radius: 0 0 6px 6px; - border-radius: 0 0 6px 6px; - *zoom: 1; - -webkit-box-shadow: inset 0 1px 0 #ffffff; - -moz-box-shadow: inset 0 1px 0 #ffffff; - box-shadow: inset 0 1px 0 #ffffff; -} - -.modal-footer:before, -.modal-footer:after { - display: table; - line-height: 0; - content: ""; -} - -.modal-footer:after { - clear: both; -} - -.modal-footer .btn + .btn { - margin-bottom: 0; - margin-left: 5px; -} - -.modal-footer .btn-group .btn + .btn { - margin-left: -1px; -} - -.modal-footer .btn-block + .btn-block { - margin-left: 0; -} - -.tooltip { - position: absolute; - z-index: 1030; - display: block; - padding: 5px; - font-size: 11px; - opacity: 0; - filter: alpha(opacity=0); - visibility: visible; -} - -.tooltip.in { - opacity: 0.8; - filter: alpha(opacity=80); -} - -.tooltip.top { - margin-top: -3px; -} - -.tooltip.right { - margin-left: 3px; -} - -.tooltip.bottom { - margin-top: 3px; -} - -.tooltip.left { - margin-left: -3px; -} - -.tooltip-inner { - max-width: 200px; - padding: 3px 8px; - color: #ffffff; - text-align: center; - text-decoration: none; - background-color: #000000; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; -} - -.tooltip-arrow { - position: absolute; - width: 0; - height: 0; - border-color: transparent; - border-style: solid; -} - -.tooltip.top .tooltip-arrow { - bottom: 0; - left: 50%; - margin-left: -5px; - border-top-color: #000000; - border-width: 5px 5px 0; -} - -.tooltip.right .tooltip-arrow { - top: 50%; - left: 0; - margin-top: -5px; - border-right-color: #000000; - border-width: 5px 5px 5px 0; -} - -.tooltip.left .tooltip-arrow { - top: 50%; - right: 0; - margin-top: -5px; - border-left-color: #000000; - border-width: 5px 0 5px 5px; -} - -.tooltip.bottom .tooltip-arrow { - top: 0; - left: 50%; - margin-left: -5px; - border-bottom-color: #000000; - border-width: 0 5px 5px; -} - -.popover { - position: absolute; - top: 0; - left: 0; - z-index: 1010; - display: none; - width: 236px; - padding: 1px; - text-align: left; - white-space: normal; - background-color: #ffffff; - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, 0.2); - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; - -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); - -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); - box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); - -webkit-background-clip: padding-box; - -moz-background-clip: padding; - background-clip: padding-box; -} - -.popover.top { - margin-top: -10px; -} - -.popover.right { - margin-left: 10px; -} - -.popover.bottom { - margin-top: 10px; -} - -.popover.left { - margin-left: -10px; -} - -.popover-title { - padding: 8px 14px; - margin: 0; - font-size: 14px; - font-weight: normal; - line-height: 18px; - background-color: #f7f7f7; - border-bottom: 1px solid #ebebeb; - -webkit-border-radius: 5px 5px 0 0; - -moz-border-radius: 5px 5px 0 0; - border-radius: 5px 5px 0 0; -} - -.popover-content { - padding: 9px 14px; -} - -.popover .arrow, -.popover .arrow:after { - position: absolute; - display: block; - width: 0; - height: 0; - border-color: transparent; - border-style: solid; -} - -.popover .arrow { - border-width: 11px; -} - -.popover .arrow:after { - border-width: 10px; - content: ""; -} - -.popover.top .arrow { - bottom: -11px; - left: 50%; - margin-left: -11px; - border-top-color: #999; - border-top-color: rgba(0, 0, 0, 0.25); - border-bottom-width: 0; -} - -.popover.top .arrow:after { - bottom: 1px; - margin-left: -10px; - border-top-color: #ffffff; - border-bottom-width: 0; -} - -.popover.right .arrow { - top: 50%; - left: -11px; - margin-top: -11px; - border-right-color: #999; - border-right-color: rgba(0, 0, 0, 0.25); - border-left-width: 0; -} - -.popover.right .arrow:after { - bottom: -10px; - left: 1px; - border-right-color: #ffffff; - border-left-width: 0; -} - -.popover.bottom .arrow { - top: -11px; - left: 50%; - margin-left: -11px; - border-bottom-color: #999; - border-bottom-color: rgba(0, 0, 0, 0.25); - border-top-width: 0; -} - -.popover.bottom .arrow:after { - top: 1px; - margin-left: -10px; - border-bottom-color: #ffffff; - border-top-width: 0; -} - -.popover.left .arrow { - top: 50%; - right: -11px; - margin-top: -11px; - border-left-color: #999; - border-left-color: rgba(0, 0, 0, 0.25); - border-right-width: 0; -} - -.popover.left .arrow:after { - right: 1px; - bottom: -10px; - border-left-color: #ffffff; - border-right-width: 0; -} - -.thumbnails { - margin-left: -20px; - list-style: none; - *zoom: 1; -} - -.thumbnails:before, -.thumbnails:after { - display: table; - line-height: 0; - content: ""; -} - -.thumbnails:after { - clear: both; -} - -.row-fluid .thumbnails { - margin-left: 0; -} - -.thumbnails > li { - float: left; - margin-bottom: 20px; - margin-left: 20px; -} - -.thumbnail { - display: block; - padding: 4px; - line-height: 20px; - border: 1px solid #ddd; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055); - -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055); - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055); - -webkit-transition: all 0.2s ease-in-out; - -moz-transition: all 0.2s ease-in-out; - -o-transition: all 0.2s ease-in-out; - transition: all 0.2s ease-in-out; -} - -a.thumbnail:hover { - border-color: #0088cc; - -webkit-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); - -moz-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); - box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); -} - -.thumbnail > img { - display: block; - max-width: 100%; - margin-right: auto; - margin-left: auto; -} - -.thumbnail .caption { - padding: 9px; - color: #555555; -} - -.media, -.media-body { - overflow: hidden; - *overflow: visible; - zoom: 1; -} - -.media, -.media .media { - margin-top: 15px; -} - -.media:first-child { - margin-top: 0; -} - -.media-object { - display: block; -} - -.media-heading { - margin: 0 0 5px; -} - -.media .pull-left { - margin-right: 10px; -} - -.media .pull-right { - margin-left: 10px; -} - -.media-list { - margin-left: 0; - list-style: none; -} - -.label, -.badge { - display: inline-block; - padding: 2px 4px; - font-size: 11.844px; - font-weight: bold; - line-height: 14px; - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - white-space: nowrap; - vertical-align: baseline; - background-color: #999999; -} - -.label { - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; -} - -.badge { - padding-right: 9px; - padding-left: 9px; - -webkit-border-radius: 9px; - -moz-border-radius: 9px; - border-radius: 9px; -} - -.label:empty, -.badge:empty { - display: none; -} - -a.label:hover, -a.badge:hover { - color: #ffffff; - text-decoration: none; - cursor: pointer; -} - -.label-important, -.badge-important { - background-color: #b94a48; -} - -.label-important[href], -.badge-important[href] { - background-color: #953b39; -} - -.label-warning, -.badge-warning { - background-color: #f89406; -} - -.label-warning[href], -.badge-warning[href] { - background-color: #c67605; -} - -.label-success, -.badge-success { - background-color: #468847; -} - -.label-success[href], -.badge-success[href] { - background-color: #356635; -} - -.label-info, -.badge-info { - background-color: #3a87ad; -} - -.label-info[href], -.badge-info[href] { - background-color: #2d6987; -} - -.label-inverse, -.badge-inverse { - background-color: #333333; -} - -.label-inverse[href], -.badge-inverse[href] { - background-color: #1a1a1a; -} - -.btn .label, -.btn .badge { - position: relative; - top: -1px; -} - -.btn-mini .label, -.btn-mini .badge { - top: 0; -} - -@-webkit-keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} - -@-moz-keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} - -@-ms-keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} - -@-o-keyframes progress-bar-stripes { - from { - background-position: 0 0; - } - to { - background-position: 40px 0; - } -} - -@keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} - -.progress { - height: 20px; - margin-bottom: 20px; - overflow: hidden; - background-color: #f7f7f7; - background-image: -moz-linear-gradient(top, #f5f5f5, #f9f9f9); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9)); - background-image: -webkit-linear-gradient(top, #f5f5f5, #f9f9f9); - background-image: -o-linear-gradient(top, #f5f5f5, #f9f9f9); - background-image: linear-gradient(to bottom, #f5f5f5, #f9f9f9); - background-repeat: repeat-x; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0); - -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); - -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); - box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); -} - -.progress .bar { - float: left; - width: 0; - height: 100%; - font-size: 12px; - color: #ffffff; - text-align: center; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #0e90d2; - background-image: -moz-linear-gradient(top, #149bdf, #0480be); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be)); - background-image: -webkit-linear-gradient(top, #149bdf, #0480be); - background-image: -o-linear-gradient(top, #149bdf, #0480be); - background-image: linear-gradient(to bottom, #149bdf, #0480be); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf', endColorstr='#ff0480be', GradientType=0); - -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); - -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); - box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - -webkit-transition: width 0.6s ease; - -moz-transition: width 0.6s ease; - -o-transition: width 0.6s ease; - transition: width 0.6s ease; -} - -.progress .bar + .bar { - -webkit-box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15); - -moz-box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15); - box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15); -} - -.progress-striped .bar { - background-color: #149bdf; - background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - -webkit-background-size: 40px 40px; - -moz-background-size: 40px 40px; - -o-background-size: 40px 40px; - background-size: 40px 40px; -} - -.progress.active .bar { - -webkit-animation: progress-bar-stripes 2s linear infinite; - -moz-animation: progress-bar-stripes 2s linear infinite; - -ms-animation: progress-bar-stripes 2s linear infinite; - -o-animation: progress-bar-stripes 2s linear infinite; - animation: progress-bar-stripes 2s linear infinite; -} - -.progress-danger .bar, -.progress .bar-danger { - background-color: #dd514c; - background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35)); - background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35); - background-image: -o-linear-gradient(top, #ee5f5b, #c43c35); - background-image: linear-gradient(to bottom, #ee5f5b, #c43c35); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffc43c35', GradientType=0); -} - -.progress-danger.progress-striped .bar, -.progress-striped .bar-danger { - background-color: #ee5f5b; - background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); -} - -.progress-success .bar, -.progress .bar-success { - background-color: #5eb95e; - background-image: -moz-linear-gradient(top, #62c462, #57a957); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957)); - background-image: -webkit-linear-gradient(top, #62c462, #57a957); - background-image: -o-linear-gradient(top, #62c462, #57a957); - background-image: linear-gradient(to bottom, #62c462, #57a957); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff57a957', GradientType=0); -} - -.progress-success.progress-striped .bar, -.progress-striped .bar-success { - background-color: #62c462; - background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); -} - -.progress-info .bar, -.progress .bar-info { - background-color: #4bb1cf; - background-image: -moz-linear-gradient(top, #5bc0de, #339bb9); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9)); - background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9); - background-image: -o-linear-gradient(top, #5bc0de, #339bb9); - background-image: linear-gradient(to bottom, #5bc0de, #339bb9); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff339bb9', GradientType=0); -} - -.progress-info.progress-striped .bar, -.progress-striped .bar-info { - background-color: #5bc0de; - background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); -} - -.progress-warning .bar, -.progress .bar-warning { - background-color: #faa732; - background-image: -moz-linear-gradient(top, #fbb450, #f89406); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406)); - background-image: -webkit-linear-gradient(top, #fbb450, #f89406); - background-image: -o-linear-gradient(top, #fbb450, #f89406); - background-image: linear-gradient(to bottom, #fbb450, #f89406); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0); -} - -.progress-warning.progress-striped .bar, -.progress-striped .bar-warning { - background-color: #fbb450; - background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); -} - -.accordion { - margin-bottom: 20px; -} - -.accordion-group { - margin-bottom: 2px; - border: 1px solid #e5e5e5; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; -} - -.accordion-heading { - border-bottom: 0; -} - -.accordion-heading .accordion-toggle { - display: block; - padding: 8px 15px; -} - -.accordion-toggle { - cursor: pointer; -} - -.accordion-inner { - padding: 9px 15px; - border-top: 1px solid #e5e5e5; -} - -.carousel { - position: relative; - margin-bottom: 20px; - line-height: 1; -} - -.carousel-inner { - position: relative; - width: 100%; - overflow: hidden; -} - -.carousel-inner > .item { - position: relative; - display: none; - -webkit-transition: 0.6s ease-in-out left; - -moz-transition: 0.6s ease-in-out left; - -o-transition: 0.6s ease-in-out left; - transition: 0.6s ease-in-out left; -} - -.carousel-inner > .item > img { - display: block; - line-height: 1; -} - -.carousel-inner > .active, -.carousel-inner > .next, -.carousel-inner > .prev { - display: block; -} - -.carousel-inner > .active { - left: 0; -} - -.carousel-inner > .next, -.carousel-inner > .prev { - position: absolute; - top: 0; - width: 100%; -} - -.carousel-inner > .next { - left: 100%; -} - -.carousel-inner > .prev { - left: -100%; -} - -.carousel-inner > .next.left, -.carousel-inner > .prev.right { - left: 0; -} - -.carousel-inner > .active.left { - left: -100%; -} - -.carousel-inner > .active.right { - left: 100%; -} - -.carousel-control { - position: absolute; - top: 40%; - left: 15px; - width: 40px; - height: 40px; - margin-top: -20px; - font-size: 60px; - font-weight: 100; - line-height: 30px; - color: #ffffff; - text-align: center; - background: #222222; - border: 3px solid #ffffff; - -webkit-border-radius: 23px; - -moz-border-radius: 23px; - border-radius: 23px; - opacity: 0.5; - filter: alpha(opacity=50); -} - -.carousel-control.right { - right: 15px; - left: auto; -} - -.carousel-control:hover { - color: #ffffff; - text-decoration: none; - opacity: 0.9; - filter: alpha(opacity=90); -} - -.carousel-caption { - position: absolute; - right: 0; - bottom: 0; - left: 0; - padding: 15px; - background: #333333; - background: rgba(0, 0, 0, 0.75); -} - -.carousel-caption h4, -.carousel-caption p { - line-height: 20px; - color: #ffffff; -} - -.carousel-caption h4 { - margin: 0 0 5px; -} - -.carousel-caption p { - margin-bottom: 0; -} - -.hero-unit { - padding: 60px; - margin-bottom: 30px; - font-size: 18px; - font-weight: 200; - line-height: 30px; - color: inherit; - background-color: #eeeeee; - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; -} - -.hero-unit h1 { - margin-bottom: 0; - font-size: 60px; - line-height: 1; - letter-spacing: -1px; - color: inherit; -} - -.hero-unit li { - line-height: 30px; -} - -.pull-right { - float: right; -} - -.pull-left { - float: left; -} - -.hide { - display: none; -} - -.show { - display: block; -} - -.invisible { - visibility: hidden; -} - -.affix { - position: fixed; -} diff --git a/public/css/805-bootstrap-responsive.css b/public/css/805-bootstrap-responsive.css deleted file mode 100644 index a3352d774..000000000 --- a/public/css/805-bootstrap-responsive.css +++ /dev/null @@ -1,1092 +0,0 @@ -/*! - * Bootstrap Responsive v2.2.2 - * - * Copyright 2012 Twitter, Inc - * Licensed under the Apache License v2.0 - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Designed and built with all the love in the world @twitter by @mdo and @fat. - */ - -@-ms-viewport { - width: device-width; -} - -.clearfix { - *zoom: 1; -} - -.clearfix:before, -.clearfix:after { - display: table; - line-height: 0; - content: ""; -} - -.clearfix:after { - clear: both; -} - -.hide-text { - font: 0/0 a; - color: transparent; - text-shadow: none; - background-color: transparent; - border: 0; -} - -.input-block-level { - display: block; - width: 100%; - min-height: 30px; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} - -.hidden { - display: none; - visibility: hidden; -} - -.visible-phone { - display: none !important; -} - -.visible-tablet { - display: none !important; -} - -.hidden-desktop { - display: none !important; -} - -.visible-desktop { - display: inherit !important; -} - -@media (min-width: 768px) and (max-width: 979px) { - .hidden-desktop { - display: inherit !important; - } - .visible-desktop { - display: none !important ; - } - .visible-tablet { - display: inherit !important; - } - .hidden-tablet { - display: none !important; - } -} - -@media (max-width: 767px) { - .hidden-desktop { - display: inherit !important; - } - .visible-desktop { - display: none !important; - } - .visible-phone { - display: inherit !important; - } - .hidden-phone { - display: none !important; - } -} - -@media (min-width: 1200px) { - .row { - margin-left: -30px; - *zoom: 1; - } - .row:before, - .row:after { - display: table; - line-height: 0; - content: ""; - } - .row:after { - clear: both; - } - [class*="span"] { - float: left; - min-height: 1px; - margin-left: 30px; - } - .container, - .navbar-static-top .container, - .navbar-fixed-top .container, - .navbar-fixed-bottom .container { - width: 1170px; - } - .span12 { - width: 1170px; - } - .span11 { - width: 1070px; - } - .span10 { - width: 970px; - } - .span9 { - width: 870px; - } - .span8 { - width: 770px; - } - .span7 { - width: 670px; - } - .span6 { - width: 570px; - } - .span5 { - width: 470px; - } - .span4 { - width: 370px; - } - .span3 { - width: 270px; - } - .span2 { - width: 170px; - } - .span1 { - width: 70px; - } - .offset12 { - margin-left: 1230px; - } - .offset11 { - margin-left: 1130px; - } - .offset10 { - margin-left: 1030px; - } - .offset9 { - margin-left: 930px; - } - .offset8 { - margin-left: 830px; - } - .offset7 { - margin-left: 730px; - } - .offset6 { - margin-left: 630px; - } - .offset5 { - margin-left: 530px; - } - .offset4 { - margin-left: 430px; - } - .offset3 { - margin-left: 330px; - } - .offset2 { - margin-left: 230px; - } - .offset1 { - margin-left: 130px; - } - .row-fluid { - width: 100%; - *zoom: 1; - } - .row-fluid:before, - .row-fluid:after { - display: table; - line-height: 0; - content: ""; - } - .row-fluid:after { - clear: both; - } - .row-fluid [class*="span"] { - display: block; - float: left; - width: 100%; - min-height: 30px; - margin-left: 2.564102564102564%; - *margin-left: 2.5109110747408616%; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - } - .row-fluid [class*="span"]:first-child { - margin-left: 0; - } - .row-fluid .controls-row [class*="span"] + [class*="span"] { - margin-left: 2.564102564102564%; - } - .row-fluid .span12 { - width: 100%; - *width: 99.94680851063829%; - } - .row-fluid .span11 { - width: 91.45299145299145%; - *width: 91.39979996362975%; - } - .row-fluid .span10 { - width: 82.90598290598291%; - *width: 82.8527914166212%; - } - .row-fluid .span9 { - width: 74.35897435897436%; - *width: 74.30578286961266%; - } - .row-fluid .span8 { - width: 65.81196581196582%; - *width: 65.75877432260411%; - } - .row-fluid .span7 { - width: 57.26495726495726%; - *width: 57.21176577559556%; - } - .row-fluid .span6 { - width: 48.717948717948715%; - *width: 48.664757228587014%; - } - .row-fluid .span5 { - width: 40.17094017094017%; - *width: 40.11774868157847%; - } - .row-fluid .span4 { - width: 31.623931623931625%; - *width: 31.570740134569924%; - } - .row-fluid .span3 { - width: 23.076923076923077%; - *width: 23.023731587561375%; - } - .row-fluid .span2 { - width: 14.52991452991453%; - *width: 14.476723040552828%; - } - .row-fluid .span1 { - width: 5.982905982905983%; - *width: 5.929714493544281%; - } - .row-fluid .offset12 { - margin-left: 105.12820512820512%; - *margin-left: 105.02182214948171%; - } - .row-fluid .offset12:first-child { - margin-left: 102.56410256410257%; - *margin-left: 102.45771958537915%; - } - .row-fluid .offset11 { - margin-left: 96.58119658119658%; - *margin-left: 96.47481360247316%; - } - .row-fluid .offset11:first-child { - margin-left: 94.01709401709402%; - *margin-left: 93.91071103837061%; - } - .row-fluid .offset10 { - margin-left: 88.03418803418803%; - *margin-left: 87.92780505546462%; - } - .row-fluid .offset10:first-child { - margin-left: 85.47008547008548%; - *margin-left: 85.36370249136206%; - } - .row-fluid .offset9 { - margin-left: 79.48717948717949%; - *margin-left: 79.38079650845607%; - } - .row-fluid .offset9:first-child { - margin-left: 76.92307692307693%; - *margin-left: 76.81669394435352%; - } - .row-fluid .offset8 { - margin-left: 70.94017094017094%; - *margin-left: 70.83378796144753%; - } - .row-fluid .offset8:first-child { - margin-left: 68.37606837606839%; - *margin-left: 68.26968539734497%; - } - .row-fluid .offset7 { - margin-left: 62.393162393162385%; - *margin-left: 62.28677941443899%; - } - .row-fluid .offset7:first-child { - margin-left: 59.82905982905982%; - *margin-left: 59.72267685033642%; - } - .row-fluid .offset6 { - margin-left: 53.84615384615384%; - *margin-left: 53.739770867430444%; - } - .row-fluid .offset6:first-child { - margin-left: 51.28205128205128%; - *margin-left: 51.175668303327875%; - } - .row-fluid .offset5 { - margin-left: 45.299145299145295%; - *margin-left: 45.1927623204219%; - } - .row-fluid .offset5:first-child { - margin-left: 42.73504273504273%; - *margin-left: 42.62865975631933%; - } - .row-fluid .offset4 { - margin-left: 36.75213675213675%; - *margin-left: 36.645753773413354%; - } - .row-fluid .offset4:first-child { - margin-left: 34.18803418803419%; - *margin-left: 34.081651209310785%; - } - .row-fluid .offset3 { - margin-left: 28.205128205128204%; - *margin-left: 28.0987452264048%; - } - .row-fluid .offset3:first-child { - margin-left: 25.641025641025642%; - *margin-left: 25.53464266230224%; - } - .row-fluid .offset2 { - margin-left: 19.65811965811966%; - *margin-left: 19.551736679396257%; - } - .row-fluid .offset2:first-child { - margin-left: 17.094017094017094%; - *margin-left: 16.98763411529369%; - } - .row-fluid .offset1 { - margin-left: 11.11111111111111%; - *margin-left: 11.004728132387708%; - } - .row-fluid .offset1:first-child { - margin-left: 8.547008547008547%; - *margin-left: 8.440625568285142%; - } - input, - textarea, - .uneditable-input { - margin-left: 0; - } - .controls-row [class*="span"] + [class*="span"] { - margin-left: 30px; - } - input.span12, - textarea.span12, - .uneditable-input.span12 { - width: 1156px; - } - input.span11, - textarea.span11, - .uneditable-input.span11 { - width: 1056px; - } - input.span10, - textarea.span10, - .uneditable-input.span10 { - width: 956px; - } - input.span9, - textarea.span9, - .uneditable-input.span9 { - width: 856px; - } - input.span8, - textarea.span8, - .uneditable-input.span8 { - width: 756px; - } - input.span7, - textarea.span7, - .uneditable-input.span7 { - width: 656px; - } - input.span6, - textarea.span6, - .uneditable-input.span6 { - width: 556px; - } - input.span5, - textarea.span5, - .uneditable-input.span5 { - width: 456px; - } - input.span4, - textarea.span4, - .uneditable-input.span4 { - width: 356px; - } - input.span3, - textarea.span3, - .uneditable-input.span3 { - width: 256px; - } - input.span2, - textarea.span2, - .uneditable-input.span2 { - width: 156px; - } - input.span1, - textarea.span1, - .uneditable-input.span1 { - width: 56px; - } - .thumbnails { - margin-left: -30px; - } - .thumbnails > li { - margin-left: 30px; - } - .row-fluid .thumbnails { - margin-left: 0; - } -} - -@media (min-width: 768px) and (max-width: 979px) { - .row { - margin-left: -20px; - *zoom: 1; - } - .row:before, - .row:after { - display: table; - line-height: 0; - content: ""; - } - .row:after { - clear: both; - } - [class*="span"] { - float: left; - min-height: 1px; - margin-left: 20px; - } - .container, - .navbar-static-top .container, - .navbar-fixed-top .container, - .navbar-fixed-bottom .container { - width: 724px; - } - .span12 { - width: 724px; - } - .span11 { - width: 662px; - } - .span10 { - width: 600px; - } - .span9 { - width: 538px; - } - .span8 { - width: 476px; - } - .span7 { - width: 414px; - } - .span6 { - width: 352px; - } - .span5 { - width: 290px; - } - .span4 { - width: 228px; - } - .span3 { - width: 166px; - } - .span2 { - width: 104px; - } - .span1 { - width: 42px; - } - .offset12 { - margin-left: 764px; - } - .offset11 { - margin-left: 702px; - } - .offset10 { - margin-left: 640px; - } - .offset9 { - margin-left: 578px; - } - .offset8 { - margin-left: 516px; - } - .offset7 { - margin-left: 454px; - } - .offset6 { - margin-left: 392px; - } - .offset5 { - margin-left: 330px; - } - .offset4 { - margin-left: 268px; - } - .offset3 { - margin-left: 206px; - } - .offset2 { - margin-left: 144px; - } - .offset1 { - margin-left: 82px; - } - .row-fluid { - width: 100%; - *zoom: 1; - } - .row-fluid:before, - .row-fluid:after { - display: table; - line-height: 0; - content: ""; - } - .row-fluid:after { - clear: both; - } - .row-fluid [class*="span"] { - display: block; - float: left; - width: 100%; - min-height: 30px; - margin-left: 2.7624309392265194%; - *margin-left: 2.709239449864817%; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - } - .row-fluid [class*="span"]:first-child { - margin-left: 0; - } - .row-fluid .controls-row [class*="span"] + [class*="span"] { - margin-left: 2.7624309392265194%; - } - .row-fluid .span12 { - width: 100%; - *width: 99.94680851063829%; - } - .row-fluid .span11 { - width: 91.43646408839778%; - *width: 91.38327259903608%; - } - .row-fluid .span10 { - width: 82.87292817679558%; - *width: 82.81973668743387%; - } - .row-fluid .span9 { - width: 74.30939226519337%; - *width: 74.25620077583166%; - } - .row-fluid .span8 { - width: 65.74585635359117%; - *width: 65.69266486422946%; - } - .row-fluid .span7 { - width: 57.18232044198895%; - *width: 57.12912895262725%; - } - .row-fluid .span6 { - width: 48.61878453038674%; - *width: 48.56559304102504%; - } - .row-fluid .span5 { - width: 40.05524861878453%; - *width: 40.00205712942283%; - } - .row-fluid .span4 { - width: 31.491712707182323%; - *width: 31.43852121782062%; - } - .row-fluid .span3 { - width: 22.92817679558011%; - *width: 22.87498530621841%; - } - .row-fluid .span2 { - width: 14.3646408839779%; - *width: 14.311449394616199%; - } - .row-fluid .span1 { - width: 5.801104972375691%; - *width: 5.747913483013988%; - } - .row-fluid .offset12 { - margin-left: 105.52486187845304%; - *margin-left: 105.41847889972962%; - } - .row-fluid .offset12:first-child { - margin-left: 102.76243093922652%; - *margin-left: 102.6560479605031%; - } - .row-fluid .offset11 { - margin-left: 96.96132596685082%; - *margin-left: 96.8549429881274%; - } - .row-fluid .offset11:first-child { - margin-left: 94.1988950276243%; - *margin-left: 94.09251204890089%; - } - .row-fluid .offset10 { - margin-left: 88.39779005524862%; - *margin-left: 88.2914070765252%; - } - .row-fluid .offset10:first-child { - margin-left: 85.6353591160221%; - *margin-left: 85.52897613729868%; - } - .row-fluid .offset9 { - margin-left: 79.8342541436464%; - *margin-left: 79.72787116492299%; - } - .row-fluid .offset9:first-child { - margin-left: 77.07182320441989%; - *margin-left: 76.96544022569647%; - } - .row-fluid .offset8 { - margin-left: 71.2707182320442%; - *margin-left: 71.16433525332079%; - } - .row-fluid .offset8:first-child { - margin-left: 68.50828729281768%; - *margin-left: 68.40190431409427%; - } - .row-fluid .offset7 { - margin-left: 62.70718232044199%; - *margin-left: 62.600799341718584%; - } - .row-fluid .offset7:first-child { - margin-left: 59.94475138121547%; - *margin-left: 59.838368402492065%; - } - .row-fluid .offset6 { - margin-left: 54.14364640883978%; - *margin-left: 54.037263430116376%; - } - .row-fluid .offset6:first-child { - margin-left: 51.38121546961326%; - *margin-left: 51.27483249088986%; - } - .row-fluid .offset5 { - margin-left: 45.58011049723757%; - *margin-left: 45.47372751851417%; - } - .row-fluid .offset5:first-child { - margin-left: 42.81767955801105%; - *margin-left: 42.71129657928765%; - } - .row-fluid .offset4 { - margin-left: 37.01657458563536%; - *margin-left: 36.91019160691196%; - } - .row-fluid .offset4:first-child { - margin-left: 34.25414364640884%; - *margin-left: 34.14776066768544%; - } - .row-fluid .offset3 { - margin-left: 28.45303867403315%; - *margin-left: 28.346655695309746%; - } - .row-fluid .offset3:first-child { - margin-left: 25.69060773480663%; - *margin-left: 25.584224756083227%; - } - .row-fluid .offset2 { - margin-left: 19.88950276243094%; - *margin-left: 19.783119783707537%; - } - .row-fluid .offset2:first-child { - margin-left: 17.12707182320442%; - *margin-left: 17.02068884448102%; - } - .row-fluid .offset1 { - margin-left: 11.32596685082873%; - *margin-left: 11.219583872105325%; - } - .row-fluid .offset1:first-child { - margin-left: 8.56353591160221%; - *margin-left: 8.457152932878806%; - } - input, - textarea, - .uneditable-input { - margin-left: 0; - } - .controls-row [class*="span"] + [class*="span"] { - margin-left: 20px; - } - input.span12, - textarea.span12, - .uneditable-input.span12 { - width: 710px; - } - input.span11, - textarea.span11, - .uneditable-input.span11 { - width: 648px; - } - input.span10, - textarea.span10, - .uneditable-input.span10 { - width: 586px; - } - input.span9, - textarea.span9, - .uneditable-input.span9 { - width: 524px; - } - input.span8, - textarea.span8, - .uneditable-input.span8 { - width: 462px; - } - input.span7, - textarea.span7, - .uneditable-input.span7 { - width: 400px; - } - input.span6, - textarea.span6, - .uneditable-input.span6 { - width: 338px; - } - input.span5, - textarea.span5, - .uneditable-input.span5 { - width: 276px; - } - input.span4, - textarea.span4, - .uneditable-input.span4 { - width: 214px; - } - input.span3, - textarea.span3, - .uneditable-input.span3 { - width: 152px; - } - input.span2, - textarea.span2, - .uneditable-input.span2 { - width: 90px; - } - input.span1, - textarea.span1, - .uneditable-input.span1 { - width: 28px; - } -} - -@media (max-width: 767px) { - body { - padding-right: 20px; - padding-left: 20px; - } - .navbar-fixed-top, - .navbar-fixed-bottom, - .navbar-static-top { - margin-right: -20px; - margin-left: -20px; - } - .container-fluid { - padding: 0; - } - .dl-horizontal dt { - float: none; - width: auto; - clear: none; - text-align: left; - } - .dl-horizontal dd { - margin-left: 0; - } - .container { - width: auto; - } - .row-fluid { - width: 100%; - } - .row, - .thumbnails { - margin-left: 0; - } - .thumbnails > li { - float: none; - margin-left: 0; - } - [class*="span"], - .uneditable-input[class*="span"], - .row-fluid [class*="span"] { - display: block; - float: none; - width: 100%; - margin-left: 0; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - } - .span12, - .row-fluid .span12 { - width: 100%; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - } - .row-fluid [class*="offset"]:first-child { - margin-left: 0; - } - .input-large, - .input-xlarge, - .input-xxlarge, - input[class*="span"], - select[class*="span"], - textarea[class*="span"], - .uneditable-input { - display: block; - width: 100%; - min-height: 30px; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - } - .input-prepend input, - .input-append input, - .input-prepend input[class*="span"], - .input-append input[class*="span"] { - display: inline-block; - width: auto; - } - .controls-row [class*="span"] + [class*="span"] { - margin-left: 0; - } - .modal { - position: fixed; - top: 20px; - right: 20px; - left: 20px; - width: auto; - margin: 0; - } - .modal.fade { - top: -100px; - } - .modal.fade.in { - top: 20px; - } -} - -@media (max-width: 480px) { - .nav-collapse { - -webkit-transform: translate3d(0, 0, 0); - } - .page-header h1 small { - display: block; - line-height: 20px; - } - input[type="checkbox"], - input[type="radio"] { - border: 1px solid #ccc; - } - .form-horizontal .control-label { - float: none; - width: auto; - padding-top: 0; - text-align: left; - } - .form-horizontal .controls { - margin-left: 0; - } - .form-horizontal .control-list { - padding-top: 0; - } - .form-horizontal .form-actions { - padding-right: 10px; - padding-left: 10px; - } - .media .pull-left, - .media .pull-right { - display: block; - float: none; - margin-bottom: 10px; - } - .media-object { - margin-right: 0; - margin-left: 0; - } - .modal { - top: 10px; - right: 10px; - left: 10px; - } - .modal-header .close { - padding: 10px; - margin: -10px; - } - .carousel-caption { - position: static; - } -} - -@media (max-width: 979px) { - body { - padding-top: 0; - } - .navbar-fixed-top, - .navbar-fixed-bottom { - position: static; - } - .navbar-fixed-top { - margin-bottom: 20px; - } - .navbar-fixed-bottom { - margin-top: 20px; - } - .navbar-fixed-top .navbar-inner, - .navbar-fixed-bottom .navbar-inner { - padding: 5px; - } - .navbar .container { - width: auto; - padding: 0; - } - .navbar .brand { - padding-right: 10px; - padding-left: 10px; - margin: 0 0 0 -5px; - } - .nav-collapse { - clear: both; - } - .nav-collapse .nav { - float: none; - margin: 0 0 10px; - } - .nav-collapse .nav > li { - float: none; - } - .nav-collapse .nav > li > a { - margin-bottom: 2px; - } - .nav-collapse .nav > .divider-vertical { - display: none; - } - .nav-collapse .nav .nav-header { - color: #777777; - text-shadow: none; - } - .nav-collapse .nav > li > a, - .nav-collapse .dropdown-menu a { - padding: 9px 15px; - font-weight: bold; - color: #777777; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; - } - .nav-collapse .btn { - padding: 4px 10px 4px; - font-weight: normal; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - } - .nav-collapse .dropdown-menu li + li a { - margin-bottom: 2px; - } - .nav-collapse .nav > li > a:hover, - .nav-collapse .dropdown-menu a:hover { - background-color: #f2f2f2; - } - .navbar-inverse .nav-collapse .nav > li > a, - .navbar-inverse .nav-collapse .dropdown-menu a { - color: #999999; - } - .navbar-inverse .nav-collapse .nav > li > a:hover, - .navbar-inverse .nav-collapse .dropdown-menu a:hover { - background-color: #111111; - } - .nav-collapse.in .btn-group { - padding: 0; - margin-top: 5px; - } - .nav-collapse .dropdown-menu { - position: static; - top: auto; - left: auto; - display: none; - float: none; - max-width: none; - padding: 0; - margin: 0 15px; - background-color: transparent; - border: none; - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; - -webkit-box-shadow: none; - -moz-box-shadow: none; - box-shadow: none; - } - .nav-collapse .open > .dropdown-menu { - display: block; - } - .nav-collapse .dropdown-menu:before, - .nav-collapse .dropdown-menu:after { - display: none; - } - .nav-collapse .dropdown-menu .divider { - display: none; - } - .nav-collapse .nav > li > .dropdown-menu:before, - .nav-collapse .nav > li > .dropdown-menu:after { - display: none; - } - .nav-collapse .navbar-form, - .nav-collapse .navbar-search { - float: none; - padding: 10px 15px; - margin: 10px 0; - border-top: 1px solid #f2f2f2; - border-bottom: 1px solid #f2f2f2; - -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); - -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); - } - .navbar-inverse .nav-collapse .navbar-form, - .navbar-inverse .nav-collapse .navbar-search { - border-top-color: #111111; - border-bottom-color: #111111; - } - .navbar .nav-collapse .nav.pull-right { - float: none; - margin-left: 0; - } - .nav-collapse, - .nav-collapse.collapse { - height: 0; - overflow: hidden; - } - .navbar .btn-navbar { - display: block; - } - .navbar-static .navbar-inner { - padding-right: 10px; - padding-left: 10px; - } -} - -@media (min-width: 980px) { - .nav-collapse.collapse { - height: auto !important; - overflow: visible !important; - } -} diff --git a/public/css/810-override_container_app.css b/public/css/810-override_container_app.css deleted file mode 100644 index 9befa4c59..000000000 --- a/public/css/810-override_container_app.css +++ /dev/null @@ -1,35 +0,0 @@ - -.container > footer p { - text-align: center; /* center align it with the container */ -} - -.container-fluid > footer p { - text-align: center; /* center align it with the container */ -} - -/* The white background content wrapper */ -.page-content { - background-color: #fff; - padding: 20px; - margin: 0 -20px; /* negative indent the amount of the padding to maintain the grid system */ - -webkit-border-radius: 0 0 6px 6px; - -moz-border-radius: 0 0 6px 6px; - border-radius: 0 0 6px 6px; - -webkit-box-shadow: 0 1px 2px rgba(0,0,0,.15); - -moz-box-shadow: 0 1px 2px rgba(0,0,0,.15); - box-shadow: 0 1px 2px rgba(0,0,0,.15); -} - - -/* Page header tweaks */ -.page-header { - background-color: #f5f5f5; - padding: 20px 20px 10px; - margin: -20px -20px 20px; -} - -/* -.topbar .btn { - border: 0; -}*/ - diff --git a/public/css/820-bootstrap-override.css b/public/css/820-bootstrap-override.css deleted file mode 100644 index f87455d7a..000000000 --- a/public/css/820-bootstrap-override.css +++ /dev/null @@ -1,149 +0,0 @@ -.hero-unit { - background-color: #f5f5f5; - margin-bottom: 30px; - padding: 60px; - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; -} -.hero-unit h1 { - margin-bottom: 0; - font-size: 60px; - line-height: 1; - letter-spacing: -1px; -} -.hero-unit p { - font-size: 18px; - font-weight: 200; - line-height: 27px; -} - -.container > footer { - padding-top: 30px; -} - -.container-fluid > footer { - padding-top: 30px; -} - - -.carousel-unit-top { - background-color: #f5f5f5; - padding-bottom: 20px; - padding-top: 0px; - padding-left: 50px; - padding-right: 50px; - - border: 1px solid #DDDDDD; - border-bottom: 0px; - - -webkit-border-top-left-radius: 6px; - -webkit-border-top-right-radius: 6px; - -moz-border-radius-topleft: 6px; - -moz-border-radius-topright: 6px; - border-top-left-radius: 6px; - border-top-right-radius: 6px; -} - - -.carousel-unit-bottom { - background-color: #f5f5f5; - padding-bottom: 30px; - padding-top: 0px; - padding-left: 50px; - padding-right: 50px; - margin-bottom: 20px; - - border: 1px solid #DDDDDD; - border-top: 0px; - - -webkit-border-bottom-right-radius: 6px; - -webkit-border-bottom-left-radius: 6px; - -moz-border-radius-bottomright: 6px; - -moz-border-radius-bottomleft: 6px; - border-bottom-right-radius: 6px; - border-bottom-left-radius: 6px; -} - -.carousel-unit-bottom h1 { - margin-bottom: 0; - font-size: 30px; - line-height: 1; - letter-spacing: -1px; - text-align: center; -} - -.carousel-unit-bottom p { - font-size: 18px; - font-weight: 200; - line-height: 27px; - text-align: center; - padding-top: 10px; -} - -.carousel-unit-top div#carousel-tabs { - padding-bottom: 0px; -} - -.tabs { - margin-top: -1px !important; -} - - - -/****************************************************************************/ -/*********************** DATA TABLES *******************************/ -/****************************************************************************/ - -/* http://datatables.net/blog/Twitter_Bootstrap_2 */ - -div.dataTables_length label { - float: left; - text-align: left; -} - -div.dataTables_length select { - width: 75px; -} - -div.dataTables_filter label { - float: right; -} - -div.dataTables_info { - padding-top: 8px; -} - -div.dataTables_paginate { - float: right; - margin: 0; -} - -table.table { - clear: both; - margin-bottom: 6px !important; -} - -table.table thead .sorting, -table.table thead .sorting_asc, -table.table thead .sorting_desc, -table.table thead .sorting_asc_disabled, -table.table thead .sorting_desc_disabled { - cursor: pointer; - *cursor: hand; -} - -table.table thead .sorting { background: url('images/sort_both.png') no-repeat center right; } -table.table thead .sorting_asc { background: url('images/sort_asc.png') no-repeat center right; } -table.table thead .sorting_desc { background: url('images/sort_desc.png') no-repeat center right; } - -table.table thead .sorting_asc_disabled { background: url('images/sort_asc_disabled.png') no-repeat center right; } -table.table thead .sorting_desc_disabled { background: url('images/sort_desc_disabled.png') no-repeat center right; } - -table.dataTable th:active { - outline: none; -} - -/****************************************************************************/ - - diff --git a/public/css/830-bootstrap-wysihtml5.css b/public/css/830-bootstrap-wysihtml5.css deleted file mode 100644 index 6a8623b51..000000000 --- a/public/css/830-bootstrap-wysihtml5.css +++ /dev/null @@ -1,36 +0,0 @@ -ul.wysihtml5-toolbar { - margin: 0; - padding: 0; - display: block; -} - -ul.wysihtml5-toolbar::after { - clear: both; - display: table; - content: ""; -} - -ul.wysihtml5-toolbar > li { - float: left; - display: list-item; - list-style: none; - margin: 0 5px 10px 0; -} - -ul.wysihtml5-toolbar a[data-wysihtml5-command=bold] { - font-weight: bold; -} - -ul.wysihtml5-toolbar a[data-wysihtml5-command=italic] { - font-style: italic; -} - -ul.wysihtml5-toolbar a.btn.wysihtml5-command-active { - background-image: none; - -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05); - -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05); - box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05); - background-color: #E6E6E6; - background-color: #D9D9D9 9; - outline: 0; -} \ No newline at end of file diff --git a/public/css/900-ixp-manager.css b/public/css/900-ixp-manager.css deleted file mode 100644 index fc7e2e51a..000000000 --- a/public/css/900-ixp-manager.css +++ /dev/null @@ -1,312 +0,0 @@ -@CHARSET "UTF-8"; - - -body { - padding-top: 60px; - padding-bottom: 40px; -} - -.sidebar-nav { - padding: 9px 0; -} - -.mono { - font-family: Menlo, Monaco, Consolas, "Courier New", monospace; - /* font-size: 12px; */ -} - -.oss-overlay -{ - position: absolute; - z-index: 2000; - background-color:black; - opacity: 0.4; -} - -a.aplain, a:link.aplain, a:visited.aplain, a:hover.aplain, a:active.aplain { - color: #fff; - text-decoration: none; -} - - -.meetings { - margin-left: 20px; - margin-right: 20px; - width: 80%; -} - -.meetings_index_container { - display: block; -} - -.meetings_index { - display: block; - text-align: right; -} - -.meeting h1 { - color: #4fa500; - font-size: 146.5%; - font-weight: bold; - padding-bottom: 0px; - margin-bottom: 0px; -} - -.meeting h4 { - color: #4fa500; - font-size: 100%; - font-weight: normal; - font-style: italic; -} - -.meeting.title { - padding-bottom: 2px; - border-bottom: 2px solid #4fa500; - margin-bottom: 10px; -} - -.meetingitem h1 { - color: #000000; - font-size: 116%; - font-weight: bold; - padding-bottom: 0px; - margin-bottom: 0px; -} - -.meetingitem a, .meetingitem a:link, .meetingitem a:visited { - color: #000000; - text-decoration: none; - border-bottom: 1px dotted #000000; -} - - -.meetingitem h4 { - color: #000000; - font-size: 100%; - font-weight: normal; - font-style: italic; -} - -.meetingitem.title { - margin-bottom: 10px; -} - -.meetingitem.title.icons { - float: right; -} - -.meetingitem.title.icons img { - float: right; - padding-left: 10px; -} - - -.meetingitem dd { - color: #000000; - font-size: 100%; - font-weight: normal; - font-style: none; - margin-bottom: 10px; - padding-left: 20px; - padding-bottom: 10px; -} - -.meeting.buttons { - text-align: center; -} - -.list_preamble_container { - display: block; -} - -.list_preamble { - display: block; - text-align: right; -} - - - - - - -.pm-table { - margin-bottom: 18px; - margin-left: auto; - margin-right: auto; -} - - -.pm-table th { - padding: 0px; - text-align: center; - vertical-align: bottom; - border: 1px solid #dddddd; - border-bottom-width: 2px; - border-top: none; - font-weight: bold; -} - -.pm-table th.asn { - cursor: hand; - cursor: pointer; -} - -.pm-table th:last-child { - border-right-width: 2px; -} - -.pm-table td { - text-align: center; - vertical-align: top; - border: 1px solid #dddddd; -} - -.pm-table td.peering { - cursor: hand; - cursor: pointer; -} - -.pm-table td.zoom1 { - padding: 0px 1px; - font-size: 8px; - line-height: 12px; -} - -.pm-table td.zoom2 { - padding: 2px 4px; - font-size: 11px; - line-height: 15px; -} - -.pm-table td.zoom3 { - padding: 4px 8px; - font-size: 13px; - line-height: 18px; -} - -.pm-table td.zoom4 { - padding: 6px 10px; - font-size: 15px; - line-height: 20px; -} - -.pm-table td.zoom5 { - padding: 8px 12px; - font-size: 18px; - line-height: 24px; -} - -.pm-table th.zoom1 { - font-size: 8px; - line-height: 12px; -} - -.pm-table th.zoom2 { - font-size: 11px; - line-height: 15px; -} - -.pm-table th.zoom3 { - font-size: 13px; - line-height: 18px; -} - -.pm-table th.zoom4 { - font-size: 15px; - line-height: 20px; -} - -.pm-table th.zoom5 { - font-size: 18px; - line-height: 24px; -} - -.pm-table td:last-child { - border-right-width: 2px; -} - - - -.pm-table th.name { - border-left: none; - border-right: none; - border-top: none; -} - -.pm-table th.asn { - border-left: none; - border-top: none; - border-right-width: 2px; -} - -.pm-table td.name { - text-align: left; - border-left: none; - border-right: none; - font-weight: bold; -} - -.pm-table td.asn { - text-align: right; - border-left: none; - border-right-width: 2px; - font-weight: bold; -} - -.pm-table tr:last-child { - border-bottom-width: 2px; -} - - -.pm-table td.peered { - background-color: #00cc00; - width: 10px; -} - -.pm-table td.not-peered { - background-color: #d63030; - width: 10px; -} - -.pm-table td.bilateral-rs { - width: 10px; -} - -.pm-table td.rs-only { - width: 10px; -} - -.pm-table td.bilateral-only { - width: 10px; -} - - -.pm-table tr.hover, .pm-table colgroup.hover, .pm-table td.hover { - background-color: #eee; - border-left: 2px solid #555; - border-right: 2px solid #555; -} - -.pm-table td.hover2 { - border-left: 1px solid #555; - border-right: 1px solid #555; -} - - -.pm-table th.highlight, .pm-table td.highlight { - background-color: #fcf6cf; -} - -.pm-table th.highlight2, .pm-table td.highlight2 { - background-color: #eee; -} - - - -/* Bootstrap Dropdowns in Breadcrumbs: - http://stackoverflow.com/questions/12194573/bootstrap-dropdown-within-breadcrumb */ -.dropdown-menu li { - display: block; -} - - diff --git a/public/css/chosen-sprite.png b/public/css/chosen-sprite.png deleted file mode 100644 index 113dc9885..000000000 Binary files a/public/css/chosen-sprite.png and /dev/null differ diff --git a/public/css/images/back_disabled.png b/public/css/images/back_disabled.png deleted file mode 100644 index 881de7976..000000000 Binary files a/public/css/images/back_disabled.png and /dev/null differ diff --git a/public/css/images/back_enabled.png b/public/css/images/back_enabled.png deleted file mode 100644 index c608682b0..000000000 Binary files a/public/css/images/back_enabled.png and /dev/null differ diff --git a/public/css/images/back_enabled_hover.png b/public/css/images/back_enabled_hover.png deleted file mode 100644 index d300f1064..000000000 Binary files a/public/css/images/back_enabled_hover.png and /dev/null differ diff --git a/public/css/images/border.png b/public/css/images/border.png deleted file mode 100644 index f463a10d8..000000000 Binary files a/public/css/images/border.png and /dev/null differ diff --git a/public/css/images/controls.png b/public/css/images/controls.png deleted file mode 100644 index dcfd6fb9f..000000000 Binary files a/public/css/images/controls.png and /dev/null differ diff --git a/public/css/images/forward_disabled.png b/public/css/images/forward_disabled.png deleted file mode 100644 index 6a6ded7de..000000000 Binary files a/public/css/images/forward_disabled.png and /dev/null differ diff --git a/public/css/images/forward_enabled.png b/public/css/images/forward_enabled.png deleted file mode 100644 index a4e6b5384..000000000 Binary files a/public/css/images/forward_enabled.png and /dev/null differ diff --git a/public/css/images/forward_enabled_hover.png b/public/css/images/forward_enabled_hover.png deleted file mode 100644 index fc46c5ebf..000000000 Binary files a/public/css/images/forward_enabled_hover.png and /dev/null differ diff --git a/public/css/images/ie6/borderBottomCenter.png b/public/css/images/ie6/borderBottomCenter.png deleted file mode 100644 index 0d4475edf..000000000 Binary files a/public/css/images/ie6/borderBottomCenter.png and /dev/null differ diff --git a/public/css/images/ie6/borderBottomLeft.png b/public/css/images/ie6/borderBottomLeft.png deleted file mode 100644 index 2775eba89..000000000 Binary files a/public/css/images/ie6/borderBottomLeft.png and /dev/null differ diff --git a/public/css/images/ie6/borderBottomRight.png b/public/css/images/ie6/borderBottomRight.png deleted file mode 100644 index f7f51379c..000000000 Binary files a/public/css/images/ie6/borderBottomRight.png and /dev/null differ diff --git a/public/css/images/ie6/borderMiddleLeft.png b/public/css/images/ie6/borderMiddleLeft.png deleted file mode 100644 index a2d63d156..000000000 Binary files a/public/css/images/ie6/borderMiddleLeft.png and /dev/null differ diff --git a/public/css/images/ie6/borderMiddleRight.png b/public/css/images/ie6/borderMiddleRight.png deleted file mode 100644 index fd7c3e849..000000000 Binary files a/public/css/images/ie6/borderMiddleRight.png and /dev/null differ diff --git a/public/css/images/ie6/borderTopCenter.png b/public/css/images/ie6/borderTopCenter.png deleted file mode 100644 index 2937a9cf9..000000000 Binary files a/public/css/images/ie6/borderTopCenter.png and /dev/null differ diff --git a/public/css/images/ie6/borderTopLeft.png b/public/css/images/ie6/borderTopLeft.png deleted file mode 100644 index f9d458b5b..000000000 Binary files a/public/css/images/ie6/borderTopLeft.png and /dev/null differ diff --git a/public/css/images/ie6/borderTopRight.png b/public/css/images/ie6/borderTopRight.png deleted file mode 100644 index 74b8583cf..000000000 Binary files a/public/css/images/ie6/borderTopRight.png and /dev/null differ diff --git a/public/css/images/loading.gif b/public/css/images/loading.gif deleted file mode 100644 index b4695d811..000000000 Binary files a/public/css/images/loading.gif and /dev/null differ diff --git a/public/css/images/loading_background.png b/public/css/images/loading_background.png deleted file mode 100644 index 6ae83e697..000000000 Binary files a/public/css/images/loading_background.png and /dev/null differ diff --git a/public/css/images/overlay.png b/public/css/images/overlay.png deleted file mode 100644 index 53ea98f70..000000000 Binary files a/public/css/images/overlay.png and /dev/null differ diff --git a/public/css/images/sort_asc.png b/public/css/images/sort_asc.png deleted file mode 100644 index a88d7975f..000000000 Binary files a/public/css/images/sort_asc.png and /dev/null differ diff --git a/public/css/images/sort_asc_disabled.png b/public/css/images/sort_asc_disabled.png deleted file mode 100644 index 4e144cf0b..000000000 Binary files a/public/css/images/sort_asc_disabled.png and /dev/null differ diff --git a/public/css/images/sort_both.png b/public/css/images/sort_both.png deleted file mode 100644 index 18670406b..000000000 Binary files a/public/css/images/sort_both.png and /dev/null differ diff --git a/public/css/images/sort_desc.png b/public/css/images/sort_desc.png deleted file mode 100644 index def071ed5..000000000 Binary files a/public/css/images/sort_desc.png and /dev/null differ diff --git a/public/css/images/sort_desc_disabled.png b/public/css/images/sort_desc_disabled.png deleted file mode 100644 index 7824973cc..000000000 Binary files a/public/css/images/sort_desc_disabled.png and /dev/null differ diff --git a/public/css/images/ui-bg_diagonals-thick_18_b81900_40x40.png b/public/css/images/ui-bg_diagonals-thick_18_b81900_40x40.png deleted file mode 100644 index 954e22dbd..000000000 Binary files a/public/css/images/ui-bg_diagonals-thick_18_b81900_40x40.png and /dev/null differ diff --git a/public/css/images/ui-bg_diagonals-thick_20_666666_40x40.png b/public/css/images/ui-bg_diagonals-thick_20_666666_40x40.png deleted file mode 100644 index 64ece5707..000000000 Binary files a/public/css/images/ui-bg_diagonals-thick_20_666666_40x40.png and /dev/null differ diff --git a/public/css/images/ui-bg_flat_0_aaaaaa_40x100.png b/public/css/images/ui-bg_flat_0_aaaaaa_40x100.png deleted file mode 100644 index 5b5dab2ab..000000000 Binary files a/public/css/images/ui-bg_flat_0_aaaaaa_40x100.png and /dev/null differ diff --git a/public/css/images/ui-bg_flat_10_000000_40x100.png b/public/css/images/ui-bg_flat_10_000000_40x100.png deleted file mode 100644 index abdc01082..000000000 Binary files a/public/css/images/ui-bg_flat_10_000000_40x100.png and /dev/null differ diff --git a/public/css/images/ui-bg_flat_75_ffffff_40x100.png b/public/css/images/ui-bg_flat_75_ffffff_40x100.png deleted file mode 100644 index ac8b229af..000000000 Binary files a/public/css/images/ui-bg_flat_75_ffffff_40x100.png and /dev/null differ diff --git a/public/css/images/ui-bg_glass_100_f6f6f6_1x400.png b/public/css/images/ui-bg_glass_100_f6f6f6_1x400.png deleted file mode 100644 index 9b383f4d2..000000000 Binary files a/public/css/images/ui-bg_glass_100_f6f6f6_1x400.png and /dev/null differ diff --git a/public/css/images/ui-bg_glass_100_fdf5ce_1x400.png b/public/css/images/ui-bg_glass_100_fdf5ce_1x400.png deleted file mode 100644 index a23baad25..000000000 Binary files a/public/css/images/ui-bg_glass_100_fdf5ce_1x400.png and /dev/null differ diff --git a/public/css/images/ui-bg_glass_55_fbf9ee_1x400.png b/public/css/images/ui-bg_glass_55_fbf9ee_1x400.png deleted file mode 100644 index ad3d6346e..000000000 Binary files a/public/css/images/ui-bg_glass_55_fbf9ee_1x400.png and /dev/null differ diff --git a/public/css/images/ui-bg_glass_65_ffffff_1x400.png b/public/css/images/ui-bg_glass_65_ffffff_1x400.png deleted file mode 100644 index 42ccba269..000000000 Binary files a/public/css/images/ui-bg_glass_65_ffffff_1x400.png and /dev/null differ diff --git a/public/css/images/ui-bg_glass_75_dadada_1x400.png b/public/css/images/ui-bg_glass_75_dadada_1x400.png deleted file mode 100644 index 5a46b47cb..000000000 Binary files a/public/css/images/ui-bg_glass_75_dadada_1x400.png and /dev/null differ diff --git a/public/css/images/ui-bg_glass_75_e6e6e6_1x400.png b/public/css/images/ui-bg_glass_75_e6e6e6_1x400.png deleted file mode 100644 index 86c2baa65..000000000 Binary files a/public/css/images/ui-bg_glass_75_e6e6e6_1x400.png and /dev/null differ diff --git a/public/css/images/ui-bg_glass_95_fef1ec_1x400.png b/public/css/images/ui-bg_glass_95_fef1ec_1x400.png deleted file mode 100644 index 4443fdc1a..000000000 Binary files a/public/css/images/ui-bg_glass_95_fef1ec_1x400.png and /dev/null differ diff --git a/public/css/images/ui-bg_gloss-wave_35_f6a828_500x100.png b/public/css/images/ui-bg_gloss-wave_35_f6a828_500x100.png deleted file mode 100644 index 39d5824d6..000000000 Binary files a/public/css/images/ui-bg_gloss-wave_35_f6a828_500x100.png and /dev/null differ diff --git a/public/css/images/ui-bg_highlight-soft_100_eeeeee_1x100.png b/public/css/images/ui-bg_highlight-soft_100_eeeeee_1x100.png deleted file mode 100644 index f1273672d..000000000 Binary files a/public/css/images/ui-bg_highlight-soft_100_eeeeee_1x100.png and /dev/null differ diff --git a/public/css/images/ui-bg_highlight-soft_75_cccccc_1x100.png b/public/css/images/ui-bg_highlight-soft_75_cccccc_1x100.png deleted file mode 100644 index 7c9fa6c6e..000000000 Binary files a/public/css/images/ui-bg_highlight-soft_75_cccccc_1x100.png and /dev/null differ diff --git a/public/css/images/ui-bg_highlight-soft_75_ffe45c_1x100.png b/public/css/images/ui-bg_highlight-soft_75_ffe45c_1x100.png deleted file mode 100644 index 359397acf..000000000 Binary files a/public/css/images/ui-bg_highlight-soft_75_ffe45c_1x100.png and /dev/null differ diff --git a/public/css/images/ui-icons_222222_256x240.png b/public/css/images/ui-icons_222222_256x240.png deleted file mode 100644 index b273ff111..000000000 Binary files a/public/css/images/ui-icons_222222_256x240.png and /dev/null differ diff --git a/public/css/images/ui-icons_228ef1_256x240.png b/public/css/images/ui-icons_228ef1_256x240.png deleted file mode 100644 index a641a371a..000000000 Binary files a/public/css/images/ui-icons_228ef1_256x240.png and /dev/null differ diff --git a/public/css/images/ui-icons_2e83ff_256x240.png b/public/css/images/ui-icons_2e83ff_256x240.png deleted file mode 100644 index 45e8928e5..000000000 Binary files a/public/css/images/ui-icons_2e83ff_256x240.png and /dev/null differ diff --git a/public/css/images/ui-icons_454545_256x240.png b/public/css/images/ui-icons_454545_256x240.png deleted file mode 100644 index 7ec70d11b..000000000 Binary files a/public/css/images/ui-icons_454545_256x240.png and /dev/null differ diff --git a/public/css/images/ui-icons_888888_256x240.png b/public/css/images/ui-icons_888888_256x240.png deleted file mode 100644 index 5ba708c39..000000000 Binary files a/public/css/images/ui-icons_888888_256x240.png and /dev/null differ diff --git a/public/css/images/ui-icons_cd0a0a_256x240.png b/public/css/images/ui-icons_cd0a0a_256x240.png deleted file mode 100644 index 7930a5580..000000000 Binary files a/public/css/images/ui-icons_cd0a0a_256x240.png and /dev/null differ diff --git a/public/css/images/ui-icons_ef8c08_256x240.png b/public/css/images/ui-icons_ef8c08_256x240.png deleted file mode 100644 index 85e63e9f6..000000000 Binary files a/public/css/images/ui-icons_ef8c08_256x240.png and /dev/null differ diff --git a/public/css/images/ui-icons_ffd27a_256x240.png b/public/css/images/ui-icons_ffd27a_256x240.png deleted file mode 100644 index e117effa3..000000000 Binary files a/public/css/images/ui-icons_ffd27a_256x240.png and /dev/null differ diff --git a/public/css/images/ui-icons_ffffff_256x240.png b/public/css/images/ui-icons_ffffff_256x240.png deleted file mode 100644 index 42f8f992c..000000000 Binary files a/public/css/images/ui-icons_ffffff_256x240.png and /dev/null differ diff --git a/public/css/ixp-manager.css b/public/css/ixp-manager.css new file mode 100644 index 000000000..79dcab91f --- /dev/null +++ b/public/css/ixp-manager.css @@ -0,0 +1,817 @@ +@CHARSET "UTF-8"; + + + + + + + +/* +* BEGIN CSS used for Bootstrap 4 +*/ + + + + +/***********************************************************************************/ +/************************************* NAV BAR *************************************/ +/***********************************************************************************/ + + +#navbarSupportedContent a { + font-size: 15px; +} + +@media (max-width: 768px) { + #side-navbar { + display: block; + margin-left: -220px; + width: 220px !important; + height: 96%; + overflow: scroll; + transition: all 0.3s ease; + } + + #side-navbar.active { + margin-left: 0; + z-index: 200; + position: absolute; + display: block !important; + } + + #slide-reveal-overlay{ + position: fixed; + left: 0px; + height: 100%; + width: 100%; + z-index: 100; + background-color: rgba(0, 0, 0, 0.5); + } +} + +@media (max-width: 992px) { + #side-navbar .nav-item a{ + font-size: 0.9rem; + } + + #side-navbar h6{ + font-size: 0.9rem; + } +} + + +/* +:::::::::::::::::::::::::::::::::::::::::::::::::::: +COL-LG +*/ + +/* Large devices (desktops, 992px and up) */ +@media (min-width: 992px) { + #navbarSupportedContent a{ + font-size: 14px; + } + + #div-header-select-customer{ + width: 150px; + } + +} + +/* Large devices (desktops, 992px and up) */ +@media (min-width: 992px ) and (max-width: 1205px) { + + .navbar-expand-lg #navbarSupportedContent .navbar-nav .nav-link{ + padding-right:.3rem; + padding-left:.3rem + } +} + +/* +:::::::::::::::::::::::::::::::::::::::::::::::::::: +COL-XL +*/ +/* Extra large devices (large desktops, 1200px and up) */ +@media (min-width: 1200px) { + #div-header-select-customer{ + width: 175px; + } +} + + .former-help-text { + display: none; + } + + .form-check input[type="checkbox"]{ + margin-right: 10px !important; + } + + .reset-button-well{ + float: right; + position: absolute; + margin-left: 55%; + } + + .table_view_info{ + border-collapse: separate; + border-spacing: 15px 15px; + } + + .table_view_info tr td{ + vertical-align: top; + } + + .dropdown-submenu { + position: relative !important; + } + + .dropdown-submenu .dropdown-menu-l { + top: 0; + left: -163px; + margin-top: -1px; + } + + .former-input-col-sm-6 .form-group > div{ + -ms-flex:0 0 50%; + flex:0 0 50%; + max-width:50%; + } + + .former-label-col-sm-6 .form-group > label{ + -ms-flex:0 0 33.333333%; + flex:0 0 33.333333%; + max-width:33.333333%; + } + + + .fa-facebook:hover { + color: #3B5998; + + } + + .fa-twitter:hover { + color: #4099FF; + + } + + .fa-linkedin:hover { + color: #007bb5; + + } + + .fa-globe:hover, .fa-github:hover { + color: grey; + + } + + .dropup .dropdown-toggle:after{ + margin-top : 4px; + } + + .error-border-select{ + border: 1px solid #dc3545 !important; + } + + .valid-border-select{ + border: 1px solid #28a745 !important; + } + + .a-disabled { + pointer-events: none; + cursor: default; + opacity: 0.6; + } + + .cursor-pointer { + cursor: pointer; + } + + .center-dd-caret:after{ + margin-bottom: auto; + margin-top: auto; + } + + .cursor-default{ + pointer-events: none; + cursor: default; + } + + .dataTable .dropdown-item:focus, .dropdown-item:hover{ + width: auto; + color: #16181b; + text-decoration: none; + background-color: #f8f9fa; + } + + .dropdown-item.active:hover { + background-color: #005BC3!important; + color: white!important; + } + /* + #################################################### + M E D I A Q U E R I E S + #################################################### + */ + + /* + :::::::::::::::::::::::::::::::::::::::::::::::::::: + Bootstrap 4 breakpoints + */ + + + /* + :::::::::::::::::::::::::::::::::::::::::::::::::::: + COL-SM + */ + /* Small devices (landscape phones, 544px and up) */ + @media (max-width: 576px) { + .h2 {font-size: 1.6rem} + } + + + + /* + :::::::::::::::::::::::::::::::::::::::::::::::::::: + COL-MD + */ + /* Medium devices (tablets, 768px and up) */ + @media (min-width: 768px) { + #div-header-select-customer{ + width: 300px; + } + } + + +/* +* END CSS used for Bootstrap 4 +*/ + +.no-padding{ + padding: 0!important; +} + +.padding-10{ + padding: 10px 15px!important; +} + + +.mono { + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; + /* font-size: 12px; */ +} + +.ixpm-select-input-xs { + height: 20px; + line-height: 20px; +} + + + +/* The white background content wrapper */ +.page-content { + background-color: #fff !important; + padding: 20px; + margin: 0 -20px; /* negative indent the amount of the padding to maintain the grid system */ + -webkit-border-radius: 0 0 6px 6px; + -moz-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; + -webkit-box-shadow: 0 1px 2px rgba(0,0,0,.15); + -moz-box-shadow: 0 1px 2px rgba(0,0,0,.15); + box-shadow: 0 1px 2px rgba(0,0,0,.15); +} + + +.list_preamble_container { + display: block; +} + +.list_preamble { + display: block; + text-align: right; +} + + + + + + +.pm-table { + margin-bottom: 18px; + margin-left: auto; + margin-right: auto; +} + + +.pm-table th { + padding: 0px; + text-align: center; + vertical-align: bottom; + border: 1px solid #dddddd; + border-bottom-width: 2px; + border-top: none; + font-weight: bold; +} + +.pm-table th.asn { + cursor: hand; + cursor: pointer; +} + +.pm-table th:last-child { + border-right-width: 2px; +} + +.pm-table td { + text-align: center; + vertical-align: top; + border: 1px solid #dddddd; +} + +.pm-table td.peering { + cursor: hand; + cursor: pointer; +} + +.pm-table td.zoom1 { + padding: 0px 1px; + font-size: 8px; + line-height: 12px; +} + +.pm-table td.zoom2 { + padding: 2px 4px; + font-size: 11px; + line-height: 15px; +} + +.pm-table td.zoom3 { + padding: 4px 8px; + font-size: 13px; + line-height: 18px; +} + +.pm-table td.zoom4 { + padding: 6px 10px; + font-size: 15px; + line-height: 20px; +} + +.pm-table td.zoom5 { + padding: 8px 12px; + font-size: 18px; + line-height: 24px; +} + +.pm-table th.zoom1 { + font-size: 8px; + line-height: 12px; +} + +.pm-table th.zoom2 { + font-size: 11px; + line-height: 15px; +} + +.pm-table th.zoom3 { + font-size: 13px; + line-height: 18px; +} + +.pm-table th.zoom4 { + font-size: 15px; + line-height: 20px; +} + +.pm-table th.zoom5 { + font-size: 18px; + line-height: 24px; +} + +.pm-table td:last-child { + border-right-width: 2px; +} + + + +.pm-table th.name { + border-left: none; + border-right: none; + border-top: none; +} + +.pm-table th.asn { + border-left: none; + border-top: none; + border-right-width: 2px; +} + +.pm-table td.name { + text-align: left; + border-left: none; + border-right: none; + font-weight: bold; +} + +.pm-table td.asn { + text-align: right; + border-left: none; + border-right-width: 2px; + font-weight: bold; +} + +.pm-table tr:last-child { + border-bottom-width: 2px; +} + + +.pm-table td.peered { + background-color: #00cc00; + width: 10px; +} + +.pm-table td.not-peered { + background-color: #d63030; + width: 10px; +} + +.pm-table td.bilateral-rs { + width: 10px; +} + +.pm-table td.rs-only { + width: 10px; +} + +.pm-table td.bilateral-only { + width: 10px; +} + + +.pm-table tr.hover, .pm-table colgroup.hover, .pm-table td.hover { + background-color: #eee; + border-left: 2px solid #555; + border-right: 2px solid #555; +} + +.pm-table td.hover2 { + border-left: 1px solid #555; + border-right: 1px solid #555; +} + + +.pm-table th.highlight, .pm-table td.highlight { + background-color: #fcf6cf; +} + +.pm-table th.highlight2, .pm-table td.highlight2 { + background-color: #eee; +} + +.controls-fieldset-bordered { + margin-left: 90px; +} + +.legend-fieldset-bordered { + font-size: 14px; + line-height: 20px; + margin-bottom: 0px; + border-style: none none none; + width: none; + padding: 5px; +} + +.fieldset-bordered { + border: 1px solid #CCCCCC; + padding: 10px; + border-radius: 4px 4px 4px 4px; + transition: border 0.2s linear 0s, box-shadow 0.2s linear 0s; +} + +.info_box .title { + position: relative; + top: -30px; + margin-left: 0em; + padding: 2px 7px; + display: inline; + font-size: 1em; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + border: 1px solid #dddddd; + background-color: #fafafa; + background-color: #f9f9f9; + background-image: -moz-linear-gradient( top, #ffffff, #f0f0f0 ); + background-image: -webkit-gradient( linear, 0 0, 0 100%, from(#ffffff), to(#f0f0f0) ); + background-image: -webkit-linear-gradient(top, #ffffff, #f0f0f0); + background-image: -o-linear-gradient(top, #ffffff, #f0f0f0); + background-image: linear-gradient(to bottom, #ffffff, #f0f0f0); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff0f0f0', GradientType=0); +} + +.info_box .title a{ + color:#555555; + text-decoration:none; +} +.info_box .content{ + margin-top:-10px; +} + + +/* Bootstrap Dropdowns in Breadcrumbs: + http://stackoverflow.com/questions/12194573/bootstrap-dropdown-within-breadcrumb */ +.dropdown-menu li { + display: block; +} + + + + + + +.droppable { + border: #ccc 1px solid; + border-radius: 8px; + background: #eee; + color: #666; + padding: 20px; + margin: 10px; + clear: both; + text-align: center; +} + +.droppable.hover { + background: #ddd; +} + +.uploadList { + margin: 0; + padding: 0; + list-style: none; +} + +.uploadItem { + overflow: hidden; + border-bottom: #BCBCBC 1px solid; + margin: 0 20px; + padding: 3px; +} + +.uploadItem span { + overflow: hidden; + width: 150px; + float: left; + display: block; +} + +a.addInputRow, +a.delInputRow, +.uploadItem a { + display: inline-block; + background: url(add.png) no-repeat; + height: 16px; + width: 16px; + text-indent: -999px; +} + +.uploadItem a { + float: left; + display: block; + padding-left: 20px; + background-image: url(delete.png); +} + +a.delInputRow { + background-image: url(delete.png); +} + +.progress { + margin: 5px 0; + height: 15px; + border-radius: 3px; + background: #545A74; +} + +.upperCase { + text-transform:uppercase; +} + +.lowerCase { + text-transform:lowercase; +} + + + +td a:hover{ + text-decoration: none; +} + + +.lb-sm { + font-size: 12px; +} + +.lb-md { + font-size: 16px; +} + +.lb-lg { + font-size: 20px; +} + +.asn-table{ + margin-left: 30px; + font-family: 'Courier New', Courier, monospace; +} + +.asn-table tr td{ + padding : 1px; + padding-left: 5px; +} + +.scrollable-dropdown { + height: auto; + max-height: 200px; + overflow-x: hidden; +} + +.table-no-border > thead > tr > th, +.table-no-border > tbody > tr > th, +.table-no-border > tfoot > tr > th, +.table-no-border > thead > tr > td, +.table-no-border > tbody > tr > td, +.table-no-border > tfoot > tr > td { + border: none !important; +} + +.page-content .page-header li.pull-right { + list-style-type: none !important; +} + +.loader { + border: 6px solid #f3f3f3; + border-radius: 50%; + border-top: 6px solid #3498db; + width: 30px; + height: 30px; + -webkit-animation: spin 2s linear infinite; /* Safari */ + animation: spin 2s linear infinite; +} + +/* Safari */ +@-webkit-keyframes spin { + 0% { -webkit-transform: rotate(0deg); } + 100% { -webkit-transform: rotate(360deg); } +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +.wrap { + -ms-word-break: break-all; + word-break: break-all; + + /* Non standard for webkit */ + word-break: break-word; + + -webkit-hyphens: auto; + -moz-hyphens: auto; + -ms-hyphens: auto; + hyphens: auto; +} + + + +.form-control::placeholder { /* Chrome, Firefox, Opera, Safari 10.1+ */ + font-weight: lighter; + opacity: 1; /* Firefox */ +} + +.select2-container--default .select2-selection { + width: 100%; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + border-radius: .125rem; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + padding-left: .5rem; + padding-right: 1.5rem; + line-height: 1.25; + --tw-text-opacity: 1; + border-width: 1px; + --tw-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06); + box-shadow: var(--tw-ring-offset-shadow, 0 0 rgba(0, 0, 0, 0)), var(--tw-ring-shadow, 0 0 rgba(0, 0, 0, 0)), var(--tw-shadow); + height: 2.25rem; + border-color: #ced4da; +} +.select2-container--default .select2-selection .select2-selection__rendered { + color: rgb(96 111 123 / var(--tw-text-opacity)); + line-height: 1.25rem; +} +.select2-container--default .select2-selection .select2-selection__arrow { + height: 2.25rem; + width: 1.5rem; +} + +/***** Tab Panels START *****/ +.tabNavMenu{ + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + padding-left: 0; + margin-bottom: 0; + list-style: none; + border-bottom: 1px solid rgba(0,0,0,.125); +} +.tabButton { + margin-bottom: -1px; + padding: .5rem 1rem; + background-color: transparent; + border: 1px solid transparent; + border-bottom: 0; + border-top-left-radius: .5rem; + border-top-right-radius: .5rem; + color: #718096; + font-size: .85em; + margin-right: .25rem; +} +.tabButton:hover { + border-color: rgba(0,0,0,.075); +} +.tabButton.active { + color: #4a5568; + background-color: #fff; + border-color: rgba(0,0,0,.125); +} +.tabContent {} +.tabContent .tabPanel { + overflow: hidden; + box-sizing: border-box; + padding: 0; + height: 0; + border: none; + border-bottom-left-radius: .5rem; + border-bottom-right-radius: .5rem; + opacity: 0; + transition: opacity 500ms; +} +.tabContent .tabPanel.active { + opacity: 1; + border: 1px solid rgba(0,0,0,.125); + border-top: none; + height: auto; + padding: 1rem 1.5rem; +} +.inputWrapper { + padding: 1rem; +} +.inputWrapper:nth-child(odd) { + background: rgba(0,0,0,.025); +} +.inputWrapper .form-group { + margin-bottom: 0; +} +.inputWrapper .form-group .form-check label { + display: block; + font-size: .875rem; + font-weight: 700; + color: rgb(96 111 123); +} +/***** Tab Panels END *******/ + +/* above 80px scrolldown */ +.badgeSwitcherFix { + position: fixed; + top: 0; + right: 0; + padding: .5rem; + background: rgba(255, 255, 255, .9); + border-radius: 0 0 0 1rem; +} + +/*** Diagnostics Result Page Start ***/ +.info-line { + position: relative; + height: fit-content !important; +} +.info-content { + position: relative; + overflow: hidden; + width: calc(100% - 6.5rem); + line-height: 1.2rem; + height: 1.2rem; + border: 1px solid transparent; + font-size: .9rem; + transition: height .25s; +} +.info-content .info-extra-content { + font-size: .75rem; + position: relative; +} +.info-content .info-extra-content pre { + margin: 0; +} +/**** Diagnostics Result Page End ****/ diff --git a/public/css/ixp-pack.css b/public/css/ixp-pack.css new file mode 100644 index 000000000..2952ed0cc --- /dev/null +++ b/public/css/ixp-pack.css @@ -0,0 +1,9 @@ +/*! + * Bootstrap v4.6.2 (https://getbootstrap.com/) + * Copyright 2011-2022 The Bootstrap Authors + * Copyright 2011-2022 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus:not(:focus-visible){outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([class]){color:inherit;text-decoration:none}a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit;text-align:-webkit-match-parent}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:2.5rem}.h2,h2{font-size:2rem}.h3,h3{font-size:1.75rem}.h4,h4{font-size:1.5rem}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem;font-weight:300;line-height:1.2}.display-2{font-size:5.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4.5rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:.875em;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote-footer{display:block;font-size:.875em;color:#6c757d}.blockquote-footer::before{content:"\2014\00A0"}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#6c757d}code{font-size:87.5%;color:#e83e8c;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#212529}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container,.container-fluid,.container-lg,.container-md,.container-sm,.container-xl{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-ms-flex-order:-1;order:-1}.order-last{-ms-flex-order:13;order:13}.order-0{-ms-flex-order:0;order:0}.order-1{-ms-flex-order:1;order:1}.order-2{-ms-flex-order:2;order:2}.order-3{-ms-flex-order:3;order:3}.order-4{-ms-flex-order:4;order:4}.order-5{-ms-flex-order:5;order:5}.order-6{-ms-flex-order:6;order:6}.order-7{-ms-flex-order:7;order:7}.order-8{-ms-flex-order:8;order:8}.order-9{-ms-flex-order:9;order:9}.order-10{-ms-flex-order:10;order:10}.order-11{-ms-flex-order:11;order:11}.order-12{-ms-flex-order:12;order:12}.offset-1{margin-left:8.333333%}.offset-2{margin-left:16.666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.333333%}.offset-5{margin-left:41.666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.333333%}.offset-8{margin-left:66.666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.333333%}.offset-11{margin-left:91.666667%}@media (min-width:576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-sm-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-sm-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-sm-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-sm-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-sm-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-sm-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-sm-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-sm-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-sm-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-sm-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-sm-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-sm-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-sm-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-ms-flex-order:-1;order:-1}.order-sm-last{-ms-flex-order:13;order:13}.order-sm-0{-ms-flex-order:0;order:0}.order-sm-1{-ms-flex-order:1;order:1}.order-sm-2{-ms-flex-order:2;order:2}.order-sm-3{-ms-flex-order:3;order:3}.order-sm-4{-ms-flex-order:4;order:4}.order-sm-5{-ms-flex-order:5;order:5}.order-sm-6{-ms-flex-order:6;order:6}.order-sm-7{-ms-flex-order:7;order:7}.order-sm-8{-ms-flex-order:8;order:8}.order-sm-9{-ms-flex-order:9;order:9}.order-sm-10{-ms-flex-order:10;order:10}.order-sm-11{-ms-flex-order:11;order:11}.order-sm-12{-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.333333%}.offset-sm-2{margin-left:16.666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.333333%}.offset-sm-5{margin-left:41.666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.333333%}.offset-sm-8{margin-left:66.666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.333333%}.offset-sm-11{margin-left:91.666667%}}@media (min-width:768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-md-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-md-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-md-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-md-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-md-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-md-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-md-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-md-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-md-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-md-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-md-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-md-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-md-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-md-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-ms-flex-order:-1;order:-1}.order-md-last{-ms-flex-order:13;order:13}.order-md-0{-ms-flex-order:0;order:0}.order-md-1{-ms-flex-order:1;order:1}.order-md-2{-ms-flex-order:2;order:2}.order-md-3{-ms-flex-order:3;order:3}.order-md-4{-ms-flex-order:4;order:4}.order-md-5{-ms-flex-order:5;order:5}.order-md-6{-ms-flex-order:6;order:6}.order-md-7{-ms-flex-order:7;order:7}.order-md-8{-ms-flex-order:8;order:8}.order-md-9{-ms-flex-order:9;order:9}.order-md-10{-ms-flex-order:10;order:10}.order-md-11{-ms-flex-order:11;order:11}.order-md-12{-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.333333%}.offset-md-2{margin-left:16.666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.333333%}.offset-md-5{margin-left:41.666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.333333%}.offset-md-8{margin-left:66.666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.333333%}.offset-md-11{margin-left:91.666667%}}@media (min-width:992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-lg-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-lg-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-lg-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-lg-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-lg-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-lg-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-lg-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-lg-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-lg-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-lg-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-lg-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-lg-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-lg-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-ms-flex-order:-1;order:-1}.order-lg-last{-ms-flex-order:13;order:13}.order-lg-0{-ms-flex-order:0;order:0}.order-lg-1{-ms-flex-order:1;order:1}.order-lg-2{-ms-flex-order:2;order:2}.order-lg-3{-ms-flex-order:3;order:3}.order-lg-4{-ms-flex-order:4;order:4}.order-lg-5{-ms-flex-order:5;order:5}.order-lg-6{-ms-flex-order:6;order:6}.order-lg-7{-ms-flex-order:7;order:7}.order-lg-8{-ms-flex-order:8;order:8}.order-lg-9{-ms-flex-order:9;order:9}.order-lg-10{-ms-flex-order:10;order:10}.order-lg-11{-ms-flex-order:11;order:11}.order-lg-12{-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.333333%}.offset-lg-2{margin-left:16.666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.333333%}.offset-lg-5{margin-left:41.666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.333333%}.offset-lg-8{margin-left:66.666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.333333%}.offset-lg-11{margin-left:91.666667%}}@media (min-width:1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-xl-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-xl-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-xl-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-xl-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-xl-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-xl-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-xl-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-xl-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-xl-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-xl-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-xl-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-xl-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-xl-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-ms-flex-order:-1;order:-1}.order-xl-last{-ms-flex-order:13;order:13}.order-xl-0{-ms-flex-order:0;order:0}.order-xl-1{-ms-flex-order:1;order:1}.order-xl-2{-ms-flex-order:2;order:2}.order-xl-3{-ms-flex-order:3;order:3}.order-xl-4{-ms-flex-order:4;order:4}.order-xl-5{-ms-flex-order:5;order:5}.order-xl-6{-ms-flex-order:6;order:6}.order-xl-7{-ms-flex-order:7;order:7}.order-xl-8{-ms-flex-order:8;order:8}.order-xl-9{-ms-flex-order:9;order:9}.order-xl-10{-ms-flex-order:10;order:10}.order-xl-11{-ms-flex-order:11;order:11}.order-xl-12{-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.333333%}.offset-xl-2{margin-left:16.666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.333333%}.offset-xl-5{margin-left:41.666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.333333%}.offset-xl-8{margin-left:66.666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.333333%}.offset-xl-11{margin-left:91.666667%}}.table{width:100%;margin-bottom:1rem;color:#212529}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody{border-top:2px solid #dee2e6}.table-sm td,.table-sm th{padding:.3rem}.table-bordered{border:1px solid #dee2e6}.table-bordered td,.table-bordered th{border:1px solid #dee2e6}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-borderless tbody+tbody,.table-borderless td,.table-borderless th,.table-borderless thead th{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.05)}.table-hover tbody tr:hover{color:#212529;background-color:rgba(0,0,0,.075)}.table-primary,.table-primary>td,.table-primary>th{background-color:#b8daff}.table-primary tbody+tbody,.table-primary td,.table-primary th,.table-primary thead th{border-color:#7abaff}.table-hover .table-primary:hover{background-color:#9fcdff}.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#9fcdff}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#d6d8db}.table-secondary tbody+tbody,.table-secondary td,.table-secondary th,.table-secondary thead th{border-color:#b3b7bb}.table-hover .table-secondary:hover{background-color:#c8cbcf}.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#c8cbcf}.table-success,.table-success>td,.table-success>th{background-color:#c3e6cb}.table-success tbody+tbody,.table-success td,.table-success th,.table-success thead th{border-color:#8fd19e}.table-hover .table-success:hover{background-color:#b1dfbb}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#b1dfbb}.table-info,.table-info>td,.table-info>th{background-color:#bee5eb}.table-info tbody+tbody,.table-info td,.table-info th,.table-info thead th{border-color:#86cfda}.table-hover .table-info:hover{background-color:#abdde5}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#abdde5}.table-warning,.table-warning>td,.table-warning>th{background-color:#ffeeba}.table-warning tbody+tbody,.table-warning td,.table-warning th,.table-warning thead th{border-color:#ffdf7e}.table-hover .table-warning:hover{background-color:#ffe8a1}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#ffe8a1}.table-danger,.table-danger>td,.table-danger>th{background-color:#f5c6cb}.table-danger tbody+tbody,.table-danger td,.table-danger th,.table-danger thead th{border-color:#ed969e}.table-hover .table-danger:hover{background-color:#f1b0b7}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f1b0b7}.table-light,.table-light>td,.table-light>th{background-color:#fdfdfe}.table-light tbody+tbody,.table-light td,.table-light th,.table-light thead th{border-color:#fbfcfc}.table-hover .table-light:hover{background-color:#ececf6}.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>td,.table-dark>th{background-color:#c6c8ca}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#95999c}.table-hover .table-dark:hover{background-color:#b9bbbe}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>td,.table-active>th{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,.075)}.table .thead-dark th{color:#fff;background-color:#343a40;border-color:#454d55}.table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#fff;background-color:#343a40}.table-dark td,.table-dark th,.table-dark thead th{border-color:#454d55}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,.05)}.table-dark.table-hover tbody tr:hover{color:#fff;background-color:rgba(255,255,255,.075)}@media (max-width:575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-sm>.table-bordered{border:0}}@media (max-width:767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-md>.table-bordered{border:0}}@media (max-width:991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-lg>.table-bordered{border:0}}@media (max-width:1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:focus{color:#495057;background-color:#fff;border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.form-control::-webkit-input-placeholder{color:#6c757d;opacity:1}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control:-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}input[type=date].form-control,input[type=datetime-local].form-control,input[type=month].form-control,input[type=time].form-control{-webkit-appearance:none;-moz-appearance:none;appearance:none}select.form-control:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}select.form-control:focus::-ms-value{color:#495057;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;font-size:1rem;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.form-control-lg{height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}select.form-control[multiple],select.form-control[size]{height:auto}textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{color:#6c757d}.form-check-label{margin-bottom:0}.form-check-inline{display:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:#28a745}.valid-tooltip{position:absolute;top:100%;left:0;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(40,167,69,.9);border-radius:.25rem}.form-row>.col>.valid-tooltip,.form-row>[class*=col-]>.valid-tooltip{left:5px}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:#28a745;padding-right:calc(1.5em + .75rem)!important;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.was-validated select.form-control:valid,select.form-control.is-valid{padding-right:3rem!important;background-position:right 1.5rem center}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-valid,.was-validated .custom-select:valid{border-color:#28a745;padding-right:calc(.75em + 2.3125rem)!important;background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") right .75rem center/8px 10px no-repeat,#fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem) no-repeat}.custom-select.is-valid:focus,.was-validated .custom-select:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#28a745}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#28a745}.custom-control-input.is-valid~.custom-control-label::before,.was-validated .custom-control-input:valid~.custom-control-label::before{border-color:#28a745}.custom-control-input.is-valid:checked~.custom-control-label::before,.was-validated .custom-control-input:valid:checked~.custom-control-label::before{border-color:#34ce57;background-color:#34ce57}.custom-control-input.is-valid:focus~.custom-control-label::before,.was-validated .custom-control-input:valid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-control-input.is-valid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:valid:focus:not(:checked)~.custom-control-label::before{border-color:#28a745}.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#28a745}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;left:0;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.form-row>.col>.invalid-tooltip,.form-row>[class*=col-]>.invalid-tooltip{left:5px}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:calc(1.5em + .75rem)!important;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.was-validated select.form-control:invalid,select.form-control.is-invalid{padding-right:3rem!important;background-position:right 1.5rem center}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-invalid,.was-validated .custom-select:invalid{border-color:#dc3545;padding-right:calc(.75em + 2.3125rem)!important;background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") right .75rem center/8px 10px no-repeat,#fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e") center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem) no-repeat}.custom-select.is-invalid:focus,.was-validated .custom-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#dc3545}.custom-control-input.is-invalid~.custom-control-label::before,.was-validated .custom-control-input:invalid~.custom-control-label::before{border-color:#dc3545}.custom-control-input.is-invalid:checked~.custom-control-label::before,.was-validated .custom-control-input:invalid:checked~.custom-control-label::before{border-color:#e4606d;background-color:#e4606d}.custom-control-input.is-invalid:focus~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-control-input.is-invalid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus:not(:checked)~.custom-control-label::before{border-color:#dc3545}.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#dc3545}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-inline{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width:576px){.form-inline label{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;margin-bottom:0}.form-inline .form-group{display:-ms-flexbox;display:flex;-ms-flex:0 0 auto;flex:0 0 auto;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center;margin-bottom:0}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .custom-select,.form-inline .input-group{width:auto}.form-inline .form-check{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;-ms-flex-negative:0;flex-shrink:0;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;color:#212529;text-align:center;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#212529;text-decoration:none}.btn.focus,.btn:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.btn.disabled,.btn:disabled{opacity:.65}.btn:not(:disabled):not(.disabled){cursor:pointer}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:hover{color:#fff;background-color:#0069d9;border-color:#0062cc}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#0069d9;border-color:#0062cc;box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0062cc;border-color:#005cbf}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5a6268;border-color:#545b62}.btn-secondary.focus,.btn-secondary:focus{color:#fff;background-color:#5a6268;border-color:#545b62;box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#545b62;border-color:#4e555b}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-success{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:hover{color:#fff;background-color:#218838;border-color:#1e7e34}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#218838;border-color:#1e7e34;box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#1e7e34;border-color:#1c7430}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-info{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:hover{color:#fff;background-color:#138496;border-color:#117a8b}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#138496;border-color:#117a8b;box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#117a8b;border-color:#10707f}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-warning{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#212529;background-color:#e0a800;border-color:#d39e00}.btn-warning.focus,.btn-warning:focus{color:#212529;background-color:#e0a800;border-color:#d39e00;box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#212529;background-color:#d39e00;border-color:#c69500}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#c82333;border-color:#bd2130}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c82333;border-color:#bd2130;box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#bd2130;border-color:#b21f2d}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-light{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#212529;background-color:#e2e6ea;border-color:#dae0e5}.btn-light.focus,.btn-light:focus{color:#212529;background-color:#e2e6ea;border-color:#dae0e5;box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-light.disabled,.btn-light:disabled{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#212529;background-color:#dae0e5;border-color:#d3d9df}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark.focus,.btn-dark:focus{color:#fff;background-color:#23272b;border-color:#1d2124;box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-outline-primary{color:#007bff;border-color:#007bff}.btn-outline-primary:hover{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary.focus,.btn-outline-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#007bff;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-secondary{color:#6c757d;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary.focus,.btn-outline-secondary:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-success{color:#28a745;border-color:#28a745}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success.focus,.btn-outline-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-info{color:#17a2b8;border-color:#17a2b8}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info.focus,.btn-outline-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-warning{color:#ffc107;border-color:#ffc107}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning.focus,.btn-outline-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-danger{color:#dc3545;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger.focus,.btn-outline-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.focus,.btn-outline-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-dark{color:#343a40;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.focus,.btn-outline-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-link{font-weight:400;color:#007bff;text-decoration:none}.btn-link:hover{color:#0056b3;text-decoration:underline}.btn-link.focus,.btn-link:focus{text-decoration:underline}.btn-link.disabled,.btn-link:disabled{color:#6c757d;pointer-events:none}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.collapsing.width{width:0;height:auto;transition:width .35s ease}@media (prefers-reduced-motion:reduce){.collapsing.width{transition:none}}.dropdown,.dropleft,.dropright,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu-left{right:auto;left:0}.dropdown-menu-right{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-left{right:auto;left:0}.dropdown-menu-sm-right{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-left{right:auto;left:0}.dropdown-menu-md-right{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-left{right:auto;left:0}.dropdown-menu-lg-right{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-left{right:auto;left:0}.dropdown-menu-xl-right{right:0;left:auto}}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-toggle::after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropleft .dropdown-toggle::after{display:none}.dropleft .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-menu[x-placement^=bottom],.dropdown-menu[x-placement^=left],.dropdown-menu[x-placement^=right],.dropdown-menu[x-placement^=top]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#16181b;text-decoration:none;background-color:#e9ecef}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#007bff}.dropdown-item.disabled,.dropdown-item:disabled{color:#adb5bd;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1.5rem;color:#212529}.btn-group,.btn-group-vertical{position:relative;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;-ms-flex:1 1 auto;flex:1 1 auto}.btn-group-vertical>.btn:hover,.btn-group>.btn:hover{z-index:1}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus{z-index:1}.btn-toolbar{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-pack:start;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropright .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropleft .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{-ms-flex-direction:column;flex-direction:column;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:center;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio],.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control,.input-group>.form-control-plaintext{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;width:1%;min-width:0;margin-bottom:0}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control,.input-group>.form-control-plaintext+.custom-file,.input-group>.form-control-plaintext+.custom-select,.input-group>.form-control-plaintext+.form-control{margin-left:-1px}.input-group>.custom-file .custom-file-input:focus~.custom-file-label,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label::after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group:not(.has-validation)>.custom-file:not(:last-child) .custom-file-label,.input-group:not(.has-validation)>.custom-file:not(:last-child) .custom-file-label::after,.input-group:not(.has-validation)>.custom-select:not(:last-child),.input-group:not(.has-validation)>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group.has-validation>.custom-file:nth-last-child(n+3) .custom-file-label,.input-group.has-validation>.custom-file:nth-last-child(n+3) .custom-file-label::after,.input-group.has-validation>.custom-select:nth-last-child(n+3),.input-group.has-validation>.form-control:nth-last-child(n+3){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-append,.input-group-prepend{display:-ms-flexbox;display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn:focus,.input-group-prepend .btn:focus{z-index:3}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.375rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group-lg>.custom-select,.input-group-lg>.form-control:not(textarea){height:calc(1.5em + 1rem + 2px)}.input-group-lg>.custom-select,.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.input-group-sm>.custom-select,.input-group-sm>.form-control:not(textarea){height:calc(1.5em + .5rem + 2px)}.input-group-sm>.custom-select,.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.input-group-lg>.custom-select,.input-group-sm>.custom-select{padding-right:1.75rem}.input-group.has-validation>.input-group-append:nth-last-child(n+3)>.btn,.input-group.has-validation>.input-group-append:nth-last-child(n+3)>.input-group-text,.input-group:not(.has-validation)>.input-group-append:not(:last-child)>.btn,.input-group:not(.has-validation)>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;z-index:1;display:block;min-height:1.5rem;padding-left:1.5rem;-webkit-print-color-adjust:exact;color-adjust:exact;print-color-adjust:exact}.custom-control-inline{display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;left:0;z-index:-1;width:1rem;height:1.25rem;opacity:0}.custom-control-input:checked~.custom-control-label::before{color:#fff;border-color:#007bff;background-color:#007bff}.custom-control-input:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-control-input:focus:not(:checked)~.custom-control-label::before{border-color:#80bdff}.custom-control-input:not(:disabled):active~.custom-control-label::before{color:#fff;background-color:#b3d7ff;border-color:#b3d7ff}.custom-control-input:disabled~.custom-control-label,.custom-control-input[disabled]~.custom-control-label{color:#6c757d}.custom-control-input:disabled~.custom-control-label::before,.custom-control-input[disabled]~.custom-control-label::before{background-color:#e9ecef}.custom-control-label{position:relative;margin-bottom:0;vertical-align:top}.custom-control-label::before{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;pointer-events:none;content:"";background-color:#fff;border:1px solid #adb5bd}.custom-control-label::after{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:"";background:50%/50% 50% no-repeat}.custom-checkbox .custom-control-label::before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::before{border-color:#007bff;background-color:#007bff}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-radio .custom-control-label::before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-switch{padding-left:2.25rem}.custom-switch .custom-control-label::before{left:-2.25rem;width:1.75rem;pointer-events:all;border-radius:.5rem}.custom-switch .custom-control-label::after{top:calc(.25rem + 2px);left:calc(-2.25rem + 2px);width:calc(1rem - 4px);height:calc(1rem - 4px);background-color:#adb5bd;border-radius:.5rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-switch .custom-control-label::after{transition:none}}.custom-switch .custom-control-input:checked~.custom-control-label::after{background-color:#fff;-webkit-transform:translateX(.75rem);transform:translateX(.75rem)}.custom-switch .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-select{display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem 1.75rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;vertical-align:middle;background:#fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") right .75rem center/8px 10px no-repeat;border:1px solid #ced4da;border-radius:.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#6c757d;background-color:#e9ecef}.custom-select::-ms-expand{display:none}.custom-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}.custom-select-sm{height:calc(1.5em + .5rem + 2px);padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.custom-select-lg{height:calc(1.5em + 1rem + 2px);padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.custom-file{position:relative;display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(1.5em + .75rem + 2px);margin:0;overflow:hidden;opacity:0}.custom-file-input:focus~.custom-file-label{border-color:#80bdff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-file-input:disabled~.custom-file-label,.custom-file-input[disabled]~.custom-file-label{background-color:#e9ecef}.custom-file-input:lang(en)~.custom-file-label::after{content:"Browse"}.custom-file-input~.custom-file-label[data-browse]::after{content:attr(data-browse)}.custom-file-label{position:absolute;top:0;right:0;left:0;z-index:1;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;overflow:hidden;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:calc(1.5em + .75rem);padding:.375rem .75rem;line-height:1.5;color:#495057;content:"Browse";background-color:#e9ecef;border-left:inherit;border-radius:0 .25rem .25rem 0}.custom-range{width:100%;height:1.4rem;padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:0}.custom-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#007bff;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#b3d7ff}.custom-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#007bff;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-moz-range-thumb{-moz-transition:none;transition:none}}.custom-range::-moz-range-thumb:active{background-color:#b3d7ff}.custom-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:.2rem;margin-left:.2rem;background-color:#007bff;border:0;border-radius:1rem;-ms-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-ms-thumb{-ms-transition:none;transition:none}}.custom-range::-ms-thumb:active{background-color:#b3d7ff}.custom-range::-ms-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:.5rem}.custom-range::-ms-fill-lower{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px;background-color:#dee2e6;border-radius:1rem}.custom-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.custom-range:disabled::-webkit-slider-runnable-track{cursor:default}.custom-range:disabled::-moz-range-thumb{background-color:#adb5bd}.custom-range:disabled::-moz-range-track{cursor:default}.custom-range:disabled::-ms-thumb{background-color:#adb5bd}.custom-control-label::before,.custom-file-label,.custom-select{transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-control-label::before,.custom-file-label,.custom-select{transition:none}}.nav{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-link{margin-bottom:-1px;background-color:transparent;border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{isolation:isolate;border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{background:0 0;border:0;border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#007bff}.nav-fill .nav-item,.nav-fill>.nav-link{-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item,.nav-justified>.nav-link{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;padding:.5rem 1rem}.navbar .container,.navbar .container-fluid,.navbar .container-lg,.navbar .container-md,.navbar .container-sm,.navbar .container-xl{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{-ms-flex-preferred-size:100%;flex-basis:100%;-ms-flex-positive:1;flex-grow:1;-ms-flex-align:center;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:50%/100% 100% no-repeat}.navbar-nav-scroll{max-height:75vh;overflow-y:auto}@media (max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{padding-right:0;padding-left:0}}@media (min-width:576px){.navbar-expand-sm{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-nav-scroll{overflow:visible}.navbar-expand-sm .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (max-width:767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{padding-right:0;padding-left:0}}@media (min-width:768px){.navbar-expand-md{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-nav-scroll{overflow:visible}.navbar-expand-md .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (max-width:991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{padding-right:0;padding-left:0}}@media (min-width:992px){.navbar-expand-lg{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-nav-scroll{overflow:visible}.navbar-expand-lg .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (max-width:1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{padding-right:0;padding-left:0}}@media (min-width:1200px){.navbar-expand-xl{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-nav-scroll{overflow:visible}.navbar-expand-xl .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-nav-scroll{overflow:visible}.navbar-expand .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.5%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a{color:rgba(0,0,0,.9)}.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.5);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.5%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,.5)}.navbar-dark .navbar-text a{color:#fff}.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0}.card-body{-ms-flex:1 1 auto;flex:1 1 auto;min-height:1px;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-footer{padding:.75rem 1.25rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.625rem;margin-bottom:-.75rem;margin-left:-.625rem;border-bottom:0}.card-header-pills{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem;border-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom,.card-img-top{-ms-flex-negative:0;flex-shrink:0;width:100%}.card-img,.card-img-top{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck .card{margin-bottom:15px}@media (min-width:576px){.card-deck{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{-ms-flex:1 0 0%;flex:1 0 0%;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group>.card{margin-bottom:15px}@media (min-width:576px){.card-group{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-ms-flex:1 0 0%;flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.card-columns .card{margin-bottom:.75rem}@media (min-width:576px){.card-columns{-webkit-column-count:3;-moz-column-count:3;column-count:3;-webkit-column-gap:1.25rem;-moz-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion{overflow-anchor:none}.accordion>.card{overflow:hidden}.accordion>.card:not(:last-of-type){border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion>.card:not(:first-of-type){border-top-left-radius:0;border-top-right-radius:0}.accordion>.card>.card-header{border-radius:0;margin-bottom:-1px}.breadcrumb{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{float:left;padding-right:.5rem;color:#6c757d;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#6c757d}.pagination{display:-ms-flexbox;display:flex;padding-left:0;list-style:none;border-radius:.25rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#007bff;background-color:#fff;border:1px solid #dee2e6}.page-link:hover{z-index:2;color:#0056b3;text-decoration:none;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:3;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item.active .page-link{z-index:3;color:#fff;background-color:#007bff;border-color:#007bff}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dee2e6}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.badge{transition:none}}a.badge:focus,a.badge:hover{text-decoration:none}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#007bff}a.badge-primary:focus,a.badge-primary:hover{color:#fff;background-color:#0062cc}a.badge-primary.focus,a.badge-primary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.badge-secondary{color:#fff;background-color:#6c757d}a.badge-secondary:focus,a.badge-secondary:hover{color:#fff;background-color:#545b62}a.badge-secondary.focus,a.badge-secondary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.badge-success{color:#fff;background-color:#28a745}a.badge-success:focus,a.badge-success:hover{color:#fff;background-color:#1e7e34}a.badge-success.focus,a.badge-success:focus{outline:0;box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.badge-info{color:#fff;background-color:#17a2b8}a.badge-info:focus,a.badge-info:hover{color:#fff;background-color:#117a8b}a.badge-info.focus,a.badge-info:focus{outline:0;box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.badge-warning{color:#212529;background-color:#ffc107}a.badge-warning:focus,a.badge-warning:hover{color:#212529;background-color:#d39e00}a.badge-warning.focus,a.badge-warning:focus{outline:0;box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.badge-danger{color:#fff;background-color:#dc3545}a.badge-danger:focus,a.badge-danger:hover{color:#fff;background-color:#bd2130}a.badge-danger.focus,a.badge-danger:focus{outline:0;box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.badge-light{color:#212529;background-color:#f8f9fa}a.badge-light:focus,a.badge-light:hover{color:#212529;background-color:#dae0e5}a.badge-light.focus,a.badge-light:focus{outline:0;box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.badge-dark{color:#fff;background-color:#343a40}a.badge-dark:focus,a.badge-dark:hover{color:#fff;background-color:#1d2124}a.badge-dark.focus,a.badge-dark:focus{outline:0;box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:.3rem}@media (min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:4rem}.alert-dismissible .close{position:absolute;top:0;right:0;z-index:2;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#004085;background-color:#cce5ff;border-color:#b8daff}.alert-primary hr{border-top-color:#9fcdff}.alert-primary .alert-link{color:#002752}.alert-secondary{color:#383d41;background-color:#e2e3e5;border-color:#d6d8db}.alert-secondary hr{border-top-color:#c8cbcf}.alert-secondary .alert-link{color:#202326}.alert-success{color:#155724;background-color:#d4edda;border-color:#c3e6cb}.alert-success hr{border-top-color:#b1dfbb}.alert-success .alert-link{color:#0b2e13}.alert-info{color:#0c5460;background-color:#d1ecf1;border-color:#bee5eb}.alert-info hr{border-top-color:#abdde5}.alert-info .alert-link{color:#062c33}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.alert-warning hr{border-top-color:#ffe8a1}.alert-warning .alert-link{color:#533f03}.alert-danger{color:#721c24;background-color:#f8d7da;border-color:#f5c6cb}.alert-danger hr{border-top-color:#f1b0b7}.alert-danger .alert-link{color:#491217}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}@-webkit-keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:-ms-flexbox;display:flex;height:1rem;overflow:hidden;line-height:0;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;overflow:hidden;color:#fff;text-align:center;white-space:nowrap;background-color:#007bff;transition:width .6s ease}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:1s linear infinite progress-bar-stripes;animation:1s linear infinite progress-bar-stripes}@media (prefers-reduced-motion:reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.media{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start}.media-body{-ms-flex:1;flex:1}.list-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:.25rem}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#007bff;border-color:#007bff}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:-1px;border-top-width:1px}.list-group-horizontal{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}@media (min-width:576px){.list-group-horizontal-sm{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:768px){.list-group-horizontal-md{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:992px){.list-group-horizontal-lg{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:1200px){.list-group-horizontal-xl{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 1px}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{color:#004085;background-color:#b8daff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#004085;background-color:#9fcdff}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#004085;border-color:#004085}.list-group-item-secondary{color:#383d41;background-color:#d6d8db}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#383d41;background-color:#c8cbcf}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#383d41;border-color:#383d41}.list-group-item-success{color:#155724;background-color:#c3e6cb}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#155724;background-color:#b1dfbb}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#155724;border-color:#155724}.list-group-item-info{color:#0c5460;background-color:#bee5eb}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#0c5460;background-color:#abdde5}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#0c5460;border-color:#0c5460}.list-group-item-warning{color:#856404;background-color:#ffeeba}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#856404;background-color:#ffe8a1}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#856404;border-color:#856404}.list-group-item-danger{color:#721c24;background-color:#f5c6cb}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#721c24;background-color:#f1b0b7}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#721c24;border-color:#721c24}.list-group-item-light{color:#818182;background-color:#fdfdfe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#818182;background-color:#ececf6}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#818182;border-color:#818182}.list-group-item-dark{color:#1b1e21;background-color:#c6c8ca}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#1b1e21;background-color:#b9bbbe}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1b1e21;border-color:#1b1e21}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:hover{color:#000;text-decoration:none}.close:not(:disabled):not(.disabled):focus,.close:not(:disabled):not(.disabled):hover{opacity:.75}button.close{padding:0;background-color:transparent;border:0}a.close.disabled{pointer-events:none}.toast{-ms-flex-preferred-size:350px;flex-basis:350px;max-width:350px;font-size:.875rem;background-color:rgba(255,255,255,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .25rem .75rem rgba(0,0,0,.1);opacity:0;border-radius:.25rem}.toast:not(:last-child){margin-bottom:.75rem}.toast.showing{opacity:1}.toast.show{display:block;opacity:1}.toast.hide{display:none}.toast-header{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.25rem .75rem;color:#6c757d;background-color:rgba(255,255,255,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05);border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.toast-body{padding:.75rem}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;left:0;z-index:1050;display:none;width:100%;height:100%;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out;-webkit-transform:translate(0,-50px);transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{-webkit-transform:none;transform:none}.modal.modal-static .modal-dialog{-webkit-transform:scale(1.02);transform:scale(1.02)}.modal-dialog-scrollable{display:-ms-flexbox;display:flex;max-height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 1rem);overflow:hidden}.modal-dialog-scrollable .modal-footer,.modal-dialog-scrollable .modal-header{-ms-flex-negative:0;flex-shrink:0}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;min-height:calc(100% - 1rem)}.modal-dialog-centered::before{display:block;height:calc(100vh - 1rem);height:-webkit-min-content;height:-moz-min-content;height:min-content;content:""}.modal-dialog-centered.modal-dialog-scrollable{-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;height:100%}.modal-dialog-centered.modal-dialog-scrollable .modal-content{max-height:none}.modal-dialog-centered.modal-dialog-scrollable::before{content:none}.modal-content{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:justify;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #dee2e6;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.modal-header .close{padding:1rem 1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:end;justify-content:flex-end;padding:.75rem;border-top:1px solid #dee2e6;border-bottom-right-radius:calc(.3rem - 1px);border-bottom-left-radius:calc(.3rem - 1px)}.modal-footer>*{margin:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{max-height:calc(100% - 3.5rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-dialog-centered::before{height:calc(100vh - 3.5rem);height:-webkit-min-content;height:-moz-min-content;height:min-content}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow::before,.bs-tooltip-top .arrow::before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow::before,.bs-tooltip-right .arrow::before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow::before,.bs-tooltip-bottom .arrow::before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow::before,.bs-tooltip-left .arrow::before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .arrow{position:absolute;display:block;width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow::after,.popover .arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top]>.arrow,.bs-popover-top>.arrow{bottom:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=top]>.arrow::before,.bs-popover-top>.arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=top]>.arrow::after,.bs-popover-top>.arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right]>.arrow,.bs-popover-right>.arrow{left:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right]>.arrow::before,.bs-popover-right>.arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=right]>.arrow::after,.bs-popover-right>.arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom]>.arrow,.bs-popover-bottom>.arrow{top:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=bottom]>.arrow::before,.bs-popover-bottom>.arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom]>.arrow::after,.bs-popover-bottom>.arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left]>.arrow,.bs-popover-left>.arrow{right:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left]>.arrow::before,.bs-popover-left>.arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left]>.arrow::after,.bs-popover-left>.arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{-ms-touch-action:pan-y;touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;transition:transform .6s ease-in-out,-webkit-transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-right,.carousel-item-next:not(.carousel-item-left){-webkit-transform:translateX(100%);transform:translateX(100%)}.active.carousel-item-left,.carousel-item-prev:not(.carousel-item-right){-webkit-transform:translateX(-100%);transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;-webkit-transform:none;transform:none}.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:15%;padding:0;color:#fff;text-align:center;background:0 0;border:0;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:50%/100% 100% no-repeat}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5L3.75 4l-2.5 2.5L2.75 8l4-4-4-4z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:15;display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{box-sizing:content-box;-ms-flex:0 1 auto;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators li{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}@-webkit-keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:-.125em;border:.25em solid currentcolor;border-right-color:transparent;border-radius:50%;-webkit-animation:.75s linear infinite spinner-border;animation:.75s linear infinite spinner-border}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@-webkit-keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1;-webkit-transform:none;transform:none}}@keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1;-webkit-transform:none;transform:none}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:-.125em;background-color:currentcolor;border-radius:50%;opacity:0;-webkit-animation:.75s linear infinite spinner-grow;animation:.75s linear infinite spinner-grow}.spinner-grow-sm{width:1rem;height:1rem}@media (prefers-reduced-motion:reduce){.spinner-border,.spinner-grow{-webkit-animation-duration:1.5s;animation-duration:1.5s}}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#007bff!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#0062cc!important}.bg-secondary{background-color:#6c757d!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#545b62!important}.bg-success{background-color:#28a745!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#1e7e34!important}.bg-info{background-color:#17a2b8!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#117a8b!important}.bg-warning{background-color:#ffc107!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#d39e00!important}.bg-danger{background-color:#dc3545!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#bd2130!important}.bg-light{background-color:#f8f9fa!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#dae0e5!important}.bg-dark{background-color:#343a40!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#1d2124!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #dee2e6!important}.border-top{border-top:1px solid #dee2e6!important}.border-right{border-right:1px solid #dee2e6!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-left{border-left:1px solid #dee2e6!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#007bff!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#28a745!important}.border-info{border-color:#17a2b8!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#343a40!important}.border-white{border-color:#fff!important}.rounded-sm{border-radius:.2rem!important}.rounded{border-radius:.25rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-right{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-lg{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-0{border-radius:0!important}.clearfix::after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:-ms-flexbox!important;display:flex!important}.d-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:-ms-flexbox!important;display:flex!important}.d-sm-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:-ms-flexbox!important;display:flex!important}.d-md-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:-ms-flexbox!important;display:flex!important}.d-lg-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:-ms-flexbox!important;display:flex!important}.d-xl-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:-ms-flexbox!important;display:flex!important}.d-print-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive::before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9::before{padding-top:42.857143%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-4by3::before{padding-top:75%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-center{-ms-flex-align:center!important;align-items:center!important}.align-items-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}@media (min-width:576px){.flex-sm-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-sm-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-sm-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-sm-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-sm-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-sm-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-sm-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-sm-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-sm-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-sm-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-sm-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-sm-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-sm-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-sm-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-sm-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-sm-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-sm-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-sm-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-sm-center{-ms-flex-align:center!important;align-items:center!important}.align-items-sm-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-sm-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-sm-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-sm-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-sm-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-sm-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-sm-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-sm-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-sm-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-sm-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-sm-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-sm-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-sm-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-sm-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:768px){.flex-md-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-md-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-md-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-md-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-md-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-md-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-md-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-md-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-md-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-md-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-md-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-md-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-md-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-md-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-md-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-md-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-md-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-md-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-md-center{-ms-flex-align:center!important;align-items:center!important}.align-items-md-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-md-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-md-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-md-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-md-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-md-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-md-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-md-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-md-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-md-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-md-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-md-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-md-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-md-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-lg-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-lg-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-lg-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-lg-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-lg-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-lg-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-lg-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-lg-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-lg-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-lg-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-lg-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-lg-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-lg-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-lg-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-lg-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-lg-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-lg-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-lg-center{-ms-flex-align:center!important;align-items:center!important}.align-items-lg-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-lg-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-lg-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-lg-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-lg-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-lg-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-lg-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-lg-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-lg-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-lg-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-lg-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-lg-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-lg-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-lg-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-xl-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-xl-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-xl-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-xl-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-xl-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-xl-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-xl-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-xl-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-xl-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-xl-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-xl-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-xl-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-xl-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-xl-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-xl-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-xl-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-xl-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-xl-center{-ms-flex-align:center!important;align-items:center!important}.align-items-xl-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-xl-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-xl-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-xl-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-xl-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-xl-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-xl-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-xl-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-xl-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-xl-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-xl-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-xl-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-xl-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-xl-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:992px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media (min-width:1200px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.user-select-all{-webkit-user-select:all!important;-moz-user-select:all!important;user-select:all!important}.user-select-auto{-webkit-user-select:auto!important;-moz-user-select:auto!important;-ms-user-select:auto!important;user-select:auto!important}.user-select-none{-webkit-user-select:none!important;-moz-user-select:none!important;-ms-user-select:none!important;user-select:none!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}@supports ((position:-webkit-sticky) or (position:sticky)){.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.min-vw-100{min-width:100vw!important}.min-vh-100{min-height:100vh!important}.vw-100{width:100vw!important}.vh-100{height:100vh!important}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-n1{margin:-.25rem!important}.mt-n1,.my-n1{margin-top:-.25rem!important}.mr-n1,.mx-n1{margin-right:-.25rem!important}.mb-n1,.my-n1{margin-bottom:-.25rem!important}.ml-n1,.mx-n1{margin-left:-.25rem!important}.m-n2{margin:-.5rem!important}.mt-n2,.my-n2{margin-top:-.5rem!important}.mr-n2,.mx-n2{margin-right:-.5rem!important}.mb-n2,.my-n2{margin-bottom:-.5rem!important}.ml-n2,.mx-n2{margin-left:-.5rem!important}.m-n3{margin:-1rem!important}.mt-n3,.my-n3{margin-top:-1rem!important}.mr-n3,.mx-n3{margin-right:-1rem!important}.mb-n3,.my-n3{margin-bottom:-1rem!important}.ml-n3,.mx-n3{margin-left:-1rem!important}.m-n4{margin:-1.5rem!important}.mt-n4,.my-n4{margin-top:-1.5rem!important}.mr-n4,.mx-n4{margin-right:-1.5rem!important}.mb-n4,.my-n4{margin-bottom:-1.5rem!important}.ml-n4,.mx-n4{margin-left:-1.5rem!important}.m-n5{margin:-3rem!important}.mt-n5,.my-n5{margin-top:-3rem!important}.mr-n5,.mx-n5{margin-right:-3rem!important}.mb-n5,.my-n5{margin-bottom:-3rem!important}.ml-n5,.mx-n5{margin-left:-3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-n1{margin:-.25rem!important}.mt-sm-n1,.my-sm-n1{margin-top:-.25rem!important}.mr-sm-n1,.mx-sm-n1{margin-right:-.25rem!important}.mb-sm-n1,.my-sm-n1{margin-bottom:-.25rem!important}.ml-sm-n1,.mx-sm-n1{margin-left:-.25rem!important}.m-sm-n2{margin:-.5rem!important}.mt-sm-n2,.my-sm-n2{margin-top:-.5rem!important}.mr-sm-n2,.mx-sm-n2{margin-right:-.5rem!important}.mb-sm-n2,.my-sm-n2{margin-bottom:-.5rem!important}.ml-sm-n2,.mx-sm-n2{margin-left:-.5rem!important}.m-sm-n3{margin:-1rem!important}.mt-sm-n3,.my-sm-n3{margin-top:-1rem!important}.mr-sm-n3,.mx-sm-n3{margin-right:-1rem!important}.mb-sm-n3,.my-sm-n3{margin-bottom:-1rem!important}.ml-sm-n3,.mx-sm-n3{margin-left:-1rem!important}.m-sm-n4{margin:-1.5rem!important}.mt-sm-n4,.my-sm-n4{margin-top:-1.5rem!important}.mr-sm-n4,.mx-sm-n4{margin-right:-1.5rem!important}.mb-sm-n4,.my-sm-n4{margin-bottom:-1.5rem!important}.ml-sm-n4,.mx-sm-n4{margin-left:-1.5rem!important}.m-sm-n5{margin:-3rem!important}.mt-sm-n5,.my-sm-n5{margin-top:-3rem!important}.mr-sm-n5,.mx-sm-n5{margin-right:-3rem!important}.mb-sm-n5,.my-sm-n5{margin-bottom:-3rem!important}.ml-sm-n5,.mx-sm-n5{margin-left:-3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-n1{margin:-.25rem!important}.mt-md-n1,.my-md-n1{margin-top:-.25rem!important}.mr-md-n1,.mx-md-n1{margin-right:-.25rem!important}.mb-md-n1,.my-md-n1{margin-bottom:-.25rem!important}.ml-md-n1,.mx-md-n1{margin-left:-.25rem!important}.m-md-n2{margin:-.5rem!important}.mt-md-n2,.my-md-n2{margin-top:-.5rem!important}.mr-md-n2,.mx-md-n2{margin-right:-.5rem!important}.mb-md-n2,.my-md-n2{margin-bottom:-.5rem!important}.ml-md-n2,.mx-md-n2{margin-left:-.5rem!important}.m-md-n3{margin:-1rem!important}.mt-md-n3,.my-md-n3{margin-top:-1rem!important}.mr-md-n3,.mx-md-n3{margin-right:-1rem!important}.mb-md-n3,.my-md-n3{margin-bottom:-1rem!important}.ml-md-n3,.mx-md-n3{margin-left:-1rem!important}.m-md-n4{margin:-1.5rem!important}.mt-md-n4,.my-md-n4{margin-top:-1.5rem!important}.mr-md-n4,.mx-md-n4{margin-right:-1.5rem!important}.mb-md-n4,.my-md-n4{margin-bottom:-1.5rem!important}.ml-md-n4,.mx-md-n4{margin-left:-1.5rem!important}.m-md-n5{margin:-3rem!important}.mt-md-n5,.my-md-n5{margin-top:-3rem!important}.mr-md-n5,.mx-md-n5{margin-right:-3rem!important}.mb-md-n5,.my-md-n5{margin-bottom:-3rem!important}.ml-md-n5,.mx-md-n5{margin-left:-3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-n1{margin:-.25rem!important}.mt-lg-n1,.my-lg-n1{margin-top:-.25rem!important}.mr-lg-n1,.mx-lg-n1{margin-right:-.25rem!important}.mb-lg-n1,.my-lg-n1{margin-bottom:-.25rem!important}.ml-lg-n1,.mx-lg-n1{margin-left:-.25rem!important}.m-lg-n2{margin:-.5rem!important}.mt-lg-n2,.my-lg-n2{margin-top:-.5rem!important}.mr-lg-n2,.mx-lg-n2{margin-right:-.5rem!important}.mb-lg-n2,.my-lg-n2{margin-bottom:-.5rem!important}.ml-lg-n2,.mx-lg-n2{margin-left:-.5rem!important}.m-lg-n3{margin:-1rem!important}.mt-lg-n3,.my-lg-n3{margin-top:-1rem!important}.mr-lg-n3,.mx-lg-n3{margin-right:-1rem!important}.mb-lg-n3,.my-lg-n3{margin-bottom:-1rem!important}.ml-lg-n3,.mx-lg-n3{margin-left:-1rem!important}.m-lg-n4{margin:-1.5rem!important}.mt-lg-n4,.my-lg-n4{margin-top:-1.5rem!important}.mr-lg-n4,.mx-lg-n4{margin-right:-1.5rem!important}.mb-lg-n4,.my-lg-n4{margin-bottom:-1.5rem!important}.ml-lg-n4,.mx-lg-n4{margin-left:-1.5rem!important}.m-lg-n5{margin:-3rem!important}.mt-lg-n5,.my-lg-n5{margin-top:-3rem!important}.mr-lg-n5,.mx-lg-n5{margin-right:-3rem!important}.mb-lg-n5,.my-lg-n5{margin-bottom:-3rem!important}.ml-lg-n5,.mx-lg-n5{margin-left:-3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-n1{margin:-.25rem!important}.mt-xl-n1,.my-xl-n1{margin-top:-.25rem!important}.mr-xl-n1,.mx-xl-n1{margin-right:-.25rem!important}.mb-xl-n1,.my-xl-n1{margin-bottom:-.25rem!important}.ml-xl-n1,.mx-xl-n1{margin-left:-.25rem!important}.m-xl-n2{margin:-.5rem!important}.mt-xl-n2,.my-xl-n2{margin-top:-.5rem!important}.mr-xl-n2,.mx-xl-n2{margin-right:-.5rem!important}.mb-xl-n2,.my-xl-n2{margin-bottom:-.5rem!important}.ml-xl-n2,.mx-xl-n2{margin-left:-.5rem!important}.m-xl-n3{margin:-1rem!important}.mt-xl-n3,.my-xl-n3{margin-top:-1rem!important}.mr-xl-n3,.mx-xl-n3{margin-right:-1rem!important}.mb-xl-n3,.my-xl-n3{margin-bottom:-1rem!important}.ml-xl-n3,.mx-xl-n3{margin-left:-1rem!important}.m-xl-n4{margin:-1.5rem!important}.mt-xl-n4,.my-xl-n4{margin-top:-1.5rem!important}.mr-xl-n4,.mx-xl-n4{margin-right:-1.5rem!important}.mb-xl-n4,.my-xl-n4{margin-bottom:-1.5rem!important}.ml-xl-n4,.mx-xl-n4{margin-left:-1.5rem!important}.m-xl-n5{margin:-3rem!important}.mt-xl-n5,.my-xl-n5{margin-top:-3rem!important}.mr-xl-n5,.mx-xl-n5{margin-right:-3rem!important}.mb-xl-n5,.my-xl-n5{margin-bottom:-3rem!important}.ml-xl-n5,.mx-xl-n5{margin-left:-3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;pointer-events:auto;content:"";background-color:rgba(0,0,0,0)}.text-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace!important}.text-justify{text-align:justify!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media (min-width:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-lighter{font-weight:lighter!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-weight-bolder{font-weight:bolder!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}.text-primary{color:#007bff!important}a.text-primary:focus,a.text-primary:hover{color:#0056b3!important}.text-secondary{color:#6c757d!important}a.text-secondary:focus,a.text-secondary:hover{color:#494f54!important}.text-success{color:#28a745!important}a.text-success:focus,a.text-success:hover{color:#19692c!important}.text-info{color:#17a2b8!important}a.text-info:focus,a.text-info:hover{color:#0f6674!important}.text-warning{color:#ffc107!important}a.text-warning:focus,a.text-warning:hover{color:#ba8b00!important}.text-danger{color:#dc3545!important}a.text-danger:focus,a.text-danger:hover{color:#a71d2a!important}.text-light{color:#f8f9fa!important}a.text-light:focus,a.text-light:hover{color:#cbd3da!important}.text-dark{color:#343a40!important}a.text-dark:focus,a.text-dark:hover{color:#121416!important}.text-body{color:#212529!important}.text-muted{color:#6c757d!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:rgba(255,255,255,.5)!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.text-decoration-none{text-decoration:none!important}.text-break{word-break:break-word!important;word-wrap:break-word!important}.text-reset{color:inherit!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,::after,::before{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]::after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #adb5bd;page-break-inside:avoid}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}body{min-width:992px!important}.container{min-width:992px!important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #dee2e6!important}.table-dark{color:inherit}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#dee2e6}.table .thead-dark th{color:inherit;border-color:#dee2e6}}:root{--dt-row-selected:2,117,216;--dt-row-selected-text:255,255,255;--dt-row-selected-link:9,10,11;--dt-row-stripe:0,0,0;--dt-row-hover:0,0,0;--dt-column-ordering:0,0,0;--dt-html-background:white}:root.dark{--dt-html-background:rgb(33, 37, 41)}table.dataTable td.dt-control{text-align:center;cursor:pointer}table.dataTable td.dt-control:before{display:inline-block;color:rgba(0,0,0,.5);content:"▶"}table.dataTable tr.dt-hasChild td.dt-control:before{content:"▼"}:root[data-bs-theme=dark] table.dataTable td.dt-control:before,html.dark table.dataTable td.dt-control:before{color:rgba(255,255,255,.5)}:root[data-bs-theme=dark] table.dataTable tr.dt-hasChild td.dt-control:before,html.dark table.dataTable tr.dt-hasChild td.dt-control:before{color:rgba(255,255,255,.5)}table.dataTable thead>tr>td.sorting,table.dataTable thead>tr>td.sorting_asc,table.dataTable thead>tr>td.sorting_asc_disabled,table.dataTable thead>tr>td.sorting_desc,table.dataTable thead>tr>td.sorting_desc_disabled,table.dataTable thead>tr>th.sorting,table.dataTable thead>tr>th.sorting_asc,table.dataTable thead>tr>th.sorting_asc_disabled,table.dataTable thead>tr>th.sorting_desc,table.dataTable thead>tr>th.sorting_desc_disabled{cursor:pointer;position:relative;padding-right:26px}table.dataTable thead>tr>td.sorting:after,table.dataTable thead>tr>td.sorting:before,table.dataTable thead>tr>td.sorting_asc:after,table.dataTable thead>tr>td.sorting_asc:before,table.dataTable thead>tr>td.sorting_asc_disabled:after,table.dataTable thead>tr>td.sorting_asc_disabled:before,table.dataTable thead>tr>td.sorting_desc:after,table.dataTable thead>tr>td.sorting_desc:before,table.dataTable thead>tr>td.sorting_desc_disabled:after,table.dataTable thead>tr>td.sorting_desc_disabled:before,table.dataTable thead>tr>th.sorting:after,table.dataTable thead>tr>th.sorting:before,table.dataTable thead>tr>th.sorting_asc:after,table.dataTable thead>tr>th.sorting_asc:before,table.dataTable thead>tr>th.sorting_asc_disabled:after,table.dataTable thead>tr>th.sorting_asc_disabled:before,table.dataTable thead>tr>th.sorting_desc:after,table.dataTable thead>tr>th.sorting_desc:before,table.dataTable thead>tr>th.sorting_desc_disabled:after,table.dataTable thead>tr>th.sorting_desc_disabled:before{position:absolute;display:block;opacity:.125;right:10px;line-height:9px;font-size:.8em}table.dataTable thead>tr>td.sorting:before,table.dataTable thead>tr>td.sorting_asc:before,table.dataTable thead>tr>td.sorting_asc_disabled:before,table.dataTable thead>tr>td.sorting_desc:before,table.dataTable thead>tr>td.sorting_desc_disabled:before,table.dataTable thead>tr>th.sorting:before,table.dataTable thead>tr>th.sorting_asc:before,table.dataTable thead>tr>th.sorting_asc_disabled:before,table.dataTable thead>tr>th.sorting_desc:before,table.dataTable thead>tr>th.sorting_desc_disabled:before{bottom:50%;content:"▲";content:"▲"/""}table.dataTable thead>tr>td.sorting:after,table.dataTable thead>tr>td.sorting_asc:after,table.dataTable thead>tr>td.sorting_asc_disabled:after,table.dataTable thead>tr>td.sorting_desc:after,table.dataTable thead>tr>td.sorting_desc_disabled:after,table.dataTable thead>tr>th.sorting:after,table.dataTable thead>tr>th.sorting_asc:after,table.dataTable thead>tr>th.sorting_asc_disabled:after,table.dataTable thead>tr>th.sorting_desc:after,table.dataTable thead>tr>th.sorting_desc_disabled:after{top:50%;content:"▼";content:"▼"/""}table.dataTable thead>tr>td.sorting_asc:before,table.dataTable thead>tr>td.sorting_desc:after,table.dataTable thead>tr>th.sorting_asc:before,table.dataTable thead>tr>th.sorting_desc:after{opacity:.6}table.dataTable thead>tr>td.sorting_asc_disabled:before,table.dataTable thead>tr>td.sorting_desc_disabled:after,table.dataTable thead>tr>th.sorting_asc_disabled:before,table.dataTable thead>tr>th.sorting_desc_disabled:after{display:none}table.dataTable thead>tr>td:active,table.dataTable thead>tr>th:active{outline:0}div.dataTables_scrollBody>table.dataTable>thead>tr>td:after,div.dataTables_scrollBody>table.dataTable>thead>tr>td:before,div.dataTables_scrollBody>table.dataTable>thead>tr>th:after,div.dataTables_scrollBody>table.dataTable>thead>tr>th:before{display:none}div.dataTables_processing{position:absolute;top:50%;left:50%;width:200px;margin-left:-100px;margin-top:-26px;text-align:center;padding:2px;z-index:10}div.dataTables_processing>div:last-child{position:relative;width:80px;height:15px;margin:1em auto}div.dataTables_processing>div:last-child>div{position:absolute;top:0;width:13px;height:13px;border-radius:50%;background:#0275d8;background:rgb(var(--dt-row-selected));animation-timing-function:cubic-bezier(0,1,1,0)}div.dataTables_processing>div:last-child>div:first-child{left:8px;animation:datatables-loader-1 .6s infinite}div.dataTables_processing>div:last-child>div:nth-child(2){left:8px;animation:datatables-loader-2 .6s infinite}div.dataTables_processing>div:last-child>div:nth-child(3){left:32px;animation:datatables-loader-2 .6s infinite}div.dataTables_processing>div:last-child>div:nth-child(4){left:56px;animation:datatables-loader-3 .6s infinite}@keyframes datatables-loader-1{0%{transform:scale(0)}100%{transform:scale(1)}}@keyframes datatables-loader-3{0%{transform:scale(1)}100%{transform:scale(0)}}@keyframes datatables-loader-2{0%{transform:translate(0,0)}100%{transform:translate(24px,0)}}table.dataTable.nowrap td,table.dataTable.nowrap th{white-space:nowrap}table.dataTable td.dt-left,table.dataTable th.dt-left{text-align:left}table.dataTable td.dataTables_empty,table.dataTable td.dt-center,table.dataTable th.dt-center{text-align:center}table.dataTable td.dt-right,table.dataTable th.dt-right{text-align:right}table.dataTable td.dt-justify,table.dataTable th.dt-justify{text-align:justify}table.dataTable td.dt-nowrap,table.dataTable th.dt-nowrap{white-space:nowrap}table.dataTable tfoot td,table.dataTable tfoot th,table.dataTable thead td,table.dataTable thead th{text-align:left}table.dataTable tfoot td.dt-head-left,table.dataTable tfoot th.dt-head-left,table.dataTable thead td.dt-head-left,table.dataTable thead th.dt-head-left{text-align:left}table.dataTable tfoot td.dt-head-center,table.dataTable tfoot th.dt-head-center,table.dataTable thead td.dt-head-center,table.dataTable thead th.dt-head-center{text-align:center}table.dataTable tfoot td.dt-head-right,table.dataTable tfoot th.dt-head-right,table.dataTable thead td.dt-head-right,table.dataTable thead th.dt-head-right{text-align:right}table.dataTable tfoot td.dt-head-justify,table.dataTable tfoot th.dt-head-justify,table.dataTable thead td.dt-head-justify,table.dataTable thead th.dt-head-justify{text-align:justify}table.dataTable tfoot td.dt-head-nowrap,table.dataTable tfoot th.dt-head-nowrap,table.dataTable thead td.dt-head-nowrap,table.dataTable thead th.dt-head-nowrap{white-space:nowrap}table.dataTable tbody td.dt-body-left,table.dataTable tbody th.dt-body-left{text-align:left}table.dataTable tbody td.dt-body-center,table.dataTable tbody th.dt-body-center{text-align:center}table.dataTable tbody td.dt-body-right,table.dataTable tbody th.dt-body-right{text-align:right}table.dataTable tbody td.dt-body-justify,table.dataTable tbody th.dt-body-justify{text-align:justify}table.dataTable tbody td.dt-body-nowrap,table.dataTable tbody th.dt-body-nowrap{white-space:nowrap}table.dataTable{clear:both;margin-top:6px!important;margin-bottom:6px!important;max-width:none!important;border-collapse:separate!important;border-spacing:0}table.dataTable td,table.dataTable th{-webkit-box-sizing:content-box;box-sizing:content-box}table.dataTable td.dataTables_empty,table.dataTable th.dataTables_empty{text-align:center}table.dataTable.nowrap td,table.dataTable.nowrap th{white-space:nowrap}table.dataTable.table-striped>tbody>tr:nth-of-type(odd){background-color:transparent}table.dataTable>tbody>tr{background-color:transparent}table.dataTable>tbody>tr.selected>*{box-shadow:inset 0 0 0 9999px #0275d8;box-shadow:inset 0 0 0 9999px rgb(var(--dt-row-selected));color:#fff;color:rgb(var(--dt-row-selected-text))}table.dataTable>tbody>tr.selected a{color:#090a0b;color:rgb(var(--dt-row-selected-link))}table.dataTable.table-striped>tbody>tr.odd>*{box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-stripe),.05)}table.dataTable.table-striped>tbody>tr.odd.selected>*{box-shadow:inset 0 0 0 9999px rgba(2,117,216,.95);box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-selected),.95)}table.dataTable.table-hover>tbody>tr:hover>*{box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-hover),.075)}table.dataTable.table-hover>tbody>tr.selected:hover>*{box-shadow:inset 0 0 0 9999px rgba(2,117,216,.975);box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-selected),.975)}div.dataTables_wrapper div.dataTables_length label{font-weight:400;text-align:left;white-space:nowrap}div.dataTables_wrapper div.dataTables_length select{width:auto;display:inline-block}div.dataTables_wrapper div.dataTables_filter{text-align:right}div.dataTables_wrapper div.dataTables_filter label{font-weight:400;white-space:nowrap;text-align:left}div.dataTables_wrapper div.dataTables_filter input{margin-left:.5em;display:inline-block;width:auto}div.dataTables_wrapper div.dataTables_info{padding-top:.85em}div.dataTables_wrapper div.dataTables_paginate{margin:0;white-space:nowrap;text-align:right}div.dataTables_wrapper div.dataTables_paginate ul.pagination{margin:2px 0;white-space:nowrap;justify-content:flex-end}div.dataTables_wrapper div.dataTables_processing{position:absolute;top:50%;left:50%;width:200px;margin-left:-100px;margin-top:-26px;text-align:center;padding:1em 0}div.dataTables_scrollHead table.dataTable{margin-bottom:0!important}div.dataTables_scrollBody>table{border-top:none;margin-top:0!important;margin-bottom:0!important}div.dataTables_scrollBody>table>thead .sorting:after,div.dataTables_scrollBody>table>thead .sorting:before,div.dataTables_scrollBody>table>thead .sorting_asc:after,div.dataTables_scrollBody>table>thead .sorting_asc:before,div.dataTables_scrollBody>table>thead .sorting_desc:after,div.dataTables_scrollBody>table>thead .sorting_desc:before{display:none}div.dataTables_scrollBody>table>tbody tr:first-child td,div.dataTables_scrollBody>table>tbody tr:first-child th{border-top:none}div.dataTables_scrollFoot>.dataTables_scrollFootInner{box-sizing:content-box}div.dataTables_scrollFoot>.dataTables_scrollFootInner>table{margin-top:0!important;border-top:none}@media screen and (max-width:767px){div.dataTables_wrapper div.dataTables_filter,div.dataTables_wrapper div.dataTables_info,div.dataTables_wrapper div.dataTables_length,div.dataTables_wrapper div.dataTables_paginate{text-align:center}div.dataTables_wrapper div.dataTables_paginate ul.pagination{justify-content:center!important}}table.dataTable.table-sm>thead>tr>th:not(.sorting_disabled){padding-right:20px}table.table-bordered.dataTable{border-right-width:0}table.table-bordered.dataTable td,table.table-bordered.dataTable th{border-left-width:0}table.table-bordered.dataTable td:last-child,table.table-bordered.dataTable th:last-child{border-right-width:1px}table.table-bordered.dataTable tbody td,table.table-bordered.dataTable tbody th{border-bottom-width:0}div.dataTables_scrollHead table.table-bordered{border-bottom-width:0}div.table-responsive>div.dataTables_wrapper>div.row{margin:0}div.table-responsive>div.dataTables_wrapper>div.row>div[class^=col-]:first-child{padding-left:0}div.table-responsive>div.dataTables_wrapper>div.row>div[class^=col-]:last-child{padding-right:0}.select2-container{box-sizing:border-box;display:inline-block;margin:0;position:relative;vertical-align:middle}.select2-container .select2-selection--single{box-sizing:border-box;cursor:pointer;display:block;height:28px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--single .select2-selection__rendered{display:block;padding-left:8px;padding-right:20px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.select2-container .select2-selection--single .select2-selection__clear{position:relative}.select2-container[dir=rtl] .select2-selection--single .select2-selection__rendered{padding-right:8px;padding-left:20px}.select2-container .select2-selection--multiple{box-sizing:border-box;cursor:pointer;display:block;min-height:32px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--multiple .select2-selection__rendered{display:inline-block;overflow:hidden;padding-left:8px;text-overflow:ellipsis;white-space:nowrap}.select2-container .select2-search--inline{float:left}.select2-container .select2-search--inline .select2-search__field{box-sizing:border-box;border:none;font-size:100%;margin-top:5px;padding:0}.select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none}.select2-dropdown{background-color:#fff;border:1px solid #aaa;border-radius:4px;box-sizing:border-box;display:block;position:absolute;left:-100000px;width:100%;z-index:1051}.select2-results{display:block}.select2-results__options{list-style:none;margin:0;padding:0}.select2-results__option{padding:6px;user-select:none;-webkit-user-select:none}.select2-results__option[aria-selected]{cursor:pointer}.select2-container--open .select2-dropdown{left:0}.select2-container--open .select2-dropdown--above{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--open .select2-dropdown--below{border-top:none;border-top-left-radius:0;border-top-right-radius:0}.select2-search--dropdown{display:block;padding:4px}.select2-search--dropdown .select2-search__field{padding:4px;width:100%;box-sizing:border-box}.select2-search--dropdown .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none}.select2-search--dropdown.select2-search--hide{display:none}.select2-close-mask{border:0;margin:0;padding:0;display:block;position:fixed;left:0;top:0;min-height:100%;min-width:100%;height:auto;width:auto;opacity:0;z-index:99;background-color:#fff}.select2-hidden-accessible{border:0!important;clip:rect(0 0 0 0)!important;-webkit-clip-path:inset(50%)!important;clip-path:inset(50%)!important;height:1px!important;overflow:hidden!important;padding:0!important;position:absolute!important;width:1px!important;white-space:nowrap!important}.select2-container--default .select2-selection--single{background-color:#fff;border:1px solid #aaa;border-radius:4px}.select2-container--default .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px}.select2-container--default .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:700}.select2-container--default .select2-selection--single .select2-selection__placeholder{color:#999}.select2-container--default .select2-selection--single .select2-selection__arrow{height:26px;position:absolute;top:1px;right:1px;width:20px}.select2-container--default .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0}.select2-container--default[dir=rtl] .select2-selection--single .select2-selection__clear{float:left}.select2-container--default[dir=rtl] .select2-selection--single .select2-selection__arrow{left:1px;right:auto}.select2-container--default.select2-container--disabled .select2-selection--single{background-color:#eee;cursor:default}.select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear{display:none}.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px}.select2-container--default .select2-selection--multiple{background-color:#fff;border:1px solid #aaa;border-radius:4px;cursor:text}.select2-container--default .select2-selection--multiple .select2-selection__rendered{box-sizing:border-box;list-style:none;margin:0;padding:0 5px;width:100%}.select2-container--default .select2-selection--multiple .select2-selection__rendered li{list-style:none}.select2-container--default .select2-selection--multiple .select2-selection__clear{cursor:pointer;float:right;font-weight:700;margin-top:5px;margin-right:10px;padding:1px}.select2-container--default .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove{color:#999;cursor:pointer;display:inline-block;font-weight:700;margin-right:2px}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover{color:#333}.select2-container--default[dir=rtl] .select2-selection--multiple .select2-search--inline,.select2-container--default[dir=rtl] .select2-selection--multiple .select2-selection__choice{float:right}.select2-container--default[dir=rtl] .select2-selection--multiple .select2-selection__choice{margin-left:5px;margin-right:auto}.select2-container--default[dir=rtl] .select2-selection--multiple .select2-selection__choice__remove{margin-left:2px;margin-right:auto}.select2-container--default.select2-container--focus .select2-selection--multiple{border:solid #000 1px;outline:0}.select2-container--default.select2-container--disabled .select2-selection--multiple{background-color:#eee;cursor:default}.select2-container--default.select2-container--disabled .select2-selection__choice__remove{display:none}.select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple,.select2-container--default.select2-container--open.select2-container--above .select2-selection--single{border-top-left-radius:0;border-top-right-radius:0}.select2-container--default.select2-container--open.select2-container--below .select2-selection--multiple,.select2-container--default.select2-container--open.select2-container--below .select2-selection--single{border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--default .select2-search--dropdown .select2-search__field{border:1px solid #aaa}.select2-container--default .select2-search--inline .select2-search__field{background:0 0;border:none;outline:0;box-shadow:none;-webkit-appearance:textfield}.select2-container--default .select2-results>.select2-results__options{max-height:200px;overflow-y:auto}.select2-container--default .select2-results__option[role=group]{padding:0}.select2-container--default .select2-results__option[aria-disabled=true]{color:#999}.select2-container--default .select2-results__option[aria-selected=true]{background-color:#ddd}.select2-container--default .select2-results__option .select2-results__option{padding-left:1em}.select2-container--default .select2-results__option .select2-results__option .select2-results__group{padding-left:0}.select2-container--default .select2-results__option .select2-results__option .select2-results__option{margin-left:-1em;padding-left:2em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-2em;padding-left:3em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-3em;padding-left:4em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-4em;padding-left:5em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-5em;padding-left:6em}.select2-container--default .select2-results__option--highlighted[aria-selected]{background-color:#5897fb;color:#fff}.select2-container--default .select2-results__group{cursor:default;display:block;padding:6px}.select2-container--classic .select2-selection--single{background-color:#f7f7f7;border:1px solid #aaa;border-radius:4px;outline:0;background-image:-webkit-linear-gradient(top,#fff 50%,#eee 100%);background-image:-o-linear-gradient(top,#fff 50%,#eee 100%);background-image:linear-gradient(to bottom,#fff 50%,#eee 100%);background-repeat:repeat-x}.select2-container--classic .select2-selection--single:focus{border:1px solid #5897fb}.select2-container--classic .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px}.select2-container--classic .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:700;margin-right:10px}.select2-container--classic .select2-selection--single .select2-selection__placeholder{color:#999}.select2-container--classic .select2-selection--single .select2-selection__arrow{background-color:#ddd;border:none;border-left:1px solid #aaa;border-top-right-radius:4px;border-bottom-right-radius:4px;height:26px;position:absolute;top:1px;right:1px;width:20px;background-image:-webkit-linear-gradient(top,#eee 50%,#ccc 100%);background-image:-o-linear-gradient(top,#eee 50%,#ccc 100%);background-image:linear-gradient(to bottom,#eee 50%,#ccc 100%);background-repeat:repeat-x}.select2-container--classic .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0}.select2-container--classic[dir=rtl] .select2-selection--single .select2-selection__clear{float:left}.select2-container--classic[dir=rtl] .select2-selection--single .select2-selection__arrow{border:none;border-right:1px solid #aaa;border-radius:0;border-top-left-radius:4px;border-bottom-left-radius:4px;left:1px;right:auto}.select2-container--classic.select2-container--open .select2-selection--single{border:1px solid #5897fb}.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow{background:0 0;border:none}.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px}.select2-container--classic.select2-container--open.select2-container--above .select2-selection--single{border-top:none;border-top-left-radius:0;border-top-right-radius:0;background-image:-webkit-linear-gradient(top,#fff 0,#eee 50%);background-image:-o-linear-gradient(top,#fff 0,#eee 50%);background-image:linear-gradient(to bottom,#fff 0,#eee 50%);background-repeat:repeat-x}.select2-container--classic.select2-container--open.select2-container--below .select2-selection--single{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0;background-image:-webkit-linear-gradient(top,#eee 50%,#fff 100%);background-image:-o-linear-gradient(top,#eee 50%,#fff 100%);background-image:linear-gradient(to bottom,#eee 50%,#fff 100%);background-repeat:repeat-x}.select2-container--classic .select2-selection--multiple{background-color:#fff;border:1px solid #aaa;border-radius:4px;cursor:text;outline:0}.select2-container--classic .select2-selection--multiple:focus{border:1px solid #5897fb}.select2-container--classic .select2-selection--multiple .select2-selection__rendered{list-style:none;margin:0;padding:0 5px}.select2-container--classic .select2-selection--multiple .select2-selection__clear{display:none}.select2-container--classic .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px}.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove{color:#888;cursor:pointer;display:inline-block;font-weight:700;margin-right:2px}.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover{color:#555}.select2-container--classic[dir=rtl] .select2-selection--multiple .select2-selection__choice{float:right;margin-left:5px;margin-right:auto}.select2-container--classic[dir=rtl] .select2-selection--multiple .select2-selection__choice__remove{margin-left:2px;margin-right:auto}.select2-container--classic.select2-container--open .select2-selection--multiple{border:1px solid #5897fb}.select2-container--classic.select2-container--open.select2-container--above .select2-selection--multiple{border-top:none;border-top-left-radius:0;border-top-right-radius:0}.select2-container--classic.select2-container--open.select2-container--below .select2-selection--multiple{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--classic .select2-search--dropdown .select2-search__field{border:1px solid #aaa;outline:0}.select2-container--classic .select2-search--inline .select2-search__field{outline:0;box-shadow:none}.select2-container--classic .select2-dropdown{background-color:#fff;border:1px solid transparent}.select2-container--classic .select2-dropdown--above{border-bottom:none}.select2-container--classic .select2-dropdown--below{border-top:none}.select2-container--classic .select2-results>.select2-results__options{max-height:200px;overflow-y:auto}.select2-container--classic .select2-results__option[role=group]{padding:0}.select2-container--classic .select2-results__option[aria-disabled=true]{color:grey}.select2-container--classic .select2-results__option--highlighted[aria-selected]{background-color:#3875d7;color:#fff}.select2-container--classic .select2-results__group{cursor:default;display:block;padding:6px}.select2-container--classic.select2-container--open .select2-dropdown{border-color:#5897fb}/*! + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:FontAwesome;src:url('../fonts/fontawesome-webfont.eot?v=4.7.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');font-weight:400;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-webkit-transform:scale(-1,1);-ms-transform:scale(-1,1);transform:scale(-1,1)}.fa-flip-vertical{-webkit-transform:scale(1,-1);-ms-transform:scale(1,-1);transform:scale(1,-1)}:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-rotate-90{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-close:before,.fa-remove:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-cog:before,.fa-gear:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-repeat:before,.fa-rotate-right:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-image:before,.fa-photo:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-exclamation-triangle:before,.fa-warning:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-cogs:before,.fa-gears:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-floppy-o:before,.fa-save:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-bars:before,.fa-navicon:before,.fa-reorder:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-sort:before,.fa-unsorted:before{content:"\f0dc"}.fa-sort-desc:before,.fa-sort-down:before{content:"\f0dd"}.fa-sort-asc:before,.fa-sort-up:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-gavel:before,.fa-legal:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-bolt:before,.fa-flash:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-clipboard:before,.fa-paste:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-chain-broken:before,.fa-unlink:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-caret-square-o-down:before,.fa-toggle-down:before{content:"\f150"}.fa-caret-square-o-up:before,.fa-toggle-up:before{content:"\f151"}.fa-caret-square-o-right:before,.fa-toggle-right:before{content:"\f152"}.fa-eur:before,.fa-euro:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-inr:before,.fa-rupee:before{content:"\f156"}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen:before{content:"\f157"}.fa-rouble:before,.fa-rub:before,.fa-ruble:before{content:"\f158"}.fa-krw:before,.fa-won:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-caret-square-o-left:before,.fa-toggle-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-try:before,.fa-turkish-lira:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-bank:before,.fa-institution:before,.fa-university:before{content:"\f19c"}.fa-graduation-cap:before,.fa-mortar-board:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-image-o:before,.fa-file-photo-o:before,.fa-file-picture-o:before{content:"\f1c5"}.fa-file-archive-o:before,.fa-file-zip-o:before{content:"\f1c6"}.fa-file-audio-o:before,.fa-file-sound-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-ring:before,.fa-life-saver:before,.fa-support:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before,.fa-resistance:before{content:"\f1d0"}.fa-empire:before,.fa-ge:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-hacker-news:before,.fa-y-combinator-square:before,.fa-yc-square:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-paper-plane:before,.fa-send:before{content:"\f1d8"}.fa-paper-plane-o:before,.fa-send-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-futbol-o:before,.fa-soccer-ball-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-ils:before,.fa-shekel:before,.fa-sheqel:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-bed:before,.fa-hotel:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-y-combinator:before,.fa-yc:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery-full:before,.fa-battery:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-paper-o:before,.fa-hand-stop-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-television:before,.fa-tv:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-american-sign-language-interpreting:before,.fa-asl-interpreting:before{content:"\f2a3"}.fa-deaf:before,.fa-deafness:before,.fa-hard-of-hearing:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-sign-language:before,.fa-signing:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-address-card:before,.fa-vcard:before{content:"\f2bb"}.fa-address-card-o:before,.fa-vcard-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer-full:before,.fa-thermometer:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bath:before,.fa-bathtub:before,.fa-s15:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}:root{--dt-row-selected:2,117,216;--dt-row-selected-text:255,255,255;--dt-row-selected-link:9,10,11;--dt-row-stripe:0,0,0;--dt-row-hover:0,0,0;--dt-column-ordering:0,0,0;--dt-html-background:white}:root.dark{--dt-html-background:rgb(33, 37, 41)}table.dataTable td.dt-control{text-align:center;cursor:pointer}table.dataTable td.dt-control:before{display:inline-block;color:rgba(0,0,0,.5);content:"▶"}table.dataTable tr.dt-hasChild td.dt-control:before{content:"▼"}:root[data-bs-theme=dark] table.dataTable td.dt-control:before,html.dark table.dataTable td.dt-control:before{color:rgba(255,255,255,.5)}:root[data-bs-theme=dark] table.dataTable tr.dt-hasChild td.dt-control:before,html.dark table.dataTable tr.dt-hasChild td.dt-control:before{color:rgba(255,255,255,.5)}table.dataTable thead>tr>td.sorting,table.dataTable thead>tr>td.sorting_asc,table.dataTable thead>tr>td.sorting_asc_disabled,table.dataTable thead>tr>td.sorting_desc,table.dataTable thead>tr>td.sorting_desc_disabled,table.dataTable thead>tr>th.sorting,table.dataTable thead>tr>th.sorting_asc,table.dataTable thead>tr>th.sorting_asc_disabled,table.dataTable thead>tr>th.sorting_desc,table.dataTable thead>tr>th.sorting_desc_disabled{cursor:pointer;position:relative;padding-right:26px}table.dataTable thead>tr>td.sorting:after,table.dataTable thead>tr>td.sorting:before,table.dataTable thead>tr>td.sorting_asc:after,table.dataTable thead>tr>td.sorting_asc:before,table.dataTable thead>tr>td.sorting_asc_disabled:after,table.dataTable thead>tr>td.sorting_asc_disabled:before,table.dataTable thead>tr>td.sorting_desc:after,table.dataTable thead>tr>td.sorting_desc:before,table.dataTable thead>tr>td.sorting_desc_disabled:after,table.dataTable thead>tr>td.sorting_desc_disabled:before,table.dataTable thead>tr>th.sorting:after,table.dataTable thead>tr>th.sorting:before,table.dataTable thead>tr>th.sorting_asc:after,table.dataTable thead>tr>th.sorting_asc:before,table.dataTable thead>tr>th.sorting_asc_disabled:after,table.dataTable thead>tr>th.sorting_asc_disabled:before,table.dataTable thead>tr>th.sorting_desc:after,table.dataTable thead>tr>th.sorting_desc:before,table.dataTable thead>tr>th.sorting_desc_disabled:after,table.dataTable thead>tr>th.sorting_desc_disabled:before{position:absolute;display:block;opacity:.125;right:10px;line-height:9px;font-size:.8em}table.dataTable thead>tr>td.sorting:before,table.dataTable thead>tr>td.sorting_asc:before,table.dataTable thead>tr>td.sorting_asc_disabled:before,table.dataTable thead>tr>td.sorting_desc:before,table.dataTable thead>tr>td.sorting_desc_disabled:before,table.dataTable thead>tr>th.sorting:before,table.dataTable thead>tr>th.sorting_asc:before,table.dataTable thead>tr>th.sorting_asc_disabled:before,table.dataTable thead>tr>th.sorting_desc:before,table.dataTable thead>tr>th.sorting_desc_disabled:before{bottom:50%;content:"▲";content:"▲"/""}table.dataTable thead>tr>td.sorting:after,table.dataTable thead>tr>td.sorting_asc:after,table.dataTable thead>tr>td.sorting_asc_disabled:after,table.dataTable thead>tr>td.sorting_desc:after,table.dataTable thead>tr>td.sorting_desc_disabled:after,table.dataTable thead>tr>th.sorting:after,table.dataTable thead>tr>th.sorting_asc:after,table.dataTable thead>tr>th.sorting_asc_disabled:after,table.dataTable thead>tr>th.sorting_desc:after,table.dataTable thead>tr>th.sorting_desc_disabled:after{top:50%;content:"▼";content:"▼"/""}table.dataTable thead>tr>td.sorting_asc:before,table.dataTable thead>tr>td.sorting_desc:after,table.dataTable thead>tr>th.sorting_asc:before,table.dataTable thead>tr>th.sorting_desc:after{opacity:.6}table.dataTable thead>tr>td.sorting_asc_disabled:before,table.dataTable thead>tr>td.sorting_desc_disabled:after,table.dataTable thead>tr>th.sorting_asc_disabled:before,table.dataTable thead>tr>th.sorting_desc_disabled:after{display:none}table.dataTable thead>tr>td:active,table.dataTable thead>tr>th:active{outline:0}div.dataTables_scrollBody>table.dataTable>thead>tr>td:after,div.dataTables_scrollBody>table.dataTable>thead>tr>td:before,div.dataTables_scrollBody>table.dataTable>thead>tr>th:after,div.dataTables_scrollBody>table.dataTable>thead>tr>th:before{display:none}div.dataTables_processing{position:absolute;top:50%;left:50%;width:200px;margin-left:-100px;margin-top:-26px;text-align:center;padding:2px;z-index:10}div.dataTables_processing>div:last-child{position:relative;width:80px;height:15px;margin:1em auto}div.dataTables_processing>div:last-child>div{position:absolute;top:0;width:13px;height:13px;border-radius:50%;background:#0275d8;background:rgb(var(--dt-row-selected));animation-timing-function:cubic-bezier(0,1,1,0)}div.dataTables_processing>div:last-child>div:first-child{left:8px;animation:datatables-loader-1 .6s infinite}div.dataTables_processing>div:last-child>div:nth-child(2){left:8px;animation:datatables-loader-2 .6s infinite}div.dataTables_processing>div:last-child>div:nth-child(3){left:32px;animation:datatables-loader-2 .6s infinite}div.dataTables_processing>div:last-child>div:nth-child(4){left:56px;animation:datatables-loader-3 .6s infinite}@keyframes datatables-loader-1{0%{transform:scale(0)}100%{transform:scale(1)}}@keyframes datatables-loader-3{0%{transform:scale(1)}100%{transform:scale(0)}}@keyframes datatables-loader-2{0%{transform:translate(0,0)}100%{transform:translate(24px,0)}}table.dataTable.nowrap td,table.dataTable.nowrap th{white-space:nowrap}table.dataTable td.dt-left,table.dataTable th.dt-left{text-align:left}table.dataTable td.dataTables_empty,table.dataTable td.dt-center,table.dataTable th.dt-center{text-align:center}table.dataTable td.dt-right,table.dataTable th.dt-right{text-align:right}table.dataTable td.dt-justify,table.dataTable th.dt-justify{text-align:justify}table.dataTable td.dt-nowrap,table.dataTable th.dt-nowrap{white-space:nowrap}table.dataTable tfoot td,table.dataTable tfoot th,table.dataTable thead td,table.dataTable thead th{text-align:left}table.dataTable tfoot td.dt-head-left,table.dataTable tfoot th.dt-head-left,table.dataTable thead td.dt-head-left,table.dataTable thead th.dt-head-left{text-align:left}table.dataTable tfoot td.dt-head-center,table.dataTable tfoot th.dt-head-center,table.dataTable thead td.dt-head-center,table.dataTable thead th.dt-head-center{text-align:center}table.dataTable tfoot td.dt-head-right,table.dataTable tfoot th.dt-head-right,table.dataTable thead td.dt-head-right,table.dataTable thead th.dt-head-right{text-align:right}table.dataTable tfoot td.dt-head-justify,table.dataTable tfoot th.dt-head-justify,table.dataTable thead td.dt-head-justify,table.dataTable thead th.dt-head-justify{text-align:justify}table.dataTable tfoot td.dt-head-nowrap,table.dataTable tfoot th.dt-head-nowrap,table.dataTable thead td.dt-head-nowrap,table.dataTable thead th.dt-head-nowrap{white-space:nowrap}table.dataTable tbody td.dt-body-left,table.dataTable tbody th.dt-body-left{text-align:left}table.dataTable tbody td.dt-body-center,table.dataTable tbody th.dt-body-center{text-align:center}table.dataTable tbody td.dt-body-right,table.dataTable tbody th.dt-body-right{text-align:right}table.dataTable tbody td.dt-body-justify,table.dataTable tbody th.dt-body-justify{text-align:justify}table.dataTable tbody td.dt-body-nowrap,table.dataTable tbody th.dt-body-nowrap{white-space:nowrap}table.dataTable{clear:both;margin-top:6px!important;margin-bottom:6px!important;max-width:none!important;border-collapse:separate!important;border-spacing:0}table.dataTable td,table.dataTable th{-webkit-box-sizing:content-box;box-sizing:content-box}table.dataTable td.dataTables_empty,table.dataTable th.dataTables_empty{text-align:center}table.dataTable.nowrap td,table.dataTable.nowrap th{white-space:nowrap}table.dataTable.table-striped>tbody>tr:nth-of-type(odd){background-color:transparent}table.dataTable>tbody>tr{background-color:transparent}table.dataTable>tbody>tr.selected>*{box-shadow:inset 0 0 0 9999px #0275d8;box-shadow:inset 0 0 0 9999px rgb(var(--dt-row-selected));color:#fff;color:rgb(var(--dt-row-selected-text))}table.dataTable>tbody>tr.selected a{color:#090a0b;color:rgb(var(--dt-row-selected-link))}table.dataTable.table-striped>tbody>tr.odd>*{box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-stripe),.05)}table.dataTable.table-striped>tbody>tr.odd.selected>*{box-shadow:inset 0 0 0 9999px rgba(2,117,216,.95);box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-selected),.95)}table.dataTable.table-hover>tbody>tr:hover>*{box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-hover),.075)}table.dataTable.table-hover>tbody>tr.selected:hover>*{box-shadow:inset 0 0 0 9999px rgba(2,117,216,.975);box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-selected),.975)}div.dataTables_wrapper div.dataTables_length label{font-weight:400;text-align:left;white-space:nowrap}div.dataTables_wrapper div.dataTables_length select{width:auto;display:inline-block}div.dataTables_wrapper div.dataTables_filter{text-align:right}div.dataTables_wrapper div.dataTables_filter label{font-weight:400;white-space:nowrap;text-align:left}div.dataTables_wrapper div.dataTables_filter input{margin-left:.5em;display:inline-block;width:auto}div.dataTables_wrapper div.dataTables_info{padding-top:.85em}div.dataTables_wrapper div.dataTables_paginate{margin:0;white-space:nowrap;text-align:right}div.dataTables_wrapper div.dataTables_paginate ul.pagination{margin:2px 0;white-space:nowrap;justify-content:flex-end}div.dataTables_wrapper div.dataTables_processing{position:absolute;top:50%;left:50%;width:200px;margin-left:-100px;margin-top:-26px;text-align:center;padding:1em 0}div.dataTables_scrollHead table.dataTable{margin-bottom:0!important}div.dataTables_scrollBody>table{border-top:none;margin-top:0!important;margin-bottom:0!important}div.dataTables_scrollBody>table>thead .sorting:after,div.dataTables_scrollBody>table>thead .sorting:before,div.dataTables_scrollBody>table>thead .sorting_asc:after,div.dataTables_scrollBody>table>thead .sorting_asc:before,div.dataTables_scrollBody>table>thead .sorting_desc:after,div.dataTables_scrollBody>table>thead .sorting_desc:before{display:none}div.dataTables_scrollBody>table>tbody tr:first-child td,div.dataTables_scrollBody>table>tbody tr:first-child th{border-top:none}div.dataTables_scrollFoot>.dataTables_scrollFootInner{box-sizing:content-box}div.dataTables_scrollFoot>.dataTables_scrollFootInner>table{margin-top:0!important;border-top:none}@media screen and (max-width:767px){div.dataTables_wrapper div.dataTables_filter,div.dataTables_wrapper div.dataTables_info,div.dataTables_wrapper div.dataTables_length,div.dataTables_wrapper div.dataTables_paginate{text-align:center}div.dataTables_wrapper div.dataTables_paginate ul.pagination{justify-content:center!important}}table.dataTable.table-sm>thead>tr>th:not(.sorting_disabled){padding-right:20px}table.table-bordered.dataTable{border-right-width:0}table.table-bordered.dataTable td,table.table-bordered.dataTable th{border-left-width:0}table.table-bordered.dataTable td:last-child,table.table-bordered.dataTable th:last-child{border-right-width:1px}table.table-bordered.dataTable tbody td,table.table-bordered.dataTable tbody th{border-bottom-width:0}div.dataTables_scrollHead table.table-bordered{border-bottom-width:0}div.table-responsive>div.dataTables_wrapper>div.row{margin:0}div.table-responsive>div.dataTables_wrapper>div.row>div[class^=col-]:first-child{padding-left:0}div.table-responsive>div.dataTables_wrapper>div.row>div[class^=col-]:last-child{padding-right:0}table.dataTable.dtr-inline.collapsed>tbody>tr>td.child,table.dataTable.dtr-inline.collapsed>tbody>tr>td.dataTables_empty,table.dataTable.dtr-inline.collapsed>tbody>tr>th.child{cursor:default!important}table.dataTable.dtr-inline.collapsed>tbody>tr>td.child:before,table.dataTable.dtr-inline.collapsed>tbody>tr>td.dataTables_empty:before,table.dataTable.dtr-inline.collapsed>tbody>tr>th.child:before{display:none!important}table.dataTable.dtr-inline.collapsed>tbody>tr>td.dtr-control,table.dataTable.dtr-inline.collapsed>tbody>tr>th.dtr-control{cursor:pointer}table.dataTable.dtr-inline.collapsed>tbody>tr>td.dtr-control:before,table.dataTable.dtr-inline.collapsed>tbody>tr>th.dtr-control:before{margin-right:.5em;display:inline-block;color:rgba(0,0,0,.5);content:"►"}table.dataTable.dtr-inline.collapsed>tbody>tr>td.dtr-control.arrow-right::before,table.dataTable.dtr-inline.collapsed>tbody>tr>th.dtr-control.arrow-right::before{content:"◄"}table.dataTable.dtr-inline.collapsed>tbody>tr.parent>td.dtr-control:before,table.dataTable.dtr-inline.collapsed>tbody>tr.parent>th.dtr-control:before{content:"▼"}table.dataTable.dtr-inline.collapsed.compact>tbody>tr>td.dtr-control,table.dataTable.dtr-inline.collapsed.compact>tbody>tr>th.dtr-control{padding-left:.333em}table.dataTable.dtr-column>tbody>tr>td.control,table.dataTable.dtr-column>tbody>tr>td.dtr-control,table.dataTable.dtr-column>tbody>tr>th.control,table.dataTable.dtr-column>tbody>tr>th.dtr-control{cursor:pointer}table.dataTable.dtr-column>tbody>tr>td.control:before,table.dataTable.dtr-column>tbody>tr>td.dtr-control:before,table.dataTable.dtr-column>tbody>tr>th.control:before,table.dataTable.dtr-column>tbody>tr>th.dtr-control:before{display:inline-block;color:rgba(0,0,0,.5);content:"►"}table.dataTable.dtr-column>tbody>tr>td.control.arrow-right::before,table.dataTable.dtr-column>tbody>tr>td.dtr-control.arrow-right::before,table.dataTable.dtr-column>tbody>tr>th.control.arrow-right::before,table.dataTable.dtr-column>tbody>tr>th.dtr-control.arrow-right::before{content:"◄"}table.dataTable.dtr-column>tbody>tr.parent td.control:before,table.dataTable.dtr-column>tbody>tr.parent td.dtr-control:before,table.dataTable.dtr-column>tbody>tr.parent th.control:before,table.dataTable.dtr-column>tbody>tr.parent th.dtr-control:before{content:"▼"}table.dataTable>tbody>tr.child{padding:.5em 1em}table.dataTable>tbody>tr.child:hover{background:0 0!important}table.dataTable>tbody>tr.child ul.dtr-details{display:inline-block;list-style-type:none;margin:0;padding:0}table.dataTable>tbody>tr.child ul.dtr-details>li{border-bottom:1px solid #efefef;padding:.5em 0}table.dataTable>tbody>tr.child ul.dtr-details>li:first-child{padding-top:0}table.dataTable>tbody>tr.child ul.dtr-details>li:last-child{padding-bottom:0;border-bottom:none}table.dataTable>tbody>tr.child span.dtr-title{display:inline-block;min-width:75px;font-weight:700}div.dtr-modal{position:fixed;box-sizing:border-box;top:0;left:0;height:100%;width:100%;z-index:100;padding:10em 1em}div.dtr-modal div.dtr-modal-display{position:absolute;top:0;left:0;bottom:0;right:0;width:50%;height:fit-content;max-height:75%;overflow:auto;margin:auto;z-index:102;overflow:auto;background-color:#f5f5f7;border:1px solid #000;border-radius:.5em;box-shadow:0 12px 30px rgba(0,0,0,.6)}div.dtr-modal div.dtr-modal-content{position:relative;padding:2.5em}div.dtr-modal div.dtr-modal-content h2{margin-top:0}div.dtr-modal div.dtr-modal-close{position:absolute;top:6px;right:6px;width:22px;height:22px;text-align:center;border-radius:3px;cursor:pointer;z-index:12}div.dtr-modal div.dtr-modal-background{position:fixed;top:0;left:0;right:0;bottom:0;z-index:101;background:rgba(0,0,0,.6)}@media screen and (max-width:767px){div.dtr-modal div.dtr-modal-display{width:95%}}html.dark table.dataTable>tbody>tr>td.dtr-control:before{color:rgba(255,255,255,.5)!important}html.dark table.dataTable>tbody>tr.child ul.dtr-details>li{border-bottom-color:#404346}html.dark div.dtr-modal div.dtr-modal-display{background-color:#212529;border:1px solid rgba(255,255,255,.15)}div.dtr-bs-modal table.table tr:first-child td{border-top:none}#navbarSupportedContent a{font-size:15px}@media (max-width:768px){#side-navbar{display:block;margin-left:-220px;width:220px!important;height:96%;overflow:scroll;transition:all .3s ease}#side-navbar.active{margin-left:0;z-index:200;position:absolute;display:block!important}#slide-reveal-overlay{position:fixed;left:0;height:100%;width:100%;z-index:100;background-color:rgba(0,0,0,.5)}}@media (max-width:992px){#side-navbar .nav-item a{font-size:.9rem}#side-navbar h6{font-size:.9rem}}@media (min-width:992px){#navbarSupportedContent a{font-size:14px}#div-header-select-customer{width:150px}}@media (min-width:992px) and (max-width:1205px){.navbar-expand-lg #navbarSupportedContent .navbar-nav .nav-link{padding-right:.3rem;padding-left:.3rem}}@media (min-width:1200px){#div-header-select-customer{width:175px}}.former-help-text{display:none}.form-check input[type=checkbox]{margin-right:10px!important}.reset-button-well{float:right;position:absolute;margin-left:55%}.table_view_info{border-collapse:separate;border-spacing:15px 15px}.table_view_info tr td{vertical-align:top}.dropdown-submenu{position:relative!important}.dropdown-submenu .dropdown-menu-l{top:0;left:-163px;margin-top:-1px}.former-input-col-sm-6 .form-group>div{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.former-label-col-sm-6 .form-group>label{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.fa-facebook:hover{color:#3b5998}.fa-twitter:hover{color:#4099ff}.fa-linkedin:hover{color:#007bb5}.fa-github:hover,.fa-globe:hover{color:grey}.dropup .dropdown-toggle:after{margin-top:4px}.error-border-select{border:1px solid #dc3545!important}.valid-border-select{border:1px solid #28a745!important}.a-disabled{pointer-events:none;cursor:default;opacity:.6}.cursor-pointer{cursor:pointer}.center-dd-caret:after{margin-bottom:auto;margin-top:auto}.cursor-default{pointer-events:none;cursor:default}.dataTable .dropdown-item:focus,.dropdown-item:hover{width:auto;color:#16181b;text-decoration:none;background-color:#f8f9fa}.dropdown-item.active:hover{background-color:#005bc3!important;color:#fff!important}@media (max-width:576px){.h2{font-size:1.6rem}}@media (min-width:768px){#div-header-select-customer{width:300px}}.no-padding{padding:0!important}.padding-10{padding:10px 15px!important}.mono{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}.ixpm-select-input-xs{height:20px;line-height:20px}.page-content{background-color:#fff!important;padding:20px;margin:0 -20px;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.15);-moz-box-shadow:0 1px 2px rgba(0,0,0,.15);box-shadow:0 1px 2px rgba(0,0,0,.15)}.list_preamble_container{display:block}.list_preamble{display:block;text-align:right}.pm-table{margin-bottom:18px;margin-left:auto;margin-right:auto}.pm-table th{padding:0;text-align:center;vertical-align:bottom;border:1px solid #ddd;border-bottom-width:2px;border-top:none;font-weight:700}.pm-table th.asn{cursor:hand;cursor:pointer}.pm-table th:last-child{border-right-width:2px}.pm-table td{text-align:center;vertical-align:top;border:1px solid #ddd}.pm-table td.peering{cursor:hand;cursor:pointer}.pm-table td.zoom1{padding:0 1px;font-size:8px;line-height:12px}.pm-table td.zoom2{padding:2px 4px;font-size:11px;line-height:15px}.pm-table td.zoom3{padding:4px 8px;font-size:13px;line-height:18px}.pm-table td.zoom4{padding:6px 10px;font-size:15px;line-height:20px}.pm-table td.zoom5{padding:8px 12px;font-size:18px;line-height:24px}.pm-table th.zoom1{font-size:8px;line-height:12px}.pm-table th.zoom2{font-size:11px;line-height:15px}.pm-table th.zoom3{font-size:13px;line-height:18px}.pm-table th.zoom4{font-size:15px;line-height:20px}.pm-table th.zoom5{font-size:18px;line-height:24px}.pm-table td:last-child{border-right-width:2px}.pm-table th.name{border-left:none;border-right:none;border-top:none}.pm-table th.asn{border-left:none;border-top:none;border-right-width:2px}.pm-table td.name{text-align:left;border-left:none;border-right:none;font-weight:700}.pm-table td.asn{text-align:right;border-left:none;border-right-width:2px;font-weight:700}.pm-table tr:last-child{border-bottom-width:2px}.pm-table td.peered{background-color:#0c0;width:10px}.pm-table td.not-peered{background-color:#d63030;width:10px}.pm-table td.bilateral-rs{width:10px}.pm-table td.rs-only{width:10px}.pm-table td.bilateral-only{width:10px}.pm-table colgroup.hover,.pm-table td.hover,.pm-table tr.hover{background-color:#eee;border-left:2px solid #555;border-right:2px solid #555}.pm-table td.hover2{border-left:1px solid #555;border-right:1px solid #555}.pm-table td.highlight,.pm-table th.highlight{background-color:#fcf6cf}.pm-table td.highlight2,.pm-table th.highlight2{background-color:#eee}.controls-fieldset-bordered{margin-left:90px}.legend-fieldset-bordered{font-size:14px;line-height:20px;margin-bottom:0;border-style:none none none;width:none;padding:5px}.fieldset-bordered{border:1px solid #ccc;padding:10px;border-radius:4px 4px 4px 4px;transition:border .2s linear 0s,box-shadow .2s linear 0s}.info_box .title{position:relative;top:-30px;margin-left:0;padding:2px 7px;display:inline;font-size:1em;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;border:1px solid #ddd;background-color:#fafafa;background-color:#f9f9f9;background-image:-moz-linear-gradient(top,#fff,#f0f0f0);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#f0f0f0));background-image:-webkit-linear-gradient(top,#fff,#f0f0f0);background-image:-o-linear-gradient(top,#fff,#f0f0f0);background-image:linear-gradient(to bottom,#fff,#f0f0f0);background-repeat:repeat-x}.info_box .title a{color:#555;text-decoration:none}.info_box .content{margin-top:-10px}.dropdown-menu li{display:block}.droppable{border:#ccc 1px solid;border-radius:8px;background:#eee;color:#666;padding:20px;margin:10px;clear:both;text-align:center}.droppable.hover{background:#ddd}.uploadList{margin:0;padding:0;list-style:none}.uploadItem{overflow:hidden;border-bottom:#bcbcbc 1px solid;margin:0 20px;padding:3px}.uploadItem span{overflow:hidden;width:150px;float:left;display:block}.uploadItem a,a.addInputRow,a.delInputRow{display:inline-block;background:url(add.png) no-repeat;height:16px;width:16px;text-indent:-999px}.uploadItem a{float:left;display:block;padding-left:20px;background-image:url(delete.png)}a.delInputRow{background-image:url(delete.png)}.progress{margin:5px 0;height:15px;border-radius:3px;background:#545a74}.upperCase{text-transform:uppercase}.lowerCase{text-transform:lowercase}td a:hover{text-decoration:none}.lb-sm{font-size:12px}.lb-md{font-size:16px}.lb-lg{font-size:20px}.asn-table{margin-left:30px;font-family:'Courier New',Courier,monospace}.asn-table tr td{padding:1px;padding-left:5px}.scrollable-dropdown{height:auto;max-height:200px;overflow-x:hidden}.table-no-border>tbody>tr>td,.table-no-border>tbody>tr>th,.table-no-border>tfoot>tr>td,.table-no-border>tfoot>tr>th,.table-no-border>thead>tr>td,.table-no-border>thead>tr>th{border:none!important}.page-content .page-header li.pull-right{list-style-type:none!important}.loader{border:6px solid #f3f3f3;border-radius:50%;border-top:6px solid #3498db;width:30px;height:30px;-webkit-animation:spin 2s linear infinite;animation:spin 2s linear infinite}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0)}100%{-webkit-transform:rotate(360deg)}}@keyframes spin{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}.wrap{-ms-word-break:break-all;word-break:break-all;word-break:break-word;-webkit-hyphens:auto;-moz-hyphens:auto;-ms-hyphens:auto;hyphens:auto}.form-control::placeholder{font-weight:lighter;opacity:1}.select2-container--default .select2-selection{width:100%;-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:.125rem;padding-top:.5rem;padding-bottom:.5rem;padding-left:.5rem;padding-right:1.5rem;line-height:1.25;--tw-text-opacity:1;border-width:1px;--tw-shadow:0 1px 3px 0 rgba(0, 0, 0, 0.1),0 1px 2px 0 rgba(0, 0, 0, 0.06);box-shadow:var(--tw-ring-offset-shadow,0 0 transparent),var(--tw-ring-shadow,0 0 transparent),var(--tw-shadow);height:2.25rem;border-color:#ced4da}.select2-container--default .select2-selection .select2-selection__rendered{color:rgb(96 111 123 / var(--tw-text-opacity));line-height:1.25rem}.select2-container--default .select2-selection .select2-selection__arrow{height:2.25rem;width:1.5rem}.tabNavMenu{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none;border-bottom:1px solid rgba(0,0,0,.125)}.tabButton{margin-bottom:-1px;padding:.5rem 1rem;background-color:transparent;border:1px solid transparent;border-bottom:0;border-top-left-radius:.5rem;border-top-right-radius:.5rem;color:#718096;font-size:.85em;margin-right:.25rem}.tabButton:hover{border-color:rgba(0,0,0,.075)}.tabButton.active{color:#4a5568;background-color:#fff;border-color:rgba(0,0,0,.125)}.tabContent .tabPanel{overflow:hidden;box-sizing:border-box;padding:0;height:0;border:none;border-bottom-left-radius:.5rem;border-bottom-right-radius:.5rem;opacity:0;transition:opacity .5s}.tabContent .tabPanel.active{opacity:1;border:1px solid rgba(0,0,0,.125);border-top:none;height:auto;padding:1rem 1.5rem}.inputWrapper{padding:1rem}.inputWrapper:nth-child(odd){background:rgba(0,0,0,.025)}.inputWrapper .form-group{margin-bottom:0}.inputWrapper .form-group .form-check label{display:block;font-size:.875rem;font-weight:700;color:rgb(96 111 123)}.badgeSwitcherFix{position:fixed;top:0;right:0;padding:.5rem;background:rgba(255,255,255,.9);border-radius:0 0 0 1rem}.info-line{position:relative;height:fit-content!important}.info-content{position:relative;overflow:hidden;width:calc(100% - 6.5rem);line-height:1.2rem;height:1.2rem;border:1px solid transparent;font-size:.9rem;transition:height .25s}.info-content .info-extra-content{font-size:.75rem;position:relative}.info-content .info-extra-content pre{margin:0}*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*! tailwindcss v3.4.17 | MIT License | https://tailwindcss.com*/*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:""}:host,html{-webkit-text-size-adjust:100%;font-feature-settings:normal;-webkit-tap-highlight-color:transparent;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-variation-settings:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4}body{line-height:inherit;margin:0}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-feature-settings:normal;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em;font-variation-settings:normal}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{font-feature-settings:inherit;color:inherit;font-family:inherit;font-size:100%;font-variation-settings:inherit;font-weight:inherit;letter-spacing:inherit;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{color:#9ca3af;opacity:1}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]:where(:not([hidden=until-found])){display:none}h1{color:#1a202c;font-size:2.4rem;font-weight:300;line-height:1;margin-bottom:1rem}h2{font-size:2rem;font-weight:500;line-height:1.2;margin-bottom:.5rem}h3{font-size:1.75rem;line-height:1.2}h3,h4{color:#1a202c;font-weight:300;margin-bottom:.5rem}h4{font-size:1.5rem;line-height:1}h5{font-size:1.25rem;font-weight:400}h5,h6{line-height:1.2;margin-bottom:.5rem}h6{font-size:1rem;font-weight:500}p{margin-bottom:1rem}li{display:list-item}ol{list-style-type:decimal}ol,ul{display:block;margin:1em 0;padding-left:40px}ul{list-style-type:disc}dl{margin:1em 0}dl,dt{display:block}code,pre{font-size:.9em}pre{display:block;font-family:monospace;margin:1em 0;white-space:pre}hr{margin-bottom:10px}a{color:rgb(59 130 246/var(--tw-text-opacity,1))}a:hover{text-decoration-line:underline}.tw-border-t-1{border-top-width:1px}.tw-border-b-1{border-bottom-width:1px}.tw-border-1{border-width:1px}.shadow{--tw-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px 0 rgba(0,0,0,.06);box-shadow:var(--tw-ring-offset-shadow,0 0 transparent),var(--tw-ring-shadow,0 0 transparent),var(--tw-shadow)}#side-navbar{--tw-bg-opacity:1;--tw-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px 0 rgba(0,0,0,.06);background-color:rgb(255 255 255/var(--tw-bg-opacity,1));box-shadow:var(--tw-ring-offset-shadow,0 0 transparent),var(--tw-ring-shadow,0 0 transparent),var(--tw-shadow);padding-right:0}#side-navbar ol,#side-navbar ul{list-style:none;margin:0;padding:0}#side-navbar h6{--tw-border-opacity:1;border-color:rgb(241 245 248/var(--tw-border-opacity,1));border-right-width:4px;margin:0;padding:1rem 1rem 1rem .25rem}#side-navbar h6,.nav-link{--tw-text-opacity:1;color:rgb(96 111 123/var(--tw-text-opacity,1));display:block}.nav-link{font-size:.875rem;line-height:1.25rem;padding:.5rem .5rem .5rem .75rem;text-decoration-line:none}.nav-link:hover{--tw-text-opacity:1}#side-navbar li,.nav-link:hover{color:rgb(96 111 123/var(--tw-text-opacity,1))}#side-navbar li{--tw-border-opacity:1;--tw-text-opacity:1;border-color:rgb(241 245 248/var(--tw-border-opacity,1));border-right-width:4px;display:block;font-weight:500;padding:0}#side-navbar li.active{font-weight:700!important}#side-navbar li.active,#side-navbar li:hover{--tw-border-opacity:1;--tw-bg-opacity:1;background-color:rgb(241 245 248/var(--tw-bg-opacity,1));border-color:rgb(5 150 105/var(--tw-border-opacity,1))}#side-navbar li:hover{--tw-text-opacity:1;color:rgb(96 111 123/var(--tw-text-opacity,1))}.nav-sub-menu-item{margin-left:2rem}.ixpm-table a{--tw-text-opacity:1;color:rgb(39 121 189/var(--tw-text-opacity,1));text-decoration-line:none}.ixpm-table a:hover{--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity,1));text-decoration-line:underline}.btn{border-radius:.125rem;font-weight:700}.btn,.btn:hover{border-color:transparent}.btn-primary{--tw-bg-opacity:1;--tw-text-opacity:1;background-color:rgb(59 130 246/var(--tw-bg-opacity,1));color:rgb(255 255 255/var(--tw-text-opacity,1));font-weight:700}.btn-primary:hover{--tw-bg-opacity:1;background-color:rgb(39 121 189/var(--tw-bg-opacity,1))}.btn-success{--tw-bg-opacity:1;--tw-text-opacity:1;background-color:rgb(16 185 129/var(--tw-bg-opacity,1));color:rgb(255 255 255/var(--tw-text-opacity,1));font-weight:700}.btn-success:hover{--tw-bg-opacity:1;background-color:rgb(31 157 85/var(--tw-bg-opacity,1))}.btn-secondary{--tw-bg-opacity:1;--tw-text-opacity:1;background-color:rgb(135 149 161/var(--tw-bg-opacity,1));color:rgb(255 255 255/var(--tw-text-opacity,1));font-weight:700}.btn-secondary:hover{--tw-bg-opacity:1;background-color:rgb(96 111 123/var(--tw-bg-opacity,1))}.btn-outline-secondary{--tw-border-opacity:1;--tw-text-opacity:1;background-color:transparent;border-color:rgb(184 194 204/var(--tw-border-opacity,1));border-radius:.125rem;border-width:1px;color:rgb(184 194 204/var(--tw-text-opacity,1));font-weight:600}.btn-outline-secondary:hover{--tw-bg-opacity:1;background-color:rgb(184 194 204/var(--tw-bg-opacity,1))}.btn-white{--tw-border-opacity:1;--tw-bg-opacity:1;--tw-text-opacity:1;--tw-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px 0 rgba(0,0,0,.06);background-color:rgb(255 255 255/var(--tw-bg-opacity,1));border-radius:.125rem;box-shadow:var(--tw-ring-offset-shadow,0 0 transparent),var(--tw-ring-shadow,0 0 transparent),var(--tw-shadow);color:rgb(61 72 82/var(--tw-text-opacity,1));font-weight:600}.btn-white,.btn-white:hover{border-color:rgb(218 225 231/var(--tw-border-opacity,1));border-width:1px}.btn-white:hover{--tw-border-opacity:1;--tw-bg-opacity:1;background-color:rgb(241 245 248/var(--tw-bg-opacity,1))}.btn-info{--tw-border-opacity:1;--tw-bg-opacity:1;--tw-text-opacity:1;--tw-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px 0 rgba(0,0,0,.06);background-color:rgb(255 255 255/var(--tw-bg-opacity,1));border-color:rgb(20 184 166/var(--tw-border-opacity,1));border-radius:.125rem;border-width:1px;box-shadow:var(--tw-ring-offset-shadow,0 0 transparent),var(--tw-ring-shadow,0 0 transparent),var(--tw-shadow);color:rgb(20 184 166/var(--tw-text-opacity,1));font-weight:600}.btn-info:hover{--tw-border-opacity:1;--tw-bg-opacity:1;background-color:rgb(20 184 166/var(--tw-bg-opacity,1));border-color:rgb(218 225 231/var(--tw-border-opacity,1));border-width:1px}input.form-control{--tw-text-opacity:1;--tw-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px 0 rgba(0,0,0,.06);-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:.125rem;border-width:1px;box-shadow:var(--tw-ring-offset-shadow,0 0 transparent),var(--tw-ring-shadow,0 0 transparent),var(--tw-shadow);padding:.5rem 1rem;width:100%}input.form-control,input.form-control:focus{color:rgb(96 111 123/var(--tw-text-opacity,1));line-height:1.25}input.form-control:focus{--tw-text-opacity:1;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);outline:2px solid transparent;outline-offset:2px}.input-group-append .btn{--tw-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px 0 rgba(0,0,0,.06);border-color:#ced4da;box-shadow:var(--tw-ring-offset-shadow,0 0 transparent),var(--tw-ring-shadow,0 0 transparent),var(--tw-shadow)}label.control-label,label.form-check-label{--tw-text-opacity:1;color:rgb(96 111 123/var(--tw-text-opacity,1));display:block;font-size:.875rem;font-weight:700;line-height:1.25rem}.badge{--tw-text-opacity:1;border-radius:9999px;color:rgb(255 255 255/var(--tw-text-opacity,1));font-weight:700;padding:.25rem .75rem}.badge-info{--tw-bg-opacity:1;background-color:rgb(20 184 166/var(--tw-bg-opacity,1))}.badge-success{--tw-bg-opacity:1;background-color:rgb(16 185 129/var(--tw-bg-opacity,1))}.badge-secondary{--tw-bg-opacity:1;background-color:rgb(184 194 204/var(--tw-bg-opacity,1))}.badge-warning{--tw-bg-opacity:1;--tw-text-opacity:1;background-color:rgb(251 146 60/var(--tw-bg-opacity,1));color:rgb(0 0 0/var(--tw-text-opacity,1))}.card{--tw-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px 0 rgba(0,0,0,.06);border-color:#dae1e7;border-radius:.125rem;box-shadow:var(--tw-ring-offset-shadow,0 0 transparent),var(--tw-ring-shadow,0 0 transparent),var(--tw-shadow)}.card .card-header{border-bottom-width:0;border-top-left-radius:.125rem;border-top-right-radius:.125rem}a{color:rgb(108 178 235/var(--tw-text-opacity,1));display:inline-block;font-weight:700;vertical-align:baseline}a,a:hover{--tw-text-opacity:1}a:hover{color:rgb(39 121 189/var(--tw-text-opacity,1));text-decoration-line:none}.table-light td,.table-light thead th{border-color:#ced4da}.docstore table{table-layout:auto;width:100%}.docstore td{border-color:rgb(209 213 219/var(--tw-border-opacity,1));border-top-width:1px}.docstore td,.docstore td.top{--tw-border-opacity:1;padding:.5rem}.docstore td.top{border-color:rgb(209 213 219/var(--tw-border-opacity,1));border-top-width:2px}.docstore td.meta{display:none;white-space:nowrap;width:.25rem}@media (min-width:1024px){.docstore td.meta{display:table-cell;white-space:nowrap;width:.25rem}}.docstore td.icon{margin-right:1rem;padding:.5rem;text-align:center;white-space:nowrap;width:.25rem}.tw-collapse{visibility:collapse}.tw-absolute{position:absolute}.tw-relative{position:relative}.tw-inset-0{inset:0}.tw-float-right{float:right}.tw-m-0{margin:0}.tw-m-2{margin:.5rem}.tw-mx-3{margin-left:.75rem;margin-right:.75rem}.tw-mx-4{margin-left:1rem;margin-right:1rem}.tw-mx-auto{margin-left:auto;margin-right:auto}.tw-my-0{margin-bottom:0;margin-top:0}.tw-my-1{margin-bottom:.25rem;margin-top:.25rem}.tw-my-10{margin-bottom:2.5rem;margin-top:2.5rem}.tw-my-12{margin-bottom:3rem;margin-top:3rem}.tw-my-4{margin-bottom:1rem;margin-top:1rem}.tw-my-6{margin-bottom:1.5rem;margin-top:1.5rem}.tw-my-8{margin-bottom:2rem;margin-top:2rem}.tw-mb-0{margin-bottom:0}.tw-mb-16{margin-bottom:4rem}.tw-mb-2{margin-bottom:.5rem}.tw-mb-6{margin-bottom:1.5rem}.tw-mb-8{margin-bottom:2rem}.tw-ml-1{margin-left:.25rem}.tw-ml-12{margin-left:3rem}.tw-ml-2{margin-left:.5rem}.tw-ml-4{margin-left:1rem}.tw-ml-6{margin-left:1.5rem}.tw-ml-8{margin-left:2rem}.tw-mr-1{margin-right:.25rem}.tw-mr-2{margin-right:.5rem}.tw-mr-3{margin-right:.75rem}.tw-mr-4{margin-right:1rem}.tw-mr-6{margin-right:1.5rem}.tw-mt-1{margin-top:.25rem}.tw-mt-16{margin-top:4rem}.tw-mt-2{margin-top:.5rem}.tw-mt-3{margin-top:.75rem}.tw-mt-4{margin-top:1rem}.tw-mt-6{margin-top:1.5rem}.tw-mt-8{margin-top:2rem}.tw-block{display:block}.tw-inline-block{display:inline-block}.tw-inline{display:inline}.tw-flex{display:flex}.tw-inline-flex{display:inline-flex}.tw-hidden{display:none}.tw-h-24{height:6rem}.tw-h-5{height:1.25rem}.tw-h-6{height:1.5rem}.tw-h-7{height:1.75rem}.tw-h-8{height:2rem}.tw-w-10{width:2.5rem}.tw-w-20{width:5rem}.tw-w-5{width:1.25rem}.tw-w-6{width:1.5rem}.tw-w-auto{width:auto}.tw-w-full{width:100%}.tw-min-w-16{min-width:4rem}.tw-min-w-full{min-width:100%}.tw-max-w-lg{max-width:32rem}.tw-max-w-md{max-width:28rem}.tw-max-w-sm{max-width:24rem}.tw-flex-1{flex:1 1 0%}.tw-cursor-pointer{cursor:pointer}.tw-list-none{list-style-type:none}.tw-flex-wrap{flex-wrap:wrap}.tw-items-center{align-items:center}.tw-justify-start{justify-content:flex-start}.tw-justify-center{justify-content:center}.tw-justify-between{justify-content:space-between}.tw-divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-bottom-width:calc(1px*var(--tw-divide-y-reverse));border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)))}.tw-divide-gray-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(229 231 235/var(--tw-divide-opacity,1))}.tw-divide-gray-300>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(209 213 219/var(--tw-divide-opacity,1))}.tw-self-center{align-self:center}.tw-overflow-hidden{overflow:hidden}.tw-whitespace-nowrap{white-space:nowrap}.tw-rounded{border-radius:.25rem}.tw-rounded-full{border-radius:9999px}.tw-rounded-lg{border-radius:.5rem}.tw-rounded-md{border-radius:.375rem}.tw-rounded-sm{border-radius:.125rem}.tw-border-2{border-width:2px}.tw-border-b{border-bottom-width:1px}.tw-border-b-2{border-bottom-width:2px}.tw-border-l-4{border-left-width:4px}.tw-border-r-4{border-right-width:4px}.tw-border-t{border-top-width:1px}.tw-border-t-2{border-top-width:2px}.tw-border-amber-400{--tw-border-opacity:1;border-color:rgb(251 191 36/var(--tw-border-opacity,1))}.tw-border-blue-500{--tw-border-opacity:1;border-color:rgb(59 130 246/var(--tw-border-opacity,1))}.tw-border-gray-200{--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity,1))}.tw-border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity,1))}.tw-border-gray-400{--tw-border-opacity:1;border-color:rgb(156 163 175/var(--tw-border-opacity,1))}.tw-border-gray-500{--tw-border-opacity:1;border-color:rgb(107 114 128/var(--tw-border-opacity,1))}.tw-border-gray-600{--tw-border-opacity:1;border-color:rgb(75 85 99/var(--tw-border-opacity,1))}.tw-border-green-500{--tw-border-opacity:1;border-color:rgb(16 185 129/var(--tw-border-opacity,1))}.tw-border-grey-light{--tw-border-opacity:1;border-color:rgb(218 225 231/var(--tw-border-opacity,1))}.tw-border-grey-lighter{--tw-border-opacity:1;border-color:rgb(241 245 248/var(--tw-border-opacity,1))}.tw-border-lime-500{--tw-border-opacity:1;border-color:rgb(132 204 22/var(--tw-border-opacity,1))}.tw-border-orange-500{--tw-border-opacity:1;border-color:rgb(249 115 22/var(--tw-border-opacity,1))}.tw-border-red-400{--tw-border-opacity:1;border-color:rgb(248 113 113/var(--tw-border-opacity,1))}.tw-border-red-500{--tw-border-opacity:1;border-color:rgb(239 68 68/var(--tw-border-opacity,1))}.tw-border-red-600{--tw-border-opacity:1;border-color:rgb(220 38 38/var(--tw-border-opacity,1))}.tw-border-red-lighter{--tw-border-opacity:1;border-color:rgb(249 172 170/var(--tw-border-opacity,1))}.tw-border-teal-400{--tw-border-opacity:1;border-color:rgb(45 212 191/var(--tw-border-opacity,1))}.tw-border-b-gray-200{--tw-border-opacity:1;border-bottom-color:rgb(229 231 235/var(--tw-border-opacity,1))}.tw-bg-amber-400{--tw-bg-opacity:1;background-color:rgb(251 191 36/var(--tw-bg-opacity,1))}.tw-bg-blue-100{--tw-bg-opacity:1;background-color:rgb(219 234 254/var(--tw-bg-opacity,1))}.tw-bg-blue-50{--tw-bg-opacity:1;background-color:rgb(239 246 255/var(--tw-bg-opacity,1))}.tw-bg-blue-lightest{--tw-bg-opacity:1;background-color:rgb(239 248 255/var(--tw-bg-opacity,1))}.tw-bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity,1))}.tw-bg-gray-200{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity,1))}.tw-bg-gray-300{--tw-bg-opacity:1;background-color:rgb(209 213 219/var(--tw-bg-opacity,1))}.tw-bg-gray-400{--tw-bg-opacity:1;background-color:rgb(156 163 175/var(--tw-bg-opacity,1))}.tw-bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity,1))}.tw-bg-gray-500{--tw-bg-opacity:1;background-color:rgb(107 114 128/var(--tw-bg-opacity,1))}.tw-bg-gray-700{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity,1))}.tw-bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity,1))}.tw-bg-green-100{--tw-bg-opacity:1;background-color:rgb(209 250 229/var(--tw-bg-opacity,1))}.tw-bg-green-50{--tw-bg-opacity:1;background-color:rgb(236 253 245/var(--tw-bg-opacity,1))}.tw-bg-grey-lighter{--tw-bg-opacity:1;background-color:rgb(241 245 248/var(--tw-bg-opacity,1))}.tw-bg-lime-500{--tw-bg-opacity:1;background-color:rgb(132 204 22/var(--tw-bg-opacity,1))}.tw-bg-orange-100{--tw-bg-opacity:1;background-color:rgb(255 237 213/var(--tw-bg-opacity,1))}.tw-bg-pink-50{--tw-bg-opacity:1;background-color:rgb(253 242 248/var(--tw-bg-opacity,1))}.tw-bg-red-100{--tw-bg-opacity:1;background-color:rgb(254 226 226/var(--tw-bg-opacity,1))}.tw-bg-red-400{--tw-bg-opacity:1;background-color:rgb(248 113 113/var(--tw-bg-opacity,1))}.tw-bg-red-50{--tw-bg-opacity:1;background-color:rgb(254 242 242/var(--tw-bg-opacity,1))}.tw-bg-red-600{--tw-bg-opacity:1;background-color:rgb(220 38 38/var(--tw-bg-opacity,1))}.tw-bg-teal-400{--tw-bg-opacity:1;background-color:rgb(45 212 191/var(--tw-bg-opacity,1))}.tw-bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.tw-bg-yellow-50{--tw-bg-opacity:1;background-color:rgb(255 251 235/var(--tw-bg-opacity,1))}.tw-p-0{padding:0}.tw-p-1{padding:.25rem}.tw-p-2{padding:.5rem}.tw-p-4{padding:1rem}.tw-p-5{padding:1.25rem}.tw-p-6{padding:1.5rem}.tw-px-1{padding-left:.25rem;padding-right:.25rem}.tw-px-1\.5{padding-left:.375rem;padding-right:.375rem}.tw-px-10{padding-left:2.5rem;padding-right:2.5rem}.tw-px-2{padding-left:.5rem;padding-right:.5rem}.tw-px-3{padding-left:.75rem;padding-right:.75rem}.tw-px-4{padding-left:1rem;padding-right:1rem}.tw-px-6{padding-left:1.5rem;padding-right:1.5rem}.tw-px-8{padding-left:2rem;padding-right:2rem}.tw-py-0{padding-bottom:0;padding-top:0}.tw-py-0\.5{padding-bottom:.125rem;padding-top:.125rem}.tw-py-1{padding-bottom:.25rem;padding-top:.25rem}.tw-py-2{padding-bottom:.5rem;padding-top:.5rem}.tw-py-20{padding-bottom:5rem;padding-top:5rem}.tw-py-4{padding-bottom:1rem;padding-top:1rem}.tw-py-5{padding-bottom:1.25rem;padding-top:1.25rem}.tw-py-6{padding-bottom:1.5rem;padding-top:1.5rem}.tw-pb-2{padding-bottom:.5rem}.tw-pb-8{padding-bottom:2rem}.tw-pl-0{padding-left:0}.tw-pl-16{padding-left:4rem}.tw-pl-2{padding-left:.5rem}.tw-pl-4{padding-left:1rem}.tw-pr-4{padding-right:1rem}.tw-pt-0{padding-top:0}.tw-pt-4{padding-top:1rem}.tw-pt-6{padding-top:1.5rem}.tw-text-center{text-align:center}.tw-text-right{text-align:right}.tw-align-middle{vertical-align:middle}.tw-font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.tw-text-base{font-size:1rem;line-height:1.5rem}.tw-text-lg{font-size:1.125rem;line-height:1.75rem}.tw-text-sm{font-size:.875rem;line-height:1.25rem}.tw-text-xl{font-size:1.25rem;line-height:1.75rem}.tw-text-xs{font-size:.75rem;line-height:1rem}.tw-font-bold{font-weight:700}.tw-font-medium{font-weight:500}.tw-font-normal{font-weight:400}.tw-font-semibold{font-weight:600}.tw-uppercase{text-transform:uppercase}.tw-italic{font-style:italic}.tw-slashed-zero{--tw-slashed-zero:slashed-zero}.tw-lining-nums,.tw-slashed-zero{font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.tw-lining-nums{--tw-numeric-figure:lining-nums}.tw-tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.tw-leading-5{line-height:1.25rem}.tw-leading-6{line-height:1.5rem}.tw-leading-7{line-height:1.75rem}.tw-leading-8{line-height:2rem}.tw-leading-tight{line-height:1.25}.tw-text-black{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity,1))}.tw-text-blue-600{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity,1))}.tw-text-blue-700{--tw-text-opacity:1;color:rgb(29 78 216/var(--tw-text-opacity,1))}.tw-text-blue-dark{--tw-text-opacity:1;color:rgb(39 121 189/var(--tw-text-opacity,1))}.tw-text-gray-100{--tw-text-opacity:1;color:rgb(243 244 246/var(--tw-text-opacity,1))}.tw-text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity,1))}.tw-text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity,1))}.tw-text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity,1))}.tw-text-gray-800{--tw-text-opacity:1;color:rgb(31 41 55/var(--tw-text-opacity,1))}.tw-text-gray-900{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity,1))}.tw-text-green-500{--tw-text-opacity:1;color:rgb(16 185 129/var(--tw-text-opacity,1))}.tw-text-green-600{--tw-text-opacity:1;color:rgb(5 150 105/var(--tw-text-opacity,1))}.tw-text-green-700{--tw-text-opacity:1;color:rgb(4 120 87/var(--tw-text-opacity,1))}.tw-text-green-dark{--tw-text-opacity:1;color:rgb(31 157 85/var(--tw-text-opacity,1))}.tw-text-grey-dark{--tw-text-opacity:1;color:rgb(135 149 161/var(--tw-text-opacity,1))}.tw-text-grey-darker{--tw-text-opacity:1;color:rgb(96 111 123/var(--tw-text-opacity,1))}.tw-text-grey-darkest{--tw-text-opacity:1;color:rgb(61 72 82/var(--tw-text-opacity,1))}.tw-text-orange-500{--tw-text-opacity:1;color:rgb(249 115 22/var(--tw-text-opacity,1))}.tw-text-orange-700{--tw-text-opacity:1;color:rgb(194 65 12/var(--tw-text-opacity,1))}.tw-text-pink-700{--tw-text-opacity:1;color:rgb(190 24 93/var(--tw-text-opacity,1))}.tw-text-red-500{--tw-text-opacity:1;color:rgb(239 68 68/var(--tw-text-opacity,1))}.tw-text-red-600{--tw-text-opacity:1;color:rgb(220 38 38/var(--tw-text-opacity,1))}.tw-text-red-700{--tw-text-opacity:1;color:rgb(185 28 28/var(--tw-text-opacity,1))}.tw-text-red-lighter{--tw-text-opacity:1;color:rgb(249 172 170/var(--tw-text-opacity,1))}.tw-line-through{text-decoration-line:line-through}.tw-opacity-40{opacity:.4}.tw-shadow{--tw-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px -1px rgba(0,0,0,.1);--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)}.tw-shadow,.tw-shadow-lg{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.tw-shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.tw-shadow-md{--tw-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -2px rgba(0,0,0,.1);--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color)}.tw-shadow-md,.tw-shadow-sm{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.tw-shadow-sm{--tw-shadow:0 1px 2px 0 rgba(0,0,0,.05);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.tw-ring-1{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.tw-ring-inset{--tw-ring-inset:inset}.tw-ring-blue-700\/10{--tw-ring-color:rgba(29,78,216,.1)}.tw-ring-gray-300{--tw-ring-opacity:1;--tw-ring-color:rgb(209 213 219/var(--tw-ring-opacity,1))}.tw-ring-gray-500\/10{--tw-ring-color:hsla(220,9%,46%,.1)}.tw-ring-gray-800\/10{--tw-ring-color:rgba(31,41,55,.1)}.tw-ring-gray-800\/50{--tw-ring-color:rgba(31,41,55,.5)}.tw-ring-green-600\/20{--tw-ring-color:rgba(5,150,105,.2)}.tw-ring-pink-700\/10{--tw-ring-color:rgba(190,24,93,.1)}.tw-ring-red-600\/10{--tw-ring-color:rgba(220,38,38,.1)}.tw-ring-yellow-600\/20{--tw-ring-color:rgba(217,119,6,.2)}.\[a-f0-9\:\\\.\\-\]{a-f0-9:\.\-}.\[id\:\%d\]{id:%d}.hover\:tw-border-gray-800:hover{--tw-border-opacity:1;border-color:rgb(31 41 55/var(--tw-border-opacity,1))}.hover\:tw-bg-gray-50:hover{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity,1))}.hover\:tw-bg-grey-lighter:hover{--tw-bg-opacity:1;background-color:rgb(241 245 248/var(--tw-bg-opacity,1))}.hover\:tw-no-underline:hover{text-decoration-line:none}.hover\:tw-opacity-80:hover{opacity:.8}@media (min-width:640px){.sm\:tw-p-6{padding:1.5rem}.sm\:tw-px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:tw-pl-0{padding-left:0}}@media (min-width:768px){.md\:tw-mt-0{margin-top:0}.md\:tw-inline{display:inline}.md\:tw-table-cell{display:table-cell}.md\:tw-text-left{text-align:left}.md\:tw-text-right{text-align:right}}@media (min-width:1024px){.lg\:tw-inline-block{display:inline-block}.lg\:tw-inline{display:inline}.lg\:tw-table-cell{display:table-cell}} diff --git a/public/css/min.bundle-v24.css b/public/css/min.bundle-v24.css deleted file mode 100644 index 170724ec5..000000000 --- a/public/css/min.bundle-v24.css +++ /dev/null @@ -1,127 +0,0 @@ -/*! - * jQuery UI CSS Framework 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Theming/API - */.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{position:absolute!important;clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px)}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table}.ui-helper-clearfix:after{clear:both}.ui-helper-clearfix{zoom:1}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-state-disabled{cursor:default!important}.ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-overlay{position:absolute;top:0;left:0;width:100%;height:100%}/*! - * jQuery UI CSS Framework 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Theming/API - * - * To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Trebuchet%20MS,%20Tahoma,%20Verdana,%20Arial,%20sans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=f6a828&bgTextureHeader=12_gloss_wave.png&bgImgOpacityHeader=35&borderColorHeader=e78f08&fcHeader=ffffff&iconColorHeader=ffffff&bgColorContent=eeeeee&bgTextureContent=03_highlight_soft.png&bgImgOpacityContent=100&borderColorContent=dddddd&fcContent=333333&iconColorContent=222222&bgColorDefault=f6f6f6&bgTextureDefault=02_glass.png&bgImgOpacityDefault=100&borderColorDefault=cccccc&fcDefault=1c94c4&iconColorDefault=ef8c08&bgColorHover=fdf5ce&bgTextureHover=02_glass.png&bgImgOpacityHover=100&borderColorHover=fbcb09&fcHover=c77405&iconColorHover=ef8c08&bgColorActive=ffffff&bgTextureActive=02_glass.png&bgImgOpacityActive=65&borderColorActive=fbd850&fcActive=eb8f00&iconColorActive=ef8c08&bgColorHighlight=ffe45c&bgTextureHighlight=03_highlight_soft.png&bgImgOpacityHighlight=75&borderColorHighlight=fed22f&fcHighlight=363636&iconColorHighlight=228ef1&bgColorError=b81900&bgTextureError=08_diagonals_thick.png&bgImgOpacityError=18&borderColorError=cd0a0a&fcError=ffffff&iconColorError=ffd27a&bgColorOverlay=666666&bgTextureOverlay=08_diagonals_thick.png&bgImgOpacityOverlay=20&opacityOverlay=50&bgColorShadow=000000&bgTextureShadow=01_flat.png&bgImgOpacityShadow=10&opacityShadow=20&thicknessShadow=5px&offsetTopShadow=-5px&offsetLeftShadow=-5px&cornerRadiusShadow=5px - */.ui-widget{font-family:Trebuchet MS,Tahoma,Verdana,Arial,sans-serif;font-size:1.1em}.ui-widget .ui-widget{font-size:1em}.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button{font-family:Trebuchet MS,Tahoma,Verdana,Arial,sans-serif;font-size:1em}.ui-widget-content{border:1px solid #ddd;background:#eee url(images/ui-bg_highlight-soft_100_eeeeee_1x100.png) 50% top repeat-x;color:#333}.ui-widget-content a{color:#333}.ui-widget-header{border:1px solid #e78f08;background:#f6a828 url(images/ui-bg_gloss-wave_35_f6a828_500x100.png) 50% 50% repeat-x;color:#fff;font-weight:bold}.ui-widget-header a{color:#fff}.ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default{border:1px solid #ccc;background:#f6f6f6 url(images/ui-bg_glass_100_f6f6f6_1x400.png) 50% 50% repeat-x;font-weight:bold;color:#1c94c4}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited{color:#1c94c4;text-decoration:none}.ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus{border:1px solid #fbcb09;background:#fdf5ce url(images/ui-bg_glass_100_fdf5ce_1x400.png) 50% 50% repeat-x;font-weight:bold;color:#c77405}.ui-state-hover a,.ui-state-hover a:hover{color:#c77405;text-decoration:none}.ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active{border:1px solid #fbd850;background:#fff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x;font-weight:bold;color:#eb8f00}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#eb8f00;text-decoration:none}.ui-widget :active{outline:0}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #fed22f;background:#ffe45c url(images/ui-bg_highlight-soft_75_ffe45c_1x100.png) 50% top repeat-x;color:#363636}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#363636}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #cd0a0a;background:#b81900 url(images/ui-bg_diagonals-thick_18_b81900_40x40.png) 50% 50% repeat;color:#fff}.ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#fff}.ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#fff}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:bold}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:normal}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none}.ui-icon{width:16px;height:16px;background-image:url(images/ui-icons_222222_256x240.png)}.ui-widget-content .ui-icon{background-image:url(images/ui-icons_222222_256x240.png)}.ui-widget-header .ui-icon{background-image:url(images/ui-icons_ffffff_256x240.png)}.ui-state-default .ui-icon{background-image:url(images/ui-icons_ef8c08_256x240.png)}.ui-state-hover .ui-icon,.ui-state-focus .ui-icon{background-image:url(images/ui-icons_ef8c08_256x240.png)}.ui-state-active .ui-icon{background-image:url(images/ui-icons_ef8c08_256x240.png)}.ui-state-highlight .ui-icon{background-image:url(images/ui-icons_228ef1_256x240.png)}.ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url(images/ui-icons_ffd27a_256x240.png)}.ui-icon-carat-1-n{background-position:0 0}.ui-icon-carat-1-ne{background-position:-16px 0}.ui-icon-carat-1-e{background-position:-32px 0}.ui-icon-carat-1-se{background-position:-48px 0}.ui-icon-carat-1-s{background-position:-64px 0}.ui-icon-carat-1-sw{background-position:-80px 0}.ui-icon-carat-1-w{background-position:-96px 0}.ui-icon-carat-1-nw{background-position:-112px 0}.ui-icon-carat-2-n-s{background-position:-128px 0}.ui-icon-carat-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-64px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-64px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:0 -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-off{background-position:-96px -144px}.ui-icon-radio-on{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-seek-first{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-all,.ui-corner-top,.ui-corner-left,.ui-corner-tl{-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;-khtml-border-top-left-radius:4px;border-top-left-radius:4px}.ui-corner-all,.ui-corner-top,.ui-corner-right,.ui-corner-tr{-moz-border-radius-topright:4px;-webkit-border-top-right-radius:4px;-khtml-border-top-right-radius:4px;border-top-right-radius:4px}.ui-corner-all,.ui-corner-bottom,.ui-corner-left,.ui-corner-bl{-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;-khtml-border-bottom-left-radius:4px;border-bottom-left-radius:4px}.ui-corner-all,.ui-corner-bottom,.ui-corner-right,.ui-corner-br{-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;-khtml-border-bottom-right-radius:4px;border-bottom-right-radius:4px}.ui-widget-overlay{background:#666 url(images/ui-bg_diagonals-thick_20_666666_40x40.png) 50% 50% repeat;opacity:.50;filter:Alpha(Opacity=50)}.ui-widget-shadow{margin:-5px 0 0 -5px;padding:5px;background:#000 url(images/ui-bg_flat_10_000000_40x100.png) 50% 50% repeat-x;opacity:.20;filter:Alpha(Opacity=20);-moz-border-radius:5px;-khtml-border-radius:5px;-webkit-border-radius:5px;border-radius:5px}/*! - * jQuery UI Resizable 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Resizable#theming - */.ui-resizable{position:relative}.ui-resizable-handle{position:absolute;font-size:.1px;display:block}.ui-resizable-disabled .ui-resizable-handle,.ui-resizable-autohide .ui-resizable-handle{display:none}.ui-resizable-n{cursor:n-resize;height:7px;width:100%;top:-5px;left:0}.ui-resizable-s{cursor:s-resize;height:7px;width:100%;bottom:-5px;left:0}.ui-resizable-e{cursor:e-resize;width:7px;right:-5px;top:0;height:100%}.ui-resizable-w{cursor:w-resize;width:7px;left:-5px;top:0;height:100%}.ui-resizable-se{cursor:se-resize;width:12px;height:12px;right:1px;bottom:1px}.ui-resizable-sw{cursor:sw-resize;width:9px;height:9px;left:-5px;bottom:-5px}.ui-resizable-nw{cursor:nw-resize;width:9px;height:9px;left:-5px;top:-5px}.ui-resizable-ne{cursor:ne-resize;width:9px;height:9px;right:-5px;top:-5px}/*! - * jQuery UI Selectable 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Selectable#theming - */.ui-selectable-helper{position:absolute;z-index:100;border:1px dotted black}/*! - * jQuery UI Accordion 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Accordion#theming - */.ui-accordion{width:100%}.ui-accordion .ui-accordion-header{cursor:pointer;position:relative;margin-top:1px;zoom:1}.ui-accordion .ui-accordion-li-fix{display:inline}.ui-accordion .ui-accordion-header-active{border-bottom:0!important}.ui-accordion .ui-accordion-header a{display:block;font-size:1em;padding:.5em .5em .5em .7em}.ui-accordion-icons .ui-accordion-header a{padding-left:2.2em}.ui-accordion .ui-accordion-header .ui-icon{position:absolute;left:.5em;top:50%;margin-top:-8px}.ui-accordion .ui-accordion-content{padding:1em 2.2em;border-top:0;margin-top:-2px;position:relative;top:1px;margin-bottom:2px;overflow:auto;display:none;zoom:1}.ui-accordion .ui-accordion-content-active{display:block}/*! - * jQuery UI Autocomplete 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Autocomplete#theming - */.ui-autocomplete{position:absolute;cursor:default}* html .ui-autocomplete{width:1px}.ui-menu{list-style:none;padding:2px;margin:0;display:block;float:left}.ui-menu .ui-menu{margin-top:-3px}.ui-menu .ui-menu-item{margin:0;padding:0;zoom:1;float:left;clear:left;width:100%}.ui-menu .ui-menu-item a{text-decoration:none;display:block;padding:.2em .4em;line-height:1.5;zoom:1}.ui-menu .ui-menu-item a.ui-state-hover,.ui-menu .ui-menu-item a.ui-state-active{font-weight:normal;margin:-1px}/*! - * jQuery UI Button 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Button#theming - */.ui-button{display:inline-block;position:relative;padding:0;margin-right:.1em;text-decoration:none!important;cursor:pointer;text-align:center;zoom:1;overflow:visible}.ui-button-icon-only{width:2.2em}button.ui-button-icon-only{width:2.4em}.ui-button-icons-only{width:3.4em}button.ui-button-icons-only{width:3.7em}.ui-button .ui-button-text{display:block;line-height:1.4}.ui-button-text-only .ui-button-text{padding:.4em 1em}.ui-button-icon-only .ui-button-text,.ui-button-icons-only .ui-button-text{padding:.4em;text-indent:-9999999px}.ui-button-text-icon-primary .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 1em .4em 2.1em}.ui-button-text-icon-secondary .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 2.1em .4em 1em}.ui-button-text-icons .ui-button-text{padding-left:2.1em;padding-right:2.1em}input.ui-button{padding:.4em 1em}.ui-button-icon-only .ui-icon,.ui-button-text-icon-primary .ui-icon,.ui-button-text-icon-secondary .ui-icon,.ui-button-text-icons .ui-icon,.ui-button-icons-only .ui-icon{position:absolute;top:50%;margin-top:-8px}.ui-button-icon-only .ui-icon{left:50%;margin-left:-8px}.ui-button-text-icon-primary .ui-button-icon-primary,.ui-button-text-icons .ui-button-icon-primary,.ui-button-icons-only .ui-button-icon-primary{left:.5em}.ui-button-text-icon-secondary .ui-button-icon-secondary,.ui-button-text-icons .ui-button-icon-secondary,.ui-button-icons-only .ui-button-icon-secondary{right:.5em}.ui-button-text-icons .ui-button-icon-secondary,.ui-button-icons-only .ui-button-icon-secondary{right:.5em}.ui-buttonset{margin-right:7px}.ui-buttonset .ui-button{margin-left:0;margin-right:-.3em}button.ui-button::-moz-focus-inner{border:0;padding:0}/*! - * jQuery UI Dialog 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Dialog#theming - */.ui-dialog{position:absolute;padding:.2em;width:300px;overflow:hidden}.ui-dialog .ui-dialog-titlebar{padding:.4em 1em;position:relative}.ui-dialog .ui-dialog-title{float:left;margin:.1em 16px .1em 0}.ui-dialog .ui-dialog-titlebar-close{position:absolute;right:.3em;top:50%;width:19px;margin:-10px 0 0 0;padding:1px;height:18px}.ui-dialog .ui-dialog-titlebar-close span{display:block;margin:1px}.ui-dialog .ui-dialog-titlebar-close:hover,.ui-dialog .ui-dialog-titlebar-close:focus{padding:0}.ui-dialog .ui-dialog-content{position:relative;border:0;padding:.5em 1em;background:0;overflow:auto;zoom:1}.ui-dialog .ui-dialog-buttonpane{text-align:left;border-width:1px 0 0 0;background-image:none;margin:.5em 0 0 0;padding:.3em 1em .5em .4em}.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset{float:right}.ui-dialog .ui-dialog-buttonpane button{margin:.5em .4em .5em 0;cursor:pointer}.ui-dialog .ui-resizable-se{width:14px;height:14px;right:3px;bottom:3px}.ui-draggable .ui-dialog-titlebar{cursor:move}/*! - * jQuery UI Slider 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Slider#theming - */.ui-slider{position:relative;text-align:left}.ui-slider .ui-slider-handle{position:absolute;z-index:2;width:1.2em;height:1.2em;cursor:default}.ui-slider .ui-slider-range{position:absolute;z-index:1;font-size:.7em;display:block;border:0;background-position:0 0}.ui-slider-horizontal{height:.8em}.ui-slider-horizontal .ui-slider-handle{top:-.3em;margin-left:-.6em}.ui-slider-horizontal .ui-slider-range{top:0;height:100%}.ui-slider-horizontal .ui-slider-range-min{left:0}.ui-slider-horizontal .ui-slider-range-max{right:0}.ui-slider-vertical{width:.8em;height:100px}.ui-slider-vertical .ui-slider-handle{left:-.3em;margin-left:0;margin-bottom:-.6em}.ui-slider-vertical .ui-slider-range{left:0;width:100%}.ui-slider-vertical .ui-slider-range-min{bottom:0}.ui-slider-vertical .ui-slider-range-max{top:0}/*! - * jQuery UI Tabs 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Tabs#theming - */.ui-tabs{position:relative;padding:.2em;zoom:1}.ui-tabs .ui-tabs-nav{margin:0;padding:.2em .2em 0}.ui-tabs .ui-tabs-nav li{list-style:none;float:left;position:relative;top:1px;margin:0 .2em 1px 0;border-bottom:0!important;padding:0;white-space:nowrap}.ui-tabs .ui-tabs-nav li a{float:left;padding:.5em 1em;text-decoration:none}.ui-tabs .ui-tabs-nav li.ui-tabs-selected{margin-bottom:0;padding-bottom:1px}.ui-tabs .ui-tabs-nav li.ui-tabs-selected a,.ui-tabs .ui-tabs-nav li.ui-state-disabled a,.ui-tabs .ui-tabs-nav li.ui-state-processing a{cursor:text}.ui-tabs .ui-tabs-nav li a,.ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a{cursor:pointer}.ui-tabs .ui-tabs-panel{display:block;border-width:0;padding:1em 1.4em;background:0}.ui-tabs .ui-tabs-hide{display:none!important}/*! - * jQuery UI Datepicker 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Datepicker#theming - */.ui-datepicker{width:17em;padding:.2em .2em 0;display:none}.ui-datepicker .ui-datepicker-header{position:relative;padding:.2em 0}.ui-datepicker .ui-datepicker-prev,.ui-datepicker .ui-datepicker-next{position:absolute;top:2px;width:1.8em;height:1.8em}.ui-datepicker .ui-datepicker-prev-hover,.ui-datepicker .ui-datepicker-next-hover{top:1px}.ui-datepicker .ui-datepicker-prev{left:2px}.ui-datepicker .ui-datepicker-next{right:2px}.ui-datepicker .ui-datepicker-prev-hover{left:1px}.ui-datepicker .ui-datepicker-next-hover{right:1px}.ui-datepicker .ui-datepicker-prev span,.ui-datepicker .ui-datepicker-next span{display:block;position:absolute;left:50%;margin-left:-8px;top:50%;margin-top:-8px}.ui-datepicker .ui-datepicker-title{margin:0 2.3em;line-height:1.8em;text-align:center}.ui-datepicker .ui-datepicker-title select{font-size:1em;margin:1px 0}.ui-datepicker select.ui-datepicker-month-year{width:100%}.ui-datepicker select.ui-datepicker-month,.ui-datepicker select.ui-datepicker-year{width:49%}.ui-datepicker table{width:100%;font-size:.9em;border-collapse:collapse;margin:0 0 .4em}.ui-datepicker th{padding:.7em .3em;text-align:center;font-weight:bold;border:0}.ui-datepicker td{border:0;padding:1px}.ui-datepicker td span,.ui-datepicker td a{display:block;padding:.2em;text-align:right;text-decoration:none}.ui-datepicker .ui-datepicker-buttonpane{background-image:none;margin:.7em 0 0 0;padding:0 .2em;border-left:0;border-right:0;border-bottom:0}.ui-datepicker .ui-datepicker-buttonpane button{float:right;margin:.5em .2em .4em;cursor:pointer;padding:.2em .6em .3em .6em;width:auto;overflow:visible}.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current{float:left}.ui-datepicker.ui-datepicker-multi{width:auto}.ui-datepicker-multi .ui-datepicker-group{float:left}.ui-datepicker-multi .ui-datepicker-group table{width:95%;margin:0 auto .4em}.ui-datepicker-multi-2 .ui-datepicker-group{width:50%}.ui-datepicker-multi-3 .ui-datepicker-group{width:33.3%}.ui-datepicker-multi-4 .ui-datepicker-group{width:25%}.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header{border-left-width:0}.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header{border-left-width:0}.ui-datepicker-multi .ui-datepicker-buttonpane{clear:left}.ui-datepicker-row-break{clear:both;width:100%;font-size:0}.ui-datepicker-rtl{direction:rtl}.ui-datepicker-rtl .ui-datepicker-prev{right:2px;left:auto}.ui-datepicker-rtl .ui-datepicker-next{left:2px;right:auto}.ui-datepicker-rtl .ui-datepicker-prev:hover{right:1px;left:auto}.ui-datepicker-rtl .ui-datepicker-next:hover{left:1px;right:auto}.ui-datepicker-rtl .ui-datepicker-buttonpane{clear:right}.ui-datepicker-rtl .ui-datepicker-buttonpane button{float:left}.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current{float:right}.ui-datepicker-rtl .ui-datepicker-group{float:right}.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header{border-right-width:0;border-left-width:1px}.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header{border-right-width:0;border-left-width:1px}.ui-datepicker-cover{position:absolute;z-index:-1;filter:mask();top:-4px;left:-4px;width:200px;height:200px}/*! - * jQuery UI Progressbar 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Progressbar#theming - */.ui-progressbar{height:2em;text-align:left;overflow:hidden}.ui-progressbar .ui-progressbar-value{margin:-1px;height:100%}/*! - * jQuery contextMenu - Plugin for simple contextMenu handling - * - * Version: 1.5.24 - * - * Authors: Rodney Rehm, Addy Osmani (patches for FF) - * Web: http://medialize.github.com/jQuery-contextMenu/ - * - * Licensed under - * MIT License http://www.opensource.org/licenses/mit-license - * GPL v3 http://opensource.org/licenses/GPL-3.0 - * - */.context-menu-list{margin:0;padding:0;min-width:120px;max-width:250px;display:inline-block;position:absolute;list-style-type:none;border:1px solid #DDD;background:#EEE;-webkit-box-shadow:0 2px 5px rgba(0,0,0,0.5);-moz-box-shadow:0 2px 5px rgba(0,0,0,0.5);-ms-box-shadow:0 2px 5px rgba(0,0,0,0.5);-o-box-shadow:0 2px 5px rgba(0,0,0,0.5);box-shadow:0 2px 5px rgba(0,0,0,0.5);font-family:Verdana,Arial,Helvetica,sans-serif;font-size:11px}.context-menu-item{padding:2px 2px 2px 24px;background-color:#EEE;position:relative;-webkit-user-select:none;-moz-user-select:-moz-none;-ms-user-select:none;user-select:none}.context-menu-separator{padding-bottom:0;border-bottom:1px solid #DDD}.context-menu-item>label>input,.context-menu-item>label>textarea{-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}.context-menu-item.hover{cursor:pointer;background-color:#39F}.context-menu-item.disabled{color:#666}.context-menu-input.hover,.context-menu-item.disabled.hover{cursor:default;background-color:#EEE}.context-menu-submenu:after{content:">";color:#666;position:absolute;top:0;right:3px;z-index:1}.context-menu-item.icon{min-height:18px;background-repeat:no-repeat;background-position:4px 2px}.context-menu-item.icon-edit{background-image:url(images/page_white_edit.png)}.context-menu-item.icon-cut{background-image:url(images/cut.png)}.context-menu-item.icon-copy{background-image:url(images/page_white_copy.png)}.context-menu-item.icon-paste{background-image:url(images/page_white_paste.png)}.context-menu-item.icon-delete{background-image:url(images/page_white_delete.png)}.context-menu-item.icon-add{background-image:url(images/page_white_add.png)}.context-menu-item.icon-quit{background-image:url(images/door.png)}.context-menu-input>label>*{vertical-align:top}.context-menu-input>label>input[type="checkbox"],.context-menu-input>label>input[type="radio"]{margin-left:-17px}.context-menu-input>label>span{margin-left:5px}.context-menu-input>label,.context-menu-input>label>input[type="text"],.context-menu-input>label>textarea,.context-menu-input>label>select{display:block;width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;-o-box-sizing:border-box;box-sizing:border-box}.context-menu-input>label>textarea{height:100px}.context-menu-item>.context-menu-list{display:none;right:-5px;top:5px}.context-menu-item.hover>.context-menu-list{display:block}.context-menu-accesskey{text-decoration:underline}#colorbox,#cboxOverlay,#cboxWrapper{position:absolute;top:0;left:0;z-index:9999;overflow:hidden}#cboxOverlay{position:fixed;width:100%;height:100%}#cboxMiddleLeft,#cboxBottomLeft{clear:left}#cboxContent{position:relative}#cboxLoadedContent{overflow:auto}#cboxTitle{margin:0}#cboxLoadingOverlay,#cboxLoadingGraphic{position:absolute;top:0;left:0;width:100%;height:100%}#cboxPrevious,#cboxNext,#cboxClose,#cboxSlideshow{cursor:pointer}.cboxPhoto{float:left;margin:auto;border:0;display:block;max-width:none}.cboxIframe{width:100%;height:100%;display:block;border:0}#colorbox,#cboxContent,#cboxLoadedContent{box-sizing:content-box}#cboxOverlay{background:url(images/overlay.png) repeat 0 0}#cboxTopLeft{width:21px;height:21px;background:url(images/controls.png) no-repeat -101px 0}#cboxTopRight{width:21px;height:21px;background:url(images/controls.png) no-repeat -130px 0}#cboxBottomLeft{width:21px;height:21px;background:url(images/controls.png) no-repeat -101px -29px}#cboxBottomRight{width:21px;height:21px;background:url(images/controls.png) no-repeat -130px -29px}#cboxMiddleLeft{width:21px;background:url(images/controls.png) left top repeat-y}#cboxMiddleRight{width:21px;background:url(images/controls.png) right top repeat-y}#cboxTopCenter{height:21px;background:url(images/border.png) 0 0 repeat-x}#cboxBottomCenter{height:21px;background:url(images/border.png) 0 -29px repeat-x}#cboxContent{background:#fff;overflow:hidden}.cboxIframe{background:#fff}#cboxError{padding:50px;border:1px solid #ccc}#cboxLoadedContent{margin-bottom:28px}#cboxTitle{position:absolute;bottom:4px;left:0;text-align:center;width:100%;color:#949494}#cboxCurrent{position:absolute;bottom:4px;left:58px;color:#949494}#cboxSlideshow{position:absolute;bottom:4px;right:30px;color:#0092ef}#cboxPrevious{position:absolute;bottom:0;left:0;background:url(images/controls.png) no-repeat -75px 0;width:25px;height:25px;text-indent:-9999px}#cboxPrevious:hover{background-position:-75px -25px}#cboxNext{position:absolute;bottom:0;left:27px;background:url(images/controls.png) no-repeat -50px 0;width:25px;height:25px;text-indent:-9999px}#cboxNext:hover{background-position:-50px -25px}#cboxLoadingOverlay{background:url(images/loading_background.png) no-repeat center center}#cboxLoadingGraphic{background:url(images/loading.gif) no-repeat center center}#cboxClose{position:absolute;bottom:0;right:0;background:url(images/controls.png) no-repeat -25px 0;width:25px;height:25px;text-indent:-9999px}#cboxClose:hover{background-position:-25px -25px}.cboxIE #cboxTopLeft,.cboxIE #cboxTopCenter,.cboxIE #cboxTopRight,.cboxIE #cboxBottomLeft,.cboxIE #cboxBottomCenter,.cboxIE #cboxBottomRight,.cboxIE #cboxMiddleLeft,.cboxIE #cboxMiddleRight{filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#00FFFFFF,endColorstr=#00FFFFFF)}.cboxIE6 #cboxTopLeft{background:url(images/ie6/borderTopLeft.png)}.cboxIE6 #cboxTopCenter{background:url(images/ie6/borderTopCenter.png)}.cboxIE6 #cboxTopRight{background:url(images/ie6/borderTopRight.png)}.cboxIE6 #cboxBottomLeft{background:url(images/ie6/borderBottomLeft.png)}.cboxIE6 #cboxBottomCenter{background:url(images/ie6/borderBottomCenter.png)}.cboxIE6 #cboxBottomRight{background:url(images/ie6/borderBottomRight.png)}.cboxIE6 #cboxMiddleLeft{background:url(images/ie6/borderMiddleLeft.png)}.cboxIE6 #cboxMiddleRight{background:url(images/ie6/borderMiddleRight.png)}.cboxIE6 #cboxTopLeft,.cboxIE6 #cboxTopCenter,.cboxIE6 #cboxTopRight,.cboxIE6 #cboxBottomLeft,.cboxIE6 #cboxBottomCenter,.cboxIE6 #cboxBottomRight,.cboxIE6 #cboxMiddleLeft,.cboxIE6 #cboxMiddleRight{_behavior:expression(this.src = this.src ? this.src:this.currentStyle.backgroundImage.split('"')[1],this.style.background = "none",this.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src="+this.src+", sizingMethod='scale')")}.chzn-container{font-size:13px;position:relative;display:inline-block;zoom:1;*display:inline}.chzn-container .chzn-drop{background:#fff;border:1px solid #aaa;border-top:0;position:absolute;top:29px;left:0;-webkit-box-shadow:0 4px 5px rgba(0,0,0,.15);-moz-box-shadow:0 4px 5px rgba(0,0,0,.15);-o-box-shadow:0 4px 5px rgba(0,0,0,.15);box-shadow:0 4px 5px rgba(0,0,0,.15);z-index:1010}.chzn-container-single .chzn-single{background-color:#fff;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff',endColorstr='#eeeeee',GradientType=0);background-image:-webkit-gradient(linear,0% 0,0% 100%,color-stop(20%,#fff),color-stop(50%,#f6f6f6),color-stop(52%,#eee),color-stop(100%,#f4f4f4));background-image:-webkit-linear-gradient(top,#fff 20%,#f6f6f6 50%,#eee 52%,#f4f4f4 100%);background-image:-moz-linear-gradient(top,#fff 20%,#f6f6f6 50%,#eee 52%,#f4f4f4 100%);background-image:-o-linear-gradient(top,#fff 20%,#f6f6f6 50%,#eee 52%,#f4f4f4 100%);background-image:-ms-linear-gradient(top,#fff 20%,#f6f6f6 50%,#eee 52%,#f4f4f4 100%);background-image:linear-gradient(top,#fff 20%,#f6f6f6 50%,#eee 52%,#f4f4f4 100%);-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;-moz-background-clip:padding;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #aaa;-webkit-box-shadow:0 0 3px #fff inset,0 1px 1px rgba(0,0,0,0.1);-moz-box-shadow:0 0 3px #fff inset,0 1px 1px rgba(0,0,0,0.1);box-shadow:0 0 3px #fff inset,0 1px 1px rgba(0,0,0,0.1);display:block;overflow:hidden;white-space:nowrap;position:relative;height:23px;line-height:24px;padding:0 0 0 8px;color:#444;text-decoration:none}.chzn-container-single .chzn-default{color:#999}.chzn-container-single .chzn-single span{margin-right:26px;display:block;overflow:hidden;white-space:nowrap;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis;text-overflow:ellipsis}.chzn-container-single .chzn-single abbr{display:block;position:absolute;right:26px;top:6px;width:12px;height:13px;font-size:1px;background:url('chosen-sprite.png') right top no-repeat}.chzn-container-single .chzn-single abbr:hover{background-position:right -11px}.chzn-container-single.chzn-disabled .chzn-single abbr:hover{background-position:right top}.chzn-container-single .chzn-single div{position:absolute;right:0;top:0;display:block;height:100%;width:18px}.chzn-container-single .chzn-single div b{background:url('chosen-sprite.png') no-repeat 0 0;display:block;width:100%;height:100%}.chzn-container-single .chzn-search{padding:3px 4px;position:relative;margin:0;white-space:nowrap;z-index:1010}.chzn-container-single .chzn-search input{background:#fff url('chosen-sprite.png') no-repeat 100% -22px;background:url('chosen-sprite.png') no-repeat 100% -22px,-webkit-gradient(linear,0% 0,0% 100%,color-stop(1%,#eee),color-stop(15%,#fff));background:url('chosen-sprite.png') no-repeat 100% -22px,-webkit-linear-gradient(top,#eee 1%,#fff 15%);background:url('chosen-sprite.png') no-repeat 100% -22px,-moz-linear-gradient(top,#eee 1%,#fff 15%);background:url('chosen-sprite.png') no-repeat 100% -22px,-o-linear-gradient(top,#eee 1%,#fff 15%);background:url('chosen-sprite.png') no-repeat 100% -22px,-ms-linear-gradient(top,#eee 1%,#fff 15%);background:url('chosen-sprite.png') no-repeat 100% -22px,linear-gradient(top,#eee 1%,#fff 15%);margin:1px 0;padding:4px 20px 4px 5px;outline:0;border:1px solid #aaa;font-family:sans-serif;font-size:1em}.chzn-container-single .chzn-drop{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;-moz-background-clip:padding;-webkit-background-clip:padding-box;background-clip:padding-box}.chzn-container-single-nosearch .chzn-search input{position:absolute;left:-9000px}.chzn-container-multi .chzn-choices{background-color:#fff;background-image:-webkit-gradient(linear,0% 0,0% 100%,color-stop(1%,#eee),color-stop(15%,#fff));background-image:-webkit-linear-gradient(top,#eee 1%,#fff 15%);background-image:-moz-linear-gradient(top,#eee 1%,#fff 15%);background-image:-o-linear-gradient(top,#eee 1%,#fff 15%);background-image:-ms-linear-gradient(top,#eee 1%,#fff 15%);background-image:linear-gradient(top,#eee 1%,#fff 15%);border:1px solid #aaa;margin:0;padding:0;cursor:text;overflow:hidden;height:auto!important;height:1%;position:relative}.chzn-container-multi .chzn-choices li{float:left;list-style:none}.chzn-container-multi .chzn-choices .search-field{white-space:nowrap;margin:0;padding:0}.chzn-container-multi .chzn-choices .search-field input{color:#666;background:transparent!important;border:0!important;font-family:sans-serif;font-size:100%;height:15px;padding:5px;margin:1px 0;outline:0;-webkit-box-shadow:none;-moz-box-shadow:none;-o-box-shadow:none;box-shadow:none}.chzn-container-multi .chzn-choices .search-field .default{color:#999}.chzn-container-multi .chzn-choices .search-choice{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-moz-background-clip:padding;-webkit-background-clip:padding-box;background-clip:padding-box;background-color:#e4e4e4;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#f4f4f4',endColorstr='#eeeeee',GradientType=0);background-image:-webkit-gradient(linear,0% 0,0% 100%,color-stop(20%,#f4f4f4),color-stop(50%,#f0f0f0),color-stop(52%,#e8e8e8),color-stop(100%,#eee));background-image:-webkit-linear-gradient(top,#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);background-image:-moz-linear-gradient(top,#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);background-image:-o-linear-gradient(top,#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);background-image:-ms-linear-gradient(top,#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);background-image:linear-gradient(top,#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);-webkit-box-shadow:0 0 2px #fff inset,0 1px 0 rgba(0,0,0,0.05);-moz-box-shadow:0 0 2px #fff inset,0 1px 0 rgba(0,0,0,0.05);box-shadow:0 0 2px #fff inset,0 1px 0 rgba(0,0,0,0.05);color:#333;border:1px solid #aaa;line-height:13px;padding:3px 20px 3px 5px;margin:3px 0 3px 5px;position:relative;cursor:default}.chzn-container-multi .chzn-choices .search-choice-focus{background:#d4d4d4}.chzn-container-multi .chzn-choices .search-choice .search-choice-close{display:block;position:absolute;right:3px;top:4px;width:12px;height:13px;font-size:1px;background:url('chosen-sprite.png') right top no-repeat}.chzn-container-multi .chzn-choices .search-choice .search-choice-close:hover{background-position:right -11px}.chzn-container-multi .chzn-choices .search-choice-focus .search-choice-close{background-position:right -11px}.chzn-container .chzn-results{margin:0 4px 4px 0;max-height:240px;padding:0 0 0 4px;position:relative;overflow-x:hidden;overflow-y:auto;-webkit-overflow-scrolling:touch}.chzn-container-multi .chzn-results{margin:-1px 0 0;padding:0}.chzn-container .chzn-results li{display:none;line-height:15px;padding:5px 6px;margin:0;list-style:none}.chzn-container .chzn-results .active-result{cursor:pointer;display:list-item}.chzn-container .chzn-results .highlighted{background-color:#3875d7;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#3875d7',endColorstr='#2a62bc',GradientType=0);background-image:-webkit-gradient(linear,0% 0,0% 100%,color-stop(20%,#3875d7),color-stop(90%,#2a62bc));background-image:-webkit-linear-gradient(top,#3875d7 20%,#2a62bc 90%);background-image:-moz-linear-gradient(top,#3875d7 20%,#2a62bc 90%);background-image:-o-linear-gradient(top,#3875d7 20%,#2a62bc 90%);background-image:-ms-linear-gradient(top,#3875d7 20%,#2a62bc 90%);background-image:linear-gradient(top,#3875d7 20%,#2a62bc 90%);color:#fff}.chzn-container .chzn-results li em{background:#feffde;font-style:normal}.chzn-container .chzn-results .highlighted em{background:transparent}.chzn-container .chzn-results .no-results{background:#f4f4f4;display:list-item}.chzn-container .chzn-results .group-result{cursor:default;color:#999;font-weight:bold}.chzn-container .chzn-results .group-option{padding-left:15px}.chzn-container-multi .chzn-drop .result-selected{display:none}.chzn-container .chzn-results-scroll{background:white;margin:0 4px;position:absolute;text-align:center;width:321px;z-index:1}.chzn-container .chzn-results-scroll span{display:inline-block;height:17px;text-indent:-5000px;width:9px}.chzn-container .chzn-results-scroll-down{bottom:0}.chzn-container .chzn-results-scroll-down span{background:url('chosen-sprite.png') no-repeat -4px -3px}.chzn-container .chzn-results-scroll-up span{background:url('chosen-sprite.png') no-repeat -22px -3px}.chzn-container-active .chzn-single{-webkit-box-shadow:0 0 5px rgba(0,0,0,.3);-moz-box-shadow:0 0 5px rgba(0,0,0,.3);-o-box-shadow:0 0 5px rgba(0,0,0,.3);box-shadow:0 0 5px rgba(0,0,0,.3);border:1px solid #5897fb}.chzn-container-active .chzn-single-with-drop{border:1px solid #aaa;-webkit-box-shadow:0 1px 0 #fff inset;-moz-box-shadow:0 1px 0 #fff inset;-o-box-shadow:0 1px 0 #fff inset;box-shadow:0 1px 0 #fff inset;background-color:#eee;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee',endColorstr='#ffffff',GradientType=0);background-image:-webkit-gradient(linear,0% 0,0% 100%,color-stop(20%,#eee),color-stop(80%,#fff));background-image:-webkit-linear-gradient(top,#eee 20%,#fff 80%);background-image:-moz-linear-gradient(top,#eee 20%,#fff 80%);background-image:-o-linear-gradient(top,#eee 20%,#fff 80%);background-image:-ms-linear-gradient(top,#eee 20%,#fff 80%);background-image:linear-gradient(top,#eee 20%,#fff 80%);-webkit-border-bottom-left-radius:0;-webkit-border-bottom-right-radius:0;-moz-border-radius-bottomleft:0;-moz-border-radius-bottomright:0;border-bottom-left-radius:0;border-bottom-right-radius:0}.chzn-container-active .chzn-single-with-drop div{background:transparent;border-left:none}.chzn-container-active .chzn-single-with-drop div b{background-position:-18px 1px}.chzn-container-active .chzn-choices{-webkit-box-shadow:0 0 5px rgba(0,0,0,.3);-moz-box-shadow:0 0 5px rgba(0,0,0,.3);-o-box-shadow:0 0 5px rgba(0,0,0,.3);box-shadow:0 0 5px rgba(0,0,0,.3);border:1px solid #5897fb}.chzn-container-active .chzn-choices .search-field input{color:#111!important}.chzn-disabled{cursor:default;opacity:.5!important}.chzn-disabled .chzn-single{cursor:default}.chzn-disabled .chzn-choices .search-choice .search-choice-close{cursor:default}.chzn-rtl{text-align:right}.chzn-rtl .chzn-single{padding:0 8px 0 0;overflow:visible}.chzn-rtl .chzn-single span{margin-left:26px;margin-right:0;direction:rtl}.chzn-rtl .chzn-single div{left:3px;right:auto}.chzn-rtl .chzn-single abbr{left:26px;right:auto}.chzn-rtl .chzn-choices .search-field input{direction:rtl}.chzn-rtl .chzn-choices li{float:right}.chzn-rtl .chzn-choices .search-choice{padding:3px 5px 3px 19px;margin:3px 5px 3px 0}.chzn-rtl .chzn-choices .search-choice .search-choice-close{left:4px;right:auto;background-position:right top}.chzn-rtl.chzn-container-single .chzn-results{margin:0 0 4px 4px;padding:0 4px 0 0}.chzn-rtl .chzn-results .group-option{padding-left:0;padding-right:15px}.chzn-rtl.chzn-container-active .chzn-single-with-drop div{border-right:0}.chzn-rtl .chzn-search input{background:#fff url('chosen-sprite.png') no-repeat -38px -22px;background:url('chosen-sprite.png') no-repeat -38px -22px,-webkit-gradient(linear,0% 0,0% 100%,color-stop(1%,#eee),color-stop(15%,#fff));background:url('chosen-sprite.png') no-repeat -38px -22px,-webkit-linear-gradient(top,#eee 1%,#fff 15%);background:url('chosen-sprite.png') no-repeat -38px -22px,-moz-linear-gradient(top,#eee 1%,#fff 15%);background:url('chosen-sprite.png') no-repeat -38px -22px,-o-linear-gradient(top,#eee 1%,#fff 15%);background:url('chosen-sprite.png') no-repeat -38px -22px,-ms-linear-gradient(top,#eee 1%,#fff 15%);background:url('chosen-sprite.png') no-repeat -38px -22px,linear-gradient(top,#eee 1%,#fff 15%);padding:4px 5px 4px 20px;direction:rtl}/*! - * Bootstrap v2.2.2 - * - * Copyright 2012 Twitter, Inc - * Licensed under the Apache License v2.0 - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Designed and built with all the love in the world @twitter by @mdo and @fat. - */article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}a:hover,a:active{outline:0}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{width:auto\9;height:auto;max-width:100%;vertical-align:middle;border:0;-ms-interpolation-mode:bicubic}#map_canvas img,.google-maps img{max-width:none}button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle}button,input{*overflow:visible;line-height:normal}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}button,html input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button}label,select,button,input[type="button"],input[type="reset"],input[type="submit"],input[type="radio"],input[type="checkbox"]{cursor:pointer}input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}textarea{overflow:auto;vertical-align:top}@media print{*{color:#000!important;text-shadow:none!important;background:transparent!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100%!important}@page{margin:.5cm}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:20px;color:#333;background-color:#fff}a{color:#08c;text-decoration:none}a:hover{color:#005580;text-decoration:underline}.img-rounded{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.img-polaroid{padding:4px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.1);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.1);box-shadow:0 1px 3px rgba(0,0,0,0.1)}.img-circle{-webkit-border-radius:500px;-moz-border-radius:500px;border-radius:500px}.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.span12{width:940px}.span11{width:860px}.span10{width:780px}.span9{width:700px}.span8{width:620px}.span7{width:540px}.span6{width:460px}.span5{width:380px}.span4{width:300px}.span3{width:220px}.span2{width:140px}.span1{width:60px}.offset12{margin-left:980px}.offset11{margin-left:900px}.offset10{margin-left:820px}.offset9{margin-left:740px}.offset8{margin-left:660px}.offset7{margin-left:580px}.offset6{margin-left:500px}.offset5{margin-left:420px}.offset4{margin-left:340px}.offset3{margin-left:260px}.offset2{margin-left:180px}.offset1{margin-left:100px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.127659574468085%;*margin-left:2.074468085106383%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.127659574468085%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.48936170212765%;*width:91.43617021276594%}.row-fluid .span10{width:82.97872340425532%;*width:82.92553191489361%}.row-fluid .span9{width:74.46808510638297%;*width:74.41489361702126%}.row-fluid .span8{width:65.95744680851064%;*width:65.90425531914893%}.row-fluid .span7{width:57.44680851063829%;*width:57.39361702127659%}.row-fluid .span6{width:48.93617021276595%;*width:48.88297872340425%}.row-fluid .span5{width:40.42553191489362%;*width:40.37234042553192%}.row-fluid .span4{width:31.914893617021278%;*width:31.861702127659576%}.row-fluid .span3{width:23.404255319148934%;*width:23.351063829787233%}.row-fluid .span2{width:14.893617021276595%;*width:14.840425531914894%}.row-fluid .span1{width:6.382978723404255%;*width:6.329787234042553%}.row-fluid .offset12{margin-left:104.25531914893617%;*margin-left:104.14893617021275%}.row-fluid .offset12:first-child{margin-left:102.12765957446808%;*margin-left:102.02127659574467%}.row-fluid .offset11{margin-left:95.74468085106382%;*margin-left:95.6382978723404%}.row-fluid .offset11:first-child{margin-left:93.61702127659574%;*margin-left:93.51063829787232%}.row-fluid .offset10{margin-left:87.23404255319149%;*margin-left:87.12765957446807%}.row-fluid .offset10:first-child{margin-left:85.1063829787234%;*margin-left:84.99999999999999%}.row-fluid .offset9{margin-left:78.72340425531914%;*margin-left:78.61702127659572%}.row-fluid .offset9:first-child{margin-left:76.59574468085106%;*margin-left:76.48936170212764%}.row-fluid .offset8{margin-left:70.2127659574468%;*margin-left:70.10638297872339%}.row-fluid .offset8:first-child{margin-left:68.08510638297872%;*margin-left:67.9787234042553%}.row-fluid .offset7{margin-left:61.70212765957446%;*margin-left:61.59574468085106%}.row-fluid .offset7:first-child{margin-left:59.574468085106375%;*margin-left:59.46808510638297%}.row-fluid .offset6{margin-left:53.191489361702125%;*margin-left:53.085106382978715%}.row-fluid .offset6:first-child{margin-left:51.063829787234035%;*margin-left:50.95744680851063%}.row-fluid .offset5{margin-left:44.68085106382979%;*margin-left:44.57446808510638%}.row-fluid .offset5:first-child{margin-left:42.5531914893617%;*margin-left:42.4468085106383%}.row-fluid .offset4{margin-left:36.170212765957444%;*margin-left:36.06382978723405%}.row-fluid .offset4:first-child{margin-left:34.04255319148936%;*margin-left:33.93617021276596%}.row-fluid .offset3{margin-left:27.659574468085104%;*margin-left:27.5531914893617%}.row-fluid .offset3:first-child{margin-left:25.53191489361702%;*margin-left:25.425531914893618%}.row-fluid .offset2{margin-left:19.148936170212764%;*margin-left:19.04255319148936%}.row-fluid .offset2:first-child{margin-left:17.02127659574468%;*margin-left:16.914893617021278%}.row-fluid .offset1{margin-left:10.638297872340425%;*margin-left:10.53191489361702%}.row-fluid .offset1:first-child{margin-left:8.51063829787234%;*margin-left:8.404255319148938%}[class*="span"].hide,.row-fluid [class*="span"].hide{display:none}[class*="span"].pull-right,.row-fluid [class*="span"].pull-right{float:right}.container{margin-right:auto;margin-left:auto;*zoom:1}.container:before,.container:after{display:table;line-height:0;content:""}.container:after{clear:both}.container-fluid{padding-right:20px;padding-left:20px;*zoom:1}.container-fluid:before,.container-fluid:after{display:table;line-height:0;content:""}.container-fluid:after{clear:both}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:21px;font-weight:200;line-height:30px}small{font-size:85%}strong{font-weight:bold}em{font-style:italic}cite{font-style:normal}.muted{color:#999}a.muted:hover{color:#808080}.text-warning{color:#c09853}a.text-warning:hover{color:#a47e3c}.text-error{color:#b94a48}a.text-error:hover{color:#953b39}.text-info{color:#3a87ad}a.text-info:hover{color:#2d6987}.text-success{color:#468847}a.text-success:hover{color:#356635}h1,h2,h3,h4,h5,h6{margin:10px 0;font-family:inherit;font-weight:bold;line-height:20px;color:inherit;text-rendering:optimizelegibility}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;line-height:1;color:#999}h1,h2,h3{line-height:40px}h1{font-size:38.5px}h2{font-size:31.5px}h3{font-size:24.5px}h4{font-size:17.5px}h5{font-size:14px}h6{font-size:11.9px}h1 small{font-size:24.5px}h2 small{font-size:17.5px}h3 small{font-size:14px}h4 small{font-size:14px}.page-header{padding-bottom:9px;margin:20px 0 30px;border-bottom:1px solid #eee}ul,ol{padding:0;margin:0 0 10px 25px}ul ul,ul ol,ol ol,ol ul{margin-bottom:0}li{line-height:20px}ul.unstyled,ol.unstyled{margin-left:0;list-style:none}ul.inline,ol.inline{margin-left:0;list-style:none}ul.inline>li,ol.inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-bottom:20px}dt,dd{line-height:20px}dt{font-weight:bold}dd{margin-left:10px}.dl-horizontal{*zoom:1}.dl-horizontal:before,.dl-horizontal:after{display:table;line-height:0;content:""}.dl-horizontal:after{clear:both}.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}hr{margin:20px 0;border:0;border-top:1px solid #eee;border-bottom:1px solid #fff}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #999}abbr.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:0 0 0 15px;margin:0 0 20px;border-left:5px solid #eee}blockquote p{margin-bottom:0;font-size:16px;font-weight:300;line-height:25px}blockquote small{display:block;line-height:20px;color:#999}blockquote small:before{content:'\2014 \00A0'}blockquote.pull-right{float:right;padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0}blockquote.pull-right p,blockquote.pull-right small{text-align:right}blockquote.pull-right small:before{content:''}blockquote.pull-right small:after{content:'\00A0 \2014'}q:before,q:after,blockquote:before,blockquote:after{content:""}address{display:block;margin-bottom:20px;font-style:normal;line-height:20px}code,pre{padding:0 3px 2px;font-family:Monaco,Menlo,Consolas,"Courier New",monospace;font-size:12px;color:#333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}code{padding:2px 4px;color:#d14;white-space:nowrap;background-color:#f7f7f9;border:1px solid #e1e1e8}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:20px;word-break:break-all;word-wrap:break-word;white-space:pre;white-space:pre-wrap;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}pre.prettyprint{margin-bottom:20px}pre code{padding:0;color:inherit;white-space:pre;white-space:pre-wrap;background-color:transparent;border:0}.pre-scrollable{max-height:340px;overflow-y:scroll}form{margin:0 0 20px}fieldset{padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:40px;color:#333;border:0;border-bottom:1px solid #e5e5e5}legend small{font-size:15px;color:#999}label,input,button,select,textarea{font-size:14px;font-weight:normal;line-height:20px}input,button,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif}label{display:block;margin-bottom:5px}select,textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{display:inline-block;height:20px;padding:4px 6px;margin-bottom:10px;font-size:14px;line-height:20px;color:#555;vertical-align:middle;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}input,textarea,.uneditable-input{width:206px}textarea{height:auto}textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{background-color:#fff;border:1px solid #ccc;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border linear .2s,box-shadow linear .2s;-moz-transition:border linear .2s,box-shadow linear .2s;-o-transition:border linear .2s,box-shadow linear .2s;transition:border linear .2s,box-shadow linear .2s}textarea:focus,input[type="text"]:focus,input[type="password"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus,.uneditable-input:focus{border-color:rgba(82,168,236,0.8);outline:0;outline:thin dotted \9;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6)}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;*margin-top:0;line-height:normal}input[type="file"],input[type="image"],input[type="submit"],input[type="reset"],input[type="button"],input[type="radio"],input[type="checkbox"]{width:auto}select,input[type="file"]{height:30px;*margin-top:4px;line-height:30px}select{width:220px;background-color:#fff;border:1px solid #ccc}select[multiple],select[size]{height:auto}select:focus,input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.uneditable-input,.uneditable-textarea{color:#999;cursor:not-allowed;background-color:#fcfcfc;border-color:#ccc;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);box-shadow:inset 0 1px 2px rgba(0,0,0,0.025)}.uneditable-input{overflow:hidden;white-space:nowrap}.uneditable-textarea{width:auto;height:auto}input:-moz-placeholder,textarea:-moz-placeholder{color:#999}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#999}input::-webkit-input-placeholder,textarea::-webkit-input-placeholder{color:#999}.radio,.checkbox{min-height:20px;padding-left:20px}.radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-left:-20px}.controls>.radio:first-child,.controls>.checkbox:first-child{padding-top:5px}.radio.inline,.checkbox.inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle}.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px}.input-mini{width:60px}.input-small{width:90px}.input-medium{width:150px}.input-large{width:210px}.input-xlarge{width:270px}.input-xxlarge{width:530px}input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"]{float:none;margin-left:0}.input-append input[class*="span"],.input-append .uneditable-input[class*="span"],.input-prepend input[class*="span"],.input-prepend .uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"],.row-fluid .input-prepend [class*="span"],.row-fluid .input-append [class*="span"]{display:inline-block}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:926px}input.span11,textarea.span11,.uneditable-input.span11{width:846px}input.span10,textarea.span10,.uneditable-input.span10{width:766px}input.span9,textarea.span9,.uneditable-input.span9{width:686px}input.span8,textarea.span8,.uneditable-input.span8{width:606px}input.span7,textarea.span7,.uneditable-input.span7{width:526px}input.span6,textarea.span6,.uneditable-input.span6{width:446px}input.span5,textarea.span5,.uneditable-input.span5{width:366px}input.span4,textarea.span4,.uneditable-input.span4{width:286px}input.span3,textarea.span3,.uneditable-input.span3{width:206px}input.span2,textarea.span2,.uneditable-input.span2{width:126px}input.span1,textarea.span1,.uneditable-input.span1{width:46px}.controls-row{*zoom:1}.controls-row:before,.controls-row:after{display:table;line-height:0;content:""}.controls-row:after{clear:both}.controls-row [class*="span"],.row-fluid .controls-row [class*="span"]{float:left}.controls-row .checkbox[class*="span"],.controls-row .radio[class*="span"]{padding-top:5px}input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#eee}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"][readonly],input[type="checkbox"][readonly]{background-color:transparent}.control-group.warning .control-label,.control-group.warning .help-block,.control-group.warning .help-inline{color:#c09853}.control-group.warning .checkbox,.control-group.warning .radio,.control-group.warning input,.control-group.warning select,.control-group.warning textarea{color:#c09853}.control-group.warning input,.control-group.warning select,.control-group.warning textarea{border-color:#c09853;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.warning input:focus,.control-group.warning select:focus,.control-group.warning textarea:focus{border-color:#a47e3c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e}.control-group.warning .input-prepend .add-on,.control-group.warning .input-append .add-on{color:#c09853;background-color:#fcf8e3;border-color:#c09853}.control-group.error .control-label,.control-group.error .help-block,.control-group.error .help-inline{color:#b94a48}.control-group.error .checkbox,.control-group.error .radio,.control-group.error input,.control-group.error select,.control-group.error textarea{color:#b94a48}.control-group.error input,.control-group.error select,.control-group.error textarea{border-color:#b94a48;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.error input:focus,.control-group.error select:focus,.control-group.error textarea:focus{border-color:#953b39;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392}.control-group.error .input-prepend .add-on,.control-group.error .input-append .add-on{color:#b94a48;background-color:#f2dede;border-color:#b94a48}.control-group.success .control-label,.control-group.success .help-block,.control-group.success .help-inline{color:#468847}.control-group.success .checkbox,.control-group.success .radio,.control-group.success input,.control-group.success select,.control-group.success textarea{color:#468847}.control-group.success input,.control-group.success select,.control-group.success textarea{border-color:#468847;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.success input:focus,.control-group.success select:focus,.control-group.success textarea:focus{border-color:#356635;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b}.control-group.success .input-prepend .add-on,.control-group.success .input-append .add-on{color:#468847;background-color:#dff0d8;border-color:#468847}.control-group.info .control-label,.control-group.info .help-block,.control-group.info .help-inline{color:#3a87ad}.control-group.info .checkbox,.control-group.info .radio,.control-group.info input,.control-group.info select,.control-group.info textarea{color:#3a87ad}.control-group.info input,.control-group.info select,.control-group.info textarea{border-color:#3a87ad;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.info input:focus,.control-group.info select:focus,.control-group.info textarea:focus{border-color:#2d6987;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3}.control-group.info .input-prepend .add-on,.control-group.info .input-append .add-on{color:#3a87ad;background-color:#d9edf7;border-color:#3a87ad}input:focus:invalid,textarea:focus:invalid,select:focus:invalid{color:#b94a48;border-color:#ee5f5b}input:focus:invalid:focus,textarea:focus:invalid:focus,select:focus:invalid:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7}.form-actions{padding:19px 20px 20px;margin-top:20px;margin-bottom:20px;background-color:#f5f5f5;border-top:1px solid #e5e5e5;*zoom:1}.form-actions:before,.form-actions:after{display:table;line-height:0;content:""}.form-actions:after{clear:both}.help-block,.help-inline{color:#595959}.help-block{display:block;margin-bottom:10px}.help-inline{display:inline-block;*display:inline;padding-left:5px;vertical-align:middle;*zoom:1}.input-append,.input-prepend{margin-bottom:5px;font-size:0;white-space:nowrap}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input,.input-append .dropdown-menu,.input-prepend .dropdown-menu{font-size:14px}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input{position:relative;margin-bottom:0;*margin-left:0;vertical-align:top;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-append input:focus,.input-prepend input:focus,.input-append select:focus,.input-prepend select:focus,.input-append .uneditable-input:focus,.input-prepend .uneditable-input:focus{z-index:2}.input-append .add-on,.input-prepend .add-on{display:inline-block;width:auto;height:20px;min-width:16px;padding:4px 5px;font-size:14px;font-weight:normal;line-height:20px;text-align:center;text-shadow:0 1px 0 #fff;background-color:#eee;border:1px solid #ccc}.input-append .add-on,.input-prepend .add-on,.input-append .btn,.input-prepend .btn,.input-append .btn-group>.dropdown-toggle,.input-prepend .btn-group>.dropdown-toggle{vertical-align:top;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-append .active,.input-prepend .active{background-color:#a9dba9;border-color:#46a546}.input-prepend .add-on,.input-prepend .btn{margin-right:-1px}.input-prepend .add-on:first-child,.input-prepend .btn:first-child{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-append input,.input-append select,.input-append .uneditable-input{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-append input+.btn-group .btn:last-child,.input-append select+.btn-group .btn:last-child,.input-append .uneditable-input+.btn-group .btn:last-child{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-append .add-on,.input-append .btn,.input-append .btn-group{margin-left:-1px}.input-append .add-on:last-child,.input-append .btn:last-child,.input-append .btn-group:last-child>.dropdown-toggle{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append input,.input-prepend.input-append select,.input-prepend.input-append .uneditable-input{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-prepend.input-append input+.btn-group .btn,.input-prepend.input-append select+.btn-group .btn,.input-prepend.input-append .uneditable-input+.btn-group .btn{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append .add-on:first-child,.input-prepend.input-append .btn:first-child{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-prepend.input-append .add-on:last-child,.input-prepend.input-append .btn:last-child{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append .btn-group:first-child{margin-left:0}input.search-query{padding-right:14px;padding-right:4px \9;padding-left:14px;padding-left:4px \9;margin-bottom:0;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.form-search .input-append .search-query,.form-search .input-prepend .search-query{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.form-search .input-append .search-query{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px}.form-search .input-append .btn{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0}.form-search .input-prepend .search-query{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0}.form-search .input-prepend .btn{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px}.form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input,.form-search .input-prepend,.form-inline .input-prepend,.form-horizontal .input-prepend,.form-search .input-append,.form-inline .input-append,.form-horizontal .input-append{display:inline-block;*display:inline;margin-bottom:0;vertical-align:middle;*zoom:1}.form-search .hide,.form-inline .hide,.form-horizontal .hide{display:none}.form-search label,.form-inline label,.form-search .btn-group,.form-inline .btn-group{display:inline-block}.form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{margin-bottom:0}.form-search .radio,.form-search .checkbox,.form-inline .radio,.form-inline .checkbox{padding-left:0;margin-bottom:0;vertical-align:middle}.form-search .radio input[type="radio"],.form-search .checkbox input[type="checkbox"],.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:left;margin-right:3px;margin-left:0}.control-group{margin-bottom:10px}legend+.control-group{margin-top:20px;-webkit-margin-top-collapse:separate}.form-horizontal .control-group{margin-bottom:20px;*zoom:1}.form-horizontal .control-group:before,.form-horizontal .control-group:after{display:table;line-height:0;content:""}.form-horizontal .control-group:after{clear:both}.form-horizontal .control-label{float:left;width:160px;padding-top:5px;text-align:right}.form-horizontal .controls{*display:inline-block;*padding-left:20px;margin-left:180px;*margin-left:0}.form-horizontal .controls:first-child{*padding-left:180px}.form-horizontal .help-block{margin-bottom:0}.form-horizontal input+.help-block,.form-horizontal select+.help-block,.form-horizontal textarea+.help-block,.form-horizontal .uneditable-input+.help-block,.form-horizontal .input-prepend+.help-block,.form-horizontal .input-append+.help-block{margin-top:10px}.form-horizontal .form-actions{padding-left:180px}table{max-width:100%;background-color:transparent;border-collapse:collapse;border-spacing:0}.table{width:100%;margin-bottom:20px}.table th,.table td{padding:8px;line-height:20px;text-align:left;vertical-align:top;border-top:1px solid #ddd}.table th{font-weight:bold}.table thead th{vertical-align:bottom}.table caption+thead tr:first-child th,.table caption+thead tr:first-child td,.table colgroup+thead tr:first-child th,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child th,.table thead:first-child tr:first-child td{border-top:0}.table tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed th,.table-condensed td{padding:4px 5px}.table-bordered{border:1px solid #ddd;border-collapse:separate;*border-collapse:collapse;border-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.table-bordered th,.table-bordered td{border-left:1px solid #ddd}.table-bordered caption+thead tr:first-child th,.table-bordered caption+tbody tr:first-child th,.table-bordered caption+tbody tr:first-child td,.table-bordered colgroup+thead tr:first-child th,.table-bordered colgroup+tbody tr:first-child th,.table-bordered colgroup+tbody tr:first-child td,.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0}.table-bordered thead:first-child tr:first-child>th:first-child,.table-bordered tbody:first-child tr:first-child>td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}.table-bordered thead:first-child tr:first-child>th:last-child,.table-bordered tbody:first-child tr:first-child>td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px}.table-bordered thead:last-child tr:last-child>th:first-child,.table-bordered tbody:last-child tr:last-child>td:first-child,.table-bordered tfoot:last-child tr:last-child>td:first-child{-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px}.table-bordered thead:last-child tr:last-child>th:last-child,.table-bordered tbody:last-child tr:last-child>td:last-child,.table-bordered tfoot:last-child tr:last-child>td:last-child{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px}.table-bordered tfoot+tbody:last-child tr:last-child td:first-child{-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;-moz-border-radius-bottomleft:0}.table-bordered tfoot+tbody:last-child tr:last-child td:last-child{-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-bottomright:0}.table-bordered caption+thead tr:first-child th:first-child,.table-bordered caption+tbody tr:first-child td:first-child,.table-bordered colgroup+thead tr:first-child th:first-child,.table-bordered colgroup+tbody tr:first-child td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}.table-bordered caption+thead tr:first-child th:last-child,.table-bordered caption+tbody tr:first-child td:last-child,.table-bordered colgroup+thead tr:first-child th:last-child,.table-bordered colgroup+tbody tr:first-child td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px}.table-striped tbody>tr:nth-child(odd)>td,.table-striped tbody>tr:nth-child(odd)>th{background-color:#f9f9f9}.table-hover tbody tr:hover td,.table-hover tbody tr:hover th{background-color:#f5f5f5}table td[class*="span"],table th[class*="span"],.row-fluid table td[class*="span"],.row-fluid table th[class*="span"]{display:table-cell;float:none;margin-left:0}.table td.span1,.table th.span1{float:none;width:44px;margin-left:0}.table td.span2,.table th.span2{float:none;width:124px;margin-left:0}.table td.span3,.table th.span3{float:none;width:204px;margin-left:0}.table td.span4,.table th.span4{float:none;width:284px;margin-left:0}.table td.span5,.table th.span5{float:none;width:364px;margin-left:0}.table td.span6,.table th.span6{float:none;width:444px;margin-left:0}.table td.span7,.table th.span7{float:none;width:524px;margin-left:0}.table td.span8,.table th.span8{float:none;width:604px;margin-left:0}.table td.span9,.table th.span9{float:none;width:684px;margin-left:0}.table td.span10,.table th.span10{float:none;width:764px;margin-left:0}.table td.span11,.table th.span11{float:none;width:844px;margin-left:0}.table td.span12,.table th.span12{float:none;width:924px;margin-left:0}.table tbody tr.success td{background-color:#dff0d8}.table tbody tr.error td{background-color:#f2dede}.table tbody tr.warning td{background-color:#fcf8e3}.table tbody tr.info td{background-color:#d9edf7}.table-hover tbody tr.success:hover td{background-color:#d0e9c6}.table-hover tbody tr.error:hover td{background-color:#ebcccc}.table-hover tbody tr.warning:hover td{background-color:#faf2cc}.table-hover tbody tr.info:hover td{background-color:#c4e3f3}[class^="icon-"],[class*=" icon-"]{display:inline-block;width:14px;height:14px;margin-top:1px;*margin-right:.3em;line-height:14px;vertical-align:text-top;background-image:url("../img/glyphicons-halflings.png");background-position:14px 14px;background-repeat:no-repeat}.icon-white,.nav-pills>.active>a>[class^="icon-"],.nav-pills>.active>a>[class*=" icon-"],.nav-list>.active>a>[class^="icon-"],.nav-list>.active>a>[class*=" icon-"],.navbar-inverse .nav>.active>a>[class^="icon-"],.navbar-inverse .nav>.active>a>[class*=" icon-"],.dropdown-menu>li>a:hover>[class^="icon-"],.dropdown-menu>li>a:hover>[class*=" icon-"],.dropdown-menu>.active>a>[class^="icon-"],.dropdown-menu>.active>a>[class*=" icon-"],.dropdown-submenu:hover>a>[class^="icon-"],.dropdown-submenu:hover>a>[class*=" icon-"]{background-image:url("../img/glyphicons-halflings-white.png")}.icon-glass{background-position:0 0}.icon-music{background-position:-24px 0}.icon-search{background-position:-48px 0}.icon-envelope{background-position:-72px 0}.icon-heart{background-position:-96px 0}.icon-star{background-position:-120px 0}.icon-star-empty{background-position:-144px 0}.icon-user{background-position:-168px 0}.icon-film{background-position:-192px 0}.icon-th-large{background-position:-216px 0}.icon-th{background-position:-240px 0}.icon-th-list{background-position:-264px 0}.icon-ok{background-position:-288px 0}.icon-remove{background-position:-312px 0}.icon-zoom-in{background-position:-336px 0}.icon-zoom-out{background-position:-360px 0}.icon-off{background-position:-384px 0}.icon-signal{background-position:-408px 0}.icon-cog{background-position:-432px 0}.icon-trash{background-position:-456px 0}.icon-home{background-position:0 -24px}.icon-file{background-position:-24px -24px}.icon-time{background-position:-48px -24px}.icon-road{background-position:-72px -24px}.icon-download-alt{background-position:-96px -24px}.icon-download{background-position:-120px -24px}.icon-upload{background-position:-144px -24px}.icon-inbox{background-position:-168px -24px}.icon-play-circle{background-position:-192px -24px}.icon-repeat{background-position:-216px -24px}.icon-refresh{background-position:-240px -24px}.icon-list-alt{background-position:-264px -24px}.icon-lock{background-position:-287px -24px}.icon-flag{background-position:-312px -24px}.icon-headphones{background-position:-336px -24px}.icon-volume-off{background-position:-360px -24px}.icon-volume-down{background-position:-384px -24px}.icon-volume-up{background-position:-408px -24px}.icon-qrcode{background-position:-432px -24px}.icon-barcode{background-position:-456px -24px}.icon-tag{background-position:0 -48px}.icon-tags{background-position:-25px -48px}.icon-book{background-position:-48px -48px}.icon-bookmark{background-position:-72px -48px}.icon-print{background-position:-96px -48px}.icon-camera{background-position:-120px -48px}.icon-font{background-position:-144px -48px}.icon-bold{background-position:-167px -48px}.icon-italic{background-position:-192px -48px}.icon-text-height{background-position:-216px -48px}.icon-text-width{background-position:-240px -48px}.icon-align-left{background-position:-264px -48px}.icon-align-center{background-position:-288px -48px}.icon-align-right{background-position:-312px -48px}.icon-align-justify{background-position:-336px -48px}.icon-list{background-position:-360px -48px}.icon-indent-left{background-position:-384px -48px}.icon-indent-right{background-position:-408px -48px}.icon-facetime-video{background-position:-432px -48px}.icon-picture{background-position:-456px -48px}.icon-pencil{background-position:0 -72px}.icon-map-marker{background-position:-24px -72px}.icon-adjust{background-position:-48px -72px}.icon-tint{background-position:-72px -72px}.icon-edit{background-position:-96px -72px}.icon-share{background-position:-120px -72px}.icon-check{background-position:-144px -72px}.icon-move{background-position:-168px -72px}.icon-step-backward{background-position:-192px -72px}.icon-fast-backward{background-position:-216px -72px}.icon-backward{background-position:-240px -72px}.icon-play{background-position:-264px -72px}.icon-pause{background-position:-288px -72px}.icon-stop{background-position:-312px -72px}.icon-forward{background-position:-336px -72px}.icon-fast-forward{background-position:-360px -72px}.icon-step-forward{background-position:-384px -72px}.icon-eject{background-position:-408px -72px}.icon-chevron-left{background-position:-432px -72px}.icon-chevron-right{background-position:-456px -72px}.icon-plus-sign{background-position:0 -96px}.icon-minus-sign{background-position:-24px -96px}.icon-remove-sign{background-position:-48px -96px}.icon-ok-sign{background-position:-72px -96px}.icon-question-sign{background-position:-96px -96px}.icon-info-sign{background-position:-120px -96px}.icon-screenshot{background-position:-144px -96px}.icon-remove-circle{background-position:-168px -96px}.icon-ok-circle{background-position:-192px -96px}.icon-ban-circle{background-position:-216px -96px}.icon-arrow-left{background-position:-240px -96px}.icon-arrow-right{background-position:-264px -96px}.icon-arrow-up{background-position:-289px -96px}.icon-arrow-down{background-position:-312px -96px}.icon-share-alt{background-position:-336px -96px}.icon-resize-full{background-position:-360px -96px}.icon-resize-small{background-position:-384px -96px}.icon-plus{background-position:-408px -96px}.icon-minus{background-position:-433px -96px}.icon-asterisk{background-position:-456px -96px}.icon-exclamation-sign{background-position:0 -120px}.icon-gift{background-position:-24px -120px}.icon-leaf{background-position:-48px -120px}.icon-fire{background-position:-72px -120px}.icon-eye-open{background-position:-96px -120px}.icon-eye-close{background-position:-120px -120px}.icon-warning-sign{background-position:-144px -120px}.icon-plane{background-position:-168px -120px}.icon-calendar{background-position:-192px -120px}.icon-random{width:16px;background-position:-216px -120px}.icon-comment{background-position:-240px -120px}.icon-magnet{background-position:-264px -120px}.icon-chevron-up{background-position:-288px -120px}.icon-chevron-down{background-position:-313px -119px}.icon-retweet{background-position:-336px -120px}.icon-shopping-cart{background-position:-360px -120px}.icon-folder-close{background-position:-384px -120px}.icon-folder-open{width:16px;background-position:-408px -120px}.icon-resize-vertical{background-position:-432px -119px}.icon-resize-horizontal{background-position:-456px -118px}.icon-hdd{background-position:0 -144px}.icon-bullhorn{background-position:-24px -144px}.icon-bell{background-position:-48px -144px}.icon-certificate{background-position:-72px -144px}.icon-thumbs-up{background-position:-96px -144px}.icon-thumbs-down{background-position:-120px -144px}.icon-hand-right{background-position:-144px -144px}.icon-hand-left{background-position:-168px -144px}.icon-hand-up{background-position:-192px -144px}.icon-hand-down{background-position:-216px -144px}.icon-circle-arrow-right{background-position:-240px -144px}.icon-circle-arrow-left{background-position:-264px -144px}.icon-circle-arrow-up{background-position:-288px -144px}.icon-circle-arrow-down{background-position:-312px -144px}.icon-globe{background-position:-336px -144px}.icon-wrench{background-position:-360px -144px}.icon-tasks{background-position:-384px -144px}.icon-filter{background-position:-408px -144px}.icon-briefcase{background-position:-432px -144px}.icon-fullscreen{background-position:-456px -144px}.dropup,.dropdown{position:relative}.dropdown-toggle{*margin-bottom:-3px}.dropdown-toggle:active,.open .dropdown-toggle{outline:0}.caret{display:inline-block;width:0;height:0;vertical-align:top;border-top:4px solid #000;border-right:4px solid transparent;border-left:4px solid transparent;content:""}.dropdown .caret{margin-top:8px;margin-left:2px}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);*border-right-width:2px;*border-bottom-width:2px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.dropdown-menu li>a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:20px;color:#333;white-space:nowrap}.dropdown-menu li>a:hover,.dropdown-menu li>a:focus,.dropdown-submenu:hover>a{color:#fff;text-decoration:none;background-color:#0081c2;background-image:-moz-linear-gradient(top,#08c,#0077b3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#0077b3));background-image:-webkit-linear-gradient(top,#08c,#0077b3);background-image:-o-linear-gradient(top,#08c,#0077b3);background-image:linear-gradient(to bottom,#08c,#0077b3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0077b3',GradientType=0)}.dropdown-menu .active>a,.dropdown-menu .active>a:hover{color:#fff;text-decoration:none;background-color:#0081c2;background-image:-moz-linear-gradient(top,#08c,#0077b3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#0077b3));background-image:-webkit-linear-gradient(top,#08c,#0077b3);background-image:-o-linear-gradient(top,#08c,#0077b3);background-image:linear-gradient(to bottom,#08c,#0077b3);background-repeat:repeat-x;outline:0;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0077b3',GradientType=0)}.dropdown-menu .disabled>a,.dropdown-menu .disabled>a:hover{color:#999}.dropdown-menu .disabled>a:hover{text-decoration:none;cursor:default;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open{*z-index:1000}.open>.dropdown-menu{display:block}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid #000;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}.dropdown-submenu{position:relative}.dropdown-submenu>.dropdown-menu{top:0;left:100%;margin-top:-6px;margin-left:-1px;-webkit-border-radius:0 6px 6px 6px;-moz-border-radius:0 6px 6px 6px;border-radius:0 6px 6px 6px}.dropdown-submenu:hover>.dropdown-menu{display:block}.dropup .dropdown-submenu>.dropdown-menu{top:auto;bottom:0;margin-top:0;margin-bottom:-2px;-webkit-border-radius:5px 5px 5px 0;-moz-border-radius:5px 5px 5px 0;border-radius:5px 5px 5px 0}.dropdown-submenu>a:after{display:block;float:right;width:0;height:0;margin-top:5px;margin-right:-10px;border-color:transparent;border-left-color:#ccc;border-style:solid;border-width:5px 0 5px 5px;content:" "}.dropdown-submenu:hover>a:after{border-left-color:#fff}.dropdown-submenu.pull-left{float:none}.dropdown-submenu.pull-left>.dropdown-menu{left:-100%;margin-left:10px;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px}.dropdown .dropdown-menu .nav-header{padding-right:20px;padding-left:20px}.typeahead{z-index:1051;margin-top:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-large{padding:24px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.well-small{padding:9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.fade{opacity:0;-webkit-transition:opacity .15s linear;-moz-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{position:relative;height:0;overflow:hidden;-webkit-transition:height .35s ease;-moz-transition:height .35s ease;-o-transition:height .35s ease;transition:height .35s ease}.collapse.in{height:auto}.close{float:right;font-size:20px;font-weight:bold;line-height:20px;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.close:hover{color:#000;text-decoration:none;cursor:pointer;opacity:.4;filter:alpha(opacity=40)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.btn{display:inline-block;*display:inline;padding:4px 12px;margin-bottom:0;*margin-left:.3em;font-size:14px;line-height:20px;color:#333;text-align:center;text-shadow:0 1px 1px rgba(255,255,255,0.75);vertical-align:middle;cursor:pointer;background-color:#f5f5f5;*background-color:#e6e6e6;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;border:1px solid #bbb;*border:0;border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);border-bottom-color:#a2a2a2;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffe6e6e6',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);*zoom:1;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn:hover,.btn:active,.btn.active,.btn.disabled,.btn[disabled]{color:#333;background-color:#e6e6e6;*background-color:#d9d9d9}.btn:active,.btn.active{background-color:#ccc \9}.btn:first-child{*margin-left:0}.btn:hover{color:#333;text-decoration:none;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn.disabled,.btn[disabled]{cursor:default;background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-large{padding:11px 19px;font-size:17.5px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.btn-large [class^="icon-"],.btn-large [class*=" icon-"]{margin-top:4px}.btn-small{padding:2px 10px;font-size:11.9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-small [class^="icon-"],.btn-small [class*=" icon-"]{margin-top:0}.btn-mini [class^="icon-"],.btn-mini [class*=" icon-"]{margin-top:-1px}.btn-mini{padding:0 6px;font-size:10.5px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-block{display:block;width:100%;padding-right:0;padding-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active,.btn-inverse.active{color:rgba(255,255,255,0.75)}.btn{border-color:#c5c5c5;border-color:rgba(0,0,0,0.15) rgba(0,0,0,0.15) rgba(0,0,0,0.25)}.btn-primary{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#006dcc;*background-color:#04c;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;border-color:#04c #04c #002a80;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0044cc',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-primary:hover,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{color:#fff;background-color:#04c;*background-color:#003bb3}.btn-primary:active,.btn-primary.active{background-color:#039 \9}.btn-warning{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#faa732;*background-color:#f89406;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-repeat:repeat-x;border-color:#f89406 #f89406 #ad6704;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450',endColorstr='#fff89406',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-warning:hover,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{color:#fff;background-color:#f89406;*background-color:#df8505}.btn-warning:active,.btn-warning.active{background-color:#c67605 \9}.btn-danger{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#da4f49;*background-color:#bd362f;background-image:-moz-linear-gradient(top,#ee5f5b,#bd362f);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#bd362f));background-image:-webkit-linear-gradient(top,#ee5f5b,#bd362f);background-image:-o-linear-gradient(top,#ee5f5b,#bd362f);background-image:linear-gradient(to bottom,#ee5f5b,#bd362f);background-repeat:repeat-x;border-color:#bd362f #bd362f #802420;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b',endColorstr='#ffbd362f',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-danger:hover,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{color:#fff;background-color:#bd362f;*background-color:#a9302a}.btn-danger:active,.btn-danger.active{background-color:#942a25 \9}.btn-success{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#5bb75b;*background-color:#51a351;background-image:-moz-linear-gradient(top,#62c462,#51a351);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#51a351));background-image:-webkit-linear-gradient(top,#62c462,#51a351);background-image:-o-linear-gradient(top,#62c462,#51a351);background-image:linear-gradient(to bottom,#62c462,#51a351);background-repeat:repeat-x;border-color:#51a351 #51a351 #387038;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462',endColorstr='#ff51a351',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-success:hover,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{color:#fff;background-color:#51a351;*background-color:#499249}.btn-success:active,.btn-success.active{background-color:#408140 \9}.btn-info{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#49afcd;*background-color:#2f96b4;background-image:-moz-linear-gradient(top,#5bc0de,#2f96b4);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#2f96b4));background-image:-webkit-linear-gradient(top,#5bc0de,#2f96b4);background-image:-o-linear-gradient(top,#5bc0de,#2f96b4);background-image:linear-gradient(to bottom,#5bc0de,#2f96b4);background-repeat:repeat-x;border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff2f96b4',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-info:hover,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{color:#fff;background-color:#2f96b4;*background-color:#2a85a0}.btn-info:active,.btn-info.active{background-color:#24748c \9}.btn-inverse{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#363636;*background-color:#222;background-image:-moz-linear-gradient(top,#444,#222);background-image:-webkit-gradient(linear,0 0,0 100%,from(#444),to(#222));background-image:-webkit-linear-gradient(top,#444,#222);background-image:-o-linear-gradient(top,#444,#222);background-image:linear-gradient(to bottom,#444,#222);background-repeat:repeat-x;border-color:#222 #222 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff444444',endColorstr='#ff222222',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-inverse:hover,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{color:#fff;background-color:#222;*background-color:#151515}.btn-inverse:active,.btn-inverse.active{background-color:#080808 \9}button.btn,input[type="submit"].btn{*padding-top:3px;*padding-bottom:3px}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0}button.btn.btn-large,input[type="submit"].btn.btn-large{*padding-top:7px;*padding-bottom:7px}button.btn.btn-small,input[type="submit"].btn.btn-small{*padding-top:3px;*padding-bottom:3px}button.btn.btn-mini,input[type="submit"].btn.btn-mini{*padding-top:1px;*padding-bottom:1px}.btn-link,.btn-link:active,.btn-link[disabled]{background-color:transparent;background-image:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-link{color:#08c;cursor:pointer;border-color:transparent;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-link:hover{color:#005580;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover{color:#333;text-decoration:none}.btn-group{position:relative;display:inline-block;*display:inline;*margin-left:.3em;font-size:0;white-space:nowrap;vertical-align:middle;*zoom:1}.btn-group:first-child{*margin-left:0}.btn-group+.btn-group{margin-left:5px}.btn-toolbar{margin-top:10px;margin-bottom:10px;font-size:0}.btn-toolbar>.btn+.btn,.btn-toolbar>.btn-group+.btn,.btn-toolbar>.btn+.btn-group{margin-left:5px}.btn-group>.btn{position:relative;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-group>.btn+.btn{margin-left:-1px}.btn-group>.btn,.btn-group>.dropdown-menu,.btn-group>.popover{font-size:14px}.btn-group>.btn-mini{font-size:10.5px}.btn-group>.btn-small{font-size:11.9px}.btn-group>.btn-large{font-size:17.5px}.btn-group>.btn:first-child{margin-left:0;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:4px}.btn-group>.btn:last-child,.btn-group>.dropdown-toggle{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:4px}.btn-group>.btn.large:first-child{margin-left:0;-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topleft:6px}.btn-group>.btn.large:last-child,.btn-group>.large.dropdown-toggle{-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px}.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active{z-index:2}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{*padding-top:5px;padding-right:8px;*padding-bottom:5px;padding-left:8px;-webkit-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn-group>.btn-mini+.dropdown-toggle{*padding-top:2px;padding-right:5px;*padding-bottom:2px;padding-left:5px}.btn-group>.btn-small+.dropdown-toggle{*padding-top:5px;*padding-bottom:4px}.btn-group>.btn-large+.dropdown-toggle{*padding-top:7px;padding-right:12px;*padding-bottom:7px;padding-left:12px}.btn-group.open .dropdown-toggle{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn-group.open .btn.dropdown-toggle{background-color:#e6e6e6}.btn-group.open .btn-primary.dropdown-toggle{background-color:#04c}.btn-group.open .btn-warning.dropdown-toggle{background-color:#f89406}.btn-group.open .btn-danger.dropdown-toggle{background-color:#bd362f}.btn-group.open .btn-success.dropdown-toggle{background-color:#51a351}.btn-group.open .btn-info.dropdown-toggle{background-color:#2f96b4}.btn-group.open .btn-inverse.dropdown-toggle{background-color:#222}.btn .caret{margin-top:8px;margin-left:0}.btn-mini .caret,.btn-small .caret,.btn-large .caret{margin-top:6px}.btn-large .caret{border-top-width:5px;border-right-width:5px;border-left-width:5px}.dropup .btn-large .caret{border-bottom-width:5px}.btn-primary .caret,.btn-warning .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret,.btn-inverse .caret{border-top-color:#fff;border-bottom-color:#fff}.btn-group-vertical{display:inline-block;*display:inline;*zoom:1}.btn-group-vertical>.btn{display:block;float:none;max-width:100%;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-group-vertical>.btn+.btn{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:first-child{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.btn-group-vertical>.btn:last-child{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.btn-group-vertical>.btn-large:first-child{-webkit-border-radius:6px 6px 0 0;-moz-border-radius:6px 6px 0 0;border-radius:6px 6px 0 0}.btn-group-vertical>.btn-large:last-child{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}.alert{padding:8px 35px 8px 14px;margin-bottom:20px;text-shadow:0 1px 0 rgba(255,255,255,0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.alert,.alert h4{color:#c09853}.alert h4{margin:0}.alert .close{position:relative;top:-2px;right:-21px;line-height:20px}.alert-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.alert-success h4{color:#468847}.alert-danger,.alert-error{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.alert-danger h4,.alert-error h4{color:#b94a48}.alert-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.alert-info h4{color:#3a87ad}.alert-block{padding-top:14px;padding-bottom:14px}.alert-block>p,.alert-block>ul{margin-bottom:0}.alert-block p+p{margin-top:5px}.nav{margin-bottom:20px;margin-left:0;list-style:none}.nav>li>a{display:block}.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li>a>img{max-width:none}.nav>.pull-right{float:right}.nav-header{display:block;padding:3px 15px;font-size:11px;font-weight:bold;line-height:20px;color:#999;text-shadow:0 1px 0 rgba(255,255,255,0.5);text-transform:uppercase}.nav li+.nav-header{margin-top:9px}.nav-list{padding-right:15px;padding-left:15px;margin-bottom:0}.nav-list>li>a,.nav-list .nav-header{margin-right:-15px;margin-left:-15px;text-shadow:0 1px 0 rgba(255,255,255,0.5)}.nav-list>li>a{padding:3px 15px}.nav-list>.active>a,.nav-list>.active>a:hover{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.2);background-color:#08c}.nav-list [class^="icon-"],.nav-list [class*=" icon-"]{margin-right:2px}.nav-list .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.nav-tabs,.nav-pills{*zoom:1}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;line-height:0;content:""}.nav-tabs:after,.nav-pills:after{clear:both}.nav-tabs>li,.nav-pills>li{float:left}.nav-tabs>li>a,.nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{margin-bottom:-1px}.nav-tabs>li>a{padding-top:8px;padding-bottom:8px;line-height:20px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>.active>a,.nav-tabs>.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.nav-pills>.active>a,.nav-pills>.active>a:hover{color:#fff;background-color:#08c}.nav-stacked>li{float:none}.nav-stacked>li>a{margin-right:0}.nav-tabs.nav-stacked{border-bottom:0}.nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.nav-tabs.nav-stacked>li:first-child>a{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-topleft:4px}.nav-tabs.nav-stacked>li:last-child>a{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomright:4px;-moz-border-radius-bottomleft:4px}.nav-tabs.nav-stacked>li>a:hover{z-index:2;border-color:#ddd}.nav-pills.nav-stacked>li>a{margin-bottom:3px}.nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px}.nav-tabs .dropdown-menu{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}.nav-pills .dropdown-menu{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.nav .dropdown-toggle .caret{margin-top:6px;border-top-color:#08c;border-bottom-color:#08c}.nav .dropdown-toggle:hover .caret{border-top-color:#005580;border-bottom-color:#005580}.nav-tabs .dropdown-toggle .caret{margin-top:8px}.nav .active .dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff}.nav-tabs .active .dropdown-toggle .caret{border-top-color:#555;border-bottom-color:#555}.nav>.dropdown.active>a:hover{cursor:pointer}.nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>li.dropdown.open.active>a:hover{color:#fff;background-color:#999;border-color:#999}.nav li.dropdown.open .caret,.nav li.dropdown.open.active .caret,.nav li.dropdown.open a:hover .caret{border-top-color:#fff;border-bottom-color:#fff;opacity:1;filter:alpha(opacity=100)}.tabs-stacked .open>a:hover{border-color:#999}.tabbable{*zoom:1}.tabbable:before,.tabbable:after{display:table;line-height:0;content:""}.tabbable:after{clear:both}.tab-content{overflow:auto}.tabs-below>.nav-tabs,.tabs-right>.nav-tabs,.tabs-left>.nav-tabs{border-bottom:0}.tab-content>.tab-pane,.pill-content>.pill-pane{display:none}.tab-content>.active,.pill-content>.active{display:block}.tabs-below>.nav-tabs{border-top:1px solid #ddd}.tabs-below>.nav-tabs>li{margin-top:-1px;margin-bottom:0}.tabs-below>.nav-tabs>li>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.tabs-below>.nav-tabs>li>a:hover{border-top-color:#ddd;border-bottom-color:transparent}.tabs-below>.nav-tabs>.active>a,.tabs-below>.nav-tabs>.active>a:hover{border-color:transparent #ddd #ddd #ddd}.tabs-left>.nav-tabs>li,.tabs-right>.nav-tabs>li{float:none}.tabs-left>.nav-tabs>li>a,.tabs-right>.nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px}.tabs-left>.nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd}.tabs-left>.nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.tabs-left>.nav-tabs>li>a:hover{border-color:#eee #ddd #eee #eee}.tabs-left>.nav-tabs .active>a,.tabs-left>.nav-tabs .active>a:hover{border-color:#ddd transparent #ddd #ddd;*border-right-color:#fff}.tabs-right>.nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd}.tabs-right>.nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.tabs-right>.nav-tabs>li>a:hover{border-color:#eee #eee #eee #ddd}.tabs-right>.nav-tabs .active>a,.tabs-right>.nav-tabs .active>a:hover{border-color:#ddd #ddd #ddd transparent;*border-left-color:#fff}.nav>.disabled>a{color:#999}.nav>.disabled>a:hover{text-decoration:none;cursor:default;background-color:transparent}.navbar{*position:relative;*z-index:2;margin-bottom:20px;overflow:visible}.navbar-inner{min-height:40px;padding-right:20px;padding-left:20px;background-color:#fafafa;background-image:-moz-linear-gradient(top,#fff,#f2f2f2);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#f2f2f2));background-image:-webkit-linear-gradient(top,#fff,#f2f2f2);background-image:-o-linear-gradient(top,#fff,#f2f2f2);background-image:linear-gradient(to bottom,#fff,#f2f2f2);background-repeat:repeat-x;border:1px solid #d4d4d4;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#fff2f2f2',GradientType=0);*zoom:1;-webkit-box-shadow:0 1px 4px rgba(0,0,0,0.065);-moz-box-shadow:0 1px 4px rgba(0,0,0,0.065);box-shadow:0 1px 4px rgba(0,0,0,0.065)}.navbar-inner:before,.navbar-inner:after{display:table;line-height:0;content:""}.navbar-inner:after{clear:both}.navbar .container{width:auto}.nav-collapse.collapse{height:auto;overflow:visible}.navbar .brand{display:block;float:left;padding:10px 20px 10px;margin-left:-20px;font-size:20px;font-weight:200;color:#777;text-shadow:0 1px 0 #fff}.navbar .brand:hover{text-decoration:none}.navbar-text{margin-bottom:0;line-height:40px;color:#777}.navbar-link{color:#777}.navbar-link:hover{color:#333}.navbar .divider-vertical{height:40px;margin:0 9px;border-right:1px solid #fff;border-left:1px solid #f2f2f2}.navbar .btn,.navbar .btn-group{margin-top:5px}.navbar .btn-group .btn,.navbar .input-prepend .btn,.navbar .input-append .btn{margin-top:0}.navbar-form{margin-bottom:0;*zoom:1}.navbar-form:before,.navbar-form:after{display:table;line-height:0;content:""}.navbar-form:after{clear:both}.navbar-form input,.navbar-form select,.navbar-form .radio,.navbar-form .checkbox{margin-top:5px}.navbar-form input,.navbar-form select,.navbar-form .btn{display:inline-block;margin-bottom:0}.navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px}.navbar-form .input-append,.navbar-form .input-prepend{margin-top:5px;white-space:nowrap}.navbar-form .input-append input,.navbar-form .input-prepend input{margin-top:0}.navbar-search{position:relative;float:left;margin-top:5px;margin-bottom:0}.navbar-search .search-query{padding:4px 14px;margin-bottom:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.navbar-static-top{position:static;margin-bottom:0}.navbar-static-top .navbar-inner{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;margin-bottom:0}.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{border-width:0 0 1px}.navbar-fixed-bottom .navbar-inner{border-width:1px 0 0}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding-right:0;padding-left:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.navbar-fixed-top{top:0}.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{-webkit-box-shadow:0 1px 10px rgba(0,0,0,0.1);-moz-box-shadow:0 1px 10px rgba(0,0,0,0.1);box-shadow:0 1px 10px rgba(0,0,0,0.1)}.navbar-fixed-bottom{bottom:0}.navbar-fixed-bottom .navbar-inner{-webkit-box-shadow:0 -1px 10px rgba(0,0,0,0.1);-moz-box-shadow:0 -1px 10px rgba(0,0,0,0.1);box-shadow:0 -1px 10px rgba(0,0,0,0.1)}.navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0}.navbar .nav.pull-right{float:right;margin-right:0}.navbar .nav>li{float:left}.navbar .nav>li>a{float:none;padding:10px 15px 10px;color:#777;text-decoration:none;text-shadow:0 1px 0 #fff}.navbar .nav .dropdown-toggle .caret{margin-top:8px}.navbar .nav>li>a:focus,.navbar .nav>li>a:hover{color:#333;text-decoration:none;background-color:transparent}.navbar .nav>.active>a,.navbar .nav>.active>a:hover,.navbar .nav>.active>a:focus{color:#555;text-decoration:none;background-color:#e5e5e5;-webkit-box-shadow:inset 0 3px 8px rgba(0,0,0,0.125);-moz-box-shadow:inset 0 3px 8px rgba(0,0,0,0.125);box-shadow:inset 0 3px 8px rgba(0,0,0,0.125)}.navbar .btn-navbar{display:none;float:right;padding:7px 10px;margin-right:5px;margin-left:5px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#ededed;*background-color:#e5e5e5;background-image:-moz-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f2f2f2),to(#e5e5e5));background-image:-webkit-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:-o-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:linear-gradient(to bottom,#f2f2f2,#e5e5e5);background-repeat:repeat-x;border-color:#e5e5e5 #e5e5e5 #bfbfbf;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2',endColorstr='#ffe5e5e5',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075)}.navbar .btn-navbar:hover,.navbar .btn-navbar:active,.navbar .btn-navbar.active,.navbar .btn-navbar.disabled,.navbar .btn-navbar[disabled]{color:#fff;background-color:#e5e5e5;*background-color:#d9d9d9}.navbar .btn-navbar:active,.navbar .btn-navbar.active{background-color:#ccc \9}.navbar .btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,0.25);-moz-box-shadow:0 1px 0 rgba(0,0,0,0.25);box-shadow:0 1px 0 rgba(0,0,0,0.25)}.btn-navbar .icon-bar+.icon-bar{margin-top:3px}.navbar .nav>li>.dropdown-menu:before{position:absolute;top:-7px;left:9px;display:inline-block;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-left:7px solid transparent;border-bottom-color:rgba(0,0,0,0.2);content:''}.navbar .nav>li>.dropdown-menu:after{position:absolute;top:-6px;left:10px;display:inline-block;border-right:6px solid transparent;border-bottom:6px solid #fff;border-left:6px solid transparent;content:''}.navbar-fixed-bottom .nav>li>.dropdown-menu:before{top:auto;bottom:-7px;border-top:7px solid #ccc;border-bottom:0;border-top-color:rgba(0,0,0,0.2)}.navbar-fixed-bottom .nav>li>.dropdown-menu:after{top:auto;bottom:-6px;border-top:6px solid #fff;border-bottom:0}.navbar .nav li.dropdown>a:hover .caret{border-top-color:#555;border-bottom-color:#555}.navbar .nav li.dropdown.open>.dropdown-toggle,.navbar .nav li.dropdown.active>.dropdown-toggle,.navbar .nav li.dropdown.open.active>.dropdown-toggle{color:#555;background-color:#e5e5e5}.navbar .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#777;border-bottom-color:#777}.navbar .nav li.dropdown.open>.dropdown-toggle .caret,.navbar .nav li.dropdown.active>.dropdown-toggle .caret,.navbar .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#555;border-bottom-color:#555}.navbar .pull-right>li>.dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right{right:0;left:auto}.navbar .pull-right>li>.dropdown-menu:before,.navbar .nav>li>.dropdown-menu.pull-right:before{right:12px;left:auto}.navbar .pull-right>li>.dropdown-menu:after,.navbar .nav>li>.dropdown-menu.pull-right:after{right:13px;left:auto}.navbar .pull-right>li>.dropdown-menu .dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right .dropdown-menu{right:100%;left:auto;margin-right:-1px;margin-left:0;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px}.navbar-inverse .navbar-inner{background-color:#1b1b1b;background-image:-moz-linear-gradient(top,#222,#111);background-image:-webkit-gradient(linear,0 0,0 100%,from(#222),to(#111));background-image:-webkit-linear-gradient(top,#222,#111);background-image:-o-linear-gradient(top,#222,#111);background-image:linear-gradient(to bottom,#222,#111);background-repeat:repeat-x;border-color:#252525;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222',endColorstr='#ff111111',GradientType=0)}.navbar-inverse .brand,.navbar-inverse .nav>li>a{color:#999;text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.navbar-inverse .brand:hover,.navbar-inverse .nav>li>a:hover{color:#fff}.navbar-inverse .brand{color:#999}.navbar-inverse .navbar-text{color:#999}.navbar-inverse .nav>li>a:focus,.navbar-inverse .nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .nav .active>a,.navbar-inverse .nav .active>a:hover,.navbar-inverse .nav .active>a:focus{color:#fff;background-color:#111}.navbar-inverse .navbar-link{color:#999}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .divider-vertical{border-right-color:#222;border-left-color:#111}.navbar-inverse .nav li.dropdown.open>.dropdown-toggle,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle{color:#fff;background-color:#111}.navbar-inverse .nav li.dropdown>a:hover .caret{border-top-color:#fff;border-bottom-color:#fff}.navbar-inverse .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#999;border-bottom-color:#999}.navbar-inverse .nav li.dropdown.open>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff}.navbar-inverse .navbar-search .search-query{color:#fff;background-color:#515151;border-color:#111;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-webkit-transition:none;-moz-transition:none;-o-transition:none;transition:none}.navbar-inverse .navbar-search .search-query:-moz-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query:-ms-input-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query:focus,.navbar-inverse .navbar-search .search-query.focused{padding:5px 15px;color:#333;text-shadow:0 1px 0 #fff;background-color:#fff;border:0;outline:0;-webkit-box-shadow:0 0 3px rgba(0,0,0,0.15);-moz-box-shadow:0 0 3px rgba(0,0,0,0.15);box-shadow:0 0 3px rgba(0,0,0,0.15)}.navbar-inverse .btn-navbar{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e0e0e;*background-color:#040404;background-image:-moz-linear-gradient(top,#151515,#040404);background-image:-webkit-gradient(linear,0 0,0 100%,from(#151515),to(#040404));background-image:-webkit-linear-gradient(top,#151515,#040404);background-image:-o-linear-gradient(top,#151515,#040404);background-image:linear-gradient(to bottom,#151515,#040404);background-repeat:repeat-x;border-color:#040404 #040404 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff151515',endColorstr='#ff040404',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.navbar-inverse .btn-navbar:hover,.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active,.navbar-inverse .btn-navbar.disabled,.navbar-inverse .btn-navbar[disabled]{color:#fff;background-color:#040404;*background-color:#000}.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active{background-color:#000 \9}.breadcrumb{padding:8px 15px;margin:0 0 20px;list-style:none;background-color:#f5f5f5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.breadcrumb>li{display:inline-block;*display:inline;text-shadow:0 1px 0 #fff;*zoom:1}.breadcrumb>li>.divider{padding:0 5px;color:#ccc}.breadcrumb>.active{color:#999}.pagination{margin:20px 0}.pagination ul{display:inline-block;*display:inline;margin-bottom:0;margin-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*zoom:1;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.pagination ul>li{display:inline}.pagination ul>li>a,.pagination ul>li>span{float:left;padding:4px 12px;line-height:20px;text-decoration:none;background-color:#fff;border:1px solid #ddd;border-left-width:0}.pagination ul>li>a:hover,.pagination ul>.active>a,.pagination ul>.active>span{background-color:#f5f5f5}.pagination ul>.active>a,.pagination ul>.active>span{color:#999;cursor:default}.pagination ul>.disabled>span,.pagination ul>.disabled>a,.pagination ul>.disabled>a:hover{color:#999;cursor:default;background-color:transparent}.pagination ul>li:first-child>a,.pagination ul>li:first-child>span{border-left-width:1px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:4px}.pagination ul>li:last-child>a,.pagination ul>li:last-child>span{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:4px}.pagination-centered{text-align:center}.pagination-right{text-align:right}.pagination-large ul>li>a,.pagination-large ul>li>span{padding:11px 19px;font-size:17.5px}.pagination-large ul>li:first-child>a,.pagination-large ul>li:first-child>span{-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topleft:6px}.pagination-large ul>li:last-child>a,.pagination-large ul>li:last-child>span{-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px}.pagination-mini ul>li:first-child>a,.pagination-small ul>li:first-child>a,.pagination-mini ul>li:first-child>span,.pagination-small ul>li:first-child>span{-webkit-border-bottom-left-radius:3px;border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-top-left-radius:3px;-moz-border-radius-bottomleft:3px;-moz-border-radius-topleft:3px}.pagination-mini ul>li:last-child>a,.pagination-small ul>li:last-child>a,.pagination-mini ul>li:last-child>span,.pagination-small ul>li:last-child>span{-webkit-border-top-right-radius:3px;border-top-right-radius:3px;-webkit-border-bottom-right-radius:3px;border-bottom-right-radius:3px;-moz-border-radius-topright:3px;-moz-border-radius-bottomright:3px}.pagination-small ul>li>a,.pagination-small ul>li>span{padding:2px 10px;font-size:11.9px}.pagination-mini ul>li>a,.pagination-mini ul>li>span{padding:0 6px;font-size:10.5px}.pager{margin:20px 0;text-align:center;list-style:none;*zoom:1}.pager:before,.pager:after{display:table;line-height:0;content:""}.pager:after{clear:both}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.pager li>a:hover{text-decoration:none;background-color:#f5f5f5}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>span{color:#999;cursor:default;background-color:#fff}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop,.modal-backdrop.fade.in{opacity:.8;filter:alpha(opacity=80)}.modal{position:fixed;top:10%;left:50%;z-index:1050;width:560px;margin-left:-280px;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;outline:0;-webkit-box-shadow:0 3px 7px rgba(0,0,0,0.3);-moz-box-shadow:0 3px 7px rgba(0,0,0,0.3);box-shadow:0 3px 7px rgba(0,0,0,0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box}.modal.fade{top:-25%;-webkit-transition:opacity .3s linear,top .3s ease-out;-moz-transition:opacity .3s linear,top .3s ease-out;-o-transition:opacity .3s linear,top .3s ease-out;transition:opacity .3s linear,top .3s ease-out}.modal.fade.in{top:10%}.modal-header{padding:9px 15px;border-bottom:1px solid #eee}.modal-header .close{margin-top:2px}.modal-header h3{margin:0;line-height:30px}.modal-body{position:relative;max-height:400px;padding:15px;overflow-y:auto}.modal-form{margin-bottom:0}.modal-footer{padding:14px 15px 15px;margin-bottom:0;text-align:right;background-color:#f5f5f5;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;*zoom:1;-webkit-box-shadow:inset 0 1px 0 #fff;-moz-box-shadow:inset 0 1px 0 #fff;box-shadow:inset 0 1px 0 #fff}.modal-footer:before,.modal-footer:after{display:table;line-height:0;content:""}.modal-footer:after{clear:both}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.tooltip{position:absolute;z-index:1030;display:block;padding:5px;font-size:11px;opacity:0;filter:alpha(opacity=0);visibility:visible}.tooltip.in{opacity:.8;filter:alpha(opacity=80)}.tooltip.top{margin-top:-3px}.tooltip.right{margin-left:3px}.tooltip.bottom{margin-top:3px}.tooltip.left{margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top-color:#000;border-width:5px 5px 0}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-right-color:#000;border-width:5px 5px 5px 0}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-left-color:#000;border-width:5px 0 5px 5px}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-bottom-color:#000;border-width:0 5px 5px}.popover{position:absolute;top:0;left:0;z-index:1010;display:none;width:236px;padding:1px;text-align:left;white-space:normal;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;font-weight:normal;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover .arrow,.popover .arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover .arrow{border-width:11px}.popover .arrow:after{border-width:10px;content:""}.popover.top .arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,0.25);border-bottom-width:0}.popover.top .arrow:after{bottom:1px;margin-left:-10px;border-top-color:#fff;border-bottom-width:0}.popover.right .arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,0.25);border-left-width:0}.popover.right .arrow:after{bottom:-10px;left:1px;border-right-color:#fff;border-left-width:0}.popover.bottom .arrow{top:-11px;left:50%;margin-left:-11px;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,0.25);border-top-width:0}.popover.bottom .arrow:after{top:1px;margin-left:-10px;border-bottom-color:#fff;border-top-width:0}.popover.left .arrow{top:50%;right:-11px;margin-top:-11px;border-left-color:#999;border-left-color:rgba(0,0,0,0.25);border-right-width:0}.popover.left .arrow:after{right:1px;bottom:-10px;border-left-color:#fff;border-right-width:0}.thumbnails{margin-left:-20px;list-style:none;*zoom:1}.thumbnails:before,.thumbnails:after{display:table;line-height:0;content:""}.thumbnails:after{clear:both}.row-fluid .thumbnails{margin-left:0}.thumbnails>li{float:left;margin-bottom:20px;margin-left:20px}.thumbnail{display:block;padding:4px;line-height:20px;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.055);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.055);box-shadow:0 1px 3px rgba(0,0,0,0.055);-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}a.thumbnail:hover{border-color:#08c;-webkit-box-shadow:0 1px 4px rgba(0,105,214,0.25);-moz-box-shadow:0 1px 4px rgba(0,105,214,0.25);box-shadow:0 1px 4px rgba(0,105,214,0.25)}.thumbnail>img{display:block;max-width:100%;margin-right:auto;margin-left:auto}.thumbnail .caption{padding:9px;color:#555}.media,.media-body{overflow:hidden;*overflow:visible;zoom:1}.media,.media .media{margin-top:15px}.media:first-child{margin-top:0}.media-object{display:block}.media-heading{margin:0 0 5px}.media .pull-left{margin-right:10px}.media .pull-right{margin-left:10px}.media-list{margin-left:0;list-style:none}.label,.badge{display:inline-block;padding:2px 4px;font-size:11.844px;font-weight:bold;line-height:14px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);white-space:nowrap;vertical-align:baseline;background-color:#999}.label{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.badge{padding-right:9px;padding-left:9px;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px}.label:empty,.badge:empty{display:none}a.label:hover,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.label-important,.badge-important{background-color:#b94a48}.label-important[href],.badge-important[href]{background-color:#953b39}.label-warning,.badge-warning{background-color:#f89406}.label-warning[href],.badge-warning[href]{background-color:#c67605}.label-success,.badge-success{background-color:#468847}.label-success[href],.badge-success[href]{background-color:#356635}.label-info,.badge-info{background-color:#3a87ad}.label-info[href],.badge-info[href]{background-color:#2d6987}.label-inverse,.badge-inverse{background-color:#333}.label-inverse[href],.badge-inverse[href]{background-color:#1a1a1a}.btn .label,.btn .badge{position:relative;top:-1px}.btn-mini .label,.btn-mini .badge{top:0}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-moz-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-ms-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:0 0}to{background-position:40px 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f7f7f7;background-image:-moz-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f5f5f5),to(#f9f9f9));background-image:-webkit-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-o-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:linear-gradient(to bottom,#f5f5f5,#f9f9f9);background-repeat:repeat-x;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#fff9f9f9',GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress .bar{float:left;width:0;height:100%;font-size:12px;color:#fff;text-align:center;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e90d2;background-image:-moz-linear-gradient(top,#149bdf,#0480be);background-image:-webkit-gradient(linear,0 0,0 100%,from(#149bdf),to(#0480be));background-image:-webkit-linear-gradient(top,#149bdf,#0480be);background-image:-o-linear-gradient(top,#149bdf,#0480be);background-image:linear-gradient(to bottom,#149bdf,#0480be);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf',endColorstr='#ff0480be',GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-transition:width .6s ease;-moz-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress .bar+.bar{-webkit-box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15)}.progress-striped .bar{background-color:#149bdf;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;-moz-background-size:40px 40px;-o-background-size:40px 40px;background-size:40px 40px}.progress.active .bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;-ms-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-danger .bar,.progress .bar-danger{background-color:#dd514c;background-image:-moz-linear-gradient(top,#ee5f5b,#c43c35);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#c43c35));background-image:-webkit-linear-gradient(top,#ee5f5b,#c43c35);background-image:-o-linear-gradient(top,#ee5f5b,#c43c35);background-image:linear-gradient(to bottom,#ee5f5b,#c43c35);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b',endColorstr='#ffc43c35',GradientType=0)}.progress-danger.progress-striped .bar,.progress-striped .bar-danger{background-color:#ee5f5b;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-success .bar,.progress .bar-success{background-color:#5eb95e;background-image:-moz-linear-gradient(top,#62c462,#57a957);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#57a957));background-image:-webkit-linear-gradient(top,#62c462,#57a957);background-image:-o-linear-gradient(top,#62c462,#57a957);background-image:linear-gradient(to bottom,#62c462,#57a957);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462',endColorstr='#ff57a957',GradientType=0)}.progress-success.progress-striped .bar,.progress-striped .bar-success{background-color:#62c462;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-info .bar,.progress .bar-info{background-color:#4bb1cf;background-image:-moz-linear-gradient(top,#5bc0de,#339bb9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#339bb9));background-image:-webkit-linear-gradient(top,#5bc0de,#339bb9);background-image:-o-linear-gradient(top,#5bc0de,#339bb9);background-image:linear-gradient(to bottom,#5bc0de,#339bb9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff339bb9',GradientType=0)}.progress-info.progress-striped .bar,.progress-striped .bar-info{background-color:#5bc0de;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-warning .bar,.progress .bar-warning{background-color:#faa732;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450',endColorstr='#fff89406',GradientType=0)}.progress-warning.progress-striped .bar,.progress-striped .bar-warning{background-color:#fbb450;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.accordion{margin-bottom:20px}.accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.accordion-heading{border-bottom:0}.accordion-heading .accordion-toggle{display:block;padding:8px 15px}.accordion-toggle{cursor:pointer}.accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5}.carousel{position:relative;margin-bottom:20px;line-height:1}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-moz-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>img{display:block;line-height:1}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:40%;left:15px;width:40px;height:40px;margin-top:-20px;font-size:60px;font-weight:100;line-height:30px;color:#fff;text-align:center;background:#222;border:3px solid #fff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:.5;filter:alpha(opacity=50)}.carousel-control.right{right:15px;left:auto}.carousel-control:hover{color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}.carousel-caption{position:absolute;right:0;bottom:0;left:0;padding:15px;background:#333;background:rgba(0,0,0,0.75)}.carousel-caption h4,.carousel-caption p{line-height:20px;color:#fff}.carousel-caption h4{margin:0 0 5px}.carousel-caption p{margin-bottom:0}.hero-unit{padding:60px;margin-bottom:30px;font-size:18px;font-weight:200;line-height:30px;color:inherit;background-color:#eee;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;letter-spacing:-1px;color:inherit}.hero-unit li{line-height:30px}.pull-right{float:right}.pull-left{float:left}.hide{display:none}.show{display:block}.invisible{visibility:hidden}.affix{position:fixed}/*! - * Bootstrap Responsive v2.2.2 - * - * Copyright 2012 Twitter, Inc - * Licensed under the Apache License v2.0 - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Designed and built with all the love in the world @twitter by @mdo and @fat. - */@-ms-viewport{width:device-width}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.hidden{display:none;visibility:hidden}.visible-phone{display:none!important}.visible-tablet{display:none!important}.hidden-desktop{display:none!important}.visible-desktop{display:inherit!important}@media(min-width:768px) and (max-width:979px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-tablet{display:inherit!important}.hidden-tablet{display:none!important}}@media(max-width:767px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-phone{display:inherit!important}.hidden-phone{display:none!important}}@media(min-width:1200px){.row{margin-left:-30px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:30px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px}.span12{width:1170px}.span11{width:1070px}.span10{width:970px}.span9{width:870px}.span8{width:770px}.span7{width:670px}.span6{width:570px}.span5{width:470px}.span4{width:370px}.span3{width:270px}.span2{width:170px}.span1{width:70px}.offset12{margin-left:1230px}.offset11{margin-left:1130px}.offset10{margin-left:1030px}.offset9{margin-left:930px}.offset8{margin-left:830px}.offset7{margin-left:730px}.offset6{margin-left:630px}.offset5{margin-left:530px}.offset4{margin-left:430px}.offset3{margin-left:330px}.offset2{margin-left:230px}.offset1{margin-left:130px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.564102564102564%;*margin-left:2.5109110747408616%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.564102564102564%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.45299145299145%;*width:91.39979996362975%}.row-fluid .span10{width:82.90598290598291%;*width:82.8527914166212%}.row-fluid .span9{width:74.35897435897436%;*width:74.30578286961266%}.row-fluid .span8{width:65.81196581196582%;*width:65.75877432260411%}.row-fluid .span7{width:57.26495726495726%;*width:57.21176577559556%}.row-fluid .span6{width:48.717948717948715%;*width:48.664757228587014%}.row-fluid .span5{width:40.17094017094017%;*width:40.11774868157847%}.row-fluid .span4{width:31.623931623931625%;*width:31.570740134569924%}.row-fluid .span3{width:23.076923076923077%;*width:23.023731587561375%}.row-fluid .span2{width:14.52991452991453%;*width:14.476723040552828%}.row-fluid .span1{width:5.982905982905983%;*width:5.929714493544281%}.row-fluid .offset12{margin-left:105.12820512820512%;*margin-left:105.02182214948171%}.row-fluid .offset12:first-child{margin-left:102.56410256410257%;*margin-left:102.45771958537915%}.row-fluid .offset11{margin-left:96.58119658119658%;*margin-left:96.47481360247316%}.row-fluid .offset11:first-child{margin-left:94.01709401709402%;*margin-left:93.91071103837061%}.row-fluid .offset10{margin-left:88.03418803418803%;*margin-left:87.92780505546462%}.row-fluid .offset10:first-child{margin-left:85.47008547008548%;*margin-left:85.36370249136206%}.row-fluid .offset9{margin-left:79.48717948717949%;*margin-left:79.38079650845607%}.row-fluid .offset9:first-child{margin-left:76.92307692307693%;*margin-left:76.81669394435352%}.row-fluid .offset8{margin-left:70.94017094017094%;*margin-left:70.83378796144753%}.row-fluid .offset8:first-child{margin-left:68.37606837606839%;*margin-left:68.26968539734497%}.row-fluid .offset7{margin-left:62.393162393162385%;*margin-left:62.28677941443899%}.row-fluid .offset7:first-child{margin-left:59.82905982905982%;*margin-left:59.72267685033642%}.row-fluid .offset6{margin-left:53.84615384615384%;*margin-left:53.739770867430444%}.row-fluid .offset6:first-child{margin-left:51.28205128205128%;*margin-left:51.175668303327875%}.row-fluid .offset5{margin-left:45.299145299145295%;*margin-left:45.1927623204219%}.row-fluid .offset5:first-child{margin-left:42.73504273504273%;*margin-left:42.62865975631933%}.row-fluid .offset4{margin-left:36.75213675213675%;*margin-left:36.645753773413354%}.row-fluid .offset4:first-child{margin-left:34.18803418803419%;*margin-left:34.081651209310785%}.row-fluid .offset3{margin-left:28.205128205128204%;*margin-left:28.0987452264048%}.row-fluid .offset3:first-child{margin-left:25.641025641025642%;*margin-left:25.53464266230224%}.row-fluid .offset2{margin-left:19.65811965811966%;*margin-left:19.551736679396257%}.row-fluid .offset2:first-child{margin-left:17.094017094017094%;*margin-left:16.98763411529369%}.row-fluid .offset1{margin-left:11.11111111111111%;*margin-left:11.004728132387708%}.row-fluid .offset1:first-child{margin-left:8.547008547008547%;*margin-left:8.440625568285142%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:30px}input.span12,textarea.span12,.uneditable-input.span12{width:1156px}input.span11,textarea.span11,.uneditable-input.span11{width:1056px}input.span10,textarea.span10,.uneditable-input.span10{width:956px}input.span9,textarea.span9,.uneditable-input.span9{width:856px}input.span8,textarea.span8,.uneditable-input.span8{width:756px}input.span7,textarea.span7,.uneditable-input.span7{width:656px}input.span6,textarea.span6,.uneditable-input.span6{width:556px}input.span5,textarea.span5,.uneditable-input.span5{width:456px}input.span4,textarea.span4,.uneditable-input.span4{width:356px}input.span3,textarea.span3,.uneditable-input.span3{width:256px}input.span2,textarea.span2,.uneditable-input.span2{width:156px}input.span1,textarea.span1,.uneditable-input.span1{width:56px}.thumbnails{margin-left:-30px}.thumbnails>li{margin-left:30px}.row-fluid .thumbnails{margin-left:0}}@media(min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px}.span12{width:724px}.span11{width:662px}.span10{width:600px}.span9{width:538px}.span8{width:476px}.span7{width:414px}.span6{width:352px}.span5{width:290px}.span4{width:228px}.span3{width:166px}.span2{width:104px}.span1{width:42px}.offset12{margin-left:764px}.offset11{margin-left:702px}.offset10{margin-left:640px}.offset9{margin-left:578px}.offset8{margin-left:516px}.offset7{margin-left:454px}.offset6{margin-left:392px}.offset5{margin-left:330px}.offset4{margin-left:268px}.offset3{margin-left:206px}.offset2{margin-left:144px}.offset1{margin-left:82px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.7624309392265194%;*margin-left:2.709239449864817%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.7624309392265194%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.43646408839778%;*width:91.38327259903608%}.row-fluid .span10{width:82.87292817679558%;*width:82.81973668743387%}.row-fluid .span9{width:74.30939226519337%;*width:74.25620077583166%}.row-fluid .span8{width:65.74585635359117%;*width:65.69266486422946%}.row-fluid .span7{width:57.18232044198895%;*width:57.12912895262725%}.row-fluid .span6{width:48.61878453038674%;*width:48.56559304102504%}.row-fluid .span5{width:40.05524861878453%;*width:40.00205712942283%}.row-fluid .span4{width:31.491712707182323%;*width:31.43852121782062%}.row-fluid .span3{width:22.92817679558011%;*width:22.87498530621841%}.row-fluid .span2{width:14.3646408839779%;*width:14.311449394616199%}.row-fluid .span1{width:5.801104972375691%;*width:5.747913483013988%}.row-fluid .offset12{margin-left:105.52486187845304%;*margin-left:105.41847889972962%}.row-fluid .offset12:first-child{margin-left:102.76243093922652%;*margin-left:102.6560479605031%}.row-fluid .offset11{margin-left:96.96132596685082%;*margin-left:96.8549429881274%}.row-fluid .offset11:first-child{margin-left:94.1988950276243%;*margin-left:94.09251204890089%}.row-fluid .offset10{margin-left:88.39779005524862%;*margin-left:88.2914070765252%}.row-fluid .offset10:first-child{margin-left:85.6353591160221%;*margin-left:85.52897613729868%}.row-fluid .offset9{margin-left:79.8342541436464%;*margin-left:79.72787116492299%}.row-fluid .offset9:first-child{margin-left:77.07182320441989%;*margin-left:76.96544022569647%}.row-fluid .offset8{margin-left:71.2707182320442%;*margin-left:71.16433525332079%}.row-fluid .offset8:first-child{margin-left:68.50828729281768%;*margin-left:68.40190431409427%}.row-fluid .offset7{margin-left:62.70718232044199%;*margin-left:62.600799341718584%}.row-fluid .offset7:first-child{margin-left:59.94475138121547%;*margin-left:59.838368402492065%}.row-fluid .offset6{margin-left:54.14364640883978%;*margin-left:54.037263430116376%}.row-fluid .offset6:first-child{margin-left:51.38121546961326%;*margin-left:51.27483249088986%}.row-fluid .offset5{margin-left:45.58011049723757%;*margin-left:45.47372751851417%}.row-fluid .offset5:first-child{margin-left:42.81767955801105%;*margin-left:42.71129657928765%}.row-fluid .offset4{margin-left:37.01657458563536%;*margin-left:36.91019160691196%}.row-fluid .offset4:first-child{margin-left:34.25414364640884%;*margin-left:34.14776066768544%}.row-fluid .offset3{margin-left:28.45303867403315%;*margin-left:28.346655695309746%}.row-fluid .offset3:first-child{margin-left:25.69060773480663%;*margin-left:25.584224756083227%}.row-fluid .offset2{margin-left:19.88950276243094%;*margin-left:19.783119783707537%}.row-fluid .offset2:first-child{margin-left:17.12707182320442%;*margin-left:17.02068884448102%}.row-fluid .offset1{margin-left:11.32596685082873%;*margin-left:11.219583872105325%}.row-fluid .offset1:first-child{margin-left:8.56353591160221%;*margin-left:8.457152932878806%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:710px}input.span11,textarea.span11,.uneditable-input.span11{width:648px}input.span10,textarea.span10,.uneditable-input.span10{width:586px}input.span9,textarea.span9,.uneditable-input.span9{width:524px}input.span8,textarea.span8,.uneditable-input.span8{width:462px}input.span7,textarea.span7,.uneditable-input.span7{width:400px}input.span6,textarea.span6,.uneditable-input.span6{width:338px}input.span5,textarea.span5,.uneditable-input.span5{width:276px}input.span4,textarea.span4,.uneditable-input.span4{width:214px}input.span3,textarea.span3,.uneditable-input.span3{width:152px}input.span2,textarea.span2,.uneditable-input.span2{width:90px}input.span1,textarea.span1,.uneditable-input.span1{width:28px}}@media(max-width:767px){body{padding-right:20px;padding-left:20px}.navbar-fixed-top,.navbar-fixed-bottom,.navbar-static-top{margin-right:-20px;margin-left:-20px}.container-fluid{padding:0}.dl-horizontal dt{float:none;width:auto;clear:none;text-align:left}.dl-horizontal dd{margin-left:0}.container{width:auto}.row-fluid{width:100%}.row,.thumbnails{margin-left:0}.thumbnails>li{float:none;margin-left:0}[class*="span"],.uneditable-input[class*="span"],.row-fluid [class*="span"]{display:block;float:none;width:100%;margin-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.span12,.row-fluid .span12{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="offset"]:first-child{margin-left:0}.input-large,.input-xlarge,.input-xxlarge,input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.input-prepend input,.input-append input,.input-prepend input[class*="span"],.input-append input[class*="span"]{display:inline-block;width:auto}.controls-row [class*="span"]+[class*="span"]{margin-left:0}.modal{position:fixed;top:20px;right:20px;left:20px;width:auto;margin:0}.modal.fade{top:-100px}.modal.fade.in{top:20px}}@media(max-width:480px){.nav-collapse{-webkit-transform:translate3d(0,0,0)}.page-header h1 small{display:block;line-height:20px}input[type="checkbox"],input[type="radio"]{border:1px solid #ccc}.form-horizontal .control-label{float:none;width:auto;padding-top:0;text-align:left}.form-horizontal .controls{margin-left:0}.form-horizontal .control-list{padding-top:0}.form-horizontal .form-actions{padding-right:10px;padding-left:10px}.media .pull-left,.media .pull-right{display:block;float:none;margin-bottom:10px}.media-object{margin-right:0;margin-left:0}.modal{top:10px;right:10px;left:10px}.modal-header .close{padding:10px;margin:-10px}.carousel-caption{position:static}}@media(max-width:979px){body{padding-top:0}.navbar-fixed-top,.navbar-fixed-bottom{position:static}.navbar-fixed-top{margin-bottom:20px}.navbar-fixed-bottom{margin-top:20px}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding:5px}.navbar .container{width:auto;padding:0}.navbar .brand{padding-right:10px;padding-left:10px;margin:0 0 0 -5px}.nav-collapse{clear:both}.nav-collapse .nav{float:none;margin:0 0 10px}.nav-collapse .nav>li{float:none}.nav-collapse .nav>li>a{margin-bottom:2px}.nav-collapse .nav>.divider-vertical{display:none}.nav-collapse .nav .nav-header{color:#777;text-shadow:none}.nav-collapse .nav>li>a,.nav-collapse .dropdown-menu a{padding:9px 15px;font-weight:bold;color:#777;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.nav-collapse .btn{padding:4px 10px 4px;font-weight:normal;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.nav-collapse .dropdown-menu li+li a{margin-bottom:2px}.nav-collapse .nav>li>a:hover,.nav-collapse .dropdown-menu a:hover{background-color:#f2f2f2}.navbar-inverse .nav-collapse .nav>li>a,.navbar-inverse .nav-collapse .dropdown-menu a{color:#999}.navbar-inverse .nav-collapse .nav>li>a:hover,.navbar-inverse .nav-collapse .dropdown-menu a:hover{background-color:#111}.nav-collapse.in .btn-group{padding:0;margin-top:5px}.nav-collapse .dropdown-menu{position:static;top:auto;left:auto;display:none;float:none;max-width:none;padding:0;margin:0 15px;background-color:transparent;border:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.nav-collapse .open>.dropdown-menu{display:block}.nav-collapse .dropdown-menu:before,.nav-collapse .dropdown-menu:after{display:none}.nav-collapse .dropdown-menu .divider{display:none}.nav-collapse .nav>li>.dropdown-menu:before,.nav-collapse .nav>li>.dropdown-menu:after{display:none}.nav-collapse .navbar-form,.nav-collapse .navbar-search{float:none;padding:10px 15px;margin:10px 0;border-top:1px solid #f2f2f2;border-bottom:1px solid #f2f2f2;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1)}.navbar-inverse .nav-collapse .navbar-form,.navbar-inverse .nav-collapse .navbar-search{border-top-color:#111;border-bottom-color:#111}.navbar .nav-collapse .nav.pull-right{float:none;margin-left:0}.nav-collapse,.nav-collapse.collapse{height:0;overflow:hidden}.navbar .btn-navbar{display:block}.navbar-static .navbar-inner{padding-right:10px;padding-left:10px}}@media(min-width:980px){.nav-collapse.collapse{height:auto!important;overflow:visible!important}}.container>footer p{text-align:center}.container-fluid>footer p{text-align:center}.page-content{background-color:#fff;padding:20px;margin:0 -20px;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.15);-moz-box-shadow:0 1px 2px rgba(0,0,0,.15);box-shadow:0 1px 2px rgba(0,0,0,.15)}.page-header{background-color:#f5f5f5;padding:20px 20px 10px;margin:-20px -20px 20px}.hero-unit{background-color:#f5f5f5;margin-bottom:30px;padding:60px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;letter-spacing:-1px}.hero-unit p{font-size:18px;font-weight:200;line-height:27px}.container>footer{padding-top:30px}.container-fluid>footer{padding-top:30px}.carousel-unit-top{background-color:#f5f5f5;padding-bottom:20px;padding-top:0;padding-left:50px;padding-right:50px;border:1px solid #ddd;border-bottom:0;-webkit-border-top-left-radius:6px;-webkit-border-top-right-radius:6px;-moz-border-radius-topleft:6px;-moz-border-radius-topright:6px;border-top-left-radius:6px;border-top-right-radius:6px}.carousel-unit-bottom{background-color:#f5f5f5;padding-bottom:30px;padding-top:0;padding-left:50px;padding-right:50px;margin-bottom:20px;border:1px solid #ddd;border-top:0;-webkit-border-bottom-right-radius:6px;-webkit-border-bottom-left-radius:6px;-moz-border-radius-bottomright:6px;-moz-border-radius-bottomleft:6px;border-bottom-right-radius:6px;border-bottom-left-radius:6px}.carousel-unit-bottom h1{margin-bottom:0;font-size:30px;line-height:1;letter-spacing:-1px;text-align:center}.carousel-unit-bottom p{font-size:18px;font-weight:200;line-height:27px;text-align:center;padding-top:10px}.carousel-unit-top div#carousel-tabs{padding-bottom:0}.tabs{margin-top:-1px!important}div.dataTables_length label{float:left;text-align:left}div.dataTables_length select{width:75px}div.dataTables_filter label{float:right}div.dataTables_info{padding-top:8px}div.dataTables_paginate{float:right;margin:0}table.table{clear:both;margin-bottom:6px!important}table.table thead .sorting,table.table thead .sorting_asc,table.table thead .sorting_desc,table.table thead .sorting_asc_disabled,table.table thead .sorting_desc_disabled{cursor:pointer;*cursor:hand}table.table thead .sorting{background:url('images/sort_both.png') no-repeat center right}table.table thead .sorting_asc{background:url('images/sort_asc.png') no-repeat center right}table.table thead .sorting_desc{background:url('images/sort_desc.png') no-repeat center right}table.table thead .sorting_asc_disabled{background:url('images/sort_asc_disabled.png') no-repeat center right}table.table thead .sorting_desc_disabled{background:url('images/sort_desc_disabled.png') no-repeat center right}table.dataTable th:active{outline:0}ul.wysihtml5-toolbar{margin:0;padding:0;display:block}ul.wysihtml5-toolbar::after{clear:both;display:table;content:""}ul.wysihtml5-toolbar>li{float:left;display:list-item;list-style:none;margin:0 5px 10px 0}ul.wysihtml5-toolbar a[data-wysihtml5-command=bold]{font-weight:bold}ul.wysihtml5-toolbar a[data-wysihtml5-command=italic]{font-style:italic}ul.wysihtml5-toolbar a.btn.wysihtml5-command-active{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);background-color:#e6e6e6;background-color:#d9d9d9 9;outline:0}@CHARSET "UTF-8";body{padding-top:60px;padding-bottom:40px}.sidebar-nav{padding:9px 0}.mono{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}.oss-overlay{position:absolute;z-index:2000;background-color:black;opacity:.4}a.aplain,a:link.aplain,a:visited.aplain,a:hover.aplain,a:active.aplain{color:#fff;text-decoration:none}.meetings{margin-left:20px;margin-right:20px;width:80%}.meetings_index_container{display:block}.meetings_index{display:block;text-align:right}.meeting h1{color:#4fa500;font-size:146.5%;font-weight:bold;padding-bottom:0;margin-bottom:0}.meeting h4{color:#4fa500;font-size:100%;font-weight:normal;font-style:italic}.meeting.title{padding-bottom:2px;border-bottom:2px solid #4fa500;margin-bottom:10px}.meetingitem h1{color:#000;font-size:116%;font-weight:bold;padding-bottom:0;margin-bottom:0}.meetingitem a,.meetingitem a:link,.meetingitem a:visited{color:#000;text-decoration:none;border-bottom:1px dotted #000}.meetingitem h4{color:#000;font-size:100%;font-weight:normal;font-style:italic}.meetingitem.title{margin-bottom:10px}.meetingitem.title.icons{float:right}.meetingitem.title.icons img{float:right;padding-left:10px}.meetingitem dd{color:#000;font-size:100%;font-weight:normal;font-style:none;margin-bottom:10px;padding-left:20px;padding-bottom:10px}.meeting.buttons{text-align:center}.list_preamble_container{display:block}.list_preamble{display:block;text-align:right}.pm-table{margin-bottom:18px;margin-left:auto;margin-right:auto}.pm-table th{padding:0;text-align:center;vertical-align:bottom;border:1px solid #ddd;border-bottom-width:2px;border-top:0;font-weight:bold}.pm-table th.asn{cursor:hand;cursor:pointer}.pm-table th:last-child{border-right-width:2px}.pm-table td{text-align:center;vertical-align:top;border:1px solid #ddd}.pm-table td.peering{cursor:hand;cursor:pointer}.pm-table td.zoom1{padding:0 1px;font-size:8px;line-height:12px}.pm-table td.zoom2{padding:2px 4px;font-size:11px;line-height:15px}.pm-table td.zoom3{padding:4px 8px;font-size:13px;line-height:18px}.pm-table td.zoom4{padding:6px 10px;font-size:15px;line-height:20px}.pm-table td.zoom5{padding:8px 12px;font-size:18px;line-height:24px}.pm-table th.zoom1{font-size:8px;line-height:12px}.pm-table th.zoom2{font-size:11px;line-height:15px}.pm-table th.zoom3{font-size:13px;line-height:18px}.pm-table th.zoom4{font-size:15px;line-height:20px}.pm-table th.zoom5{font-size:18px;line-height:24px}.pm-table td:last-child{border-right-width:2px}.pm-table th.name{border-left:none;border-right:0;border-top:0}.pm-table th.asn{border-left:none;border-top:0;border-right-width:2px}.pm-table td.name{text-align:left;border-left:none;border-right:0;font-weight:bold}.pm-table td.asn{text-align:right;border-left:none;border-right-width:2px;font-weight:bold}.pm-table tr:last-child{border-bottom-width:2px}.pm-table td.peered{background-color:#0c0;width:10px}.pm-table td.not-peered{background-color:#d63030;width:10px}.pm-table td.bilateral-rs{width:10px}.pm-table td.rs-only{width:10px}.pm-table td.bilateral-only{width:10px}.pm-table tr.hover,.pm-table colgroup.hover,.pm-table td.hover{background-color:#eee;border-left:2px solid #555;border-right:2px solid #555}.pm-table td.hover2{border-left:1px solid #555;border-right:1px solid #555}.pm-table th.highlight,.pm-table td.highlight{background-color:#fcf6cf}.pm-table th.highlight2,.pm-table td.highlight2{background-color:#eee}.dropdown-menu li{display:block} \ No newline at end of file diff --git a/public/css/tailwind.css b/public/css/tailwind.css new file mode 100644 index 000000000..5c15faeca --- /dev/null +++ b/public/css/tailwind.css @@ -0,0 +1,2 @@ +*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: } +/*! tailwindcss v3.4.17 | MIT License | https://tailwindcss.com*/*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:""}:host,html{-webkit-text-size-adjust:100%;font-feature-settings:normal;-webkit-tap-highlight-color:transparent;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-variation-settings:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4}body{line-height:inherit;margin:0}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-feature-settings:normal;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em;font-variation-settings:normal}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{font-feature-settings:inherit;color:inherit;font-family:inherit;font-size:100%;font-variation-settings:inherit;font-weight:inherit;letter-spacing:inherit;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{color:#9ca3af;opacity:1}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]:where(:not([hidden=until-found])){display:none}h1{color:#1a202c;font-size:2.4rem;font-weight:300;line-height:1;margin-bottom:1rem}h2{font-size:2rem;font-weight:500;line-height:1.2;margin-bottom:.5rem}h3{font-size:1.75rem;line-height:1.2}h3,h4{color:#1a202c;font-weight:300;margin-bottom:.5rem}h4{font-size:1.5rem;line-height:1}h5{font-size:1.25rem;font-weight:400}h5,h6{line-height:1.2;margin-bottom:.5rem}h6{font-size:1rem;font-weight:500}p{margin-bottom:1rem}li{display:list-item}ol{list-style-type:decimal}ol,ul{display:block;margin:1em 0;padding-left:40px}ul{list-style-type:disc}dl{margin:1em 0}dl,dt{display:block}code,pre{font-size:.9em}pre{display:block;font-family:monospace;margin:1em 0;white-space:pre}hr{margin-bottom:10px}a{color:rgb(59 130 246/var(--tw-text-opacity,1))}a:hover{text-decoration-line:underline}.tw-border-t-1{border-top-width:1px}.tw-border-b-1{border-bottom-width:1px}.tw-border-1{border-width:1px}.shadow{--tw-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px 0 rgba(0,0,0,.06);box-shadow:var(--tw-ring-offset-shadow,0 0 transparent),var(--tw-ring-shadow,0 0 transparent),var(--tw-shadow)}#side-navbar{--tw-bg-opacity:1;--tw-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px 0 rgba(0,0,0,.06);background-color:rgb(255 255 255/var(--tw-bg-opacity,1));box-shadow:var(--tw-ring-offset-shadow,0 0 transparent),var(--tw-ring-shadow,0 0 transparent),var(--tw-shadow);padding-right:0}#side-navbar ol,#side-navbar ul{list-style:none;margin:0;padding:0}#side-navbar h6{--tw-border-opacity:1;border-color:rgb(241 245 248/var(--tw-border-opacity,1));border-right-width:4px;margin:0;padding:1rem 1rem 1rem .25rem}#side-navbar h6,.nav-link{--tw-text-opacity:1;color:rgb(96 111 123/var(--tw-text-opacity,1));display:block}.nav-link{font-size:.875rem;line-height:1.25rem;padding:.5rem .5rem .5rem .75rem;text-decoration-line:none}.nav-link:hover{--tw-text-opacity:1}#side-navbar li,.nav-link:hover{color:rgb(96 111 123/var(--tw-text-opacity,1))}#side-navbar li{--tw-border-opacity:1;--tw-text-opacity:1;border-color:rgb(241 245 248/var(--tw-border-opacity,1));border-right-width:4px;display:block;font-weight:500;padding:0}#side-navbar li.active{font-weight:700!important}#side-navbar li.active,#side-navbar li:hover{--tw-border-opacity:1;--tw-bg-opacity:1;background-color:rgb(241 245 248/var(--tw-bg-opacity,1));border-color:rgb(5 150 105/var(--tw-border-opacity,1))}#side-navbar li:hover{--tw-text-opacity:1;color:rgb(96 111 123/var(--tw-text-opacity,1))}.nav-sub-menu-item{margin-left:2rem}.ixpm-table a{--tw-text-opacity:1;color:rgb(39 121 189/var(--tw-text-opacity,1));text-decoration-line:none}.ixpm-table a:hover{--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity,1));text-decoration-line:underline}.btn{border-radius:.125rem;font-weight:700}.btn,.btn:hover{border-color:transparent}.btn-primary{--tw-bg-opacity:1;--tw-text-opacity:1;background-color:rgb(59 130 246/var(--tw-bg-opacity,1));color:rgb(255 255 255/var(--tw-text-opacity,1));font-weight:700}.btn-primary:hover{--tw-bg-opacity:1;background-color:rgb(39 121 189/var(--tw-bg-opacity,1))}.btn-success{--tw-bg-opacity:1;--tw-text-opacity:1;background-color:rgb(16 185 129/var(--tw-bg-opacity,1));color:rgb(255 255 255/var(--tw-text-opacity,1));font-weight:700}.btn-success:hover{--tw-bg-opacity:1;background-color:rgb(31 157 85/var(--tw-bg-opacity,1))}.btn-secondary{--tw-bg-opacity:1;--tw-text-opacity:1;background-color:rgb(135 149 161/var(--tw-bg-opacity,1));color:rgb(255 255 255/var(--tw-text-opacity,1));font-weight:700}.btn-secondary:hover{--tw-bg-opacity:1;background-color:rgb(96 111 123/var(--tw-bg-opacity,1))}.btn-outline-secondary{--tw-border-opacity:1;--tw-text-opacity:1;background-color:transparent;border-color:rgb(184 194 204/var(--tw-border-opacity,1));border-radius:.125rem;border-width:1px;color:rgb(184 194 204/var(--tw-text-opacity,1));font-weight:600}.btn-outline-secondary:hover{--tw-bg-opacity:1;background-color:rgb(184 194 204/var(--tw-bg-opacity,1))}.btn-white{--tw-border-opacity:1;--tw-bg-opacity:1;--tw-text-opacity:1;--tw-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px 0 rgba(0,0,0,.06);background-color:rgb(255 255 255/var(--tw-bg-opacity,1));border-radius:.125rem;box-shadow:var(--tw-ring-offset-shadow,0 0 transparent),var(--tw-ring-shadow,0 0 transparent),var(--tw-shadow);color:rgb(61 72 82/var(--tw-text-opacity,1));font-weight:600}.btn-white,.btn-white:hover{border-color:rgb(218 225 231/var(--tw-border-opacity,1));border-width:1px}.btn-white:hover{--tw-border-opacity:1;--tw-bg-opacity:1;background-color:rgb(241 245 248/var(--tw-bg-opacity,1))}.btn-info{--tw-border-opacity:1;--tw-bg-opacity:1;--tw-text-opacity:1;--tw-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px 0 rgba(0,0,0,.06);background-color:rgb(255 255 255/var(--tw-bg-opacity,1));border-color:rgb(20 184 166/var(--tw-border-opacity,1));border-radius:.125rem;border-width:1px;box-shadow:var(--tw-ring-offset-shadow,0 0 transparent),var(--tw-ring-shadow,0 0 transparent),var(--tw-shadow);color:rgb(20 184 166/var(--tw-text-opacity,1));font-weight:600}.btn-info:hover{--tw-border-opacity:1;--tw-bg-opacity:1;background-color:rgb(20 184 166/var(--tw-bg-opacity,1));border-color:rgb(218 225 231/var(--tw-border-opacity,1));border-width:1px}input.form-control{--tw-text-opacity:1;--tw-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px 0 rgba(0,0,0,.06);-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:.125rem;border-width:1px;box-shadow:var(--tw-ring-offset-shadow,0 0 transparent),var(--tw-ring-shadow,0 0 transparent),var(--tw-shadow);padding:.5rem 1rem;width:100%}input.form-control,input.form-control:focus{color:rgb(96 111 123/var(--tw-text-opacity,1));line-height:1.25}input.form-control:focus{--tw-text-opacity:1;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);outline:2px solid transparent;outline-offset:2px}.input-group-append .btn{--tw-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px 0 rgba(0,0,0,.06);border-color:#ced4da;box-shadow:var(--tw-ring-offset-shadow,0 0 transparent),var(--tw-ring-shadow,0 0 transparent),var(--tw-shadow)}label.control-label,label.form-check-label{--tw-text-opacity:1;color:rgb(96 111 123/var(--tw-text-opacity,1));display:block;font-size:.875rem;font-weight:700;line-height:1.25rem}.badge{--tw-text-opacity:1;border-radius:9999px;color:rgb(255 255 255/var(--tw-text-opacity,1));font-weight:700;padding:.25rem .75rem}.badge-info{--tw-bg-opacity:1;background-color:rgb(20 184 166/var(--tw-bg-opacity,1))}.badge-success{--tw-bg-opacity:1;background-color:rgb(16 185 129/var(--tw-bg-opacity,1))}.badge-secondary{--tw-bg-opacity:1;background-color:rgb(184 194 204/var(--tw-bg-opacity,1))}.badge-warning{--tw-bg-opacity:1;--tw-text-opacity:1;background-color:rgb(251 146 60/var(--tw-bg-opacity,1));color:rgb(0 0 0/var(--tw-text-opacity,1))}.card{--tw-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px 0 rgba(0,0,0,.06);border-color:#dae1e7;border-radius:.125rem;box-shadow:var(--tw-ring-offset-shadow,0 0 transparent),var(--tw-ring-shadow,0 0 transparent),var(--tw-shadow)}.card .card-header{border-bottom-width:0;border-top-left-radius:.125rem;border-top-right-radius:.125rem}a{color:rgb(108 178 235/var(--tw-text-opacity,1));display:inline-block;font-weight:700;vertical-align:baseline}a,a:hover{--tw-text-opacity:1}a:hover{color:rgb(39 121 189/var(--tw-text-opacity,1));text-decoration-line:none}.table-light td,.table-light thead th{border-color:#ced4da}.docstore table{table-layout:auto;width:100%}.docstore td{border-color:rgb(209 213 219/var(--tw-border-opacity,1));border-top-width:1px}.docstore td,.docstore td.top{--tw-border-opacity:1;padding:.5rem}.docstore td.top{border-color:rgb(209 213 219/var(--tw-border-opacity,1));border-top-width:2px}.docstore td.meta{display:none;white-space:nowrap;width:.25rem}@media (min-width:1024px){.docstore td.meta{display:table-cell;white-space:nowrap;width:.25rem}}.docstore td.icon{margin-right:1rem;padding:.5rem;text-align:center;white-space:nowrap;width:.25rem}.tw-collapse{visibility:collapse}.tw-absolute{position:absolute}.tw-relative{position:relative}.tw-inset-0{inset:0}.tw-float-right{float:right}.tw-m-0{margin:0}.tw-m-2{margin:.5rem}.tw-mx-3{margin-left:.75rem;margin-right:.75rem}.tw-mx-4{margin-left:1rem;margin-right:1rem}.tw-mx-auto{margin-left:auto;margin-right:auto}.tw-my-0{margin-bottom:0;margin-top:0}.tw-my-1{margin-bottom:.25rem;margin-top:.25rem}.tw-my-10{margin-bottom:2.5rem;margin-top:2.5rem}.tw-my-12{margin-bottom:3rem;margin-top:3rem}.tw-my-4{margin-bottom:1rem;margin-top:1rem}.tw-my-6{margin-bottom:1.5rem;margin-top:1.5rem}.tw-my-8{margin-bottom:2rem;margin-top:2rem}.tw-mb-0{margin-bottom:0}.tw-mb-16{margin-bottom:4rem}.tw-mb-2{margin-bottom:.5rem}.tw-mb-6{margin-bottom:1.5rem}.tw-mb-8{margin-bottom:2rem}.tw-ml-1{margin-left:.25rem}.tw-ml-12{margin-left:3rem}.tw-ml-2{margin-left:.5rem}.tw-ml-4{margin-left:1rem}.tw-ml-6{margin-left:1.5rem}.tw-ml-8{margin-left:2rem}.tw-mr-1{margin-right:.25rem}.tw-mr-2{margin-right:.5rem}.tw-mr-3{margin-right:.75rem}.tw-mr-4{margin-right:1rem}.tw-mr-6{margin-right:1.5rem}.tw-mt-1{margin-top:.25rem}.tw-mt-16{margin-top:4rem}.tw-mt-2{margin-top:.5rem}.tw-mt-3{margin-top:.75rem}.tw-mt-4{margin-top:1rem}.tw-mt-6{margin-top:1.5rem}.tw-mt-8{margin-top:2rem}.tw-block{display:block}.tw-inline-block{display:inline-block}.tw-inline{display:inline}.tw-flex{display:flex}.tw-inline-flex{display:inline-flex}.tw-hidden{display:none}.tw-h-24{height:6rem}.tw-h-5{height:1.25rem}.tw-h-6{height:1.5rem}.tw-h-7{height:1.75rem}.tw-h-8{height:2rem}.tw-w-10{width:2.5rem}.tw-w-20{width:5rem}.tw-w-5{width:1.25rem}.tw-w-6{width:1.5rem}.tw-w-auto{width:auto}.tw-w-full{width:100%}.tw-min-w-16{min-width:4rem}.tw-min-w-full{min-width:100%}.tw-max-w-lg{max-width:32rem}.tw-max-w-md{max-width:28rem}.tw-max-w-sm{max-width:24rem}.tw-flex-1{flex:1 1 0%}.tw-cursor-pointer{cursor:pointer}.tw-list-none{list-style-type:none}.tw-flex-wrap{flex-wrap:wrap}.tw-items-center{align-items:center}.tw-justify-start{justify-content:flex-start}.tw-justify-center{justify-content:center}.tw-justify-between{justify-content:space-between}.tw-divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-bottom-width:calc(1px*var(--tw-divide-y-reverse));border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)))}.tw-divide-gray-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(229 231 235/var(--tw-divide-opacity,1))}.tw-divide-gray-300>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(209 213 219/var(--tw-divide-opacity,1))}.tw-self-center{align-self:center}.tw-overflow-hidden{overflow:hidden}.tw-whitespace-nowrap{white-space:nowrap}.tw-rounded{border-radius:.25rem}.tw-rounded-full{border-radius:9999px}.tw-rounded-lg{border-radius:.5rem}.tw-rounded-md{border-radius:.375rem}.tw-rounded-sm{border-radius:.125rem}.tw-border-2{border-width:2px}.tw-border-b{border-bottom-width:1px}.tw-border-b-2{border-bottom-width:2px}.tw-border-l-4{border-left-width:4px}.tw-border-r-4{border-right-width:4px}.tw-border-t{border-top-width:1px}.tw-border-t-2{border-top-width:2px}.tw-border-amber-400{--tw-border-opacity:1;border-color:rgb(251 191 36/var(--tw-border-opacity,1))}.tw-border-blue-500{--tw-border-opacity:1;border-color:rgb(59 130 246/var(--tw-border-opacity,1))}.tw-border-gray-200{--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity,1))}.tw-border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity,1))}.tw-border-gray-400{--tw-border-opacity:1;border-color:rgb(156 163 175/var(--tw-border-opacity,1))}.tw-border-gray-500{--tw-border-opacity:1;border-color:rgb(107 114 128/var(--tw-border-opacity,1))}.tw-border-gray-600{--tw-border-opacity:1;border-color:rgb(75 85 99/var(--tw-border-opacity,1))}.tw-border-green-500{--tw-border-opacity:1;border-color:rgb(16 185 129/var(--tw-border-opacity,1))}.tw-border-grey-light{--tw-border-opacity:1;border-color:rgb(218 225 231/var(--tw-border-opacity,1))}.tw-border-grey-lighter{--tw-border-opacity:1;border-color:rgb(241 245 248/var(--tw-border-opacity,1))}.tw-border-lime-500{--tw-border-opacity:1;border-color:rgb(132 204 22/var(--tw-border-opacity,1))}.tw-border-orange-500{--tw-border-opacity:1;border-color:rgb(249 115 22/var(--tw-border-opacity,1))}.tw-border-red-400{--tw-border-opacity:1;border-color:rgb(248 113 113/var(--tw-border-opacity,1))}.tw-border-red-500{--tw-border-opacity:1;border-color:rgb(239 68 68/var(--tw-border-opacity,1))}.tw-border-red-600{--tw-border-opacity:1;border-color:rgb(220 38 38/var(--tw-border-opacity,1))}.tw-border-red-lighter{--tw-border-opacity:1;border-color:rgb(249 172 170/var(--tw-border-opacity,1))}.tw-border-teal-400{--tw-border-opacity:1;border-color:rgb(45 212 191/var(--tw-border-opacity,1))}.tw-border-b-gray-200{--tw-border-opacity:1;border-bottom-color:rgb(229 231 235/var(--tw-border-opacity,1))}.tw-bg-amber-400{--tw-bg-opacity:1;background-color:rgb(251 191 36/var(--tw-bg-opacity,1))}.tw-bg-blue-100{--tw-bg-opacity:1;background-color:rgb(219 234 254/var(--tw-bg-opacity,1))}.tw-bg-blue-50{--tw-bg-opacity:1;background-color:rgb(239 246 255/var(--tw-bg-opacity,1))}.tw-bg-blue-lightest{--tw-bg-opacity:1;background-color:rgb(239 248 255/var(--tw-bg-opacity,1))}.tw-bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity,1))}.tw-bg-gray-200{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity,1))}.tw-bg-gray-300{--tw-bg-opacity:1;background-color:rgb(209 213 219/var(--tw-bg-opacity,1))}.tw-bg-gray-400{--tw-bg-opacity:1;background-color:rgb(156 163 175/var(--tw-bg-opacity,1))}.tw-bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity,1))}.tw-bg-gray-500{--tw-bg-opacity:1;background-color:rgb(107 114 128/var(--tw-bg-opacity,1))}.tw-bg-gray-700{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity,1))}.tw-bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity,1))}.tw-bg-green-100{--tw-bg-opacity:1;background-color:rgb(209 250 229/var(--tw-bg-opacity,1))}.tw-bg-green-50{--tw-bg-opacity:1;background-color:rgb(236 253 245/var(--tw-bg-opacity,1))}.tw-bg-grey-lighter{--tw-bg-opacity:1;background-color:rgb(241 245 248/var(--tw-bg-opacity,1))}.tw-bg-lime-500{--tw-bg-opacity:1;background-color:rgb(132 204 22/var(--tw-bg-opacity,1))}.tw-bg-orange-100{--tw-bg-opacity:1;background-color:rgb(255 237 213/var(--tw-bg-opacity,1))}.tw-bg-pink-50{--tw-bg-opacity:1;background-color:rgb(253 242 248/var(--tw-bg-opacity,1))}.tw-bg-red-100{--tw-bg-opacity:1;background-color:rgb(254 226 226/var(--tw-bg-opacity,1))}.tw-bg-red-400{--tw-bg-opacity:1;background-color:rgb(248 113 113/var(--tw-bg-opacity,1))}.tw-bg-red-50{--tw-bg-opacity:1;background-color:rgb(254 242 242/var(--tw-bg-opacity,1))}.tw-bg-red-600{--tw-bg-opacity:1;background-color:rgb(220 38 38/var(--tw-bg-opacity,1))}.tw-bg-teal-400{--tw-bg-opacity:1;background-color:rgb(45 212 191/var(--tw-bg-opacity,1))}.tw-bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.tw-bg-yellow-50{--tw-bg-opacity:1;background-color:rgb(255 251 235/var(--tw-bg-opacity,1))}.tw-p-0{padding:0}.tw-p-1{padding:.25rem}.tw-p-2{padding:.5rem}.tw-p-4{padding:1rem}.tw-p-5{padding:1.25rem}.tw-p-6{padding:1.5rem}.tw-px-1{padding-left:.25rem;padding-right:.25rem}.tw-px-1\.5{padding-left:.375rem;padding-right:.375rem}.tw-px-10{padding-left:2.5rem;padding-right:2.5rem}.tw-px-2{padding-left:.5rem;padding-right:.5rem}.tw-px-3{padding-left:.75rem;padding-right:.75rem}.tw-px-4{padding-left:1rem;padding-right:1rem}.tw-px-6{padding-left:1.5rem;padding-right:1.5rem}.tw-px-8{padding-left:2rem;padding-right:2rem}.tw-py-0{padding-bottom:0;padding-top:0}.tw-py-0\.5{padding-bottom:.125rem;padding-top:.125rem}.tw-py-1{padding-bottom:.25rem;padding-top:.25rem}.tw-py-2{padding-bottom:.5rem;padding-top:.5rem}.tw-py-20{padding-bottom:5rem;padding-top:5rem}.tw-py-4{padding-bottom:1rem;padding-top:1rem}.tw-py-5{padding-bottom:1.25rem;padding-top:1.25rem}.tw-py-6{padding-bottom:1.5rem;padding-top:1.5rem}.tw-pb-2{padding-bottom:.5rem}.tw-pb-8{padding-bottom:2rem}.tw-pl-0{padding-left:0}.tw-pl-16{padding-left:4rem}.tw-pl-2{padding-left:.5rem}.tw-pl-4{padding-left:1rem}.tw-pr-4{padding-right:1rem}.tw-pt-0{padding-top:0}.tw-pt-4{padding-top:1rem}.tw-pt-6{padding-top:1.5rem}.tw-text-center{text-align:center}.tw-text-right{text-align:right}.tw-align-middle{vertical-align:middle}.tw-font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.tw-text-base{font-size:1rem;line-height:1.5rem}.tw-text-lg{font-size:1.125rem;line-height:1.75rem}.tw-text-sm{font-size:.875rem;line-height:1.25rem}.tw-text-xl{font-size:1.25rem;line-height:1.75rem}.tw-text-xs{font-size:.75rem;line-height:1rem}.tw-font-bold{font-weight:700}.tw-font-medium{font-weight:500}.tw-font-normal{font-weight:400}.tw-font-semibold{font-weight:600}.tw-uppercase{text-transform:uppercase}.tw-italic{font-style:italic}.tw-slashed-zero{--tw-slashed-zero:slashed-zero}.tw-lining-nums,.tw-slashed-zero{font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.tw-lining-nums{--tw-numeric-figure:lining-nums}.tw-tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.tw-leading-5{line-height:1.25rem}.tw-leading-6{line-height:1.5rem}.tw-leading-7{line-height:1.75rem}.tw-leading-8{line-height:2rem}.tw-leading-tight{line-height:1.25}.tw-text-black{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity,1))}.tw-text-blue-600{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity,1))}.tw-text-blue-700{--tw-text-opacity:1;color:rgb(29 78 216/var(--tw-text-opacity,1))}.tw-text-blue-dark{--tw-text-opacity:1;color:rgb(39 121 189/var(--tw-text-opacity,1))}.tw-text-gray-100{--tw-text-opacity:1;color:rgb(243 244 246/var(--tw-text-opacity,1))}.tw-text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity,1))}.tw-text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity,1))}.tw-text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity,1))}.tw-text-gray-800{--tw-text-opacity:1;color:rgb(31 41 55/var(--tw-text-opacity,1))}.tw-text-gray-900{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity,1))}.tw-text-green-500{--tw-text-opacity:1;color:rgb(16 185 129/var(--tw-text-opacity,1))}.tw-text-green-600{--tw-text-opacity:1;color:rgb(5 150 105/var(--tw-text-opacity,1))}.tw-text-green-700{--tw-text-opacity:1;color:rgb(4 120 87/var(--tw-text-opacity,1))}.tw-text-green-dark{--tw-text-opacity:1;color:rgb(31 157 85/var(--tw-text-opacity,1))}.tw-text-grey-dark{--tw-text-opacity:1;color:rgb(135 149 161/var(--tw-text-opacity,1))}.tw-text-grey-darker{--tw-text-opacity:1;color:rgb(96 111 123/var(--tw-text-opacity,1))}.tw-text-grey-darkest{--tw-text-opacity:1;color:rgb(61 72 82/var(--tw-text-opacity,1))}.tw-text-orange-500{--tw-text-opacity:1;color:rgb(249 115 22/var(--tw-text-opacity,1))}.tw-text-orange-700{--tw-text-opacity:1;color:rgb(194 65 12/var(--tw-text-opacity,1))}.tw-text-pink-700{--tw-text-opacity:1;color:rgb(190 24 93/var(--tw-text-opacity,1))}.tw-text-red-500{--tw-text-opacity:1;color:rgb(239 68 68/var(--tw-text-opacity,1))}.tw-text-red-600{--tw-text-opacity:1;color:rgb(220 38 38/var(--tw-text-opacity,1))}.tw-text-red-700{--tw-text-opacity:1;color:rgb(185 28 28/var(--tw-text-opacity,1))}.tw-text-red-lighter{--tw-text-opacity:1;color:rgb(249 172 170/var(--tw-text-opacity,1))}.tw-line-through{text-decoration-line:line-through}.tw-opacity-40{opacity:.4}.tw-shadow{--tw-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px -1px rgba(0,0,0,.1);--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)}.tw-shadow,.tw-shadow-lg{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.tw-shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.tw-shadow-md{--tw-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -2px rgba(0,0,0,.1);--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color)}.tw-shadow-md,.tw-shadow-sm{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.tw-shadow-sm{--tw-shadow:0 1px 2px 0 rgba(0,0,0,.05);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.tw-ring-1{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.tw-ring-inset{--tw-ring-inset:inset}.tw-ring-blue-700\/10{--tw-ring-color:rgba(29,78,216,.1)}.tw-ring-gray-300{--tw-ring-opacity:1;--tw-ring-color:rgb(209 213 219/var(--tw-ring-opacity,1))}.tw-ring-gray-500\/10{--tw-ring-color:hsla(220,9%,46%,.1)}.tw-ring-gray-800\/10{--tw-ring-color:rgba(31,41,55,.1)}.tw-ring-gray-800\/50{--tw-ring-color:rgba(31,41,55,.5)}.tw-ring-green-600\/20{--tw-ring-color:rgba(5,150,105,.2)}.tw-ring-pink-700\/10{--tw-ring-color:rgba(190,24,93,.1)}.tw-ring-red-600\/10{--tw-ring-color:rgba(220,38,38,.1)}.tw-ring-yellow-600\/20{--tw-ring-color:rgba(217,119,6,.2)}.\[a-f0-9\:\\\.\\-\]{a-f0-9:\.\-}.\[id\:\%d\]{id:%d}.hover\:tw-border-gray-800:hover{--tw-border-opacity:1;border-color:rgb(31 41 55/var(--tw-border-opacity,1))}.hover\:tw-bg-gray-50:hover{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity,1))}.hover\:tw-bg-grey-lighter:hover{--tw-bg-opacity:1;background-color:rgb(241 245 248/var(--tw-bg-opacity,1))}.hover\:tw-no-underline:hover{text-decoration-line:none}.hover\:tw-opacity-80:hover{opacity:.8}@media (min-width:640px){.sm\:tw-p-6{padding:1.5rem}.sm\:tw-px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:tw-pl-0{padding-left:0}}@media (min-width:768px){.md\:tw-mt-0{margin-top:0}.md\:tw-inline{display:inline}.md\:tw-table-cell{display:table-cell}.md\:tw-text-left{text-align:left}.md\:tw-text-right{text-align:right}}@media (min-width:1024px){.lg\:tw-inline-block{display:inline-block}.lg\:tw-inline{display:inline}.lg\:tw-table-cell{display:table-cell}} diff --git a/public/favicon.ico.dist b/public/favicon.ico.dist new file mode 100644 index 000000000..a8317f531 Binary files /dev/null and b/public/favicon.ico.dist differ diff --git a/public/fonts/FontAwesome.otf b/public/fonts/FontAwesome.otf new file mode 100644 index 000000000..401ec0f36 Binary files /dev/null and b/public/fonts/FontAwesome.otf differ diff --git a/public/fonts/fontawesome-webfont.eot b/public/fonts/fontawesome-webfont.eot new file mode 100644 index 000000000..e9f60ca95 Binary files /dev/null and b/public/fonts/fontawesome-webfont.eot differ diff --git a/public/fonts/fontawesome-webfont.svg b/public/fonts/fontawesome-webfont.svg new file mode 100644 index 000000000..855c845e5 --- /dev/null +++ b/public/fonts/fontawesome-webfont.svg @@ -0,0 +1,2671 @@ + + + + +Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 + By ,,, +Copyright Dave Gandy 2016. All rights reserved. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/fonts/fontawesome-webfont.ttf b/public/fonts/fontawesome-webfont.ttf new file mode 100644 index 000000000..35acda2fa Binary files /dev/null and b/public/fonts/fontawesome-webfont.ttf differ diff --git a/public/fonts/fontawesome-webfont.woff b/public/fonts/fontawesome-webfont.woff new file mode 100644 index 000000000..400014a4b Binary files /dev/null and b/public/fonts/fontawesome-webfont.woff differ diff --git a/public/fonts/fontawesome-webfont.woff2 b/public/fonts/fontawesome-webfont.woff2 new file mode 100644 index 000000000..4d13fc604 Binary files /dev/null and b/public/fonts/fontawesome-webfont.woff2 differ diff --git a/public/fonts/glyphicons-halflings-regular.eot b/public/fonts/glyphicons-halflings-regular.eot new file mode 100644 index 000000000..b93a4953f Binary files /dev/null and b/public/fonts/glyphicons-halflings-regular.eot differ diff --git a/public/fonts/glyphicons-halflings-regular.svg b/public/fonts/glyphicons-halflings-regular.svg new file mode 100644 index 000000000..94fb5490a --- /dev/null +++ b/public/fonts/glyphicons-halflings-regular.svg @@ -0,0 +1,288 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/fonts/glyphicons-halflings-regular.ttf b/public/fonts/glyphicons-halflings-regular.ttf new file mode 100644 index 000000000..1413fc609 Binary files /dev/null and b/public/fonts/glyphicons-halflings-regular.ttf differ diff --git a/public/fonts/glyphicons-halflings-regular.woff b/public/fonts/glyphicons-halflings-regular.woff new file mode 100644 index 000000000..9e612858f Binary files /dev/null and b/public/fonts/glyphicons-halflings-regular.woff differ diff --git a/public/fonts/glyphicons-halflings-regular.woff2 b/public/fonts/glyphicons-halflings-regular.woff2 new file mode 100644 index 000000000..64539b54c Binary files /dev/null and b/public/fonts/glyphicons-halflings-regular.woff2 differ diff --git a/public/images/22x22/apply.png b/public/images/22x22/apply.png deleted file mode 100644 index c83f7cb1f..000000000 Binary files a/public/images/22x22/apply.png and /dev/null differ diff --git a/public/images/22x22/face-crying.png b/public/images/22x22/face-crying.png deleted file mode 100644 index 573250366..000000000 Binary files a/public/images/22x22/face-crying.png and /dev/null differ diff --git a/public/images/22x22/face-smile-big.png b/public/images/22x22/face-smile-big.png deleted file mode 100644 index e9022031f..000000000 Binary files a/public/images/22x22/face-smile-big.png and /dev/null differ diff --git a/public/images/22x22/help.png b/public/images/22x22/help.png deleted file mode 100644 index 86b64075f..000000000 Binary files a/public/images/22x22/help.png and /dev/null differ diff --git a/public/images/22x22/im-user-offline.png b/public/images/22x22/im-user-offline.png deleted file mode 100644 index 49ec47cd7..000000000 Binary files a/public/images/22x22/im-user-offline.png and /dev/null differ diff --git a/public/images/22x22/im-user.png b/public/images/22x22/im-user.png deleted file mode 100644 index 69de2e48f..000000000 Binary files a/public/images/22x22/im-user.png and /dev/null differ diff --git a/public/images/22x22/never.png b/public/images/22x22/never.png deleted file mode 100644 index b72e1a3ac..000000000 Binary files a/public/images/22x22/never.png and /dev/null differ diff --git a/public/images/22x22/no-note.png b/public/images/22x22/no-note.png deleted file mode 100644 index 1c50b6b67..000000000 Binary files a/public/images/22x22/no-note.png and /dev/null differ diff --git a/public/images/22x22/no.png b/public/images/22x22/no.png deleted file mode 100644 index 5492295e7..000000000 Binary files a/public/images/22x22/no.png and /dev/null differ diff --git a/public/images/22x22/note.png b/public/images/22x22/note.png deleted file mode 100644 index 84d15cb77..000000000 Binary files a/public/images/22x22/note.png and /dev/null differ diff --git a/public/images/22x22/player_stop.png b/public/images/22x22/player_stop.png deleted file mode 100644 index 2a7e1bf24..000000000 Binary files a/public/images/22x22/player_stop.png and /dev/null differ diff --git a/public/images/22x22/presentation.png b/public/images/22x22/presentation.png deleted file mode 100644 index a7894a00d..000000000 Binary files a/public/images/22x22/presentation.png and /dev/null differ diff --git a/public/images/22x22/unknown.png b/public/images/22x22/unknown.png deleted file mode 100644 index 86b64075f..000000000 Binary files a/public/images/22x22/unknown.png and /dev/null differ diff --git a/public/images/22x22/video.png b/public/images/22x22/video.png deleted file mode 100644 index 462612261..000000000 Binary files a/public/images/22x22/video.png and /dev/null differ diff --git a/public/images/22x22/waiting.png b/public/images/22x22/waiting.png deleted file mode 100644 index 1c6cb4e52..000000000 Binary files a/public/images/22x22/waiting.png and /dev/null differ diff --git a/public/images/22x22/yes.png b/public/images/22x22/yes.png deleted file mode 100644 index 050dcc53b..000000000 Binary files a/public/images/22x22/yes.png and /dev/null differ diff --git a/public/images/300x1.png b/public/images/300x1.png deleted file mode 100644 index f4d814fe8..000000000 Binary files a/public/images/300x1.png and /dev/null differ diff --git a/public/images/32x32/check.png b/public/images/32x32/check.png deleted file mode 100644 index 8c53978bb..000000000 Binary files a/public/images/32x32/check.png and /dev/null differ diff --git a/public/images/32x32/cross.png b/public/images/32x32/cross.png deleted file mode 100644 index f9185251a..000000000 Binary files a/public/images/32x32/cross.png and /dev/null differ diff --git a/public/images/arrow-left-double.png b/public/images/arrow-left-double.png deleted file mode 100644 index a9d66123e..000000000 Binary files a/public/images/arrow-left-double.png and /dev/null differ diff --git a/public/images/arrow-left.png b/public/images/arrow-left.png deleted file mode 100644 index b56cfee03..000000000 Binary files a/public/images/arrow-left.png and /dev/null differ diff --git a/public/images/arrow-right-double.png b/public/images/arrow-right-double.png deleted file mode 100644 index 2702a3ddd..000000000 Binary files a/public/images/arrow-right-double.png and /dev/null differ diff --git a/public/images/arrow-right.png b/public/images/arrow-right.png deleted file mode 100644 index 0acee70bc..000000000 Binary files a/public/images/arrow-right.png and /dev/null differ diff --git a/public/images/arrow_down.gif b/public/images/arrow_down.gif deleted file mode 100644 index da059abde..000000000 Binary files a/public/images/arrow_down.gif and /dev/null differ diff --git a/public/images/arrow_up.gif b/public/images/arrow_up.gif deleted file mode 100644 index a5990e96a..000000000 Binary files a/public/images/arrow_up.gif and /dev/null differ diff --git a/public/images/background.jpg b/public/images/background.jpg deleted file mode 100644 index 4b16b3c59..000000000 Binary files a/public/images/background.jpg and /dev/null differ diff --git a/public/images/colorbox/border.png b/public/images/colorbox/border.png deleted file mode 100644 index f463a10d8..000000000 Binary files a/public/images/colorbox/border.png and /dev/null differ diff --git a/public/images/colorbox/controls.png b/public/images/colorbox/controls.png deleted file mode 100644 index dcfd6fb9f..000000000 Binary files a/public/images/colorbox/controls.png and /dev/null differ diff --git a/public/images/colorbox/loading.gif b/public/images/colorbox/loading.gif deleted file mode 100644 index b4695d811..000000000 Binary files a/public/images/colorbox/loading.gif and /dev/null differ diff --git a/public/images/colorbox/loading_background.png b/public/images/colorbox/loading_background.png deleted file mode 100644 index 6ae83e697..000000000 Binary files a/public/images/colorbox/loading_background.png and /dev/null differ diff --git a/public/images/colorbox/overlay.png b/public/images/colorbox/overlay.png deleted file mode 100644 index 53ea98f70..000000000 Binary files a/public/images/colorbox/overlay.png and /dev/null differ diff --git a/public/images/cut.png b/public/images/cut.png deleted file mode 100644 index f215d6f6b..000000000 Binary files a/public/images/cut.png and /dev/null differ diff --git a/public/images/door.png b/public/images/door.png deleted file mode 100644 index 369fc46ed..000000000 Binary files a/public/images/door.png and /dev/null differ diff --git a/public/images/icon_no.png b/public/images/icon_no.png deleted file mode 100644 index 4d84554de..000000000 Binary files a/public/images/icon_no.png and /dev/null differ diff --git a/public/images/icon_password.png b/public/images/icon_password.png deleted file mode 100644 index 04a05c1cb..000000000 Binary files a/public/images/icon_password.png and /dev/null differ diff --git a/public/images/icon_refresh.png b/public/images/icon_refresh.png deleted file mode 100644 index 1c026d32c..000000000 Binary files a/public/images/icon_refresh.png and /dev/null differ diff --git a/public/images/icon_unknown.png b/public/images/icon_unknown.png deleted file mode 100644 index 4ed65a97e..000000000 Binary files a/public/images/icon_unknown.png and /dev/null differ diff --git a/public/images/icon_yes.png b/public/images/icon_yes.png deleted file mode 100644 index 5b0f6a617..000000000 Binary files a/public/images/icon_yes.png and /dev/null differ diff --git a/public/images/image-missing.png b/public/images/image-missing.png index b46b8d5ed..e5cf86cb7 100644 Binary files a/public/images/image-missing.png and b/public/images/image-missing.png differ diff --git a/public/images/inconsistent1.gif b/public/images/inconsistent1.gif deleted file mode 100644 index b5612fe75..000000000 Binary files a/public/images/inconsistent1.gif and /dev/null differ diff --git a/public/images/inconsistent2.gif b/public/images/inconsistent2.gif deleted file mode 100644 index 657abe790..000000000 Binary files a/public/images/inconsistent2.gif and /dev/null differ diff --git a/public/images/inex-green-corner-153x168.gif b/public/images/inex-green-corner-153x168.gif deleted file mode 100644 index 326f0c8fd..000000000 Binary files a/public/images/inex-green-corner-153x168.gif and /dev/null differ diff --git a/public/images/inex-logo-325x165.png b/public/images/inex-logo-325x165.png new file mode 100644 index 000000000..e9b455c88 Binary files /dev/null and b/public/images/inex-logo-325x165.png differ diff --git a/public/images/inex-logo-600x228.jpg b/public/images/inex-logo-600x228.jpg new file mode 100644 index 000000000..fa2b4c8cc Binary files /dev/null and b/public/images/inex-logo-600x228.jpg differ diff --git a/public/images/ixp-manager-white.png b/public/images/ixp-manager-white.png new file mode 100644 index 000000000..fa0ac0fe9 Binary files /dev/null and b/public/images/ixp-manager-white.png differ diff --git a/public/images/ixp-manager.png b/public/images/ixp-manager.png new file mode 100644 index 000000000..cc7c47a83 Binary files /dev/null and b/public/images/ixp-manager.png differ diff --git a/public/images/ixpmanager-dot-i.gif b/public/images/ixpmanager-dot-i.gif deleted file mode 100644 index a8b601fcd..000000000 Binary files a/public/images/ixpmanager-dot-i.gif and /dev/null differ diff --git a/public/images/joomla-admin/acroread.png b/public/images/joomla-admin/acroread.png deleted file mode 100644 index a16a821b3..000000000 Binary files a/public/images/joomla-admin/acroread.png and /dev/null differ diff --git a/public/images/joomla-admin/addedit.png b/public/images/joomla-admin/addedit.png deleted file mode 100644 index 70888c4a0..000000000 Binary files a/public/images/joomla-admin/addedit.png and /dev/null differ diff --git a/public/images/joomla-admin/background.jpg b/public/images/joomla-admin/background.jpg deleted file mode 100644 index 9df2091d0..000000000 Binary files a/public/images/joomla-admin/background.jpg and /dev/null differ diff --git a/public/images/joomla-admin/backup.png b/public/images/joomla-admin/backup.png deleted file mode 100644 index 0c936e663..000000000 Binary files a/public/images/joomla-admin/backup.png and /dev/null differ diff --git a/public/images/joomla-admin/browser.png b/public/images/joomla-admin/browser.png deleted file mode 100644 index 7f6e767d1..000000000 Binary files a/public/images/joomla-admin/browser.png and /dev/null differ diff --git a/public/images/joomla-admin/cabinets.png b/public/images/joomla-admin/cabinets.png deleted file mode 100644 index a742a7344..000000000 Binary files a/public/images/joomla-admin/cabinets.png and /dev/null differ diff --git a/public/images/joomla-admin/categories.png b/public/images/joomla-admin/categories.png deleted file mode 100644 index 98d8f7fa8..000000000 Binary files a/public/images/joomla-admin/categories.png and /dev/null differ diff --git a/public/images/joomla-admin/change-log.png b/public/images/joomla-admin/change-log.png deleted file mode 100644 index b97edd4d1..000000000 Binary files a/public/images/joomla-admin/change-log.png and /dev/null differ diff --git a/public/images/joomla-admin/checkin.png b/public/images/joomla-admin/checkin.png deleted file mode 100644 index 7acd11b15..000000000 Binary files a/public/images/joomla-admin/checkin.png and /dev/null differ diff --git a/public/images/joomla-admin/config.png b/public/images/joomla-admin/config.png deleted file mode 100644 index af81c2738..000000000 Binary files a/public/images/joomla-admin/config.png and /dev/null differ diff --git a/public/images/joomla-admin/console.png b/public/images/joomla-admin/console.png deleted file mode 100644 index d5cbd5fc4..000000000 Binary files a/public/images/joomla-admin/console.png and /dev/null differ diff --git a/public/images/joomla-admin/cpanel.png b/public/images/joomla-admin/cpanel.png deleted file mode 100644 index a284986f4..000000000 Binary files a/public/images/joomla-admin/cpanel.png and /dev/null differ diff --git a/public/images/joomla-admin/dbrestore.png b/public/images/joomla-admin/dbrestore.png deleted file mode 100644 index b9163a9c8..000000000 Binary files a/public/images/joomla-admin/dbrestore.png and /dev/null differ diff --git a/public/images/joomla-admin/drive-optical.png b/public/images/joomla-admin/drive-optical.png deleted file mode 100644 index db87b4573..000000000 Binary files a/public/images/joomla-admin/drive-optical.png and /dev/null differ diff --git a/public/images/joomla-admin/drupalicon.png b/public/images/joomla-admin/drupalicon.png deleted file mode 100644 index 57c89f7de..000000000 Binary files a/public/images/joomla-admin/drupalicon.png and /dev/null differ diff --git a/public/images/joomla-admin/email.png b/public/images/joomla-admin/email.png deleted file mode 100644 index d53e62064..000000000 Binary files a/public/images/joomla-admin/email.png and /dev/null differ diff --git a/public/images/joomla-admin/frontpage.png b/public/images/joomla-admin/frontpage.png deleted file mode 100644 index 489809911..000000000 Binary files a/public/images/joomla-admin/frontpage.png and /dev/null differ diff --git a/public/images/joomla-admin/gaim.png b/public/images/joomla-admin/gaim.png deleted file mode 100644 index b44b969f0..000000000 Binary files a/public/images/joomla-admin/gaim.png and /dev/null differ diff --git a/public/images/joomla-admin/generic.png b/public/images/joomla-admin/generic.png deleted file mode 100644 index 1def629d7..000000000 Binary files a/public/images/joomla-admin/generic.png and /dev/null differ diff --git a/public/images/joomla-admin/header_bg.png b/public/images/joomla-admin/header_bg.png deleted file mode 100644 index cdc410fd6..000000000 Binary files a/public/images/joomla-admin/header_bg.png and /dev/null differ diff --git a/public/images/joomla-admin/header_text.png b/public/images/joomla-admin/header_text.png deleted file mode 100644 index 0a4c00b32..000000000 Binary files a/public/images/joomla-admin/header_text.png and /dev/null differ diff --git a/public/images/joomla-admin/help_index.png b/public/images/joomla-admin/help_index.png deleted file mode 100644 index 418e1daf8..000000000 Binary files a/public/images/joomla-admin/help_index.png and /dev/null differ diff --git a/public/images/joomla-admin/impressions.png b/public/images/joomla-admin/impressions.png deleted file mode 100644 index 1df0cb41b..000000000 Binary files a/public/images/joomla-admin/impressions.png and /dev/null differ diff --git a/public/images/joomla-admin/inbox.png b/public/images/joomla-admin/inbox.png deleted file mode 100644 index 197887902..000000000 Binary files a/public/images/joomla-admin/inbox.png and /dev/null differ diff --git a/public/images/joomla-admin/insert-link.png b/public/images/joomla-admin/insert-link.png deleted file mode 100644 index e7eb9611a..000000000 Binary files a/public/images/joomla-admin/insert-link.png and /dev/null differ diff --git a/public/images/joomla-admin/install.png b/public/images/joomla-admin/install.png deleted file mode 100644 index 43530b80f..000000000 Binary files a/public/images/joomla-admin/install.png and /dev/null differ diff --git a/public/images/joomla-admin/interface.png b/public/images/joomla-admin/interface.png deleted file mode 100644 index 3bf4c8a09..000000000 Binary files a/public/images/joomla-admin/interface.png and /dev/null differ diff --git a/public/images/joomla-admin/jabber_protocol.png b/public/images/joomla-admin/jabber_protocol.png deleted file mode 100644 index 0964749c5..000000000 Binary files a/public/images/joomla-admin/jabber_protocol.png and /dev/null differ diff --git a/public/images/joomla-admin/joomla.png b/public/images/joomla-admin/joomla.png deleted file mode 100644 index 976ee1adc..000000000 Binary files a/public/images/joomla-admin/joomla.png and /dev/null differ diff --git a/public/images/joomla-admin/kcmdrkonqi.png b/public/images/joomla-admin/kcmdrkonqi.png deleted file mode 100644 index 54d7ce45f..000000000 Binary files a/public/images/joomla-admin/kcmdrkonqi.png and /dev/null differ diff --git a/public/images/joomla-admin/kdmconfig.png b/public/images/joomla-admin/kdmconfig.png deleted file mode 100644 index d8b241ca9..000000000 Binary files a/public/images/joomla-admin/kdmconfig.png and /dev/null differ diff --git a/public/images/joomla-admin/keditbookmarks.png b/public/images/joomla-admin/keditbookmarks.png deleted file mode 100644 index d1f5e91cc..000000000 Binary files a/public/images/joomla-admin/keditbookmarks.png and /dev/null differ diff --git a/public/images/joomla-admin/kfm_home.png b/public/images/joomla-admin/kfm_home.png deleted file mode 100644 index d08e6ab00..000000000 Binary files a/public/images/joomla-admin/kfm_home.png and /dev/null differ diff --git a/public/images/joomla-admin/knetworkmanager.png b/public/images/joomla-admin/knetworkmanager.png deleted file mode 100644 index 8306829c6..000000000 Binary files a/public/images/joomla-admin/knetworkmanager.png and /dev/null differ diff --git a/public/images/joomla-admin/kontact.png b/public/images/joomla-admin/kontact.png deleted file mode 100644 index d97a8f66a..000000000 Binary files a/public/images/joomla-admin/kontact.png and /dev/null differ diff --git a/public/images/joomla-admin/kpf.png b/public/images/joomla-admin/kpf.png deleted file mode 100644 index 8e10d8d08..000000000 Binary files a/public/images/joomla-admin/kpf.png and /dev/null differ diff --git a/public/images/joomla-admin/krdc.png b/public/images/joomla-admin/krdc.png deleted file mode 100644 index 1e7d236e7..000000000 Binary files a/public/images/joomla-admin/krdc.png and /dev/null differ diff --git a/public/images/joomla-admin/krfb.png b/public/images/joomla-admin/krfb.png deleted file mode 100644 index 19c31a71f..000000000 Binary files a/public/images/joomla-admin/krfb.png and /dev/null differ diff --git a/public/images/joomla-admin/ksysguard.png b/public/images/joomla-admin/ksysguard.png deleted file mode 100644 index b636ff0e6..000000000 Binary files a/public/images/joomla-admin/ksysguard.png and /dev/null differ diff --git a/public/images/joomla-admin/langmanager.png b/public/images/joomla-admin/langmanager.png deleted file mode 100644 index f56a28874..000000000 Binary files a/public/images/joomla-admin/langmanager.png and /dev/null differ diff --git a/public/images/joomla-admin/login.gif b/public/images/joomla-admin/login.gif deleted file mode 100644 index d428eea96..000000000 Binary files a/public/images/joomla-admin/login.gif and /dev/null differ diff --git a/public/images/joomla-admin/massemail.png b/public/images/joomla-admin/massemail.png deleted file mode 100644 index dc83fb05a..000000000 Binary files a/public/images/joomla-admin/massemail.png and /dev/null differ diff --git a/public/images/joomla-admin/mediamanager.png b/public/images/joomla-admin/mediamanager.png deleted file mode 100644 index 64c8b0a51..000000000 Binary files a/public/images/joomla-admin/mediamanager.png and /dev/null differ diff --git a/public/images/joomla-admin/menu.png b/public/images/joomla-admin/menu.png deleted file mode 100644 index 54299cc8c..000000000 Binary files a/public/images/joomla-admin/menu.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/acroread.png b/public/images/joomla-admin/menu/acroread.png deleted file mode 100644 index e8efdbfa2..000000000 Binary files a/public/images/joomla-admin/menu/acroread.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/add_section.png b/public/images/joomla-admin/menu/add_section.png deleted file mode 100644 index b4b802d9e..000000000 Binary files a/public/images/joomla-admin/menu/add_section.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/arrow.png b/public/images/joomla-admin/menu/arrow.png deleted file mode 100644 index b7e9eb74f..000000000 Binary files a/public/images/joomla-admin/menu/arrow.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/backup.png b/public/images/joomla-admin/menu/backup.png deleted file mode 100644 index 6963673f7..000000000 Binary files a/public/images/joomla-admin/menu/backup.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/blank.png b/public/images/joomla-admin/menu/blank.png deleted file mode 100644 index b1dcba7b4..000000000 Binary files a/public/images/joomla-admin/menu/blank.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/cabinets.png b/public/images/joomla-admin/menu/cabinets.png deleted file mode 100644 index 2771c8543..000000000 Binary files a/public/images/joomla-admin/menu/cabinets.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/categories.png b/public/images/joomla-admin/menu/categories.png deleted file mode 100644 index 5b5b80c27..000000000 Binary files a/public/images/joomla-admin/menu/categories.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/checkin.png b/public/images/joomla-admin/menu/checkin.png deleted file mode 100644 index fd49d15f4..000000000 Binary files a/public/images/joomla-admin/menu/checkin.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/checkinbw.png b/public/images/joomla-admin/menu/checkinbw.png deleted file mode 100644 index c6d77ffd0..000000000 Binary files a/public/images/joomla-admin/menu/checkinbw.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/component.png b/public/images/joomla-admin/menu/component.png deleted file mode 100644 index d70eb0c57..000000000 Binary files a/public/images/joomla-admin/menu/component.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/config.png b/public/images/joomla-admin/menu/config.png deleted file mode 100644 index 90cf753f9..000000000 Binary files a/public/images/joomla-admin/menu/config.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/connection.png b/public/images/joomla-admin/menu/connection.png deleted file mode 100644 index 9465e0e04..000000000 Binary files a/public/images/joomla-admin/menu/connection.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/console.png b/public/images/joomla-admin/menu/console.png deleted file mode 100644 index e5655f523..000000000 Binary files a/public/images/joomla-admin/menu/console.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/content.png b/public/images/joomla-admin/menu/content.png deleted file mode 100644 index da6c77b18..000000000 Binary files a/public/images/joomla-admin/menu/content.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/contents.png b/public/images/joomla-admin/menu/contents.png deleted file mode 100644 index e2a44ecae..000000000 Binary files a/public/images/joomla-admin/menu/contents.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/controlpanel.png b/public/images/joomla-admin/menu/controlpanel.png deleted file mode 100644 index f6070017f..000000000 Binary files a/public/images/joomla-admin/menu/controlpanel.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/credits.png b/public/images/joomla-admin/menu/credits.png deleted file mode 100644 index 9490216f1..000000000 Binary files a/public/images/joomla-admin/menu/credits.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/cust-kit.png b/public/images/joomla-admin/menu/cust-kit.png deleted file mode 100644 index 7e8d7017f..000000000 Binary files a/public/images/joomla-admin/menu/cust-kit.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/db.png b/public/images/joomla-admin/menu/db.png deleted file mode 100644 index 1de1faf2e..000000000 Binary files a/public/images/joomla-admin/menu/db.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/document.png b/public/images/joomla-admin/menu/document.png deleted file mode 100644 index 7595818f1..000000000 Binary files a/public/images/joomla-admin/menu/document.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/drive-optical.png b/public/images/joomla-admin/menu/drive-optical.png deleted file mode 100644 index e3d929b6a..000000000 Binary files a/public/images/joomla-admin/menu/drive-optical.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/edit.png b/public/images/joomla-admin/menu/edit.png deleted file mode 100644 index 983190f41..000000000 Binary files a/public/images/joomla-admin/menu/edit.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/globe1.png b/public/images/joomla-admin/menu/globe1.png deleted file mode 100644 index 7edbac74a..000000000 Binary files a/public/images/joomla-admin/menu/globe1.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/globe2.png b/public/images/joomla-admin/menu/globe2.png deleted file mode 100644 index eb33934f4..000000000 Binary files a/public/images/joomla-admin/menu/globe2.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/globe3.png b/public/images/joomla-admin/menu/globe3.png deleted file mode 100644 index 37b2a4bdd..000000000 Binary files a/public/images/joomla-admin/menu/globe3.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/globe4.png b/public/images/joomla-admin/menu/globe4.png deleted file mode 100644 index 8c100a47d..000000000 Binary files a/public/images/joomla-admin/menu/globe4.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/gpg.png b/public/images/joomla-admin/menu/gpg.png deleted file mode 100644 index 04a05c1cb..000000000 Binary files a/public/images/joomla-admin/menu/gpg.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/help.png b/public/images/joomla-admin/menu/help.png deleted file mode 100644 index 2c079fed2..000000000 Binary files a/public/images/joomla-admin/menu/help.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/home.png b/public/images/joomla-admin/menu/home.png deleted file mode 100644 index 44d012f49..000000000 Binary files a/public/images/joomla-admin/menu/home.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/icon_film.png b/public/images/joomla-admin/menu/icon_film.png deleted file mode 100644 index 6ada60048..000000000 Binary files a/public/images/joomla-admin/menu/icon_film.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/icon_reel.png b/public/images/joomla-admin/menu/icon_reel.png deleted file mode 100644 index 0b4c8fe04..000000000 Binary files a/public/images/joomla-admin/menu/icon_reel.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/index.html b/public/images/joomla-admin/menu/index.html deleted file mode 100644 index 0e44bd0eb..000000000 --- a/public/images/joomla-admin/menu/index.html +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/public/images/joomla-admin/menu/insert-link.png b/public/images/joomla-admin/menu/insert-link.png deleted file mode 100644 index 05c82c628..000000000 Binary files a/public/images/joomla-admin/menu/insert-link.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/install.png b/public/images/joomla-admin/menu/install.png deleted file mode 100644 index 8d639b549..000000000 Binary files a/public/images/joomla-admin/menu/install.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/interface.png b/public/images/joomla-admin/menu/interface.png deleted file mode 100644 index b2a9d8a43..000000000 Binary files a/public/images/joomla-admin/menu/interface.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/joomla_16x16.png b/public/images/joomla-admin/menu/joomla_16x16.png deleted file mode 100644 index 8782401ee..000000000 Binary files a/public/images/joomla-admin/menu/joomla_16x16.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/kontact.png b/public/images/joomla-admin/menu/kontact.png deleted file mode 100644 index f5b04771b..000000000 Binary files a/public/images/joomla-admin/menu/kontact.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/language.png b/public/images/joomla-admin/menu/language.png deleted file mode 100644 index 1b2aee8ef..000000000 Binary files a/public/images/joomla-admin/menu/language.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/license.png b/public/images/joomla-admin/menu/license.png deleted file mode 100644 index 21adc2bef..000000000 Binary files a/public/images/joomla-admin/menu/license.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/mail.png b/public/images/joomla-admin/menu/mail.png deleted file mode 100644 index 63d6e13a8..000000000 Binary files a/public/images/joomla-admin/menu/mail.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/mainmenu.png b/public/images/joomla-admin/menu/mainmenu.png deleted file mode 100644 index b07725495..000000000 Binary files a/public/images/joomla-admin/menu/mainmenu.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/mass_email.png b/public/images/joomla-admin/menu/mass_email.png deleted file mode 100644 index 05d965cf3..000000000 Binary files a/public/images/joomla-admin/menu/mass_email.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/media.png b/public/images/joomla-admin/menu/media.png deleted file mode 100644 index 63e34d84e..000000000 Binary files a/public/images/joomla-admin/menu/media.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/meetings.png b/public/images/joomla-admin/menu/meetings.png deleted file mode 100644 index f7daa6688..000000000 Binary files a/public/images/joomla-admin/menu/meetings.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/menus.png b/public/images/joomla-admin/menu/menus.png deleted file mode 100644 index 7946c5f5a..000000000 Binary files a/public/images/joomla-admin/menu/menus.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/messaging.png b/public/images/joomla-admin/menu/messaging.png deleted file mode 100644 index b77951880..000000000 Binary files a/public/images/joomla-admin/menu/messaging.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/messaging_config.png b/public/images/joomla-admin/menu/messaging_config.png deleted file mode 100644 index ef4239995..000000000 Binary files a/public/images/joomla-admin/menu/messaging_config.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/messaging_inbox.png b/public/images/joomla-admin/menu/messaging_inbox.png deleted file mode 100644 index a44621932..000000000 Binary files a/public/images/joomla-admin/menu/messaging_inbox.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/module.png b/public/images/joomla-admin/menu/module.png deleted file mode 100644 index 4027bb788..000000000 Binary files a/public/images/joomla-admin/menu/module.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/network.png b/public/images/joomla-admin/menu/network.png deleted file mode 100644 index 3c5dbdf05..000000000 Binary files a/public/images/joomla-admin/menu/network.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/php.png b/public/images/joomla-admin/menu/php.png deleted file mode 100644 index cacaf2264..000000000 Binary files a/public/images/joomla-admin/menu/php.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/preview.png b/public/images/joomla-admin/menu/preview.png deleted file mode 100644 index a84c4cbc9..000000000 Binary files a/public/images/joomla-admin/menu/preview.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/query.png b/public/images/joomla-admin/menu/query.png deleted file mode 100644 index 39d010bc4..000000000 Binary files a/public/images/joomla-admin/menu/query.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/rack.png b/public/images/joomla-admin/menu/rack.png deleted file mode 100644 index 7330bc5b4..000000000 Binary files a/public/images/joomla-admin/menu/rack.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/remote.png b/public/images/joomla-admin/menu/remote.png deleted file mode 100644 index 9de8a241c..000000000 Binary files a/public/images/joomla-admin/menu/remote.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/restore.png b/public/images/joomla-admin/menu/restore.png deleted file mode 100644 index 8bbded4a2..000000000 Binary files a/public/images/joomla-admin/menu/restore.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/search_text.png b/public/images/joomla-admin/menu/search_text.png deleted file mode 100644 index 5d7c6d17c..000000000 Binary files a/public/images/joomla-admin/menu/search_text.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/sections.png b/public/images/joomla-admin/menu/sections.png deleted file mode 100644 index e83f7446a..000000000 Binary files a/public/images/joomla-admin/menu/sections.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/spacer.png b/public/images/joomla-admin/menu/spacer.png deleted file mode 100644 index ff2a8baa0..000000000 Binary files a/public/images/joomla-admin/menu/spacer.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/statistics.png b/public/images/joomla-admin/menu/statistics.png deleted file mode 100644 index ce1793b59..000000000 Binary files a/public/images/joomla-admin/menu/statistics.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/switch.png b/public/images/joomla-admin/menu/switch.png deleted file mode 100644 index a39e89ef9..000000000 Binary files a/public/images/joomla-admin/menu/switch.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/sysinfo.png b/public/images/joomla-admin/menu/sysinfo.png deleted file mode 100644 index 40d95f0c1..000000000 Binary files a/public/images/joomla-admin/menu/sysinfo.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/system-users.png b/public/images/joomla-admin/menu/system-users.png deleted file mode 100644 index d01016102..000000000 Binary files a/public/images/joomla-admin/menu/system-users.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/template.png b/public/images/joomla-admin/menu/template.png deleted file mode 100644 index f1f0b2858..000000000 Binary files a/public/images/joomla-admin/menu/template.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/tooltip.png b/public/images/joomla-admin/menu/tooltip.png deleted file mode 100644 index 6da328d73..000000000 Binary files a/public/images/joomla-admin/menu/tooltip.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/trash.png b/public/images/joomla-admin/menu/trash.png deleted file mode 100644 index 195f6fd0e..000000000 Binary files a/public/images/joomla-admin/menu/trash.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/tux.png b/public/images/joomla-admin/menu/tux.png deleted file mode 100644 index 48939bd5c..000000000 Binary files a/public/images/joomla-admin/menu/tux.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/user.png b/public/images/joomla-admin/menu/user.png deleted file mode 100644 index 1678d1c7e..000000000 Binary files a/public/images/joomla-admin/menu/user.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/users.png b/public/images/joomla-admin/menu/users.png deleted file mode 100644 index dfd57152a..000000000 Binary files a/public/images/joomla-admin/menu/users.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/users_add.png b/public/images/joomla-admin/menu/users_add.png deleted file mode 100644 index 24e6c5047..000000000 Binary files a/public/images/joomla-admin/menu/users_add.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/vendors.png b/public/images/joomla-admin/menu/vendors.png deleted file mode 100644 index 16b0de147..000000000 Binary files a/public/images/joomla-admin/menu/vendors.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/vlan.png b/public/images/joomla-admin/menu/vlan.png deleted file mode 100644 index 071fc1865..000000000 Binary files a/public/images/joomla-admin/menu/vlan.png and /dev/null differ diff --git a/public/images/joomla-admin/menu/warning.png b/public/images/joomla-admin/menu/warning.png deleted file mode 100644 index 6a5d33460..000000000 Binary files a/public/images/joomla-admin/menu/warning.png and /dev/null differ diff --git a/public/images/joomla-admin/message_config.png b/public/images/joomla-admin/message_config.png deleted file mode 100644 index a77098606..000000000 Binary files a/public/images/joomla-admin/message_config.png and /dev/null differ diff --git a/public/images/joomla-admin/module.png b/public/images/joomla-admin/module.png deleted file mode 100644 index ef0b7d7f4..000000000 Binary files a/public/images/joomla-admin/module.png and /dev/null differ diff --git a/public/images/joomla-admin/multimedia.png b/public/images/joomla-admin/multimedia.png deleted file mode 100644 index dc766a89c..000000000 Binary files a/public/images/joomla-admin/multimedia.png and /dev/null differ diff --git a/public/images/joomla-admin/package.png b/public/images/joomla-admin/package.png deleted file mode 100644 index db59aecca..000000000 Binary files a/public/images/joomla-admin/package.png and /dev/null differ diff --git a/public/images/joomla-admin/package_multimedia.png b/public/images/joomla-admin/package_multimedia.png deleted file mode 100644 index 97edf2f95..000000000 Binary files a/public/images/joomla-admin/package_multimedia.png and /dev/null differ diff --git a/public/images/joomla-admin/package_network.png b/public/images/joomla-admin/package_network.png deleted file mode 100644 index 47b78a93c..000000000 Binary files a/public/images/joomla-admin/package_network.png and /dev/null differ diff --git a/public/images/joomla-admin/package_settings.png b/public/images/joomla-admin/package_settings.png deleted file mode 100644 index 8b171e0bd..000000000 Binary files a/public/images/joomla-admin/package_settings.png and /dev/null differ diff --git a/public/images/joomla-admin/package_system.png b/public/images/joomla-admin/package_system.png deleted file mode 100644 index 3ed096d9d..000000000 Binary files a/public/images/joomla-admin/package_system.png and /dev/null differ diff --git a/public/images/joomla-admin/package_utilities.png b/public/images/joomla-admin/package_utilities.png deleted file mode 100644 index 4858a848d..000000000 Binary files a/public/images/joomla-admin/package_utilities.png and /dev/null differ diff --git a/public/images/joomla-admin/package_wordprocessing.png b/public/images/joomla-admin/package_wordprocessing.png deleted file mode 100644 index 774fcae09..000000000 Binary files a/public/images/joomla-admin/package_wordprocessing.png and /dev/null differ diff --git a/public/images/joomla-admin/personal.png b/public/images/joomla-admin/personal.png deleted file mode 100644 index 90aedf0f8..000000000 Binary files a/public/images/joomla-admin/personal.png and /dev/null differ diff --git a/public/images/joomla-admin/query.png b/public/images/joomla-admin/query.png deleted file mode 100644 index f5be6baac..000000000 Binary files a/public/images/joomla-admin/query.png and /dev/null differ diff --git a/public/images/joomla-admin/remote.png b/public/images/joomla-admin/remote.png deleted file mode 100644 index b4cf93a09..000000000 Binary files a/public/images/joomla-admin/remote.png and /dev/null differ diff --git a/public/images/joomla-admin/samba.png b/public/images/joomla-admin/samba.png deleted file mode 100644 index d980263d8..000000000 Binary files a/public/images/joomla-admin/samba.png and /dev/null differ diff --git a/public/images/joomla-admin/searchtext.png b/public/images/joomla-admin/searchtext.png deleted file mode 100644 index ae44ac49d..000000000 Binary files a/public/images/joomla-admin/searchtext.png and /dev/null differ diff --git a/public/images/joomla-admin/sections.png b/public/images/joomla-admin/sections.png deleted file mode 100644 index 777a1bb0e..000000000 Binary files a/public/images/joomla-admin/sections.png and /dev/null differ diff --git a/public/images/joomla-admin/security.png b/public/images/joomla-admin/security.png deleted file mode 100644 index 0cfd85fbd..000000000 Binary files a/public/images/joomla-admin/security.png and /dev/null differ diff --git a/public/images/joomla-admin/stop.png b/public/images/joomla-admin/stop.png deleted file mode 100644 index 6576cd38d..000000000 Binary files a/public/images/joomla-admin/stop.png and /dev/null differ diff --git a/public/images/joomla-admin/switch.png b/public/images/joomla-admin/switch.png deleted file mode 100644 index b602ff54f..000000000 Binary files a/public/images/joomla-admin/switch.png and /dev/null differ diff --git a/public/images/joomla-admin/system-users.png b/public/images/joomla-admin/system-users.png deleted file mode 100644 index e5577d08a..000000000 Binary files a/public/images/joomla-admin/system-users.png and /dev/null differ diff --git a/public/images/joomla-admin/systeminfo.png b/public/images/joomla-admin/systeminfo.png deleted file mode 100644 index 801ae8ba7..000000000 Binary files a/public/images/joomla-admin/systeminfo.png and /dev/null differ diff --git a/public/images/joomla-admin/templatemanager.png b/public/images/joomla-admin/templatemanager.png deleted file mode 100644 index 0f1470e3f..000000000 Binary files a/public/images/joomla-admin/templatemanager.png and /dev/null differ diff --git a/public/images/joomla-admin/trash.png b/public/images/joomla-admin/trash.png deleted file mode 100644 index 2ebb672b8..000000000 Binary files a/public/images/joomla-admin/trash.png and /dev/null differ diff --git a/public/images/joomla-admin/tux.png b/public/images/joomla-admin/tux.png deleted file mode 100644 index d75f61b08..000000000 Binary files a/public/images/joomla-admin/tux.png and /dev/null differ diff --git a/public/images/joomla-admin/user.png b/public/images/joomla-admin/user.png deleted file mode 100644 index 0c6ba8fa6..000000000 Binary files a/public/images/joomla-admin/user.png and /dev/null differ diff --git a/public/images/joomla-admin/vendor.png b/public/images/joomla-admin/vendor.png deleted file mode 100644 index 886904e1d..000000000 Binary files a/public/images/joomla-admin/vendor.png and /dev/null differ diff --git a/public/images/joomla-admin/vlan.png b/public/images/joomla-admin/vlan.png deleted file mode 100644 index ffdce8b4e..000000000 Binary files a/public/images/joomla-admin/vlan.png and /dev/null differ diff --git a/public/images/joomla-admin/xfmail.png b/public/images/joomla-admin/xfmail.png deleted file mode 100644 index 20fc52bd9..000000000 Binary files a/public/images/joomla-admin/xfmail.png and /dev/null differ diff --git a/public/images/no.gif b/public/images/no.gif deleted file mode 100644 index e31e076b5..000000000 Binary files a/public/images/no.gif and /dev/null differ diff --git a/public/images/no.png b/public/images/no.png deleted file mode 100644 index 383b8138d..000000000 Binary files a/public/images/no.png and /dev/null differ diff --git a/public/images/page_white_add.png b/public/images/page_white_add.png deleted file mode 100644 index a70de096e..000000000 Binary files a/public/images/page_white_add.png and /dev/null differ diff --git a/public/images/page_white_copy.png b/public/images/page_white_copy.png deleted file mode 100644 index a9f31a278..000000000 Binary files a/public/images/page_white_copy.png and /dev/null differ diff --git a/public/images/page_white_delete.png b/public/images/page_white_delete.png deleted file mode 100644 index af1ecaf29..000000000 Binary files a/public/images/page_white_delete.png and /dev/null differ diff --git a/public/images/page_white_edit.png b/public/images/page_white_edit.png deleted file mode 100644 index b93e77600..000000000 Binary files a/public/images/page_white_edit.png and /dev/null differ diff --git a/public/images/page_white_paste.png b/public/images/page_white_paste.png deleted file mode 100644 index 5b2cbb3fd..000000000 Binary files a/public/images/page_white_paste.png and /dev/null differ diff --git a/public/images/pdb-logo-coloured.png b/public/images/pdb-logo-coloured.png new file mode 100644 index 000000000..be656e70c Binary files /dev/null and b/public/images/pdb-logo-coloured.png differ diff --git a/public/images/throbber-small.gif b/public/images/throbber-small.gif deleted file mode 100644 index cce32f20f..000000000 Binary files a/public/images/throbber-small.gif and /dev/null differ diff --git a/public/images/throbber_16px.gif b/public/images/throbber_16px.gif deleted file mode 100644 index cce32f20f..000000000 Binary files a/public/images/throbber_16px.gif and /dev/null differ diff --git a/public/images/throbber_32px.gif b/public/images/throbber_32px.gif deleted file mode 100644 index e2a116c72..000000000 Binary files a/public/images/throbber_32px.gif and /dev/null differ diff --git a/public/images/ui/ui-bg_flat_0_aaaaaa_40x100.png b/public/images/ui/ui-bg_flat_0_aaaaaa_40x100.png deleted file mode 100644 index 5b5dab2ab..000000000 Binary files a/public/images/ui/ui-bg_flat_0_aaaaaa_40x100.png and /dev/null differ diff --git a/public/images/ui/ui-bg_flat_55_fbec88_40x100.png b/public/images/ui/ui-bg_flat_55_fbec88_40x100.png deleted file mode 100644 index 47acaadd7..000000000 Binary files a/public/images/ui/ui-bg_flat_55_fbec88_40x100.png and /dev/null differ diff --git a/public/images/ui/ui-bg_glass_75_d0e5f5_1x400.png b/public/images/ui/ui-bg_glass_75_d0e5f5_1x400.png deleted file mode 100644 index 9d149b1c6..000000000 Binary files a/public/images/ui/ui-bg_glass_75_d0e5f5_1x400.png and /dev/null differ diff --git a/public/images/ui/ui-bg_glass_85_dfeffc_1x400.png b/public/images/ui/ui-bg_glass_85_dfeffc_1x400.png deleted file mode 100644 index 014951529..000000000 Binary files a/public/images/ui/ui-bg_glass_85_dfeffc_1x400.png and /dev/null differ diff --git a/public/images/ui/ui-bg_glass_95_fef1ec_1x400.png b/public/images/ui/ui-bg_glass_95_fef1ec_1x400.png deleted file mode 100644 index 4443fdc1a..000000000 Binary files a/public/images/ui/ui-bg_glass_95_fef1ec_1x400.png and /dev/null differ diff --git a/public/images/ui/ui-bg_gloss-wave_55_5c9ccc_500x100.png b/public/images/ui/ui-bg_gloss-wave_55_5c9ccc_500x100.png deleted file mode 100644 index 81ecc362d..000000000 Binary files a/public/images/ui/ui-bg_gloss-wave_55_5c9ccc_500x100.png and /dev/null differ diff --git a/public/images/ui/ui-bg_inset-hard_100_f5f8f9_1x100.png b/public/images/ui/ui-bg_inset-hard_100_f5f8f9_1x100.png deleted file mode 100644 index 4f3faf8aa..000000000 Binary files a/public/images/ui/ui-bg_inset-hard_100_f5f8f9_1x100.png and /dev/null differ diff --git a/public/images/ui/ui-bg_inset-hard_100_fcfdfd_1x100.png b/public/images/ui/ui-bg_inset-hard_100_fcfdfd_1x100.png deleted file mode 100644 index 38c38335d..000000000 Binary files a/public/images/ui/ui-bg_inset-hard_100_fcfdfd_1x100.png and /dev/null differ diff --git a/public/images/ui/ui-icons_217bc0_256x240.png b/public/images/ui/ui-icons_217bc0_256x240.png deleted file mode 100644 index 6f4bd87c0..000000000 Binary files a/public/images/ui/ui-icons_217bc0_256x240.png and /dev/null differ diff --git a/public/images/ui/ui-icons_2e83ff_256x240.png b/public/images/ui/ui-icons_2e83ff_256x240.png deleted file mode 100644 index 09d1cdc85..000000000 Binary files a/public/images/ui/ui-icons_2e83ff_256x240.png and /dev/null differ diff --git a/public/images/ui/ui-icons_469bdd_256x240.png b/public/images/ui/ui-icons_469bdd_256x240.png deleted file mode 100644 index bd2cf079a..000000000 Binary files a/public/images/ui/ui-icons_469bdd_256x240.png and /dev/null differ diff --git a/public/images/ui/ui-icons_6da8d5_256x240.png b/public/images/ui/ui-icons_6da8d5_256x240.png deleted file mode 100644 index 3d6f567f4..000000000 Binary files a/public/images/ui/ui-icons_6da8d5_256x240.png and /dev/null differ diff --git a/public/images/ui/ui-icons_cd0a0a_256x240.png b/public/images/ui/ui-icons_cd0a0a_256x240.png deleted file mode 100644 index 2ab019b73..000000000 Binary files a/public/images/ui/ui-icons_cd0a0a_256x240.png and /dev/null differ diff --git a/public/images/ui/ui-icons_d8e7f3_256x240.png b/public/images/ui/ui-icons_d8e7f3_256x240.png deleted file mode 100644 index c11e92507..000000000 Binary files a/public/images/ui/ui-icons_d8e7f3_256x240.png and /dev/null differ diff --git a/public/images/ui/ui-icons_f9bd01_256x240.png b/public/images/ui/ui-icons_f9bd01_256x240.png deleted file mode 100644 index 78625024d..000000000 Binary files a/public/images/ui/ui-icons_f9bd01_256x240.png and /dev/null differ diff --git a/public/images/yes.gif b/public/images/yes.gif deleted file mode 100644 index e9166ed99..000000000 Binary files a/public/images/yes.gif and /dev/null differ diff --git a/public/img/icons.png b/public/img/icons.png new file mode 100644 index 000000000..3446eb063 Binary files /dev/null and b/public/img/icons.png differ diff --git a/public/index.php b/public/index.php index ccc422c91..f09a5ecc1 100644 --- a/public/index.php +++ b/public/index.php @@ -1,60 +1,92 @@ make( Kernel::class ); -$application->bootstrap() - ->run(); +$response = tap($kernel->handle( + $request = Request::capture() +))->send(); +$kernel->terminate($request, $response); \ No newline at end of file diff --git a/public/js/200-jquery-1.8.1.js b/public/js/200-jquery-1.8.1.js deleted file mode 100644 index 973ef3d9a..000000000 --- a/public/js/200-jquery-1.8.1.js +++ /dev/null @@ -1,9301 +0,0 @@ -/*! - * jQuery JavaScript Library v1.8.1 - * http://jquery.com/ - * - * Includes Sizzle.js - * http://sizzlejs.com/ - * - * Copyright 2012 jQuery Foundation and other contributors - * Released under the MIT license - * http://jquery.org/license - * - * Date: Thu Aug 30 2012 17:17:22 GMT-0400 (Eastern Daylight Time) - */ -(function( window, undefined ) { -var - // A central reference to the root jQuery(document) - rootjQuery, - - // The deferred used on DOM ready - readyList, - - // Use the correct document accordingly with window argument (sandbox) - document = window.document, - location = window.location, - navigator = window.navigator, - - // Map over jQuery in case of overwrite - _jQuery = window.jQuery, - - // Map over the $ in case of overwrite - _$ = window.$, - - // Save a reference to some core methods - core_push = Array.prototype.push, - core_slice = Array.prototype.slice, - core_indexOf = Array.prototype.indexOf, - core_toString = Object.prototype.toString, - core_hasOwn = Object.prototype.hasOwnProperty, - core_trim = String.prototype.trim, - - // Define a local copy of jQuery - jQuery = function( selector, context ) { - // The jQuery object is actually just the init constructor 'enhanced' - return new jQuery.fn.init( selector, context, rootjQuery ); - }, - - // Used for matching numbers - core_pnum = /[\-+]?(?:\d*\.|)\d+(?:[eE][\-+]?\d+|)/.source, - - // Used for detecting and trimming whitespace - core_rnotwhite = /\S/, - core_rspace = /\s+/, - - // Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE) - rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, - - // A simple way to check for HTML strings - // Prioritize #id over to avoid XSS via location.hash (#9521) - rquickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/, - - // Match a standalone tag - rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/, - - // JSON RegExp - rvalidchars = /^[\],:{}\s]*$/, - rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, - rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g, - rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g, - - // Matches dashed string for camelizing - rmsPrefix = /^-ms-/, - rdashAlpha = /-([\da-z])/gi, - - // Used by jQuery.camelCase as callback to replace() - fcamelCase = function( all, letter ) { - return ( letter + "" ).toUpperCase(); - }, - - // The ready event handler and self cleanup method - DOMContentLoaded = function() { - if ( document.addEventListener ) { - document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); - jQuery.ready(); - } else if ( document.readyState === "complete" ) { - // we're here because readyState === "complete" in oldIE - // which is good enough for us to call the dom ready! - document.detachEvent( "onreadystatechange", DOMContentLoaded ); - jQuery.ready(); - } - }, - - // [[Class]] -> type pairs - class2type = {}; - -jQuery.fn = jQuery.prototype = { - constructor: jQuery, - init: function( selector, context, rootjQuery ) { - var match, elem, ret, doc; - - // Handle $(""), $(null), $(undefined), $(false) - if ( !selector ) { - return this; - } - - // Handle $(DOMElement) - if ( selector.nodeType ) { - this.context = this[0] = selector; - this.length = 1; - return this; - } - - // Handle HTML strings - if ( typeof selector === "string" ) { - if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { - // Assume that strings that start and end with <> are HTML and skip the regex check - match = [ null, selector, null ]; - - } else { - match = rquickExpr.exec( selector ); - } - - // Match html or make sure no context is specified for #id - if ( match && (match[1] || !context) ) { - - // HANDLE: $(html) -> $(array) - if ( match[1] ) { - context = context instanceof jQuery ? context[0] : context; - doc = ( context && context.nodeType ? context.ownerDocument || context : document ); - - // scripts is true for back-compat - selector = jQuery.parseHTML( match[1], doc, true ); - if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { - this.attr.call( selector, context, true ); - } - - return jQuery.merge( this, selector ); - - // HANDLE: $(#id) - } else { - elem = document.getElementById( match[2] ); - - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - if ( elem && elem.parentNode ) { - // Handle the case where IE and Opera return items - // by name instead of ID - if ( elem.id !== match[2] ) { - return rootjQuery.find( selector ); - } - - // Otherwise, we inject the element directly into the jQuery object - this.length = 1; - this[0] = elem; - } - - this.context = document; - this.selector = selector; - return this; - } - - // HANDLE: $(expr, $(...)) - } else if ( !context || context.jquery ) { - return ( context || rootjQuery ).find( selector ); - - // HANDLE: $(expr, context) - // (which is just equivalent to: $(context).find(expr) - } else { - return this.constructor( context ).find( selector ); - } - - // HANDLE: $(function) - // Shortcut for document ready - } else if ( jQuery.isFunction( selector ) ) { - return rootjQuery.ready( selector ); - } - - if ( selector.selector !== undefined ) { - this.selector = selector.selector; - this.context = selector.context; - } - - return jQuery.makeArray( selector, this ); - }, - - // Start with an empty selector - selector: "", - - // The current version of jQuery being used - jquery: "1.8.1", - - // The default length of a jQuery object is 0 - length: 0, - - // The number of elements contained in the matched element set - size: function() { - return this.length; - }, - - toArray: function() { - return core_slice.call( this ); - }, - - // Get the Nth element in the matched element set OR - // Get the whole matched element set as a clean array - get: function( num ) { - return num == null ? - - // Return a 'clean' array - this.toArray() : - - // Return just the object - ( num < 0 ? this[ this.length + num ] : this[ num ] ); - }, - - // Take an array of elements and push it onto the stack - // (returning the new matched element set) - pushStack: function( elems, name, selector ) { - - // Build a new jQuery matched element set - var ret = jQuery.merge( this.constructor(), elems ); - - // Add the old object onto the stack (as a reference) - ret.prevObject = this; - - ret.context = this.context; - - if ( name === "find" ) { - ret.selector = this.selector + ( this.selector ? " " : "" ) + selector; - } else if ( name ) { - ret.selector = this.selector + "." + name + "(" + selector + ")"; - } - - // Return the newly-formed element set - return ret; - }, - - // Execute a callback for every element in the matched set. - // (You can seed the arguments with an array of args, but this is - // only used internally.) - each: function( callback, args ) { - return jQuery.each( this, callback, args ); - }, - - ready: function( fn ) { - // Add the callback - jQuery.ready.promise().done( fn ); - - return this; - }, - - eq: function( i ) { - i = +i; - return i === -1 ? - this.slice( i ) : - this.slice( i, i + 1 ); - }, - - first: function() { - return this.eq( 0 ); - }, - - last: function() { - return this.eq( -1 ); - }, - - slice: function() { - return this.pushStack( core_slice.apply( this, arguments ), - "slice", core_slice.call(arguments).join(",") ); - }, - - map: function( callback ) { - return this.pushStack( jQuery.map(this, function( elem, i ) { - return callback.call( elem, i, elem ); - })); - }, - - end: function() { - return this.prevObject || this.constructor(null); - }, - - // For internal use only. - // Behaves like an Array's method, not like a jQuery method. - push: core_push, - sort: [].sort, - splice: [].splice -}; - -// Give the init function the jQuery prototype for later instantiation -jQuery.fn.init.prototype = jQuery.fn; - -jQuery.extend = jQuery.fn.extend = function() { - var options, name, src, copy, copyIsArray, clone, - target = arguments[0] || {}, - i = 1, - length = arguments.length, - deep = false; - - // Handle a deep copy situation - if ( typeof target === "boolean" ) { - deep = target; - target = arguments[1] || {}; - // skip the boolean and the target - i = 2; - } - - // Handle case when target is a string or something (possible in deep copy) - if ( typeof target !== "object" && !jQuery.isFunction(target) ) { - target = {}; - } - - // extend jQuery itself if only one argument is passed - if ( length === i ) { - target = this; - --i; - } - - for ( ; i < length; i++ ) { - // Only deal with non-null/undefined values - if ( (options = arguments[ i ]) != null ) { - // Extend the base object - for ( name in options ) { - src = target[ name ]; - copy = options[ name ]; - - // Prevent never-ending loop - if ( target === copy ) { - continue; - } - - // Recurse if we're merging plain objects or arrays - if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { - if ( copyIsArray ) { - copyIsArray = false; - clone = src && jQuery.isArray(src) ? src : []; - - } else { - clone = src && jQuery.isPlainObject(src) ? src : {}; - } - - // Never move original objects, clone them - target[ name ] = jQuery.extend( deep, clone, copy ); - - // Don't bring in undefined values - } else if ( copy !== undefined ) { - target[ name ] = copy; - } - } - } - } - - // Return the modified object - return target; -}; - -jQuery.extend({ - noConflict: function( deep ) { - if ( window.$ === jQuery ) { - window.$ = _$; - } - - if ( deep && window.jQuery === jQuery ) { - window.jQuery = _jQuery; - } - - return jQuery; - }, - - // Is the DOM ready to be used? Set to true once it occurs. - isReady: false, - - // A counter to track how many items to wait for before - // the ready event fires. See #6781 - readyWait: 1, - - // Hold (or release) the ready event - holdReady: function( hold ) { - if ( hold ) { - jQuery.readyWait++; - } else { - jQuery.ready( true ); - } - }, - - // Handle when the DOM is ready - ready: function( wait ) { - - // Abort if there are pending holds or we're already ready - if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { - return; - } - - // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). - if ( !document.body ) { - return setTimeout( jQuery.ready, 1 ); - } - - // Remember that the DOM is ready - jQuery.isReady = true; - - // If a normal DOM Ready event fired, decrement, and wait if need be - if ( wait !== true && --jQuery.readyWait > 0 ) { - return; - } - - // If there are functions bound, to execute - readyList.resolveWith( document, [ jQuery ] ); - - // Trigger any bound ready events - if ( jQuery.fn.trigger ) { - jQuery( document ).trigger("ready").off("ready"); - } - }, - - // See test/unit/core.js for details concerning isFunction. - // Since version 1.3, DOM methods and functions like alert - // aren't supported. They return false on IE (#2968). - isFunction: function( obj ) { - return jQuery.type(obj) === "function"; - }, - - isArray: Array.isArray || function( obj ) { - return jQuery.type(obj) === "array"; - }, - - isWindow: function( obj ) { - return obj != null && obj == obj.window; - }, - - isNumeric: function( obj ) { - return !isNaN( parseFloat(obj) ) && isFinite( obj ); - }, - - type: function( obj ) { - return obj == null ? - String( obj ) : - class2type[ core_toString.call(obj) ] || "object"; - }, - - isPlainObject: function( obj ) { - // Must be an Object. - // Because of IE, we also have to check the presence of the constructor property. - // Make sure that DOM nodes and window objects don't pass through, as well - if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { - return false; - } - - try { - // Not own constructor property must be Object - if ( obj.constructor && - !core_hasOwn.call(obj, "constructor") && - !core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { - return false; - } - } catch ( e ) { - // IE8,9 Will throw exceptions on certain host objects #9897 - return false; - } - - // Own properties are enumerated firstly, so to speed up, - // if last one is own, then all properties are own. - - var key; - for ( key in obj ) {} - - return key === undefined || core_hasOwn.call( obj, key ); - }, - - isEmptyObject: function( obj ) { - var name; - for ( name in obj ) { - return false; - } - return true; - }, - - error: function( msg ) { - throw new Error( msg ); - }, - - // data: string of html - // context (optional): If specified, the fragment will be created in this context, defaults to document - // scripts (optional): If true, will include scripts passed in the html string - parseHTML: function( data, context, scripts ) { - var parsed; - if ( !data || typeof data !== "string" ) { - return null; - } - if ( typeof context === "boolean" ) { - scripts = context; - context = 0; - } - context = context || document; - - // Single tag - if ( (parsed = rsingleTag.exec( data )) ) { - return [ context.createElement( parsed[1] ) ]; - } - - parsed = jQuery.buildFragment( [ data ], context, scripts ? null : [] ); - return jQuery.merge( [], - (parsed.cacheable ? jQuery.clone( parsed.fragment ) : parsed.fragment).childNodes ); - }, - - parseJSON: function( data ) { - if ( !data || typeof data !== "string") { - return null; - } - - // Make sure leading/trailing whitespace is removed (IE can't handle it) - data = jQuery.trim( data ); - - // Attempt to parse using the native JSON parser first - if ( window.JSON && window.JSON.parse ) { - return window.JSON.parse( data ); - } - - // Make sure the incoming data is actual JSON - // Logic borrowed from http://json.org/json2.js - if ( rvalidchars.test( data.replace( rvalidescape, "@" ) - .replace( rvalidtokens, "]" ) - .replace( rvalidbraces, "")) ) { - - return ( new Function( "return " + data ) )(); - - } - jQuery.error( "Invalid JSON: " + data ); - }, - - // Cross-browser xml parsing - parseXML: function( data ) { - var xml, tmp; - if ( !data || typeof data !== "string" ) { - return null; - } - try { - if ( window.DOMParser ) { // Standard - tmp = new DOMParser(); - xml = tmp.parseFromString( data , "text/xml" ); - } else { // IE - xml = new ActiveXObject( "Microsoft.XMLDOM" ); - xml.async = "false"; - xml.loadXML( data ); - } - } catch( e ) { - xml = undefined; - } - if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) { - jQuery.error( "Invalid XML: " + data ); - } - return xml; - }, - - noop: function() {}, - - // Evaluates a script in a global context - // Workarounds based on findings by Jim Driscoll - // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context - globalEval: function( data ) { - if ( data && core_rnotwhite.test( data ) ) { - // We use execScript on Internet Explorer - // We use an anonymous function so that context is window - // rather than jQuery in Firefox - ( window.execScript || function( data ) { - window[ "eval" ].call( window, data ); - } )( data ); - } - }, - - // Convert dashed to camelCase; used by the css and data modules - // Microsoft forgot to hump their vendor prefix (#9572) - camelCase: function( string ) { - return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); - }, - - nodeName: function( elem, name ) { - return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase(); - }, - - // args is for internal usage only - each: function( obj, callback, args ) { - var name, - i = 0, - length = obj.length, - isObj = length === undefined || jQuery.isFunction( obj ); - - if ( args ) { - if ( isObj ) { - for ( name in obj ) { - if ( callback.apply( obj[ name ], args ) === false ) { - break; - } - } - } else { - for ( ; i < length; ) { - if ( callback.apply( obj[ i++ ], args ) === false ) { - break; - } - } - } - - // A special, fast, case for the most common use of each - } else { - if ( isObj ) { - for ( name in obj ) { - if ( callback.call( obj[ name ], name, obj[ name ] ) === false ) { - break; - } - } - } else { - for ( ; i < length; ) { - if ( callback.call( obj[ i ], i, obj[ i++ ] ) === false ) { - break; - } - } - } - } - - return obj; - }, - - // Use native String.trim function wherever possible - trim: core_trim && !core_trim.call("\uFEFF\xA0") ? - function( text ) { - return text == null ? - "" : - core_trim.call( text ); - } : - - // Otherwise use our own trimming functionality - function( text ) { - return text == null ? - "" : - text.toString().replace( rtrim, "" ); - }, - - // results is for internal usage only - makeArray: function( arr, results ) { - var type, - ret = results || []; - - if ( arr != null ) { - // The window, strings (and functions) also have 'length' - // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930 - type = jQuery.type( arr ); - - if ( arr.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( arr ) ) { - core_push.call( ret, arr ); - } else { - jQuery.merge( ret, arr ); - } - } - - return ret; - }, - - inArray: function( elem, arr, i ) { - var len; - - if ( arr ) { - if ( core_indexOf ) { - return core_indexOf.call( arr, elem, i ); - } - - len = arr.length; - i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; - - for ( ; i < len; i++ ) { - // Skip accessing in sparse arrays - if ( i in arr && arr[ i ] === elem ) { - return i; - } - } - } - - return -1; - }, - - merge: function( first, second ) { - var l = second.length, - i = first.length, - j = 0; - - if ( typeof l === "number" ) { - for ( ; j < l; j++ ) { - first[ i++ ] = second[ j ]; - } - - } else { - while ( second[j] !== undefined ) { - first[ i++ ] = second[ j++ ]; - } - } - - first.length = i; - - return first; - }, - - grep: function( elems, callback, inv ) { - var retVal, - ret = [], - i = 0, - length = elems.length; - inv = !!inv; - - // Go through the array, only saving the items - // that pass the validator function - for ( ; i < length; i++ ) { - retVal = !!callback( elems[ i ], i ); - if ( inv !== retVal ) { - ret.push( elems[ i ] ); - } - } - - return ret; - }, - - // arg is for internal usage only - map: function( elems, callback, arg ) { - var value, key, - ret = [], - i = 0, - length = elems.length, - // jquery objects are treated as arrays - isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ; - - // Go through the array, translating each of the items to their - if ( isArray ) { - for ( ; i < length; i++ ) { - value = callback( elems[ i ], i, arg ); - - if ( value != null ) { - ret[ ret.length ] = value; - } - } - - // Go through every key on the object, - } else { - for ( key in elems ) { - value = callback( elems[ key ], key, arg ); - - if ( value != null ) { - ret[ ret.length ] = value; - } - } - } - - // Flatten any nested arrays - return ret.concat.apply( [], ret ); - }, - - // A global GUID counter for objects - guid: 1, - - // Bind a function to a context, optionally partially applying any - // arguments. - proxy: function( fn, context ) { - var tmp, args, proxy; - - if ( typeof context === "string" ) { - tmp = fn[ context ]; - context = fn; - fn = tmp; - } - - // Quick check to determine if target is callable, in the spec - // this throws a TypeError, but we will just return undefined. - if ( !jQuery.isFunction( fn ) ) { - return undefined; - } - - // Simulated bind - args = core_slice.call( arguments, 2 ); - proxy = function() { - return fn.apply( context, args.concat( core_slice.call( arguments ) ) ); - }; - - // Set the guid of unique handler to the same of original handler, so it can be removed - proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++; - - return proxy; - }, - - // Multifunctional method to get and set values of a collection - // The value/s can optionally be executed if it's a function - access: function( elems, fn, key, value, chainable, emptyGet, pass ) { - var exec, - bulk = key == null, - i = 0, - length = elems.length; - - // Sets many values - if ( key && typeof key === "object" ) { - for ( i in key ) { - jQuery.access( elems, fn, i, key[i], 1, emptyGet, value ); - } - chainable = 1; - - // Sets one value - } else if ( value !== undefined ) { - // Optionally, function values get executed if exec is true - exec = pass === undefined && jQuery.isFunction( value ); - - if ( bulk ) { - // Bulk operations only iterate when executing function values - if ( exec ) { - exec = fn; - fn = function( elem, key, value ) { - return exec.call( jQuery( elem ), value ); - }; - - // Otherwise they run against the entire set - } else { - fn.call( elems, value ); - fn = null; - } - } - - if ( fn ) { - for (; i < length; i++ ) { - fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass ); - } - } - - chainable = 1; - } - - return chainable ? - elems : - - // Gets - bulk ? - fn.call( elems ) : - length ? fn( elems[0], key ) : emptyGet; - }, - - now: function() { - return ( new Date() ).getTime(); - } -}); - -jQuery.ready.promise = function( obj ) { - if ( !readyList ) { - - readyList = jQuery.Deferred(); - - // Catch cases where $(document).ready() is called after the browser event has already occurred. - // we once tried to use readyState "interactive" here, but it caused issues like the one - // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 - if ( document.readyState === "complete" ) { - // Handle it asynchronously to allow scripts the opportunity to delay ready - setTimeout( jQuery.ready, 1 ); - - // Standards-based browsers support DOMContentLoaded - } else if ( document.addEventListener ) { - // Use the handy event callback - document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); - - // A fallback to window.onload, that will always work - window.addEventListener( "load", jQuery.ready, false ); - - // If IE event model is used - } else { - // Ensure firing before onload, maybe late but safe also for iframes - document.attachEvent( "onreadystatechange", DOMContentLoaded ); - - // A fallback to window.onload, that will always work - window.attachEvent( "onload", jQuery.ready ); - - // If IE and not a frame - // continually check to see if the document is ready - var top = false; - - try { - top = window.frameElement == null && document.documentElement; - } catch(e) {} - - if ( top && top.doScroll ) { - (function doScrollCheck() { - if ( !jQuery.isReady ) { - - try { - // Use the trick by Diego Perini - // http://javascript.nwbox.com/IEContentLoaded/ - top.doScroll("left"); - } catch(e) { - return setTimeout( doScrollCheck, 50 ); - } - - // and execute any waiting functions - jQuery.ready(); - } - })(); - } - } - } - return readyList.promise( obj ); -}; - -// Populate the class2type map -jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) { - class2type[ "[object " + name + "]" ] = name.toLowerCase(); -}); - -// All jQuery objects should point back to these -rootjQuery = jQuery(document); -// String to Object options format cache -var optionsCache = {}; - -// Convert String-formatted options into Object-formatted ones and store in cache -function createOptions( options ) { - var object = optionsCache[ options ] = {}; - jQuery.each( options.split( core_rspace ), function( _, flag ) { - object[ flag ] = true; - }); - return object; -} - -/* - * Create a callback list using the following parameters: - * - * options: an optional list of space-separated options that will change how - * the callback list behaves or a more traditional option object - * - * By default a callback list will act like an event callback list and can be - * "fired" multiple times. - * - * Possible options: - * - * once: will ensure the callback list can only be fired once (like a Deferred) - * - * memory: will keep track of previous values and will call any callback added - * after the list has been fired right away with the latest "memorized" - * values (like a Deferred) - * - * unique: will ensure a callback can only be added once (no duplicate in the list) - * - * stopOnFalse: interrupt callings when a callback returns false - * - */ -jQuery.Callbacks = function( options ) { - - // Convert options from String-formatted to Object-formatted if needed - // (we check in cache first) - options = typeof options === "string" ? - ( optionsCache[ options ] || createOptions( options ) ) : - jQuery.extend( {}, options ); - - var // Last fire value (for non-forgettable lists) - memory, - // Flag to know if list was already fired - fired, - // Flag to know if list is currently firing - firing, - // First callback to fire (used internally by add and fireWith) - firingStart, - // End of the loop when firing - firingLength, - // Index of currently firing callback (modified by remove if needed) - firingIndex, - // Actual callback list - list = [], - // Stack of fire calls for repeatable lists - stack = !options.once && [], - // Fire callbacks - fire = function( data ) { - memory = options.memory && data; - fired = true; - firingIndex = firingStart || 0; - firingStart = 0; - firingLength = list.length; - firing = true; - for ( ; list && firingIndex < firingLength; firingIndex++ ) { - if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { - memory = false; // To prevent further calls using add - break; - } - } - firing = false; - if ( list ) { - if ( stack ) { - if ( stack.length ) { - fire( stack.shift() ); - } - } else if ( memory ) { - list = []; - } else { - self.disable(); - } - } - }, - // Actual Callbacks object - self = { - // Add a callback or a collection of callbacks to the list - add: function() { - if ( list ) { - // First, we save the current length - var start = list.length; - (function add( args ) { - jQuery.each( args, function( _, arg ) { - var type = jQuery.type( arg ); - if ( type === "function" && ( !options.unique || !self.has( arg ) ) ) { - list.push( arg ); - } else if ( arg && arg.length && type !== "string" ) { - // Inspect recursively - add( arg ); - } - }); - })( arguments ); - // Do we need to add the callbacks to the - // current firing batch? - if ( firing ) { - firingLength = list.length; - // With memory, if we're not firing then - // we should call right away - } else if ( memory ) { - firingStart = start; - fire( memory ); - } - } - return this; - }, - // Remove a callback from the list - remove: function() { - if ( list ) { - jQuery.each( arguments, function( _, arg ) { - var index; - while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { - list.splice( index, 1 ); - // Handle firing indexes - if ( firing ) { - if ( index <= firingLength ) { - firingLength--; - } - if ( index <= firingIndex ) { - firingIndex--; - } - } - } - }); - } - return this; - }, - // Control if a given callback is in the list - has: function( fn ) { - return jQuery.inArray( fn, list ) > -1; - }, - // Remove all callbacks from the list - empty: function() { - list = []; - return this; - }, - // Have the list do nothing anymore - disable: function() { - list = stack = memory = undefined; - return this; - }, - // Is it disabled? - disabled: function() { - return !list; - }, - // Lock the list in its current state - lock: function() { - stack = undefined; - if ( !memory ) { - self.disable(); - } - return this; - }, - // Is it locked? - locked: function() { - return !stack; - }, - // Call all callbacks with the given context and arguments - fireWith: function( context, args ) { - args = args || []; - args = [ context, args.slice ? args.slice() : args ]; - if ( list && ( !fired || stack ) ) { - if ( firing ) { - stack.push( args ); - } else { - fire( args ); - } - } - return this; - }, - // Call all the callbacks with the given arguments - fire: function() { - self.fireWith( this, arguments ); - return this; - }, - // To know if the callbacks have already been called at least once - fired: function() { - return !!fired; - } - }; - - return self; -}; -jQuery.extend({ - - Deferred: function( func ) { - var tuples = [ - // action, add listener, listener list, final state - [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], - [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], - [ "notify", "progress", jQuery.Callbacks("memory") ] - ], - state = "pending", - promise = { - state: function() { - return state; - }, - always: function() { - deferred.done( arguments ).fail( arguments ); - return this; - }, - then: function( /* fnDone, fnFail, fnProgress */ ) { - var fns = arguments; - return jQuery.Deferred(function( newDefer ) { - jQuery.each( tuples, function( i, tuple ) { - var action = tuple[ 0 ], - fn = fns[ i ]; - // deferred[ done | fail | progress ] for forwarding actions to newDefer - deferred[ tuple[1] ]( jQuery.isFunction( fn ) ? - function() { - var returned = fn.apply( this, arguments ); - if ( returned && jQuery.isFunction( returned.promise ) ) { - returned.promise() - .done( newDefer.resolve ) - .fail( newDefer.reject ) - .progress( newDefer.notify ); - } else { - newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] ); - } - } : - newDefer[ action ] - ); - }); - fns = null; - }).promise(); - }, - // Get a promise for this deferred - // If obj is provided, the promise aspect is added to the object - promise: function( obj ) { - return typeof obj === "object" ? jQuery.extend( obj, promise ) : promise; - } - }, - deferred = {}; - - // Keep pipe for back-compat - promise.pipe = promise.then; - - // Add list-specific methods - jQuery.each( tuples, function( i, tuple ) { - var list = tuple[ 2 ], - stateString = tuple[ 3 ]; - - // promise[ done | fail | progress ] = list.add - promise[ tuple[1] ] = list.add; - - // Handle state - if ( stateString ) { - list.add(function() { - // state = [ resolved | rejected ] - state = stateString; - - // [ reject_list | resolve_list ].disable; progress_list.lock - }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); - } - - // deferred[ resolve | reject | notify ] = list.fire - deferred[ tuple[0] ] = list.fire; - deferred[ tuple[0] + "With" ] = list.fireWith; - }); - - // Make the deferred a promise - promise.promise( deferred ); - - // Call given func if any - if ( func ) { - func.call( deferred, deferred ); - } - - // All done! - return deferred; - }, - - // Deferred helper - when: function( subordinate /* , ..., subordinateN */ ) { - var i = 0, - resolveValues = core_slice.call( arguments ), - length = resolveValues.length, - - // the count of uncompleted subordinates - remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, - - // the master Deferred. If resolveValues consist of only a single Deferred, just use that. - deferred = remaining === 1 ? subordinate : jQuery.Deferred(), - - // Update function for both resolve and progress values - updateFunc = function( i, contexts, values ) { - return function( value ) { - contexts[ i ] = this; - values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value; - if( values === progressValues ) { - deferred.notifyWith( contexts, values ); - } else if ( !( --remaining ) ) { - deferred.resolveWith( contexts, values ); - } - }; - }, - - progressValues, progressContexts, resolveContexts; - - // add listeners to Deferred subordinates; treat others as resolved - if ( length > 1 ) { - progressValues = new Array( length ); - progressContexts = new Array( length ); - resolveContexts = new Array( length ); - for ( ; i < length; i++ ) { - if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { - resolveValues[ i ].promise() - .done( updateFunc( i, resolveContexts, resolveValues ) ) - .fail( deferred.reject ) - .progress( updateFunc( i, progressContexts, progressValues ) ); - } else { - --remaining; - } - } - } - - // if we're not waiting on anything, resolve the master - if ( !remaining ) { - deferred.resolveWith( resolveContexts, resolveValues ); - } - - return deferred.promise(); - } -}); -jQuery.support = (function() { - - var support, - all, - a, - select, - opt, - input, - fragment, - eventName, - i, - isSupported, - clickFn, - div = document.createElement("div"); - - // Preliminary tests - div.setAttribute( "className", "t" ); - div.innerHTML = "
    a"; - - all = div.getElementsByTagName("*"); - a = div.getElementsByTagName("a")[ 0 ]; - a.style.cssText = "top:1px;float:left;opacity:.5"; - - // Can't get basic test support - if ( !all || !all.length || !a ) { - return {}; - } - - // First batch of supports tests - select = document.createElement("select"); - opt = select.appendChild( document.createElement("option") ); - input = div.getElementsByTagName("input")[ 0 ]; - - support = { - // IE strips leading whitespace when .innerHTML is used - leadingWhitespace: ( div.firstChild.nodeType === 3 ), - - // Make sure that tbody elements aren't automatically inserted - // IE will insert them into empty tables - tbody: !div.getElementsByTagName("tbody").length, - - // Make sure that link elements get serialized correctly by innerHTML - // This requires a wrapper element in IE - htmlSerialize: !!div.getElementsByTagName("link").length, - - // Get the style information from getAttribute - // (IE uses .cssText instead) - style: /top/.test( a.getAttribute("style") ), - - // Make sure that URLs aren't manipulated - // (IE normalizes it by default) - hrefNormalized: ( a.getAttribute("href") === "/a" ), - - // Make sure that element opacity exists - // (IE uses filter instead) - // Use a regex to work around a WebKit issue. See #5145 - opacity: /^0.5/.test( a.style.opacity ), - - // Verify style float existence - // (IE uses styleFloat instead of cssFloat) - cssFloat: !!a.style.cssFloat, - - // Make sure that if no value is specified for a checkbox - // that it defaults to "on". - // (WebKit defaults to "" instead) - checkOn: ( input.value === "on" ), - - // Make sure that a selected-by-default option has a working selected property. - // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) - optSelected: opt.selected, - - // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) - getSetAttribute: div.className !== "t", - - // Tests for enctype support on a form(#6743) - enctype: !!document.createElement("form").enctype, - - // Makes sure cloning an html5 element does not cause problems - // Where outerHTML is undefined, this still works - html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav>", - - // jQuery.support.boxModel DEPRECATED in 1.8 since we don't support Quirks Mode - boxModel: ( document.compatMode === "CSS1Compat" ), - - // Will be defined later - submitBubbles: true, - changeBubbles: true, - focusinBubbles: false, - deleteExpando: true, - noCloneEvent: true, - inlineBlockNeedsLayout: false, - shrinkWrapBlocks: false, - reliableMarginRight: true, - boxSizingReliable: true, - pixelPosition: false - }; - - // Make sure checked status is properly cloned - input.checked = true; - support.noCloneChecked = input.cloneNode( true ).checked; - - // Make sure that the options inside disabled selects aren't marked as disabled - // (WebKit marks them as disabled) - select.disabled = true; - support.optDisabled = !opt.disabled; - - // Test to see if it's possible to delete an expando from an element - // Fails in Internet Explorer - try { - delete div.test; - } catch( e ) { - support.deleteExpando = false; - } - - if ( !div.addEventListener && div.attachEvent && div.fireEvent ) { - div.attachEvent( "onclick", clickFn = function() { - // Cloning a node shouldn't copy over any - // bound event handlers (IE does this) - support.noCloneEvent = false; - }); - div.cloneNode( true ).fireEvent("onclick"); - div.detachEvent( "onclick", clickFn ); - } - - // Check if a radio maintains its value - // after being appended to the DOM - input = document.createElement("input"); - input.value = "t"; - input.setAttribute( "type", "radio" ); - support.radioValue = input.value === "t"; - - input.setAttribute( "checked", "checked" ); - - // #11217 - WebKit loses check when the name is after the checked attribute - input.setAttribute( "name", "t" ); - - div.appendChild( input ); - fragment = document.createDocumentFragment(); - fragment.appendChild( div.lastChild ); - - // WebKit doesn't clone checked state correctly in fragments - support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; - - // Check if a disconnected checkbox will retain its checked - // value of true after appended to the DOM (IE6/7) - support.appendChecked = input.checked; - - fragment.removeChild( input ); - fragment.appendChild( div ); - - // Technique from Juriy Zaytsev - // http://perfectionkills.com/detecting-event-support-without-browser-sniffing/ - // We only care about the case where non-standard event systems - // are used, namely in IE. Short-circuiting here helps us to - // avoid an eval call (in setAttribute) which can cause CSP - // to go haywire. See: https://developer.mozilla.org/en/Security/CSP - if ( div.attachEvent ) { - for ( i in { - submit: true, - change: true, - focusin: true - }) { - eventName = "on" + i; - isSupported = ( eventName in div ); - if ( !isSupported ) { - div.setAttribute( eventName, "return;" ); - isSupported = ( typeof div[ eventName ] === "function" ); - } - support[ i + "Bubbles" ] = isSupported; - } - } - - // Run tests that need a body at doc ready - jQuery(function() { - var container, div, tds, marginDiv, - divReset = "padding:0;margin:0;border:0;display:block;overflow:hidden;", - body = document.getElementsByTagName("body")[0]; - - if ( !body ) { - // Return for frameset docs that don't have a body - return; - } - - container = document.createElement("div"); - container.style.cssText = "visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px"; - body.insertBefore( container, body.firstChild ); - - // Construct the test element - div = document.createElement("div"); - container.appendChild( div ); - - // Check if table cells still have offsetWidth/Height when they are set - // to display:none and there are still other visible table cells in a - // table row; if so, offsetWidth/Height are not reliable for use when - // determining if an element has been hidden directly using - // display:none (it is still safe to use offsets if a parent element is - // hidden; don safety goggles and see bug #4512 for more information). - // (only IE 8 fails this test) - div.innerHTML = "
    t
    "; - tds = div.getElementsByTagName("td"); - tds[ 0 ].style.cssText = "padding:0;margin:0;border:0;display:none"; - isSupported = ( tds[ 0 ].offsetHeight === 0 ); - - tds[ 0 ].style.display = ""; - tds[ 1 ].style.display = "none"; - - // Check if empty table cells still have offsetWidth/Height - // (IE <= 8 fail this test) - support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 ); - - // Check box-sizing and margin behavior - div.innerHTML = ""; - div.style.cssText = "box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;"; - support.boxSizing = ( div.offsetWidth === 4 ); - support.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== 1 ); - - // NOTE: To any future maintainer, we've window.getComputedStyle - // because jsdom on node.js will break without it. - if ( window.getComputedStyle ) { - support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%"; - support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px"; - - // Check if div with explicit width and no margin-right incorrectly - // gets computed margin-right based on width of container. For more - // info see bug #3333 - // Fails in WebKit before Feb 2011 nightlies - // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right - marginDiv = document.createElement("div"); - marginDiv.style.cssText = div.style.cssText = divReset; - marginDiv.style.marginRight = marginDiv.style.width = "0"; - div.style.width = "1px"; - div.appendChild( marginDiv ); - support.reliableMarginRight = - !parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight ); - } - - if ( typeof div.style.zoom !== "undefined" ) { - // Check if natively block-level elements act like inline-block - // elements when setting their display to 'inline' and giving - // them layout - // (IE < 8 does this) - div.innerHTML = ""; - div.style.cssText = divReset + "width:1px;padding:1px;display:inline;zoom:1"; - support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 ); - - // Check if elements with layout shrink-wrap their children - // (IE 6 does this) - div.style.display = "block"; - div.style.overflow = "visible"; - div.innerHTML = "
    "; - div.firstChild.style.width = "5px"; - support.shrinkWrapBlocks = ( div.offsetWidth !== 3 ); - - container.style.zoom = 1; - } - - // Null elements to avoid leaks in IE - body.removeChild( container ); - container = div = tds = marginDiv = null; - }); - - // Null elements to avoid leaks in IE - fragment.removeChild( div ); - all = a = select = opt = input = fragment = div = null; - - return support; -})(); -var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/, - rmultiDash = /([A-Z])/g; - -jQuery.extend({ - cache: {}, - - deletedIds: [], - - // Please use with caution - uuid: 0, - - // Unique for each copy of jQuery on the page - // Non-digits removed to match rinlinejQuery - expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ), - - // The following elements throw uncatchable exceptions if you - // attempt to add expando properties to them. - noData: { - "embed": true, - // Ban all objects except for Flash (which handle expandos) - "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000", - "applet": true - }, - - hasData: function( elem ) { - elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; - return !!elem && !isEmptyDataObject( elem ); - }, - - data: function( elem, name, data, pvt /* Internal Use Only */ ) { - if ( !jQuery.acceptData( elem ) ) { - return; - } - - var thisCache, ret, - internalKey = jQuery.expando, - getByName = typeof name === "string", - - // We have to handle DOM nodes and JS objects differently because IE6-7 - // can't GC object references properly across the DOM-JS boundary - isNode = elem.nodeType, - - // Only DOM nodes need the global jQuery cache; JS object data is - // attached directly to the object so GC can occur automatically - cache = isNode ? jQuery.cache : elem, - - // Only defining an ID for JS objects if its cache already exists allows - // the code to shortcut on the same path as a DOM node with no cache - id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey; - - // Avoid doing any more work than we need to when trying to get data on an - // object that has no data at all - if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && getByName && data === undefined ) { - return; - } - - if ( !id ) { - // Only DOM nodes need a new unique ID for each element since their data - // ends up in the global cache - if ( isNode ) { - elem[ internalKey ] = id = jQuery.deletedIds.pop() || ++jQuery.uuid; - } else { - id = internalKey; - } - } - - if ( !cache[ id ] ) { - cache[ id ] = {}; - - // Avoids exposing jQuery metadata on plain JS objects when the object - // is serialized using JSON.stringify - if ( !isNode ) { - cache[ id ].toJSON = jQuery.noop; - } - } - - // An object can be passed to jQuery.data instead of a key/value pair; this gets - // shallow copied over onto the existing cache - if ( typeof name === "object" || typeof name === "function" ) { - if ( pvt ) { - cache[ id ] = jQuery.extend( cache[ id ], name ); - } else { - cache[ id ].data = jQuery.extend( cache[ id ].data, name ); - } - } - - thisCache = cache[ id ]; - - // jQuery data() is stored in a separate object inside the object's internal data - // cache in order to avoid key collisions between internal data and user-defined - // data. - if ( !pvt ) { - if ( !thisCache.data ) { - thisCache.data = {}; - } - - thisCache = thisCache.data; - } - - if ( data !== undefined ) { - thisCache[ jQuery.camelCase( name ) ] = data; - } - - // Check for both converted-to-camel and non-converted data property names - // If a data property was specified - if ( getByName ) { - - // First Try to find as-is property data - ret = thisCache[ name ]; - - // Test for null|undefined property data - if ( ret == null ) { - - // Try to find the camelCased property - ret = thisCache[ jQuery.camelCase( name ) ]; - } - } else { - ret = thisCache; - } - - return ret; - }, - - removeData: function( elem, name, pvt /* Internal Use Only */ ) { - if ( !jQuery.acceptData( elem ) ) { - return; - } - - var thisCache, i, l, - - isNode = elem.nodeType, - - // See jQuery.data for more information - cache = isNode ? jQuery.cache : elem, - id = isNode ? elem[ jQuery.expando ] : jQuery.expando; - - // If there is already no cache entry for this object, there is no - // purpose in continuing - if ( !cache[ id ] ) { - return; - } - - if ( name ) { - - thisCache = pvt ? cache[ id ] : cache[ id ].data; - - if ( thisCache ) { - - // Support array or space separated string names for data keys - if ( !jQuery.isArray( name ) ) { - - // try the string as a key before any manipulation - if ( name in thisCache ) { - name = [ name ]; - } else { - - // split the camel cased version by spaces unless a key with the spaces exists - name = jQuery.camelCase( name ); - if ( name in thisCache ) { - name = [ name ]; - } else { - name = name.split(" "); - } - } - } - - for ( i = 0, l = name.length; i < l; i++ ) { - delete thisCache[ name[i] ]; - } - - // If there is no data left in the cache, we want to continue - // and let the cache object itself get destroyed - if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) { - return; - } - } - } - - // See jQuery.data for more information - if ( !pvt ) { - delete cache[ id ].data; - - // Don't destroy the parent cache unless the internal data object - // had been the only thing left in it - if ( !isEmptyDataObject( cache[ id ] ) ) { - return; - } - } - - // Destroy the cache - if ( isNode ) { - jQuery.cleanData( [ elem ], true ); - - // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080) - } else if ( jQuery.support.deleteExpando || cache != cache.window ) { - delete cache[ id ]; - - // When all else fails, null - } else { - cache[ id ] = null; - } - }, - - // For internal use only. - _data: function( elem, name, data ) { - return jQuery.data( elem, name, data, true ); - }, - - // A method for determining if a DOM node can handle the data expando - acceptData: function( elem ) { - var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ]; - - // nodes accept data unless otherwise specified; rejection can be conditional - return !noData || noData !== true && elem.getAttribute("classid") === noData; - } -}); - -jQuery.fn.extend({ - data: function( key, value ) { - var parts, part, attr, name, l, - elem = this[0], - i = 0, - data = null; - - // Gets all values - if ( key === undefined ) { - if ( this.length ) { - data = jQuery.data( elem ); - - if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) { - attr = elem.attributes; - for ( l = attr.length; i < l; i++ ) { - name = attr[i].name; - - if ( name.indexOf( "data-" ) === 0 ) { - name = jQuery.camelCase( name.substring(5) ); - - dataAttr( elem, name, data[ name ] ); - } - } - jQuery._data( elem, "parsedAttrs", true ); - } - } - - return data; - } - - // Sets multiple values - if ( typeof key === "object" ) { - return this.each(function() { - jQuery.data( this, key ); - }); - } - - parts = key.split( ".", 2 ); - parts[1] = parts[1] ? "." + parts[1] : ""; - part = parts[1] + "!"; - - return jQuery.access( this, function( value ) { - - if ( value === undefined ) { - data = this.triggerHandler( "getData" + part, [ parts[0] ] ); - - // Try to fetch any internally stored data first - if ( data === undefined && elem ) { - data = jQuery.data( elem, key ); - data = dataAttr( elem, key, data ); - } - - return data === undefined && parts[1] ? - this.data( parts[0] ) : - data; - } - - parts[1] = value; - this.each(function() { - var self = jQuery( this ); - - self.triggerHandler( "setData" + part, parts ); - jQuery.data( this, key, value ); - self.triggerHandler( "changeData" + part, parts ); - }); - }, null, value, arguments.length > 1, null, false ); - }, - - removeData: function( key ) { - return this.each(function() { - jQuery.removeData( this, key ); - }); - } -}); - -function dataAttr( elem, key, data ) { - // If nothing was found internally, try to fetch any - // data from the HTML5 data-* attribute - if ( data === undefined && elem.nodeType === 1 ) { - - var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); - - data = elem.getAttribute( name ); - - if ( typeof data === "string" ) { - try { - data = data === "true" ? true : - data === "false" ? false : - data === "null" ? null : - // Only convert to a number if it doesn't change the string - +data + "" === data ? +data : - rbrace.test( data ) ? jQuery.parseJSON( data ) : - data; - } catch( e ) {} - - // Make sure we set the data so it isn't changed later - jQuery.data( elem, key, data ); - - } else { - data = undefined; - } - } - - return data; -} - -// checks a cache object for emptiness -function isEmptyDataObject( obj ) { - var name; - for ( name in obj ) { - - // if the public data object is empty, the private is still empty - if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { - continue; - } - if ( name !== "toJSON" ) { - return false; - } - } - - return true; -} -jQuery.extend({ - queue: function( elem, type, data ) { - var queue; - - if ( elem ) { - type = ( type || "fx" ) + "queue"; - queue = jQuery._data( elem, type ); - - // Speed up dequeue by getting out quickly if this is just a lookup - if ( data ) { - if ( !queue || jQuery.isArray(data) ) { - queue = jQuery._data( elem, type, jQuery.makeArray(data) ); - } else { - queue.push( data ); - } - } - return queue || []; - } - }, - - dequeue: function( elem, type ) { - type = type || "fx"; - - var queue = jQuery.queue( elem, type ), - startLength = queue.length, - fn = queue.shift(), - hooks = jQuery._queueHooks( elem, type ), - next = function() { - jQuery.dequeue( elem, type ); - }; - - // If the fx queue is dequeued, always remove the progress sentinel - if ( fn === "inprogress" ) { - fn = queue.shift(); - startLength--; - } - - if ( fn ) { - - // Add a progress sentinel to prevent the fx queue from being - // automatically dequeued - if ( type === "fx" ) { - queue.unshift( "inprogress" ); - } - - // clear up the last queue stop function - delete hooks.stop; - fn.call( elem, next, hooks ); - } - - if ( !startLength && hooks ) { - hooks.empty.fire(); - } - }, - - // not intended for public consumption - generates a queueHooks object, or returns the current one - _queueHooks: function( elem, type ) { - var key = type + "queueHooks"; - return jQuery._data( elem, key ) || jQuery._data( elem, key, { - empty: jQuery.Callbacks("once memory").add(function() { - jQuery.removeData( elem, type + "queue", true ); - jQuery.removeData( elem, key, true ); - }) - }); - } -}); - -jQuery.fn.extend({ - queue: function( type, data ) { - var setter = 2; - - if ( typeof type !== "string" ) { - data = type; - type = "fx"; - setter--; - } - - if ( arguments.length < setter ) { - return jQuery.queue( this[0], type ); - } - - return data === undefined ? - this : - this.each(function() { - var queue = jQuery.queue( this, type, data ); - - // ensure a hooks for this queue - jQuery._queueHooks( this, type ); - - if ( type === "fx" && queue[0] !== "inprogress" ) { - jQuery.dequeue( this, type ); - } - }); - }, - dequeue: function( type ) { - return this.each(function() { - jQuery.dequeue( this, type ); - }); - }, - // Based off of the plugin by Clint Helfers, with permission. - // http://blindsignals.com/index.php/2009/07/jquery-delay/ - delay: function( time, type ) { - time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; - type = type || "fx"; - - return this.queue( type, function( next, hooks ) { - var timeout = setTimeout( next, time ); - hooks.stop = function() { - clearTimeout( timeout ); - }; - }); - }, - clearQueue: function( type ) { - return this.queue( type || "fx", [] ); - }, - // Get a promise resolved when queues of a certain type - // are emptied (fx is the type by default) - promise: function( type, obj ) { - var tmp, - count = 1, - defer = jQuery.Deferred(), - elements = this, - i = this.length, - resolve = function() { - if ( !( --count ) ) { - defer.resolveWith( elements, [ elements ] ); - } - }; - - if ( typeof type !== "string" ) { - obj = type; - type = undefined; - } - type = type || "fx"; - - while( i-- ) { - tmp = jQuery._data( elements[ i ], type + "queueHooks" ); - if ( tmp && tmp.empty ) { - count++; - tmp.empty.add( resolve ); - } - } - resolve(); - return defer.promise( obj ); - } -}); -var nodeHook, boolHook, fixSpecified, - rclass = /[\t\r\n]/g, - rreturn = /\r/g, - rtype = /^(?:button|input)$/i, - rfocusable = /^(?:button|input|object|select|textarea)$/i, - rclickable = /^a(?:rea|)$/i, - rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i, - getSetAttribute = jQuery.support.getSetAttribute; - -jQuery.fn.extend({ - attr: function( name, value ) { - return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 ); - }, - - removeAttr: function( name ) { - return this.each(function() { - jQuery.removeAttr( this, name ); - }); - }, - - prop: function( name, value ) { - return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 ); - }, - - removeProp: function( name ) { - name = jQuery.propFix[ name ] || name; - return this.each(function() { - // try/catch handles cases where IE balks (such as removing a property on window) - try { - this[ name ] = undefined; - delete this[ name ]; - } catch( e ) {} - }); - }, - - addClass: function( value ) { - var classNames, i, l, elem, - setClass, c, cl; - - if ( jQuery.isFunction( value ) ) { - return this.each(function( j ) { - jQuery( this ).addClass( value.call(this, j, this.className) ); - }); - } - - if ( value && typeof value === "string" ) { - classNames = value.split( core_rspace ); - - for ( i = 0, l = this.length; i < l; i++ ) { - elem = this[ i ]; - - if ( elem.nodeType === 1 ) { - if ( !elem.className && classNames.length === 1 ) { - elem.className = value; - - } else { - setClass = " " + elem.className + " "; - - for ( c = 0, cl = classNames.length; c < cl; c++ ) { - if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) { - setClass += classNames[ c ] + " "; - } - } - elem.className = jQuery.trim( setClass ); - } - } - } - } - - return this; - }, - - removeClass: function( value ) { - var removes, className, elem, c, cl, i, l; - - if ( jQuery.isFunction( value ) ) { - return this.each(function( j ) { - jQuery( this ).removeClass( value.call(this, j, this.className) ); - }); - } - if ( (value && typeof value === "string") || value === undefined ) { - removes = ( value || "" ).split( core_rspace ); - - for ( i = 0, l = this.length; i < l; i++ ) { - elem = this[ i ]; - if ( elem.nodeType === 1 && elem.className ) { - - className = (" " + elem.className + " ").replace( rclass, " " ); - - // loop over each item in the removal list - for ( c = 0, cl = removes.length; c < cl; c++ ) { - // Remove until there is nothing to remove, - while ( className.indexOf(" " + removes[ c ] + " ") > -1 ) { - className = className.replace( " " + removes[ c ] + " " , " " ); - } - } - elem.className = value ? jQuery.trim( className ) : ""; - } - } - } - - return this; - }, - - toggleClass: function( value, stateVal ) { - var type = typeof value, - isBool = typeof stateVal === "boolean"; - - if ( jQuery.isFunction( value ) ) { - return this.each(function( i ) { - jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); - }); - } - - return this.each(function() { - if ( type === "string" ) { - // toggle individual class names - var className, - i = 0, - self = jQuery( this ), - state = stateVal, - classNames = value.split( core_rspace ); - - while ( (className = classNames[ i++ ]) ) { - // check each className given, space separated list - state = isBool ? state : !self.hasClass( className ); - self[ state ? "addClass" : "removeClass" ]( className ); - } - - } else if ( type === "undefined" || type === "boolean" ) { - if ( this.className ) { - // store className if set - jQuery._data( this, "__className__", this.className ); - } - - // toggle whole className - this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || ""; - } - }); - }, - - hasClass: function( selector ) { - var className = " " + selector + " ", - i = 0, - l = this.length; - for ( ; i < l; i++ ) { - if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) { - return true; - } - } - - return false; - }, - - val: function( value ) { - var hooks, ret, isFunction, - elem = this[0]; - - if ( !arguments.length ) { - if ( elem ) { - hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ]; - - if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { - return ret; - } - - ret = elem.value; - - return typeof ret === "string" ? - // handle most common string cases - ret.replace(rreturn, "") : - // handle cases where value is null/undef or number - ret == null ? "" : ret; - } - - return; - } - - isFunction = jQuery.isFunction( value ); - - return this.each(function( i ) { - var val, - self = jQuery(this); - - if ( this.nodeType !== 1 ) { - return; - } - - if ( isFunction ) { - val = value.call( this, i, self.val() ); - } else { - val = value; - } - - // Treat null/undefined as ""; convert numbers to string - if ( val == null ) { - val = ""; - } else if ( typeof val === "number" ) { - val += ""; - } else if ( jQuery.isArray( val ) ) { - val = jQuery.map(val, function ( value ) { - return value == null ? "" : value + ""; - }); - } - - hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; - - // If set returns undefined, fall back to normal setting - if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { - this.value = val; - } - }); - } -}); - -jQuery.extend({ - valHooks: { - option: { - get: function( elem ) { - // attributes.value is undefined in Blackberry 4.7 but - // uses .value. See #6932 - var val = elem.attributes.value; - return !val || val.specified ? elem.value : elem.text; - } - }, - select: { - get: function( elem ) { - var value, i, max, option, - index = elem.selectedIndex, - values = [], - options = elem.options, - one = elem.type === "select-one"; - - // Nothing was selected - if ( index < 0 ) { - return null; - } - - // Loop through all the selected options - i = one ? index : 0; - max = one ? index + 1 : options.length; - for ( ; i < max; i++ ) { - option = options[ i ]; - - // Don't return options that are disabled or in a disabled optgroup - if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) && - (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) { - - // Get the specific value for the option - value = jQuery( option ).val(); - - // We don't need an array for one selects - if ( one ) { - return value; - } - - // Multi-Selects return an array - values.push( value ); - } - } - - // Fixes Bug #2551 -- select.val() broken in IE after form.reset() - if ( one && !values.length && options.length ) { - return jQuery( options[ index ] ).val(); - } - - return values; - }, - - set: function( elem, value ) { - var values = jQuery.makeArray( value ); - - jQuery(elem).find("option").each(function() { - this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0; - }); - - if ( !values.length ) { - elem.selectedIndex = -1; - } - return values; - } - } - }, - - // Unused in 1.8, left in so attrFn-stabbers won't die; remove in 1.9 - attrFn: {}, - - attr: function( elem, name, value, pass ) { - var ret, hooks, notxml, - nType = elem.nodeType; - - // don't get/set attributes on text, comment and attribute nodes - if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { - return; - } - - if ( pass && jQuery.isFunction( jQuery.fn[ name ] ) ) { - return jQuery( elem )[ name ]( value ); - } - - // Fallback to prop when attributes are not supported - if ( typeof elem.getAttribute === "undefined" ) { - return jQuery.prop( elem, name, value ); - } - - notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); - - // All attributes are lowercase - // Grab necessary hook if one is defined - if ( notxml ) { - name = name.toLowerCase(); - hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook ); - } - - if ( value !== undefined ) { - - if ( value === null ) { - jQuery.removeAttr( elem, name ); - return; - - } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) { - return ret; - - } else { - elem.setAttribute( name, "" + value ); - return value; - } - - } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) { - return ret; - - } else { - - ret = elem.getAttribute( name ); - - // Non-existent attributes return null, we normalize to undefined - return ret === null ? - undefined : - ret; - } - }, - - removeAttr: function( elem, value ) { - var propName, attrNames, name, isBool, - i = 0; - - if ( value && elem.nodeType === 1 ) { - - attrNames = value.split( core_rspace ); - - for ( ; i < attrNames.length; i++ ) { - name = attrNames[ i ]; - - if ( name ) { - propName = jQuery.propFix[ name ] || name; - isBool = rboolean.test( name ); - - // See #9699 for explanation of this approach (setting first, then removal) - // Do not do this for boolean attributes (see #10870) - if ( !isBool ) { - jQuery.attr( elem, name, "" ); - } - elem.removeAttribute( getSetAttribute ? name : propName ); - - // Set corresponding property to false for boolean attributes - if ( isBool && propName in elem ) { - elem[ propName ] = false; - } - } - } - } - }, - - attrHooks: { - type: { - set: function( elem, value ) { - // We can't allow the type property to be changed (since it causes problems in IE) - if ( rtype.test( elem.nodeName ) && elem.parentNode ) { - jQuery.error( "type property can't be changed" ); - } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { - // Setting the type on a radio button after the value resets the value in IE6-9 - // Reset value to it's default in case type is set after value - // This is for element creation - var val = elem.value; - elem.setAttribute( "type", value ); - if ( val ) { - elem.value = val; - } - return value; - } - } - }, - // Use the value property for back compat - // Use the nodeHook for button elements in IE6/7 (#1954) - value: { - get: function( elem, name ) { - if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { - return nodeHook.get( elem, name ); - } - return name in elem ? - elem.value : - null; - }, - set: function( elem, value, name ) { - if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { - return nodeHook.set( elem, value, name ); - } - // Does not return so that setAttribute is also used - elem.value = value; - } - } - }, - - propFix: { - tabindex: "tabIndex", - readonly: "readOnly", - "for": "htmlFor", - "class": "className", - maxlength: "maxLength", - cellspacing: "cellSpacing", - cellpadding: "cellPadding", - rowspan: "rowSpan", - colspan: "colSpan", - usemap: "useMap", - frameborder: "frameBorder", - contenteditable: "contentEditable" - }, - - prop: function( elem, name, value ) { - var ret, hooks, notxml, - nType = elem.nodeType; - - // don't get/set properties on text, comment and attribute nodes - if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { - return; - } - - notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); - - if ( notxml ) { - // Fix name and attach hooks - name = jQuery.propFix[ name ] || name; - hooks = jQuery.propHooks[ name ]; - } - - if ( value !== undefined ) { - if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { - return ret; - - } else { - return ( elem[ name ] = value ); - } - - } else { - if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { - return ret; - - } else { - return elem[ name ]; - } - } - }, - - propHooks: { - tabIndex: { - get: function( elem ) { - // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set - // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ - var attributeNode = elem.getAttributeNode("tabindex"); - - return attributeNode && attributeNode.specified ? - parseInt( attributeNode.value, 10 ) : - rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? - 0 : - undefined; - } - } - } -}); - -// Hook for boolean attributes -boolHook = { - get: function( elem, name ) { - // Align boolean attributes with corresponding properties - // Fall back to attribute presence where some booleans are not supported - var attrNode, - property = jQuery.prop( elem, name ); - return property === true || typeof property !== "boolean" && ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ? - name.toLowerCase() : - undefined; - }, - set: function( elem, value, name ) { - var propName; - if ( value === false ) { - // Remove boolean attributes when set to false - jQuery.removeAttr( elem, name ); - } else { - // value is true since we know at this point it's type boolean and not false - // Set boolean attributes to the same name and set the DOM property - propName = jQuery.propFix[ name ] || name; - if ( propName in elem ) { - // Only set the IDL specifically if it already exists on the element - elem[ propName ] = true; - } - - elem.setAttribute( name, name.toLowerCase() ); - } - return name; - } -}; - -// IE6/7 do not support getting/setting some attributes with get/setAttribute -if ( !getSetAttribute ) { - - fixSpecified = { - name: true, - id: true, - coords: true - }; - - // Use this for any attribute in IE6/7 - // This fixes almost every IE6/7 issue - nodeHook = jQuery.valHooks.button = { - get: function( elem, name ) { - var ret; - ret = elem.getAttributeNode( name ); - return ret && ( fixSpecified[ name ] ? ret.value !== "" : ret.specified ) ? - ret.value : - undefined; - }, - set: function( elem, value, name ) { - // Set the existing or create a new attribute node - var ret = elem.getAttributeNode( name ); - if ( !ret ) { - ret = document.createAttribute( name ); - elem.setAttributeNode( ret ); - } - return ( ret.value = value + "" ); - } - }; - - // Set width and height to auto instead of 0 on empty string( Bug #8150 ) - // This is for removals - jQuery.each([ "width", "height" ], function( i, name ) { - jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { - set: function( elem, value ) { - if ( value === "" ) { - elem.setAttribute( name, "auto" ); - return value; - } - } - }); - }); - - // Set contenteditable to false on removals(#10429) - // Setting to empty string throws an error as an invalid value - jQuery.attrHooks.contenteditable = { - get: nodeHook.get, - set: function( elem, value, name ) { - if ( value === "" ) { - value = "false"; - } - nodeHook.set( elem, value, name ); - } - }; -} - - -// Some attributes require a special call on IE -if ( !jQuery.support.hrefNormalized ) { - jQuery.each([ "href", "src", "width", "height" ], function( i, name ) { - jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { - get: function( elem ) { - var ret = elem.getAttribute( name, 2 ); - return ret === null ? undefined : ret; - } - }); - }); -} - -if ( !jQuery.support.style ) { - jQuery.attrHooks.style = { - get: function( elem ) { - // Return undefined in the case of empty string - // Normalize to lowercase since IE uppercases css property names - return elem.style.cssText.toLowerCase() || undefined; - }, - set: function( elem, value ) { - return ( elem.style.cssText = "" + value ); - } - }; -} - -// Safari mis-reports the default selected property of an option -// Accessing the parent's selectedIndex property fixes it -if ( !jQuery.support.optSelected ) { - jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, { - get: function( elem ) { - var parent = elem.parentNode; - - if ( parent ) { - parent.selectedIndex; - - // Make sure that it also works with optgroups, see #5701 - if ( parent.parentNode ) { - parent.parentNode.selectedIndex; - } - } - return null; - } - }); -} - -// IE6/7 call enctype encoding -if ( !jQuery.support.enctype ) { - jQuery.propFix.enctype = "encoding"; -} - -// Radios and checkboxes getter/setter -if ( !jQuery.support.checkOn ) { - jQuery.each([ "radio", "checkbox" ], function() { - jQuery.valHooks[ this ] = { - get: function( elem ) { - // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified - return elem.getAttribute("value") === null ? "on" : elem.value; - } - }; - }); -} -jQuery.each([ "radio", "checkbox" ], function() { - jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], { - set: function( elem, value ) { - if ( jQuery.isArray( value ) ) { - return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 ); - } - } - }); -}); -var rformElems = /^(?:textarea|input|select)$/i, - rtypenamespace = /^([^\.]*|)(?:\.(.+)|)$/, - rhoverHack = /(?:^|\s)hover(\.\S+|)\b/, - rkeyEvent = /^key/, - rmouseEvent = /^(?:mouse|contextmenu)|click/, - rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, - hoverHack = function( events ) { - return jQuery.event.special.hover ? events : events.replace( rhoverHack, "mouseenter$1 mouseleave$1" ); - }; - -/* - * Helper functions for managing events -- not part of the public interface. - * Props to Dean Edwards' addEvent library for many of the ideas. - */ -jQuery.event = { - - add: function( elem, types, handler, data, selector ) { - - var elemData, eventHandle, events, - t, tns, type, namespaces, handleObj, - handleObjIn, handlers, special; - - // Don't attach events to noData or text/comment nodes (allow plain objects tho) - if ( elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler || !(elemData = jQuery._data( elem )) ) { - return; - } - - // Caller can pass in an object of custom data in lieu of the handler - if ( handler.handler ) { - handleObjIn = handler; - handler = handleObjIn.handler; - selector = handleObjIn.selector; - } - - // Make sure that the handler has a unique ID, used to find/remove it later - if ( !handler.guid ) { - handler.guid = jQuery.guid++; - } - - // Init the element's event structure and main handler, if this is the first - events = elemData.events; - if ( !events ) { - elemData.events = events = {}; - } - eventHandle = elemData.handle; - if ( !eventHandle ) { - elemData.handle = eventHandle = function( e ) { - // Discard the second event of a jQuery.event.trigger() and - // when an event is called after a page has unloaded - return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ? - jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : - undefined; - }; - // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events - eventHandle.elem = elem; - } - - // Handle multiple events separated by a space - // jQuery(...).bind("mouseover mouseout", fn); - types = jQuery.trim( hoverHack(types) ).split( " " ); - for ( t = 0; t < types.length; t++ ) { - - tns = rtypenamespace.exec( types[t] ) || []; - type = tns[1]; - namespaces = ( tns[2] || "" ).split( "." ).sort(); - - // If event changes its type, use the special event handlers for the changed type - special = jQuery.event.special[ type ] || {}; - - // If selector defined, determine special event api type, otherwise given type - type = ( selector ? special.delegateType : special.bindType ) || type; - - // Update special based on newly reset type - special = jQuery.event.special[ type ] || {}; - - // handleObj is passed to all event handlers - handleObj = jQuery.extend({ - type: type, - origType: tns[1], - data: data, - handler: handler, - guid: handler.guid, - selector: selector, - namespace: namespaces.join(".") - }, handleObjIn ); - - // Init the event handler queue if we're the first - handlers = events[ type ]; - if ( !handlers ) { - handlers = events[ type ] = []; - handlers.delegateCount = 0; - - // Only use addEventListener/attachEvent if the special events handler returns false - if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { - // Bind the global event handler to the element - if ( elem.addEventListener ) { - elem.addEventListener( type, eventHandle, false ); - - } else if ( elem.attachEvent ) { - elem.attachEvent( "on" + type, eventHandle ); - } - } - } - - if ( special.add ) { - special.add.call( elem, handleObj ); - - if ( !handleObj.handler.guid ) { - handleObj.handler.guid = handler.guid; - } - } - - // Add to the element's handler list, delegates in front - if ( selector ) { - handlers.splice( handlers.delegateCount++, 0, handleObj ); - } else { - handlers.push( handleObj ); - } - - // Keep track of which events have ever been used, for event optimization - jQuery.event.global[ type ] = true; - } - - // Nullify elem to prevent memory leaks in IE - elem = null; - }, - - global: {}, - - // Detach an event or set of events from an element - remove: function( elem, types, handler, selector, mappedTypes ) { - - var t, tns, type, origType, namespaces, origCount, - j, events, special, eventType, handleObj, - elemData = jQuery.hasData( elem ) && jQuery._data( elem ); - - if ( !elemData || !(events = elemData.events) ) { - return; - } - - // Once for each type.namespace in types; type may be omitted - types = jQuery.trim( hoverHack( types || "" ) ).split(" "); - for ( t = 0; t < types.length; t++ ) { - tns = rtypenamespace.exec( types[t] ) || []; - type = origType = tns[1]; - namespaces = tns[2]; - - // Unbind all events (on this namespace, if provided) for the element - if ( !type ) { - for ( type in events ) { - jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); - } - continue; - } - - special = jQuery.event.special[ type ] || {}; - type = ( selector? special.delegateType : special.bindType ) || type; - eventType = events[ type ] || []; - origCount = eventType.length; - namespaces = namespaces ? new RegExp("(^|\\.)" + namespaces.split(".").sort().join("\\.(?:.*\\.|)") + "(\\.|$)") : null; - - // Remove matching events - for ( j = 0; j < eventType.length; j++ ) { - handleObj = eventType[ j ]; - - if ( ( mappedTypes || origType === handleObj.origType ) && - ( !handler || handler.guid === handleObj.guid ) && - ( !namespaces || namespaces.test( handleObj.namespace ) ) && - ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { - eventType.splice( j--, 1 ); - - if ( handleObj.selector ) { - eventType.delegateCount--; - } - if ( special.remove ) { - special.remove.call( elem, handleObj ); - } - } - } - - // Remove generic event handler if we removed something and no more handlers exist - // (avoids potential for endless recursion during removal of special event handlers) - if ( eventType.length === 0 && origCount !== eventType.length ) { - if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) { - jQuery.removeEvent( elem, type, elemData.handle ); - } - - delete events[ type ]; - } - } - - // Remove the expando if it's no longer used - if ( jQuery.isEmptyObject( events ) ) { - delete elemData.handle; - - // removeData also checks for emptiness and clears the expando if empty - // so use it instead of delete - jQuery.removeData( elem, "events", true ); - } - }, - - // Events that are safe to short-circuit if no handlers are attached. - // Native DOM events should not be added, they may have inline handlers. - customEvent: { - "getData": true, - "setData": true, - "changeData": true - }, - - trigger: function( event, data, elem, onlyHandlers ) { - // Don't do events on text and comment nodes - if ( elem && (elem.nodeType === 3 || elem.nodeType === 8) ) { - return; - } - - // Event object or event type - var cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType, - type = event.type || event, - namespaces = []; - - // focus/blur morphs to focusin/out; ensure we're not firing them right now - if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { - return; - } - - if ( type.indexOf( "!" ) >= 0 ) { - // Exclusive events trigger only for the exact event (no namespaces) - type = type.slice(0, -1); - exclusive = true; - } - - if ( type.indexOf( "." ) >= 0 ) { - // Namespaced trigger; create a regexp to match event type in handle() - namespaces = type.split("."); - type = namespaces.shift(); - namespaces.sort(); - } - - if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) { - // No jQuery handlers for this event type, and it can't have inline handlers - return; - } - - // Caller can pass in an Event, Object, or just an event type string - event = typeof event === "object" ? - // jQuery.Event object - event[ jQuery.expando ] ? event : - // Object literal - new jQuery.Event( type, event ) : - // Just the event type (string) - new jQuery.Event( type ); - - event.type = type; - event.isTrigger = true; - event.exclusive = exclusive; - event.namespace = namespaces.join( "." ); - event.namespace_re = event.namespace? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)") : null; - ontype = type.indexOf( ":" ) < 0 ? "on" + type : ""; - - // Handle a global trigger - if ( !elem ) { - - // TODO: Stop taunting the data cache; remove global events and always attach to document - cache = jQuery.cache; - for ( i in cache ) { - if ( cache[ i ].events && cache[ i ].events[ type ] ) { - jQuery.event.trigger( event, data, cache[ i ].handle.elem, true ); - } - } - return; - } - - // Clean up the event in case it is being reused - event.result = undefined; - if ( !event.target ) { - event.target = elem; - } - - // Clone any incoming data and prepend the event, creating the handler arg list - data = data != null ? jQuery.makeArray( data ) : []; - data.unshift( event ); - - // Allow special events to draw outside the lines - special = jQuery.event.special[ type ] || {}; - if ( special.trigger && special.trigger.apply( elem, data ) === false ) { - return; - } - - // Determine event propagation path in advance, per W3C events spec (#9951) - // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) - eventPath = [[ elem, special.bindType || type ]]; - if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { - - bubbleType = special.delegateType || type; - cur = rfocusMorph.test( bubbleType + type ) ? elem : elem.parentNode; - for ( old = elem; cur; cur = cur.parentNode ) { - eventPath.push([ cur, bubbleType ]); - old = cur; - } - - // Only add window if we got to document (e.g., not plain obj or detached DOM) - if ( old === (elem.ownerDocument || document) ) { - eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]); - } - } - - // Fire handlers on the event path - for ( i = 0; i < eventPath.length && !event.isPropagationStopped(); i++ ) { - - cur = eventPath[i][0]; - event.type = eventPath[i][1]; - - handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); - if ( handle ) { - handle.apply( cur, data ); - } - // Note that this is a bare JS function and not a jQuery handler - handle = ontype && cur[ ontype ]; - if ( handle && jQuery.acceptData( cur ) && handle.apply( cur, data ) === false ) { - event.preventDefault(); - } - } - event.type = type; - - // If nobody prevented the default action, do it now - if ( !onlyHandlers && !event.isDefaultPrevented() ) { - - if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) && - !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) { - - // Call a native DOM method on the target with the same name name as the event. - // Can't use an .isFunction() check here because IE6/7 fails that test. - // Don't do default actions on window, that's where global variables be (#6170) - // IE<9 dies on focus/blur to hidden element (#1486) - if ( ontype && elem[ type ] && ((type !== "focus" && type !== "blur") || event.target.offsetWidth !== 0) && !jQuery.isWindow( elem ) ) { - - // Don't re-trigger an onFOO event when we call its FOO() method - old = elem[ ontype ]; - - if ( old ) { - elem[ ontype ] = null; - } - - // Prevent re-triggering of the same event, since we already bubbled it above - jQuery.event.triggered = type; - elem[ type ](); - jQuery.event.triggered = undefined; - - if ( old ) { - elem[ ontype ] = old; - } - } - } - } - - return event.result; - }, - - dispatch: function( event ) { - - // Make a writable jQuery.Event from the native event object - event = jQuery.event.fix( event || window.event ); - - var i, j, cur, ret, selMatch, matched, matches, handleObj, sel, related, - handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] || []), - delegateCount = handlers.delegateCount, - args = [].slice.call( arguments ), - run_all = !event.exclusive && !event.namespace, - special = jQuery.event.special[ event.type ] || {}, - handlerQueue = []; - - // Use the fix-ed jQuery.Event rather than the (read-only) native event - args[0] = event; - event.delegateTarget = this; - - // Call the preDispatch hook for the mapped type, and let it bail if desired - if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { - return; - } - - // Determine handlers that should run if there are delegated events - // Avoid non-left-click bubbling in Firefox (#3861) - if ( delegateCount && !(event.button && event.type === "click") ) { - - for ( cur = event.target; cur != this; cur = cur.parentNode || this ) { - - // Don't process clicks (ONLY) on disabled elements (#6911, #8165, #11382, #11764) - if ( cur.disabled !== true || event.type !== "click" ) { - selMatch = {}; - matches = []; - for ( i = 0; i < delegateCount; i++ ) { - handleObj = handlers[ i ]; - sel = handleObj.selector; - - if ( selMatch[ sel ] === undefined ) { - selMatch[ sel ] = jQuery( sel, this ).index( cur ) >= 0; - } - if ( selMatch[ sel ] ) { - matches.push( handleObj ); - } - } - if ( matches.length ) { - handlerQueue.push({ elem: cur, matches: matches }); - } - } - } - } - - // Add the remaining (directly-bound) handlers - if ( handlers.length > delegateCount ) { - handlerQueue.push({ elem: this, matches: handlers.slice( delegateCount ) }); - } - - // Run delegates first; they may want to stop propagation beneath us - for ( i = 0; i < handlerQueue.length && !event.isPropagationStopped(); i++ ) { - matched = handlerQueue[ i ]; - event.currentTarget = matched.elem; - - for ( j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped(); j++ ) { - handleObj = matched.matches[ j ]; - - // Triggered event must either 1) be non-exclusive and have no namespace, or - // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). - if ( run_all || (!event.namespace && !handleObj.namespace) || event.namespace_re && event.namespace_re.test( handleObj.namespace ) ) { - - event.data = handleObj.data; - event.handleObj = handleObj; - - ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) - .apply( matched.elem, args ); - - if ( ret !== undefined ) { - event.result = ret; - if ( ret === false ) { - event.preventDefault(); - event.stopPropagation(); - } - } - } - } - } - - // Call the postDispatch hook for the mapped type - if ( special.postDispatch ) { - special.postDispatch.call( this, event ); - } - - return event.result; - }, - - // Includes some event props shared by KeyEvent and MouseEvent - // *** attrChange attrName relatedNode srcElement are not normalized, non-W3C, deprecated, will be removed in 1.8 *** - props: "attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), - - fixHooks: {}, - - keyHooks: { - props: "char charCode key keyCode".split(" "), - filter: function( event, original ) { - - // Add which for key events - if ( event.which == null ) { - event.which = original.charCode != null ? original.charCode : original.keyCode; - } - - return event; - } - }, - - mouseHooks: { - props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "), - filter: function( event, original ) { - var eventDoc, doc, body, - button = original.button, - fromElement = original.fromElement; - - // Calculate pageX/Y if missing and clientX/Y available - if ( event.pageX == null && original.clientX != null ) { - eventDoc = event.target.ownerDocument || document; - doc = eventDoc.documentElement; - body = eventDoc.body; - - event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); - event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); - } - - // Add relatedTarget, if necessary - if ( !event.relatedTarget && fromElement ) { - event.relatedTarget = fromElement === event.target ? original.toElement : fromElement; - } - - // Add which for click: 1 === left; 2 === middle; 3 === right - // Note: button is not normalized, so don't use it - if ( !event.which && button !== undefined ) { - event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); - } - - return event; - } - }, - - fix: function( event ) { - if ( event[ jQuery.expando ] ) { - return event; - } - - // Create a writable copy of the event object and normalize some properties - var i, prop, - originalEvent = event, - fixHook = jQuery.event.fixHooks[ event.type ] || {}, - copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; - - event = jQuery.Event( originalEvent ); - - for ( i = copy.length; i; ) { - prop = copy[ --i ]; - event[ prop ] = originalEvent[ prop ]; - } - - // Fix target property, if necessary (#1925, IE 6/7/8 & Safari2) - if ( !event.target ) { - event.target = originalEvent.srcElement || document; - } - - // Target should not be a text node (#504, Safari) - if ( event.target.nodeType === 3 ) { - event.target = event.target.parentNode; - } - - // For mouse/key events, metaKey==false if it's undefined (#3368, #11328; IE6/7/8) - event.metaKey = !!event.metaKey; - - return fixHook.filter? fixHook.filter( event, originalEvent ) : event; - }, - - special: { - load: { - // Prevent triggered image.load events from bubbling to window.load - noBubble: true - }, - - focus: { - delegateType: "focusin" - }, - blur: { - delegateType: "focusout" - }, - - beforeunload: { - setup: function( data, namespaces, eventHandle ) { - // We only want to do this special case on windows - if ( jQuery.isWindow( this ) ) { - this.onbeforeunload = eventHandle; - } - }, - - teardown: function( namespaces, eventHandle ) { - if ( this.onbeforeunload === eventHandle ) { - this.onbeforeunload = null; - } - } - } - }, - - simulate: function( type, elem, event, bubble ) { - // Piggyback on a donor event to simulate a different one. - // Fake originalEvent to avoid donor's stopPropagation, but if the - // simulated event prevents default then we do the same on the donor. - var e = jQuery.extend( - new jQuery.Event(), - event, - { type: type, - isSimulated: true, - originalEvent: {} - } - ); - if ( bubble ) { - jQuery.event.trigger( e, null, elem ); - } else { - jQuery.event.dispatch.call( elem, e ); - } - if ( e.isDefaultPrevented() ) { - event.preventDefault(); - } - } -}; - -// Some plugins are using, but it's undocumented/deprecated and will be removed. -// The 1.7 special event interface should provide all the hooks needed now. -jQuery.event.handle = jQuery.event.dispatch; - -jQuery.removeEvent = document.removeEventListener ? - function( elem, type, handle ) { - if ( elem.removeEventListener ) { - elem.removeEventListener( type, handle, false ); - } - } : - function( elem, type, handle ) { - var name = "on" + type; - - if ( elem.detachEvent ) { - - // #8545, #7054, preventing memory leaks for custom events in IE6-8 – - // detachEvent needed property on element, by name of that event, to properly expose it to GC - if ( typeof elem[ name ] === "undefined" ) { - elem[ name ] = null; - } - - elem.detachEvent( name, handle ); - } - }; - -jQuery.Event = function( src, props ) { - // Allow instantiation without the 'new' keyword - if ( !(this instanceof jQuery.Event) ) { - return new jQuery.Event( src, props ); - } - - // Event object - if ( src && src.type ) { - this.originalEvent = src; - this.type = src.type; - - // Events bubbling up the document may have been marked as prevented - // by a handler lower down the tree; reflect the correct value. - this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false || - src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse; - - // Event type - } else { - this.type = src; - } - - // Put explicitly provided properties onto the event object - if ( props ) { - jQuery.extend( this, props ); - } - - // Create a timestamp if incoming event doesn't have one - this.timeStamp = src && src.timeStamp || jQuery.now(); - - // Mark it as fixed - this[ jQuery.expando ] = true; -}; - -function returnFalse() { - return false; -} -function returnTrue() { - return true; -} - -// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding -// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html -jQuery.Event.prototype = { - preventDefault: function() { - this.isDefaultPrevented = returnTrue; - - var e = this.originalEvent; - if ( !e ) { - return; - } - - // if preventDefault exists run it on the original event - if ( e.preventDefault ) { - e.preventDefault(); - - // otherwise set the returnValue property of the original event to false (IE) - } else { - e.returnValue = false; - } - }, - stopPropagation: function() { - this.isPropagationStopped = returnTrue; - - var e = this.originalEvent; - if ( !e ) { - return; - } - // if stopPropagation exists run it on the original event - if ( e.stopPropagation ) { - e.stopPropagation(); - } - // otherwise set the cancelBubble property of the original event to true (IE) - e.cancelBubble = true; - }, - stopImmediatePropagation: function() { - this.isImmediatePropagationStopped = returnTrue; - this.stopPropagation(); - }, - isDefaultPrevented: returnFalse, - isPropagationStopped: returnFalse, - isImmediatePropagationStopped: returnFalse -}; - -// Create mouseenter/leave events using mouseover/out and event-time checks -jQuery.each({ - mouseenter: "mouseover", - mouseleave: "mouseout" -}, function( orig, fix ) { - jQuery.event.special[ orig ] = { - delegateType: fix, - bindType: fix, - - handle: function( event ) { - var ret, - target = this, - related = event.relatedTarget, - handleObj = event.handleObj, - selector = handleObj.selector; - - // For mousenter/leave call the handler if related is outside the target. - // NB: No relatedTarget if the mouse left/entered the browser window - if ( !related || (related !== target && !jQuery.contains( target, related )) ) { - event.type = handleObj.origType; - ret = handleObj.handler.apply( this, arguments ); - event.type = fix; - } - return ret; - } - }; -}); - -// IE submit delegation -if ( !jQuery.support.submitBubbles ) { - - jQuery.event.special.submit = { - setup: function() { - // Only need this for delegated form submit events - if ( jQuery.nodeName( this, "form" ) ) { - return false; - } - - // Lazy-add a submit handler when a descendant form may potentially be submitted - jQuery.event.add( this, "click._submit keypress._submit", function( e ) { - // Node name check avoids a VML-related crash in IE (#9807) - var elem = e.target, - form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; - if ( form && !jQuery._data( form, "_submit_attached" ) ) { - jQuery.event.add( form, "submit._submit", function( event ) { - event._submit_bubble = true; - }); - jQuery._data( form, "_submit_attached", true ); - } - }); - // return undefined since we don't need an event listener - }, - - postDispatch: function( event ) { - // If form was submitted by the user, bubble the event up the tree - if ( event._submit_bubble ) { - delete event._submit_bubble; - if ( this.parentNode && !event.isTrigger ) { - jQuery.event.simulate( "submit", this.parentNode, event, true ); - } - } - }, - - teardown: function() { - // Only need this for delegated form submit events - if ( jQuery.nodeName( this, "form" ) ) { - return false; - } - - // Remove delegated handlers; cleanData eventually reaps submit handlers attached above - jQuery.event.remove( this, "._submit" ); - } - }; -} - -// IE change delegation and checkbox/radio fix -if ( !jQuery.support.changeBubbles ) { - - jQuery.event.special.change = { - - setup: function() { - - if ( rformElems.test( this.nodeName ) ) { - // IE doesn't fire change on a check/radio until blur; trigger it on click - // after a propertychange. Eat the blur-change in special.change.handle. - // This still fires onchange a second time for check/radio after blur. - if ( this.type === "checkbox" || this.type === "radio" ) { - jQuery.event.add( this, "propertychange._change", function( event ) { - if ( event.originalEvent.propertyName === "checked" ) { - this._just_changed = true; - } - }); - jQuery.event.add( this, "click._change", function( event ) { - if ( this._just_changed && !event.isTrigger ) { - this._just_changed = false; - } - // Allow triggered, simulated change events (#11500) - jQuery.event.simulate( "change", this, event, true ); - }); - } - return false; - } - // Delegated event; lazy-add a change handler on descendant inputs - jQuery.event.add( this, "beforeactivate._change", function( e ) { - var elem = e.target; - - if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "_change_attached" ) ) { - jQuery.event.add( elem, "change._change", function( event ) { - if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { - jQuery.event.simulate( "change", this.parentNode, event, true ); - } - }); - jQuery._data( elem, "_change_attached", true ); - } - }); - }, - - handle: function( event ) { - var elem = event.target; - - // Swallow native change events from checkbox/radio, we already triggered them above - if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) { - return event.handleObj.handler.apply( this, arguments ); - } - }, - - teardown: function() { - jQuery.event.remove( this, "._change" ); - - return !rformElems.test( this.nodeName ); - } - }; -} - -// Create "bubbling" focus and blur events -if ( !jQuery.support.focusinBubbles ) { - jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { - - // Attach a single capturing handler while someone wants focusin/focusout - var attaches = 0, - handler = function( event ) { - jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); - }; - - jQuery.event.special[ fix ] = { - setup: function() { - if ( attaches++ === 0 ) { - document.addEventListener( orig, handler, true ); - } - }, - teardown: function() { - if ( --attaches === 0 ) { - document.removeEventListener( orig, handler, true ); - } - } - }; - }); -} - -jQuery.fn.extend({ - - on: function( types, selector, data, fn, /*INTERNAL*/ one ) { - var origFn, type; - - // Types can be a map of types/handlers - if ( typeof types === "object" ) { - // ( types-Object, selector, data ) - if ( typeof selector !== "string" ) { // && selector != null - // ( types-Object, data ) - data = data || selector; - selector = undefined; - } - for ( type in types ) { - this.on( type, selector, data, types[ type ], one ); - } - return this; - } - - if ( data == null && fn == null ) { - // ( types, fn ) - fn = selector; - data = selector = undefined; - } else if ( fn == null ) { - if ( typeof selector === "string" ) { - // ( types, selector, fn ) - fn = data; - data = undefined; - } else { - // ( types, data, fn ) - fn = data; - data = selector; - selector = undefined; - } - } - if ( fn === false ) { - fn = returnFalse; - } else if ( !fn ) { - return this; - } - - if ( one === 1 ) { - origFn = fn; - fn = function( event ) { - // Can use an empty set, since event contains the info - jQuery().off( event ); - return origFn.apply( this, arguments ); - }; - // Use same guid so caller can remove using origFn - fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); - } - return this.each( function() { - jQuery.event.add( this, types, fn, data, selector ); - }); - }, - one: function( types, selector, data, fn ) { - return this.on( types, selector, data, fn, 1 ); - }, - off: function( types, selector, fn ) { - var handleObj, type; - if ( types && types.preventDefault && types.handleObj ) { - // ( event ) dispatched jQuery.Event - handleObj = types.handleObj; - jQuery( types.delegateTarget ).off( - handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, - handleObj.selector, - handleObj.handler - ); - return this; - } - if ( typeof types === "object" ) { - // ( types-object [, selector] ) - for ( type in types ) { - this.off( type, selector, types[ type ] ); - } - return this; - } - if ( selector === false || typeof selector === "function" ) { - // ( types [, fn] ) - fn = selector; - selector = undefined; - } - if ( fn === false ) { - fn = returnFalse; - } - return this.each(function() { - jQuery.event.remove( this, types, fn, selector ); - }); - }, - - bind: function( types, data, fn ) { - return this.on( types, null, data, fn ); - }, - unbind: function( types, fn ) { - return this.off( types, null, fn ); - }, - - live: function( types, data, fn ) { - jQuery( this.context ).on( types, this.selector, data, fn ); - return this; - }, - die: function( types, fn ) { - jQuery( this.context ).off( types, this.selector || "**", fn ); - return this; - }, - - delegate: function( selector, types, data, fn ) { - return this.on( types, selector, data, fn ); - }, - undelegate: function( selector, types, fn ) { - // ( namespace ) or ( selector, types [, fn] ) - return arguments.length == 1? this.off( selector, "**" ) : this.off( types, selector || "**", fn ); - }, - - trigger: function( type, data ) { - return this.each(function() { - jQuery.event.trigger( type, data, this ); - }); - }, - triggerHandler: function( type, data ) { - if ( this[0] ) { - return jQuery.event.trigger( type, data, this[0], true ); - } - }, - - toggle: function( fn ) { - // Save reference to arguments for access in closure - var args = arguments, - guid = fn.guid || jQuery.guid++, - i = 0, - toggler = function( event ) { - // Figure out which function to execute - var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i; - jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 ); - - // Make sure that clicks stop - event.preventDefault(); - - // and execute the function - return args[ lastToggle ].apply( this, arguments ) || false; - }; - - // link all the functions, so any of them can unbind this click handler - toggler.guid = guid; - while ( i < args.length ) { - args[ i++ ].guid = guid; - } - - return this.click( toggler ); - }, - - hover: function( fnOver, fnOut ) { - return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); - } -}); - -jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " + - "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + - "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) { - - // Handle event binding - jQuery.fn[ name ] = function( data, fn ) { - if ( fn == null ) { - fn = data; - data = null; - } - - return arguments.length > 0 ? - this.on( name, null, data, fn ) : - this.trigger( name ); - }; - - if ( rkeyEvent.test( name ) ) { - jQuery.event.fixHooks[ name ] = jQuery.event.keyHooks; - } - - if ( rmouseEvent.test( name ) ) { - jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks; - } -}); -/*! - * Sizzle CSS Selector Engine - * Copyright 2012 jQuery Foundation and other contributors - * Released under the MIT license - * http://sizzlejs.com/ - */ -(function( window, undefined ) { - -var dirruns, - cachedruns, - assertGetIdNotName, - Expr, - getText, - isXML, - contains, - compile, - sortOrder, - hasDuplicate, - - baseHasDuplicate = true, - strundefined = "undefined", - - expando = ( "sizcache" + Math.random() ).replace( ".", "" ), - - document = window.document, - docElem = document.documentElement, - done = 0, - slice = [].slice, - push = [].push, - - // Augment a function for special use by Sizzle - markFunction = function( fn, value ) { - fn[ expando ] = value || true; - return fn; - }, - - createCache = function() { - var cache = {}, - keys = []; - - return markFunction(function( key, value ) { - // Only keep the most recent entries - if ( keys.push( key ) > Expr.cacheLength ) { - delete cache[ keys.shift() ]; - } - - return (cache[ key ] = value); - }, cache ); - }, - - classCache = createCache(), - tokenCache = createCache(), - compilerCache = createCache(), - - // Regex - - // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace - whitespace = "[\\x20\\t\\r\\n\\f]", - // http://www.w3.org/TR/css3-syntax/#characters - characterEncoding = "(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+", - - // Loosely modeled on CSS identifier characters - // An unquoted value should be a CSS identifier (http://www.w3.org/TR/css3-selectors/#attribute-selectors) - // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier - identifier = characterEncoding.replace( "w", "w#" ), - - // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors - operators = "([*^$|!~]?=)", - attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace + - "*(?:" + operators + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]", - - // Prefer arguments not in parens/brackets, - // then attribute selectors and non-pseudos (denoted by :), - // then anything else - // These preferences are here to reduce the number of selectors - // needing tokenize in the PSEUDO preFilter - pseudos = ":(" + characterEncoding + ")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:" + attributes + ")|[^:]|\\\\.)*|.*))\\)|)", - - // For matchExpr.POS and matchExpr.needsContext - pos = ":(nth|eq|gt|lt|first|last|even|odd)(?:\\(((?:-\\d)?\\d*)\\)|)(?=[^-]|$)", - - // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter - rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), - - rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), - rcombinators = new RegExp( "^" + whitespace + "*([\\x20\\t\\r\\n\\f>+~])" + whitespace + "*" ), - rpseudo = new RegExp( pseudos ), - - // Easily-parseable/retrievable ID or TAG or CLASS selectors - rquickExpr = /^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/, - - rnot = /^:not/, - rsibling = /[\x20\t\r\n\f]*[+~]/, - rendsWithNot = /:not\($/, - - rheader = /h\d/i, - rinputs = /input|select|textarea|button/i, - - rbackslash = /\\(?!\\)/g, - - matchExpr = { - "ID": new RegExp( "^#(" + characterEncoding + ")" ), - "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), - "NAME": new RegExp( "^\\[name=['\"]?(" + characterEncoding + ")['\"]?\\]" ), - "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), - "ATTR": new RegExp( "^" + attributes ), - "PSEUDO": new RegExp( "^" + pseudos ), - "CHILD": new RegExp( "^:(only|nth|last|first)-child(?:\\(" + whitespace + - "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + - "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), - "POS": new RegExp( pos, "ig" ), - // For use in libraries implementing .is() - "needsContext": new RegExp( "^" + whitespace + "*[>+~]|" + pos, "i" ) - }, - - // Support - - // Used for testing something on an element - assert = function( fn ) { - var div = document.createElement("div"); - - try { - return fn( div ); - } catch (e) { - return false; - } finally { - // release memory in IE - div = null; - } - }, - - // Check if getElementsByTagName("*") returns only elements - assertTagNameNoComments = assert(function( div ) { - div.appendChild( document.createComment("") ); - return !div.getElementsByTagName("*").length; - }), - - // Check if getAttribute returns normalized href attributes - assertHrefNotNormalized = assert(function( div ) { - div.innerHTML = ""; - return div.firstChild && typeof div.firstChild.getAttribute !== strundefined && - div.firstChild.getAttribute("href") === "#"; - }), - - // Check if attributes should be retrieved by attribute nodes - assertAttributes = assert(function( div ) { - div.innerHTML = ""; - var type = typeof div.lastChild.getAttribute("multiple"); - // IE8 returns a string for some attributes even when not present - return type !== "boolean" && type !== "string"; - }), - - // Check if getElementsByClassName can be trusted - assertUsableClassName = assert(function( div ) { - // Opera can't find a second classname (in 9.6) - div.innerHTML = ""; - if ( !div.getElementsByClassName || !div.getElementsByClassName("e").length ) { - return false; - } - - // Safari 3.2 caches class attributes and doesn't catch changes - div.lastChild.className = "e"; - return div.getElementsByClassName("e").length === 2; - }), - - // Check if getElementById returns elements by name - // Check if getElementsByName privileges form controls or returns elements by ID - assertUsableName = assert(function( div ) { - // Inject content - div.id = expando + 0; - div.innerHTML = "
    "; - docElem.insertBefore( div, docElem.firstChild ); - - // Test - var pass = document.getElementsByName && - // buggy browsers will return fewer than the correct 2 - document.getElementsByName( expando ).length === 2 + - // buggy browsers will return more than the correct 0 - document.getElementsByName( expando + 0 ).length; - assertGetIdNotName = !document.getElementById( expando ); - - // Cleanup - docElem.removeChild( div ); - - return pass; - }); - -// If slice is not available, provide a backup -try { - slice.call( docElem.childNodes, 0 )[0].nodeType; -} catch ( e ) { - slice = function( i ) { - var elem, results = []; - for ( ; (elem = this[i]); i++ ) { - results.push( elem ); - } - return results; - }; -} - -function Sizzle( selector, context, results, seed ) { - results = results || []; - context = context || document; - var match, elem, xml, m, - nodeType = context.nodeType; - - if ( nodeType !== 1 && nodeType !== 9 ) { - return []; - } - - if ( !selector || typeof selector !== "string" ) { - return results; - } - - xml = isXML( context ); - - if ( !xml && !seed ) { - if ( (match = rquickExpr.exec( selector )) ) { - // Speed-up: Sizzle("#ID") - if ( (m = match[1]) ) { - if ( nodeType === 9 ) { - elem = context.getElementById( m ); - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - if ( elem && elem.parentNode ) { - // Handle the case where IE, Opera, and Webkit return items - // by name instead of ID - if ( elem.id === m ) { - results.push( elem ); - return results; - } - } else { - return results; - } - } else { - // Context is not a document - if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && - contains( context, elem ) && elem.id === m ) { - results.push( elem ); - return results; - } - } - - // Speed-up: Sizzle("TAG") - } else if ( match[2] ) { - push.apply( results, slice.call(context.getElementsByTagName( selector ), 0) ); - return results; - - // Speed-up: Sizzle(".CLASS") - } else if ( (m = match[3]) && assertUsableClassName && context.getElementsByClassName ) { - push.apply( results, slice.call(context.getElementsByClassName( m ), 0) ); - return results; - } - } - } - - // All others - return select( selector, context, results, seed, xml ); -} - -Sizzle.matches = function( expr, elements ) { - return Sizzle( expr, null, null, elements ); -}; - -Sizzle.matchesSelector = function( elem, expr ) { - return Sizzle( expr, null, null, [ elem ] ).length > 0; -}; - -// Returns a function to use in pseudos for input types -function createInputPseudo( type ) { - return function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && elem.type === type; - }; -} - -// Returns a function to use in pseudos for buttons -function createButtonPseudo( type ) { - return function( elem ) { - var name = elem.nodeName.toLowerCase(); - return (name === "input" || name === "button") && elem.type === type; - }; -} - -/** - * Utility function for retrieving the text value of an array of DOM nodes - * @param {Array|Element} elem - */ -getText = Sizzle.getText = function( elem ) { - var node, - ret = "", - i = 0, - nodeType = elem.nodeType; - - if ( nodeType ) { - if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { - // Use textContent for elements - // innerText usage removed for consistency of new lines (see #11153) - if ( typeof elem.textContent === "string" ) { - return elem.textContent; - } else { - // Traverse its children - for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { - ret += getText( elem ); - } - } - } else if ( nodeType === 3 || nodeType === 4 ) { - return elem.nodeValue; - } - // Do not include comment or processing instruction nodes - } else { - - // If no nodeType, this is expected to be an array - for ( ; (node = elem[i]); i++ ) { - // Do not traverse comment nodes - ret += getText( node ); - } - } - return ret; -}; - -isXML = Sizzle.isXML = function isXML( elem ) { - // documentElement is verified for cases where it doesn't yet exist - // (such as loading iframes in IE - #4833) - var documentElement = elem && (elem.ownerDocument || elem).documentElement; - return documentElement ? documentElement.nodeName !== "HTML" : false; -}; - -// Element contains another -contains = Sizzle.contains = docElem.contains ? - function( a, b ) { - var adown = a.nodeType === 9 ? a.documentElement : a, - bup = b && b.parentNode; - return a === bup || !!( bup && bup.nodeType === 1 && adown.contains && adown.contains(bup) ); - } : - docElem.compareDocumentPosition ? - function( a, b ) { - return b && !!( a.compareDocumentPosition( b ) & 16 ); - } : - function( a, b ) { - while ( (b = b.parentNode) ) { - if ( b === a ) { - return true; - } - } - return false; - }; - -Sizzle.attr = function( elem, name ) { - var attr, - xml = isXML( elem ); - - if ( !xml ) { - name = name.toLowerCase(); - } - if ( Expr.attrHandle[ name ] ) { - return Expr.attrHandle[ name ]( elem ); - } - if ( assertAttributes || xml ) { - return elem.getAttribute( name ); - } - attr = elem.getAttributeNode( name ); - return attr ? - typeof elem[ name ] === "boolean" ? - elem[ name ] ? name : null : - attr.specified ? attr.value : null : - null; -}; - -Expr = Sizzle.selectors = { - - // Can be adjusted by the user - cacheLength: 50, - - createPseudo: markFunction, - - match: matchExpr, - - order: new RegExp( "ID|TAG" + - (assertUsableName ? "|NAME" : "") + - (assertUsableClassName ? "|CLASS" : "") - ), - - // IE6/7 return a modified href - attrHandle: assertHrefNotNormalized ? - {} : - { - "href": function( elem ) { - return elem.getAttribute( "href", 2 ); - }, - "type": function( elem ) { - return elem.getAttribute("type"); - } - }, - - find: { - "ID": assertGetIdNotName ? - function( id, context, xml ) { - if ( typeof context.getElementById !== strundefined && !xml ) { - var m = context.getElementById( id ); - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - return m && m.parentNode ? [m] : []; - } - } : - function( id, context, xml ) { - if ( typeof context.getElementById !== strundefined && !xml ) { - var m = context.getElementById( id ); - - return m ? - m.id === id || typeof m.getAttributeNode !== strundefined && m.getAttributeNode("id").value === id ? - [m] : - undefined : - []; - } - }, - - "TAG": assertTagNameNoComments ? - function( tag, context ) { - if ( typeof context.getElementsByTagName !== strundefined ) { - return context.getElementsByTagName( tag ); - } - } : - function( tag, context ) { - var results = context.getElementsByTagName( tag ); - - // Filter out possible comments - if ( tag === "*" ) { - var elem, - tmp = [], - i = 0; - - for ( ; (elem = results[i]); i++ ) { - if ( elem.nodeType === 1 ) { - tmp.push( elem ); - } - } - - return tmp; - } - return results; - }, - - "NAME": function( tag, context ) { - if ( typeof context.getElementsByName !== strundefined ) { - return context.getElementsByName( name ); - } - }, - - "CLASS": function( className, context, xml ) { - if ( typeof context.getElementsByClassName !== strundefined && !xml ) { - return context.getElementsByClassName( className ); - } - } - }, - - relative: { - ">": { dir: "parentNode", first: true }, - " ": { dir: "parentNode" }, - "+": { dir: "previousSibling", first: true }, - "~": { dir: "previousSibling" } - }, - - preFilter: { - "ATTR": function( match ) { - match[1] = match[1].replace( rbackslash, "" ); - - // Move the given value to match[3] whether quoted or unquoted - match[3] = ( match[4] || match[5] || "" ).replace( rbackslash, "" ); - - if ( match[2] === "~=" ) { - match[3] = " " + match[3] + " "; - } - - return match.slice( 0, 4 ); - }, - - "CHILD": function( match ) { - /* matches from matchExpr.CHILD - 1 type (only|nth|...) - 2 argument (even|odd|\d*|\d*n([+-]\d+)?|...) - 3 xn-component of xn+y argument ([+-]?\d*n|) - 4 sign of xn-component - 5 x of xn-component - 6 sign of y-component - 7 y of y-component - */ - match[1] = match[1].toLowerCase(); - - if ( match[1] === "nth" ) { - // nth-child requires argument - if ( !match[2] ) { - Sizzle.error( match[0] ); - } - - // numeric x and y parameters for Expr.filter.CHILD - // remember that false/true cast respectively to 0/1 - match[3] = +( match[3] ? match[4] + (match[5] || 1) : 2 * ( match[2] === "even" || match[2] === "odd" ) ); - match[4] = +( ( match[6] + match[7] ) || match[2] === "odd" ); - - // other types prohibit arguments - } else if ( match[2] ) { - Sizzle.error( match[0] ); - } - - return match; - }, - - "PSEUDO": function( match, context, xml ) { - var unquoted, excess; - if ( matchExpr["CHILD"].test( match[0] ) ) { - return null; - } - - if ( match[3] ) { - match[2] = match[3]; - } else if ( (unquoted = match[4]) ) { - // Only check arguments that contain a pseudo - if ( rpseudo.test(unquoted) && - // Get excess from tokenize (recursively) - (excess = tokenize( unquoted, context, xml, true )) && - // advance to the next closing parenthesis - (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { - - // excess is a negative index - unquoted = unquoted.slice( 0, excess ); - match[0] = match[0].slice( 0, excess ); - } - match[2] = unquoted; - } - - // Return only captures needed by the pseudo filter method (type and argument) - return match.slice( 0, 3 ); - } - }, - - filter: { - "ID": assertGetIdNotName ? - function( id ) { - id = id.replace( rbackslash, "" ); - return function( elem ) { - return elem.getAttribute("id") === id; - }; - } : - function( id ) { - id = id.replace( rbackslash, "" ); - return function( elem ) { - var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); - return node && node.value === id; - }; - }, - - "TAG": function( nodeName ) { - if ( nodeName === "*" ) { - return function() { return true; }; - } - nodeName = nodeName.replace( rbackslash, "" ).toLowerCase(); - - return function( elem ) { - return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; - }; - }, - - "CLASS": function( className ) { - var pattern = classCache[ expando ][ className ]; - if ( !pattern ) { - pattern = classCache( className, new RegExp("(^|" + whitespace + ")" + className + "(" + whitespace + "|$)") ); - } - return function( elem ) { - return pattern.test( elem.className || (typeof elem.getAttribute !== strundefined && elem.getAttribute("class")) || "" ); - }; - }, - - "ATTR": function( name, operator, check ) { - if ( !operator ) { - return function( elem ) { - return Sizzle.attr( elem, name ) != null; - }; - } - - return function( elem ) { - var result = Sizzle.attr( elem, name ), - value = result + ""; - - if ( result == null ) { - return operator === "!="; - } - - switch ( operator ) { - case "=": - return value === check; - case "!=": - return value !== check; - case "^=": - return check && value.indexOf( check ) === 0; - case "*=": - return check && value.indexOf( check ) > -1; - case "$=": - return check && value.substr( value.length - check.length ) === check; - case "~=": - return ( " " + value + " " ).indexOf( check ) > -1; - case "|=": - return value === check || value.substr( 0, check.length + 1 ) === check + "-"; - } - }; - }, - - "CHILD": function( type, argument, first, last ) { - - if ( type === "nth" ) { - var doneName = done++; - - return function( elem ) { - var parent, diff, - count = 0, - node = elem; - - if ( first === 1 && last === 0 ) { - return true; - } - - parent = elem.parentNode; - - if ( parent && (parent[ expando ] !== doneName || !elem.sizset) ) { - for ( node = parent.firstChild; node; node = node.nextSibling ) { - if ( node.nodeType === 1 ) { - node.sizset = ++count; - if ( node === elem ) { - break; - } - } - } - - parent[ expando ] = doneName; - } - - diff = elem.sizset - last; - - if ( first === 0 ) { - return diff === 0; - - } else { - return ( diff % first === 0 && diff / first >= 0 ); - } - }; - } - - return function( elem ) { - var node = elem; - - switch ( type ) { - case "only": - case "first": - while ( (node = node.previousSibling) ) { - if ( node.nodeType === 1 ) { - return false; - } - } - - if ( type === "first" ) { - return true; - } - - node = elem; - - /* falls through */ - case "last": - while ( (node = node.nextSibling) ) { - if ( node.nodeType === 1 ) { - return false; - } - } - - return true; - } - }; - }, - - "PSEUDO": function( pseudo, argument, context, xml ) { - // pseudo-class names are case-insensitive - // http://www.w3.org/TR/selectors/#pseudo-classes - // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters - var args, - fn = Expr.pseudos[ pseudo ] || Expr.pseudos[ pseudo.toLowerCase() ]; - - if ( !fn ) { - Sizzle.error( "unsupported pseudo: " + pseudo ); - } - - // The user may use createPseudo to indicate that - // arguments are needed to create the filter function - // just as Sizzle does - if ( !fn[ expando ] ) { - if ( fn.length > 1 ) { - args = [ pseudo, pseudo, "", argument ]; - return function( elem ) { - return fn( elem, 0, args ); - }; - } - return fn; - } - - return fn( argument, context, xml ); - } - }, - - pseudos: { - "not": markFunction(function( selector, context, xml ) { - // Trim the selector passed to compile - // to avoid treating leading and trailing - // spaces as combinators - var matcher = compile( selector.replace( rtrim, "$1" ), context, xml ); - return function( elem ) { - return !matcher( elem ); - }; - }), - - "enabled": function( elem ) { - return elem.disabled === false; - }, - - "disabled": function( elem ) { - return elem.disabled === true; - }, - - "checked": function( elem ) { - // In CSS3, :checked should return both checked and selected elements - // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - var nodeName = elem.nodeName.toLowerCase(); - return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); - }, - - "selected": function( elem ) { - // Accessing this property makes selected-by-default - // options in Safari work properly - if ( elem.parentNode ) { - elem.parentNode.selectedIndex; - } - - return elem.selected === true; - }, - - "parent": function( elem ) { - return !Expr.pseudos["empty"]( elem ); - }, - - "empty": function( elem ) { - // http://www.w3.org/TR/selectors/#empty-pseudo - // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)), - // not comment, processing instructions, or others - // Thanks to Diego Perini for the nodeName shortcut - // Greater than "@" means alpha characters (specifically not starting with "#" or "?") - var nodeType; - elem = elem.firstChild; - while ( elem ) { - if ( elem.nodeName > "@" || (nodeType = elem.nodeType) === 3 || nodeType === 4 ) { - return false; - } - elem = elem.nextSibling; - } - return true; - }, - - "contains": markFunction(function( text ) { - return function( elem ) { - return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; - }; - }), - - "has": markFunction(function( selector ) { - return function( elem ) { - return Sizzle( selector, elem ).length > 0; - }; - }), - - "header": function( elem ) { - return rheader.test( elem.nodeName ); - }, - - "text": function( elem ) { - var type, attr; - // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) - // use getAttribute instead to test this case - return elem.nodeName.toLowerCase() === "input" && - (type = elem.type) === "text" && - ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === type ); - }, - - // Input types - "radio": createInputPseudo("radio"), - "checkbox": createInputPseudo("checkbox"), - "file": createInputPseudo("file"), - "password": createInputPseudo("password"), - "image": createInputPseudo("image"), - - "submit": createButtonPseudo("submit"), - "reset": createButtonPseudo("reset"), - - "button": function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && elem.type === "button" || name === "button"; - }, - - "input": function( elem ) { - return rinputs.test( elem.nodeName ); - }, - - "focus": function( elem ) { - var doc = elem.ownerDocument; - return elem === doc.activeElement && (!doc.hasFocus || doc.hasFocus()) && !!(elem.type || elem.href); - }, - - "active": function( elem ) { - return elem === elem.ownerDocument.activeElement; - } - }, - - setFilters: { - "first": function( elements, argument, not ) { - return not ? elements.slice( 1 ) : [ elements[0] ]; - }, - - "last": function( elements, argument, not ) { - var elem = elements.pop(); - return not ? elements : [ elem ]; - }, - - "even": function( elements, argument, not ) { - var results = [], - i = not ? 1 : 0, - len = elements.length; - for ( ; i < len; i = i + 2 ) { - results.push( elements[i] ); - } - return results; - }, - - "odd": function( elements, argument, not ) { - var results = [], - i = not ? 0 : 1, - len = elements.length; - for ( ; i < len; i = i + 2 ) { - results.push( elements[i] ); - } - return results; - }, - - "lt": function( elements, argument, not ) { - return not ? elements.slice( +argument ) : elements.slice( 0, +argument ); - }, - - "gt": function( elements, argument, not ) { - return not ? elements.slice( 0, +argument + 1 ) : elements.slice( +argument + 1 ); - }, - - "eq": function( elements, argument, not ) { - var elem = elements.splice( +argument, 1 ); - return not ? elements : elem; - } - } -}; - -function siblingCheck( a, b, ret ) { - if ( a === b ) { - return ret; - } - - var cur = a.nextSibling; - - while ( cur ) { - if ( cur === b ) { - return -1; - } - - cur = cur.nextSibling; - } - - return 1; -} - -sortOrder = docElem.compareDocumentPosition ? - function( a, b ) { - if ( a === b ) { - hasDuplicate = true; - return 0; - } - - return ( !a.compareDocumentPosition || !b.compareDocumentPosition ? - a.compareDocumentPosition : - a.compareDocumentPosition(b) & 4 - ) ? -1 : 1; - } : - function( a, b ) { - // The nodes are identical, we can exit early - if ( a === b ) { - hasDuplicate = true; - return 0; - - // Fallback to using sourceIndex (in IE) if it's available on both nodes - } else if ( a.sourceIndex && b.sourceIndex ) { - return a.sourceIndex - b.sourceIndex; - } - - var al, bl, - ap = [], - bp = [], - aup = a.parentNode, - bup = b.parentNode, - cur = aup; - - // If the nodes are siblings (or identical) we can do a quick check - if ( aup === bup ) { - return siblingCheck( a, b ); - - // If no parents were found then the nodes are disconnected - } else if ( !aup ) { - return -1; - - } else if ( !bup ) { - return 1; - } - - // Otherwise they're somewhere else in the tree so we need - // to build up a full list of the parentNodes for comparison - while ( cur ) { - ap.unshift( cur ); - cur = cur.parentNode; - } - - cur = bup; - - while ( cur ) { - bp.unshift( cur ); - cur = cur.parentNode; - } - - al = ap.length; - bl = bp.length; - - // Start walking down the tree looking for a discrepancy - for ( var i = 0; i < al && i < bl; i++ ) { - if ( ap[i] !== bp[i] ) { - return siblingCheck( ap[i], bp[i] ); - } - } - - // We ended someplace up the tree so do a sibling check - return i === al ? - siblingCheck( a, bp[i], -1 ) : - siblingCheck( ap[i], b, 1 ); - }; - -// Always assume the presence of duplicates if sort doesn't -// pass them to our comparison function (as in Google Chrome). -[0, 0].sort( sortOrder ); -baseHasDuplicate = !hasDuplicate; - -// Document sorting and removing duplicates -Sizzle.uniqueSort = function( results ) { - var elem, - i = 1; - - hasDuplicate = baseHasDuplicate; - results.sort( sortOrder ); - - if ( hasDuplicate ) { - for ( ; (elem = results[i]); i++ ) { - if ( elem === results[ i - 1 ] ) { - results.splice( i--, 1 ); - } - } - } - - return results; -}; - -Sizzle.error = function( msg ) { - throw new Error( "Syntax error, unrecognized expression: " + msg ); -}; - -function tokenize( selector, context, xml, parseOnly ) { - var matched, match, tokens, type, - soFar, groups, group, i, - preFilters, filters, - checkContext = !xml && context !== document, - // Token cache should maintain spaces - key = ( checkContext ? "" : "" ) + selector.replace( rtrim, "$1" ), - cached = tokenCache[ expando ][ key ]; - - if ( cached ) { - return parseOnly ? 0 : slice.call( cached, 0 ); - } - - soFar = selector; - groups = []; - i = 0; - preFilters = Expr.preFilter; - filters = Expr.filter; - - while ( soFar ) { - - // Comma and first run - if ( !matched || (match = rcomma.exec( soFar )) ) { - if ( match ) { - soFar = soFar.slice( match[0].length ); - tokens.selector = group; - } - groups.push( tokens = [] ); - group = ""; - - // Need to make sure we're within a narrower context if necessary - // Adding a descendant combinator will generate what is needed - if ( checkContext ) { - soFar = " " + soFar; - } - } - - matched = false; - - // Combinators - if ( (match = rcombinators.exec( soFar )) ) { - group += match[0]; - soFar = soFar.slice( match[0].length ); - - // Cast descendant combinators to space - matched = tokens.push({ - part: match.pop().replace( rtrim, " " ), - string: match[0], - captures: match - }); - } - - // Filters - for ( type in filters ) { - if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || - ( match = preFilters[ type ](match, context, xml) )) ) { - - group += match[0]; - soFar = soFar.slice( match[0].length ); - matched = tokens.push({ - part: type, - string: match.shift(), - captures: match - }); - } - } - - if ( !matched ) { - break; - } - } - - // Attach the full group as a selector - if ( group ) { - tokens.selector = group; - } - - // Return the length of the invalid excess - // if we're just parsing - // Otherwise, throw an error or return tokens - return parseOnly ? - soFar.length : - soFar ? - Sizzle.error( selector ) : - // Cache the tokens - slice.call( tokenCache(key, groups), 0 ); -} - -function addCombinator( matcher, combinator, context, xml ) { - var dir = combinator.dir, - doneName = done++; - - if ( !matcher ) { - // If there is no matcher to check, check against the context - matcher = function( elem ) { - return elem === context; - }; - } - return combinator.first ? - function( elem ) { - while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 ) { - return matcher( elem ) && elem; - } - } - } : - xml ? - function( elem ) { - while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 ) { - if ( matcher( elem ) ) { - return elem; - } - } - } - } : - function( elem ) { - var cache, - dirkey = doneName + "." + dirruns, - cachedkey = dirkey + "." + cachedruns; - while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 ) { - if ( (cache = elem[ expando ]) === cachedkey ) { - return elem.sizset; - } else if ( typeof cache === "string" && cache.indexOf(dirkey) === 0 ) { - if ( elem.sizset ) { - return elem; - } - } else { - elem[ expando ] = cachedkey; - if ( matcher( elem ) ) { - elem.sizset = true; - return elem; - } - elem.sizset = false; - } - } - } - }; -} - -function addMatcher( higher, deeper ) { - return higher ? - function( elem ) { - var result = deeper( elem ); - return result && higher( result === true ? elem : result ); - } : - deeper; -} - -// ["TAG", ">", "ID", " ", "CLASS"] -function matcherFromTokens( tokens, context, xml ) { - var token, matcher, - i = 0; - - for ( ; (token = tokens[i]); i++ ) { - if ( Expr.relative[ token.part ] ) { - matcher = addCombinator( matcher, Expr.relative[ token.part ], context, xml ); - } else { - matcher = addMatcher( matcher, Expr.filter[ token.part ].apply(null, token.captures.concat( context, xml )) ); - } - } - - return matcher; -} - -function matcherFromGroupMatchers( matchers ) { - return function( elem ) { - var matcher, - j = 0; - for ( ; (matcher = matchers[j]); j++ ) { - if ( matcher(elem) ) { - return true; - } - } - return false; - }; -} - -compile = Sizzle.compile = function( selector, context, xml ) { - var group, i, len, - cached = compilerCache[ expando ][ selector ]; - - // Return a cached group function if already generated (context dependent) - if ( cached && cached.context === context ) { - return cached; - } - - // Generate a function of recursive functions that can be used to check each element - group = tokenize( selector, context, xml ); - for ( i = 0, len = group.length; i < len; i++ ) { - group[i] = matcherFromTokens(group[i], context, xml); - } - - // Cache the compiled function - cached = compilerCache( selector, matcherFromGroupMatchers(group) ); - cached.context = context; - cached.runs = cached.dirruns = 0; - return cached; -}; - -function multipleContexts( selector, contexts, results, seed ) { - var i = 0, - len = contexts.length; - for ( ; i < len; i++ ) { - Sizzle( selector, contexts[i], results, seed ); - } -} - -function handlePOSGroup( selector, posfilter, argument, contexts, seed, not ) { - var results, - fn = Expr.setFilters[ posfilter.toLowerCase() ]; - - if ( !fn ) { - Sizzle.error( posfilter ); - } - - if ( selector || !(results = seed) ) { - multipleContexts( selector || "*", contexts, (results = []), seed ); - } - - return results.length > 0 ? fn( results, argument, not ) : []; -} - -function handlePOS( groups, context, results, seed ) { - var group, part, j, groupLen, token, selector, - anchor, elements, match, matched, - lastIndex, currentContexts, not, - i = 0, - len = groups.length, - rpos = matchExpr["POS"], - // This is generated here in case matchExpr["POS"] is extended - rposgroups = new RegExp( "^" + rpos.source + "(?!" + whitespace + ")", "i" ), - // This is for making sure non-participating - // matching groups are represented cross-browser (IE6-8) - setUndefined = function() { - var i = 1, - len = arguments.length - 2; - for ( ; i < len; i++ ) { - if ( arguments[i] === undefined ) { - match[i] = undefined; - } - } - }; - - for ( ; i < len; i++ ) { - group = groups[i]; - part = ""; - elements = seed; - for ( j = 0, groupLen = group.length; j < groupLen; j++ ) { - token = group[j]; - selector = token.string; - if ( token.part === "PSEUDO" ) { - // Reset regex index to 0 - rpos.exec(""); - anchor = 0; - while ( (match = rpos.exec( selector )) ) { - matched = true; - lastIndex = rpos.lastIndex = match.index + match[0].length; - if ( lastIndex > anchor ) { - part += selector.slice( anchor, match.index ); - anchor = lastIndex; - currentContexts = [ context ]; - - if ( rcombinators.test(part) ) { - if ( elements ) { - currentContexts = elements; - } - elements = seed; - } - - if ( (not = rendsWithNot.test( part )) ) { - part = part.slice( 0, -5 ).replace( rcombinators, "$&*" ); - anchor++; - } - - if ( match.length > 1 ) { - match[0].replace( rposgroups, setUndefined ); - } - elements = handlePOSGroup( part, match[1], match[2], currentContexts, elements, not ); - } - part = ""; - } - - } - - if ( !matched ) { - part += selector; - } - matched = false; - } - - if ( part ) { - if ( rcombinators.test(part) ) { - multipleContexts( part, elements || [ context ], results, seed ); - } else { - Sizzle( part, context, results, seed ? seed.concat(elements) : elements ); - } - } else { - push.apply( results, elements ); - } - } - - // Do not sort if this is a single filter - return len === 1 ? results : Sizzle.uniqueSort( results ); -} - -function select( selector, context, results, seed, xml ) { - // Remove excessive whitespace - selector = selector.replace( rtrim, "$1" ); - var elements, matcher, cached, elem, - i, tokens, token, lastToken, findContext, type, - match = tokenize( selector, context, xml ), - contextNodeType = context.nodeType; - - // POS handling - if ( matchExpr["POS"].test(selector) ) { - return handlePOS( match, context, results, seed ); - } - - if ( seed ) { - elements = slice.call( seed, 0 ); - - // To maintain document order, only narrow the - // set if there is one group - } else if ( match.length === 1 ) { - - // Take a shortcut and set the context if the root selector is an ID - if ( (tokens = slice.call( match[0], 0 )).length > 2 && - (token = tokens[0]).part === "ID" && - contextNodeType === 9 && !xml && - Expr.relative[ tokens[1].part ] ) { - - context = Expr.find["ID"]( token.captures[0].replace( rbackslash, "" ), context, xml )[0]; - if ( !context ) { - return results; - } - - selector = selector.slice( tokens.shift().string.length ); - } - - findContext = ( (match = rsibling.exec( tokens[0].string )) && !match.index && context.parentNode ) || context; - - // Reduce the set if possible - lastToken = ""; - for ( i = tokens.length - 1; i >= 0; i-- ) { - token = tokens[i]; - type = token.part; - lastToken = token.string + lastToken; - if ( Expr.relative[ type ] ) { - break; - } - if ( Expr.order.test(type) ) { - elements = Expr.find[ type ]( token.captures[0].replace( rbackslash, "" ), findContext, xml ); - if ( elements == null ) { - continue; - } else { - selector = selector.slice( 0, selector.length - lastToken.length ) + - lastToken.replace( matchExpr[ type ], "" ); - - if ( !selector ) { - push.apply( results, slice.call(elements, 0) ); - } - - break; - } - } - } - } - - // Only loop over the given elements once - if ( selector ) { - matcher = compile( selector, context, xml ); - dirruns = matcher.dirruns++; - if ( elements == null ) { - elements = Expr.find["TAG"]( "*", (rsibling.test( selector ) && context.parentNode) || context ); - } - - for ( i = 0; (elem = elements[i]); i++ ) { - cachedruns = matcher.runs++; - if ( matcher(elem) ) { - results.push( elem ); - } - } - } - - return results; -} - -if ( document.querySelectorAll ) { - (function() { - var disconnectedMatch, - oldSelect = select, - rescape = /'|\\/g, - rattributeQuotes = /\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g, - rbuggyQSA = [], - // matchesSelector(:active) reports false when true (IE9/Opera 11.5) - // A support test would require too much code (would include document ready) - // just skip matchesSelector for :active - rbuggyMatches = [":active"], - matches = docElem.matchesSelector || - docElem.mozMatchesSelector || - docElem.webkitMatchesSelector || - docElem.oMatchesSelector || - docElem.msMatchesSelector; - - // Build QSA regex - // Regex strategy adopted from Diego Perini - assert(function( div ) { - // Select is set to empty string on purpose - // This is to test IE's treatment of not explictly - // setting a boolean content attribute, - // since its presence should be enough - // http://bugs.jquery.com/ticket/12359 - div.innerHTML = ""; - - // IE8 - Some boolean attributes are not treated correctly - if ( !div.querySelectorAll("[selected]").length ) { - rbuggyQSA.push( "\\[" + whitespace + "*(?:checked|disabled|ismap|multiple|readonly|selected|value)" ); - } - - // Webkit/Opera - :checked should return selected option elements - // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - // IE8 throws error here (do not put tests after this one) - if ( !div.querySelectorAll(":checked").length ) { - rbuggyQSA.push(":checked"); - } - }); - - assert(function( div ) { - - // Opera 10-12/IE9 - ^= $= *= and empty values - // Should not select anything - div.innerHTML = "

    "; - if ( div.querySelectorAll("[test^='']").length ) { - rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:\"\"|'')" ); - } - - // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) - // IE8 throws error here (do not put tests after this one) - div.innerHTML = ""; - if ( !div.querySelectorAll(":enabled").length ) { - rbuggyQSA.push(":enabled", ":disabled"); - } - }); - - rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); - - select = function( selector, context, results, seed, xml ) { - // Only use querySelectorAll when not filtering, - // when this is not xml, - // and when no QSA bugs apply - if ( !seed && !xml && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { - if ( context.nodeType === 9 ) { - try { - push.apply( results, slice.call(context.querySelectorAll( selector ), 0) ); - return results; - } catch(qsaError) {} - // qSA works strangely on Element-rooted queries - // We can work around this by specifying an extra ID on the root - // and working up from there (Thanks to Andrew Dupont for the technique) - // IE 8 doesn't work on object elements - } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { - var groups, i, len, - old = context.getAttribute("id"), - nid = old || expando, - newContext = rsibling.test( selector ) && context.parentNode || context; - - if ( old ) { - nid = nid.replace( rescape, "\\$&" ); - } else { - context.setAttribute( "id", nid ); - } - - groups = tokenize(selector, context, xml); - // Trailing space is unnecessary - // There is always a context check - nid = "[id='" + nid + "']"; - for ( i = 0, len = groups.length; i < len; i++ ) { - groups[i] = nid + groups[i].selector; - } - try { - push.apply( results, slice.call( newContext.querySelectorAll( - groups.join(",") - ), 0 ) ); - return results; - } catch(qsaError) { - } finally { - if ( !old ) { - context.removeAttribute("id"); - } - } - } - } - - return oldSelect( selector, context, results, seed, xml ); - }; - - if ( matches ) { - assert(function( div ) { - // Check to see if it's possible to do matchesSelector - // on a disconnected node (IE 9) - disconnectedMatch = matches.call( div, "div" ); - - // This should fail with an exception - // Gecko does not error, returns false instead - try { - matches.call( div, "[test!='']:sizzle" ); - rbuggyMatches.push( matchExpr["PSEUDO"].source, matchExpr["POS"].source, "!=" ); - } catch ( e ) {} - }); - - // rbuggyMatches always contains :active, so no need for a length check - rbuggyMatches = /* rbuggyMatches.length && */ new RegExp( rbuggyMatches.join("|") ); - - Sizzle.matchesSelector = function( elem, expr ) { - // Make sure that attribute selectors are quoted - expr = expr.replace( rattributeQuotes, "='$1']" ); - - // rbuggyMatches always contains :active, so no need for an existence check - if ( !isXML( elem ) && !rbuggyMatches.test( expr ) && (!rbuggyQSA || !rbuggyQSA.test( expr )) ) { - try { - var ret = matches.call( elem, expr ); - - // IE 9's matchesSelector returns false on disconnected nodes - if ( ret || disconnectedMatch || - // As well, disconnected nodes are said to be in a document - // fragment in IE 9 - elem.document && elem.document.nodeType !== 11 ) { - return ret; - } - } catch(e) {} - } - - return Sizzle( expr, null, null, [ elem ] ).length > 0; - }; - } - })(); -} - -// Deprecated -Expr.setFilters["nth"] = Expr.setFilters["eq"]; - -// Back-compat -Expr.filters = Expr.pseudos; - -// Override sizzle attribute retrieval -Sizzle.attr = jQuery.attr; -jQuery.find = Sizzle; -jQuery.expr = Sizzle.selectors; -jQuery.expr[":"] = jQuery.expr.pseudos; -jQuery.unique = Sizzle.uniqueSort; -jQuery.text = Sizzle.getText; -jQuery.isXMLDoc = Sizzle.isXML; -jQuery.contains = Sizzle.contains; - - -})( window ); -var runtil = /Until$/, - rparentsprev = /^(?:parents|prev(?:Until|All))/, - isSimple = /^.[^:#\[\.,]*$/, - rneedsContext = jQuery.expr.match.needsContext, - // methods guaranteed to produce a unique set when starting from a unique set - guaranteedUnique = { - children: true, - contents: true, - next: true, - prev: true - }; - -jQuery.fn.extend({ - find: function( selector ) { - var i, l, length, n, r, ret, - self = this; - - if ( typeof selector !== "string" ) { - return jQuery( selector ).filter(function() { - for ( i = 0, l = self.length; i < l; i++ ) { - if ( jQuery.contains( self[ i ], this ) ) { - return true; - } - } - }); - } - - ret = this.pushStack( "", "find", selector ); - - for ( i = 0, l = this.length; i < l; i++ ) { - length = ret.length; - jQuery.find( selector, this[i], ret ); - - if ( i > 0 ) { - // Make sure that the results are unique - for ( n = length; n < ret.length; n++ ) { - for ( r = 0; r < length; r++ ) { - if ( ret[r] === ret[n] ) { - ret.splice(n--, 1); - break; - } - } - } - } - } - - return ret; - }, - - has: function( target ) { - var i, - targets = jQuery( target, this ), - len = targets.length; - - return this.filter(function() { - for ( i = 0; i < len; i++ ) { - if ( jQuery.contains( this, targets[i] ) ) { - return true; - } - } - }); - }, - - not: function( selector ) { - return this.pushStack( winnow(this, selector, false), "not", selector); - }, - - filter: function( selector ) { - return this.pushStack( winnow(this, selector, true), "filter", selector ); - }, - - is: function( selector ) { - return !!selector && ( - typeof selector === "string" ? - // If this is a positional/relative selector, check membership in the returned set - // so $("p:first").is("p:last") won't return true for a doc with two "p". - rneedsContext.test( selector ) ? - jQuery( selector, this.context ).index( this[0] ) >= 0 : - jQuery.filter( selector, this ).length > 0 : - this.filter( selector ).length > 0 ); - }, - - closest: function( selectors, context ) { - var cur, - i = 0, - l = this.length, - ret = [], - pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ? - jQuery( selectors, context || this.context ) : - 0; - - for ( ; i < l; i++ ) { - cur = this[i]; - - while ( cur && cur.ownerDocument && cur !== context && cur.nodeType !== 11 ) { - if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) { - ret.push( cur ); - break; - } - cur = cur.parentNode; - } - } - - ret = ret.length > 1 ? jQuery.unique( ret ) : ret; - - return this.pushStack( ret, "closest", selectors ); - }, - - // Determine the position of an element within - // the matched set of elements - index: function( elem ) { - - // No argument, return index in parent - if ( !elem ) { - return ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1; - } - - // index in selector - if ( typeof elem === "string" ) { - return jQuery.inArray( this[0], jQuery( elem ) ); - } - - // Locate the position of the desired element - return jQuery.inArray( - // If it receives a jQuery object, the first element is used - elem.jquery ? elem[0] : elem, this ); - }, - - add: function( selector, context ) { - var set = typeof selector === "string" ? - jQuery( selector, context ) : - jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ), - all = jQuery.merge( this.get(), set ); - - return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ? - all : - jQuery.unique( all ) ); - }, - - addBack: function( selector ) { - return this.add( selector == null ? - this.prevObject : this.prevObject.filter(selector) - ); - } -}); - -jQuery.fn.andSelf = jQuery.fn.addBack; - -// A painfully simple check to see if an element is disconnected -// from a document (should be improved, where feasible). -function isDisconnected( node ) { - return !node || !node.parentNode || node.parentNode.nodeType === 11; -} - -function sibling( cur, dir ) { - do { - cur = cur[ dir ]; - } while ( cur && cur.nodeType !== 1 ); - - return cur; -} - -jQuery.each({ - parent: function( elem ) { - var parent = elem.parentNode; - return parent && parent.nodeType !== 11 ? parent : null; - }, - parents: function( elem ) { - return jQuery.dir( elem, "parentNode" ); - }, - parentsUntil: function( elem, i, until ) { - return jQuery.dir( elem, "parentNode", until ); - }, - next: function( elem ) { - return sibling( elem, "nextSibling" ); - }, - prev: function( elem ) { - return sibling( elem, "previousSibling" ); - }, - nextAll: function( elem ) { - return jQuery.dir( elem, "nextSibling" ); - }, - prevAll: function( elem ) { - return jQuery.dir( elem, "previousSibling" ); - }, - nextUntil: function( elem, i, until ) { - return jQuery.dir( elem, "nextSibling", until ); - }, - prevUntil: function( elem, i, until ) { - return jQuery.dir( elem, "previousSibling", until ); - }, - siblings: function( elem ) { - return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); - }, - children: function( elem ) { - return jQuery.sibling( elem.firstChild ); - }, - contents: function( elem ) { - return jQuery.nodeName( elem, "iframe" ) ? - elem.contentDocument || elem.contentWindow.document : - jQuery.merge( [], elem.childNodes ); - } -}, function( name, fn ) { - jQuery.fn[ name ] = function( until, selector ) { - var ret = jQuery.map( this, fn, until ); - - if ( !runtil.test( name ) ) { - selector = until; - } - - if ( selector && typeof selector === "string" ) { - ret = jQuery.filter( selector, ret ); - } - - ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret; - - if ( this.length > 1 && rparentsprev.test( name ) ) { - ret = ret.reverse(); - } - - return this.pushStack( ret, name, core_slice.call( arguments ).join(",") ); - }; -}); - -jQuery.extend({ - filter: function( expr, elems, not ) { - if ( not ) { - expr = ":not(" + expr + ")"; - } - - return elems.length === 1 ? - jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] : - jQuery.find.matches(expr, elems); - }, - - dir: function( elem, dir, until ) { - var matched = [], - cur = elem[ dir ]; - - while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { - if ( cur.nodeType === 1 ) { - matched.push( cur ); - } - cur = cur[dir]; - } - return matched; - }, - - sibling: function( n, elem ) { - var r = []; - - for ( ; n; n = n.nextSibling ) { - if ( n.nodeType === 1 && n !== elem ) { - r.push( n ); - } - } - - return r; - } -}); - -// Implement the identical functionality for filter and not -function winnow( elements, qualifier, keep ) { - - // Can't pass null or undefined to indexOf in Firefox 4 - // Set to 0 to skip string check - qualifier = qualifier || 0; - - if ( jQuery.isFunction( qualifier ) ) { - return jQuery.grep(elements, function( elem, i ) { - var retVal = !!qualifier.call( elem, i, elem ); - return retVal === keep; - }); - - } else if ( qualifier.nodeType ) { - return jQuery.grep(elements, function( elem, i ) { - return ( elem === qualifier ) === keep; - }); - - } else if ( typeof qualifier === "string" ) { - var filtered = jQuery.grep(elements, function( elem ) { - return elem.nodeType === 1; - }); - - if ( isSimple.test( qualifier ) ) { - return jQuery.filter(qualifier, filtered, !keep); - } else { - qualifier = jQuery.filter( qualifier, filtered ); - } - } - - return jQuery.grep(elements, function( elem, i ) { - return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep; - }); -} -function createSafeFragment( document ) { - var list = nodeNames.split( "|" ), - safeFrag = document.createDocumentFragment(); - - if ( safeFrag.createElement ) { - while ( list.length ) { - safeFrag.createElement( - list.pop() - ); - } - } - return safeFrag; -} - -var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" + - "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", - rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g, - rleadingWhitespace = /^\s+/, - rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, - rtagName = /<([\w:]+)/, - rtbody = /]", "i"), - rcheckableType = /^(?:checkbox|radio)$/, - // checked="checked" or checked - rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, - rscriptType = /\/(java|ecma)script/i, - rcleanScript = /^\s*\s*$/g, - wrapMap = { - option: [ 1, "" ], - legend: [ 1, "
    ", "
    " ], - thead: [ 1, "", "
    " ], - tr: [ 2, "", "
    " ], - td: [ 3, "", "
    " ], - col: [ 2, "", "
    " ], - area: [ 1, "", "" ], - _default: [ 0, "", "" ] - }, - safeFragment = createSafeFragment( document ), - fragmentDiv = safeFragment.appendChild( document.createElement("div") ); - -wrapMap.optgroup = wrapMap.option; -wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; -wrapMap.th = wrapMap.td; - -// IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags, -// unless wrapped in a div with non-breaking characters in front of it. -if ( !jQuery.support.htmlSerialize ) { - wrapMap._default = [ 1, "X
    ", "
    " ]; -} - -jQuery.fn.extend({ - text: function( value ) { - return jQuery.access( this, function( value ) { - return value === undefined ? - jQuery.text( this ) : - this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) ); - }, null, value, arguments.length ); - }, - - wrapAll: function( html ) { - if ( jQuery.isFunction( html ) ) { - return this.each(function(i) { - jQuery(this).wrapAll( html.call(this, i) ); - }); - } - - if ( this[0] ) { - // The elements to wrap the target around - var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true); - - if ( this[0].parentNode ) { - wrap.insertBefore( this[0] ); - } - - wrap.map(function() { - var elem = this; - - while ( elem.firstChild && elem.firstChild.nodeType === 1 ) { - elem = elem.firstChild; - } - - return elem; - }).append( this ); - } - - return this; - }, - - wrapInner: function( html ) { - if ( jQuery.isFunction( html ) ) { - return this.each(function(i) { - jQuery(this).wrapInner( html.call(this, i) ); - }); - } - - return this.each(function() { - var self = jQuery( this ), - contents = self.contents(); - - if ( contents.length ) { - contents.wrapAll( html ); - - } else { - self.append( html ); - } - }); - }, - - wrap: function( html ) { - var isFunction = jQuery.isFunction( html ); - - return this.each(function(i) { - jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html ); - }); - }, - - unwrap: function() { - return this.parent().each(function() { - if ( !jQuery.nodeName( this, "body" ) ) { - jQuery( this ).replaceWith( this.childNodes ); - } - }).end(); - }, - - append: function() { - return this.domManip(arguments, true, function( elem ) { - if ( this.nodeType === 1 || this.nodeType === 11 ) { - this.appendChild( elem ); - } - }); - }, - - prepend: function() { - return this.domManip(arguments, true, function( elem ) { - if ( this.nodeType === 1 || this.nodeType === 11 ) { - this.insertBefore( elem, this.firstChild ); - } - }); - }, - - before: function() { - if ( !isDisconnected( this[0] ) ) { - return this.domManip(arguments, false, function( elem ) { - this.parentNode.insertBefore( elem, this ); - }); - } - - if ( arguments.length ) { - var set = jQuery.clean( arguments ); - return this.pushStack( jQuery.merge( set, this ), "before", this.selector ); - } - }, - - after: function() { - if ( !isDisconnected( this[0] ) ) { - return this.domManip(arguments, false, function( elem ) { - this.parentNode.insertBefore( elem, this.nextSibling ); - }); - } - - if ( arguments.length ) { - var set = jQuery.clean( arguments ); - return this.pushStack( jQuery.merge( this, set ), "after", this.selector ); - } - }, - - // keepData is for internal use only--do not document - remove: function( selector, keepData ) { - var elem, - i = 0; - - for ( ; (elem = this[i]) != null; i++ ) { - if ( !selector || jQuery.filter( selector, [ elem ] ).length ) { - if ( !keepData && elem.nodeType === 1 ) { - jQuery.cleanData( elem.getElementsByTagName("*") ); - jQuery.cleanData( [ elem ] ); - } - - if ( elem.parentNode ) { - elem.parentNode.removeChild( elem ); - } - } - } - - return this; - }, - - empty: function() { - var elem, - i = 0; - - for ( ; (elem = this[i]) != null; i++ ) { - // Remove element nodes and prevent memory leaks - if ( elem.nodeType === 1 ) { - jQuery.cleanData( elem.getElementsByTagName("*") ); - } - - // Remove any remaining nodes - while ( elem.firstChild ) { - elem.removeChild( elem.firstChild ); - } - } - - return this; - }, - - clone: function( dataAndEvents, deepDataAndEvents ) { - dataAndEvents = dataAndEvents == null ? false : dataAndEvents; - deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; - - return this.map( function () { - return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); - }); - }, - - html: function( value ) { - return jQuery.access( this, function( value ) { - var elem = this[0] || {}, - i = 0, - l = this.length; - - if ( value === undefined ) { - return elem.nodeType === 1 ? - elem.innerHTML.replace( rinlinejQuery, "" ) : - undefined; - } - - // See if we can take a shortcut and just use innerHTML - if ( typeof value === "string" && !rnoInnerhtml.test( value ) && - ( jQuery.support.htmlSerialize || !rnoshimcache.test( value ) ) && - ( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) && - !wrapMap[ ( rtagName.exec( value ) || ["", ""] )[1].toLowerCase() ] ) { - - value = value.replace( rxhtmlTag, "<$1>" ); - - try { - for (; i < l; i++ ) { - // Remove element nodes and prevent memory leaks - elem = this[i] || {}; - if ( elem.nodeType === 1 ) { - jQuery.cleanData( elem.getElementsByTagName( "*" ) ); - elem.innerHTML = value; - } - } - - elem = 0; - - // If using innerHTML throws an exception, use the fallback method - } catch(e) {} - } - - if ( elem ) { - this.empty().append( value ); - } - }, null, value, arguments.length ); - }, - - replaceWith: function( value ) { - if ( !isDisconnected( this[0] ) ) { - // Make sure that the elements are removed from the DOM before they are inserted - // this can help fix replacing a parent with child elements - if ( jQuery.isFunction( value ) ) { - return this.each(function(i) { - var self = jQuery(this), old = self.html(); - self.replaceWith( value.call( this, i, old ) ); - }); - } - - if ( typeof value !== "string" ) { - value = jQuery( value ).detach(); - } - - return this.each(function() { - var next = this.nextSibling, - parent = this.parentNode; - - jQuery( this ).remove(); - - if ( next ) { - jQuery(next).before( value ); - } else { - jQuery(parent).append( value ); - } - }); - } - - return this.length ? - this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value ) : - this; - }, - - detach: function( selector ) { - return this.remove( selector, true ); - }, - - domManip: function( args, table, callback ) { - - // Flatten any nested arrays - args = [].concat.apply( [], args ); - - var results, first, fragment, iNoClone, - i = 0, - value = args[0], - scripts = [], - l = this.length; - - // We can't cloneNode fragments that contain checked, in WebKit - if ( !jQuery.support.checkClone && l > 1 && typeof value === "string" && rchecked.test( value ) ) { - return this.each(function() { - jQuery(this).domManip( args, table, callback ); - }); - } - - if ( jQuery.isFunction(value) ) { - return this.each(function(i) { - var self = jQuery(this); - args[0] = value.call( this, i, table ? self.html() : undefined ); - self.domManip( args, table, callback ); - }); - } - - if ( this[0] ) { - results = jQuery.buildFragment( args, this, scripts ); - fragment = results.fragment; - first = fragment.firstChild; - - if ( fragment.childNodes.length === 1 ) { - fragment = first; - } - - if ( first ) { - table = table && jQuery.nodeName( first, "tr" ); - - // Use the original fragment for the last item instead of the first because it can end up - // being emptied incorrectly in certain situations (#8070). - // Fragments from the fragment cache must always be cloned and never used in place. - for ( iNoClone = results.cacheable || l - 1; i < l; i++ ) { - callback.call( - table && jQuery.nodeName( this[i], "table" ) ? - findOrAppend( this[i], "tbody" ) : - this[i], - i === iNoClone ? - fragment : - jQuery.clone( fragment, true, true ) - ); - } - } - - // Fix #11809: Avoid leaking memory - fragment = first = null; - - if ( scripts.length ) { - jQuery.each( scripts, function( i, elem ) { - if ( elem.src ) { - if ( jQuery.ajax ) { - jQuery.ajax({ - url: elem.src, - type: "GET", - dataType: "script", - async: false, - global: false, - "throws": true - }); - } else { - jQuery.error("no ajax"); - } - } else { - jQuery.globalEval( ( elem.text || elem.textContent || elem.innerHTML || "" ).replace( rcleanScript, "" ) ); - } - - if ( elem.parentNode ) { - elem.parentNode.removeChild( elem ); - } - }); - } - } - - return this; - } -}); - -function findOrAppend( elem, tag ) { - return elem.getElementsByTagName( tag )[0] || elem.appendChild( elem.ownerDocument.createElement( tag ) ); -} - -function cloneCopyEvent( src, dest ) { - - if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) { - return; - } - - var type, i, l, - oldData = jQuery._data( src ), - curData = jQuery._data( dest, oldData ), - events = oldData.events; - - if ( events ) { - delete curData.handle; - curData.events = {}; - - for ( type in events ) { - for ( i = 0, l = events[ type ].length; i < l; i++ ) { - jQuery.event.add( dest, type, events[ type ][ i ] ); - } - } - } - - // make the cloned public data object a copy from the original - if ( curData.data ) { - curData.data = jQuery.extend( {}, curData.data ); - } -} - -function cloneFixAttributes( src, dest ) { - var nodeName; - - // We do not need to do anything for non-Elements - if ( dest.nodeType !== 1 ) { - return; - } - - // clearAttributes removes the attributes, which we don't want, - // but also removes the attachEvent events, which we *do* want - if ( dest.clearAttributes ) { - dest.clearAttributes(); - } - - // mergeAttributes, in contrast, only merges back on the - // original attributes, not the events - if ( dest.mergeAttributes ) { - dest.mergeAttributes( src ); - } - - nodeName = dest.nodeName.toLowerCase(); - - if ( nodeName === "object" ) { - // IE6-10 improperly clones children of object elements using classid. - // IE10 throws NoModificationAllowedError if parent is null, #12132. - if ( dest.parentNode ) { - dest.outerHTML = src.outerHTML; - } - - // This path appears unavoidable for IE9. When cloning an object - // element in IE9, the outerHTML strategy above is not sufficient. - // If the src has innerHTML and the destination does not, - // copy the src.innerHTML into the dest.innerHTML. #10324 - if ( jQuery.support.html5Clone && (src.innerHTML && !jQuery.trim(dest.innerHTML)) ) { - dest.innerHTML = src.innerHTML; - } - - } else if ( nodeName === "input" && rcheckableType.test( src.type ) ) { - // IE6-8 fails to persist the checked state of a cloned checkbox - // or radio button. Worse, IE6-7 fail to give the cloned element - // a checked appearance if the defaultChecked value isn't also set - - dest.defaultChecked = dest.checked = src.checked; - - // IE6-7 get confused and end up setting the value of a cloned - // checkbox/radio button to an empty string instead of "on" - if ( dest.value !== src.value ) { - dest.value = src.value; - } - - // IE6-8 fails to return the selected option to the default selected - // state when cloning options - } else if ( nodeName === "option" ) { - dest.selected = src.defaultSelected; - - // IE6-8 fails to set the defaultValue to the correct value when - // cloning other types of input fields - } else if ( nodeName === "input" || nodeName === "textarea" ) { - dest.defaultValue = src.defaultValue; - - // IE blanks contents when cloning scripts - } else if ( nodeName === "script" && dest.text !== src.text ) { - dest.text = src.text; - } - - // Event data gets referenced instead of copied if the expando - // gets copied too - dest.removeAttribute( jQuery.expando ); -} - -jQuery.buildFragment = function( args, context, scripts ) { - var fragment, cacheable, cachehit, - first = args[ 0 ]; - - // Set context from what may come in as undefined or a jQuery collection or a node - // Updated to fix #12266 where accessing context[0] could throw an exception in IE9/10 & - // also doubles as fix for #8950 where plain objects caused createDocumentFragment exception - context = context || document; - context = !context.nodeType && context[0] || context; - context = context.ownerDocument || context; - - // Only cache "small" (1/2 KB) HTML strings that are associated with the main document - // Cloning options loses the selected state, so don't cache them - // IE 6 doesn't like it when you put or elements in a fragment - // Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache - // Lastly, IE6,7,8 will not correctly reuse cached fragments that were created from unknown elems #10501 - if ( args.length === 1 && typeof first === "string" && first.length < 512 && context === document && - first.charAt(0) === "<" && !rnocache.test( first ) && - (jQuery.support.checkClone || !rchecked.test( first )) && - (jQuery.support.html5Clone || !rnoshimcache.test( first )) ) { - - // Mark cacheable and look for a hit - cacheable = true; - fragment = jQuery.fragments[ first ]; - cachehit = fragment !== undefined; - } - - if ( !fragment ) { - fragment = context.createDocumentFragment(); - jQuery.clean( args, context, fragment, scripts ); - - // Update the cache, but only store false - // unless this is a second parsing of the same content - if ( cacheable ) { - jQuery.fragments[ first ] = cachehit && fragment; - } - } - - return { fragment: fragment, cacheable: cacheable }; -}; - -jQuery.fragments = {}; - -jQuery.each({ - appendTo: "append", - prependTo: "prepend", - insertBefore: "before", - insertAfter: "after", - replaceAll: "replaceWith" -}, function( name, original ) { - jQuery.fn[ name ] = function( selector ) { - var elems, - i = 0, - ret = [], - insert = jQuery( selector ), - l = insert.length, - parent = this.length === 1 && this[0].parentNode; - - if ( (parent == null || parent && parent.nodeType === 11 && parent.childNodes.length === 1) && l === 1 ) { - insert[ original ]( this[0] ); - return this; - } else { - for ( ; i < l; i++ ) { - elems = ( i > 0 ? this.clone(true) : this ).get(); - jQuery( insert[i] )[ original ]( elems ); - ret = ret.concat( elems ); - } - - return this.pushStack( ret, name, insert.selector ); - } - }; -}); - -function getAll( elem ) { - if ( typeof elem.getElementsByTagName !== "undefined" ) { - return elem.getElementsByTagName( "*" ); - - } else if ( typeof elem.querySelectorAll !== "undefined" ) { - return elem.querySelectorAll( "*" ); - - } else { - return []; - } -} - -// Used in clean, fixes the defaultChecked property -function fixDefaultChecked( elem ) { - if ( rcheckableType.test( elem.type ) ) { - elem.defaultChecked = elem.checked; - } -} - -jQuery.extend({ - clone: function( elem, dataAndEvents, deepDataAndEvents ) { - var srcElements, - destElements, - i, - clone; - - if ( jQuery.support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) { - clone = elem.cloneNode( true ); - - // IE<=8 does not properly clone detached, unknown element nodes - } else { - fragmentDiv.innerHTML = elem.outerHTML; - fragmentDiv.removeChild( clone = fragmentDiv.firstChild ); - } - - if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) && - (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) { - // IE copies events bound via attachEvent when using cloneNode. - // Calling detachEvent on the clone will also remove the events - // from the original. In order to get around this, we use some - // proprietary methods to clear the events. Thanks to MooTools - // guys for this hotness. - - cloneFixAttributes( elem, clone ); - - // Using Sizzle here is crazy slow, so we use getElementsByTagName instead - srcElements = getAll( elem ); - destElements = getAll( clone ); - - // Weird iteration because IE will replace the length property - // with an element if you are cloning the body and one of the - // elements on the page has a name or id of "length" - for ( i = 0; srcElements[i]; ++i ) { - // Ensure that the destination node is not null; Fixes #9587 - if ( destElements[i] ) { - cloneFixAttributes( srcElements[i], destElements[i] ); - } - } - } - - // Copy the events from the original to the clone - if ( dataAndEvents ) { - cloneCopyEvent( elem, clone ); - - if ( deepDataAndEvents ) { - srcElements = getAll( elem ); - destElements = getAll( clone ); - - for ( i = 0; srcElements[i]; ++i ) { - cloneCopyEvent( srcElements[i], destElements[i] ); - } - } - } - - srcElements = destElements = null; - - // Return the cloned set - return clone; - }, - - clean: function( elems, context, fragment, scripts ) { - var i, j, elem, tag, wrap, depth, div, hasBody, tbody, len, handleScript, jsTags, - safe = context === document && safeFragment, - ret = []; - - // Ensure that context is a document - if ( !context || typeof context.createDocumentFragment === "undefined" ) { - context = document; - } - - // Use the already-created safe fragment if context permits - for ( i = 0; (elem = elems[i]) != null; i++ ) { - if ( typeof elem === "number" ) { - elem += ""; - } - - if ( !elem ) { - continue; - } - - // Convert html string into DOM nodes - if ( typeof elem === "string" ) { - if ( !rhtml.test( elem ) ) { - elem = context.createTextNode( elem ); - } else { - // Ensure a safe container in which to render the html - safe = safe || createSafeFragment( context ); - div = context.createElement("div"); - safe.appendChild( div ); - - // Fix "XHTML"-style tags in all browsers - elem = elem.replace(rxhtmlTag, "<$1>"); - - // Go to html and back, then peel off extra wrappers - tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase(); - wrap = wrapMap[ tag ] || wrapMap._default; - depth = wrap[0]; - div.innerHTML = wrap[1] + elem + wrap[2]; - - // Move to the right depth - while ( depth-- ) { - div = div.lastChild; - } - - // Remove IE's autoinserted from table fragments - if ( !jQuery.support.tbody ) { - - // String was a , *may* have spurious - hasBody = rtbody.test(elem); - tbody = tag === "table" && !hasBody ? - div.firstChild && div.firstChild.childNodes : - - // String was a bare or - wrap[1] === "
    " && !hasBody ? - div.childNodes : - []; - - for ( j = tbody.length - 1; j >= 0 ; --j ) { - if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) { - tbody[ j ].parentNode.removeChild( tbody[ j ] ); - } - } - } - - // IE completely kills leading whitespace when innerHTML is used - if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) { - div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild ); - } - - elem = div.childNodes; - - // Take out of fragment container (we need a fresh div each time) - div.parentNode.removeChild( div ); - } - } - - if ( elem.nodeType ) { - ret.push( elem ); - } else { - jQuery.merge( ret, elem ); - } - } - - // Fix #11356: Clear elements from safeFragment - if ( div ) { - elem = div = safe = null; - } - - // Reset defaultChecked for any radios and checkboxes - // about to be appended to the DOM in IE 6/7 (#8060) - if ( !jQuery.support.appendChecked ) { - for ( i = 0; (elem = ret[i]) != null; i++ ) { - if ( jQuery.nodeName( elem, "input" ) ) { - fixDefaultChecked( elem ); - } else if ( typeof elem.getElementsByTagName !== "undefined" ) { - jQuery.grep( elem.getElementsByTagName("input"), fixDefaultChecked ); - } - } - } - - // Append elements to a provided document fragment - if ( fragment ) { - // Special handling of each script element - handleScript = function( elem ) { - // Check if we consider it executable - if ( !elem.type || rscriptType.test( elem.type ) ) { - // Detach the script and store it in the scripts array (if provided) or the fragment - // Return truthy to indicate that it has been handled - return scripts ? - scripts.push( elem.parentNode ? elem.parentNode.removeChild( elem ) : elem ) : - fragment.appendChild( elem ); - } - }; - - for ( i = 0; (elem = ret[i]) != null; i++ ) { - // Check if we're done after handling an executable script - if ( !( jQuery.nodeName( elem, "script" ) && handleScript( elem ) ) ) { - // Append to fragment and handle embedded scripts - fragment.appendChild( elem ); - if ( typeof elem.getElementsByTagName !== "undefined" ) { - // handleScript alters the DOM, so use jQuery.merge to ensure snapshot iteration - jsTags = jQuery.grep( jQuery.merge( [], elem.getElementsByTagName("script") ), handleScript ); - - // Splice the scripts into ret after their former ancestor and advance our index beyond them - ret.splice.apply( ret, [i + 1, 0].concat( jsTags ) ); - i += jsTags.length; - } - } - } - } - - return ret; - }, - - cleanData: function( elems, /* internal */ acceptData ) { - var data, id, elem, type, - i = 0, - internalKey = jQuery.expando, - cache = jQuery.cache, - deleteExpando = jQuery.support.deleteExpando, - special = jQuery.event.special; - - for ( ; (elem = elems[i]) != null; i++ ) { - - if ( acceptData || jQuery.acceptData( elem ) ) { - - id = elem[ internalKey ]; - data = id && cache[ id ]; - - if ( data ) { - if ( data.events ) { - for ( type in data.events ) { - if ( special[ type ] ) { - jQuery.event.remove( elem, type ); - - // This is a shortcut to avoid jQuery.event.remove's overhead - } else { - jQuery.removeEvent( elem, type, data.handle ); - } - } - } - - // Remove cache only if it was not already removed by jQuery.event.remove - if ( cache[ id ] ) { - - delete cache[ id ]; - - // IE does not allow us to delete expando properties from nodes, - // nor does it have a removeAttribute function on Document nodes; - // we must handle all of these cases - if ( deleteExpando ) { - delete elem[ internalKey ]; - - } else if ( elem.removeAttribute ) { - elem.removeAttribute( internalKey ); - - } else { - elem[ internalKey ] = null; - } - - jQuery.deletedIds.push( id ); - } - } - } - } - } -}); -// Limit scope pollution from any deprecated API -(function() { - -var matched, browser; - -// Use of jQuery.browser is frowned upon. -// More details: http://api.jquery.com/jQuery.browser -// jQuery.uaMatch maintained for back-compat -jQuery.uaMatch = function( ua ) { - ua = ua.toLowerCase(); - - var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) || - /(webkit)[ \/]([\w.]+)/.exec( ua ) || - /(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) || - /(msie) ([\w.]+)/.exec( ua ) || - ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) || - []; - - return { - browser: match[ 1 ] || "", - version: match[ 2 ] || "0" - }; -}; - -matched = jQuery.uaMatch( navigator.userAgent ); -browser = {}; - -if ( matched.browser ) { - browser[ matched.browser ] = true; - browser.version = matched.version; -} - -// Chrome is Webkit, but Webkit is also Safari. -if ( browser.chrome ) { - browser.webkit = true; -} else if ( browser.webkit ) { - browser.safari = true; -} - -jQuery.browser = browser; - -jQuery.sub = function() { - function jQuerySub( selector, context ) { - return new jQuerySub.fn.init( selector, context ); - } - jQuery.extend( true, jQuerySub, this ); - jQuerySub.superclass = this; - jQuerySub.fn = jQuerySub.prototype = this(); - jQuerySub.fn.constructor = jQuerySub; - jQuerySub.sub = this.sub; - jQuerySub.fn.init = function init( selector, context ) { - if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) { - context = jQuerySub( context ); - } - - return jQuery.fn.init.call( this, selector, context, rootjQuerySub ); - }; - jQuerySub.fn.init.prototype = jQuerySub.fn; - var rootjQuerySub = jQuerySub(document); - return jQuerySub; -}; - -})(); -var curCSS, iframe, iframeDoc, - ralpha = /alpha\([^)]*\)/i, - ropacity = /opacity=([^)]*)/, - rposition = /^(top|right|bottom|left)$/, - // swappable if display is none or starts with table except "table", "table-cell", or "table-caption" - // see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display - rdisplayswap = /^(none|table(?!-c[ea]).+)/, - rmargin = /^margin/, - rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ), - rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ), - rrelNum = new RegExp( "^([-+])=(" + core_pnum + ")", "i" ), - elemdisplay = {}, - - cssShow = { position: "absolute", visibility: "hidden", display: "block" }, - cssNormalTransform = { - letterSpacing: 0, - fontWeight: 400 - }, - - cssExpand = [ "Top", "Right", "Bottom", "Left" ], - cssPrefixes = [ "Webkit", "O", "Moz", "ms" ], - - eventsToggle = jQuery.fn.toggle; - -// return a css property mapped to a potentially vendor prefixed property -function vendorPropName( style, name ) { - - // shortcut for names that are not vendor prefixed - if ( name in style ) { - return name; - } - - // check for vendor prefixed names - var capName = name.charAt(0).toUpperCase() + name.slice(1), - origName = name, - i = cssPrefixes.length; - - while ( i-- ) { - name = cssPrefixes[ i ] + capName; - if ( name in style ) { - return name; - } - } - - return origName; -} - -function isHidden( elem, el ) { - elem = el || elem; - return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem ); -} - -function showHide( elements, show ) { - var elem, display, - values = [], - index = 0, - length = elements.length; - - for ( ; index < length; index++ ) { - elem = elements[ index ]; - if ( !elem.style ) { - continue; - } - values[ index ] = jQuery._data( elem, "olddisplay" ); - if ( show ) { - // Reset the inline display of this element to learn if it is - // being hidden by cascaded rules or not - if ( !values[ index ] && elem.style.display === "none" ) { - elem.style.display = ""; - } - - // Set elements which have been overridden with display: none - // in a stylesheet to whatever the default browser style is - // for such an element - if ( elem.style.display === "" && isHidden( elem ) ) { - values[ index ] = jQuery._data( elem, "olddisplay", css_defaultDisplay(elem.nodeName) ); - } - } else { - display = curCSS( elem, "display" ); - - if ( !values[ index ] && display !== "none" ) { - jQuery._data( elem, "olddisplay", display ); - } - } - } - - // Set the display of most of the elements in a second loop - // to avoid the constant reflow - for ( index = 0; index < length; index++ ) { - elem = elements[ index ]; - if ( !elem.style ) { - continue; - } - if ( !show || elem.style.display === "none" || elem.style.display === "" ) { - elem.style.display = show ? values[ index ] || "" : "none"; - } - } - - return elements; -} - -jQuery.fn.extend({ - css: function( name, value ) { - return jQuery.access( this, function( elem, name, value ) { - return value !== undefined ? - jQuery.style( elem, name, value ) : - jQuery.css( elem, name ); - }, name, value, arguments.length > 1 ); - }, - show: function() { - return showHide( this, true ); - }, - hide: function() { - return showHide( this ); - }, - toggle: function( state, fn2 ) { - var bool = typeof state === "boolean"; - - if ( jQuery.isFunction( state ) && jQuery.isFunction( fn2 ) ) { - return eventsToggle.apply( this, arguments ); - } - - return this.each(function() { - if ( bool ? state : isHidden( this ) ) { - jQuery( this ).show(); - } else { - jQuery( this ).hide(); - } - }); - } -}); - -jQuery.extend({ - // Add in style property hooks for overriding the default - // behavior of getting and setting a style property - cssHooks: { - opacity: { - get: function( elem, computed ) { - if ( computed ) { - // We should always get a number back from opacity - var ret = curCSS( elem, "opacity" ); - return ret === "" ? "1" : ret; - - } - } - } - }, - - // Exclude the following css properties to add px - cssNumber: { - "fillOpacity": true, - "fontWeight": true, - "lineHeight": true, - "opacity": true, - "orphans": true, - "widows": true, - "zIndex": true, - "zoom": true - }, - - // Add in properties whose names you wish to fix before - // setting or getting the value - cssProps: { - // normalize float css property - "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat" - }, - - // Get and set the style property on a DOM Node - style: function( elem, name, value, extra ) { - // Don't set styles on text and comment nodes - if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { - return; - } - - // Make sure that we're working with the right name - var ret, type, hooks, - origName = jQuery.camelCase( name ), - style = elem.style; - - name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) ); - - // gets hook for the prefixed version - // followed by the unprefixed version - hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; - - // Check if we're setting a value - if ( value !== undefined ) { - type = typeof value; - - // convert relative number strings (+= or -=) to relative numbers. #7345 - if ( type === "string" && (ret = rrelNum.exec( value )) ) { - value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) ); - // Fixes bug #9237 - type = "number"; - } - - // Make sure that NaN and null values aren't set. See: #7116 - if ( value == null || type === "number" && isNaN( value ) ) { - return; - } - - // If a number was passed in, add 'px' to the (except for certain CSS properties) - if ( type === "number" && !jQuery.cssNumber[ origName ] ) { - value += "px"; - } - - // If a hook was provided, use that value, otherwise just set the specified value - if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) { - // Wrapped to prevent IE from throwing errors when 'invalid' values are provided - // Fixes bug #5509 - try { - style[ name ] = value; - } catch(e) {} - } - - } else { - // If a hook was provided get the non-computed value from there - if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) { - return ret; - } - - // Otherwise just get the value from the style object - return style[ name ]; - } - }, - - css: function( elem, name, numeric, extra ) { - var val, num, hooks, - origName = jQuery.camelCase( name ); - - // Make sure that we're working with the right name - name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) ); - - // gets hook for the prefixed version - // followed by the unprefixed version - hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; - - // If a hook was provided get the computed value from there - if ( hooks && "get" in hooks ) { - val = hooks.get( elem, true, extra ); - } - - // Otherwise, if a way to get the computed value exists, use that - if ( val === undefined ) { - val = curCSS( elem, name ); - } - - //convert "normal" to computed value - if ( val === "normal" && name in cssNormalTransform ) { - val = cssNormalTransform[ name ]; - } - - // Return, converting to number if forced or a qualifier was provided and val looks numeric - if ( numeric || extra !== undefined ) { - num = parseFloat( val ); - return numeric || jQuery.isNumeric( num ) ? num || 0 : val; - } - return val; - }, - - // A method for quickly swapping in/out CSS properties to get correct calculations - swap: function( elem, options, callback ) { - var ret, name, - old = {}; - - // Remember the old values, and insert the new ones - for ( name in options ) { - old[ name ] = elem.style[ name ]; - elem.style[ name ] = options[ name ]; - } - - ret = callback.call( elem ); - - // Revert the old values - for ( name in options ) { - elem.style[ name ] = old[ name ]; - } - - return ret; - } -}); - -// NOTE: To any future maintainer, we've window.getComputedStyle -// because jsdom on node.js will break without it. -if ( window.getComputedStyle ) { - curCSS = function( elem, name ) { - var ret, width, minWidth, maxWidth, - computed = window.getComputedStyle( elem, null ), - style = elem.style; - - if ( computed ) { - - ret = computed[ name ]; - if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) { - ret = jQuery.style( elem, name ); - } - - // A tribute to the "awesome hack by Dean Edwards" - // Chrome < 17 and Safari 5.0 uses "computed value" instead of "used value" for margin-right - // Safari 5.1.7 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels - // this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values - if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) { - width = style.width; - minWidth = style.minWidth; - maxWidth = style.maxWidth; - - style.minWidth = style.maxWidth = style.width = ret; - ret = computed.width; - - style.width = width; - style.minWidth = minWidth; - style.maxWidth = maxWidth; - } - } - - return ret; - }; -} else if ( document.documentElement.currentStyle ) { - curCSS = function( elem, name ) { - var left, rsLeft, - ret = elem.currentStyle && elem.currentStyle[ name ], - style = elem.style; - - // Avoid setting ret to empty string here - // so we don't default to auto - if ( ret == null && style && style[ name ] ) { - ret = style[ name ]; - } - - // From the awesome hack by Dean Edwards - // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 - - // If we're not dealing with a regular pixel number - // but a number that has a weird ending, we need to convert it to pixels - // but not position css attributes, as those are proportional to the parent element instead - // and we can't measure the parent instead because it might trigger a "stacking dolls" problem - if ( rnumnonpx.test( ret ) && !rposition.test( name ) ) { - - // Remember the original values - left = style.left; - rsLeft = elem.runtimeStyle && elem.runtimeStyle.left; - - // Put in the new values to get a computed value out - if ( rsLeft ) { - elem.runtimeStyle.left = elem.currentStyle.left; - } - style.left = name === "fontSize" ? "1em" : ret; - ret = style.pixelLeft + "px"; - - // Revert the changed values - style.left = left; - if ( rsLeft ) { - elem.runtimeStyle.left = rsLeft; - } - } - - return ret === "" ? "auto" : ret; - }; -} - -function setPositiveNumber( elem, value, subtract ) { - var matches = rnumsplit.exec( value ); - return matches ? - Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) : - value; -} - -function augmentWidthOrHeight( elem, name, extra, isBorderBox ) { - var i = extra === ( isBorderBox ? "border" : "content" ) ? - // If we already have the right measurement, avoid augmentation - 4 : - // Otherwise initialize for horizontal or vertical properties - name === "width" ? 1 : 0, - - val = 0; - - for ( ; i < 4; i += 2 ) { - // both box models exclude margin, so add it if we want it - if ( extra === "margin" ) { - // we use jQuery.css instead of curCSS here - // because of the reliableMarginRight CSS hook! - val += jQuery.css( elem, extra + cssExpand[ i ], true ); - } - - // From this point on we use curCSS for maximum performance (relevant in animations) - if ( isBorderBox ) { - // border-box includes padding, so remove it if we want content - if ( extra === "content" ) { - val -= parseFloat( curCSS( elem, "padding" + cssExpand[ i ] ) ) || 0; - } - - // at this point, extra isn't border nor margin, so remove border - if ( extra !== "margin" ) { - val -= parseFloat( curCSS( elem, "border" + cssExpand[ i ] + "Width" ) ) || 0; - } - } else { - // at this point, extra isn't content, so add padding - val += parseFloat( curCSS( elem, "padding" + cssExpand[ i ] ) ) || 0; - - // at this point, extra isn't content nor padding, so add border - if ( extra !== "padding" ) { - val += parseFloat( curCSS( elem, "border" + cssExpand[ i ] + "Width" ) ) || 0; - } - } - } - - return val; -} - -function getWidthOrHeight( elem, name, extra ) { - - // Start with offset property, which is equivalent to the border-box value - var val = name === "width" ? elem.offsetWidth : elem.offsetHeight, - valueIsBorderBox = true, - isBorderBox = jQuery.support.boxSizing && jQuery.css( elem, "boxSizing" ) === "border-box"; - - // some non-html elements return undefined for offsetWidth, so check for null/undefined - // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285 - // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668 - if ( val <= 0 || val == null ) { - // Fall back to computed then uncomputed css if necessary - val = curCSS( elem, name ); - if ( val < 0 || val == null ) { - val = elem.style[ name ]; - } - - // Computed unit is not pixels. Stop here and return. - if ( rnumnonpx.test(val) ) { - return val; - } - - // we need the check for style in case a browser which returns unreliable values - // for getComputedStyle silently falls back to the reliable elem.style - valueIsBorderBox = isBorderBox && ( jQuery.support.boxSizingReliable || val === elem.style[ name ] ); - - // Normalize "", auto, and prepare for extra - val = parseFloat( val ) || 0; - } - - // use the active box-sizing model to add/subtract irrelevant styles - return ( val + - augmentWidthOrHeight( - elem, - name, - extra || ( isBorderBox ? "border" : "content" ), - valueIsBorderBox - ) - ) + "px"; -} - - -// Try to determine the default display value of an element -function css_defaultDisplay( nodeName ) { - if ( elemdisplay[ nodeName ] ) { - return elemdisplay[ nodeName ]; - } - - var elem = jQuery( "<" + nodeName + ">" ).appendTo( document.body ), - display = elem.css("display"); - elem.remove(); - - // If the simple way fails, - // get element's real default display by attaching it to a temp iframe - if ( display === "none" || display === "" ) { - // Use the already-created iframe if possible - iframe = document.body.appendChild( - iframe || jQuery.extend( document.createElement("iframe"), { - frameBorder: 0, - width: 0, - height: 0 - }) - ); - - // Create a cacheable copy of the iframe document on first call. - // IE and Opera will allow us to reuse the iframeDoc without re-writing the fake HTML - // document to it; WebKit & Firefox won't allow reusing the iframe document. - if ( !iframeDoc || !iframe.createElement ) { - iframeDoc = ( iframe.contentWindow || iframe.contentDocument ).document; - iframeDoc.write(""); - iframeDoc.close(); - } - - elem = iframeDoc.body.appendChild( iframeDoc.createElement(nodeName) ); - - display = curCSS( elem, "display" ); - document.body.removeChild( iframe ); - } - - // Store the correct default display - elemdisplay[ nodeName ] = display; - - return display; -} - -jQuery.each([ "height", "width" ], function( i, name ) { - jQuery.cssHooks[ name ] = { - get: function( elem, computed, extra ) { - if ( computed ) { - // certain elements can have dimension info if we invisibly show them - // however, it must have a current display style that would benefit from this - if ( elem.offsetWidth === 0 && rdisplayswap.test( curCSS( elem, "display" ) ) ) { - return jQuery.swap( elem, cssShow, function() { - return getWidthOrHeight( elem, name, extra ); - }); - } else { - return getWidthOrHeight( elem, name, extra ); - } - } - }, - - set: function( elem, value, extra ) { - return setPositiveNumber( elem, value, extra ? - augmentWidthOrHeight( - elem, - name, - extra, - jQuery.support.boxSizing && jQuery.css( elem, "boxSizing" ) === "border-box" - ) : 0 - ); - } - }; -}); - -if ( !jQuery.support.opacity ) { - jQuery.cssHooks.opacity = { - get: function( elem, computed ) { - // IE uses filters for opacity - return ropacity.test( (computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "" ) ? - ( 0.01 * parseFloat( RegExp.$1 ) ) + "" : - computed ? "1" : ""; - }, - - set: function( elem, value ) { - var style = elem.style, - currentStyle = elem.currentStyle, - opacity = jQuery.isNumeric( value ) ? "alpha(opacity=" + value * 100 + ")" : "", - filter = currentStyle && currentStyle.filter || style.filter || ""; - - // IE has trouble with opacity if it does not have layout - // Force it by setting the zoom level - style.zoom = 1; - - // if setting opacity to 1, and no other filters exist - attempt to remove filter attribute #6652 - if ( value >= 1 && jQuery.trim( filter.replace( ralpha, "" ) ) === "" && - style.removeAttribute ) { - - // Setting style.filter to null, "" & " " still leave "filter:" in the cssText - // if "filter:" is present at all, clearType is disabled, we want to avoid this - // style.removeAttribute is IE Only, but so apparently is this code path... - style.removeAttribute( "filter" ); - - // if there there is no filter style applied in a css rule, we are done - if ( currentStyle && !currentStyle.filter ) { - return; - } - } - - // otherwise, set new filter values - style.filter = ralpha.test( filter ) ? - filter.replace( ralpha, opacity ) : - filter + " " + opacity; - } - }; -} - -// These hooks cannot be added until DOM ready because the support test -// for it is not run until after DOM ready -jQuery(function() { - if ( !jQuery.support.reliableMarginRight ) { - jQuery.cssHooks.marginRight = { - get: function( elem, computed ) { - // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right - // Work around by temporarily setting element display to inline-block - return jQuery.swap( elem, { "display": "inline-block" }, function() { - if ( computed ) { - return curCSS( elem, "marginRight" ); - } - }); - } - }; - } - - // Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084 - // getComputedStyle returns percent when specified for top/left/bottom/right - // rather than make the css module depend on the offset module, we just check for it here - if ( !jQuery.support.pixelPosition && jQuery.fn.position ) { - jQuery.each( [ "top", "left" ], function( i, prop ) { - jQuery.cssHooks[ prop ] = { - get: function( elem, computed ) { - if ( computed ) { - var ret = curCSS( elem, prop ); - // if curCSS returns percentage, fallback to offset - return rnumnonpx.test( ret ) ? jQuery( elem ).position()[ prop ] + "px" : ret; - } - } - }; - }); - } - -}); - -if ( jQuery.expr && jQuery.expr.filters ) { - jQuery.expr.filters.hidden = function( elem ) { - return ( elem.offsetWidth === 0 && elem.offsetHeight === 0 ) || (!jQuery.support.reliableHiddenOffsets && ((elem.style && elem.style.display) || curCSS( elem, "display" )) === "none"); - }; - - jQuery.expr.filters.visible = function( elem ) { - return !jQuery.expr.filters.hidden( elem ); - }; -} - -// These hooks are used by animate to expand properties -jQuery.each({ - margin: "", - padding: "", - border: "Width" -}, function( prefix, suffix ) { - jQuery.cssHooks[ prefix + suffix ] = { - expand: function( value ) { - var i, - - // assumes a single number if not a string - parts = typeof value === "string" ? value.split(" ") : [ value ], - expanded = {}; - - for ( i = 0; i < 4; i++ ) { - expanded[ prefix + cssExpand[ i ] + suffix ] = - parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; - } - - return expanded; - } - }; - - if ( !rmargin.test( prefix ) ) { - jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; - } -}); -var r20 = /%20/g, - rbracket = /\[\]$/, - rCRLF = /\r?\n/g, - rinput = /^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i, - rselectTextarea = /^(?:select|textarea)/i; - -jQuery.fn.extend({ - serialize: function() { - return jQuery.param( this.serializeArray() ); - }, - serializeArray: function() { - return this.map(function(){ - return this.elements ? jQuery.makeArray( this.elements ) : this; - }) - .filter(function(){ - return this.name && !this.disabled && - ( this.checked || rselectTextarea.test( this.nodeName ) || - rinput.test( this.type ) ); - }) - .map(function( i, elem ){ - var val = jQuery( this ).val(); - - return val == null ? - null : - jQuery.isArray( val ) ? - jQuery.map( val, function( val, i ){ - return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; - }) : - { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; - }).get(); - } -}); - -//Serialize an array of form elements or a set of -//key/values into a query string -jQuery.param = function( a, traditional ) { - var prefix, - s = [], - add = function( key, value ) { - // If value is a function, invoke it and return its value - value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value ); - s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value ); - }; - - // Set traditional to true for jQuery <= 1.3.2 behavior. - if ( traditional === undefined ) { - traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional; - } - - // If an array was passed in, assume that it is an array of form elements. - if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { - // Serialize the form elements - jQuery.each( a, function() { - add( this.name, this.value ); - }); - - } else { - // If traditional, encode the "old" way (the way 1.3.2 or older - // did it), otherwise encode params recursively. - for ( prefix in a ) { - buildParams( prefix, a[ prefix ], traditional, add ); - } - } - - // Return the resulting serialization - return s.join( "&" ).replace( r20, "+" ); -}; - -function buildParams( prefix, obj, traditional, add ) { - var name; - - if ( jQuery.isArray( obj ) ) { - // Serialize array item. - jQuery.each( obj, function( i, v ) { - if ( traditional || rbracket.test( prefix ) ) { - // Treat each array item as a scalar. - add( prefix, v ); - - } else { - // If array item is non-scalar (array or object), encode its - // numeric index to resolve deserialization ambiguity issues. - // Note that rack (as of 1.0.0) can't currently deserialize - // nested arrays properly, and attempting to do so may cause - // a server error. Possible fixes are to modify rack's - // deserialization algorithm or to provide an option or flag - // to force array serialization to be shallow. - buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add ); - } - }); - - } else if ( !traditional && jQuery.type( obj ) === "object" ) { - // Serialize object item. - for ( name in obj ) { - buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); - } - - } else { - // Serialize scalar item. - add( prefix, obj ); - } -} -var // Document location - ajaxLocation, - // Document location segments - ajaxLocParts, - - rhash = /#.*$/, - rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL - // #7653, #8125, #8152: local protocol detection - rlocalProtocol = /^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/, - rnoContent = /^(?:GET|HEAD)$/, - rprotocol = /^\/\//, - rquery = /\?/, - rscript = /)<[^<]*)*<\/script>/gi, - rts = /([?&])_=[^&]*/, - rurl = /^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/, - - // Keep a copy of the old load method - _load = jQuery.fn.load, - - /* Prefilters - * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) - * 2) These are called: - * - BEFORE asking for a transport - * - AFTER param serialization (s.data is a string if s.processData is true) - * 3) key is the dataType - * 4) the catchall symbol "*" can be used - * 5) execution will start with transport dataType and THEN continue down to "*" if needed - */ - prefilters = {}, - - /* Transports bindings - * 1) key is the dataType - * 2) the catchall symbol "*" can be used - * 3) selection will start with transport dataType and THEN go to "*" if needed - */ - transports = {}, - - // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression - allTypes = ["*/"] + ["*"]; - -// #8138, IE may throw an exception when accessing -// a field from window.location if document.domain has been set -try { - ajaxLocation = location.href; -} catch( e ) { - // Use the href attribute of an A element - // since IE will modify it given document.location - ajaxLocation = document.createElement( "a" ); - ajaxLocation.href = ""; - ajaxLocation = ajaxLocation.href; -} - -// Segment location into parts -ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || []; - -// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport -function addToPrefiltersOrTransports( structure ) { - - // dataTypeExpression is optional and defaults to "*" - return function( dataTypeExpression, func ) { - - if ( typeof dataTypeExpression !== "string" ) { - func = dataTypeExpression; - dataTypeExpression = "*"; - } - - var dataType, list, placeBefore, - dataTypes = dataTypeExpression.toLowerCase().split( core_rspace ), - i = 0, - length = dataTypes.length; - - if ( jQuery.isFunction( func ) ) { - // For each dataType in the dataTypeExpression - for ( ; i < length; i++ ) { - dataType = dataTypes[ i ]; - // We control if we're asked to add before - // any existing element - placeBefore = /^\+/.test( dataType ); - if ( placeBefore ) { - dataType = dataType.substr( 1 ) || "*"; - } - list = structure[ dataType ] = structure[ dataType ] || []; - // then we add to the structure accordingly - list[ placeBefore ? "unshift" : "push" ]( func ); - } - } - }; -} - -// Base inspection function for prefilters and transports -function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR, - dataType /* internal */, inspected /* internal */ ) { - - dataType = dataType || options.dataTypes[ 0 ]; - inspected = inspected || {}; - - inspected[ dataType ] = true; - - var selection, - list = structure[ dataType ], - i = 0, - length = list ? list.length : 0, - executeOnly = ( structure === prefilters ); - - for ( ; i < length && ( executeOnly || !selection ); i++ ) { - selection = list[ i ]( options, originalOptions, jqXHR ); - // If we got redirected to another dataType - // we try there if executing only and not done already - if ( typeof selection === "string" ) { - if ( !executeOnly || inspected[ selection ] ) { - selection = undefined; - } else { - options.dataTypes.unshift( selection ); - selection = inspectPrefiltersOrTransports( - structure, options, originalOptions, jqXHR, selection, inspected ); - } - } - } - // If we're only executing or nothing was selected - // we try the catchall dataType if not done already - if ( ( executeOnly || !selection ) && !inspected[ "*" ] ) { - selection = inspectPrefiltersOrTransports( - structure, options, originalOptions, jqXHR, "*", inspected ); - } - // unnecessary when only executing (prefilters) - // but it'll be ignored by the caller in that case - return selection; -} - -// A special extend for ajax options -// that takes "flat" options (not to be deep extended) -// Fixes #9887 -function ajaxExtend( target, src ) { - var key, deep, - flatOptions = jQuery.ajaxSettings.flatOptions || {}; - for ( key in src ) { - if ( src[ key ] !== undefined ) { - ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; - } - } - if ( deep ) { - jQuery.extend( true, target, deep ); - } -} - -jQuery.fn.load = function( url, params, callback ) { - if ( typeof url !== "string" && _load ) { - return _load.apply( this, arguments ); - } - - // Don't do a request if no elements are being requested - if ( !this.length ) { - return this; - } - - var selector, type, response, - self = this, - off = url.indexOf(" "); - - if ( off >= 0 ) { - selector = url.slice( off, url.length ); - url = url.slice( 0, off ); - } - - // If it's a function - if ( jQuery.isFunction( params ) ) { - - // We assume that it's the callback - callback = params; - params = undefined; - - // Otherwise, build a param string - } else if ( params && typeof params === "object" ) { - type = "POST"; - } - - // Request the remote document - jQuery.ajax({ - url: url, - - // if "type" variable is undefined, then "GET" method will be used - type: type, - dataType: "html", - data: params, - complete: function( jqXHR, status ) { - if ( callback ) { - self.each( callback, response || [ jqXHR.responseText, status, jqXHR ] ); - } - } - }).done(function( responseText ) { - - // Save response for use in complete callback - response = arguments; - - // See if a selector was specified - self.html( selector ? - - // Create a dummy div to hold the results - jQuery("
    ") - - // inject the contents of the document in, removing the scripts - // to avoid any 'Permission Denied' errors in IE - .append( responseText.replace( rscript, "" ) ) - - // Locate the specified elements - .find( selector ) : - - // If not, just inject the full result - responseText ); - - }); - - return this; -}; - -// Attach a bunch of functions for handling common AJAX events -jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split( " " ), function( i, o ){ - jQuery.fn[ o ] = function( f ){ - return this.on( o, f ); - }; -}); - -jQuery.each( [ "get", "post" ], function( i, method ) { - jQuery[ method ] = function( url, data, callback, type ) { - // shift arguments if data argument was omitted - if ( jQuery.isFunction( data ) ) { - type = type || callback; - callback = data; - data = undefined; - } - - return jQuery.ajax({ - type: method, - url: url, - data: data, - success: callback, - dataType: type - }); - }; -}); - -jQuery.extend({ - - getScript: function( url, callback ) { - return jQuery.get( url, undefined, callback, "script" ); - }, - - getJSON: function( url, data, callback ) { - return jQuery.get( url, data, callback, "json" ); - }, - - // Creates a full fledged settings object into target - // with both ajaxSettings and settings fields. - // If target is omitted, writes into ajaxSettings. - ajaxSetup: function( target, settings ) { - if ( settings ) { - // Building a settings object - ajaxExtend( target, jQuery.ajaxSettings ); - } else { - // Extending ajaxSettings - settings = target; - target = jQuery.ajaxSettings; - } - ajaxExtend( target, settings ); - return target; - }, - - ajaxSettings: { - url: ajaxLocation, - isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ), - global: true, - type: "GET", - contentType: "application/x-www-form-urlencoded; charset=UTF-8", - processData: true, - async: true, - /* - timeout: 0, - data: null, - dataType: null, - username: null, - password: null, - cache: null, - throws: false, - traditional: false, - headers: {}, - */ - - accepts: { - xml: "application/xml, text/xml", - html: "text/html", - text: "text/plain", - json: "application/json, text/javascript", - "*": allTypes - }, - - contents: { - xml: /xml/, - html: /html/, - json: /json/ - }, - - responseFields: { - xml: "responseXML", - text: "responseText" - }, - - // List of data converters - // 1) key format is "source_type destination_type" (a single space in-between) - // 2) the catchall symbol "*" can be used for source_type - converters: { - - // Convert anything to text - "* text": window.String, - - // Text to html (true = no transformation) - "text html": true, - - // Evaluate text as a json expression - "text json": jQuery.parseJSON, - - // Parse text as xml - "text xml": jQuery.parseXML - }, - - // For options that shouldn't be deep extended: - // you can add your own custom options here if - // and when you create one that shouldn't be - // deep extended (see ajaxExtend) - flatOptions: { - context: true, - url: true - } - }, - - ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), - ajaxTransport: addToPrefiltersOrTransports( transports ), - - // Main method - ajax: function( url, options ) { - - // If url is an object, simulate pre-1.5 signature - if ( typeof url === "object" ) { - options = url; - url = undefined; - } - - // Force options to be an object - options = options || {}; - - var // ifModified key - ifModifiedKey, - // Response headers - responseHeadersString, - responseHeaders, - // transport - transport, - // timeout handle - timeoutTimer, - // Cross-domain detection vars - parts, - // To know if global events are to be dispatched - fireGlobals, - // Loop variable - i, - // Create the final options object - s = jQuery.ajaxSetup( {}, options ), - // Callbacks context - callbackContext = s.context || s, - // Context for global events - // It's the callbackContext if one was provided in the options - // and if it's a DOM node or a jQuery collection - globalEventContext = callbackContext !== s && - ( callbackContext.nodeType || callbackContext instanceof jQuery ) ? - jQuery( callbackContext ) : jQuery.event, - // Deferreds - deferred = jQuery.Deferred(), - completeDeferred = jQuery.Callbacks( "once memory" ), - // Status-dependent callbacks - statusCode = s.statusCode || {}, - // Headers (they are sent all at once) - requestHeaders = {}, - requestHeadersNames = {}, - // The jqXHR state - state = 0, - // Default abort message - strAbort = "canceled", - // Fake xhr - jqXHR = { - - readyState: 0, - - // Caches the header - setRequestHeader: function( name, value ) { - if ( !state ) { - var lname = name.toLowerCase(); - name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name; - requestHeaders[ name ] = value; - } - return this; - }, - - // Raw string - getAllResponseHeaders: function() { - return state === 2 ? responseHeadersString : null; - }, - - // Builds headers hashtable if needed - getResponseHeader: function( key ) { - var match; - if ( state === 2 ) { - if ( !responseHeaders ) { - responseHeaders = {}; - while( ( match = rheaders.exec( responseHeadersString ) ) ) { - responseHeaders[ match[1].toLowerCase() ] = match[ 2 ]; - } - } - match = responseHeaders[ key.toLowerCase() ]; - } - return match === undefined ? null : match; - }, - - // Overrides response content-type header - overrideMimeType: function( type ) { - if ( !state ) { - s.mimeType = type; - } - return this; - }, - - // Cancel the request - abort: function( statusText ) { - statusText = statusText || strAbort; - if ( transport ) { - transport.abort( statusText ); - } - done( 0, statusText ); - return this; - } - }; - - // Callback for when everything is done - // It is defined here because jslint complains if it is declared - // at the end of the function (which would be more logical and readable) - function done( status, nativeStatusText, responses, headers ) { - var isSuccess, success, error, response, modified, - statusText = nativeStatusText; - - // Called once - if ( state === 2 ) { - return; - } - - // State is "done" now - state = 2; - - // Clear timeout if it exists - if ( timeoutTimer ) { - clearTimeout( timeoutTimer ); - } - - // Dereference transport for early garbage collection - // (no matter how long the jqXHR object will be used) - transport = undefined; - - // Cache response headers - responseHeadersString = headers || ""; - - // Set readyState - jqXHR.readyState = status > 0 ? 4 : 0; - - // Get response data - if ( responses ) { - response = ajaxHandleResponses( s, jqXHR, responses ); - } - - // If successful, handle type chaining - if ( status >= 200 && status < 300 || status === 304 ) { - - // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. - if ( s.ifModified ) { - - modified = jqXHR.getResponseHeader("Last-Modified"); - if ( modified ) { - jQuery.lastModified[ ifModifiedKey ] = modified; - } - modified = jqXHR.getResponseHeader("Etag"); - if ( modified ) { - jQuery.etag[ ifModifiedKey ] = modified; - } - } - - // If not modified - if ( status === 304 ) { - - statusText = "notmodified"; - isSuccess = true; - - // If we have data - } else { - - isSuccess = ajaxConvert( s, response ); - statusText = isSuccess.state; - success = isSuccess.data; - error = isSuccess.error; - isSuccess = !error; - } - } else { - // We extract error from statusText - // then normalize statusText and status for non-aborts - error = statusText; - if ( !statusText || status ) { - statusText = "error"; - if ( status < 0 ) { - status = 0; - } - } - } - - // Set data for the fake xhr object - jqXHR.status = status; - jqXHR.statusText = "" + ( nativeStatusText || statusText ); - - // Success/Error - if ( isSuccess ) { - deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); - } else { - deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); - } - - // Status-dependent callbacks - jqXHR.statusCode( statusCode ); - statusCode = undefined; - - if ( fireGlobals ) { - globalEventContext.trigger( "ajax" + ( isSuccess ? "Success" : "Error" ), - [ jqXHR, s, isSuccess ? success : error ] ); - } - - // Complete - completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); - - if ( fireGlobals ) { - globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); - // Handle the global AJAX counter - if ( !( --jQuery.active ) ) { - jQuery.event.trigger( "ajaxStop" ); - } - } - } - - // Attach deferreds - deferred.promise( jqXHR ); - jqXHR.success = jqXHR.done; - jqXHR.error = jqXHR.fail; - jqXHR.complete = completeDeferred.add; - - // Status-dependent callbacks - jqXHR.statusCode = function( map ) { - if ( map ) { - var tmp; - if ( state < 2 ) { - for ( tmp in map ) { - statusCode[ tmp ] = [ statusCode[tmp], map[tmp] ]; - } - } else { - tmp = map[ jqXHR.status ]; - jqXHR.always( tmp ); - } - } - return this; - }; - - // Remove hash character (#7531: and string promotion) - // Add protocol if not provided (#5866: IE7 issue with protocol-less urls) - // We also use the url parameter if available - s.url = ( ( url || s.url ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" ); - - // Extract dataTypes list - s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( core_rspace ); - - // Determine if a cross-domain request is in order - if ( s.crossDomain == null ) { - parts = rurl.exec( s.url.toLowerCase() ); - s.crossDomain = !!( parts && - ( parts[ 1 ] != ajaxLocParts[ 1 ] || parts[ 2 ] != ajaxLocParts[ 2 ] || - ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) != - ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) ) - ); - } - - // Convert data if not already a string - if ( s.data && s.processData && typeof s.data !== "string" ) { - s.data = jQuery.param( s.data, s.traditional ); - } - - // Apply prefilters - inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); - - // If request was aborted inside a prefilter, stop there - if ( state === 2 ) { - return jqXHR; - } - - // We can fire global events as of now if asked to - fireGlobals = s.global; - - // Uppercase the type - s.type = s.type.toUpperCase(); - - // Determine if request has content - s.hasContent = !rnoContent.test( s.type ); - - // Watch for a new set of requests - if ( fireGlobals && jQuery.active++ === 0 ) { - jQuery.event.trigger( "ajaxStart" ); - } - - // More options handling for requests with no content - if ( !s.hasContent ) { - - // If data is available, append data to url - if ( s.data ) { - s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.data; - // #9682: remove data so that it's not used in an eventual retry - delete s.data; - } - - // Get ifModifiedKey before adding the anti-cache parameter - ifModifiedKey = s.url; - - // Add anti-cache in url if needed - if ( s.cache === false ) { - - var ts = jQuery.now(), - // try replacing _= if it is there - ret = s.url.replace( rts, "$1_=" + ts ); - - // if nothing was replaced, add timestamp to the end - s.url = ret + ( ( ret === s.url ) ? ( rquery.test( s.url ) ? "&" : "?" ) + "_=" + ts : "" ); - } - } - - // Set the correct header, if data is being sent - if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { - jqXHR.setRequestHeader( "Content-Type", s.contentType ); - } - - // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. - if ( s.ifModified ) { - ifModifiedKey = ifModifiedKey || s.url; - if ( jQuery.lastModified[ ifModifiedKey ] ) { - jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ ifModifiedKey ] ); - } - if ( jQuery.etag[ ifModifiedKey ] ) { - jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ ifModifiedKey ] ); - } - } - - // Set the Accepts header for the server, depending on the dataType - jqXHR.setRequestHeader( - "Accept", - s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ? - s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : - s.accepts[ "*" ] - ); - - // Check for headers option - for ( i in s.headers ) { - jqXHR.setRequestHeader( i, s.headers[ i ] ); - } - - // Allow custom headers/mimetypes and early abort - if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) { - // Abort if not done already and return - return jqXHR.abort(); - - } - - // aborting is no longer a cancellation - strAbort = "abort"; - - // Install callbacks on deferreds - for ( i in { success: 1, error: 1, complete: 1 } ) { - jqXHR[ i ]( s[ i ] ); - } - - // Get transport - transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); - - // If no transport, we auto-abort - if ( !transport ) { - done( -1, "No Transport" ); - } else { - jqXHR.readyState = 1; - // Send global event - if ( fireGlobals ) { - globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); - } - // Timeout - if ( s.async && s.timeout > 0 ) { - timeoutTimer = setTimeout( function(){ - jqXHR.abort( "timeout" ); - }, s.timeout ); - } - - try { - state = 1; - transport.send( requestHeaders, done ); - } catch (e) { - // Propagate exception as error if not done - if ( state < 2 ) { - done( -1, e ); - // Simply rethrow otherwise - } else { - throw e; - } - } - } - - return jqXHR; - }, - - // Counter for holding the number of active queries - active: 0, - - // Last-Modified header cache for next request - lastModified: {}, - etag: {} - -}); - -/* Handles responses to an ajax request: - * - sets all responseXXX fields accordingly - * - finds the right dataType (mediates between content-type and expected dataType) - * - returns the corresponding response - */ -function ajaxHandleResponses( s, jqXHR, responses ) { - - var ct, type, finalDataType, firstDataType, - contents = s.contents, - dataTypes = s.dataTypes, - responseFields = s.responseFields; - - // Fill responseXXX fields - for ( type in responseFields ) { - if ( type in responses ) { - jqXHR[ responseFields[type] ] = responses[ type ]; - } - } - - // Remove auto dataType and get content-type in the process - while( dataTypes[ 0 ] === "*" ) { - dataTypes.shift(); - if ( ct === undefined ) { - ct = s.mimeType || jqXHR.getResponseHeader( "content-type" ); - } - } - - // Check if we're dealing with a known content-type - if ( ct ) { - for ( type in contents ) { - if ( contents[ type ] && contents[ type ].test( ct ) ) { - dataTypes.unshift( type ); - break; - } - } - } - - // Check to see if we have a response for the expected dataType - if ( dataTypes[ 0 ] in responses ) { - finalDataType = dataTypes[ 0 ]; - } else { - // Try convertible dataTypes - for ( type in responses ) { - if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) { - finalDataType = type; - break; - } - if ( !firstDataType ) { - firstDataType = type; - } - } - // Or just use first one - finalDataType = finalDataType || firstDataType; - } - - // If we found a dataType - // We add the dataType to the list if needed - // and return the corresponding response - if ( finalDataType ) { - if ( finalDataType !== dataTypes[ 0 ] ) { - dataTypes.unshift( finalDataType ); - } - return responses[ finalDataType ]; - } -} - -// Chain conversions given the request and the original response -function ajaxConvert( s, response ) { - - var conv, conv2, current, tmp, - // Work with a copy of dataTypes in case we need to modify it for conversion - dataTypes = s.dataTypes.slice(), - prev = dataTypes[ 0 ], - converters = {}, - i = 0; - - // Apply the dataFilter if provided - if ( s.dataFilter ) { - response = s.dataFilter( response, s.dataType ); - } - - // Create converters map with lowercased keys - if ( dataTypes[ 1 ] ) { - for ( conv in s.converters ) { - converters[ conv.toLowerCase() ] = s.converters[ conv ]; - } - } - - // Convert to each sequential dataType, tolerating list modification - for ( ; (current = dataTypes[++i]); ) { - - // There's only work to do if current dataType is non-auto - if ( current !== "*" ) { - - // Convert response if prev dataType is non-auto and differs from current - if ( prev !== "*" && prev !== current ) { - - // Seek a direct converter - conv = converters[ prev + " " + current ] || converters[ "* " + current ]; - - // If none found, seek a pair - if ( !conv ) { - for ( conv2 in converters ) { - - // If conv2 outputs current - tmp = conv2.split(" "); - if ( tmp[ 1 ] === current ) { - - // If prev can be converted to accepted input - conv = converters[ prev + " " + tmp[ 0 ] ] || - converters[ "* " + tmp[ 0 ] ]; - if ( conv ) { - // Condense equivalence converters - if ( conv === true ) { - conv = converters[ conv2 ]; - - // Otherwise, insert the intermediate dataType - } else if ( converters[ conv2 ] !== true ) { - current = tmp[ 0 ]; - dataTypes.splice( i--, 0, current ); - } - - break; - } - } - } - } - - // Apply converter (if not an equivalence) - if ( conv !== true ) { - - // Unless errors are allowed to bubble, catch and return them - if ( conv && s["throws"] ) { - response = conv( response ); - } else { - try { - response = conv( response ); - } catch ( e ) { - return { state: "parsererror", error: conv ? e : "No conversion from " + prev + " to " + current }; - } - } - } - } - - // Update prev for next iteration - prev = current; - } - } - - return { state: "success", data: response }; -} -var oldCallbacks = [], - rquestion = /\?/, - rjsonp = /(=)\?(?=&|$)|\?\?/, - nonce = jQuery.now(); - -// Default jsonp settings -jQuery.ajaxSetup({ - jsonp: "callback", - jsonpCallback: function() { - var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce++ ) ); - this[ callback ] = true; - return callback; - } -}); - -// Detect, normalize options and install callbacks for jsonp requests -jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) { - - var callbackName, overwritten, responseContainer, - data = s.data, - url = s.url, - hasCallback = s.jsonp !== false, - replaceInUrl = hasCallback && rjsonp.test( url ), - replaceInData = hasCallback && !replaceInUrl && typeof data === "string" && - !( s.contentType || "" ).indexOf("application/x-www-form-urlencoded") && - rjsonp.test( data ); - - // Handle iff the expected data type is "jsonp" or we have a parameter to set - if ( s.dataTypes[ 0 ] === "jsonp" || replaceInUrl || replaceInData ) { - - // Get callback name, remembering preexisting value associated with it - callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ? - s.jsonpCallback() : - s.jsonpCallback; - overwritten = window[ callbackName ]; - - // Insert callback into url or form data - if ( replaceInUrl ) { - s.url = url.replace( rjsonp, "$1" + callbackName ); - } else if ( replaceInData ) { - s.data = data.replace( rjsonp, "$1" + callbackName ); - } else if ( hasCallback ) { - s.url += ( rquestion.test( url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName; - } - - // Use data converter to retrieve json after script execution - s.converters["script json"] = function() { - if ( !responseContainer ) { - jQuery.error( callbackName + " was not called" ); - } - return responseContainer[ 0 ]; - }; - - // force json dataType - s.dataTypes[ 0 ] = "json"; - - // Install callback - window[ callbackName ] = function() { - responseContainer = arguments; - }; - - // Clean-up function (fires after converters) - jqXHR.always(function() { - // Restore preexisting value - window[ callbackName ] = overwritten; - - // Save back as free - if ( s[ callbackName ] ) { - // make sure that re-using the options doesn't screw things around - s.jsonpCallback = originalSettings.jsonpCallback; - - // save the callback name for future use - oldCallbacks.push( callbackName ); - } - - // Call if it was a function and we have a response - if ( responseContainer && jQuery.isFunction( overwritten ) ) { - overwritten( responseContainer[ 0 ] ); - } - - responseContainer = overwritten = undefined; - }); - - // Delegate to script - return "script"; - } -}); -// Install script dataType -jQuery.ajaxSetup({ - accepts: { - script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript" - }, - contents: { - script: /javascript|ecmascript/ - }, - converters: { - "text script": function( text ) { - jQuery.globalEval( text ); - return text; - } - } -}); - -// Handle cache's special case and global -jQuery.ajaxPrefilter( "script", function( s ) { - if ( s.cache === undefined ) { - s.cache = false; - } - if ( s.crossDomain ) { - s.type = "GET"; - s.global = false; - } -}); - -// Bind script tag hack transport -jQuery.ajaxTransport( "script", function(s) { - - // This transport only deals with cross domain requests - if ( s.crossDomain ) { - - var script, - head = document.head || document.getElementsByTagName( "head" )[0] || document.documentElement; - - return { - - send: function( _, callback ) { - - script = document.createElement( "script" ); - - script.async = "async"; - - if ( s.scriptCharset ) { - script.charset = s.scriptCharset; - } - - script.src = s.url; - - // Attach handlers for all browsers - script.onload = script.onreadystatechange = function( _, isAbort ) { - - if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) { - - // Handle memory leak in IE - script.onload = script.onreadystatechange = null; - - // Remove the script - if ( head && script.parentNode ) { - head.removeChild( script ); - } - - // Dereference the script - script = undefined; - - // Callback if not abort - if ( !isAbort ) { - callback( 200, "success" ); - } - } - }; - // Use insertBefore instead of appendChild to circumvent an IE6 bug. - // This arises when a base node is used (#2709 and #4378). - head.insertBefore( script, head.firstChild ); - }, - - abort: function() { - if ( script ) { - script.onload( 0, 1 ); - } - } - }; - } -}); -var xhrCallbacks, - // #5280: Internet Explorer will keep connections alive if we don't abort on unload - xhrOnUnloadAbort = window.ActiveXObject ? function() { - // Abort all pending requests - for ( var key in xhrCallbacks ) { - xhrCallbacks[ key ]( 0, 1 ); - } - } : false, - xhrId = 0; - -// Functions to create xhrs -function createStandardXHR() { - try { - return new window.XMLHttpRequest(); - } catch( e ) {} -} - -function createActiveXHR() { - try { - return new window.ActiveXObject( "Microsoft.XMLHTTP" ); - } catch( e ) {} -} - -// Create the request object -// (This is still attached to ajaxSettings for backward compatibility) -jQuery.ajaxSettings.xhr = window.ActiveXObject ? - /* Microsoft failed to properly - * implement the XMLHttpRequest in IE7 (can't request local files), - * so we use the ActiveXObject when it is available - * Additionally XMLHttpRequest can be disabled in IE7/IE8 so - * we need a fallback. - */ - function() { - return !this.isLocal && createStandardXHR() || createActiveXHR(); - } : - // For all other browsers, use the standard XMLHttpRequest object - createStandardXHR; - -// Determine support properties -(function( xhr ) { - jQuery.extend( jQuery.support, { - ajax: !!xhr, - cors: !!xhr && ( "withCredentials" in xhr ) - }); -})( jQuery.ajaxSettings.xhr() ); - -// Create transport if the browser can provide an xhr -if ( jQuery.support.ajax ) { - - jQuery.ajaxTransport(function( s ) { - // Cross domain only allowed if supported through XMLHttpRequest - if ( !s.crossDomain || jQuery.support.cors ) { - - var callback; - - return { - send: function( headers, complete ) { - - // Get a new xhr - var handle, i, - xhr = s.xhr(); - - // Open the socket - // Passing null username, generates a login popup on Opera (#2865) - if ( s.username ) { - xhr.open( s.type, s.url, s.async, s.username, s.password ); - } else { - xhr.open( s.type, s.url, s.async ); - } - - // Apply custom fields if provided - if ( s.xhrFields ) { - for ( i in s.xhrFields ) { - xhr[ i ] = s.xhrFields[ i ]; - } - } - - // Override mime type if needed - if ( s.mimeType && xhr.overrideMimeType ) { - xhr.overrideMimeType( s.mimeType ); - } - - // X-Requested-With header - // For cross-domain requests, seeing as conditions for a preflight are - // akin to a jigsaw puzzle, we simply never set it to be sure. - // (it can always be set on a per-request basis or even using ajaxSetup) - // For same-domain requests, won't change header if already provided. - if ( !s.crossDomain && !headers["X-Requested-With"] ) { - headers[ "X-Requested-With" ] = "XMLHttpRequest"; - } - - // Need an extra try/catch for cross domain requests in Firefox 3 - try { - for ( i in headers ) { - xhr.setRequestHeader( i, headers[ i ] ); - } - } catch( _ ) {} - - // Do send the request - // This may raise an exception which is actually - // handled in jQuery.ajax (so no try/catch here) - xhr.send( ( s.hasContent && s.data ) || null ); - - // Listener - callback = function( _, isAbort ) { - - var status, - statusText, - responseHeaders, - responses, - xml; - - // Firefox throws exceptions when accessing properties - // of an xhr when a network error occurred - // http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE) - try { - - // Was never called and is aborted or complete - if ( callback && ( isAbort || xhr.readyState === 4 ) ) { - - // Only called once - callback = undefined; - - // Do not keep as active anymore - if ( handle ) { - xhr.onreadystatechange = jQuery.noop; - if ( xhrOnUnloadAbort ) { - delete xhrCallbacks[ handle ]; - } - } - - // If it's an abort - if ( isAbort ) { - // Abort it manually if needed - if ( xhr.readyState !== 4 ) { - xhr.abort(); - } - } else { - status = xhr.status; - responseHeaders = xhr.getAllResponseHeaders(); - responses = {}; - xml = xhr.responseXML; - - // Construct response list - if ( xml && xml.documentElement /* #4958 */ ) { - responses.xml = xml; - } - - // When requesting binary data, IE6-9 will throw an exception - // on any attempt to access responseText (#11426) - try { - responses.text = xhr.responseText; - } catch( _ ) { - } - - // Firefox throws an exception when accessing - // statusText for faulty cross-domain requests - try { - statusText = xhr.statusText; - } catch( e ) { - // We normalize with Webkit giving an empty statusText - statusText = ""; - } - - // Filter status for non standard behaviors - - // If the request is local and we have data: assume a success - // (success with no data won't get notified, that's the best we - // can do given current implementations) - if ( !status && s.isLocal && !s.crossDomain ) { - status = responses.text ? 200 : 404; - // IE - #1450: sometimes returns 1223 when it should be 204 - } else if ( status === 1223 ) { - status = 204; - } - } - } - } catch( firefoxAccessException ) { - if ( !isAbort ) { - complete( -1, firefoxAccessException ); - } - } - - // Call complete if needed - if ( responses ) { - complete( status, statusText, responses, responseHeaders ); - } - }; - - if ( !s.async ) { - // if we're in sync mode we fire the callback - callback(); - } else if ( xhr.readyState === 4 ) { - // (IE6 & IE7) if it's in cache and has been - // retrieved directly we need to fire the callback - setTimeout( callback, 0 ); - } else { - handle = ++xhrId; - if ( xhrOnUnloadAbort ) { - // Create the active xhrs callbacks list if needed - // and attach the unload handler - if ( !xhrCallbacks ) { - xhrCallbacks = {}; - jQuery( window ).unload( xhrOnUnloadAbort ); - } - // Add to list of active xhrs callbacks - xhrCallbacks[ handle ] = callback; - } - xhr.onreadystatechange = callback; - } - }, - - abort: function() { - if ( callback ) { - callback(0,1); - } - } - }; - } - }); -} -var fxNow, timerId, - rfxtypes = /^(?:toggle|show|hide)$/, - rfxnum = new RegExp( "^(?:([-+])=|)(" + core_pnum + ")([a-z%]*)$", "i" ), - rrun = /queueHooks$/, - animationPrefilters = [ defaultPrefilter ], - tweeners = { - "*": [function( prop, value ) { - var end, unit, prevScale, - tween = this.createTween( prop, value ), - parts = rfxnum.exec( value ), - target = tween.cur(), - start = +target || 0, - scale = 1; - - if ( parts ) { - end = +parts[2]; - unit = parts[3] || ( jQuery.cssNumber[ prop ] ? "" : "px" ); - - // We need to compute starting value - if ( unit !== "px" && start ) { - // Iteratively approximate from a nonzero starting point - // Prefer the current property, because this process will be trivial if it uses the same units - // Fallback to end or a simple constant - start = jQuery.css( tween.elem, prop, true ) || end || 1; - - do { - // If previous iteration zeroed out, double until we get *something* - // Use a string for doubling factor so we don't accidentally see scale as unchanged below - prevScale = scale = scale || ".5"; - - // Adjust and apply - start = start / scale; - jQuery.style( tween.elem, prop, start + unit ); - - // Update scale, tolerating zeroes from tween.cur() - scale = tween.cur() / target; - - // Stop looping if we've hit the mark or scale is unchanged - } while ( scale !== 1 && scale !== prevScale ); - } - - tween.unit = unit; - tween.start = start; - // If a +=/-= token was provided, we're doing a relative animation - tween.end = parts[1] ? start + ( parts[1] + 1 ) * end : end; - } - return tween; - }] - }; - -// Animations created synchronously will run synchronously -function createFxNow() { - setTimeout(function() { - fxNow = undefined; - }, 0 ); - return ( fxNow = jQuery.now() ); -} - -function createTweens( animation, props ) { - jQuery.each( props, function( prop, value ) { - var collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ), - index = 0, - length = collection.length; - for ( ; index < length; index++ ) { - if ( collection[ index ].call( animation, prop, value ) ) { - - // we're done with this property - return; - } - } - }); -} - -function Animation( elem, properties, options ) { - var result, - index = 0, - tweenerIndex = 0, - length = animationPrefilters.length, - deferred = jQuery.Deferred().always( function() { - // don't match elem in the :animated selector - delete tick.elem; - }), - tick = function() { - var currentTime = fxNow || createFxNow(), - remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), - percent = 1 - ( remaining / animation.duration || 0 ), - index = 0, - length = animation.tweens.length; - - for ( ; index < length ; index++ ) { - animation.tweens[ index ].run( percent ); - } - - deferred.notifyWith( elem, [ animation, percent, remaining ]); - - if ( percent < 1 && length ) { - return remaining; - } else { - deferred.resolveWith( elem, [ animation ] ); - return false; - } - }, - animation = deferred.promise({ - elem: elem, - props: jQuery.extend( {}, properties ), - opts: jQuery.extend( true, { specialEasing: {} }, options ), - originalProperties: properties, - originalOptions: options, - startTime: fxNow || createFxNow(), - duration: options.duration, - tweens: [], - createTween: function( prop, end, easing ) { - var tween = jQuery.Tween( elem, animation.opts, prop, end, - animation.opts.specialEasing[ prop ] || animation.opts.easing ); - animation.tweens.push( tween ); - return tween; - }, - stop: function( gotoEnd ) { - var index = 0, - // if we are going to the end, we want to run all the tweens - // otherwise we skip this part - length = gotoEnd ? animation.tweens.length : 0; - - for ( ; index < length ; index++ ) { - animation.tweens[ index ].run( 1 ); - } - - // resolve when we played the last frame - // otherwise, reject - if ( gotoEnd ) { - deferred.resolveWith( elem, [ animation, gotoEnd ] ); - } else { - deferred.rejectWith( elem, [ animation, gotoEnd ] ); - } - return this; - } - }), - props = animation.props; - - propFilter( props, animation.opts.specialEasing ); - - for ( ; index < length ; index++ ) { - result = animationPrefilters[ index ].call( animation, elem, props, animation.opts ); - if ( result ) { - return result; - } - } - - createTweens( animation, props ); - - if ( jQuery.isFunction( animation.opts.start ) ) { - animation.opts.start.call( elem, animation ); - } - - jQuery.fx.timer( - jQuery.extend( tick, { - anim: animation, - queue: animation.opts.queue, - elem: elem - }) - ); - - // attach callbacks from options - return animation.progress( animation.opts.progress ) - .done( animation.opts.done, animation.opts.complete ) - .fail( animation.opts.fail ) - .always( animation.opts.always ); -} - -function propFilter( props, specialEasing ) { - var index, name, easing, value, hooks; - - // camelCase, specialEasing and expand cssHook pass - for ( index in props ) { - name = jQuery.camelCase( index ); - easing = specialEasing[ name ]; - value = props[ index ]; - if ( jQuery.isArray( value ) ) { - easing = value[ 1 ]; - value = props[ index ] = value[ 0 ]; - } - - if ( index !== name ) { - props[ name ] = value; - delete props[ index ]; - } - - hooks = jQuery.cssHooks[ name ]; - if ( hooks && "expand" in hooks ) { - value = hooks.expand( value ); - delete props[ name ]; - - // not quite $.extend, this wont overwrite keys already present. - // also - reusing 'index' from above because we have the correct "name" - for ( index in value ) { - if ( !( index in props ) ) { - props[ index ] = value[ index ]; - specialEasing[ index ] = easing; - } - } - } else { - specialEasing[ name ] = easing; - } - } -} - -jQuery.Animation = jQuery.extend( Animation, { - - tweener: function( props, callback ) { - if ( jQuery.isFunction( props ) ) { - callback = props; - props = [ "*" ]; - } else { - props = props.split(" "); - } - - var prop, - index = 0, - length = props.length; - - for ( ; index < length ; index++ ) { - prop = props[ index ]; - tweeners[ prop ] = tweeners[ prop ] || []; - tweeners[ prop ].unshift( callback ); - } - }, - - prefilter: function( callback, prepend ) { - if ( prepend ) { - animationPrefilters.unshift( callback ); - } else { - animationPrefilters.push( callback ); - } - } -}); - -function defaultPrefilter( elem, props, opts ) { - var index, prop, value, length, dataShow, tween, hooks, oldfire, - anim = this, - style = elem.style, - orig = {}, - handled = [], - hidden = elem.nodeType && isHidden( elem ); - - // handle queue: false promises - if ( !opts.queue ) { - hooks = jQuery._queueHooks( elem, "fx" ); - if ( hooks.unqueued == null ) { - hooks.unqueued = 0; - oldfire = hooks.empty.fire; - hooks.empty.fire = function() { - if ( !hooks.unqueued ) { - oldfire(); - } - }; - } - hooks.unqueued++; - - anim.always(function() { - // doing this makes sure that the complete handler will be called - // before this completes - anim.always(function() { - hooks.unqueued--; - if ( !jQuery.queue( elem, "fx" ).length ) { - hooks.empty.fire(); - } - }); - }); - } - - // height/width overflow pass - if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) { - // Make sure that nothing sneaks out - // Record all 3 overflow attributes because IE does not - // change the overflow attribute when overflowX and - // overflowY are set to the same value - opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; - - // Set display property to inline-block for height/width - // animations on inline elements that are having width/height animated - if ( jQuery.css( elem, "display" ) === "inline" && - jQuery.css( elem, "float" ) === "none" ) { - - // inline-level elements accept inline-block; - // block-level elements need to be inline with layout - if ( !jQuery.support.inlineBlockNeedsLayout || css_defaultDisplay( elem.nodeName ) === "inline" ) { - style.display = "inline-block"; - - } else { - style.zoom = 1; - } - } - } - - if ( opts.overflow ) { - style.overflow = "hidden"; - if ( !jQuery.support.shrinkWrapBlocks ) { - anim.done(function() { - style.overflow = opts.overflow[ 0 ]; - style.overflowX = opts.overflow[ 1 ]; - style.overflowY = opts.overflow[ 2 ]; - }); - } - } - - - // show/hide pass - for ( index in props ) { - value = props[ index ]; - if ( rfxtypes.exec( value ) ) { - delete props[ index ]; - if ( value === ( hidden ? "hide" : "show" ) ) { - continue; - } - handled.push( index ); - } - } - - length = handled.length; - if ( length ) { - dataShow = jQuery._data( elem, "fxshow" ) || jQuery._data( elem, "fxshow", {} ); - if ( hidden ) { - jQuery( elem ).show(); - } else { - anim.done(function() { - jQuery( elem ).hide(); - }); - } - anim.done(function() { - var prop; - jQuery.removeData( elem, "fxshow", true ); - for ( prop in orig ) { - jQuery.style( elem, prop, orig[ prop ] ); - } - }); - for ( index = 0 ; index < length ; index++ ) { - prop = handled[ index ]; - tween = anim.createTween( prop, hidden ? dataShow[ prop ] : 0 ); - orig[ prop ] = dataShow[ prop ] || jQuery.style( elem, prop ); - - if ( !( prop in dataShow ) ) { - dataShow[ prop ] = tween.start; - if ( hidden ) { - tween.end = tween.start; - tween.start = prop === "width" || prop === "height" ? 1 : 0; - } - } - } - } -} - -function Tween( elem, options, prop, end, easing ) { - return new Tween.prototype.init( elem, options, prop, end, easing ); -} -jQuery.Tween = Tween; - -Tween.prototype = { - constructor: Tween, - init: function( elem, options, prop, end, easing, unit ) { - this.elem = elem; - this.prop = prop; - this.easing = easing || "swing"; - this.options = options; - this.start = this.now = this.cur(); - this.end = end; - this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); - }, - cur: function() { - var hooks = Tween.propHooks[ this.prop ]; - - return hooks && hooks.get ? - hooks.get( this ) : - Tween.propHooks._default.get( this ); - }, - run: function( percent ) { - var eased, - hooks = Tween.propHooks[ this.prop ]; - - if ( this.options.duration ) { - this.pos = eased = jQuery.easing[ this.easing ]( - percent, this.options.duration * percent, 0, 1, this.options.duration - ); - } else { - this.pos = eased = percent; - } - this.now = ( this.end - this.start ) * eased + this.start; - - if ( this.options.step ) { - this.options.step.call( this.elem, this.now, this ); - } - - if ( hooks && hooks.set ) { - hooks.set( this ); - } else { - Tween.propHooks._default.set( this ); - } - return this; - } -}; - -Tween.prototype.init.prototype = Tween.prototype; - -Tween.propHooks = { - _default: { - get: function( tween ) { - var result; - - if ( tween.elem[ tween.prop ] != null && - (!tween.elem.style || tween.elem.style[ tween.prop ] == null) ) { - return tween.elem[ tween.prop ]; - } - - // passing any value as a 4th parameter to .css will automatically - // attempt a parseFloat and fallback to a string if the parse fails - // so, simple values such as "10px" are parsed to Float. - // complex values such as "rotate(1rad)" are returned as is. - result = jQuery.css( tween.elem, tween.prop, false, "" ); - // Empty strings, null, undefined and "auto" are converted to 0. - return !result || result === "auto" ? 0 : result; - }, - set: function( tween ) { - // use step hook for back compat - use cssHook if its there - use .style if its - // available and use plain properties where available - if ( jQuery.fx.step[ tween.prop ] ) { - jQuery.fx.step[ tween.prop ]( tween ); - } else if ( tween.elem.style && ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || jQuery.cssHooks[ tween.prop ] ) ) { - jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); - } else { - tween.elem[ tween.prop ] = tween.now; - } - } - } -}; - -// Remove in 2.0 - this supports IE8's panic based approach -// to setting things on disconnected nodes - -Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { - set: function( tween ) { - if ( tween.elem.nodeType && tween.elem.parentNode ) { - tween.elem[ tween.prop ] = tween.now; - } - } -}; - -jQuery.each([ "toggle", "show", "hide" ], function( i, name ) { - var cssFn = jQuery.fn[ name ]; - jQuery.fn[ name ] = function( speed, easing, callback ) { - return speed == null || typeof speed === "boolean" || - // special check for .toggle( handler, handler, ... ) - ( !i && jQuery.isFunction( speed ) && jQuery.isFunction( easing ) ) ? - cssFn.apply( this, arguments ) : - this.animate( genFx( name, true ), speed, easing, callback ); - }; -}); - -jQuery.fn.extend({ - fadeTo: function( speed, to, easing, callback ) { - - // show any hidden elements after setting opacity to 0 - return this.filter( isHidden ).css( "opacity", 0 ).show() - - // animate to the value specified - .end().animate({ opacity: to }, speed, easing, callback ); - }, - animate: function( prop, speed, easing, callback ) { - var empty = jQuery.isEmptyObject( prop ), - optall = jQuery.speed( speed, easing, callback ), - doAnimation = function() { - // Operate on a copy of prop so per-property easing won't be lost - var anim = Animation( this, jQuery.extend( {}, prop ), optall ); - - // Empty animations resolve immediately - if ( empty ) { - anim.stop( true ); - } - }; - - return empty || optall.queue === false ? - this.each( doAnimation ) : - this.queue( optall.queue, doAnimation ); - }, - stop: function( type, clearQueue, gotoEnd ) { - var stopQueue = function( hooks ) { - var stop = hooks.stop; - delete hooks.stop; - stop( gotoEnd ); - }; - - if ( typeof type !== "string" ) { - gotoEnd = clearQueue; - clearQueue = type; - type = undefined; - } - if ( clearQueue && type !== false ) { - this.queue( type || "fx", [] ); - } - - return this.each(function() { - var dequeue = true, - index = type != null && type + "queueHooks", - timers = jQuery.timers, - data = jQuery._data( this ); - - if ( index ) { - if ( data[ index ] && data[ index ].stop ) { - stopQueue( data[ index ] ); - } - } else { - for ( index in data ) { - if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { - stopQueue( data[ index ] ); - } - } - } - - for ( index = timers.length; index--; ) { - if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) { - timers[ index ].anim.stop( gotoEnd ); - dequeue = false; - timers.splice( index, 1 ); - } - } - - // start the next in the queue if the last step wasn't forced - // timers currently will call their complete callbacks, which will dequeue - // but only if they were gotoEnd - if ( dequeue || !gotoEnd ) { - jQuery.dequeue( this, type ); - } - }); - } -}); - -// Generate parameters to create a standard animation -function genFx( type, includeWidth ) { - var which, - attrs = { height: type }, - i = 0; - - // if we include width, step value is 1 to do all cssExpand values, - // if we don't include width, step value is 2 to skip over Left and Right - includeWidth = includeWidth? 1 : 0; - for( ; i < 4 ; i += 2 - includeWidth ) { - which = cssExpand[ i ]; - attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; - } - - if ( includeWidth ) { - attrs.opacity = attrs.width = type; - } - - return attrs; -} - -// Generate shortcuts for custom animations -jQuery.each({ - slideDown: genFx("show"), - slideUp: genFx("hide"), - slideToggle: genFx("toggle"), - fadeIn: { opacity: "show" }, - fadeOut: { opacity: "hide" }, - fadeToggle: { opacity: "toggle" } -}, function( name, props ) { - jQuery.fn[ name ] = function( speed, easing, callback ) { - return this.animate( props, speed, easing, callback ); - }; -}); - -jQuery.speed = function( speed, easing, fn ) { - var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { - complete: fn || !fn && easing || - jQuery.isFunction( speed ) && speed, - duration: speed, - easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing - }; - - opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration : - opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default; - - // normalize opt.queue - true/undefined/null -> "fx" - if ( opt.queue == null || opt.queue === true ) { - opt.queue = "fx"; - } - - // Queueing - opt.old = opt.complete; - - opt.complete = function() { - if ( jQuery.isFunction( opt.old ) ) { - opt.old.call( this ); - } - - if ( opt.queue ) { - jQuery.dequeue( this, opt.queue ); - } - }; - - return opt; -}; - -jQuery.easing = { - linear: function( p ) { - return p; - }, - swing: function( p ) { - return 0.5 - Math.cos( p*Math.PI ) / 2; - } -}; - -jQuery.timers = []; -jQuery.fx = Tween.prototype.init; -jQuery.fx.tick = function() { - var timer, - timers = jQuery.timers, - i = 0; - - for ( ; i < timers.length; i++ ) { - timer = timers[ i ]; - // Checks the timer has not already been removed - if ( !timer() && timers[ i ] === timer ) { - timers.splice( i--, 1 ); - } - } - - if ( !timers.length ) { - jQuery.fx.stop(); - } -}; - -jQuery.fx.timer = function( timer ) { - if ( timer() && jQuery.timers.push( timer ) && !timerId ) { - timerId = setInterval( jQuery.fx.tick, jQuery.fx.interval ); - } -}; - -jQuery.fx.interval = 13; - -jQuery.fx.stop = function() { - clearInterval( timerId ); - timerId = null; -}; - -jQuery.fx.speeds = { - slow: 600, - fast: 200, - // Default speed - _default: 400 -}; - -// Back Compat <1.8 extension point -jQuery.fx.step = {}; - -if ( jQuery.expr && jQuery.expr.filters ) { - jQuery.expr.filters.animated = function( elem ) { - return jQuery.grep(jQuery.timers, function( fn ) { - return elem === fn.elem; - }).length; - }; -} -var rroot = /^(?:body|html)$/i; - -jQuery.fn.offset = function( options ) { - if ( arguments.length ) { - return options === undefined ? - this : - this.each(function( i ) { - jQuery.offset.setOffset( this, options, i ); - }); - } - - var box, docElem, body, win, clientTop, clientLeft, scrollTop, scrollLeft, top, left, - elem = this[ 0 ], - doc = elem && elem.ownerDocument; - - if ( !doc ) { - return; - } - - if ( (body = doc.body) === elem ) { - return jQuery.offset.bodyOffset( elem ); - } - - docElem = doc.documentElement; - - // Make sure we're not dealing with a disconnected DOM node - if ( !jQuery.contains( docElem, elem ) ) { - return { top: 0, left: 0 }; - } - - box = elem.getBoundingClientRect(); - win = getWindow( doc ); - clientTop = docElem.clientTop || body.clientTop || 0; - clientLeft = docElem.clientLeft || body.clientLeft || 0; - scrollTop = win.pageYOffset || docElem.scrollTop; - scrollLeft = win.pageXOffset || docElem.scrollLeft; - top = box.top + scrollTop - clientTop; - left = box.left + scrollLeft - clientLeft; - - return { top: top, left: left }; -}; - -jQuery.offset = { - - bodyOffset: function( body ) { - var top = body.offsetTop, - left = body.offsetLeft; - - if ( jQuery.support.doesNotIncludeMarginInBodyOffset ) { - top += parseFloat( jQuery.css(body, "marginTop") ) || 0; - left += parseFloat( jQuery.css(body, "marginLeft") ) || 0; - } - - return { top: top, left: left }; - }, - - setOffset: function( elem, options, i ) { - var position = jQuery.css( elem, "position" ); - - // set position first, in-case top/left are set even on static elem - if ( position === "static" ) { - elem.style.position = "relative"; - } - - var curElem = jQuery( elem ), - curOffset = curElem.offset(), - curCSSTop = jQuery.css( elem, "top" ), - curCSSLeft = jQuery.css( elem, "left" ), - calculatePosition = ( position === "absolute" || position === "fixed" ) && jQuery.inArray("auto", [curCSSTop, curCSSLeft]) > -1, - props = {}, curPosition = {}, curTop, curLeft; - - // need to be able to calculate position if either top or left is auto and position is either absolute or fixed - if ( calculatePosition ) { - curPosition = curElem.position(); - curTop = curPosition.top; - curLeft = curPosition.left; - } else { - curTop = parseFloat( curCSSTop ) || 0; - curLeft = parseFloat( curCSSLeft ) || 0; - } - - if ( jQuery.isFunction( options ) ) { - options = options.call( elem, i, curOffset ); - } - - if ( options.top != null ) { - props.top = ( options.top - curOffset.top ) + curTop; - } - if ( options.left != null ) { - props.left = ( options.left - curOffset.left ) + curLeft; - } - - if ( "using" in options ) { - options.using.call( elem, props ); - } else { - curElem.css( props ); - } - } -}; - - -jQuery.fn.extend({ - - position: function() { - if ( !this[0] ) { - return; - } - - var elem = this[0], - - // Get *real* offsetParent - offsetParent = this.offsetParent(), - - // Get correct offsets - offset = this.offset(), - parentOffset = rroot.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset(); - - // Subtract element margins - // note: when an element has margin: auto the offsetLeft and marginLeft - // are the same in Safari causing offset.left to incorrectly be 0 - offset.top -= parseFloat( jQuery.css(elem, "marginTop") ) || 0; - offset.left -= parseFloat( jQuery.css(elem, "marginLeft") ) || 0; - - // Add offsetParent borders - parentOffset.top += parseFloat( jQuery.css(offsetParent[0], "borderTopWidth") ) || 0; - parentOffset.left += parseFloat( jQuery.css(offsetParent[0], "borderLeftWidth") ) || 0; - - // Subtract the two offsets - return { - top: offset.top - parentOffset.top, - left: offset.left - parentOffset.left - }; - }, - - offsetParent: function() { - return this.map(function() { - var offsetParent = this.offsetParent || document.body; - while ( offsetParent && (!rroot.test(offsetParent.nodeName) && jQuery.css(offsetParent, "position") === "static") ) { - offsetParent = offsetParent.offsetParent; - } - return offsetParent || document.body; - }); - } -}); - - -// Create scrollLeft and scrollTop methods -jQuery.each( {scrollLeft: "pageXOffset", scrollTop: "pageYOffset"}, function( method, prop ) { - var top = /Y/.test( prop ); - - jQuery.fn[ method ] = function( val ) { - return jQuery.access( this, function( elem, method, val ) { - var win = getWindow( elem ); - - if ( val === undefined ) { - return win ? (prop in win) ? win[ prop ] : - win.document.documentElement[ method ] : - elem[ method ]; - } - - if ( win ) { - win.scrollTo( - !top ? val : jQuery( win ).scrollLeft(), - top ? val : jQuery( win ).scrollTop() - ); - - } else { - elem[ method ] = val; - } - }, method, val, arguments.length, null ); - }; -}); - -function getWindow( elem ) { - return jQuery.isWindow( elem ) ? - elem : - elem.nodeType === 9 ? - elem.defaultView || elem.parentWindow : - false; -} -// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods -jQuery.each( { Height: "height", Width: "width" }, function( name, type ) { - jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) { - // margin is only for outerHeight, outerWidth - jQuery.fn[ funcName ] = function( margin, value ) { - var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ), - extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" ); - - return jQuery.access( this, function( elem, type, value ) { - var doc; - - if ( jQuery.isWindow( elem ) ) { - // As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there - // isn't a whole lot we can do. See pull request at this URL for discussion: - // https://github.com/jquery/jquery/pull/764 - return elem.document.documentElement[ "client" + name ]; - } - - // Get document width or height - if ( elem.nodeType === 9 ) { - doc = elem.documentElement; - - // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height], whichever is greatest - // unfortunately, this causes bug #3838 in IE6/8 only, but there is currently no good, small way to fix it. - return Math.max( - elem.body[ "scroll" + name ], doc[ "scroll" + name ], - elem.body[ "offset" + name ], doc[ "offset" + name ], - doc[ "client" + name ] - ); - } - - return value === undefined ? - // Get width or height on the element, requesting but not forcing parseFloat - jQuery.css( elem, type, value, extra ) : - - // Set width or height on the element - jQuery.style( elem, type, value, extra ); - }, type, chainable ? margin : undefined, chainable, null ); - }; - }); -}); -// Expose jQuery to the global object -window.jQuery = window.$ = jQuery; - -// Expose jQuery as an AMD module, but only for AMD loaders that -// understand the issues with loading multiple versions of jQuery -// in a page that all might call define(). The loader will indicate -// they have special allowances for multiple jQuery versions by -// specifying define.amd.jQuery = true. Register as a named module, -// since jQuery can be concatenated with other files that may use define, -// but not use a proper concatenation script that understands anonymous -// AMD modules. A named AMD is safest and most robust way to register. -// Lowercase jquery is used because AMD module names are derived from -// file names, and jQuery is normally delivered in a lowercase file name. -// Do this after creating the global so that if an AMD module wants to call -// noConflict to hide this version of jQuery, it will work. -if ( typeof define === "function" && define.amd && define.amd.jQuery ) { - define( "jquery", [], function () { return jQuery; } ); -} - -})( window ); diff --git a/public/js/210-jquery-ui-1.8.23.custom.js b/public/js/210-jquery-ui-1.8.23.custom.js deleted file mode 100644 index 2ed0a262c..000000000 --- a/public/js/210-jquery-ui-1.8.23.custom.js +++ /dev/null @@ -1,11713 +0,0 @@ -/*! - * jQuery UI 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI - */ -(function( $, undefined ) { - -// prevent duplicate loading -// this is only a problem because we proxy existing functions -// and we don't want to double proxy them -$.ui = $.ui || {}; -if ( $.ui.version ) { - return; -} - -$.extend( $.ui, { - version: "1.8.23", - - keyCode: { - ALT: 18, - BACKSPACE: 8, - CAPS_LOCK: 20, - COMMA: 188, - COMMAND: 91, - COMMAND_LEFT: 91, // COMMAND - COMMAND_RIGHT: 93, - CONTROL: 17, - DELETE: 46, - DOWN: 40, - END: 35, - ENTER: 13, - ESCAPE: 27, - HOME: 36, - INSERT: 45, - LEFT: 37, - MENU: 93, // COMMAND_RIGHT - NUMPAD_ADD: 107, - NUMPAD_DECIMAL: 110, - NUMPAD_DIVIDE: 111, - NUMPAD_ENTER: 108, - NUMPAD_MULTIPLY: 106, - NUMPAD_SUBTRACT: 109, - PAGE_DOWN: 34, - PAGE_UP: 33, - PERIOD: 190, - RIGHT: 39, - SHIFT: 16, - SPACE: 32, - TAB: 9, - UP: 38, - WINDOWS: 91 // COMMAND - } -}); - -// plugins -$.fn.extend({ - propAttr: $.fn.prop || $.fn.attr, - - _focus: $.fn.focus, - focus: function( delay, fn ) { - return typeof delay === "number" ? - this.each(function() { - var elem = this; - setTimeout(function() { - $( elem ).focus(); - if ( fn ) { - fn.call( elem ); - } - }, delay ); - }) : - this._focus.apply( this, arguments ); - }, - - scrollParent: function() { - var scrollParent; - if (($.browser.msie && (/(static|relative)/).test(this.css('position'))) || (/absolute/).test(this.css('position'))) { - scrollParent = this.parents().filter(function() { - return (/(relative|absolute|fixed)/).test($.curCSS(this,'position',1)) && (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1)); - }).eq(0); - } else { - scrollParent = this.parents().filter(function() { - return (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1)); - }).eq(0); - } - - return (/fixed/).test(this.css('position')) || !scrollParent.length ? $(document) : scrollParent; - }, - - zIndex: function( zIndex ) { - if ( zIndex !== undefined ) { - return this.css( "zIndex", zIndex ); - } - - if ( this.length ) { - var elem = $( this[ 0 ] ), position, value; - while ( elem.length && elem[ 0 ] !== document ) { - // Ignore z-index if position is set to a value where z-index is ignored by the browser - // This makes behavior of this function consistent across browsers - // WebKit always returns auto if the element is positioned - position = elem.css( "position" ); - if ( position === "absolute" || position === "relative" || position === "fixed" ) { - // IE returns 0 when zIndex is not specified - // other browsers return a string - // we ignore the case of nested elements with an explicit value of 0 - //
    - value = parseInt( elem.css( "zIndex" ), 10 ); - if ( !isNaN( value ) && value !== 0 ) { - return value; - } - } - elem = elem.parent(); - } - } - - return 0; - }, - - disableSelection: function() { - return this.bind( ( $.support.selectstart ? "selectstart" : "mousedown" ) + - ".ui-disableSelection", function( event ) { - event.preventDefault(); - }); - }, - - enableSelection: function() { - return this.unbind( ".ui-disableSelection" ); - } -}); - -// support: jQuery <1.8 -if ( !$( "" ).outerWidth( 1 ).jquery ) { - $.each( [ "Width", "Height" ], function( i, name ) { - var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ], - type = name.toLowerCase(), - orig = { - innerWidth: $.fn.innerWidth, - innerHeight: $.fn.innerHeight, - outerWidth: $.fn.outerWidth, - outerHeight: $.fn.outerHeight - }; - - function reduce( elem, size, border, margin ) { - $.each( side, function() { - size -= parseFloat( $.curCSS( elem, "padding" + this, true) ) || 0; - if ( border ) { - size -= parseFloat( $.curCSS( elem, "border" + this + "Width", true) ) || 0; - } - if ( margin ) { - size -= parseFloat( $.curCSS( elem, "margin" + this, true) ) || 0; - } - }); - return size; - } - - $.fn[ "inner" + name ] = function( size ) { - if ( size === undefined ) { - return orig[ "inner" + name ].call( this ); - } - - return this.each(function() { - $( this ).css( type, reduce( this, size ) + "px" ); - }); - }; - - $.fn[ "outer" + name] = function( size, margin ) { - if ( typeof size !== "number" ) { - return orig[ "outer" + name ].call( this, size ); - } - - return this.each(function() { - $( this).css( type, reduce( this, size, true, margin ) + "px" ); - }); - }; - }); -} - -// selectors -function focusable( element, isTabIndexNotNaN ) { - var nodeName = element.nodeName.toLowerCase(); - if ( "area" === nodeName ) { - var map = element.parentNode, - mapName = map.name, - img; - if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) { - return false; - } - img = $( "img[usemap=#" + mapName + "]" )[0]; - return !!img && visible( img ); - } - return ( /input|select|textarea|button|object/.test( nodeName ) - ? !element.disabled - : "a" == nodeName - ? element.href || isTabIndexNotNaN - : isTabIndexNotNaN) - // the element and all of its ancestors must be visible - && visible( element ); -} - -function visible( element ) { - return !$( element ).parents().andSelf().filter(function() { - return $.curCSS( this, "visibility" ) === "hidden" || - $.expr.filters.hidden( this ); - }).length; -} - -$.extend( $.expr[ ":" ], { - data: $.expr.createPseudo ? - $.expr.createPseudo(function( dataName ) { - return function( elem ) { - return !!$.data( elem, dataName ); - }; - }) : - // support: jQuery <1.8 - function( elem, i, match ) { - return !!$.data( elem, match[ 3 ] ); - }, - - focusable: function( element ) { - return focusable( element, !isNaN( $.attr( element, "tabindex" ) ) ); - }, - - tabbable: function( element ) { - var tabIndex = $.attr( element, "tabindex" ), - isTabIndexNaN = isNaN( tabIndex ); - return ( isTabIndexNaN || tabIndex >= 0 ) && focusable( element, !isTabIndexNaN ); - } -}); - -// support -$(function() { - var body = document.body, - div = body.appendChild( div = document.createElement( "div" ) ); - - // access offsetHeight before setting the style to prevent a layout bug - // in IE 9 which causes the elemnt to continue to take up space even - // after it is removed from the DOM (#8026) - div.offsetHeight; - - $.extend( div.style, { - minHeight: "100px", - height: "auto", - padding: 0, - borderWidth: 0 - }); - - $.support.minHeight = div.offsetHeight === 100; - $.support.selectstart = "onselectstart" in div; - - // set display to none to avoid a layout bug in IE - // http://dev.jquery.com/ticket/4014 - body.removeChild( div ).style.display = "none"; -}); - -// jQuery <1.4.3 uses curCSS, in 1.4.3 - 1.7.2 curCSS = css, 1.8+ only has css -if ( !$.curCSS ) { - $.curCSS = $.css; -} - - - - - -// deprecated -$.extend( $.ui, { - // $.ui.plugin is deprecated. Use the proxy pattern instead. - plugin: { - add: function( module, option, set ) { - var proto = $.ui[ module ].prototype; - for ( var i in set ) { - proto.plugins[ i ] = proto.plugins[ i ] || []; - proto.plugins[ i ].push( [ option, set[ i ] ] ); - } - }, - call: function( instance, name, args ) { - var set = instance.plugins[ name ]; - if ( !set || !instance.element[ 0 ].parentNode ) { - return; - } - - for ( var i = 0; i < set.length; i++ ) { - if ( instance.options[ set[ i ][ 0 ] ] ) { - set[ i ][ 1 ].apply( instance.element, args ); - } - } - } - }, - - // will be deprecated when we switch to jQuery 1.4 - use jQuery.contains() - contains: function( a, b ) { - return document.compareDocumentPosition ? - a.compareDocumentPosition( b ) & 16 : - a !== b && a.contains( b ); - }, - - // only used by resizable - hasScroll: function( el, a ) { - - //If overflow is hidden, the element might have extra content, but the user wants to hide it - if ( $( el ).css( "overflow" ) === "hidden") { - return false; - } - - var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop", - has = false; - - if ( el[ scroll ] > 0 ) { - return true; - } - - // TODO: determine which cases actually cause this to happen - // if the element doesn't have the scroll set, see if it's possible to - // set the scroll - el[ scroll ] = 1; - has = ( el[ scroll ] > 0 ); - el[ scroll ] = 0; - return has; - }, - - // these are odd functions, fix the API or move into individual plugins - isOverAxis: function( x, reference, size ) { - //Determines when x coordinate is over "b" element axis - return ( x > reference ) && ( x < ( reference + size ) ); - }, - isOver: function( y, x, top, left, height, width ) { - //Determines when x, y coordinates is over "b" element - return $.ui.isOverAxis( y, top, height ) && $.ui.isOverAxis( x, left, width ); - } -}); - -})( jQuery ); -/*! - * jQuery UI Widget 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Widget - */ -(function( $, undefined ) { - -// jQuery 1.4+ -if ( $.cleanData ) { - var _cleanData = $.cleanData; - $.cleanData = function( elems ) { - for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) { - try { - $( elem ).triggerHandler( "remove" ); - // http://bugs.jquery.com/ticket/8235 - } catch( e ) {} - } - _cleanData( elems ); - }; -} else { - var _remove = $.fn.remove; - $.fn.remove = function( selector, keepData ) { - return this.each(function() { - if ( !keepData ) { - if ( !selector || $.filter( selector, [ this ] ).length ) { - $( "*", this ).add( [ this ] ).each(function() { - try { - $( this ).triggerHandler( "remove" ); - // http://bugs.jquery.com/ticket/8235 - } catch( e ) {} - }); - } - } - return _remove.call( $(this), selector, keepData ); - }); - }; -} - -$.widget = function( name, base, prototype ) { - var namespace = name.split( "." )[ 0 ], - fullName; - name = name.split( "." )[ 1 ]; - fullName = namespace + "-" + name; - - if ( !prototype ) { - prototype = base; - base = $.Widget; - } - - // create selector for plugin - $.expr[ ":" ][ fullName ] = function( elem ) { - return !!$.data( elem, name ); - }; - - $[ namespace ] = $[ namespace ] || {}; - $[ namespace ][ name ] = function( options, element ) { - // allow instantiation without initializing for simple inheritance - if ( arguments.length ) { - this._createWidget( options, element ); - } - }; - - var basePrototype = new base(); - // we need to make the options hash a property directly on the new instance - // otherwise we'll modify the options hash on the prototype that we're - // inheriting from -// $.each( basePrototype, function( key, val ) { -// if ( $.isPlainObject(val) ) { -// basePrototype[ key ] = $.extend( {}, val ); -// } -// }); - basePrototype.options = $.extend( true, {}, basePrototype.options ); - $[ namespace ][ name ].prototype = $.extend( true, basePrototype, { - namespace: namespace, - widgetName: name, - widgetEventPrefix: $[ namespace ][ name ].prototype.widgetEventPrefix || name, - widgetBaseClass: fullName - }, prototype ); - - $.widget.bridge( name, $[ namespace ][ name ] ); -}; - -$.widget.bridge = function( name, object ) { - $.fn[ name ] = function( options ) { - var isMethodCall = typeof options === "string", - args = Array.prototype.slice.call( arguments, 1 ), - returnValue = this; - - // allow multiple hashes to be passed on init - options = !isMethodCall && args.length ? - $.extend.apply( null, [ true, options ].concat(args) ) : - options; - - // prevent calls to internal methods - if ( isMethodCall && options.charAt( 0 ) === "_" ) { - return returnValue; - } - - if ( isMethodCall ) { - this.each(function() { - var instance = $.data( this, name ), - methodValue = instance && $.isFunction( instance[options] ) ? - instance[ options ].apply( instance, args ) : - instance; - // TODO: add this back in 1.9 and use $.error() (see #5972) -// if ( !instance ) { -// throw "cannot call methods on " + name + " prior to initialization; " + -// "attempted to call method '" + options + "'"; -// } -// if ( !$.isFunction( instance[options] ) ) { -// throw "no such method '" + options + "' for " + name + " widget instance"; -// } -// var methodValue = instance[ options ].apply( instance, args ); - if ( methodValue !== instance && methodValue !== undefined ) { - returnValue = methodValue; - return false; - } - }); - } else { - this.each(function() { - var instance = $.data( this, name ); - if ( instance ) { - instance.option( options || {} )._init(); - } else { - $.data( this, name, new object( options, this ) ); - } - }); - } - - return returnValue; - }; -}; - -$.Widget = function( options, element ) { - // allow instantiation without initializing for simple inheritance - if ( arguments.length ) { - this._createWidget( options, element ); - } -}; - -$.Widget.prototype = { - widgetName: "widget", - widgetEventPrefix: "", - options: { - disabled: false - }, - _createWidget: function( options, element ) { - // $.widget.bridge stores the plugin instance, but we do it anyway - // so that it's stored even before the _create function runs - $.data( element, this.widgetName, this ); - this.element = $( element ); - this.options = $.extend( true, {}, - this.options, - this._getCreateOptions(), - options ); - - var self = this; - this.element.bind( "remove." + this.widgetName, function() { - self.destroy(); - }); - - this._create(); - this._trigger( "create" ); - this._init(); - }, - _getCreateOptions: function() { - return $.metadata && $.metadata.get( this.element[0] )[ this.widgetName ]; - }, - _create: function() {}, - _init: function() {}, - - destroy: function() { - this.element - .unbind( "." + this.widgetName ) - .removeData( this.widgetName ); - this.widget() - .unbind( "." + this.widgetName ) - .removeAttr( "aria-disabled" ) - .removeClass( - this.widgetBaseClass + "-disabled " + - "ui-state-disabled" ); - }, - - widget: function() { - return this.element; - }, - - option: function( key, value ) { - var options = key; - - if ( arguments.length === 0 ) { - // don't return a reference to the internal hash - return $.extend( {}, this.options ); - } - - if (typeof key === "string" ) { - if ( value === undefined ) { - return this.options[ key ]; - } - options = {}; - options[ key ] = value; - } - - this._setOptions( options ); - - return this; - }, - _setOptions: function( options ) { - var self = this; - $.each( options, function( key, value ) { - self._setOption( key, value ); - }); - - return this; - }, - _setOption: function( key, value ) { - this.options[ key ] = value; - - if ( key === "disabled" ) { - this.widget() - [ value ? "addClass" : "removeClass"]( - this.widgetBaseClass + "-disabled" + " " + - "ui-state-disabled" ) - .attr( "aria-disabled", value ); - } - - return this; - }, - - enable: function() { - return this._setOption( "disabled", false ); - }, - disable: function() { - return this._setOption( "disabled", true ); - }, - - _trigger: function( type, event, data ) { - var prop, orig, - callback = this.options[ type ]; - - data = data || {}; - event = $.Event( event ); - event.type = ( type === this.widgetEventPrefix ? - type : - this.widgetEventPrefix + type ).toLowerCase(); - // the original event may come from any element - // so we need to reset the target on the new event - event.target = this.element[ 0 ]; - - // copy original event properties over to the new event - orig = event.originalEvent; - if ( orig ) { - for ( prop in orig ) { - if ( !( prop in event ) ) { - event[ prop ] = orig[ prop ]; - } - } - } - - this.element.trigger( event, data ); - - return !( $.isFunction(callback) && - callback.call( this.element[0], event, data ) === false || - event.isDefaultPrevented() ); - } -}; - -})( jQuery ); -/*! - * jQuery UI Mouse 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Mouse - * - * Depends: - * jquery.ui.widget.js - */ -(function( $, undefined ) { - -var mouseHandled = false; -$( document ).mouseup( function( e ) { - mouseHandled = false; -}); - -$.widget("ui.mouse", { - options: { - cancel: ':input,option', - distance: 1, - delay: 0 - }, - _mouseInit: function() { - var self = this; - - this.element - .bind('mousedown.'+this.widgetName, function(event) { - return self._mouseDown(event); - }) - .bind('click.'+this.widgetName, function(event) { - if (true === $.data(event.target, self.widgetName + '.preventClickEvent')) { - $.removeData(event.target, self.widgetName + '.preventClickEvent'); - event.stopImmediatePropagation(); - return false; - } - }); - - this.started = false; - }, - - // TODO: make sure destroying one instance of mouse doesn't mess with - // other instances of mouse - _mouseDestroy: function() { - this.element.unbind('.'+this.widgetName); - if ( this._mouseMoveDelegate ) { - $(document) - .unbind('mousemove.'+this.widgetName, this._mouseMoveDelegate) - .unbind('mouseup.'+this.widgetName, this._mouseUpDelegate); - } - }, - - _mouseDown: function(event) { - // don't let more than one widget handle mouseStart - if( mouseHandled ) { return }; - - // we may have missed mouseup (out of window) - (this._mouseStarted && this._mouseUp(event)); - - this._mouseDownEvent = event; - - var self = this, - btnIsLeft = (event.which == 1), - // event.target.nodeName works around a bug in IE 8 with - // disabled inputs (#7620) - elIsCancel = (typeof this.options.cancel == "string" && event.target.nodeName ? $(event.target).closest(this.options.cancel).length : false); - if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) { - return true; - } - - this.mouseDelayMet = !this.options.delay; - if (!this.mouseDelayMet) { - this._mouseDelayTimer = setTimeout(function() { - self.mouseDelayMet = true; - }, this.options.delay); - } - - if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) { - this._mouseStarted = (this._mouseStart(event) !== false); - if (!this._mouseStarted) { - event.preventDefault(); - return true; - } - } - - // Click event may never have fired (Gecko & Opera) - if (true === $.data(event.target, this.widgetName + '.preventClickEvent')) { - $.removeData(event.target, this.widgetName + '.preventClickEvent'); - } - - // these delegates are required to keep context - this._mouseMoveDelegate = function(event) { - return self._mouseMove(event); - }; - this._mouseUpDelegate = function(event) { - return self._mouseUp(event); - }; - $(document) - .bind('mousemove.'+this.widgetName, this._mouseMoveDelegate) - .bind('mouseup.'+this.widgetName, this._mouseUpDelegate); - - event.preventDefault(); - - mouseHandled = true; - return true; - }, - - _mouseMove: function(event) { - // IE mouseup check - mouseup happened when mouse was out of window - if ($.browser.msie && !(document.documentMode >= 9) && !event.button) { - return this._mouseUp(event); - } - - if (this._mouseStarted) { - this._mouseDrag(event); - return event.preventDefault(); - } - - if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) { - this._mouseStarted = - (this._mouseStart(this._mouseDownEvent, event) !== false); - (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event)); - } - - return !this._mouseStarted; - }, - - _mouseUp: function(event) { - $(document) - .unbind('mousemove.'+this.widgetName, this._mouseMoveDelegate) - .unbind('mouseup.'+this.widgetName, this._mouseUpDelegate); - - if (this._mouseStarted) { - this._mouseStarted = false; - - if (event.target == this._mouseDownEvent.target) { - $.data(event.target, this.widgetName + '.preventClickEvent', true); - } - - this._mouseStop(event); - } - - return false; - }, - - _mouseDistanceMet: function(event) { - return (Math.max( - Math.abs(this._mouseDownEvent.pageX - event.pageX), - Math.abs(this._mouseDownEvent.pageY - event.pageY) - ) >= this.options.distance - ); - }, - - _mouseDelayMet: function(event) { - return this.mouseDelayMet; - }, - - // These are placeholder methods, to be overriden by extending plugin - _mouseStart: function(event) {}, - _mouseDrag: function(event) {}, - _mouseStop: function(event) {}, - _mouseCapture: function(event) { return true; } -}); - -})(jQuery); -/*! - * jQuery UI Position 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Position - */ -(function( $, undefined ) { - -$.ui = $.ui || {}; - -var horizontalPositions = /left|center|right/, - verticalPositions = /top|center|bottom/, - center = "center", - support = {}, - _position = $.fn.position, - _offset = $.fn.offset; - -$.fn.position = function( options ) { - if ( !options || !options.of ) { - return _position.apply( this, arguments ); - } - - // make a copy, we don't want to modify arguments - options = $.extend( {}, options ); - - var target = $( options.of ), - targetElem = target[0], - collision = ( options.collision || "flip" ).split( " " ), - offset = options.offset ? options.offset.split( " " ) : [ 0, 0 ], - targetWidth, - targetHeight, - basePosition; - - if ( targetElem.nodeType === 9 ) { - targetWidth = target.width(); - targetHeight = target.height(); - basePosition = { top: 0, left: 0 }; - // TODO: use $.isWindow() in 1.9 - } else if ( targetElem.setTimeout ) { - targetWidth = target.width(); - targetHeight = target.height(); - basePosition = { top: target.scrollTop(), left: target.scrollLeft() }; - } else if ( targetElem.preventDefault ) { - // force left top to allow flipping - options.at = "left top"; - targetWidth = targetHeight = 0; - basePosition = { top: options.of.pageY, left: options.of.pageX }; - } else { - targetWidth = target.outerWidth(); - targetHeight = target.outerHeight(); - basePosition = target.offset(); - } - - // force my and at to have valid horizontal and veritcal positions - // if a value is missing or invalid, it will be converted to center - $.each( [ "my", "at" ], function() { - var pos = ( options[this] || "" ).split( " " ); - if ( pos.length === 1) { - pos = horizontalPositions.test( pos[0] ) ? - pos.concat( [center] ) : - verticalPositions.test( pos[0] ) ? - [ center ].concat( pos ) : - [ center, center ]; - } - pos[ 0 ] = horizontalPositions.test( pos[0] ) ? pos[ 0 ] : center; - pos[ 1 ] = verticalPositions.test( pos[1] ) ? pos[ 1 ] : center; - options[ this ] = pos; - }); - - // normalize collision option - if ( collision.length === 1 ) { - collision[ 1 ] = collision[ 0 ]; - } - - // normalize offset option - offset[ 0 ] = parseInt( offset[0], 10 ) || 0; - if ( offset.length === 1 ) { - offset[ 1 ] = offset[ 0 ]; - } - offset[ 1 ] = parseInt( offset[1], 10 ) || 0; - - if ( options.at[0] === "right" ) { - basePosition.left += targetWidth; - } else if ( options.at[0] === center ) { - basePosition.left += targetWidth / 2; - } - - if ( options.at[1] === "bottom" ) { - basePosition.top += targetHeight; - } else if ( options.at[1] === center ) { - basePosition.top += targetHeight / 2; - } - - basePosition.left += offset[ 0 ]; - basePosition.top += offset[ 1 ]; - - return this.each(function() { - var elem = $( this ), - elemWidth = elem.outerWidth(), - elemHeight = elem.outerHeight(), - marginLeft = parseInt( $.curCSS( this, "marginLeft", true ) ) || 0, - marginTop = parseInt( $.curCSS( this, "marginTop", true ) ) || 0, - collisionWidth = elemWidth + marginLeft + - ( parseInt( $.curCSS( this, "marginRight", true ) ) || 0 ), - collisionHeight = elemHeight + marginTop + - ( parseInt( $.curCSS( this, "marginBottom", true ) ) || 0 ), - position = $.extend( {}, basePosition ), - collisionPosition; - - if ( options.my[0] === "right" ) { - position.left -= elemWidth; - } else if ( options.my[0] === center ) { - position.left -= elemWidth / 2; - } - - if ( options.my[1] === "bottom" ) { - position.top -= elemHeight; - } else if ( options.my[1] === center ) { - position.top -= elemHeight / 2; - } - - // prevent fractions if jQuery version doesn't support them (see #5280) - if ( !support.fractions ) { - position.left = Math.round( position.left ); - position.top = Math.round( position.top ); - } - - collisionPosition = { - left: position.left - marginLeft, - top: position.top - marginTop - }; - - $.each( [ "left", "top" ], function( i, dir ) { - if ( $.ui.position[ collision[i] ] ) { - $.ui.position[ collision[i] ][ dir ]( position, { - targetWidth: targetWidth, - targetHeight: targetHeight, - elemWidth: elemWidth, - elemHeight: elemHeight, - collisionPosition: collisionPosition, - collisionWidth: collisionWidth, - collisionHeight: collisionHeight, - offset: offset, - my: options.my, - at: options.at - }); - } - }); - - if ( $.fn.bgiframe ) { - elem.bgiframe(); - } - elem.offset( $.extend( position, { using: options.using } ) ); - }); -}; - -$.ui.position = { - fit: { - left: function( position, data ) { - var win = $( window ), - over = data.collisionPosition.left + data.collisionWidth - win.width() - win.scrollLeft(); - position.left = over > 0 ? position.left - over : Math.max( position.left - data.collisionPosition.left, position.left ); - }, - top: function( position, data ) { - var win = $( window ), - over = data.collisionPosition.top + data.collisionHeight - win.height() - win.scrollTop(); - position.top = over > 0 ? position.top - over : Math.max( position.top - data.collisionPosition.top, position.top ); - } - }, - - flip: { - left: function( position, data ) { - if ( data.at[0] === center ) { - return; - } - var win = $( window ), - over = data.collisionPosition.left + data.collisionWidth - win.width() - win.scrollLeft(), - myOffset = data.my[ 0 ] === "left" ? - -data.elemWidth : - data.my[ 0 ] === "right" ? - data.elemWidth : - 0, - atOffset = data.at[ 0 ] === "left" ? - data.targetWidth : - -data.targetWidth, - offset = -2 * data.offset[ 0 ]; - position.left += data.collisionPosition.left < 0 ? - myOffset + atOffset + offset : - over > 0 ? - myOffset + atOffset + offset : - 0; - }, - top: function( position, data ) { - if ( data.at[1] === center ) { - return; - } - var win = $( window ), - over = data.collisionPosition.top + data.collisionHeight - win.height() - win.scrollTop(), - myOffset = data.my[ 1 ] === "top" ? - -data.elemHeight : - data.my[ 1 ] === "bottom" ? - data.elemHeight : - 0, - atOffset = data.at[ 1 ] === "top" ? - data.targetHeight : - -data.targetHeight, - offset = -2 * data.offset[ 1 ]; - position.top += data.collisionPosition.top < 0 ? - myOffset + atOffset + offset : - over > 0 ? - myOffset + atOffset + offset : - 0; - } - } -}; - -// offset setter from jQuery 1.4 -if ( !$.offset.setOffset ) { - $.offset.setOffset = function( elem, options ) { - // set position first, in-case top/left are set even on static elem - if ( /static/.test( $.curCSS( elem, "position" ) ) ) { - elem.style.position = "relative"; - } - var curElem = $( elem ), - curOffset = curElem.offset(), - curTop = parseInt( $.curCSS( elem, "top", true ), 10 ) || 0, - curLeft = parseInt( $.curCSS( elem, "left", true ), 10) || 0, - props = { - top: (options.top - curOffset.top) + curTop, - left: (options.left - curOffset.left) + curLeft - }; - - if ( 'using' in options ) { - options.using.call( elem, props ); - } else { - curElem.css( props ); - } - }; - - $.fn.offset = function( options ) { - var elem = this[ 0 ]; - if ( !elem || !elem.ownerDocument ) { return null; } - if ( options ) { - if ( $.isFunction( options ) ) { - return this.each(function( i ) { - $( this ).offset( options.call( this, i, $( this ).offset() ) ); - }); - } - return this.each(function() { - $.offset.setOffset( this, options ); - }); - } - return _offset.call( this ); - }; -} - -// jQuery <1.4.3 uses curCSS, in 1.4.3 - 1.7.2 curCSS = css, 1.8+ only has css -if ( !$.curCSS ) { - $.curCSS = $.css; -} - -// fraction support test (older versions of jQuery don't support fractions) -(function () { - var body = document.getElementsByTagName( "body" )[ 0 ], - div = document.createElement( "div" ), - testElement, testElementParent, testElementStyle, offset, offsetTotal; - - //Create a "fake body" for testing based on method used in jQuery.support - testElement = document.createElement( body ? "div" : "body" ); - testElementStyle = { - visibility: "hidden", - width: 0, - height: 0, - border: 0, - margin: 0, - background: "none" - }; - if ( body ) { - $.extend( testElementStyle, { - position: "absolute", - left: "-1000px", - top: "-1000px" - }); - } - for ( var i in testElementStyle ) { - testElement.style[ i ] = testElementStyle[ i ]; - } - testElement.appendChild( div ); - testElementParent = body || document.documentElement; - testElementParent.insertBefore( testElement, testElementParent.firstChild ); - - div.style.cssText = "position: absolute; left: 10.7432222px; top: 10.432325px; height: 30px; width: 201px;"; - - offset = $( div ).offset( function( _, offset ) { - return offset; - }).offset(); - - testElement.innerHTML = ""; - testElementParent.removeChild( testElement ); - - offsetTotal = offset.top + offset.left + ( body ? 2000 : 0 ); - support.fractions = offsetTotal > 21 && offsetTotal < 22; -})(); - -}( jQuery )); -/*! - * jQuery UI Draggable 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Draggables - * - * Depends: - * jquery.ui.core.js - * jquery.ui.mouse.js - * jquery.ui.widget.js - */ -(function( $, undefined ) { - -$.widget("ui.draggable", $.ui.mouse, { - widgetEventPrefix: "drag", - options: { - addClasses: true, - appendTo: "parent", - axis: false, - connectToSortable: false, - containment: false, - cursor: "auto", - cursorAt: false, - grid: false, - handle: false, - helper: "original", - iframeFix: false, - opacity: false, - refreshPositions: false, - revert: false, - revertDuration: 500, - scope: "default", - scroll: true, - scrollSensitivity: 20, - scrollSpeed: 20, - snap: false, - snapMode: "both", - snapTolerance: 20, - stack: false, - zIndex: false - }, - _create: function() { - - if (this.options.helper == 'original' && !(/^(?:r|a|f)/).test(this.element.css("position"))) - this.element[0].style.position = 'relative'; - - (this.options.addClasses && this.element.addClass("ui-draggable")); - (this.options.disabled && this.element.addClass("ui-draggable-disabled")); - - this._mouseInit(); - - }, - - destroy: function() { - if(!this.element.data('draggable')) return; - this.element - .removeData("draggable") - .unbind(".draggable") - .removeClass("ui-draggable" - + " ui-draggable-dragging" - + " ui-draggable-disabled"); - this._mouseDestroy(); - - return this; - }, - - _mouseCapture: function(event) { - - var o = this.options; - - // among others, prevent a drag on a resizable-handle - if (this.helper || o.disabled || $(event.target).is('.ui-resizable-handle')) - return false; - - //Quit if we're not on a valid handle - this.handle = this._getHandle(event); - if (!this.handle) - return false; - - if ( o.iframeFix ) { - $(o.iframeFix === true ? "iframe" : o.iframeFix).each(function() { - $('
    ') - .css({ - width: this.offsetWidth+"px", height: this.offsetHeight+"px", - position: "absolute", opacity: "0.001", zIndex: 1000 - }) - .css($(this).offset()) - .appendTo("body"); - }); - } - - return true; - - }, - - _mouseStart: function(event) { - - var o = this.options; - - //Create and append the visible helper - this.helper = this._createHelper(event); - - this.helper.addClass("ui-draggable-dragging"); - - //Cache the helper size - this._cacheHelperProportions(); - - //If ddmanager is used for droppables, set the global draggable - if($.ui.ddmanager) - $.ui.ddmanager.current = this; - - /* - * - Position generation - - * This block generates everything position related - it's the core of draggables. - */ - - //Cache the margins of the original element - this._cacheMargins(); - - //Store the helper's css position - this.cssPosition = this.helper.css("position"); - this.scrollParent = this.helper.scrollParent(); - - //The element's absolute position on the page minus margins - this.offset = this.positionAbs = this.element.offset(); - this.offset = { - top: this.offset.top - this.margins.top, - left: this.offset.left - this.margins.left - }; - - $.extend(this.offset, { - click: { //Where the click happened, relative to the element - left: event.pageX - this.offset.left, - top: event.pageY - this.offset.top - }, - parent: this._getParentOffset(), - relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper - }); - - //Generate the original position - this.originalPosition = this.position = this._generatePosition(event); - this.originalPageX = event.pageX; - this.originalPageY = event.pageY; - - //Adjust the mouse offset relative to the helper if 'cursorAt' is supplied - (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt)); - - //Set a containment if given in the options - if(o.containment) - this._setContainment(); - - //Trigger event + callbacks - if(this._trigger("start", event) === false) { - this._clear(); - return false; - } - - //Recache the helper size - this._cacheHelperProportions(); - - //Prepare the droppable offsets - if ($.ui.ddmanager && !o.dropBehaviour) - $.ui.ddmanager.prepareOffsets(this, event); - - - this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position - - //If the ddmanager is used for droppables, inform the manager that dragging has started (see #5003) - if ( $.ui.ddmanager ) $.ui.ddmanager.dragStart(this, event); - - return true; - }, - - _mouseDrag: function(event, noPropagation) { - - //Compute the helpers position - this.position = this._generatePosition(event); - this.positionAbs = this._convertPositionTo("absolute"); - - //Call plugins and callbacks and use the resulting position if something is returned - if (!noPropagation) { - var ui = this._uiHash(); - if(this._trigger('drag', event, ui) === false) { - this._mouseUp({}); - return false; - } - this.position = ui.position; - } - - if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px'; - if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px'; - if($.ui.ddmanager) $.ui.ddmanager.drag(this, event); - - return false; - }, - - _mouseStop: function(event) { - - //If we are using droppables, inform the manager about the drop - var dropped = false; - if ($.ui.ddmanager && !this.options.dropBehaviour) - dropped = $.ui.ddmanager.drop(this, event); - - //if a drop comes from outside (a sortable) - if(this.dropped) { - dropped = this.dropped; - this.dropped = false; - } - - //if the original element is no longer in the DOM don't bother to continue (see #8269) - var element = this.element[0], elementInDom = false; - while ( element && (element = element.parentNode) ) { - if (element == document ) { - elementInDom = true; - } - } - if ( !elementInDom && this.options.helper === "original" ) - return false; - - if((this.options.revert == "invalid" && !dropped) || (this.options.revert == "valid" && dropped) || this.options.revert === true || ($.isFunction(this.options.revert) && this.options.revert.call(this.element, dropped))) { - var self = this; - $(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() { - if(self._trigger("stop", event) !== false) { - self._clear(); - } - }); - } else { - if(this._trigger("stop", event) !== false) { - this._clear(); - } - } - - return false; - }, - - _mouseUp: function(event) { - if (this.options.iframeFix === true) { - $("div.ui-draggable-iframeFix").each(function() { - this.parentNode.removeChild(this); - }); //Remove frame helpers - } - - //If the ddmanager is used for droppables, inform the manager that dragging has stopped (see #5003) - if( $.ui.ddmanager ) $.ui.ddmanager.dragStop(this, event); - - return $.ui.mouse.prototype._mouseUp.call(this, event); - }, - - cancel: function() { - - if(this.helper.is(".ui-draggable-dragging")) { - this._mouseUp({}); - } else { - this._clear(); - } - - return this; - - }, - - _getHandle: function(event) { - - var handle = !this.options.handle || !$(this.options.handle, this.element).length ? true : false; - $(this.options.handle, this.element) - .find("*") - .andSelf() - .each(function() { - if(this == event.target) handle = true; - }); - - return handle; - - }, - - _createHelper: function(event) { - - var o = this.options; - var helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper == 'clone' ? this.element.clone().removeAttr('id') : this.element); - - if(!helper.parents('body').length) - helper.appendTo((o.appendTo == 'parent' ? this.element[0].parentNode : o.appendTo)); - - if(helper[0] != this.element[0] && !(/(fixed|absolute)/).test(helper.css("position"))) - helper.css("position", "absolute"); - - return helper; - - }, - - _adjustOffsetFromHelper: function(obj) { - if (typeof obj == 'string') { - obj = obj.split(' '); - } - if ($.isArray(obj)) { - obj = {left: +obj[0], top: +obj[1] || 0}; - } - if ('left' in obj) { - this.offset.click.left = obj.left + this.margins.left; - } - if ('right' in obj) { - this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left; - } - if ('top' in obj) { - this.offset.click.top = obj.top + this.margins.top; - } - if ('bottom' in obj) { - this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top; - } - }, - - _getParentOffset: function() { - - //Get the offsetParent and cache its position - this.offsetParent = this.helper.offsetParent(); - var po = this.offsetParent.offset(); - - // This is a special case where we need to modify a offset calculated on start, since the following happened: - // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent - // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that - // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag - if(this.cssPosition == 'absolute' && this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) { - po.left += this.scrollParent.scrollLeft(); - po.top += this.scrollParent.scrollTop(); - } - - if((this.offsetParent[0] == document.body) //This needs to be actually done for all browsers, since pageX/pageY includes this information - || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() == 'html' && $.browser.msie)) //Ugly IE fix - po = { top: 0, left: 0 }; - - return { - top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0), - left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0) - }; - - }, - - _getRelativeOffset: function() { - - if(this.cssPosition == "relative") { - var p = this.element.position(); - return { - top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(), - left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft() - }; - } else { - return { top: 0, left: 0 }; - } - - }, - - _cacheMargins: function() { - this.margins = { - left: (parseInt(this.element.css("marginLeft"),10) || 0), - top: (parseInt(this.element.css("marginTop"),10) || 0), - right: (parseInt(this.element.css("marginRight"),10) || 0), - bottom: (parseInt(this.element.css("marginBottom"),10) || 0) - }; - }, - - _cacheHelperProportions: function() { - this.helperProportions = { - width: this.helper.outerWidth(), - height: this.helper.outerHeight() - }; - }, - - _setContainment: function() { - - var o = this.options; - if(o.containment == 'parent') o.containment = this.helper[0].parentNode; - if(o.containment == 'document' || o.containment == 'window') this.containment = [ - o.containment == 'document' ? 0 : $(window).scrollLeft() - this.offset.relative.left - this.offset.parent.left, - o.containment == 'document' ? 0 : $(window).scrollTop() - this.offset.relative.top - this.offset.parent.top, - (o.containment == 'document' ? 0 : $(window).scrollLeft()) + $(o.containment == 'document' ? document : window).width() - this.helperProportions.width - this.margins.left, - (o.containment == 'document' ? 0 : $(window).scrollTop()) + ($(o.containment == 'document' ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top - ]; - - if(!(/^(document|window|parent)$/).test(o.containment) && o.containment.constructor != Array) { - var c = $(o.containment); - var ce = c[0]; if(!ce) return; - var co = c.offset(); - var over = ($(ce).css("overflow") != 'hidden'); - - this.containment = [ - (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0), - (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0), - (over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left - this.margins.right, - (over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),10) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top - this.margins.bottom - ]; - this.relative_container = c; - - } else if(o.containment.constructor == Array) { - this.containment = o.containment; - } - - }, - - _convertPositionTo: function(d, pos) { - - if(!pos) pos = this.position; - var mod = d == "absolute" ? 1 : -1; - var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName); - - return { - top: ( - pos.top // The absolute mouse position - + this.offset.relative.top * mod // Only for relative positioned nodes: Relative offset from element to offset parent - + this.offset.parent.top * mod // The offsetParent's offset without borders (offset + border) - - ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod) - ), - left: ( - pos.left // The absolute mouse position - + this.offset.relative.left * mod // Only for relative positioned nodes: Relative offset from element to offset parent - + this.offset.parent.left * mod // The offsetParent's offset without borders (offset + border) - - ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod) - ) - }; - - }, - - _generatePosition: function(event) { - - var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName); - var pageX = event.pageX; - var pageY = event.pageY; - - /* - * - Position constraining - - * Constrain the position to a mix of grid, containment. - */ - - if(this.originalPosition) { //If we are not dragging yet, we won't check for options - var containment; - if(this.containment) { - if (this.relative_container){ - var co = this.relative_container.offset(); - containment = [ this.containment[0] + co.left, - this.containment[1] + co.top, - this.containment[2] + co.left, - this.containment[3] + co.top ]; - } - else { - containment = this.containment; - } - - if(event.pageX - this.offset.click.left < containment[0]) pageX = containment[0] + this.offset.click.left; - if(event.pageY - this.offset.click.top < containment[1]) pageY = containment[1] + this.offset.click.top; - if(event.pageX - this.offset.click.left > containment[2]) pageX = containment[2] + this.offset.click.left; - if(event.pageY - this.offset.click.top > containment[3]) pageY = containment[3] + this.offset.click.top; - } - - if(o.grid) { - //Check for grid elements set to 0 to prevent divide by 0 error causing invalid argument errors in IE (see ticket #6950) - var top = o.grid[1] ? this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1] : this.originalPageY; - pageY = containment ? (!(top - this.offset.click.top < containment[1] || top - this.offset.click.top > containment[3]) ? top : (!(top - this.offset.click.top < containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top; - - var left = o.grid[0] ? this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0] : this.originalPageX; - pageX = containment ? (!(left - this.offset.click.left < containment[0] || left - this.offset.click.left > containment[2]) ? left : (!(left - this.offset.click.left < containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left; - } - - } - - return { - top: ( - pageY // The absolute mouse position - - this.offset.click.top // Click offset (relative to the element) - - this.offset.relative.top // Only for relative positioned nodes: Relative offset from element to offset parent - - this.offset.parent.top // The offsetParent's offset without borders (offset + border) - + ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) )) - ), - left: ( - pageX // The absolute mouse position - - this.offset.click.left // Click offset (relative to the element) - - this.offset.relative.left // Only for relative positioned nodes: Relative offset from element to offset parent - - this.offset.parent.left // The offsetParent's offset without borders (offset + border) - + ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() )) - ) - }; - - }, - - _clear: function() { - this.helper.removeClass("ui-draggable-dragging"); - if(this.helper[0] != this.element[0] && !this.cancelHelperRemoval) this.helper.remove(); - //if($.ui.ddmanager) $.ui.ddmanager.current = null; - this.helper = null; - this.cancelHelperRemoval = false; - }, - - // From now on bulk stuff - mainly helpers - - _trigger: function(type, event, ui) { - ui = ui || this._uiHash(); - $.ui.plugin.call(this, type, [event, ui]); - if(type == "drag") this.positionAbs = this._convertPositionTo("absolute"); //The absolute position has to be recalculated after plugins - return $.Widget.prototype._trigger.call(this, type, event, ui); - }, - - plugins: {}, - - _uiHash: function(event) { - return { - helper: this.helper, - position: this.position, - originalPosition: this.originalPosition, - offset: this.positionAbs - }; - } - -}); - -$.extend($.ui.draggable, { - version: "1.8.23" -}); - -$.ui.plugin.add("draggable", "connectToSortable", { - start: function(event, ui) { - - var inst = $(this).data("draggable"), o = inst.options, - uiSortable = $.extend({}, ui, { item: inst.element }); - inst.sortables = []; - $(o.connectToSortable).each(function() { - var sortable = $.data(this, 'sortable'); - if (sortable && !sortable.options.disabled) { - inst.sortables.push({ - instance: sortable, - shouldRevert: sortable.options.revert - }); - sortable.refreshPositions(); // Call the sortable's refreshPositions at drag start to refresh the containerCache since the sortable container cache is used in drag and needs to be up to date (this will ensure it's initialised as well as being kept in step with any changes that might have happened on the page). - sortable._trigger("activate", event, uiSortable); - } - }); - - }, - stop: function(event, ui) { - - //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper - var inst = $(this).data("draggable"), - uiSortable = $.extend({}, ui, { item: inst.element }); - - $.each(inst.sortables, function() { - if(this.instance.isOver) { - - this.instance.isOver = 0; - - inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance - this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work) - - //The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: 'valid/invalid' - if(this.shouldRevert) this.instance.options.revert = true; - - //Trigger the stop of the sortable - this.instance._mouseStop(event); - - this.instance.options.helper = this.instance.options._helper; - - //If the helper has been the original item, restore properties in the sortable - if(inst.options.helper == 'original') - this.instance.currentItem.css({ top: 'auto', left: 'auto' }); - - } else { - this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance - this.instance._trigger("deactivate", event, uiSortable); - } - - }); - - }, - drag: function(event, ui) { - - var inst = $(this).data("draggable"), self = this; - - var checkPos = function(o) { - var dyClick = this.offset.click.top, dxClick = this.offset.click.left; - var helperTop = this.positionAbs.top, helperLeft = this.positionAbs.left; - var itemHeight = o.height, itemWidth = o.width; - var itemTop = o.top, itemLeft = o.left; - - return $.ui.isOver(helperTop + dyClick, helperLeft + dxClick, itemTop, itemLeft, itemHeight, itemWidth); - }; - - $.each(inst.sortables, function(i) { - - //Copy over some variables to allow calling the sortable's native _intersectsWith - this.instance.positionAbs = inst.positionAbs; - this.instance.helperProportions = inst.helperProportions; - this.instance.offset.click = inst.offset.click; - - if(this.instance._intersectsWith(this.instance.containerCache)) { - - //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once - if(!this.instance.isOver) { - - this.instance.isOver = 1; - //Now we fake the start of dragging for the sortable instance, - //by cloning the list group item, appending it to the sortable and using it as inst.currentItem - //We can then fire the start event of the sortable with our passed browser event, and our own helper (so it doesn't create a new one) - this.instance.currentItem = $(self).clone().removeAttr('id').appendTo(this.instance.element).data("sortable-item", true); - this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it - this.instance.options.helper = function() { return ui.helper[0]; }; - - event.target = this.instance.currentItem[0]; - this.instance._mouseCapture(event, true); - this.instance._mouseStart(event, true, true); - - //Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes - this.instance.offset.click.top = inst.offset.click.top; - this.instance.offset.click.left = inst.offset.click.left; - this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left; - this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top; - - inst._trigger("toSortable", event); - inst.dropped = this.instance.element; //draggable revert needs that - //hack so receive/update callbacks work (mostly) - inst.currentItem = inst.element; - this.instance.fromOutside = inst; - - } - - //Provided we did all the previous steps, we can fire the drag event of the sortable on every draggable drag, when it intersects with the sortable - if(this.instance.currentItem) this.instance._mouseDrag(event); - - } else { - - //If it doesn't intersect with the sortable, and it intersected before, - //we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval - if(this.instance.isOver) { - - this.instance.isOver = 0; - this.instance.cancelHelperRemoval = true; - - //Prevent reverting on this forced stop - this.instance.options.revert = false; - - // The out event needs to be triggered independently - this.instance._trigger('out', event, this.instance._uiHash(this.instance)); - - this.instance._mouseStop(event, true); - this.instance.options.helper = this.instance.options._helper; - - //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size - this.instance.currentItem.remove(); - if(this.instance.placeholder) this.instance.placeholder.remove(); - - inst._trigger("fromSortable", event); - inst.dropped = false; //draggable revert needs that - } - - }; - - }); - - } -}); - -$.ui.plugin.add("draggable", "cursor", { - start: function(event, ui) { - var t = $('body'), o = $(this).data('draggable').options; - if (t.css("cursor")) o._cursor = t.css("cursor"); - t.css("cursor", o.cursor); - }, - stop: function(event, ui) { - var o = $(this).data('draggable').options; - if (o._cursor) $('body').css("cursor", o._cursor); - } -}); - -$.ui.plugin.add("draggable", "opacity", { - start: function(event, ui) { - var t = $(ui.helper), o = $(this).data('draggable').options; - if(t.css("opacity")) o._opacity = t.css("opacity"); - t.css('opacity', o.opacity); - }, - stop: function(event, ui) { - var o = $(this).data('draggable').options; - if(o._opacity) $(ui.helper).css('opacity', o._opacity); - } -}); - -$.ui.plugin.add("draggable", "scroll", { - start: function(event, ui) { - var i = $(this).data("draggable"); - if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') i.overflowOffset = i.scrollParent.offset(); - }, - drag: function(event, ui) { - - var i = $(this).data("draggable"), o = i.options, scrolled = false; - - if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') { - - if(!o.axis || o.axis != 'x') { - if((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) - i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed; - else if(event.pageY - i.overflowOffset.top < o.scrollSensitivity) - i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed; - } - - if(!o.axis || o.axis != 'y') { - if((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) - i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed; - else if(event.pageX - i.overflowOffset.left < o.scrollSensitivity) - i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed; - } - - } else { - - if(!o.axis || o.axis != 'x') { - if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) - scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed); - else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) - scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed); - } - - if(!o.axis || o.axis != 'y') { - if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity) - scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed); - else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) - scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed); - } - - } - - if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) - $.ui.ddmanager.prepareOffsets(i, event); - - } -}); - -$.ui.plugin.add("draggable", "snap", { - start: function(event, ui) { - - var i = $(this).data("draggable"), o = i.options; - i.snapElements = []; - - $(o.snap.constructor != String ? ( o.snap.items || ':data(draggable)' ) : o.snap).each(function() { - var $t = $(this); var $o = $t.offset(); - if(this != i.element[0]) i.snapElements.push({ - item: this, - width: $t.outerWidth(), height: $t.outerHeight(), - top: $o.top, left: $o.left - }); - }); - - }, - drag: function(event, ui) { - - var inst = $(this).data("draggable"), o = inst.options; - var d = o.snapTolerance; - - var x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width, - y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height; - - for (var i = inst.snapElements.length - 1; i >= 0; i--){ - - var l = inst.snapElements[i].left, r = l + inst.snapElements[i].width, - t = inst.snapElements[i].top, b = t + inst.snapElements[i].height; - - //Yes, I know, this is insane ;) - if(!((l-d < x1 && x1 < r+d && t-d < y1 && y1 < b+d) || (l-d < x1 && x1 < r+d && t-d < y2 && y2 < b+d) || (l-d < x2 && x2 < r+d && t-d < y1 && y1 < b+d) || (l-d < x2 && x2 < r+d && t-d < y2 && y2 < b+d))) { - if(inst.snapElements[i].snapping) (inst.options.snap.release && inst.options.snap.release.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item }))); - inst.snapElements[i].snapping = false; - continue; - } - - if(o.snapMode != 'inner') { - var ts = Math.abs(t - y2) <= d; - var bs = Math.abs(b - y1) <= d; - var ls = Math.abs(l - x2) <= d; - var rs = Math.abs(r - x1) <= d; - if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top; - if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top; - if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left; - if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left; - } - - var first = (ts || bs || ls || rs); - - if(o.snapMode != 'outer') { - var ts = Math.abs(t - y1) <= d; - var bs = Math.abs(b - y2) <= d; - var ls = Math.abs(l - x1) <= d; - var rs = Math.abs(r - x2) <= d; - if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top - inst.margins.top; - if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top - inst.margins.top; - if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left - inst.margins.left; - if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left - inst.margins.left; - } - - if(!inst.snapElements[i].snapping && (ts || bs || ls || rs || first)) - (inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item }))); - inst.snapElements[i].snapping = (ts || bs || ls || rs || first); - - }; - - } -}); - -$.ui.plugin.add("draggable", "stack", { - start: function(event, ui) { - - var o = $(this).data("draggable").options; - - var group = $.makeArray($(o.stack)).sort(function(a,b) { - return (parseInt($(a).css("zIndex"),10) || 0) - (parseInt($(b).css("zIndex"),10) || 0); - }); - if (!group.length) { return; } - - var min = parseInt(group[0].style.zIndex) || 0; - $(group).each(function(i) { - this.style.zIndex = min + i; - }); - - this[0].style.zIndex = min + group.length; - - } -}); - -$.ui.plugin.add("draggable", "zIndex", { - start: function(event, ui) { - var t = $(ui.helper), o = $(this).data("draggable").options; - if(t.css("zIndex")) o._zIndex = t.css("zIndex"); - t.css('zIndex', o.zIndex); - }, - stop: function(event, ui) { - var o = $(this).data("draggable").options; - if(o._zIndex) $(ui.helper).css('zIndex', o._zIndex); - } -}); - -})(jQuery); -/*! - * jQuery UI Droppable 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Droppables - * - * Depends: - * jquery.ui.core.js - * jquery.ui.widget.js - * jquery.ui.mouse.js - * jquery.ui.draggable.js - */ -(function( $, undefined ) { - -$.widget("ui.droppable", { - widgetEventPrefix: "drop", - options: { - accept: '*', - activeClass: false, - addClasses: true, - greedy: false, - hoverClass: false, - scope: 'default', - tolerance: 'intersect' - }, - _create: function() { - - var o = this.options, accept = o.accept; - this.isover = 0; this.isout = 1; - - this.accept = $.isFunction(accept) ? accept : function(d) { - return d.is(accept); - }; - - //Store the droppable's proportions - this.proportions = { width: this.element[0].offsetWidth, height: this.element[0].offsetHeight }; - - // Add the reference and positions to the manager - $.ui.ddmanager.droppables[o.scope] = $.ui.ddmanager.droppables[o.scope] || []; - $.ui.ddmanager.droppables[o.scope].push(this); - - (o.addClasses && this.element.addClass("ui-droppable")); - - }, - - destroy: function() { - var drop = $.ui.ddmanager.droppables[this.options.scope]; - for ( var i = 0; i < drop.length; i++ ) - if ( drop[i] == this ) - drop.splice(i, 1); - - this.element - .removeClass("ui-droppable ui-droppable-disabled") - .removeData("droppable") - .unbind(".droppable"); - - return this; - }, - - _setOption: function(key, value) { - - if(key == 'accept') { - this.accept = $.isFunction(value) ? value : function(d) { - return d.is(value); - }; - } - $.Widget.prototype._setOption.apply(this, arguments); - }, - - _activate: function(event) { - var draggable = $.ui.ddmanager.current; - if(this.options.activeClass) this.element.addClass(this.options.activeClass); - (draggable && this._trigger('activate', event, this.ui(draggable))); - }, - - _deactivate: function(event) { - var draggable = $.ui.ddmanager.current; - if(this.options.activeClass) this.element.removeClass(this.options.activeClass); - (draggable && this._trigger('deactivate', event, this.ui(draggable))); - }, - - _over: function(event) { - - var draggable = $.ui.ddmanager.current; - if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return; // Bail if draggable and droppable are same element - - if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) { - if(this.options.hoverClass) this.element.addClass(this.options.hoverClass); - this._trigger('over', event, this.ui(draggable)); - } - - }, - - _out: function(event) { - - var draggable = $.ui.ddmanager.current; - if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return; // Bail if draggable and droppable are same element - - if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) { - if(this.options.hoverClass) this.element.removeClass(this.options.hoverClass); - this._trigger('out', event, this.ui(draggable)); - } - - }, - - _drop: function(event,custom) { - - var draggable = custom || $.ui.ddmanager.current; - if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return false; // Bail if draggable and droppable are same element - - var childrenIntersection = false; - this.element.find(":data(droppable)").not(".ui-draggable-dragging").each(function() { - var inst = $.data(this, 'droppable'); - if( - inst.options.greedy - && !inst.options.disabled - && inst.options.scope == draggable.options.scope - && inst.accept.call(inst.element[0], (draggable.currentItem || draggable.element)) - && $.ui.intersect(draggable, $.extend(inst, { offset: inst.element.offset() }), inst.options.tolerance) - ) { childrenIntersection = true; return false; } - }); - if(childrenIntersection) return false; - - if(this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) { - if(this.options.activeClass) this.element.removeClass(this.options.activeClass); - if(this.options.hoverClass) this.element.removeClass(this.options.hoverClass); - this._trigger('drop', event, this.ui(draggable)); - return this.element; - } - - return false; - - }, - - ui: function(c) { - return { - draggable: (c.currentItem || c.element), - helper: c.helper, - position: c.position, - offset: c.positionAbs - }; - } - -}); - -$.extend($.ui.droppable, { - version: "1.8.23" -}); - -$.ui.intersect = function(draggable, droppable, toleranceMode) { - - if (!droppable.offset) return false; - - var x1 = (draggable.positionAbs || draggable.position.absolute).left, x2 = x1 + draggable.helperProportions.width, - y1 = (draggable.positionAbs || draggable.position.absolute).top, y2 = y1 + draggable.helperProportions.height; - var l = droppable.offset.left, r = l + droppable.proportions.width, - t = droppable.offset.top, b = t + droppable.proportions.height; - - switch (toleranceMode) { - case 'fit': - return (l <= x1 && x2 <= r - && t <= y1 && y2 <= b); - break; - case 'intersect': - return (l < x1 + (draggable.helperProportions.width / 2) // Right Half - && x2 - (draggable.helperProportions.width / 2) < r // Left Half - && t < y1 + (draggable.helperProportions.height / 2) // Bottom Half - && y2 - (draggable.helperProportions.height / 2) < b ); // Top Half - break; - case 'pointer': - var draggableLeft = ((draggable.positionAbs || draggable.position.absolute).left + (draggable.clickOffset || draggable.offset.click).left), - draggableTop = ((draggable.positionAbs || draggable.position.absolute).top + (draggable.clickOffset || draggable.offset.click).top), - isOver = $.ui.isOver(draggableTop, draggableLeft, t, l, droppable.proportions.height, droppable.proportions.width); - return isOver; - break; - case 'touch': - return ( - (y1 >= t && y1 <= b) || // Top edge touching - (y2 >= t && y2 <= b) || // Bottom edge touching - (y1 < t && y2 > b) // Surrounded vertically - ) && ( - (x1 >= l && x1 <= r) || // Left edge touching - (x2 >= l && x2 <= r) || // Right edge touching - (x1 < l && x2 > r) // Surrounded horizontally - ); - break; - default: - return false; - break; - } - -}; - -/* - This manager tracks offsets of draggables and droppables -*/ -$.ui.ddmanager = { - current: null, - droppables: { 'default': [] }, - prepareOffsets: function(t, event) { - - var m = $.ui.ddmanager.droppables[t.options.scope] || []; - var type = event ? event.type : null; // workaround for #2317 - var list = (t.currentItem || t.element).find(":data(droppable)").andSelf(); - - droppablesLoop: for (var i = 0; i < m.length; i++) { - - if(m[i].options.disabled || (t && !m[i].accept.call(m[i].element[0],(t.currentItem || t.element)))) continue; //No disabled and non-accepted - for (var j=0; j < list.length; j++) { if(list[j] == m[i].element[0]) { m[i].proportions.height = 0; continue droppablesLoop; } }; //Filter out elements in the current dragged item - m[i].visible = m[i].element.css("display") != "none"; if(!m[i].visible) continue; //If the element is not visible, continue - - if(type == "mousedown") m[i]._activate.call(m[i], event); //Activate the droppable if used directly from draggables - - m[i].offset = m[i].element.offset(); - m[i].proportions = { width: m[i].element[0].offsetWidth, height: m[i].element[0].offsetHeight }; - - } - - }, - drop: function(draggable, event) { - - var dropped = false; - $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() { - - if(!this.options) return; - if (!this.options.disabled && this.visible && $.ui.intersect(draggable, this, this.options.tolerance)) - dropped = this._drop.call(this, event) || dropped; - - if (!this.options.disabled && this.visible && this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) { - this.isout = 1; this.isover = 0; - this._deactivate.call(this, event); - } - - }); - return dropped; - - }, - dragStart: function( draggable, event ) { - //Listen for scrolling so that if the dragging causes scrolling the position of the droppables can be recalculated (see #5003) - draggable.element.parents( ":not(body,html)" ).bind( "scroll.droppable", function() { - if( !draggable.options.refreshPositions ) $.ui.ddmanager.prepareOffsets( draggable, event ); - }); - }, - drag: function(draggable, event) { - - //If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse. - if(draggable.options.refreshPositions) $.ui.ddmanager.prepareOffsets(draggable, event); - - //Run through all droppables and check their positions based on specific tolerance options - $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() { - - if(this.options.disabled || this.greedyChild || !this.visible) return; - var intersects = $.ui.intersect(draggable, this, this.options.tolerance); - - var c = !intersects && this.isover == 1 ? 'isout' : (intersects && this.isover == 0 ? 'isover' : null); - if(!c) return; - - var parentInstance; - if (this.options.greedy) { - var parent = this.element.parents(':data(droppable):eq(0)'); - if (parent.length) { - parentInstance = $.data(parent[0], 'droppable'); - parentInstance.greedyChild = (c == 'isover' ? 1 : 0); - } - } - - // we just moved into a greedy child - if (parentInstance && c == 'isover') { - parentInstance['isover'] = 0; - parentInstance['isout'] = 1; - parentInstance._out.call(parentInstance, event); - } - - this[c] = 1; this[c == 'isout' ? 'isover' : 'isout'] = 0; - this[c == "isover" ? "_over" : "_out"].call(this, event); - - // we just moved out of a greedy child - if (parentInstance && c == 'isout') { - parentInstance['isout'] = 0; - parentInstance['isover'] = 1; - parentInstance._over.call(parentInstance, event); - } - }); - - }, - dragStop: function( draggable, event ) { - draggable.element.parents( ":not(body,html)" ).unbind( "scroll.droppable" ); - //Call prepareOffsets one final time since IE does not fire return scroll events when overflow was caused by drag (see #5003) - if( !draggable.options.refreshPositions ) $.ui.ddmanager.prepareOffsets( draggable, event ); - } -}; - -})(jQuery); -/*! - * jQuery UI Resizable 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Resizables - * - * Depends: - * jquery.ui.core.js - * jquery.ui.mouse.js - * jquery.ui.widget.js - */ -(function( $, undefined ) { - -$.widget("ui.resizable", $.ui.mouse, { - widgetEventPrefix: "resize", - options: { - alsoResize: false, - animate: false, - animateDuration: "slow", - animateEasing: "swing", - aspectRatio: false, - autoHide: false, - containment: false, - ghost: false, - grid: false, - handles: "e,s,se", - helper: false, - maxHeight: null, - maxWidth: null, - minHeight: 10, - minWidth: 10, - zIndex: 1000 - }, - _create: function() { - - var self = this, o = this.options; - this.element.addClass("ui-resizable"); - - $.extend(this, { - _aspectRatio: !!(o.aspectRatio), - aspectRatio: o.aspectRatio, - originalElement: this.element, - _proportionallyResizeElements: [], - _helper: o.helper || o.ghost || o.animate ? o.helper || 'ui-resizable-helper' : null - }); - - //Wrap the element if it cannot hold child nodes - if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)) { - - //Create a wrapper element and set the wrapper to the new current internal element - this.element.wrap( - $('
    ').css({ - position: this.element.css('position'), - width: this.element.outerWidth(), - height: this.element.outerHeight(), - top: this.element.css('top'), - left: this.element.css('left') - }) - ); - - //Overwrite the original this.element - this.element = this.element.parent().data( - "resizable", this.element.data('resizable') - ); - - this.elementIsWrapper = true; - - //Move margins to the wrapper - this.element.css({ marginLeft: this.originalElement.css("marginLeft"), marginTop: this.originalElement.css("marginTop"), marginRight: this.originalElement.css("marginRight"), marginBottom: this.originalElement.css("marginBottom") }); - this.originalElement.css({ marginLeft: 0, marginTop: 0, marginRight: 0, marginBottom: 0}); - - //Prevent Safari textarea resize - this.originalResizeStyle = this.originalElement.css('resize'); - this.originalElement.css('resize', 'none'); - - //Push the actual element to our proportionallyResize internal array - this._proportionallyResizeElements.push(this.originalElement.css({ position: 'static', zoom: 1, display: 'block' })); - - // avoid IE jump (hard set the margin) - this.originalElement.css({ margin: this.originalElement.css('margin') }); - - // fix handlers offset - this._proportionallyResize(); - - } - - this.handles = o.handles || (!$('.ui-resizable-handle', this.element).length ? "e,s,se" : { n: '.ui-resizable-n', e: '.ui-resizable-e', s: '.ui-resizable-s', w: '.ui-resizable-w', se: '.ui-resizable-se', sw: '.ui-resizable-sw', ne: '.ui-resizable-ne', nw: '.ui-resizable-nw' }); - if(this.handles.constructor == String) { - - if(this.handles == 'all') this.handles = 'n,e,s,w,se,sw,ne,nw'; - var n = this.handles.split(","); this.handles = {}; - - for(var i = 0; i < n.length; i++) { - - var handle = $.trim(n[i]), hname = 'ui-resizable-'+handle; - var axis = $('
    '); - - // Apply zIndex to all handles - see #7960 - axis.css({ zIndex: o.zIndex }); - - //TODO : What's going on here? - if ('se' == handle) { - axis.addClass('ui-icon ui-icon-gripsmall-diagonal-se'); - }; - - //Insert into internal handles object and append to element - this.handles[handle] = '.ui-resizable-'+handle; - this.element.append(axis); - } - - } - - this._renderAxis = function(target) { - - target = target || this.element; - - for(var i in this.handles) { - - if(this.handles[i].constructor == String) - this.handles[i] = $(this.handles[i], this.element).show(); - - //Apply pad to wrapper element, needed to fix axis position (textarea, inputs, scrolls) - if (this.elementIsWrapper && this.originalElement[0].nodeName.match(/textarea|input|select|button/i)) { - - var axis = $(this.handles[i], this.element), padWrapper = 0; - - //Checking the correct pad and border - padWrapper = /sw|ne|nw|se|n|s/.test(i) ? axis.outerHeight() : axis.outerWidth(); - - //The padding type i have to apply... - var padPos = [ 'padding', - /ne|nw|n/.test(i) ? 'Top' : - /se|sw|s/.test(i) ? 'Bottom' : - /^e$/.test(i) ? 'Right' : 'Left' ].join(""); - - target.css(padPos, padWrapper); - - this._proportionallyResize(); - - } - - //TODO: What's that good for? There's not anything to be executed left - if(!$(this.handles[i]).length) - continue; - - } - }; - - //TODO: make renderAxis a prototype function - this._renderAxis(this.element); - - this._handles = $('.ui-resizable-handle', this.element) - .disableSelection(); - - //Matching axis name - this._handles.mouseover(function() { - if (!self.resizing) { - if (this.className) - var axis = this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i); - //Axis, default = se - self.axis = axis && axis[1] ? axis[1] : 'se'; - } - }); - - //If we want to auto hide the elements - if (o.autoHide) { - this._handles.hide(); - $(this.element) - .addClass("ui-resizable-autohide") - .hover(function() { - if (o.disabled) return; - $(this).removeClass("ui-resizable-autohide"); - self._handles.show(); - }, - function(){ - if (o.disabled) return; - if (!self.resizing) { - $(this).addClass("ui-resizable-autohide"); - self._handles.hide(); - } - }); - } - - //Initialize the mouse interaction - this._mouseInit(); - - }, - - destroy: function() { - - this._mouseDestroy(); - - var _destroy = function(exp) { - $(exp).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing") - .removeData("resizable").unbind(".resizable").find('.ui-resizable-handle').remove(); - }; - - //TODO: Unwrap at same DOM position - if (this.elementIsWrapper) { - _destroy(this.element); - var wrapper = this.element; - wrapper.after( - this.originalElement.css({ - position: wrapper.css('position'), - width: wrapper.outerWidth(), - height: wrapper.outerHeight(), - top: wrapper.css('top'), - left: wrapper.css('left') - }) - ).remove(); - } - - this.originalElement.css('resize', this.originalResizeStyle); - _destroy(this.originalElement); - - return this; - }, - - _mouseCapture: function(event) { - var handle = false; - for (var i in this.handles) { - if ($(this.handles[i])[0] == event.target) { - handle = true; - } - } - - return !this.options.disabled && handle; - }, - - _mouseStart: function(event) { - - var o = this.options, iniPos = this.element.position(), el = this.element; - - this.resizing = true; - this.documentScroll = { top: $(document).scrollTop(), left: $(document).scrollLeft() }; - - // bugfix for http://dev.jquery.com/ticket/1749 - if (el.is('.ui-draggable') || (/absolute/).test(el.css('position'))) { - el.css({ position: 'absolute', top: iniPos.top, left: iniPos.left }); - } - - this._renderProxy(); - - var curleft = num(this.helper.css('left')), curtop = num(this.helper.css('top')); - - if (o.containment) { - curleft += $(o.containment).scrollLeft() || 0; - curtop += $(o.containment).scrollTop() || 0; - } - - //Store needed variables - this.offset = this.helper.offset(); - this.position = { left: curleft, top: curtop }; - this.size = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() }; - this.originalSize = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() }; - this.originalPosition = { left: curleft, top: curtop }; - this.sizeDiff = { width: el.outerWidth() - el.width(), height: el.outerHeight() - el.height() }; - this.originalMousePosition = { left: event.pageX, top: event.pageY }; - - //Aspect Ratio - this.aspectRatio = (typeof o.aspectRatio == 'number') ? o.aspectRatio : ((this.originalSize.width / this.originalSize.height) || 1); - - var cursor = $('.ui-resizable-' + this.axis).css('cursor'); - $('body').css('cursor', cursor == 'auto' ? this.axis + '-resize' : cursor); - - el.addClass("ui-resizable-resizing"); - this._propagate("start", event); - return true; - }, - - _mouseDrag: function(event) { - - //Increase performance, avoid regex - var el = this.helper, o = this.options, props = {}, - self = this, smp = this.originalMousePosition, a = this.axis; - - var dx = (event.pageX-smp.left)||0, dy = (event.pageY-smp.top)||0; - var trigger = this._change[a]; - if (!trigger) return false; - - // Calculate the attrs that will be change - var data = trigger.apply(this, [event, dx, dy]), ie6 = $.browser.msie && $.browser.version < 7, csdif = this.sizeDiff; - - // Put this in the mouseDrag handler since the user can start pressing shift while resizing - this._updateVirtualBoundaries(event.shiftKey); - if (this._aspectRatio || event.shiftKey) - data = this._updateRatio(data, event); - - data = this._respectSize(data, event); - - // plugins callbacks need to be called first - this._propagate("resize", event); - - el.css({ - top: this.position.top + "px", left: this.position.left + "px", - width: this.size.width + "px", height: this.size.height + "px" - }); - - if (!this._helper && this._proportionallyResizeElements.length) - this._proportionallyResize(); - - this._updateCache(data); - - // calling the user callback at the end - this._trigger('resize', event, this.ui()); - - return false; - }, - - _mouseStop: function(event) { - - this.resizing = false; - var o = this.options, self = this; - - if(this._helper) { - var pr = this._proportionallyResizeElements, ista = pr.length && (/textarea/i).test(pr[0].nodeName), - soffseth = ista && $.ui.hasScroll(pr[0], 'left') /* TODO - jump height */ ? 0 : self.sizeDiff.height, - soffsetw = ista ? 0 : self.sizeDiff.width; - - var s = { width: (self.helper.width() - soffsetw), height: (self.helper.height() - soffseth) }, - left = (parseInt(self.element.css('left'), 10) + (self.position.left - self.originalPosition.left)) || null, - top = (parseInt(self.element.css('top'), 10) + (self.position.top - self.originalPosition.top)) || null; - - if (!o.animate) - this.element.css($.extend(s, { top: top, left: left })); - - self.helper.height(self.size.height); - self.helper.width(self.size.width); - - if (this._helper && !o.animate) this._proportionallyResize(); - } - - $('body').css('cursor', 'auto'); - - this.element.removeClass("ui-resizable-resizing"); - - this._propagate("stop", event); - - if (this._helper) this.helper.remove(); - return false; - - }, - - _updateVirtualBoundaries: function(forceAspectRatio) { - var o = this.options, pMinWidth, pMaxWidth, pMinHeight, pMaxHeight, b; - - b = { - minWidth: isNumber(o.minWidth) ? o.minWidth : 0, - maxWidth: isNumber(o.maxWidth) ? o.maxWidth : Infinity, - minHeight: isNumber(o.minHeight) ? o.minHeight : 0, - maxHeight: isNumber(o.maxHeight) ? o.maxHeight : Infinity - }; - - if(this._aspectRatio || forceAspectRatio) { - // We want to create an enclosing box whose aspect ration is the requested one - // First, compute the "projected" size for each dimension based on the aspect ratio and other dimension - pMinWidth = b.minHeight * this.aspectRatio; - pMinHeight = b.minWidth / this.aspectRatio; - pMaxWidth = b.maxHeight * this.aspectRatio; - pMaxHeight = b.maxWidth / this.aspectRatio; - - if(pMinWidth > b.minWidth) b.minWidth = pMinWidth; - if(pMinHeight > b.minHeight) b.minHeight = pMinHeight; - if(pMaxWidth < b.maxWidth) b.maxWidth = pMaxWidth; - if(pMaxHeight < b.maxHeight) b.maxHeight = pMaxHeight; - } - this._vBoundaries = b; - }, - - _updateCache: function(data) { - var o = this.options; - this.offset = this.helper.offset(); - if (isNumber(data.left)) this.position.left = data.left; - if (isNumber(data.top)) this.position.top = data.top; - if (isNumber(data.height)) this.size.height = data.height; - if (isNumber(data.width)) this.size.width = data.width; - }, - - _updateRatio: function(data, event) { - - var o = this.options, cpos = this.position, csize = this.size, a = this.axis; - - if (isNumber(data.height)) data.width = (data.height * this.aspectRatio); - else if (isNumber(data.width)) data.height = (data.width / this.aspectRatio); - - if (a == 'sw') { - data.left = cpos.left + (csize.width - data.width); - data.top = null; - } - if (a == 'nw') { - data.top = cpos.top + (csize.height - data.height); - data.left = cpos.left + (csize.width - data.width); - } - - return data; - }, - - _respectSize: function(data, event) { - - var el = this.helper, o = this._vBoundaries, pRatio = this._aspectRatio || event.shiftKey, a = this.axis, - ismaxw = isNumber(data.width) && o.maxWidth && (o.maxWidth < data.width), ismaxh = isNumber(data.height) && o.maxHeight && (o.maxHeight < data.height), - isminw = isNumber(data.width) && o.minWidth && (o.minWidth > data.width), isminh = isNumber(data.height) && o.minHeight && (o.minHeight > data.height); - - if (isminw) data.width = o.minWidth; - if (isminh) data.height = o.minHeight; - if (ismaxw) data.width = o.maxWidth; - if (ismaxh) data.height = o.maxHeight; - - var dw = this.originalPosition.left + this.originalSize.width, dh = this.position.top + this.size.height; - var cw = /sw|nw|w/.test(a), ch = /nw|ne|n/.test(a); - - if (isminw && cw) data.left = dw - o.minWidth; - if (ismaxw && cw) data.left = dw - o.maxWidth; - if (isminh && ch) data.top = dh - o.minHeight; - if (ismaxh && ch) data.top = dh - o.maxHeight; - - // fixing jump error on top/left - bug #2330 - var isNotwh = !data.width && !data.height; - if (isNotwh && !data.left && data.top) data.top = null; - else if (isNotwh && !data.top && data.left) data.left = null; - - return data; - }, - - _proportionallyResize: function() { - - var o = this.options; - if (!this._proportionallyResizeElements.length) return; - var element = this.helper || this.element; - - for (var i=0; i < this._proportionallyResizeElements.length; i++) { - - var prel = this._proportionallyResizeElements[i]; - - if (!this.borderDif) { - var b = [prel.css('borderTopWidth'), prel.css('borderRightWidth'), prel.css('borderBottomWidth'), prel.css('borderLeftWidth')], - p = [prel.css('paddingTop'), prel.css('paddingRight'), prel.css('paddingBottom'), prel.css('paddingLeft')]; - - this.borderDif = $.map(b, function(v, i) { - var border = parseInt(v,10)||0, padding = parseInt(p[i],10)||0; - return border + padding; - }); - } - - if ($.browser.msie && !(!($(element).is(':hidden') || $(element).parents(':hidden').length))) - continue; - - prel.css({ - height: (element.height() - this.borderDif[0] - this.borderDif[2]) || 0, - width: (element.width() - this.borderDif[1] - this.borderDif[3]) || 0 - }); - - }; - - }, - - _renderProxy: function() { - - var el = this.element, o = this.options; - this.elementOffset = el.offset(); - - if(this._helper) { - - this.helper = this.helper || $('
    '); - - // fix ie6 offset TODO: This seems broken - var ie6 = $.browser.msie && $.browser.version < 7, ie6offset = (ie6 ? 1 : 0), - pxyoffset = ( ie6 ? 2 : -1 ); - - this.helper.addClass(this._helper).css({ - width: this.element.outerWidth() + pxyoffset, - height: this.element.outerHeight() + pxyoffset, - position: 'absolute', - left: this.elementOffset.left - ie6offset +'px', - top: this.elementOffset.top - ie6offset +'px', - zIndex: ++o.zIndex //TODO: Don't modify option - }); - - this.helper - .appendTo("body") - .disableSelection(); - - } else { - this.helper = this.element; - } - - }, - - _change: { - e: function(event, dx, dy) { - return { width: this.originalSize.width + dx }; - }, - w: function(event, dx, dy) { - var o = this.options, cs = this.originalSize, sp = this.originalPosition; - return { left: sp.left + dx, width: cs.width - dx }; - }, - n: function(event, dx, dy) { - var o = this.options, cs = this.originalSize, sp = this.originalPosition; - return { top: sp.top + dy, height: cs.height - dy }; - }, - s: function(event, dx, dy) { - return { height: this.originalSize.height + dy }; - }, - se: function(event, dx, dy) { - return $.extend(this._change.s.apply(this, arguments), this._change.e.apply(this, [event, dx, dy])); - }, - sw: function(event, dx, dy) { - return $.extend(this._change.s.apply(this, arguments), this._change.w.apply(this, [event, dx, dy])); - }, - ne: function(event, dx, dy) { - return $.extend(this._change.n.apply(this, arguments), this._change.e.apply(this, [event, dx, dy])); - }, - nw: function(event, dx, dy) { - return $.extend(this._change.n.apply(this, arguments), this._change.w.apply(this, [event, dx, dy])); - } - }, - - _propagate: function(n, event) { - $.ui.plugin.call(this, n, [event, this.ui()]); - (n != "resize" && this._trigger(n, event, this.ui())); - }, - - plugins: {}, - - ui: function() { - return { - originalElement: this.originalElement, - element: this.element, - helper: this.helper, - position: this.position, - size: this.size, - originalSize: this.originalSize, - originalPosition: this.originalPosition - }; - } - -}); - -$.extend($.ui.resizable, { - version: "1.8.23" -}); - -/* - * Resizable Extensions - */ - -$.ui.plugin.add("resizable", "alsoResize", { - - start: function (event, ui) { - var self = $(this).data("resizable"), o = self.options; - - var _store = function (exp) { - $(exp).each(function() { - var el = $(this); - el.data("resizable-alsoresize", { - width: parseInt(el.width(), 10), height: parseInt(el.height(), 10), - left: parseInt(el.css('left'), 10), top: parseInt(el.css('top'), 10) - }); - }); - }; - - if (typeof(o.alsoResize) == 'object' && !o.alsoResize.parentNode) { - if (o.alsoResize.length) { o.alsoResize = o.alsoResize[0]; _store(o.alsoResize); } - else { $.each(o.alsoResize, function (exp) { _store(exp); }); } - }else{ - _store(o.alsoResize); - } - }, - - resize: function (event, ui) { - var self = $(this).data("resizable"), o = self.options, os = self.originalSize, op = self.originalPosition; - - var delta = { - height: (self.size.height - os.height) || 0, width: (self.size.width - os.width) || 0, - top: (self.position.top - op.top) || 0, left: (self.position.left - op.left) || 0 - }, - - _alsoResize = function (exp, c) { - $(exp).each(function() { - var el = $(this), start = $(this).data("resizable-alsoresize"), style = {}, - css = c && c.length ? c : el.parents(ui.originalElement[0]).length ? ['width', 'height'] : ['width', 'height', 'top', 'left']; - - $.each(css, function (i, prop) { - var sum = (start[prop]||0) + (delta[prop]||0); - if (sum && sum >= 0) - style[prop] = sum || null; - }); - - el.css(style); - }); - }; - - if (typeof(o.alsoResize) == 'object' && !o.alsoResize.nodeType) { - $.each(o.alsoResize, function (exp, c) { _alsoResize(exp, c); }); - }else{ - _alsoResize(o.alsoResize); - } - }, - - stop: function (event, ui) { - $(this).removeData("resizable-alsoresize"); - } -}); - -$.ui.plugin.add("resizable", "animate", { - - stop: function(event, ui) { - var self = $(this).data("resizable"), o = self.options; - - var pr = self._proportionallyResizeElements, ista = pr.length && (/textarea/i).test(pr[0].nodeName), - soffseth = ista && $.ui.hasScroll(pr[0], 'left') /* TODO - jump height */ ? 0 : self.sizeDiff.height, - soffsetw = ista ? 0 : self.sizeDiff.width; - - var style = { width: (self.size.width - soffsetw), height: (self.size.height - soffseth) }, - left = (parseInt(self.element.css('left'), 10) + (self.position.left - self.originalPosition.left)) || null, - top = (parseInt(self.element.css('top'), 10) + (self.position.top - self.originalPosition.top)) || null; - - self.element.animate( - $.extend(style, top && left ? { top: top, left: left } : {}), { - duration: o.animateDuration, - easing: o.animateEasing, - step: function() { - - var data = { - width: parseInt(self.element.css('width'), 10), - height: parseInt(self.element.css('height'), 10), - top: parseInt(self.element.css('top'), 10), - left: parseInt(self.element.css('left'), 10) - }; - - if (pr && pr.length) $(pr[0]).css({ width: data.width, height: data.height }); - - // propagating resize, and updating values for each animation step - self._updateCache(data); - self._propagate("resize", event); - - } - } - ); - } - -}); - -$.ui.plugin.add("resizable", "containment", { - - start: function(event, ui) { - var self = $(this).data("resizable"), o = self.options, el = self.element; - var oc = o.containment, ce = (oc instanceof $) ? oc.get(0) : (/parent/.test(oc)) ? el.parent().get(0) : oc; - if (!ce) return; - - self.containerElement = $(ce); - - if (/document/.test(oc) || oc == document) { - self.containerOffset = { left: 0, top: 0 }; - self.containerPosition = { left: 0, top: 0 }; - - self.parentData = { - element: $(document), left: 0, top: 0, - width: $(document).width(), height: $(document).height() || document.body.parentNode.scrollHeight - }; - } - - // i'm a node, so compute top, left, right, bottom - else { - var element = $(ce), p = []; - $([ "Top", "Right", "Left", "Bottom" ]).each(function(i, name) { p[i] = num(element.css("padding" + name)); }); - - self.containerOffset = element.offset(); - self.containerPosition = element.position(); - self.containerSize = { height: (element.innerHeight() - p[3]), width: (element.innerWidth() - p[1]) }; - - var co = self.containerOffset, ch = self.containerSize.height, cw = self.containerSize.width, - width = ($.ui.hasScroll(ce, "left") ? ce.scrollWidth : cw ), height = ($.ui.hasScroll(ce) ? ce.scrollHeight : ch); - - self.parentData = { - element: ce, left: co.left, top: co.top, width: width, height: height - }; - } - }, - - resize: function(event, ui) { - var self = $(this).data("resizable"), o = self.options, - ps = self.containerSize, co = self.containerOffset, cs = self.size, cp = self.position, - pRatio = self._aspectRatio || event.shiftKey, cop = { top:0, left:0 }, ce = self.containerElement; - - if (ce[0] != document && (/static/).test(ce.css('position'))) cop = co; - - if (cp.left < (self._helper ? co.left : 0)) { - self.size.width = self.size.width + (self._helper ? (self.position.left - co.left) : (self.position.left - cop.left)); - if (pRatio) self.size.height = self.size.width / self.aspectRatio; - self.position.left = o.helper ? co.left : 0; - } - - if (cp.top < (self._helper ? co.top : 0)) { - self.size.height = self.size.height + (self._helper ? (self.position.top - co.top) : self.position.top); - if (pRatio) self.size.width = self.size.height * self.aspectRatio; - self.position.top = self._helper ? co.top : 0; - } - - self.offset.left = self.parentData.left+self.position.left; - self.offset.top = self.parentData.top+self.position.top; - - var woset = Math.abs( (self._helper ? self.offset.left - cop.left : (self.offset.left - cop.left)) + self.sizeDiff.width ), - hoset = Math.abs( (self._helper ? self.offset.top - cop.top : (self.offset.top - co.top)) + self.sizeDiff.height ); - - var isParent = self.containerElement.get(0) == self.element.parent().get(0), - isOffsetRelative = /relative|absolute/.test(self.containerElement.css('position')); - - if(isParent && isOffsetRelative) woset -= self.parentData.left; - - if (woset + self.size.width >= self.parentData.width) { - self.size.width = self.parentData.width - woset; - if (pRatio) self.size.height = self.size.width / self.aspectRatio; - } - - if (hoset + self.size.height >= self.parentData.height) { - self.size.height = self.parentData.height - hoset; - if (pRatio) self.size.width = self.size.height * self.aspectRatio; - } - }, - - stop: function(event, ui){ - var self = $(this).data("resizable"), o = self.options, cp = self.position, - co = self.containerOffset, cop = self.containerPosition, ce = self.containerElement; - - var helper = $(self.helper), ho = helper.offset(), w = helper.outerWidth() - self.sizeDiff.width, h = helper.outerHeight() - self.sizeDiff.height; - - if (self._helper && !o.animate && (/relative/).test(ce.css('position'))) - $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h }); - - if (self._helper && !o.animate && (/static/).test(ce.css('position'))) - $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h }); - - } -}); - -$.ui.plugin.add("resizable", "ghost", { - - start: function(event, ui) { - - var self = $(this).data("resizable"), o = self.options, cs = self.size; - - self.ghost = self.originalElement.clone(); - self.ghost - .css({ opacity: .25, display: 'block', position: 'relative', height: cs.height, width: cs.width, margin: 0, left: 0, top: 0 }) - .addClass('ui-resizable-ghost') - .addClass(typeof o.ghost == 'string' ? o.ghost : ''); - - self.ghost.appendTo(self.helper); - - }, - - resize: function(event, ui){ - var self = $(this).data("resizable"), o = self.options; - if (self.ghost) self.ghost.css({ position: 'relative', height: self.size.height, width: self.size.width }); - }, - - stop: function(event, ui){ - var self = $(this).data("resizable"), o = self.options; - if (self.ghost && self.helper) self.helper.get(0).removeChild(self.ghost.get(0)); - } - -}); - -$.ui.plugin.add("resizable", "grid", { - - resize: function(event, ui) { - var self = $(this).data("resizable"), o = self.options, cs = self.size, os = self.originalSize, op = self.originalPosition, a = self.axis, ratio = o._aspectRatio || event.shiftKey; - o.grid = typeof o.grid == "number" ? [o.grid, o.grid] : o.grid; - var ox = Math.round((cs.width - os.width) / (o.grid[0]||1)) * (o.grid[0]||1), oy = Math.round((cs.height - os.height) / (o.grid[1]||1)) * (o.grid[1]||1); - - if (/^(se|s|e)$/.test(a)) { - self.size.width = os.width + ox; - self.size.height = os.height + oy; - } - else if (/^(ne)$/.test(a)) { - self.size.width = os.width + ox; - self.size.height = os.height + oy; - self.position.top = op.top - oy; - } - else if (/^(sw)$/.test(a)) { - self.size.width = os.width + ox; - self.size.height = os.height + oy; - self.position.left = op.left - ox; - } - else { - self.size.width = os.width + ox; - self.size.height = os.height + oy; - self.position.top = op.top - oy; - self.position.left = op.left - ox; - } - } - -}); - -var num = function(v) { - return parseInt(v, 10) || 0; -}; - -var isNumber = function(value) { - return !isNaN(parseInt(value, 10)); -}; - -})(jQuery); -/*! - * jQuery UI Selectable 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Selectables - * - * Depends: - * jquery.ui.core.js - * jquery.ui.mouse.js - * jquery.ui.widget.js - */ -(function( $, undefined ) { - -$.widget("ui.selectable", $.ui.mouse, { - options: { - appendTo: 'body', - autoRefresh: true, - distance: 0, - filter: '*', - tolerance: 'touch' - }, - _create: function() { - var self = this; - - this.element.addClass("ui-selectable"); - - this.dragged = false; - - // cache selectee children based on filter - var selectees; - this.refresh = function() { - selectees = $(self.options.filter, self.element[0]); - selectees.addClass("ui-selectee"); - selectees.each(function() { - var $this = $(this); - var pos = $this.offset(); - $.data(this, "selectable-item", { - element: this, - $element: $this, - left: pos.left, - top: pos.top, - right: pos.left + $this.outerWidth(), - bottom: pos.top + $this.outerHeight(), - startselected: false, - selected: $this.hasClass('ui-selected'), - selecting: $this.hasClass('ui-selecting'), - unselecting: $this.hasClass('ui-unselecting') - }); - }); - }; - this.refresh(); - - this.selectees = selectees.addClass("ui-selectee"); - - this._mouseInit(); - - this.helper = $("
    "); - }, - - destroy: function() { - this.selectees - .removeClass("ui-selectee") - .removeData("selectable-item"); - this.element - .removeClass("ui-selectable ui-selectable-disabled") - .removeData("selectable") - .unbind(".selectable"); - this._mouseDestroy(); - - return this; - }, - - _mouseStart: function(event) { - var self = this; - - this.opos = [event.pageX, event.pageY]; - - if (this.options.disabled) - return; - - var options = this.options; - - this.selectees = $(options.filter, this.element[0]); - - this._trigger("start", event); - - $(options.appendTo).append(this.helper); - // position helper (lasso) - this.helper.css({ - "left": event.clientX, - "top": event.clientY, - "width": 0, - "height": 0 - }); - - if (options.autoRefresh) { - this.refresh(); - } - - this.selectees.filter('.ui-selected').each(function() { - var selectee = $.data(this, "selectable-item"); - selectee.startselected = true; - if (!event.metaKey && !event.ctrlKey) { - selectee.$element.removeClass('ui-selected'); - selectee.selected = false; - selectee.$element.addClass('ui-unselecting'); - selectee.unselecting = true; - // selectable UNSELECTING callback - self._trigger("unselecting", event, { - unselecting: selectee.element - }); - } - }); - - $(event.target).parents().andSelf().each(function() { - var selectee = $.data(this, "selectable-item"); - if (selectee) { - var doSelect = (!event.metaKey && !event.ctrlKey) || !selectee.$element.hasClass('ui-selected'); - selectee.$element - .removeClass(doSelect ? "ui-unselecting" : "ui-selected") - .addClass(doSelect ? "ui-selecting" : "ui-unselecting"); - selectee.unselecting = !doSelect; - selectee.selecting = doSelect; - selectee.selected = doSelect; - // selectable (UN)SELECTING callback - if (doSelect) { - self._trigger("selecting", event, { - selecting: selectee.element - }); - } else { - self._trigger("unselecting", event, { - unselecting: selectee.element - }); - } - return false; - } - }); - - }, - - _mouseDrag: function(event) { - var self = this; - this.dragged = true; - - if (this.options.disabled) - return; - - var options = this.options; - - var x1 = this.opos[0], y1 = this.opos[1], x2 = event.pageX, y2 = event.pageY; - if (x1 > x2) { var tmp = x2; x2 = x1; x1 = tmp; } - if (y1 > y2) { var tmp = y2; y2 = y1; y1 = tmp; } - this.helper.css({left: x1, top: y1, width: x2-x1, height: y2-y1}); - - this.selectees.each(function() { - var selectee = $.data(this, "selectable-item"); - //prevent helper from being selected if appendTo: selectable - if (!selectee || selectee.element == self.element[0]) - return; - var hit = false; - if (options.tolerance == 'touch') { - hit = ( !(selectee.left > x2 || selectee.right < x1 || selectee.top > y2 || selectee.bottom < y1) ); - } else if (options.tolerance == 'fit') { - hit = (selectee.left > x1 && selectee.right < x2 && selectee.top > y1 && selectee.bottom < y2); - } - - if (hit) { - // SELECT - if (selectee.selected) { - selectee.$element.removeClass('ui-selected'); - selectee.selected = false; - } - if (selectee.unselecting) { - selectee.$element.removeClass('ui-unselecting'); - selectee.unselecting = false; - } - if (!selectee.selecting) { - selectee.$element.addClass('ui-selecting'); - selectee.selecting = true; - // selectable SELECTING callback - self._trigger("selecting", event, { - selecting: selectee.element - }); - } - } else { - // UNSELECT - if (selectee.selecting) { - if ((event.metaKey || event.ctrlKey) && selectee.startselected) { - selectee.$element.removeClass('ui-selecting'); - selectee.selecting = false; - selectee.$element.addClass('ui-selected'); - selectee.selected = true; - } else { - selectee.$element.removeClass('ui-selecting'); - selectee.selecting = false; - if (selectee.startselected) { - selectee.$element.addClass('ui-unselecting'); - selectee.unselecting = true; - } - // selectable UNSELECTING callback - self._trigger("unselecting", event, { - unselecting: selectee.element - }); - } - } - if (selectee.selected) { - if (!event.metaKey && !event.ctrlKey && !selectee.startselected) { - selectee.$element.removeClass('ui-selected'); - selectee.selected = false; - - selectee.$element.addClass('ui-unselecting'); - selectee.unselecting = true; - // selectable UNSELECTING callback - self._trigger("unselecting", event, { - unselecting: selectee.element - }); - } - } - } - }); - - return false; - }, - - _mouseStop: function(event) { - var self = this; - - this.dragged = false; - - var options = this.options; - - $('.ui-unselecting', this.element[0]).each(function() { - var selectee = $.data(this, "selectable-item"); - selectee.$element.removeClass('ui-unselecting'); - selectee.unselecting = false; - selectee.startselected = false; - self._trigger("unselected", event, { - unselected: selectee.element - }); - }); - $('.ui-selecting', this.element[0]).each(function() { - var selectee = $.data(this, "selectable-item"); - selectee.$element.removeClass('ui-selecting').addClass('ui-selected'); - selectee.selecting = false; - selectee.selected = true; - selectee.startselected = true; - self._trigger("selected", event, { - selected: selectee.element - }); - }); - this._trigger("stop", event); - - this.helper.remove(); - - return false; - } - -}); - -$.extend($.ui.selectable, { - version: "1.8.23" -}); - -})(jQuery); -/*! - * jQuery UI Sortable 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Sortables - * - * Depends: - * jquery.ui.core.js - * jquery.ui.mouse.js - * jquery.ui.widget.js - */ -(function( $, undefined ) { - -$.widget("ui.sortable", $.ui.mouse, { - widgetEventPrefix: "sort", - ready: false, - options: { - appendTo: "parent", - axis: false, - connectWith: false, - containment: false, - cursor: 'auto', - cursorAt: false, - dropOnEmpty: true, - forcePlaceholderSize: false, - forceHelperSize: false, - grid: false, - handle: false, - helper: "original", - items: '> *', - opacity: false, - placeholder: false, - revert: false, - scroll: true, - scrollSensitivity: 20, - scrollSpeed: 20, - scope: "default", - tolerance: "intersect", - zIndex: 1000 - }, - _create: function() { - - var o = this.options; - this.containerCache = {}; - this.element.addClass("ui-sortable"); - - //Get the items - this.refresh(); - - //Let's determine if the items are being displayed horizontally - this.floating = this.items.length ? o.axis === 'x' || (/left|right/).test(this.items[0].item.css('float')) || (/inline|table-cell/).test(this.items[0].item.css('display')) : false; - - //Let's determine the parent's offset - this.offset = this.element.offset(); - - //Initialize mouse events for interaction - this._mouseInit(); - - //We're ready to go - this.ready = true - - }, - - destroy: function() { - $.Widget.prototype.destroy.call( this ); - this.element - .removeClass("ui-sortable ui-sortable-disabled"); - this._mouseDestroy(); - - for ( var i = this.items.length - 1; i >= 0; i-- ) - this.items[i].item.removeData(this.widgetName + "-item"); - - return this; - }, - - _setOption: function(key, value){ - if ( key === "disabled" ) { - this.options[ key ] = value; - - this.widget() - [ value ? "addClass" : "removeClass"]( "ui-sortable-disabled" ); - } else { - // Don't call widget base _setOption for disable as it adds ui-state-disabled class - $.Widget.prototype._setOption.apply(this, arguments); - } - }, - - _mouseCapture: function(event, overrideHandle) { - var that = this; - - if (this.reverting) { - return false; - } - - if(this.options.disabled || this.options.type == 'static') return false; - - //We have to refresh the items data once first - this._refreshItems(event); - - //Find out if the clicked node (or one of its parents) is a actual item in this.items - var currentItem = null, self = this, nodes = $(event.target).parents().each(function() { - if($.data(this, that.widgetName + '-item') == self) { - currentItem = $(this); - return false; - } - }); - if($.data(event.target, that.widgetName + '-item') == self) currentItem = $(event.target); - - if(!currentItem) return false; - if(this.options.handle && !overrideHandle) { - var validHandle = false; - - $(this.options.handle, currentItem).find("*").andSelf().each(function() { if(this == event.target) validHandle = true; }); - if(!validHandle) return false; - } - - this.currentItem = currentItem; - this._removeCurrentsFromItems(); - return true; - - }, - - _mouseStart: function(event, overrideHandle, noActivation) { - - var o = this.options, self = this; - this.currentContainer = this; - - //We only need to call refreshPositions, because the refreshItems call has been moved to mouseCapture - this.refreshPositions(); - - //Create and append the visible helper - this.helper = this._createHelper(event); - - //Cache the helper size - this._cacheHelperProportions(); - - /* - * - Position generation - - * This block generates everything position related - it's the core of draggables. - */ - - //Cache the margins of the original element - this._cacheMargins(); - - //Get the next scrolling parent - this.scrollParent = this.helper.scrollParent(); - - //The element's absolute position on the page minus margins - this.offset = this.currentItem.offset(); - this.offset = { - top: this.offset.top - this.margins.top, - left: this.offset.left - this.margins.left - }; - - $.extend(this.offset, { - click: { //Where the click happened, relative to the element - left: event.pageX - this.offset.left, - top: event.pageY - this.offset.top - }, - parent: this._getParentOffset(), - relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper - }); - - // Only after we got the offset, we can change the helper's position to absolute - // TODO: Still need to figure out a way to make relative sorting possible - this.helper.css("position", "absolute"); - this.cssPosition = this.helper.css("position"); - - //Generate the original position - this.originalPosition = this._generatePosition(event); - this.originalPageX = event.pageX; - this.originalPageY = event.pageY; - - //Adjust the mouse offset relative to the helper if 'cursorAt' is supplied - (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt)); - - //Cache the former DOM position - this.domPosition = { prev: this.currentItem.prev()[0], parent: this.currentItem.parent()[0] }; - - //If the helper is not the original, hide the original so it's not playing any role during the drag, won't cause anything bad this way - if(this.helper[0] != this.currentItem[0]) { - this.currentItem.hide(); - } - - //Create the placeholder - this._createPlaceholder(); - - //Set a containment if given in the options - if(o.containment) - this._setContainment(); - - if(o.cursor) { // cursor option - if ($('body').css("cursor")) this._storedCursor = $('body').css("cursor"); - $('body').css("cursor", o.cursor); - } - - if(o.opacity) { // opacity option - if (this.helper.css("opacity")) this._storedOpacity = this.helper.css("opacity"); - this.helper.css("opacity", o.opacity); - } - - if(o.zIndex) { // zIndex option - if (this.helper.css("zIndex")) this._storedZIndex = this.helper.css("zIndex"); - this.helper.css("zIndex", o.zIndex); - } - - //Prepare scrolling - if(this.scrollParent[0] != document && this.scrollParent[0].tagName != 'HTML') - this.overflowOffset = this.scrollParent.offset(); - - //Call callbacks - this._trigger("start", event, this._uiHash()); - - //Recache the helper size - if(!this._preserveHelperProportions) - this._cacheHelperProportions(); - - - //Post 'activate' events to possible containers - if(!noActivation) { - for (var i = this.containers.length - 1; i >= 0; i--) { this.containers[i]._trigger("activate", event, self._uiHash(this)); } - } - - //Prepare possible droppables - if($.ui.ddmanager) - $.ui.ddmanager.current = this; - - if ($.ui.ddmanager && !o.dropBehaviour) - $.ui.ddmanager.prepareOffsets(this, event); - - this.dragging = true; - - this.helper.addClass("ui-sortable-helper"); - this._mouseDrag(event); //Execute the drag once - this causes the helper not to be visible before getting its correct position - return true; - - }, - - _mouseDrag: function(event) { - - //Compute the helpers position - this.position = this._generatePosition(event); - this.positionAbs = this._convertPositionTo("absolute"); - - if (!this.lastPositionAbs) { - this.lastPositionAbs = this.positionAbs; - } - - //Do scrolling - if(this.options.scroll) { - var o = this.options, scrolled = false; - if(this.scrollParent[0] != document && this.scrollParent[0].tagName != 'HTML') { - - if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) - this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed; - else if(event.pageY - this.overflowOffset.top < o.scrollSensitivity) - this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed; - - if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) - this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed; - else if(event.pageX - this.overflowOffset.left < o.scrollSensitivity) - this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed; - - } else { - - if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) - scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed); - else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) - scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed); - - if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity) - scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed); - else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) - scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed); - - } - - if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) - $.ui.ddmanager.prepareOffsets(this, event); - } - - //Regenerate the absolute position used for position checks - this.positionAbs = this._convertPositionTo("absolute"); - - //Set the helper position - if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px'; - if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px'; - - //Rearrange - for (var i = this.items.length - 1; i >= 0; i--) { - - //Cache variables and intersection, continue if no intersection - var item = this.items[i], itemElement = item.item[0], intersection = this._intersectsWithPointer(item); - if (!intersection) continue; - - if(itemElement != this.currentItem[0] //cannot intersect with itself - && this.placeholder[intersection == 1 ? "next" : "prev"]()[0] != itemElement //no useless actions that have been done before - && !$.ui.contains(this.placeholder[0], itemElement) //no action if the item moved is the parent of the item checked - && (this.options.type == 'semi-dynamic' ? !$.ui.contains(this.element[0], itemElement) : true) - //&& itemElement.parentNode == this.placeholder[0].parentNode // only rearrange items within the same container - ) { - - this.direction = intersection == 1 ? "down" : "up"; - - if (this.options.tolerance == "pointer" || this._intersectsWithSides(item)) { - this._rearrange(event, item); - } else { - break; - } - - this._trigger("change", event, this._uiHash()); - break; - } - } - - //Post events to containers - this._contactContainers(event); - - //Interconnect with droppables - if($.ui.ddmanager) $.ui.ddmanager.drag(this, event); - - //Call callbacks - this._trigger('sort', event, this._uiHash()); - - this.lastPositionAbs = this.positionAbs; - return false; - - }, - - _mouseStop: function(event, noPropagation) { - - if(!event) return; - - //If we are using droppables, inform the manager about the drop - if ($.ui.ddmanager && !this.options.dropBehaviour) - $.ui.ddmanager.drop(this, event); - - if(this.options.revert) { - var self = this; - var cur = self.placeholder.offset(); - - self.reverting = true; - - $(this.helper).animate({ - left: cur.left - this.offset.parent.left - self.margins.left + (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollLeft), - top: cur.top - this.offset.parent.top - self.margins.top + (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollTop) - }, parseInt(this.options.revert, 10) || 500, function() { - self._clear(event); - }); - } else { - this._clear(event, noPropagation); - } - - return false; - - }, - - cancel: function() { - - var self = this; - - if(this.dragging) { - - this._mouseUp({ target: null }); - - if(this.options.helper == "original") - this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"); - else - this.currentItem.show(); - - //Post deactivating events to containers - for (var i = this.containers.length - 1; i >= 0; i--){ - this.containers[i]._trigger("deactivate", null, self._uiHash(this)); - if(this.containers[i].containerCache.over) { - this.containers[i]._trigger("out", null, self._uiHash(this)); - this.containers[i].containerCache.over = 0; - } - } - - } - - if (this.placeholder) { - //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node! - if(this.placeholder[0].parentNode) this.placeholder[0].parentNode.removeChild(this.placeholder[0]); - if(this.options.helper != "original" && this.helper && this.helper[0].parentNode) this.helper.remove(); - - $.extend(this, { - helper: null, - dragging: false, - reverting: false, - _noFinalSort: null - }); - - if(this.domPosition.prev) { - $(this.domPosition.prev).after(this.currentItem); - } else { - $(this.domPosition.parent).prepend(this.currentItem); - } - } - - return this; - - }, - - serialize: function(o) { - - var items = this._getItemsAsjQuery(o && o.connected); - var str = []; o = o || {}; - - $(items).each(function() { - var res = ($(o.item || this).attr(o.attribute || 'id') || '').match(o.expression || (/(.+)[-=_](.+)/)); - if(res) str.push((o.key || res[1]+'[]')+'='+(o.key && o.expression ? res[1] : res[2])); - }); - - if(!str.length && o.key) { - str.push(o.key + '='); - } - - return str.join('&'); - - }, - - toArray: function(o) { - - var items = this._getItemsAsjQuery(o && o.connected); - var ret = []; o = o || {}; - - items.each(function() { ret.push($(o.item || this).attr(o.attribute || 'id') || ''); }); - return ret; - - }, - - /* Be careful with the following core functions */ - _intersectsWith: function(item) { - - var x1 = this.positionAbs.left, - x2 = x1 + this.helperProportions.width, - y1 = this.positionAbs.top, - y2 = y1 + this.helperProportions.height; - - var l = item.left, - r = l + item.width, - t = item.top, - b = t + item.height; - - var dyClick = this.offset.click.top, - dxClick = this.offset.click.left; - - var isOverElement = (y1 + dyClick) > t && (y1 + dyClick) < b && (x1 + dxClick) > l && (x1 + dxClick) < r; - - if( this.options.tolerance == "pointer" - || this.options.forcePointerForContainers - || (this.options.tolerance != "pointer" && this.helperProportions[this.floating ? 'width' : 'height'] > item[this.floating ? 'width' : 'height']) - ) { - return isOverElement; - } else { - - return (l < x1 + (this.helperProportions.width / 2) // Right Half - && x2 - (this.helperProportions.width / 2) < r // Left Half - && t < y1 + (this.helperProportions.height / 2) // Bottom Half - && y2 - (this.helperProportions.height / 2) < b ); // Top Half - - } - }, - - _intersectsWithPointer: function(item) { - - var isOverElementHeight = (this.options.axis === 'x') || $.ui.isOverAxis(this.positionAbs.top + this.offset.click.top, item.top, item.height), - isOverElementWidth = (this.options.axis === 'y') || $.ui.isOverAxis(this.positionAbs.left + this.offset.click.left, item.left, item.width), - isOverElement = isOverElementHeight && isOverElementWidth, - verticalDirection = this._getDragVerticalDirection(), - horizontalDirection = this._getDragHorizontalDirection(); - - if (!isOverElement) - return false; - - return this.floating ? - ( ((horizontalDirection && horizontalDirection == "right") || verticalDirection == "down") ? 2 : 1 ) - : ( verticalDirection && (verticalDirection == "down" ? 2 : 1) ); - - }, - - _intersectsWithSides: function(item) { - - var isOverBottomHalf = $.ui.isOverAxis(this.positionAbs.top + this.offset.click.top, item.top + (item.height/2), item.height), - isOverRightHalf = $.ui.isOverAxis(this.positionAbs.left + this.offset.click.left, item.left + (item.width/2), item.width), - verticalDirection = this._getDragVerticalDirection(), - horizontalDirection = this._getDragHorizontalDirection(); - - if (this.floating && horizontalDirection) { - return ((horizontalDirection == "right" && isOverRightHalf) || (horizontalDirection == "left" && !isOverRightHalf)); - } else { - return verticalDirection && ((verticalDirection == "down" && isOverBottomHalf) || (verticalDirection == "up" && !isOverBottomHalf)); - } - - }, - - _getDragVerticalDirection: function() { - var delta = this.positionAbs.top - this.lastPositionAbs.top; - return delta != 0 && (delta > 0 ? "down" : "up"); - }, - - _getDragHorizontalDirection: function() { - var delta = this.positionAbs.left - this.lastPositionAbs.left; - return delta != 0 && (delta > 0 ? "right" : "left"); - }, - - refresh: function(event) { - this._refreshItems(event); - this.refreshPositions(); - return this; - }, - - _connectWith: function() { - var options = this.options; - return options.connectWith.constructor == String - ? [options.connectWith] - : options.connectWith; - }, - - _getItemsAsjQuery: function(connected) { - - var self = this; - var items = []; - var queries = []; - var connectWith = this._connectWith(); - - if(connectWith && connected) { - for (var i = connectWith.length - 1; i >= 0; i--){ - var cur = $(connectWith[i]); - for (var j = cur.length - 1; j >= 0; j--){ - var inst = $.data(cur[j], this.widgetName); - if(inst && inst != this && !inst.options.disabled) { - queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element) : $(inst.options.items, inst.element).not(".ui-sortable-helper").not('.ui-sortable-placeholder'), inst]); - } - }; - }; - } - - queries.push([$.isFunction(this.options.items) ? this.options.items.call(this.element, null, { options: this.options, item: this.currentItem }) : $(this.options.items, this.element).not(".ui-sortable-helper").not('.ui-sortable-placeholder'), this]); - - for (var i = queries.length - 1; i >= 0; i--){ - queries[i][0].each(function() { - items.push(this); - }); - }; - - return $(items); - - }, - - _removeCurrentsFromItems: function() { - - var list = this.currentItem.find(":data(" + this.widgetName + "-item)"); - - for (var i=0; i < this.items.length; i++) { - - for (var j=0; j < list.length; j++) { - if(list[j] == this.items[i].item[0]) - this.items.splice(i,1); - }; - - }; - - }, - - _refreshItems: function(event) { - - this.items = []; - this.containers = [this]; - var items = this.items; - var self = this; - var queries = [[$.isFunction(this.options.items) ? this.options.items.call(this.element[0], event, { item: this.currentItem }) : $(this.options.items, this.element), this]]; - var connectWith = this._connectWith(); - - if(connectWith && this.ready) { //Shouldn't be run the first time through due to massive slow-down - for (var i = connectWith.length - 1; i >= 0; i--){ - var cur = $(connectWith[i]); - for (var j = cur.length - 1; j >= 0; j--){ - var inst = $.data(cur[j], this.widgetName); - if(inst && inst != this && !inst.options.disabled) { - queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element[0], event, { item: this.currentItem }) : $(inst.options.items, inst.element), inst]); - this.containers.push(inst); - } - }; - }; - } - - for (var i = queries.length - 1; i >= 0; i--) { - var targetData = queries[i][1]; - var _queries = queries[i][0]; - - for (var j=0, queriesLength = _queries.length; j < queriesLength; j++) { - var item = $(_queries[j]); - - item.data(this.widgetName + '-item', targetData); // Data for target checking (mouse manager) - - items.push({ - item: item, - instance: targetData, - width: 0, height: 0, - left: 0, top: 0 - }); - }; - }; - - }, - - refreshPositions: function(fast) { - - //This has to be redone because due to the item being moved out/into the offsetParent, the offsetParent's position will change - if(this.offsetParent && this.helper) { - this.offset.parent = this._getParentOffset(); - } - - for (var i = this.items.length - 1; i >= 0; i--){ - var item = this.items[i]; - - //We ignore calculating positions of all connected containers when we're not over them - if(item.instance != this.currentContainer && this.currentContainer && item.item[0] != this.currentItem[0]) - continue; - - var t = this.options.toleranceElement ? $(this.options.toleranceElement, item.item) : item.item; - - if (!fast) { - item.width = t.outerWidth(); - item.height = t.outerHeight(); - } - - var p = t.offset(); - item.left = p.left; - item.top = p.top; - }; - - if(this.options.custom && this.options.custom.refreshContainers) { - this.options.custom.refreshContainers.call(this); - } else { - for (var i = this.containers.length - 1; i >= 0; i--){ - var p = this.containers[i].element.offset(); - this.containers[i].containerCache.left = p.left; - this.containers[i].containerCache.top = p.top; - this.containers[i].containerCache.width = this.containers[i].element.outerWidth(); - this.containers[i].containerCache.height = this.containers[i].element.outerHeight(); - }; - } - - return this; - }, - - _createPlaceholder: function(that) { - - var self = that || this, o = self.options; - - if(!o.placeholder || o.placeholder.constructor == String) { - var className = o.placeholder; - o.placeholder = { - element: function() { - - var el = $(document.createElement(self.currentItem[0].nodeName)) - .addClass(className || self.currentItem[0].className+" ui-sortable-placeholder") - .removeClass("ui-sortable-helper")[0]; - - if(!className) - el.style.visibility = "hidden"; - - return el; - }, - update: function(container, p) { - - // 1. If a className is set as 'placeholder option, we don't force sizes - the class is responsible for that - // 2. The option 'forcePlaceholderSize can be enabled to force it even if a class name is specified - if(className && !o.forcePlaceholderSize) return; - - //If the element doesn't have a actual height by itself (without styles coming from a stylesheet), it receives the inline height from the dragged item - if(!p.height()) { p.height(self.currentItem.innerHeight() - parseInt(self.currentItem.css('paddingTop')||0, 10) - parseInt(self.currentItem.css('paddingBottom')||0, 10)); }; - if(!p.width()) { p.width(self.currentItem.innerWidth() - parseInt(self.currentItem.css('paddingLeft')||0, 10) - parseInt(self.currentItem.css('paddingRight')||0, 10)); }; - } - }; - } - - //Create the placeholder - self.placeholder = $(o.placeholder.element.call(self.element, self.currentItem)); - - //Append it after the actual current item - self.currentItem.after(self.placeholder); - - //Update the size of the placeholder (TODO: Logic to fuzzy, see line 316/317) - o.placeholder.update(self, self.placeholder); - - }, - - _contactContainers: function(event) { - - // get innermost container that intersects with item - var innermostContainer = null, innermostIndex = null; - - - for (var i = this.containers.length - 1; i >= 0; i--){ - - // never consider a container that's located within the item itself - if($.ui.contains(this.currentItem[0], this.containers[i].element[0])) - continue; - - if(this._intersectsWith(this.containers[i].containerCache)) { - - // if we've already found a container and it's more "inner" than this, then continue - if(innermostContainer && $.ui.contains(this.containers[i].element[0], innermostContainer.element[0])) - continue; - - innermostContainer = this.containers[i]; - innermostIndex = i; - - } else { - // container doesn't intersect. trigger "out" event if necessary - if(this.containers[i].containerCache.over) { - this.containers[i]._trigger("out", event, this._uiHash(this)); - this.containers[i].containerCache.over = 0; - } - } - - } - - // if no intersecting containers found, return - if(!innermostContainer) return; - - // move the item into the container if it's not there already - if(this.containers.length === 1) { - this.containers[innermostIndex]._trigger("over", event, this._uiHash(this)); - this.containers[innermostIndex].containerCache.over = 1; - } else if(this.currentContainer != this.containers[innermostIndex]) { - - //When entering a new container, we will find the item with the least distance and append our item near it - var dist = 10000; var itemWithLeastDistance = null; var base = this.positionAbs[this.containers[innermostIndex].floating ? 'left' : 'top']; - for (var j = this.items.length - 1; j >= 0; j--) { - if(!$.ui.contains(this.containers[innermostIndex].element[0], this.items[j].item[0])) continue; - var cur = this.containers[innermostIndex].floating ? this.items[j].item.offset().left : this.items[j].item.offset().top; - if(Math.abs(cur - base) < dist) { - dist = Math.abs(cur - base); itemWithLeastDistance = this.items[j]; - this.direction = (cur - base > 0) ? 'down' : 'up'; - } - } - - if(!itemWithLeastDistance && !this.options.dropOnEmpty) //Check if dropOnEmpty is enabled - return; - - this.currentContainer = this.containers[innermostIndex]; - itemWithLeastDistance ? this._rearrange(event, itemWithLeastDistance, null, true) : this._rearrange(event, null, this.containers[innermostIndex].element, true); - this._trigger("change", event, this._uiHash()); - this.containers[innermostIndex]._trigger("change", event, this._uiHash(this)); - - //Update the placeholder - this.options.placeholder.update(this.currentContainer, this.placeholder); - - this.containers[innermostIndex]._trigger("over", event, this._uiHash(this)); - this.containers[innermostIndex].containerCache.over = 1; - } - - - }, - - _createHelper: function(event) { - - var o = this.options; - var helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event, this.currentItem])) : (o.helper == 'clone' ? this.currentItem.clone() : this.currentItem); - - if(!helper.parents('body').length) //Add the helper to the DOM if that didn't happen already - $(o.appendTo != 'parent' ? o.appendTo : this.currentItem[0].parentNode)[0].appendChild(helper[0]); - - if(helper[0] == this.currentItem[0]) - this._storedCSS = { width: this.currentItem[0].style.width, height: this.currentItem[0].style.height, position: this.currentItem.css("position"), top: this.currentItem.css("top"), left: this.currentItem.css("left") }; - - if(helper[0].style.width == '' || o.forceHelperSize) helper.width(this.currentItem.width()); - if(helper[0].style.height == '' || o.forceHelperSize) helper.height(this.currentItem.height()); - - return helper; - - }, - - _adjustOffsetFromHelper: function(obj) { - if (typeof obj == 'string') { - obj = obj.split(' '); - } - if ($.isArray(obj)) { - obj = {left: +obj[0], top: +obj[1] || 0}; - } - if ('left' in obj) { - this.offset.click.left = obj.left + this.margins.left; - } - if ('right' in obj) { - this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left; - } - if ('top' in obj) { - this.offset.click.top = obj.top + this.margins.top; - } - if ('bottom' in obj) { - this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top; - } - }, - - _getParentOffset: function() { - - - //Get the offsetParent and cache its position - this.offsetParent = this.helper.offsetParent(); - var po = this.offsetParent.offset(); - - // This is a special case where we need to modify a offset calculated on start, since the following happened: - // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent - // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that - // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag - if(this.cssPosition == 'absolute' && this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) { - po.left += this.scrollParent.scrollLeft(); - po.top += this.scrollParent.scrollTop(); - } - - if((this.offsetParent[0] == document.body) //This needs to be actually done for all browsers, since pageX/pageY includes this information - || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() == 'html' && $.browser.msie)) //Ugly IE fix - po = { top: 0, left: 0 }; - - return { - top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0), - left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0) - }; - - }, - - _getRelativeOffset: function() { - - if(this.cssPosition == "relative") { - var p = this.currentItem.position(); - return { - top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(), - left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft() - }; - } else { - return { top: 0, left: 0 }; - } - - }, - - _cacheMargins: function() { - this.margins = { - left: (parseInt(this.currentItem.css("marginLeft"),10) || 0), - top: (parseInt(this.currentItem.css("marginTop"),10) || 0) - }; - }, - - _cacheHelperProportions: function() { - this.helperProportions = { - width: this.helper.outerWidth(), - height: this.helper.outerHeight() - }; - }, - - _setContainment: function() { - - var o = this.options; - if(o.containment == 'parent') o.containment = this.helper[0].parentNode; - if(o.containment == 'document' || o.containment == 'window') this.containment = [ - 0 - this.offset.relative.left - this.offset.parent.left, - 0 - this.offset.relative.top - this.offset.parent.top, - $(o.containment == 'document' ? document : window).width() - this.helperProportions.width - this.margins.left, - ($(o.containment == 'document' ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top - ]; - - if(!(/^(document|window|parent)$/).test(o.containment)) { - var ce = $(o.containment)[0]; - var co = $(o.containment).offset(); - var over = ($(ce).css("overflow") != 'hidden'); - - this.containment = [ - co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0) - this.margins.left, - co.top + (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0) - this.margins.top, - co.left+(over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left, - co.top+(over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),10) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top - ]; - } - - }, - - _convertPositionTo: function(d, pos) { - - if(!pos) pos = this.position; - var mod = d == "absolute" ? 1 : -1; - var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName); - - return { - top: ( - pos.top // The absolute mouse position - + this.offset.relative.top * mod // Only for relative positioned nodes: Relative offset from element to offset parent - + this.offset.parent.top * mod // The offsetParent's offset without borders (offset + border) - - ($.browser.safari && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod) - ), - left: ( - pos.left // The absolute mouse position - + this.offset.relative.left * mod // Only for relative positioned nodes: Relative offset from element to offset parent - + this.offset.parent.left * mod // The offsetParent's offset without borders (offset + border) - - ($.browser.safari && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod) - ) - }; - - }, - - _generatePosition: function(event) { - - var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName); - - // This is another very weird special case that only happens for relative elements: - // 1. If the css position is relative - // 2. and the scroll parent is the document or similar to the offset parent - // we have to refresh the relative offset during the scroll so there are no jumps - if(this.cssPosition == 'relative' && !(this.scrollParent[0] != document && this.scrollParent[0] != this.offsetParent[0])) { - this.offset.relative = this._getRelativeOffset(); - } - - var pageX = event.pageX; - var pageY = event.pageY; - - /* - * - Position constraining - - * Constrain the position to a mix of grid, containment. - */ - - if(this.originalPosition) { //If we are not dragging yet, we won't check for options - - if(this.containment) { - if(event.pageX - this.offset.click.left < this.containment[0]) pageX = this.containment[0] + this.offset.click.left; - if(event.pageY - this.offset.click.top < this.containment[1]) pageY = this.containment[1] + this.offset.click.top; - if(event.pageX - this.offset.click.left > this.containment[2]) pageX = this.containment[2] + this.offset.click.left; - if(event.pageY - this.offset.click.top > this.containment[3]) pageY = this.containment[3] + this.offset.click.top; - } - - if(o.grid) { - var top = this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1]; - pageY = this.containment ? (!(top - this.offset.click.top < this.containment[1] || top - this.offset.click.top > this.containment[3]) ? top : (!(top - this.offset.click.top < this.containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top; - - var left = this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0]; - pageX = this.containment ? (!(left - this.offset.click.left < this.containment[0] || left - this.offset.click.left > this.containment[2]) ? left : (!(left - this.offset.click.left < this.containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left; - } - - } - - return { - top: ( - pageY // The absolute mouse position - - this.offset.click.top // Click offset (relative to the element) - - this.offset.relative.top // Only for relative positioned nodes: Relative offset from element to offset parent - - this.offset.parent.top // The offsetParent's offset without borders (offset + border) - + ($.browser.safari && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) )) - ), - left: ( - pageX // The absolute mouse position - - this.offset.click.left // Click offset (relative to the element) - - this.offset.relative.left // Only for relative positioned nodes: Relative offset from element to offset parent - - this.offset.parent.left // The offsetParent's offset without borders (offset + border) - + ($.browser.safari && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() )) - ) - }; - - }, - - _rearrange: function(event, i, a, hardRefresh) { - - a ? a[0].appendChild(this.placeholder[0]) : i.item[0].parentNode.insertBefore(this.placeholder[0], (this.direction == 'down' ? i.item[0] : i.item[0].nextSibling)); - - //Various things done here to improve the performance: - // 1. we create a setTimeout, that calls refreshPositions - // 2. on the instance, we have a counter variable, that get's higher after every append - // 3. on the local scope, we copy the counter variable, and check in the timeout, if it's still the same - // 4. this lets only the last addition to the timeout stack through - this.counter = this.counter ? ++this.counter : 1; - var self = this, counter = this.counter; - - window.setTimeout(function() { - if(counter == self.counter) self.refreshPositions(!hardRefresh); //Precompute after each DOM insertion, NOT on mousemove - },0); - - }, - - _clear: function(event, noPropagation) { - - this.reverting = false; - // We delay all events that have to be triggered to after the point where the placeholder has been removed and - // everything else normalized again - var delayedTriggers = [], self = this; - - // We first have to update the dom position of the actual currentItem - // Note: don't do it if the current item is already removed (by a user), or it gets reappended (see #4088) - if(!this._noFinalSort && this.currentItem.parent().length) this.placeholder.before(this.currentItem); - this._noFinalSort = null; - - if(this.helper[0] == this.currentItem[0]) { - for(var i in this._storedCSS) { - if(this._storedCSS[i] == 'auto' || this._storedCSS[i] == 'static') this._storedCSS[i] = ''; - } - this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"); - } else { - this.currentItem.show(); - } - - if(this.fromOutside && !noPropagation) delayedTriggers.push(function(event) { this._trigger("receive", event, this._uiHash(this.fromOutside)); }); - if((this.fromOutside || this.domPosition.prev != this.currentItem.prev().not(".ui-sortable-helper")[0] || this.domPosition.parent != this.currentItem.parent()[0]) && !noPropagation) delayedTriggers.push(function(event) { this._trigger("update", event, this._uiHash()); }); //Trigger update callback if the DOM position has changed - if(!$.ui.contains(this.element[0], this.currentItem[0])) { //Node was moved out of the current element - if(!noPropagation) delayedTriggers.push(function(event) { this._trigger("remove", event, this._uiHash()); }); - for (var i = this.containers.length - 1; i >= 0; i--){ - if($.ui.contains(this.containers[i].element[0], this.currentItem[0]) && !noPropagation) { - delayedTriggers.push((function(c) { return function(event) { c._trigger("receive", event, this._uiHash(this)); }; }).call(this, this.containers[i])); - delayedTriggers.push((function(c) { return function(event) { c._trigger("update", event, this._uiHash(this)); }; }).call(this, this.containers[i])); - } - }; - }; - - //Post events to containers - for (var i = this.containers.length - 1; i >= 0; i--){ - if(!noPropagation) delayedTriggers.push((function(c) { return function(event) { c._trigger("deactivate", event, this._uiHash(this)); }; }).call(this, this.containers[i])); - if(this.containers[i].containerCache.over) { - delayedTriggers.push((function(c) { return function(event) { c._trigger("out", event, this._uiHash(this)); }; }).call(this, this.containers[i])); - this.containers[i].containerCache.over = 0; - } - } - - //Do what was originally in plugins - if(this._storedCursor) $('body').css("cursor", this._storedCursor); //Reset cursor - if(this._storedOpacity) this.helper.css("opacity", this._storedOpacity); //Reset opacity - if(this._storedZIndex) this.helper.css("zIndex", this._storedZIndex == 'auto' ? '' : this._storedZIndex); //Reset z-index - - this.dragging = false; - if(this.cancelHelperRemoval) { - if(!noPropagation) { - this._trigger("beforeStop", event, this._uiHash()); - for (var i=0; i < delayedTriggers.length; i++) { delayedTriggers[i].call(this, event); }; //Trigger all delayed events - this._trigger("stop", event, this._uiHash()); - } - - this.fromOutside = false; - return false; - } - - if(!noPropagation) this._trigger("beforeStop", event, this._uiHash()); - - //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node! - this.placeholder[0].parentNode.removeChild(this.placeholder[0]); - - if(this.helper[0] != this.currentItem[0]) this.helper.remove(); this.helper = null; - - if(!noPropagation) { - for (var i=0; i < delayedTriggers.length; i++) { delayedTriggers[i].call(this, event); }; //Trigger all delayed events - this._trigger("stop", event, this._uiHash()); - } - - this.fromOutside = false; - return true; - - }, - - _trigger: function() { - if ($.Widget.prototype._trigger.apply(this, arguments) === false) { - this.cancel(); - } - }, - - _uiHash: function(inst) { - var self = inst || this; - return { - helper: self.helper, - placeholder: self.placeholder || $([]), - position: self.position, - originalPosition: self.originalPosition, - offset: self.positionAbs, - item: self.currentItem, - sender: inst ? inst.element : null - }; - } - -}); - -$.extend($.ui.sortable, { - version: "1.8.23" -}); - -})(jQuery); -/*! - * jQuery UI Accordion 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Accordion - * - * Depends: - * jquery.ui.core.js - * jquery.ui.widget.js - */ -(function( $, undefined ) { - -$.widget( "ui.accordion", { - options: { - active: 0, - animated: "slide", - autoHeight: true, - clearStyle: false, - collapsible: false, - event: "click", - fillSpace: false, - header: "> li > :first-child,> :not(li):even", - icons: { - header: "ui-icon-triangle-1-e", - headerSelected: "ui-icon-triangle-1-s" - }, - navigation: false, - navigationFilter: function() { - return this.href.toLowerCase() === location.href.toLowerCase(); - } - }, - - _create: function() { - var self = this, - options = self.options; - - self.running = 0; - - self.element - .addClass( "ui-accordion ui-widget ui-helper-reset" ) - // in lack of child-selectors in CSS - // we need to mark top-LIs in a UL-accordion for some IE-fix - .children( "li" ) - .addClass( "ui-accordion-li-fix" ); - - self.headers = self.element.find( options.header ) - .addClass( "ui-accordion-header ui-helper-reset ui-state-default ui-corner-all" ) - .bind( "mouseenter.accordion", function() { - if ( options.disabled ) { - return; - } - $( this ).addClass( "ui-state-hover" ); - }) - .bind( "mouseleave.accordion", function() { - if ( options.disabled ) { - return; - } - $( this ).removeClass( "ui-state-hover" ); - }) - .bind( "focus.accordion", function() { - if ( options.disabled ) { - return; - } - $( this ).addClass( "ui-state-focus" ); - }) - .bind( "blur.accordion", function() { - if ( options.disabled ) { - return; - } - $( this ).removeClass( "ui-state-focus" ); - }); - - self.headers.next() - .addClass( "ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom" ); - - if ( options.navigation ) { - var current = self.element.find( "a" ).filter( options.navigationFilter ).eq( 0 ); - if ( current.length ) { - var header = current.closest( ".ui-accordion-header" ); - if ( header.length ) { - // anchor within header - self.active = header; - } else { - // anchor within content - self.active = current.closest( ".ui-accordion-content" ).prev(); - } - } - } - - self.active = self._findActive( self.active || options.active ) - .addClass( "ui-state-default ui-state-active" ) - .toggleClass( "ui-corner-all" ) - .toggleClass( "ui-corner-top" ); - self.active.next().addClass( "ui-accordion-content-active" ); - - self._createIcons(); - self.resize(); - - // ARIA - self.element.attr( "role", "tablist" ); - - self.headers - .attr( "role", "tab" ) - .bind( "keydown.accordion", function( event ) { - return self._keydown( event ); - }) - .next() - .attr( "role", "tabpanel" ); - - self.headers - .not( self.active || "" ) - .attr({ - "aria-expanded": "false", - "aria-selected": "false", - tabIndex: -1 - }) - .next() - .hide(); - - // make sure at least one header is in the tab order - if ( !self.active.length ) { - self.headers.eq( 0 ).attr( "tabIndex", 0 ); - } else { - self.active - .attr({ - "aria-expanded": "true", - "aria-selected": "true", - tabIndex: 0 - }); - } - - // only need links in tab order for Safari - if ( !$.browser.safari ) { - self.headers.find( "a" ).attr( "tabIndex", -1 ); - } - - if ( options.event ) { - self.headers.bind( options.event.split(" ").join(".accordion ") + ".accordion", function(event) { - self._clickHandler.call( self, event, this ); - event.preventDefault(); - }); - } - }, - - _createIcons: function() { - var options = this.options; - if ( options.icons ) { - $( "" ) - .addClass( "ui-icon " + options.icons.header ) - .prependTo( this.headers ); - this.active.children( ".ui-icon" ) - .toggleClass(options.icons.header) - .toggleClass(options.icons.headerSelected); - this.element.addClass( "ui-accordion-icons" ); - } - }, - - _destroyIcons: function() { - this.headers.children( ".ui-icon" ).remove(); - this.element.removeClass( "ui-accordion-icons" ); - }, - - destroy: function() { - var options = this.options; - - this.element - .removeClass( "ui-accordion ui-widget ui-helper-reset" ) - .removeAttr( "role" ); - - this.headers - .unbind( ".accordion" ) - .removeClass( "ui-accordion-header ui-accordion-disabled ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top" ) - .removeAttr( "role" ) - .removeAttr( "aria-expanded" ) - .removeAttr( "aria-selected" ) - .removeAttr( "tabIndex" ); - - this.headers.find( "a" ).removeAttr( "tabIndex" ); - this._destroyIcons(); - var contents = this.headers.next() - .css( "display", "" ) - .removeAttr( "role" ) - .removeClass( "ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-accordion-disabled ui-state-disabled" ); - if ( options.autoHeight || options.fillHeight ) { - contents.css( "height", "" ); - } - - return $.Widget.prototype.destroy.call( this ); - }, - - _setOption: function( key, value ) { - $.Widget.prototype._setOption.apply( this, arguments ); - - if ( key == "active" ) { - this.activate( value ); - } - if ( key == "icons" ) { - this._destroyIcons(); - if ( value ) { - this._createIcons(); - } - } - // #5332 - opacity doesn't cascade to positioned elements in IE - // so we need to add the disabled class to the headers and panels - if ( key == "disabled" ) { - this.headers.add(this.headers.next()) - [ value ? "addClass" : "removeClass" ]( - "ui-accordion-disabled ui-state-disabled" ); - } - }, - - _keydown: function( event ) { - if ( this.options.disabled || event.altKey || event.ctrlKey ) { - return; - } - - var keyCode = $.ui.keyCode, - length = this.headers.length, - currentIndex = this.headers.index( event.target ), - toFocus = false; - - switch ( event.keyCode ) { - case keyCode.RIGHT: - case keyCode.DOWN: - toFocus = this.headers[ ( currentIndex + 1 ) % length ]; - break; - case keyCode.LEFT: - case keyCode.UP: - toFocus = this.headers[ ( currentIndex - 1 + length ) % length ]; - break; - case keyCode.SPACE: - case keyCode.ENTER: - this._clickHandler( { target: event.target }, event.target ); - event.preventDefault(); - } - - if ( toFocus ) { - $( event.target ).attr( "tabIndex", -1 ); - $( toFocus ).attr( "tabIndex", 0 ); - toFocus.focus(); - return false; - } - - return true; - }, - - resize: function() { - var options = this.options, - maxHeight; - - if ( options.fillSpace ) { - if ( $.browser.msie ) { - var defOverflow = this.element.parent().css( "overflow" ); - this.element.parent().css( "overflow", "hidden"); - } - maxHeight = this.element.parent().height(); - if ($.browser.msie) { - this.element.parent().css( "overflow", defOverflow ); - } - - this.headers.each(function() { - maxHeight -= $( this ).outerHeight( true ); - }); - - this.headers.next() - .each(function() { - $( this ).height( Math.max( 0, maxHeight - - $( this ).innerHeight() + $( this ).height() ) ); - }) - .css( "overflow", "auto" ); - } else if ( options.autoHeight ) { - maxHeight = 0; - this.headers.next() - .each(function() { - maxHeight = Math.max( maxHeight, $( this ).height( "" ).height() ); - }) - .height( maxHeight ); - } - - return this; - }, - - activate: function( index ) { - // TODO this gets called on init, changing the option without an explicit call for that - this.options.active = index; - // call clickHandler with custom event - var active = this._findActive( index )[ 0 ]; - this._clickHandler( { target: active }, active ); - - return this; - }, - - _findActive: function( selector ) { - return selector - ? typeof selector === "number" - ? this.headers.filter( ":eq(" + selector + ")" ) - : this.headers.not( this.headers.not( selector ) ) - : selector === false - ? $( [] ) - : this.headers.filter( ":eq(0)" ); - }, - - // TODO isn't event.target enough? why the separate target argument? - _clickHandler: function( event, target ) { - var options = this.options; - if ( options.disabled ) { - return; - } - - // called only when using activate(false) to close all parts programmatically - if ( !event.target ) { - if ( !options.collapsible ) { - return; - } - this.active - .removeClass( "ui-state-active ui-corner-top" ) - .addClass( "ui-state-default ui-corner-all" ) - .children( ".ui-icon" ) - .removeClass( options.icons.headerSelected ) - .addClass( options.icons.header ); - this.active.next().addClass( "ui-accordion-content-active" ); - var toHide = this.active.next(), - data = { - options: options, - newHeader: $( [] ), - oldHeader: options.active, - newContent: $( [] ), - oldContent: toHide - }, - toShow = ( this.active = $( [] ) ); - this._toggle( toShow, toHide, data ); - return; - } - - // get the click target - var clicked = $( event.currentTarget || target ), - clickedIsActive = clicked[0] === this.active[0]; - - // TODO the option is changed, is that correct? - // TODO if it is correct, shouldn't that happen after determining that the click is valid? - options.active = options.collapsible && clickedIsActive ? - false : - this.headers.index( clicked ); - - // if animations are still active, or the active header is the target, ignore click - if ( this.running || ( !options.collapsible && clickedIsActive ) ) { - return; - } - - // find elements to show and hide - var active = this.active, - toShow = clicked.next(), - toHide = this.active.next(), - data = { - options: options, - newHeader: clickedIsActive && options.collapsible ? $([]) : clicked, - oldHeader: this.active, - newContent: clickedIsActive && options.collapsible ? $([]) : toShow, - oldContent: toHide - }, - down = this.headers.index( this.active[0] ) > this.headers.index( clicked[0] ); - - // when the call to ._toggle() comes after the class changes - // it causes a very odd bug in IE 8 (see #6720) - this.active = clickedIsActive ? $([]) : clicked; - this._toggle( toShow, toHide, data, clickedIsActive, down ); - - // switch classes - active - .removeClass( "ui-state-active ui-corner-top" ) - .addClass( "ui-state-default ui-corner-all" ) - .children( ".ui-icon" ) - .removeClass( options.icons.headerSelected ) - .addClass( options.icons.header ); - if ( !clickedIsActive ) { - clicked - .removeClass( "ui-state-default ui-corner-all" ) - .addClass( "ui-state-active ui-corner-top" ) - .children( ".ui-icon" ) - .removeClass( options.icons.header ) - .addClass( options.icons.headerSelected ); - clicked - .next() - .addClass( "ui-accordion-content-active" ); - } - - return; - }, - - _toggle: function( toShow, toHide, data, clickedIsActive, down ) { - var self = this, - options = self.options; - - self.toShow = toShow; - self.toHide = toHide; - self.data = data; - - var complete = function() { - if ( !self ) { - return; - } - return self._completed.apply( self, arguments ); - }; - - // trigger changestart event - self._trigger( "changestart", null, self.data ); - - // count elements to animate - self.running = toHide.size() === 0 ? toShow.size() : toHide.size(); - - if ( options.animated ) { - var animOptions = {}; - - if ( options.collapsible && clickedIsActive ) { - animOptions = { - toShow: $( [] ), - toHide: toHide, - complete: complete, - down: down, - autoHeight: options.autoHeight || options.fillSpace - }; - } else { - animOptions = { - toShow: toShow, - toHide: toHide, - complete: complete, - down: down, - autoHeight: options.autoHeight || options.fillSpace - }; - } - - if ( !options.proxied ) { - options.proxied = options.animated; - } - - if ( !options.proxiedDuration ) { - options.proxiedDuration = options.duration; - } - - options.animated = $.isFunction( options.proxied ) ? - options.proxied( animOptions ) : - options.proxied; - - options.duration = $.isFunction( options.proxiedDuration ) ? - options.proxiedDuration( animOptions ) : - options.proxiedDuration; - - var animations = $.ui.accordion.animations, - duration = options.duration, - easing = options.animated; - - if ( easing && !animations[ easing ] && !$.easing[ easing ] ) { - easing = "slide"; - } - if ( !animations[ easing ] ) { - animations[ easing ] = function( options ) { - this.slide( options, { - easing: easing, - duration: duration || 700 - }); - }; - } - - animations[ easing ]( animOptions ); - } else { - if ( options.collapsible && clickedIsActive ) { - toShow.toggle(); - } else { - toHide.hide(); - toShow.show(); - } - - complete( true ); - } - - // TODO assert that the blur and focus triggers are really necessary, remove otherwise - toHide.prev() - .attr({ - "aria-expanded": "false", - "aria-selected": "false", - tabIndex: -1 - }) - .blur(); - toShow.prev() - .attr({ - "aria-expanded": "true", - "aria-selected": "true", - tabIndex: 0 - }) - .focus(); - }, - - _completed: function( cancel ) { - this.running = cancel ? 0 : --this.running; - if ( this.running ) { - return; - } - - if ( this.options.clearStyle ) { - this.toShow.add( this.toHide ).css({ - height: "", - overflow: "" - }); - } - - // other classes are removed before the animation; this one needs to stay until completed - this.toHide.removeClass( "ui-accordion-content-active" ); - // Work around for rendering bug in IE (#5421) - if ( this.toHide.length ) { - this.toHide.parent()[0].className = this.toHide.parent()[0].className; - } - - this._trigger( "change", null, this.data ); - } -}); - -$.extend( $.ui.accordion, { - version: "1.8.23", - animations: { - slide: function( options, additions ) { - options = $.extend({ - easing: "swing", - duration: 300 - }, options, additions ); - if ( !options.toHide.size() ) { - options.toShow.animate({ - height: "show", - paddingTop: "show", - paddingBottom: "show" - }, options ); - return; - } - if ( !options.toShow.size() ) { - options.toHide.animate({ - height: "hide", - paddingTop: "hide", - paddingBottom: "hide" - }, options ); - return; - } - var overflow = options.toShow.css( "overflow" ), - percentDone = 0, - showProps = {}, - hideProps = {}, - fxAttrs = [ "height", "paddingTop", "paddingBottom" ], - originalWidth; - // fix width before calculating height of hidden element - var s = options.toShow; - originalWidth = s[0].style.width; - s.width( s.parent().width() - - parseFloat( s.css( "paddingLeft" ) ) - - parseFloat( s.css( "paddingRight" ) ) - - ( parseFloat( s.css( "borderLeftWidth" ) ) || 0 ) - - ( parseFloat( s.css( "borderRightWidth" ) ) || 0 ) ); - - $.each( fxAttrs, function( i, prop ) { - hideProps[ prop ] = "hide"; - - var parts = ( "" + $.css( options.toShow[0], prop ) ).match( /^([\d+-.]+)(.*)$/ ); - showProps[ prop ] = { - value: parts[ 1 ], - unit: parts[ 2 ] || "px" - }; - }); - options.toShow.css({ height: 0, overflow: "hidden" }).show(); - options.toHide - .filter( ":hidden" ) - .each( options.complete ) - .end() - .filter( ":visible" ) - .animate( hideProps, { - step: function( now, settings ) { - // only calculate the percent when animating height - // IE gets very inconsistent results when animating elements - // with small values, which is common for padding - if ( settings.prop == "height" ) { - percentDone = ( settings.end - settings.start === 0 ) ? 0 : - ( settings.now - settings.start ) / ( settings.end - settings.start ); - } - - options.toShow[ 0 ].style[ settings.prop ] = - ( percentDone * showProps[ settings.prop ].value ) - + showProps[ settings.prop ].unit; - }, - duration: options.duration, - easing: options.easing, - complete: function() { - if ( !options.autoHeight ) { - options.toShow.css( "height", "" ); - } - options.toShow.css({ - width: originalWidth, - overflow: overflow - }); - options.complete(); - } - }); - }, - bounceslide: function( options ) { - this.slide( options, { - easing: options.down ? "easeOutBounce" : "swing", - duration: options.down ? 1000 : 200 - }); - } - } -}); - -})( jQuery ); -/*! - * jQuery UI Autocomplete 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Autocomplete - * - * Depends: - * jquery.ui.core.js - * jquery.ui.widget.js - * jquery.ui.position.js - */ -(function( $, undefined ) { - -// used to prevent race conditions with remote data sources -var requestIndex = 0; - -$.widget( "ui.autocomplete", { - options: { - appendTo: "body", - autoFocus: false, - delay: 300, - minLength: 1, - position: { - my: "left top", - at: "left bottom", - collision: "none" - }, - source: null - }, - - pending: 0, - - _create: function() { - var self = this, - doc = this.element[ 0 ].ownerDocument, - suppressKeyPress; - this.isMultiLine = this.element.is( "textarea" ); - - this.element - .addClass( "ui-autocomplete-input" ) - .attr( "autocomplete", "off" ) - // TODO verify these actually work as intended - .attr({ - role: "textbox", - "aria-autocomplete": "list", - "aria-haspopup": "true" - }) - .bind( "keydown.autocomplete", function( event ) { - if ( self.options.disabled || self.element.propAttr( "readOnly" ) ) { - return; - } - - suppressKeyPress = false; - var keyCode = $.ui.keyCode; - switch( event.keyCode ) { - case keyCode.PAGE_UP: - self._move( "previousPage", event ); - break; - case keyCode.PAGE_DOWN: - self._move( "nextPage", event ); - break; - case keyCode.UP: - self._keyEvent( "previous", event ); - break; - case keyCode.DOWN: - self._keyEvent( "next", event ); - break; - case keyCode.ENTER: - case keyCode.NUMPAD_ENTER: - // when menu is open and has focus - if ( self.menu.active ) { - // #6055 - Opera still allows the keypress to occur - // which causes forms to submit - suppressKeyPress = true; - event.preventDefault(); - } - //passthrough - ENTER and TAB both select the current element - case keyCode.TAB: - if ( !self.menu.active ) { - return; - } - self.menu.select( event ); - break; - case keyCode.ESCAPE: - self.element.val( self.term ); - self.close( event ); - break; - default: - // keypress is triggered before the input value is changed - clearTimeout( self.searching ); - self.searching = setTimeout(function() { - // only search if the value has changed - if ( self.term != self.element.val() ) { - self.selectedItem = null; - self.search( null, event ); - } - }, self.options.delay ); - break; - } - }) - .bind( "keypress.autocomplete", function( event ) { - if ( suppressKeyPress ) { - suppressKeyPress = false; - event.preventDefault(); - } - }) - .bind( "focus.autocomplete", function() { - if ( self.options.disabled ) { - return; - } - - self.selectedItem = null; - self.previous = self.element.val(); - }) - .bind( "blur.autocomplete", function( event ) { - if ( self.options.disabled ) { - return; - } - - clearTimeout( self.searching ); - // clicks on the menu (or a button to trigger a search) will cause a blur event - self.closing = setTimeout(function() { - self.close( event ); - self._change( event ); - }, 150 ); - }); - this._initSource(); - this.menu = $( "
      " ) - .addClass( "ui-autocomplete" ) - .appendTo( $( this.options.appendTo || "body", doc )[0] ) - // prevent the close-on-blur in case of a "slow" click on the menu (long mousedown) - .mousedown(function( event ) { - // clicking on the scrollbar causes focus to shift to the body - // but we can't detect a mouseup or a click immediately afterward - // so we have to track the next mousedown and close the menu if - // the user clicks somewhere outside of the autocomplete - var menuElement = self.menu.element[ 0 ]; - if ( !$( event.target ).closest( ".ui-menu-item" ).length ) { - setTimeout(function() { - $( document ).one( 'mousedown', function( event ) { - if ( event.target !== self.element[ 0 ] && - event.target !== menuElement && - !$.ui.contains( menuElement, event.target ) ) { - self.close(); - } - }); - }, 1 ); - } - - // use another timeout to make sure the blur-event-handler on the input was already triggered - setTimeout(function() { - clearTimeout( self.closing ); - }, 13); - }) - .menu({ - focus: function( event, ui ) { - var item = ui.item.data( "item.autocomplete" ); - if ( false !== self._trigger( "focus", event, { item: item } ) ) { - // use value to match what will end up in the input, if it was a key event - if ( /^key/.test(event.originalEvent.type) ) { - self.element.val( item.value ); - } - } - }, - selected: function( event, ui ) { - var item = ui.item.data( "item.autocomplete" ), - previous = self.previous; - - // only trigger when focus was lost (click on menu) - if ( self.element[0] !== doc.activeElement ) { - self.element.focus(); - self.previous = previous; - // #6109 - IE triggers two focus events and the second - // is asynchronous, so we need to reset the previous - // term synchronously and asynchronously :-( - setTimeout(function() { - self.previous = previous; - self.selectedItem = item; - }, 1); - } - - if ( false !== self._trigger( "select", event, { item: item } ) ) { - self.element.val( item.value ); - } - // reset the term after the select event - // this allows custom select handling to work properly - self.term = self.element.val(); - - self.close( event ); - self.selectedItem = item; - }, - blur: function( event, ui ) { - // don't set the value of the text field if it's already correct - // this prevents moving the cursor unnecessarily - if ( self.menu.element.is(":visible") && - ( self.element.val() !== self.term ) ) { - self.element.val( self.term ); - } - } - }) - .zIndex( this.element.zIndex() + 1 ) - // workaround for jQuery bug #5781 http://dev.jquery.com/ticket/5781 - .css({ top: 0, left: 0 }) - .hide() - .data( "menu" ); - if ( $.fn.bgiframe ) { - this.menu.element.bgiframe(); - } - // turning off autocomplete prevents the browser from remembering the - // value when navigating through history, so we re-enable autocomplete - // if the page is unloaded before the widget is destroyed. #7790 - self.beforeunloadHandler = function() { - self.element.removeAttr( "autocomplete" ); - }; - $( window ).bind( "beforeunload", self.beforeunloadHandler ); - }, - - destroy: function() { - this.element - .removeClass( "ui-autocomplete-input" ) - .removeAttr( "autocomplete" ) - .removeAttr( "role" ) - .removeAttr( "aria-autocomplete" ) - .removeAttr( "aria-haspopup" ); - this.menu.element.remove(); - $( window ).unbind( "beforeunload", this.beforeunloadHandler ); - $.Widget.prototype.destroy.call( this ); - }, - - _setOption: function( key, value ) { - $.Widget.prototype._setOption.apply( this, arguments ); - if ( key === "source" ) { - this._initSource(); - } - if ( key === "appendTo" ) { - this.menu.element.appendTo( $( value || "body", this.element[0].ownerDocument )[0] ) - } - if ( key === "disabled" && value && this.xhr ) { - this.xhr.abort(); - } - }, - - _initSource: function() { - var self = this, - array, - url; - if ( $.isArray(this.options.source) ) { - array = this.options.source; - this.source = function( request, response ) { - response( $.ui.autocomplete.filter(array, request.term) ); - }; - } else if ( typeof this.options.source === "string" ) { - url = this.options.source; - this.source = function( request, response ) { - if ( self.xhr ) { - self.xhr.abort(); - } - self.xhr = $.ajax({ - url: url, - data: request, - dataType: "json", - success: function( data, status ) { - response( data ); - }, - error: function() { - response( [] ); - } - }); - }; - } else { - this.source = this.options.source; - } - }, - - search: function( value, event ) { - value = value != null ? value : this.element.val(); - - // always save the actual value, not the one passed as an argument - this.term = this.element.val(); - - if ( value.length < this.options.minLength ) { - return this.close( event ); - } - - clearTimeout( this.closing ); - if ( this._trigger( "search", event ) === false ) { - return; - } - - return this._search( value ); - }, - - _search: function( value ) { - this.pending++; - this.element.addClass( "ui-autocomplete-loading" ); - - this.source( { term: value }, this._response() ); - }, - - _response: function() { - var that = this, - index = ++requestIndex; - - return function( content ) { - if ( index === requestIndex ) { - that.__response( content ); - } - - that.pending--; - if ( !that.pending ) { - that.element.removeClass( "ui-autocomplete-loading" ); - } - }; - }, - - __response: function( content ) { - if ( !this.options.disabled && content && content.length ) { - content = this._normalize( content ); - this._suggest( content ); - this._trigger( "open" ); - } else { - this.close(); - } - }, - - close: function( event ) { - clearTimeout( this.closing ); - if ( this.menu.element.is(":visible") ) { - this.menu.element.hide(); - this.menu.deactivate(); - this._trigger( "close", event ); - } - }, - - _change: function( event ) { - if ( this.previous !== this.element.val() ) { - this._trigger( "change", event, { item: this.selectedItem } ); - } - }, - - _normalize: function( items ) { - // assume all items have the right format when the first item is complete - if ( items.length && items[0].label && items[0].value ) { - return items; - } - return $.map( items, function(item) { - if ( typeof item === "string" ) { - return { - label: item, - value: item - }; - } - return $.extend({ - label: item.label || item.value, - value: item.value || item.label - }, item ); - }); - }, - - _suggest: function( items ) { - var ul = this.menu.element - .empty() - .zIndex( this.element.zIndex() + 1 ); - this._renderMenu( ul, items ); - // TODO refresh should check if the active item is still in the dom, removing the need for a manual deactivate - this.menu.deactivate(); - this.menu.refresh(); - - // size and position menu - ul.show(); - this._resizeMenu(); - ul.position( $.extend({ - of: this.element - }, this.options.position )); - - if ( this.options.autoFocus ) { - this.menu.next( new $.Event("mouseover") ); - } - }, - - _resizeMenu: function() { - var ul = this.menu.element; - ul.outerWidth( Math.max( - // Firefox wraps long text (possibly a rounding bug) - // so we add 1px to avoid the wrapping (#7513) - ul.width( "" ).outerWidth() + 1, - this.element.outerWidth() - ) ); - }, - - _renderMenu: function( ul, items ) { - var self = this; - $.each( items, function( index, item ) { - self._renderItem( ul, item ); - }); - }, - - _renderItem: function( ul, item) { - return $( "
    • " ) - .data( "item.autocomplete", item ) - .append( $( "
      " ).text( item.label ) ) - .appendTo( ul ); - }, - - _move: function( direction, event ) { - if ( !this.menu.element.is(":visible") ) { - this.search( null, event ); - return; - } - if ( this.menu.first() && /^previous/.test(direction) || - this.menu.last() && /^next/.test(direction) ) { - this.element.val( this.term ); - this.menu.deactivate(); - return; - } - this.menu[ direction ]( event ); - }, - - widget: function() { - return this.menu.element; - }, - _keyEvent: function( keyEvent, event ) { - if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) { - this._move( keyEvent, event ); - - // prevents moving cursor to beginning/end of the text field in some browsers - event.preventDefault(); - } - } -}); - -$.extend( $.ui.autocomplete, { - escapeRegex: function( value ) { - return value.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); - }, - filter: function(array, term) { - var matcher = new RegExp( $.ui.autocomplete.escapeRegex(term), "i" ); - return $.grep( array, function(value) { - return matcher.test( value.label || value.value || value ); - }); - } -}); - -}( jQuery )); - -/* - * jQuery UI Menu (not officially released) - * - * This widget isn't yet finished and the API is subject to change. We plan to finish - * it for the next release. You're welcome to give it a try anyway and give us feedback, - * as long as you're okay with migrating your code later on. We can help with that, too. - * - * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Menu - * - * Depends: - * jquery.ui.core.js - * jquery.ui.widget.js - */ -(function($) { - -$.widget("ui.menu", { - _create: function() { - var self = this; - this.element - .addClass("ui-menu ui-widget ui-widget-content ui-corner-all") - .attr({ - role: "listbox", - "aria-activedescendant": "ui-active-menuitem" - }) - .click(function( event ) { - if ( !$( event.target ).closest( ".ui-menu-item a" ).length ) { - return; - } - // temporary - event.preventDefault(); - self.select( event ); - }); - this.refresh(); - }, - - refresh: function() { - var self = this; - - // don't refresh list items that are already adapted - var items = this.element.children("li:not(.ui-menu-item):has(a)") - .addClass("ui-menu-item") - .attr("role", "menuitem"); - - items.children("a") - .addClass("ui-corner-all") - .attr("tabindex", -1) - // mouseenter doesn't work with event delegation - .mouseenter(function( event ) { - self.activate( event, $(this).parent() ); - }) - .mouseleave(function() { - self.deactivate(); - }); - }, - - activate: function( event, item ) { - this.deactivate(); - if (this.hasScroll()) { - var offset = item.offset().top - this.element.offset().top, - scroll = this.element.scrollTop(), - elementHeight = this.element.height(); - if (offset < 0) { - this.element.scrollTop( scroll + offset); - } else if (offset >= elementHeight) { - this.element.scrollTop( scroll + offset - elementHeight + item.height()); - } - } - this.active = item.eq(0) - .children("a") - .addClass("ui-state-hover") - .attr("id", "ui-active-menuitem") - .end(); - this._trigger("focus", event, { item: item }); - }, - - deactivate: function() { - if (!this.active) { return; } - - this.active.children("a") - .removeClass("ui-state-hover") - .removeAttr("id"); - this._trigger("blur"); - this.active = null; - }, - - next: function(event) { - this.move("next", ".ui-menu-item:first", event); - }, - - previous: function(event) { - this.move("prev", ".ui-menu-item:last", event); - }, - - first: function() { - return this.active && !this.active.prevAll(".ui-menu-item").length; - }, - - last: function() { - return this.active && !this.active.nextAll(".ui-menu-item").length; - }, - - move: function(direction, edge, event) { - if (!this.active) { - this.activate(event, this.element.children(edge)); - return; - } - var next = this.active[direction + "All"](".ui-menu-item").eq(0); - if (next.length) { - this.activate(event, next); - } else { - this.activate(event, this.element.children(edge)); - } - }, - - // TODO merge with previousPage - nextPage: function(event) { - if (this.hasScroll()) { - // TODO merge with no-scroll-else - if (!this.active || this.last()) { - this.activate(event, this.element.children(".ui-menu-item:first")); - return; - } - var base = this.active.offset().top, - height = this.element.height(), - result = this.element.children(".ui-menu-item").filter(function() { - var close = $(this).offset().top - base - height + $(this).height(); - // TODO improve approximation - return close < 10 && close > -10; - }); - - // TODO try to catch this earlier when scrollTop indicates the last page anyway - if (!result.length) { - result = this.element.children(".ui-menu-item:last"); - } - this.activate(event, result); - } else { - this.activate(event, this.element.children(".ui-menu-item") - .filter(!this.active || this.last() ? ":first" : ":last")); - } - }, - - // TODO merge with nextPage - previousPage: function(event) { - if (this.hasScroll()) { - // TODO merge with no-scroll-else - if (!this.active || this.first()) { - this.activate(event, this.element.children(".ui-menu-item:last")); - return; - } - - var base = this.active.offset().top, - height = this.element.height(), - result = this.element.children(".ui-menu-item").filter(function() { - var close = $(this).offset().top - base + height - $(this).height(); - // TODO improve approximation - return close < 10 && close > -10; - }); - - // TODO try to catch this earlier when scrollTop indicates the last page anyway - if (!result.length) { - result = this.element.children(".ui-menu-item:first"); - } - this.activate(event, result); - } else { - this.activate(event, this.element.children(".ui-menu-item") - .filter(!this.active || this.first() ? ":last" : ":first")); - } - }, - - hasScroll: function() { - return this.element.height() < this.element[ $.fn.prop ? "prop" : "attr" ]("scrollHeight"); - }, - - select: function( event ) { - this._trigger("selected", event, { item: this.active }); - } -}); - -}(jQuery)); -/*! - * jQuery UI Button 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Button - * - * Depends: - * jquery.ui.core.js - * jquery.ui.widget.js - */ -(function( $, undefined ) { - -var lastActive, startXPos, startYPos, clickDragged, - baseClasses = "ui-button ui-widget ui-state-default ui-corner-all", - stateClasses = "ui-state-hover ui-state-active ", - typeClasses = "ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only", - formResetHandler = function() { - var buttons = $( this ).find( ":ui-button" ); - setTimeout(function() { - buttons.button( "refresh" ); - }, 1 ); - }, - radioGroup = function( radio ) { - var name = radio.name, - form = radio.form, - radios = $( [] ); - if ( name ) { - if ( form ) { - radios = $( form ).find( "[name='" + name + "']" ); - } else { - radios = $( "[name='" + name + "']", radio.ownerDocument ) - .filter(function() { - return !this.form; - }); - } - } - return radios; - }; - -$.widget( "ui.button", { - options: { - disabled: null, - text: true, - label: null, - icons: { - primary: null, - secondary: null - } - }, - _create: function() { - this.element.closest( "form" ) - .unbind( "reset.button" ) - .bind( "reset.button", formResetHandler ); - - if ( typeof this.options.disabled !== "boolean" ) { - this.options.disabled = !!this.element.propAttr( "disabled" ); - } else { - this.element.propAttr( "disabled", this.options.disabled ); - } - - this._determineButtonType(); - this.hasTitle = !!this.buttonElement.attr( "title" ); - - var self = this, - options = this.options, - toggleButton = this.type === "checkbox" || this.type === "radio", - hoverClass = "ui-state-hover" + ( !toggleButton ? " ui-state-active" : "" ), - focusClass = "ui-state-focus"; - - if ( options.label === null ) { - options.label = this.buttonElement.html(); - } - - this.buttonElement - .addClass( baseClasses ) - .attr( "role", "button" ) - .bind( "mouseenter.button", function() { - if ( options.disabled ) { - return; - } - $( this ).addClass( "ui-state-hover" ); - if ( this === lastActive ) { - $( this ).addClass( "ui-state-active" ); - } - }) - .bind( "mouseleave.button", function() { - if ( options.disabled ) { - return; - } - $( this ).removeClass( hoverClass ); - }) - .bind( "click.button", function( event ) { - if ( options.disabled ) { - event.preventDefault(); - event.stopImmediatePropagation(); - } - }); - - this.element - .bind( "focus.button", function() { - // no need to check disabled, focus won't be triggered anyway - self.buttonElement.addClass( focusClass ); - }) - .bind( "blur.button", function() { - self.buttonElement.removeClass( focusClass ); - }); - - if ( toggleButton ) { - this.element.bind( "change.button", function() { - if ( clickDragged ) { - return; - } - self.refresh(); - }); - // if mouse moves between mousedown and mouseup (drag) set clickDragged flag - // prevents issue where button state changes but checkbox/radio checked state - // does not in Firefox (see ticket #6970) - this.buttonElement - .bind( "mousedown.button", function( event ) { - if ( options.disabled ) { - return; - } - clickDragged = false; - startXPos = event.pageX; - startYPos = event.pageY; - }) - .bind( "mouseup.button", function( event ) { - if ( options.disabled ) { - return; - } - if ( startXPos !== event.pageX || startYPos !== event.pageY ) { - clickDragged = true; - } - }); - } - - if ( this.type === "checkbox" ) { - this.buttonElement.bind( "click.button", function() { - if ( options.disabled || clickDragged ) { - return false; - } - $( this ).toggleClass( "ui-state-active" ); - self.buttonElement.attr( "aria-pressed", self.element[0].checked ); - }); - } else if ( this.type === "radio" ) { - this.buttonElement.bind( "click.button", function() { - if ( options.disabled || clickDragged ) { - return false; - } - $( this ).addClass( "ui-state-active" ); - self.buttonElement.attr( "aria-pressed", "true" ); - - var radio = self.element[ 0 ]; - radioGroup( radio ) - .not( radio ) - .map(function() { - return $( this ).button( "widget" )[ 0 ]; - }) - .removeClass( "ui-state-active" ) - .attr( "aria-pressed", "false" ); - }); - } else { - this.buttonElement - .bind( "mousedown.button", function() { - if ( options.disabled ) { - return false; - } - $( this ).addClass( "ui-state-active" ); - lastActive = this; - $( document ).one( "mouseup", function() { - lastActive = null; - }); - }) - .bind( "mouseup.button", function() { - if ( options.disabled ) { - return false; - } - $( this ).removeClass( "ui-state-active" ); - }) - .bind( "keydown.button", function(event) { - if ( options.disabled ) { - return false; - } - if ( event.keyCode == $.ui.keyCode.SPACE || event.keyCode == $.ui.keyCode.ENTER ) { - $( this ).addClass( "ui-state-active" ); - } - }) - .bind( "keyup.button", function() { - $( this ).removeClass( "ui-state-active" ); - }); - - if ( this.buttonElement.is("a") ) { - this.buttonElement.keyup(function(event) { - if ( event.keyCode === $.ui.keyCode.SPACE ) { - // TODO pass through original event correctly (just as 2nd argument doesn't work) - $( this ).click(); - } - }); - } - } - - // TODO: pull out $.Widget's handling for the disabled option into - // $.Widget.prototype._setOptionDisabled so it's easy to proxy and can - // be overridden by individual plugins - this._setOption( "disabled", options.disabled ); - this._resetButton(); - }, - - _determineButtonType: function() { - - if ( this.element.is(":checkbox") ) { - this.type = "checkbox"; - } else if ( this.element.is(":radio") ) { - this.type = "radio"; - } else if ( this.element.is("input") ) { - this.type = "input"; - } else { - this.type = "button"; - } - - if ( this.type === "checkbox" || this.type === "radio" ) { - // we don't search against the document in case the element - // is disconnected from the DOM - var ancestor = this.element.parents().filter(":last"), - labelSelector = "label[for='" + this.element.attr("id") + "']"; - this.buttonElement = ancestor.find( labelSelector ); - if ( !this.buttonElement.length ) { - ancestor = ancestor.length ? ancestor.siblings() : this.element.siblings(); - this.buttonElement = ancestor.filter( labelSelector ); - if ( !this.buttonElement.length ) { - this.buttonElement = ancestor.find( labelSelector ); - } - } - this.element.addClass( "ui-helper-hidden-accessible" ); - - var checked = this.element.is( ":checked" ); - if ( checked ) { - this.buttonElement.addClass( "ui-state-active" ); - } - this.buttonElement.attr( "aria-pressed", checked ); - } else { - this.buttonElement = this.element; - } - }, - - widget: function() { - return this.buttonElement; - }, - - destroy: function() { - this.element - .removeClass( "ui-helper-hidden-accessible" ); - this.buttonElement - .removeClass( baseClasses + " " + stateClasses + " " + typeClasses ) - .removeAttr( "role" ) - .removeAttr( "aria-pressed" ) - .html( this.buttonElement.find(".ui-button-text").html() ); - - if ( !this.hasTitle ) { - this.buttonElement.removeAttr( "title" ); - } - - $.Widget.prototype.destroy.call( this ); - }, - - _setOption: function( key, value ) { - $.Widget.prototype._setOption.apply( this, arguments ); - if ( key === "disabled" ) { - if ( value ) { - this.element.propAttr( "disabled", true ); - } else { - this.element.propAttr( "disabled", false ); - } - return; - } - this._resetButton(); - }, - - refresh: function() { - var isDisabled = this.element.is( ":disabled" ); - if ( isDisabled !== this.options.disabled ) { - this._setOption( "disabled", isDisabled ); - } - if ( this.type === "radio" ) { - radioGroup( this.element[0] ).each(function() { - if ( $( this ).is( ":checked" ) ) { - $( this ).button( "widget" ) - .addClass( "ui-state-active" ) - .attr( "aria-pressed", "true" ); - } else { - $( this ).button( "widget" ) - .removeClass( "ui-state-active" ) - .attr( "aria-pressed", "false" ); - } - }); - } else if ( this.type === "checkbox" ) { - if ( this.element.is( ":checked" ) ) { - this.buttonElement - .addClass( "ui-state-active" ) - .attr( "aria-pressed", "true" ); - } else { - this.buttonElement - .removeClass( "ui-state-active" ) - .attr( "aria-pressed", "false" ); - } - } - }, - - _resetButton: function() { - if ( this.type === "input" ) { - if ( this.options.label ) { - this.element.val( this.options.label ); - } - return; - } - var buttonElement = this.buttonElement.removeClass( typeClasses ), - buttonText = $( "", this.element[0].ownerDocument ) - .addClass( "ui-button-text" ) - .html( this.options.label ) - .appendTo( buttonElement.empty() ) - .text(), - icons = this.options.icons, - multipleIcons = icons.primary && icons.secondary, - buttonClasses = []; - - if ( icons.primary || icons.secondary ) { - if ( this.options.text ) { - buttonClasses.push( "ui-button-text-icon" + ( multipleIcons ? "s" : ( icons.primary ? "-primary" : "-secondary" ) ) ); - } - - if ( icons.primary ) { - buttonElement.prepend( "" ); - } - - if ( icons.secondary ) { - buttonElement.append( "" ); - } - - if ( !this.options.text ) { - buttonClasses.push( multipleIcons ? "ui-button-icons-only" : "ui-button-icon-only" ); - - if ( !this.hasTitle ) { - buttonElement.attr( "title", buttonText ); - } - } - } else { - buttonClasses.push( "ui-button-text-only" ); - } - buttonElement.addClass( buttonClasses.join( " " ) ); - } -}); - -$.widget( "ui.buttonset", { - options: { - items: ":button, :submit, :reset, :checkbox, :radio, a, :data(button)" - }, - - _create: function() { - this.element.addClass( "ui-buttonset" ); - }, - - _init: function() { - this.refresh(); - }, - - _setOption: function( key, value ) { - if ( key === "disabled" ) { - this.buttons.button( "option", key, value ); - } - - $.Widget.prototype._setOption.apply( this, arguments ); - }, - - refresh: function() { - var rtl = this.element.css( "direction" ) === "rtl"; - - this.buttons = this.element.find( this.options.items ) - .filter( ":ui-button" ) - .button( "refresh" ) - .end() - .not( ":ui-button" ) - .button() - .end() - .map(function() { - return $( this ).button( "widget" )[ 0 ]; - }) - .removeClass( "ui-corner-all ui-corner-left ui-corner-right" ) - .filter( ":first" ) - .addClass( rtl ? "ui-corner-right" : "ui-corner-left" ) - .end() - .filter( ":last" ) - .addClass( rtl ? "ui-corner-left" : "ui-corner-right" ) - .end() - .end(); - }, - - destroy: function() { - this.element.removeClass( "ui-buttonset" ); - this.buttons - .map(function() { - return $( this ).button( "widget" )[ 0 ]; - }) - .removeClass( "ui-corner-left ui-corner-right" ) - .end() - .button( "destroy" ); - - $.Widget.prototype.destroy.call( this ); - } -}); - -}( jQuery ) ); -/*! - * jQuery UI Dialog 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Dialog - * - * Depends: - * jquery.ui.core.js - * jquery.ui.widget.js - * jquery.ui.button.js - * jquery.ui.draggable.js - * jquery.ui.mouse.js - * jquery.ui.position.js - * jquery.ui.resizable.js - */ -(function( $, undefined ) { - -var uiDialogClasses = - 'ui-dialog ' + - 'ui-widget ' + - 'ui-widget-content ' + - 'ui-corner-all ', - sizeRelatedOptions = { - buttons: true, - height: true, - maxHeight: true, - maxWidth: true, - minHeight: true, - minWidth: true, - width: true - }, - resizableRelatedOptions = { - maxHeight: true, - maxWidth: true, - minHeight: true, - minWidth: true - }; - -$.widget("ui.dialog", { - options: { - autoOpen: true, - buttons: {}, - closeOnEscape: true, - closeText: 'close', - dialogClass: '', - draggable: true, - hide: null, - height: 'auto', - maxHeight: false, - maxWidth: false, - minHeight: 150, - minWidth: 150, - modal: false, - position: { - my: 'center', - at: 'center', - collision: 'fit', - // ensure that the titlebar is never outside the document - using: function(pos) { - var topOffset = $(this).css(pos).offset().top; - if (topOffset < 0) { - $(this).css('top', pos.top - topOffset); - } - } - }, - resizable: true, - show: null, - stack: true, - title: '', - width: 300, - zIndex: 1000 - }, - - _create: function() { - this.originalTitle = this.element.attr('title'); - // #5742 - .attr() might return a DOMElement - if ( typeof this.originalTitle !== "string" ) { - this.originalTitle = ""; - } - - this.options.title = this.options.title || this.originalTitle; - var self = this, - options = self.options, - - title = options.title || ' ', - titleId = $.ui.dialog.getTitleId(self.element), - - uiDialog = (self.uiDialog = $('
      ')) - .appendTo(document.body) - .hide() - .addClass(uiDialogClasses + options.dialogClass) - .css({ - zIndex: options.zIndex - }) - // setting tabIndex makes the div focusable - // setting outline to 0 prevents a border on focus in Mozilla - .attr('tabIndex', -1).css('outline', 0).keydown(function(event) { - if (options.closeOnEscape && !event.isDefaultPrevented() && event.keyCode && - event.keyCode === $.ui.keyCode.ESCAPE) { - - self.close(event); - event.preventDefault(); - } - }) - .attr({ - role: 'dialog', - 'aria-labelledby': titleId - }) - .mousedown(function(event) { - self.moveToTop(false, event); - }), - - uiDialogContent = self.element - .show() - .removeAttr('title') - .addClass( - 'ui-dialog-content ' + - 'ui-widget-content') - .appendTo(uiDialog), - - uiDialogTitlebar = (self.uiDialogTitlebar = $('
      ')) - .addClass( - 'ui-dialog-titlebar ' + - 'ui-widget-header ' + - 'ui-corner-all ' + - 'ui-helper-clearfix' - ) - .prependTo(uiDialog), - - uiDialogTitlebarClose = $('') - .addClass( - 'ui-dialog-titlebar-close ' + - 'ui-corner-all' - ) - .attr('role', 'button') - .hover( - function() { - uiDialogTitlebarClose.addClass('ui-state-hover'); - }, - function() { - uiDialogTitlebarClose.removeClass('ui-state-hover'); - } - ) - .focus(function() { - uiDialogTitlebarClose.addClass('ui-state-focus'); - }) - .blur(function() { - uiDialogTitlebarClose.removeClass('ui-state-focus'); - }) - .click(function(event) { - self.close(event); - return false; - }) - .appendTo(uiDialogTitlebar), - - uiDialogTitlebarCloseText = (self.uiDialogTitlebarCloseText = $('')) - .addClass( - 'ui-icon ' + - 'ui-icon-closethick' - ) - .text(options.closeText) - .appendTo(uiDialogTitlebarClose), - - uiDialogTitle = $('') - .addClass('ui-dialog-title') - .attr('id', titleId) - .html(title) - .prependTo(uiDialogTitlebar); - - //handling of deprecated beforeclose (vs beforeClose) option - //Ticket #4669 http://dev.jqueryui.com/ticket/4669 - //TODO: remove in 1.9pre - if ($.isFunction(options.beforeclose) && !$.isFunction(options.beforeClose)) { - options.beforeClose = options.beforeclose; - } - - uiDialogTitlebar.find("*").add(uiDialogTitlebar).disableSelection(); - - if (options.draggable && $.fn.draggable) { - self._makeDraggable(); - } - if (options.resizable && $.fn.resizable) { - self._makeResizable(); - } - - self._createButtons(options.buttons); - self._isOpen = false; - - if ($.fn.bgiframe) { - uiDialog.bgiframe(); - } - }, - - _init: function() { - if ( this.options.autoOpen ) { - this.open(); - } - }, - - destroy: function() { - var self = this; - - if (self.overlay) { - self.overlay.destroy(); - } - self.uiDialog.hide(); - self.element - .unbind('.dialog') - .removeData('dialog') - .removeClass('ui-dialog-content ui-widget-content') - .hide().appendTo('body'); - self.uiDialog.remove(); - - if (self.originalTitle) { - self.element.attr('title', self.originalTitle); - } - - return self; - }, - - widget: function() { - return this.uiDialog; - }, - - close: function(event) { - var self = this, - maxZ, thisZ; - - if (false === self._trigger('beforeClose', event)) { - return; - } - - if (self.overlay) { - self.overlay.destroy(); - } - self.uiDialog.unbind('keypress.ui-dialog'); - - self._isOpen = false; - - if (self.options.hide) { - self.uiDialog.hide(self.options.hide, function() { - self._trigger('close', event); - }); - } else { - self.uiDialog.hide(); - self._trigger('close', event); - } - - $.ui.dialog.overlay.resize(); - - // adjust the maxZ to allow other modal dialogs to continue to work (see #4309) - if (self.options.modal) { - maxZ = 0; - $('.ui-dialog').each(function() { - if (this !== self.uiDialog[0]) { - thisZ = $(this).css('z-index'); - if(!isNaN(thisZ)) { - maxZ = Math.max(maxZ, thisZ); - } - } - }); - $.ui.dialog.maxZ = maxZ; - } - - return self; - }, - - isOpen: function() { - return this._isOpen; - }, - - // the force parameter allows us to move modal dialogs to their correct - // position on open - moveToTop: function(force, event) { - var self = this, - options = self.options, - saveScroll; - - if ((options.modal && !force) || - (!options.stack && !options.modal)) { - return self._trigger('focus', event); - } - - if (options.zIndex > $.ui.dialog.maxZ) { - $.ui.dialog.maxZ = options.zIndex; - } - if (self.overlay) { - $.ui.dialog.maxZ += 1; - self.overlay.$el.css('z-index', $.ui.dialog.overlay.maxZ = $.ui.dialog.maxZ); - } - - //Save and then restore scroll since Opera 9.5+ resets when parent z-Index is changed. - // http://ui.jquery.com/bugs/ticket/3193 - saveScroll = { scrollTop: self.element.scrollTop(), scrollLeft: self.element.scrollLeft() }; - $.ui.dialog.maxZ += 1; - self.uiDialog.css('z-index', $.ui.dialog.maxZ); - self.element.attr(saveScroll); - self._trigger('focus', event); - - return self; - }, - - open: function() { - if (this._isOpen) { return; } - - var self = this, - options = self.options, - uiDialog = self.uiDialog; - - self.overlay = options.modal ? new $.ui.dialog.overlay(self) : null; - self._size(); - self._position(options.position); - uiDialog.show(options.show); - self.moveToTop(true); - - // prevent tabbing out of modal dialogs - if ( options.modal ) { - uiDialog.bind( "keydown.ui-dialog", function( event ) { - if ( event.keyCode !== $.ui.keyCode.TAB ) { - return; - } - - var tabbables = $(':tabbable', this), - first = tabbables.filter(':first'), - last = tabbables.filter(':last'); - - if (event.target === last[0] && !event.shiftKey) { - first.focus(1); - return false; - } else if (event.target === first[0] && event.shiftKey) { - last.focus(1); - return false; - } - }); - } - - // set focus to the first tabbable element in the content area or the first button - // if there are no tabbable elements, set focus on the dialog itself - $(self.element.find(':tabbable').get().concat( - uiDialog.find('.ui-dialog-buttonpane :tabbable').get().concat( - uiDialog.get()))).eq(0).focus(); - - self._isOpen = true; - self._trigger('open'); - - return self; - }, - - _createButtons: function(buttons) { - var self = this, - hasButtons = false, - uiDialogButtonPane = $('
      ') - .addClass( - 'ui-dialog-buttonpane ' + - 'ui-widget-content ' + - 'ui-helper-clearfix' - ), - uiButtonSet = $( "
      " ) - .addClass( "ui-dialog-buttonset" ) - .appendTo( uiDialogButtonPane ); - - // if we already have a button pane, remove it - self.uiDialog.find('.ui-dialog-buttonpane').remove(); - - if (typeof buttons === 'object' && buttons !== null) { - $.each(buttons, function() { - return !(hasButtons = true); - }); - } - if (hasButtons) { - $.each(buttons, function(name, props) { - props = $.isFunction( props ) ? - { click: props, text: name } : - props; - var button = $('') - .click(function() { - props.click.apply(self.element[0], arguments); - }) - .appendTo(uiButtonSet); - // can't use .attr( props, true ) with jQuery 1.3.2. - $.each( props, function( key, value ) { - if ( key === "click" ) { - return; - } - if ( key in button ) { - button[ key ]( value ); - } else { - button.attr( key, value ); - } - }); - if ($.fn.button) { - button.button(); - } - }); - uiDialogButtonPane.appendTo(self.uiDialog); - } - }, - - _makeDraggable: function() { - var self = this, - options = self.options, - doc = $(document), - heightBeforeDrag; - - function filteredUi(ui) { - return { - position: ui.position, - offset: ui.offset - }; - } - - self.uiDialog.draggable({ - cancel: '.ui-dialog-content, .ui-dialog-titlebar-close', - handle: '.ui-dialog-titlebar', - containment: 'document', - start: function(event, ui) { - heightBeforeDrag = options.height === "auto" ? "auto" : $(this).height(); - $(this).height($(this).height()).addClass("ui-dialog-dragging"); - self._trigger('dragStart', event, filteredUi(ui)); - }, - drag: function(event, ui) { - self._trigger('drag', event, filteredUi(ui)); - }, - stop: function(event, ui) { - options.position = [ui.position.left - doc.scrollLeft(), - ui.position.top - doc.scrollTop()]; - $(this).removeClass("ui-dialog-dragging").height(heightBeforeDrag); - self._trigger('dragStop', event, filteredUi(ui)); - $.ui.dialog.overlay.resize(); - } - }); - }, - - _makeResizable: function(handles) { - handles = (handles === undefined ? this.options.resizable : handles); - var self = this, - options = self.options, - // .ui-resizable has position: relative defined in the stylesheet - // but dialogs have to use absolute or fixed positioning - position = self.uiDialog.css('position'), - resizeHandles = (typeof handles === 'string' ? - handles : - 'n,e,s,w,se,sw,ne,nw' - ); - - function filteredUi(ui) { - return { - originalPosition: ui.originalPosition, - originalSize: ui.originalSize, - position: ui.position, - size: ui.size - }; - } - - self.uiDialog.resizable({ - cancel: '.ui-dialog-content', - containment: 'document', - alsoResize: self.element, - maxWidth: options.maxWidth, - maxHeight: options.maxHeight, - minWidth: options.minWidth, - minHeight: self._minHeight(), - handles: resizeHandles, - start: function(event, ui) { - $(this).addClass("ui-dialog-resizing"); - self._trigger('resizeStart', event, filteredUi(ui)); - }, - resize: function(event, ui) { - self._trigger('resize', event, filteredUi(ui)); - }, - stop: function(event, ui) { - $(this).removeClass("ui-dialog-resizing"); - options.height = $(this).height(); - options.width = $(this).width(); - self._trigger('resizeStop', event, filteredUi(ui)); - $.ui.dialog.overlay.resize(); - } - }) - .css('position', position) - .find('.ui-resizable-se').addClass('ui-icon ui-icon-grip-diagonal-se'); - }, - - _minHeight: function() { - var options = this.options; - - if (options.height === 'auto') { - return options.minHeight; - } else { - return Math.min(options.minHeight, options.height); - } - }, - - _position: function(position) { - var myAt = [], - offset = [0, 0], - isVisible; - - if (position) { - // deep extending converts arrays to objects in jQuery <= 1.3.2 :-( - // if (typeof position == 'string' || $.isArray(position)) { - // myAt = $.isArray(position) ? position : position.split(' '); - - if (typeof position === 'string' || (typeof position === 'object' && '0' in position)) { - myAt = position.split ? position.split(' ') : [position[0], position[1]]; - if (myAt.length === 1) { - myAt[1] = myAt[0]; - } - - $.each(['left', 'top'], function(i, offsetPosition) { - if (+myAt[i] === myAt[i]) { - offset[i] = myAt[i]; - myAt[i] = offsetPosition; - } - }); - - position = { - my: myAt.join(" "), - at: myAt.join(" "), - offset: offset.join(" ") - }; - } - - position = $.extend({}, $.ui.dialog.prototype.options.position, position); - } else { - position = $.ui.dialog.prototype.options.position; - } - - // need to show the dialog to get the actual offset in the position plugin - isVisible = this.uiDialog.is(':visible'); - if (!isVisible) { - this.uiDialog.show(); - } - this.uiDialog - // workaround for jQuery bug #5781 http://dev.jquery.com/ticket/5781 - .css({ top: 0, left: 0 }) - .position($.extend({ of: window }, position)); - if (!isVisible) { - this.uiDialog.hide(); - } - }, - - _setOptions: function( options ) { - var self = this, - resizableOptions = {}, - resize = false; - - $.each( options, function( key, value ) { - self._setOption( key, value ); - - if ( key in sizeRelatedOptions ) { - resize = true; - } - if ( key in resizableRelatedOptions ) { - resizableOptions[ key ] = value; - } - }); - - if ( resize ) { - this._size(); - } - if ( this.uiDialog.is( ":data(resizable)" ) ) { - this.uiDialog.resizable( "option", resizableOptions ); - } - }, - - _setOption: function(key, value){ - var self = this, - uiDialog = self.uiDialog; - - switch (key) { - //handling of deprecated beforeclose (vs beforeClose) option - //Ticket #4669 http://dev.jqueryui.com/ticket/4669 - //TODO: remove in 1.9pre - case "beforeclose": - key = "beforeClose"; - break; - case "buttons": - self._createButtons(value); - break; - case "closeText": - // ensure that we always pass a string - self.uiDialogTitlebarCloseText.text("" + value); - break; - case "dialogClass": - uiDialog - .removeClass(self.options.dialogClass) - .addClass(uiDialogClasses + value); - break; - case "disabled": - if (value) { - uiDialog.addClass('ui-dialog-disabled'); - } else { - uiDialog.removeClass('ui-dialog-disabled'); - } - break; - case "draggable": - var isDraggable = uiDialog.is( ":data(draggable)" ); - if ( isDraggable && !value ) { - uiDialog.draggable( "destroy" ); - } - - if ( !isDraggable && value ) { - self._makeDraggable(); - } - break; - case "position": - self._position(value); - break; - case "resizable": - // currently resizable, becoming non-resizable - var isResizable = uiDialog.is( ":data(resizable)" ); - if (isResizable && !value) { - uiDialog.resizable('destroy'); - } - - // currently resizable, changing handles - if (isResizable && typeof value === 'string') { - uiDialog.resizable('option', 'handles', value); - } - - // currently non-resizable, becoming resizable - if (!isResizable && value !== false) { - self._makeResizable(value); - } - break; - case "title": - // convert whatever was passed in o a string, for html() to not throw up - $(".ui-dialog-title", self.uiDialogTitlebar).html("" + (value || ' ')); - break; - } - - $.Widget.prototype._setOption.apply(self, arguments); - }, - - _size: function() { - /* If the user has resized the dialog, the .ui-dialog and .ui-dialog-content - * divs will both have width and height set, so we need to reset them - */ - var options = this.options, - nonContentHeight, - minContentHeight, - isVisible = this.uiDialog.is( ":visible" ); - - // reset content sizing - this.element.show().css({ - width: 'auto', - minHeight: 0, - height: 0 - }); - - if (options.minWidth > options.width) { - options.width = options.minWidth; - } - - // reset wrapper sizing - // determine the height of all the non-content elements - nonContentHeight = this.uiDialog.css({ - height: 'auto', - width: options.width - }) - .height(); - minContentHeight = Math.max( 0, options.minHeight - nonContentHeight ); - - if ( options.height === "auto" ) { - // only needed for IE6 support - if ( $.support.minHeight ) { - this.element.css({ - minHeight: minContentHeight, - height: "auto" - }); - } else { - this.uiDialog.show(); - var autoHeight = this.element.css( "height", "auto" ).height(); - if ( !isVisible ) { - this.uiDialog.hide(); - } - this.element.height( Math.max( autoHeight, minContentHeight ) ); - } - } else { - this.element.height( Math.max( options.height - nonContentHeight, 0 ) ); - } - - if (this.uiDialog.is(':data(resizable)')) { - this.uiDialog.resizable('option', 'minHeight', this._minHeight()); - } - } -}); - -$.extend($.ui.dialog, { - version: "1.8.23", - - uuid: 0, - maxZ: 0, - - getTitleId: function($el) { - var id = $el.attr('id'); - if (!id) { - this.uuid += 1; - id = this.uuid; - } - return 'ui-dialog-title-' + id; - }, - - overlay: function(dialog) { - this.$el = $.ui.dialog.overlay.create(dialog); - } -}); - -$.extend($.ui.dialog.overlay, { - instances: [], - // reuse old instances due to IE memory leak with alpha transparency (see #5185) - oldInstances: [], - maxZ: 0, - events: $.map('focus,mousedown,mouseup,keydown,keypress,click'.split(','), - function(event) { return event + '.dialog-overlay'; }).join(' '), - create: function(dialog) { - if (this.instances.length === 0) { - // prevent use of anchors and inputs - // we use a setTimeout in case the overlay is created from an - // event that we're going to be cancelling (see #2804) - setTimeout(function() { - // handle $(el).dialog().dialog('close') (see #4065) - if ($.ui.dialog.overlay.instances.length) { - $(document).bind($.ui.dialog.overlay.events, function(event) { - // stop events if the z-index of the target is < the z-index of the overlay - // we cannot return true when we don't want to cancel the event (#3523) - if ($(event.target).zIndex() < $.ui.dialog.overlay.maxZ) { - return false; - } - }); - } - }, 1); - - // allow closing by pressing the escape key - $(document).bind('keydown.dialog-overlay', function(event) { - if (dialog.options.closeOnEscape && !event.isDefaultPrevented() && event.keyCode && - event.keyCode === $.ui.keyCode.ESCAPE) { - - dialog.close(event); - event.preventDefault(); - } - }); - - // handle window resize - $(window).bind('resize.dialog-overlay', $.ui.dialog.overlay.resize); - } - - var $el = (this.oldInstances.pop() || $('
      ').addClass('ui-widget-overlay')) - .appendTo(document.body) - .css({ - width: this.width(), - height: this.height() - }); - - if ($.fn.bgiframe) { - $el.bgiframe(); - } - - this.instances.push($el); - return $el; - }, - - destroy: function($el) { - var indexOf = $.inArray($el, this.instances); - if (indexOf != -1){ - this.oldInstances.push(this.instances.splice(indexOf, 1)[0]); - } - - if (this.instances.length === 0) { - $([document, window]).unbind('.dialog-overlay'); - } - - $el.remove(); - - // adjust the maxZ to allow other modal dialogs to continue to work (see #4309) - var maxZ = 0; - $.each(this.instances, function() { - maxZ = Math.max(maxZ, this.css('z-index')); - }); - this.maxZ = maxZ; - }, - - height: function() { - var scrollHeight, - offsetHeight; - // handle IE 6 - if ($.browser.msie && $.browser.version < 7) { - scrollHeight = Math.max( - document.documentElement.scrollHeight, - document.body.scrollHeight - ); - offsetHeight = Math.max( - document.documentElement.offsetHeight, - document.body.offsetHeight - ); - - if (scrollHeight < offsetHeight) { - return $(window).height() + 'px'; - } else { - return scrollHeight + 'px'; - } - // handle "good" browsers - } else { - return $(document).height() + 'px'; - } - }, - - width: function() { - var scrollWidth, - offsetWidth; - // handle IE - if ( $.browser.msie ) { - scrollWidth = Math.max( - document.documentElement.scrollWidth, - document.body.scrollWidth - ); - offsetWidth = Math.max( - document.documentElement.offsetWidth, - document.body.offsetWidth - ); - - if (scrollWidth < offsetWidth) { - return $(window).width() + 'px'; - } else { - return scrollWidth + 'px'; - } - // handle "good" browsers - } else { - return $(document).width() + 'px'; - } - }, - - resize: function() { - /* If the dialog is draggable and the user drags it past the - * right edge of the window, the document becomes wider so we - * need to stretch the overlay. If the user then drags the - * dialog back to the left, the document will become narrower, - * so we need to shrink the overlay to the appropriate size. - * This is handled by shrinking the overlay before setting it - * to the full document size. - */ - var $overlays = $([]); - $.each($.ui.dialog.overlay.instances, function() { - $overlays = $overlays.add(this); - }); - - $overlays.css({ - width: 0, - height: 0 - }).css({ - width: $.ui.dialog.overlay.width(), - height: $.ui.dialog.overlay.height() - }); - } -}); - -$.extend($.ui.dialog.overlay.prototype, { - destroy: function() { - $.ui.dialog.overlay.destroy(this.$el); - } -}); - -}(jQuery)); -/*! - * jQuery UI Slider 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Slider - * - * Depends: - * jquery.ui.core.js - * jquery.ui.mouse.js - * jquery.ui.widget.js - */ -(function( $, undefined ) { - -// number of pages in a slider -// (how many times can you page up/down to go through the whole range) -var numPages = 5; - -$.widget( "ui.slider", $.ui.mouse, { - - widgetEventPrefix: "slide", - - options: { - animate: false, - distance: 0, - max: 100, - min: 0, - orientation: "horizontal", - range: false, - step: 1, - value: 0, - values: null - }, - - _create: function() { - var self = this, - o = this.options, - existingHandles = this.element.find( ".ui-slider-handle" ).addClass( "ui-state-default ui-corner-all" ), - handle = "", - handleCount = ( o.values && o.values.length ) || 1, - handles = []; - - this._keySliding = false; - this._mouseSliding = false; - this._animateOff = true; - this._handleIndex = null; - this._detectOrientation(); - this._mouseInit(); - - this.element - .addClass( "ui-slider" + - " ui-slider-" + this.orientation + - " ui-widget" + - " ui-widget-content" + - " ui-corner-all" + - ( o.disabled ? " ui-slider-disabled ui-disabled" : "" ) ); - - this.range = $([]); - - if ( o.range ) { - if ( o.range === true ) { - if ( !o.values ) { - o.values = [ this._valueMin(), this._valueMin() ]; - } - if ( o.values.length && o.values.length !== 2 ) { - o.values = [ o.values[0], o.values[0] ]; - } - } - - this.range = $( "
      " ) - .appendTo( this.element ) - .addClass( "ui-slider-range" + - // note: this isn't the most fittingly semantic framework class for this element, - // but worked best visually with a variety of themes - " ui-widget-header" + - ( ( o.range === "min" || o.range === "max" ) ? " ui-slider-range-" + o.range : "" ) ); - } - - for ( var i = existingHandles.length; i < handleCount; i += 1 ) { - handles.push( handle ); - } - - this.handles = existingHandles.add( $( handles.join( "" ) ).appendTo( self.element ) ); - - this.handle = this.handles.eq( 0 ); - - this.handles.add( this.range ).filter( "a" ) - .click(function( event ) { - event.preventDefault(); - }) - .hover(function() { - if ( !o.disabled ) { - $( this ).addClass( "ui-state-hover" ); - } - }, function() { - $( this ).removeClass( "ui-state-hover" ); - }) - .focus(function() { - if ( !o.disabled ) { - $( ".ui-slider .ui-state-focus" ).removeClass( "ui-state-focus" ); - $( this ).addClass( "ui-state-focus" ); - } else { - $( this ).blur(); - } - }) - .blur(function() { - $( this ).removeClass( "ui-state-focus" ); - }); - - this.handles.each(function( i ) { - $( this ).data( "index.ui-slider-handle", i ); - }); - - this.handles - .keydown(function( event ) { - var index = $( this ).data( "index.ui-slider-handle" ), - allowed, - curVal, - newVal, - step; - - if ( self.options.disabled ) { - return; - } - - switch ( event.keyCode ) { - case $.ui.keyCode.HOME: - case $.ui.keyCode.END: - case $.ui.keyCode.PAGE_UP: - case $.ui.keyCode.PAGE_DOWN: - case $.ui.keyCode.UP: - case $.ui.keyCode.RIGHT: - case $.ui.keyCode.DOWN: - case $.ui.keyCode.LEFT: - event.preventDefault(); - if ( !self._keySliding ) { - self._keySliding = true; - $( this ).addClass( "ui-state-active" ); - allowed = self._start( event, index ); - if ( allowed === false ) { - return; - } - } - break; - } - - step = self.options.step; - if ( self.options.values && self.options.values.length ) { - curVal = newVal = self.values( index ); - } else { - curVal = newVal = self.value(); - } - - switch ( event.keyCode ) { - case $.ui.keyCode.HOME: - newVal = self._valueMin(); - break; - case $.ui.keyCode.END: - newVal = self._valueMax(); - break; - case $.ui.keyCode.PAGE_UP: - newVal = self._trimAlignValue( curVal + ( (self._valueMax() - self._valueMin()) / numPages ) ); - break; - case $.ui.keyCode.PAGE_DOWN: - newVal = self._trimAlignValue( curVal - ( (self._valueMax() - self._valueMin()) / numPages ) ); - break; - case $.ui.keyCode.UP: - case $.ui.keyCode.RIGHT: - if ( curVal === self._valueMax() ) { - return; - } - newVal = self._trimAlignValue( curVal + step ); - break; - case $.ui.keyCode.DOWN: - case $.ui.keyCode.LEFT: - if ( curVal === self._valueMin() ) { - return; - } - newVal = self._trimAlignValue( curVal - step ); - break; - } - - self._slide( event, index, newVal ); - }) - .keyup(function( event ) { - var index = $( this ).data( "index.ui-slider-handle" ); - - if ( self._keySliding ) { - self._keySliding = false; - self._stop( event, index ); - self._change( event, index ); - $( this ).removeClass( "ui-state-active" ); - } - - }); - - this._refreshValue(); - - this._animateOff = false; - }, - - destroy: function() { - this.handles.remove(); - this.range.remove(); - - this.element - .removeClass( "ui-slider" + - " ui-slider-horizontal" + - " ui-slider-vertical" + - " ui-slider-disabled" + - " ui-widget" + - " ui-widget-content" + - " ui-corner-all" ) - .removeData( "slider" ) - .unbind( ".slider" ); - - this._mouseDestroy(); - - return this; - }, - - _mouseCapture: function( event ) { - var o = this.options, - position, - normValue, - distance, - closestHandle, - self, - index, - allowed, - offset, - mouseOverHandle; - - if ( o.disabled ) { - return false; - } - - this.elementSize = { - width: this.element.outerWidth(), - height: this.element.outerHeight() - }; - this.elementOffset = this.element.offset(); - - position = { x: event.pageX, y: event.pageY }; - normValue = this._normValueFromMouse( position ); - distance = this._valueMax() - this._valueMin() + 1; - self = this; - this.handles.each(function( i ) { - var thisDistance = Math.abs( normValue - self.values(i) ); - if ( distance > thisDistance ) { - distance = thisDistance; - closestHandle = $( this ); - index = i; - } - }); - - // workaround for bug #3736 (if both handles of a range are at 0, - // the first is always used as the one with least distance, - // and moving it is obviously prevented by preventing negative ranges) - if( o.range === true && this.values(1) === o.min ) { - index += 1; - closestHandle = $( this.handles[index] ); - } - - allowed = this._start( event, index ); - if ( allowed === false ) { - return false; - } - this._mouseSliding = true; - - self._handleIndex = index; - - closestHandle - .addClass( "ui-state-active" ) - .focus(); - - offset = closestHandle.offset(); - mouseOverHandle = !$( event.target ).parents().andSelf().is( ".ui-slider-handle" ); - this._clickOffset = mouseOverHandle ? { left: 0, top: 0 } : { - left: event.pageX - offset.left - ( closestHandle.width() / 2 ), - top: event.pageY - offset.top - - ( closestHandle.height() / 2 ) - - ( parseInt( closestHandle.css("borderTopWidth"), 10 ) || 0 ) - - ( parseInt( closestHandle.css("borderBottomWidth"), 10 ) || 0) + - ( parseInt( closestHandle.css("marginTop"), 10 ) || 0) - }; - - if ( !this.handles.hasClass( "ui-state-hover" ) ) { - this._slide( event, index, normValue ); - } - this._animateOff = true; - return true; - }, - - _mouseStart: function( event ) { - return true; - }, - - _mouseDrag: function( event ) { - var position = { x: event.pageX, y: event.pageY }, - normValue = this._normValueFromMouse( position ); - - this._slide( event, this._handleIndex, normValue ); - - return false; - }, - - _mouseStop: function( event ) { - this.handles.removeClass( "ui-state-active" ); - this._mouseSliding = false; - - this._stop( event, this._handleIndex ); - this._change( event, this._handleIndex ); - - this._handleIndex = null; - this._clickOffset = null; - this._animateOff = false; - - return false; - }, - - _detectOrientation: function() { - this.orientation = ( this.options.orientation === "vertical" ) ? "vertical" : "horizontal"; - }, - - _normValueFromMouse: function( position ) { - var pixelTotal, - pixelMouse, - percentMouse, - valueTotal, - valueMouse; - - if ( this.orientation === "horizontal" ) { - pixelTotal = this.elementSize.width; - pixelMouse = position.x - this.elementOffset.left - ( this._clickOffset ? this._clickOffset.left : 0 ); - } else { - pixelTotal = this.elementSize.height; - pixelMouse = position.y - this.elementOffset.top - ( this._clickOffset ? this._clickOffset.top : 0 ); - } - - percentMouse = ( pixelMouse / pixelTotal ); - if ( percentMouse > 1 ) { - percentMouse = 1; - } - if ( percentMouse < 0 ) { - percentMouse = 0; - } - if ( this.orientation === "vertical" ) { - percentMouse = 1 - percentMouse; - } - - valueTotal = this._valueMax() - this._valueMin(); - valueMouse = this._valueMin() + percentMouse * valueTotal; - - return this._trimAlignValue( valueMouse ); - }, - - _start: function( event, index ) { - var uiHash = { - handle: this.handles[ index ], - value: this.value() - }; - if ( this.options.values && this.options.values.length ) { - uiHash.value = this.values( index ); - uiHash.values = this.values(); - } - return this._trigger( "start", event, uiHash ); - }, - - _slide: function( event, index, newVal ) { - var otherVal, - newValues, - allowed; - - if ( this.options.values && this.options.values.length ) { - otherVal = this.values( index ? 0 : 1 ); - - if ( ( this.options.values.length === 2 && this.options.range === true ) && - ( ( index === 0 && newVal > otherVal) || ( index === 1 && newVal < otherVal ) ) - ) { - newVal = otherVal; - } - - if ( newVal !== this.values( index ) ) { - newValues = this.values(); - newValues[ index ] = newVal; - // A slide can be canceled by returning false from the slide callback - allowed = this._trigger( "slide", event, { - handle: this.handles[ index ], - value: newVal, - values: newValues - } ); - otherVal = this.values( index ? 0 : 1 ); - if ( allowed !== false ) { - this.values( index, newVal, true ); - } - } - } else { - if ( newVal !== this.value() ) { - // A slide can be canceled by returning false from the slide callback - allowed = this._trigger( "slide", event, { - handle: this.handles[ index ], - value: newVal - } ); - if ( allowed !== false ) { - this.value( newVal ); - } - } - } - }, - - _stop: function( event, index ) { - var uiHash = { - handle: this.handles[ index ], - value: this.value() - }; - if ( this.options.values && this.options.values.length ) { - uiHash.value = this.values( index ); - uiHash.values = this.values(); - } - - this._trigger( "stop", event, uiHash ); - }, - - _change: function( event, index ) { - if ( !this._keySliding && !this._mouseSliding ) { - var uiHash = { - handle: this.handles[ index ], - value: this.value() - }; - if ( this.options.values && this.options.values.length ) { - uiHash.value = this.values( index ); - uiHash.values = this.values(); - } - - this._trigger( "change", event, uiHash ); - } - }, - - value: function( newValue ) { - if ( arguments.length ) { - this.options.value = this._trimAlignValue( newValue ); - this._refreshValue(); - this._change( null, 0 ); - return; - } - - return this._value(); - }, - - values: function( index, newValue ) { - var vals, - newValues, - i; - - if ( arguments.length > 1 ) { - this.options.values[ index ] = this._trimAlignValue( newValue ); - this._refreshValue(); - this._change( null, index ); - return; - } - - if ( arguments.length ) { - if ( $.isArray( arguments[ 0 ] ) ) { - vals = this.options.values; - newValues = arguments[ 0 ]; - for ( i = 0; i < vals.length; i += 1 ) { - vals[ i ] = this._trimAlignValue( newValues[ i ] ); - this._change( null, i ); - } - this._refreshValue(); - } else { - if ( this.options.values && this.options.values.length ) { - return this._values( index ); - } else { - return this.value(); - } - } - } else { - return this._values(); - } - }, - - _setOption: function( key, value ) { - var i, - valsLength = 0; - - if ( $.isArray( this.options.values ) ) { - valsLength = this.options.values.length; - } - - $.Widget.prototype._setOption.apply( this, arguments ); - - switch ( key ) { - case "disabled": - if ( value ) { - this.handles.filter( ".ui-state-focus" ).blur(); - this.handles.removeClass( "ui-state-hover" ); - this.handles.propAttr( "disabled", true ); - this.element.addClass( "ui-disabled" ); - } else { - this.handles.propAttr( "disabled", false ); - this.element.removeClass( "ui-disabled" ); - } - break; - case "orientation": - this._detectOrientation(); - this.element - .removeClass( "ui-slider-horizontal ui-slider-vertical" ) - .addClass( "ui-slider-" + this.orientation ); - this._refreshValue(); - break; - case "value": - this._animateOff = true; - this._refreshValue(); - this._change( null, 0 ); - this._animateOff = false; - break; - case "values": - this._animateOff = true; - this._refreshValue(); - for ( i = 0; i < valsLength; i += 1 ) { - this._change( null, i ); - } - this._animateOff = false; - break; - } - }, - - //internal value getter - // _value() returns value trimmed by min and max, aligned by step - _value: function() { - var val = this.options.value; - val = this._trimAlignValue( val ); - - return val; - }, - - //internal values getter - // _values() returns array of values trimmed by min and max, aligned by step - // _values( index ) returns single value trimmed by min and max, aligned by step - _values: function( index ) { - var val, - vals, - i; - - if ( arguments.length ) { - val = this.options.values[ index ]; - val = this._trimAlignValue( val ); - - return val; - } else { - // .slice() creates a copy of the array - // this copy gets trimmed by min and max and then returned - vals = this.options.values.slice(); - for ( i = 0; i < vals.length; i+= 1) { - vals[ i ] = this._trimAlignValue( vals[ i ] ); - } - - return vals; - } - }, - - // returns the step-aligned value that val is closest to, between (inclusive) min and max - _trimAlignValue: function( val ) { - if ( val <= this._valueMin() ) { - return this._valueMin(); - } - if ( val >= this._valueMax() ) { - return this._valueMax(); - } - var step = ( this.options.step > 0 ) ? this.options.step : 1, - valModStep = (val - this._valueMin()) % step, - alignValue = val - valModStep; - - if ( Math.abs(valModStep) * 2 >= step ) { - alignValue += ( valModStep > 0 ) ? step : ( -step ); - } - - // Since JavaScript has problems with large floats, round - // the final value to 5 digits after the decimal point (see #4124) - return parseFloat( alignValue.toFixed(5) ); - }, - - _valueMin: function() { - return this.options.min; - }, - - _valueMax: function() { - return this.options.max; - }, - - _refreshValue: function() { - var oRange = this.options.range, - o = this.options, - self = this, - animate = ( !this._animateOff ) ? o.animate : false, - valPercent, - _set = {}, - lastValPercent, - value, - valueMin, - valueMax; - - if ( this.options.values && this.options.values.length ) { - this.handles.each(function( i, j ) { - valPercent = ( self.values(i) - self._valueMin() ) / ( self._valueMax() - self._valueMin() ) * 100; - _set[ self.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%"; - $( this ).stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate ); - if ( self.options.range === true ) { - if ( self.orientation === "horizontal" ) { - if ( i === 0 ) { - self.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { left: valPercent + "%" }, o.animate ); - } - if ( i === 1 ) { - self.range[ animate ? "animate" : "css" ]( { width: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } ); - } - } else { - if ( i === 0 ) { - self.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { bottom: ( valPercent ) + "%" }, o.animate ); - } - if ( i === 1 ) { - self.range[ animate ? "animate" : "css" ]( { height: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } ); - } - } - } - lastValPercent = valPercent; - }); - } else { - value = this.value(); - valueMin = this._valueMin(); - valueMax = this._valueMax(); - valPercent = ( valueMax !== valueMin ) ? - ( value - valueMin ) / ( valueMax - valueMin ) * 100 : - 0; - _set[ self.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%"; - this.handle.stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate ); - - if ( oRange === "min" && this.orientation === "horizontal" ) { - this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { width: valPercent + "%" }, o.animate ); - } - if ( oRange === "max" && this.orientation === "horizontal" ) { - this.range[ animate ? "animate" : "css" ]( { width: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } ); - } - if ( oRange === "min" && this.orientation === "vertical" ) { - this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { height: valPercent + "%" }, o.animate ); - } - if ( oRange === "max" && this.orientation === "vertical" ) { - this.range[ animate ? "animate" : "css" ]( { height: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } ); - } - } - } - -}); - -$.extend( $.ui.slider, { - version: "1.8.23" -}); - -}(jQuery)); -/*! - * jQuery UI Tabs 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Tabs - * - * Depends: - * jquery.ui.core.js - * jquery.ui.widget.js - */ -(function( $, undefined ) { - -var tabId = 0, - listId = 0; - -function getNextTabId() { - return ++tabId; -} - -function getNextListId() { - return ++listId; -} - -$.widget( "ui.tabs", { - options: { - add: null, - ajaxOptions: null, - cache: false, - cookie: null, // e.g. { expires: 7, path: '/', domain: 'jquery.com', secure: true } - collapsible: false, - disable: null, - disabled: [], - enable: null, - event: "click", - fx: null, // e.g. { height: 'toggle', opacity: 'toggle', duration: 200 } - idPrefix: "ui-tabs-", - load: null, - panelTemplate: "
      ", - remove: null, - select: null, - show: null, - spinner: "Loading…", - tabTemplate: "
    • #{label}
    • " - }, - - _create: function() { - this._tabify( true ); - }, - - _setOption: function( key, value ) { - if ( key == "selected" ) { - if (this.options.collapsible && value == this.options.selected ) { - return; - } - this.select( value ); - } else { - this.options[ key ] = value; - this._tabify(); - } - }, - - _tabId: function( a ) { - return a.title && a.title.replace( /\s/g, "_" ).replace( /[^\w\u00c0-\uFFFF-]/g, "" ) || - this.options.idPrefix + getNextTabId(); - }, - - _sanitizeSelector: function( hash ) { - // we need this because an id may contain a ":" - return hash.replace( /:/g, "\\:" ); - }, - - _cookie: function() { - var cookie = this.cookie || - ( this.cookie = this.options.cookie.name || "ui-tabs-" + getNextListId() ); - return $.cookie.apply( null, [ cookie ].concat( $.makeArray( arguments ) ) ); - }, - - _ui: function( tab, panel ) { - return { - tab: tab, - panel: panel, - index: this.anchors.index( tab ) - }; - }, - - _cleanup: function() { - // restore all former loading tabs labels - this.lis.filter( ".ui-state-processing" ) - .removeClass( "ui-state-processing" ) - .find( "span:data(label.tabs)" ) - .each(function() { - var el = $( this ); - el.html( el.data( "label.tabs" ) ).removeData( "label.tabs" ); - }); - }, - - _tabify: function( init ) { - var self = this, - o = this.options, - fragmentId = /^#.+/; // Safari 2 reports '#' for an empty hash - - this.list = this.element.find( "ol,ul" ).eq( 0 ); - this.lis = $( " > li:has(a[href])", this.list ); - this.anchors = this.lis.map(function() { - return $( "a", this )[ 0 ]; - }); - this.panels = $( [] ); - - this.anchors.each(function( i, a ) { - var href = $( a ).attr( "href" ); - // For dynamically created HTML that contains a hash as href IE < 8 expands - // such href to the full page url with hash and then misinterprets tab as ajax. - // Same consideration applies for an added tab with a fragment identifier - // since a[href=#fragment-identifier] does unexpectedly not match. - // Thus normalize href attribute... - var hrefBase = href.split( "#" )[ 0 ], - baseEl; - if ( hrefBase && ( hrefBase === location.toString().split( "#" )[ 0 ] || - ( baseEl = $( "base" )[ 0 ]) && hrefBase === baseEl.href ) ) { - href = a.hash; - a.href = href; - } - - // inline tab - if ( fragmentId.test( href ) ) { - self.panels = self.panels.add( self.element.find( self._sanitizeSelector( href ) ) ); - // remote tab - // prevent loading the page itself if href is just "#" - } else if ( href && href !== "#" ) { - // required for restore on destroy - $.data( a, "href.tabs", href ); - - // TODO until #3808 is fixed strip fragment identifier from url - // (IE fails to load from such url) - $.data( a, "load.tabs", href.replace( /#.*$/, "" ) ); - - var id = self._tabId( a ); - a.href = "#" + id; - var $panel = self.element.find( "#" + id ); - if ( !$panel.length ) { - $panel = $( o.panelTemplate ) - .attr( "id", id ) - .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" ) - .insertAfter( self.panels[ i - 1 ] || self.list ); - $panel.data( "destroy.tabs", true ); - } - self.panels = self.panels.add( $panel ); - // invalid tab href - } else { - o.disabled.push( i ); - } - }); - - // initialization from scratch - if ( init ) { - // attach necessary classes for styling - this.element.addClass( "ui-tabs ui-widget ui-widget-content ui-corner-all" ); - this.list.addClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" ); - this.lis.addClass( "ui-state-default ui-corner-top" ); - this.panels.addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" ); - - // Selected tab - // use "selected" option or try to retrieve: - // 1. from fragment identifier in url - // 2. from cookie - // 3. from selected class attribute on
    • - if ( o.selected === undefined ) { - if ( location.hash ) { - this.anchors.each(function( i, a ) { - if ( a.hash == location.hash ) { - o.selected = i; - return false; - } - }); - } - if ( typeof o.selected !== "number" && o.cookie ) { - o.selected = parseInt( self._cookie(), 10 ); - } - if ( typeof o.selected !== "number" && this.lis.filter( ".ui-tabs-selected" ).length ) { - o.selected = this.lis.index( this.lis.filter( ".ui-tabs-selected" ) ); - } - o.selected = o.selected || ( this.lis.length ? 0 : -1 ); - } else if ( o.selected === null ) { // usage of null is deprecated, TODO remove in next release - o.selected = -1; - } - - // sanity check - default to first tab... - o.selected = ( ( o.selected >= 0 && this.anchors[ o.selected ] ) || o.selected < 0 ) - ? o.selected - : 0; - - // Take disabling tabs via class attribute from HTML - // into account and update option properly. - // A selected tab cannot become disabled. - o.disabled = $.unique( o.disabled.concat( - $.map( this.lis.filter( ".ui-state-disabled" ), function( n, i ) { - return self.lis.index( n ); - }) - ) ).sort(); - - if ( $.inArray( o.selected, o.disabled ) != -1 ) { - o.disabled.splice( $.inArray( o.selected, o.disabled ), 1 ); - } - - // highlight selected tab - this.panels.addClass( "ui-tabs-hide" ); - this.lis.removeClass( "ui-tabs-selected ui-state-active" ); - // check for length avoids error when initializing empty list - if ( o.selected >= 0 && this.anchors.length ) { - self.element.find( self._sanitizeSelector( self.anchors[ o.selected ].hash ) ).removeClass( "ui-tabs-hide" ); - this.lis.eq( o.selected ).addClass( "ui-tabs-selected ui-state-active" ); - - // seems to be expected behavior that the show callback is fired - self.element.queue( "tabs", function() { - self._trigger( "show", null, - self._ui( self.anchors[ o.selected ], self.element.find( self._sanitizeSelector( self.anchors[ o.selected ].hash ) )[ 0 ] ) ); - }); - - this.load( o.selected ); - } - - // clean up to avoid memory leaks in certain versions of IE 6 - // TODO: namespace this event - $( window ).bind( "unload", function() { - self.lis.add( self.anchors ).unbind( ".tabs" ); - self.lis = self.anchors = self.panels = null; - }); - // update selected after add/remove - } else { - o.selected = this.lis.index( this.lis.filter( ".ui-tabs-selected" ) ); - } - - // update collapsible - // TODO: use .toggleClass() - this.element[ o.collapsible ? "addClass" : "removeClass" ]( "ui-tabs-collapsible" ); - - // set or update cookie after init and add/remove respectively - if ( o.cookie ) { - this._cookie( o.selected, o.cookie ); - } - - // disable tabs - for ( var i = 0, li; ( li = this.lis[ i ] ); i++ ) { - $( li )[ $.inArray( i, o.disabled ) != -1 && - // TODO: use .toggleClass() - !$( li ).hasClass( "ui-tabs-selected" ) ? "addClass" : "removeClass" ]( "ui-state-disabled" ); - } - - // reset cache if switching from cached to not cached - if ( o.cache === false ) { - this.anchors.removeData( "cache.tabs" ); - } - - // remove all handlers before, tabify may run on existing tabs after add or option change - this.lis.add( this.anchors ).unbind( ".tabs" ); - - if ( o.event !== "mouseover" ) { - var addState = function( state, el ) { - if ( el.is( ":not(.ui-state-disabled)" ) ) { - el.addClass( "ui-state-" + state ); - } - }; - var removeState = function( state, el ) { - el.removeClass( "ui-state-" + state ); - }; - this.lis.bind( "mouseover.tabs" , function() { - addState( "hover", $( this ) ); - }); - this.lis.bind( "mouseout.tabs", function() { - removeState( "hover", $( this ) ); - }); - this.anchors.bind( "focus.tabs", function() { - addState( "focus", $( this ).closest( "li" ) ); - }); - this.anchors.bind( "blur.tabs", function() { - removeState( "focus", $( this ).closest( "li" ) ); - }); - } - - // set up animations - var hideFx, showFx; - if ( o.fx ) { - if ( $.isArray( o.fx ) ) { - hideFx = o.fx[ 0 ]; - showFx = o.fx[ 1 ]; - } else { - hideFx = showFx = o.fx; - } - } - - // Reset certain styles left over from animation - // and prevent IE's ClearType bug... - function resetStyle( $el, fx ) { - $el.css( "display", "" ); - if ( !$.support.opacity && fx.opacity ) { - $el[ 0 ].style.removeAttribute( "filter" ); - } - } - - // Show a tab... - var showTab = showFx - ? function( clicked, $show ) { - $( clicked ).closest( "li" ).addClass( "ui-tabs-selected ui-state-active" ); - $show.hide().removeClass( "ui-tabs-hide" ) // avoid flicker that way - .animate( showFx, showFx.duration || "normal", function() { - resetStyle( $show, showFx ); - self._trigger( "show", null, self._ui( clicked, $show[ 0 ] ) ); - }); - } - : function( clicked, $show ) { - $( clicked ).closest( "li" ).addClass( "ui-tabs-selected ui-state-active" ); - $show.removeClass( "ui-tabs-hide" ); - self._trigger( "show", null, self._ui( clicked, $show[ 0 ] ) ); - }; - - // Hide a tab, $show is optional... - var hideTab = hideFx - ? function( clicked, $hide ) { - $hide.animate( hideFx, hideFx.duration || "normal", function() { - self.lis.removeClass( "ui-tabs-selected ui-state-active" ); - $hide.addClass( "ui-tabs-hide" ); - resetStyle( $hide, hideFx ); - self.element.dequeue( "tabs" ); - }); - } - : function( clicked, $hide, $show ) { - self.lis.removeClass( "ui-tabs-selected ui-state-active" ); - $hide.addClass( "ui-tabs-hide" ); - self.element.dequeue( "tabs" ); - }; - - // attach tab event handler, unbind to avoid duplicates from former tabifying... - this.anchors.bind( o.event + ".tabs", function() { - var el = this, - $li = $(el).closest( "li" ), - $hide = self.panels.filter( ":not(.ui-tabs-hide)" ), - $show = self.element.find( self._sanitizeSelector( el.hash ) ); - - // If tab is already selected and not collapsible or tab disabled or - // or is already loading or click callback returns false stop here. - // Check if click handler returns false last so that it is not executed - // for a disabled or loading tab! - if ( ( $li.hasClass( "ui-tabs-selected" ) && !o.collapsible) || - $li.hasClass( "ui-state-disabled" ) || - $li.hasClass( "ui-state-processing" ) || - self.panels.filter( ":animated" ).length || - self._trigger( "select", null, self._ui( this, $show[ 0 ] ) ) === false ) { - this.blur(); - return false; - } - - o.selected = self.anchors.index( this ); - - self.abort(); - - // if tab may be closed - if ( o.collapsible ) { - if ( $li.hasClass( "ui-tabs-selected" ) ) { - o.selected = -1; - - if ( o.cookie ) { - self._cookie( o.selected, o.cookie ); - } - - self.element.queue( "tabs", function() { - hideTab( el, $hide ); - }).dequeue( "tabs" ); - - this.blur(); - return false; - } else if ( !$hide.length ) { - if ( o.cookie ) { - self._cookie( o.selected, o.cookie ); - } - - self.element.queue( "tabs", function() { - showTab( el, $show ); - }); - - // TODO make passing in node possible, see also http://dev.jqueryui.com/ticket/3171 - self.load( self.anchors.index( this ) ); - - this.blur(); - return false; - } - } - - if ( o.cookie ) { - self._cookie( o.selected, o.cookie ); - } - - // show new tab - if ( $show.length ) { - if ( $hide.length ) { - self.element.queue( "tabs", function() { - hideTab( el, $hide ); - }); - } - self.element.queue( "tabs", function() { - showTab( el, $show ); - }); - - self.load( self.anchors.index( this ) ); - } else { - throw "jQuery UI Tabs: Mismatching fragment identifier."; - } - - // Prevent IE from keeping other link focussed when using the back button - // and remove dotted border from clicked link. This is controlled via CSS - // in modern browsers; blur() removes focus from address bar in Firefox - // which can become a usability and annoying problem with tabs('rotate'). - if ( $.browser.msie ) { - this.blur(); - } - }); - - // disable click in any case - this.anchors.bind( "click.tabs", function(){ - return false; - }); - }, - - _getIndex: function( index ) { - // meta-function to give users option to provide a href string instead of a numerical index. - // also sanitizes numerical indexes to valid values. - if ( typeof index == "string" ) { - index = this.anchors.index( this.anchors.filter( "[href$='" + index + "']" ) ); - } - - return index; - }, - - destroy: function() { - var o = this.options; - - this.abort(); - - this.element - .unbind( ".tabs" ) - .removeClass( "ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible" ) - .removeData( "tabs" ); - - this.list.removeClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" ); - - this.anchors.each(function() { - var href = $.data( this, "href.tabs" ); - if ( href ) { - this.href = href; - } - var $this = $( this ).unbind( ".tabs" ); - $.each( [ "href", "load", "cache" ], function( i, prefix ) { - $this.removeData( prefix + ".tabs" ); - }); - }); - - this.lis.unbind( ".tabs" ).add( this.panels ).each(function() { - if ( $.data( this, "destroy.tabs" ) ) { - $( this ).remove(); - } else { - $( this ).removeClass([ - "ui-state-default", - "ui-corner-top", - "ui-tabs-selected", - "ui-state-active", - "ui-state-hover", - "ui-state-focus", - "ui-state-disabled", - "ui-tabs-panel", - "ui-widget-content", - "ui-corner-bottom", - "ui-tabs-hide" - ].join( " " ) ); - } - }); - - if ( o.cookie ) { - this._cookie( null, o.cookie ); - } - - return this; - }, - - add: function( url, label, index ) { - if ( index === undefined ) { - index = this.anchors.length; - } - - var self = this, - o = this.options, - $li = $( o.tabTemplate.replace( /#\{href\}/g, url ).replace( /#\{label\}/g, label ) ), - id = !url.indexOf( "#" ) ? url.replace( "#", "" ) : this._tabId( $( "a", $li )[ 0 ] ); - - $li.addClass( "ui-state-default ui-corner-top" ).data( "destroy.tabs", true ); - - // try to find an existing element before creating a new one - var $panel = self.element.find( "#" + id ); - if ( !$panel.length ) { - $panel = $( o.panelTemplate ) - .attr( "id", id ) - .data( "destroy.tabs", true ); - } - $panel.addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide" ); - - if ( index >= this.lis.length ) { - $li.appendTo( this.list ); - $panel.appendTo( this.list[ 0 ].parentNode ); - } else { - $li.insertBefore( this.lis[ index ] ); - $panel.insertBefore( this.panels[ index ] ); - } - - o.disabled = $.map( o.disabled, function( n, i ) { - return n >= index ? ++n : n; - }); - - this._tabify(); - - if ( this.anchors.length == 1 ) { - o.selected = 0; - $li.addClass( "ui-tabs-selected ui-state-active" ); - $panel.removeClass( "ui-tabs-hide" ); - this.element.queue( "tabs", function() { - self._trigger( "show", null, self._ui( self.anchors[ 0 ], self.panels[ 0 ] ) ); - }); - - this.load( 0 ); - } - - this._trigger( "add", null, this._ui( this.anchors[ index ], this.panels[ index ] ) ); - return this; - }, - - remove: function( index ) { - index = this._getIndex( index ); - var o = this.options, - $li = this.lis.eq( index ).remove(), - $panel = this.panels.eq( index ).remove(); - - // If selected tab was removed focus tab to the right or - // in case the last tab was removed the tab to the left. - if ( $li.hasClass( "ui-tabs-selected" ) && this.anchors.length > 1) { - this.select( index + ( index + 1 < this.anchors.length ? 1 : -1 ) ); - } - - o.disabled = $.map( - $.grep( o.disabled, function(n, i) { - return n != index; - }), - function( n, i ) { - return n >= index ? --n : n; - }); - - this._tabify(); - - this._trigger( "remove", null, this._ui( $li.find( "a" )[ 0 ], $panel[ 0 ] ) ); - return this; - }, - - enable: function( index ) { - index = this._getIndex( index ); - var o = this.options; - if ( $.inArray( index, o.disabled ) == -1 ) { - return; - } - - this.lis.eq( index ).removeClass( "ui-state-disabled" ); - o.disabled = $.grep( o.disabled, function( n, i ) { - return n != index; - }); - - this._trigger( "enable", null, this._ui( this.anchors[ index ], this.panels[ index ] ) ); - return this; - }, - - disable: function( index ) { - index = this._getIndex( index ); - var self = this, o = this.options; - // cannot disable already selected tab - if ( index != o.selected ) { - this.lis.eq( index ).addClass( "ui-state-disabled" ); - - o.disabled.push( index ); - o.disabled.sort(); - - this._trigger( "disable", null, this._ui( this.anchors[ index ], this.panels[ index ] ) ); - } - - return this; - }, - - select: function( index ) { - index = this._getIndex( index ); - if ( index == -1 ) { - if ( this.options.collapsible && this.options.selected != -1 ) { - index = this.options.selected; - } else { - return this; - } - } - this.anchors.eq( index ).trigger( this.options.event + ".tabs" ); - return this; - }, - - load: function( index ) { - index = this._getIndex( index ); - var self = this, - o = this.options, - a = this.anchors.eq( index )[ 0 ], - url = $.data( a, "load.tabs" ); - - this.abort(); - - // not remote or from cache - if ( !url || this.element.queue( "tabs" ).length !== 0 && $.data( a, "cache.tabs" ) ) { - this.element.dequeue( "tabs" ); - return; - } - - // load remote from here on - this.lis.eq( index ).addClass( "ui-state-processing" ); - - if ( o.spinner ) { - var span = $( "span", a ); - span.data( "label.tabs", span.html() ).html( o.spinner ); - } - - this.xhr = $.ajax( $.extend( {}, o.ajaxOptions, { - url: url, - success: function( r, s ) { - self.element.find( self._sanitizeSelector( a.hash ) ).html( r ); - - // take care of tab labels - self._cleanup(); - - if ( o.cache ) { - $.data( a, "cache.tabs", true ); - } - - self._trigger( "load", null, self._ui( self.anchors[ index ], self.panels[ index ] ) ); - try { - o.ajaxOptions.success( r, s ); - } - catch ( e ) {} - }, - error: function( xhr, s, e ) { - // take care of tab labels - self._cleanup(); - - self._trigger( "load", null, self._ui( self.anchors[ index ], self.panels[ index ] ) ); - try { - // Passing index avoid a race condition when this method is - // called after the user has selected another tab. - // Pass the anchor that initiated this request allows - // loadError to manipulate the tab content panel via $(a.hash) - o.ajaxOptions.error( xhr, s, index, a ); - } - catch ( e ) {} - } - } ) ); - - // last, so that load event is fired before show... - self.element.dequeue( "tabs" ); - - return this; - }, - - abort: function() { - // stop possibly running animations - this.element.queue( [] ); - this.panels.stop( false, true ); - - // "tabs" queue must not contain more than two elements, - // which are the callbacks for the latest clicked tab... - this.element.queue( "tabs", this.element.queue( "tabs" ).splice( -2, 2 ) ); - - // terminate pending requests from other tabs - if ( this.xhr ) { - this.xhr.abort(); - delete this.xhr; - } - - // take care of tab labels - this._cleanup(); - return this; - }, - - url: function( index, url ) { - this.anchors.eq( index ).removeData( "cache.tabs" ).data( "load.tabs", url ); - return this; - }, - - length: function() { - return this.anchors.length; - } -}); - -$.extend( $.ui.tabs, { - version: "1.8.23" -}); - -/* - * Tabs Extensions - */ - -/* - * Rotate - */ -$.extend( $.ui.tabs.prototype, { - rotation: null, - rotate: function( ms, continuing ) { - var self = this, - o = this.options; - - var rotate = self._rotate || ( self._rotate = function( e ) { - clearTimeout( self.rotation ); - self.rotation = setTimeout(function() { - var t = o.selected; - self.select( ++t < self.anchors.length ? t : 0 ); - }, ms ); - - if ( e ) { - e.stopPropagation(); - } - }); - - var stop = self._unrotate || ( self._unrotate = !continuing - ? function(e) { - if (e.clientX) { // in case of a true click - self.rotate(null); - } - } - : function( e ) { - rotate(); - }); - - // start rotation - if ( ms ) { - this.element.bind( "tabsshow", rotate ); - this.anchors.bind( o.event + ".tabs", stop ); - rotate(); - // stop rotation - } else { - clearTimeout( self.rotation ); - this.element.unbind( "tabsshow", rotate ); - this.anchors.unbind( o.event + ".tabs", stop ); - delete this._rotate; - delete this._unrotate; - } - - return this; - } -}); - -})( jQuery ); -/*! - * jQuery UI Datepicker 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Datepicker - * - * Depends: - * jquery.ui.core.js - */ -(function( $, undefined ) { - -$.extend($.ui, { datepicker: { version: "1.8.23" } }); - -var PROP_NAME = 'datepicker'; -var dpuuid = new Date().getTime(); -var instActive; - -/* Date picker manager. - Use the singleton instance of this class, $.datepicker, to interact with the date picker. - Settings for (groups of) date pickers are maintained in an instance object, - allowing multiple different settings on the same page. */ - -function Datepicker() { - this.debug = false; // Change this to true to start debugging - this._curInst = null; // The current instance in use - this._keyEvent = false; // If the last event was a key event - this._disabledInputs = []; // List of date picker inputs that have been disabled - this._datepickerShowing = false; // True if the popup picker is showing , false if not - this._inDialog = false; // True if showing within a "dialog", false if not - this._mainDivId = 'ui-datepicker-div'; // The ID of the main datepicker division - this._inlineClass = 'ui-datepicker-inline'; // The name of the inline marker class - this._appendClass = 'ui-datepicker-append'; // The name of the append marker class - this._triggerClass = 'ui-datepicker-trigger'; // The name of the trigger marker class - this._dialogClass = 'ui-datepicker-dialog'; // The name of the dialog marker class - this._disableClass = 'ui-datepicker-disabled'; // The name of the disabled covering marker class - this._unselectableClass = 'ui-datepicker-unselectable'; // The name of the unselectable cell marker class - this._currentClass = 'ui-datepicker-current-day'; // The name of the current day marker class - this._dayOverClass = 'ui-datepicker-days-cell-over'; // The name of the day hover marker class - this.regional = []; // Available regional settings, indexed by language code - this.regional[''] = { // Default regional settings - closeText: 'Done', // Display text for close link - prevText: 'Prev', // Display text for previous month link - nextText: 'Next', // Display text for next month link - currentText: 'Today', // Display text for current month link - monthNames: ['January','February','March','April','May','June', - 'July','August','September','October','November','December'], // Names of months for drop-down and formatting - monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], // For formatting - dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], // For formatting - dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], // For formatting - dayNamesMin: ['Su','Mo','Tu','We','Th','Fr','Sa'], // Column headings for days starting at Sunday - weekHeader: 'Wk', // Column header for week of the year - dateFormat: 'mm/dd/yy', // See format options on parseDate - firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ... - isRTL: false, // True if right-to-left language, false if left-to-right - showMonthAfterYear: false, // True if the year select precedes month, false for month then year - yearSuffix: '' // Additional text to append to the year in the month headers - }; - this._defaults = { // Global defaults for all the date picker instances - showOn: 'focus', // 'focus' for popup on focus, - // 'button' for trigger button, or 'both' for either - showAnim: 'fadeIn', // Name of jQuery animation for popup - showOptions: {}, // Options for enhanced animations - defaultDate: null, // Used when field is blank: actual date, - // +/-number for offset from today, null for today - appendText: '', // Display text following the input box, e.g. showing the format - buttonText: '...', // Text for trigger button - buttonImage: '', // URL for trigger button image - buttonImageOnly: false, // True if the image appears alone, false if it appears on a button - hideIfNoPrevNext: false, // True to hide next/previous month links - // if not applicable, false to just disable them - navigationAsDateFormat: false, // True if date formatting applied to prev/today/next links - gotoCurrent: false, // True if today link goes back to current selection instead - changeMonth: false, // True if month can be selected directly, false if only prev/next - changeYear: false, // True if year can be selected directly, false if only prev/next - yearRange: 'c-10:c+10', // Range of years to display in drop-down, - // either relative to today's year (-nn:+nn), relative to currently displayed year - // (c-nn:c+nn), absolute (nnnn:nnnn), or a combination of the above (nnnn:-n) - showOtherMonths: false, // True to show dates in other months, false to leave blank - selectOtherMonths: false, // True to allow selection of dates in other months, false for unselectable - showWeek: false, // True to show week of the year, false to not show it - calculateWeek: this.iso8601Week, // How to calculate the week of the year, - // takes a Date and returns the number of the week for it - shortYearCutoff: '+10', // Short year values < this are in the current century, - // > this are in the previous century, - // string value starting with '+' for current year + value - minDate: null, // The earliest selectable date, or null for no limit - maxDate: null, // The latest selectable date, or null for no limit - duration: 'fast', // Duration of display/closure - beforeShowDay: null, // Function that takes a date and returns an array with - // [0] = true if selectable, false if not, [1] = custom CSS class name(s) or '', - // [2] = cell title (optional), e.g. $.datepicker.noWeekends - beforeShow: null, // Function that takes an input field and - // returns a set of custom settings for the date picker - onSelect: null, // Define a callback function when a date is selected - onChangeMonthYear: null, // Define a callback function when the month or year is changed - onClose: null, // Define a callback function when the datepicker is closed - numberOfMonths: 1, // Number of months to show at a time - showCurrentAtPos: 0, // The position in multipe months at which to show the current month (starting at 0) - stepMonths: 1, // Number of months to step back/forward - stepBigMonths: 12, // Number of months to step back/forward for the big links - altField: '', // Selector for an alternate field to store selected dates into - altFormat: '', // The date format to use for the alternate field - constrainInput: true, // The input is constrained by the current date format - showButtonPanel: false, // True to show button panel, false to not show it - autoSize: false, // True to size the input for the date format, false to leave as is - disabled: false // The initial disabled state - }; - $.extend(this._defaults, this.regional['']); - this.dpDiv = bindHover($('
      ')); -} - -$.extend(Datepicker.prototype, { - /* Class name added to elements to indicate already configured with a date picker. */ - markerClassName: 'hasDatepicker', - - //Keep track of the maximum number of rows displayed (see #7043) - maxRows: 4, - - /* Debug logging (if enabled). */ - log: function () { - if (this.debug) - console.log.apply('', arguments); - }, - - // TODO rename to "widget" when switching to widget factory - _widgetDatepicker: function() { - return this.dpDiv; - }, - - /* Override the default settings for all instances of the date picker. - @param settings object - the new settings to use as defaults (anonymous object) - @return the manager object */ - setDefaults: function(settings) { - extendRemove(this._defaults, settings || {}); - return this; - }, - - /* Attach the date picker to a jQuery selection. - @param target element - the target input field or division or span - @param settings object - the new settings to use for this date picker instance (anonymous) */ - _attachDatepicker: function(target, settings) { - // check for settings on the control itself - in namespace 'date:' - var inlineSettings = null; - for (var attrName in this._defaults) { - var attrValue = target.getAttribute('date:' + attrName); - if (attrValue) { - inlineSettings = inlineSettings || {}; - try { - inlineSettings[attrName] = eval(attrValue); - } catch (err) { - inlineSettings[attrName] = attrValue; - } - } - } - var nodeName = target.nodeName.toLowerCase(); - var inline = (nodeName == 'div' || nodeName == 'span'); - if (!target.id) { - this.uuid += 1; - target.id = 'dp' + this.uuid; - } - var inst = this._newInst($(target), inline); - inst.settings = $.extend({}, settings || {}, inlineSettings || {}); - if (nodeName == 'input') { - this._connectDatepicker(target, inst); - } else if (inline) { - this._inlineDatepicker(target, inst); - } - }, - - /* Create a new instance object. */ - _newInst: function(target, inline) { - var id = target[0].id.replace(/([^A-Za-z0-9_-])/g, '\\\\$1'); // escape jQuery meta chars - return {id: id, input: target, // associated target - selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current selection - drawMonth: 0, drawYear: 0, // month being drawn - inline: inline, // is datepicker inline or not - dpDiv: (!inline ? this.dpDiv : // presentation div - bindHover($('
      ')))}; - }, - - /* Attach the date picker to an input field. */ - _connectDatepicker: function(target, inst) { - var input = $(target); - inst.append = $([]); - inst.trigger = $([]); - if (input.hasClass(this.markerClassName)) - return; - this._attachments(input, inst); - input.addClass(this.markerClassName).keydown(this._doKeyDown). - keypress(this._doKeyPress).keyup(this._doKeyUp). - bind("setData.datepicker", function(event, key, value) { - inst.settings[key] = value; - }).bind("getData.datepicker", function(event, key) { - return this._get(inst, key); - }); - this._autoSize(inst); - $.data(target, PROP_NAME, inst); - //If disabled option is true, disable the datepicker once it has been attached to the input (see ticket #5665) - if( inst.settings.disabled ) { - this._disableDatepicker( target ); - } - }, - - /* Make attachments based on settings. */ - _attachments: function(input, inst) { - var appendText = this._get(inst, 'appendText'); - var isRTL = this._get(inst, 'isRTL'); - if (inst.append) - inst.append.remove(); - if (appendText) { - inst.append = $('' + appendText + ''); - input[isRTL ? 'before' : 'after'](inst.append); - } - input.unbind('focus', this._showDatepicker); - if (inst.trigger) - inst.trigger.remove(); - var showOn = this._get(inst, 'showOn'); - if (showOn == 'focus' || showOn == 'both') // pop-up date picker when in the marked field - input.focus(this._showDatepicker); - if (showOn == 'button' || showOn == 'both') { // pop-up date picker when button clicked - var buttonText = this._get(inst, 'buttonText'); - var buttonImage = this._get(inst, 'buttonImage'); - inst.trigger = $(this._get(inst, 'buttonImageOnly') ? - $('').addClass(this._triggerClass). - attr({ src: buttonImage, alt: buttonText, title: buttonText }) : - $('').addClass(this._triggerClass). - html(buttonImage == '' ? buttonText : $('').attr( - { src:buttonImage, alt:buttonText, title:buttonText }))); - input[isRTL ? 'before' : 'after'](inst.trigger); - inst.trigger.click(function() { - if ($.datepicker._datepickerShowing && $.datepicker._lastInput == input[0]) - $.datepicker._hideDatepicker(); - else if ($.datepicker._datepickerShowing && $.datepicker._lastInput != input[0]) { - $.datepicker._hideDatepicker(); - $.datepicker._showDatepicker(input[0]); - } else - $.datepicker._showDatepicker(input[0]); - return false; - }); - } - }, - - /* Apply the maximum length for the date format. */ - _autoSize: function(inst) { - if (this._get(inst, 'autoSize') && !inst.inline) { - var date = new Date(2009, 12 - 1, 20); // Ensure double digits - var dateFormat = this._get(inst, 'dateFormat'); - if (dateFormat.match(/[DM]/)) { - var findMax = function(names) { - var max = 0; - var maxI = 0; - for (var i = 0; i < names.length; i++) { - if (names[i].length > max) { - max = names[i].length; - maxI = i; - } - } - return maxI; - }; - date.setMonth(findMax(this._get(inst, (dateFormat.match(/MM/) ? - 'monthNames' : 'monthNamesShort')))); - date.setDate(findMax(this._get(inst, (dateFormat.match(/DD/) ? - 'dayNames' : 'dayNamesShort'))) + 20 - date.getDay()); - } - inst.input.attr('size', this._formatDate(inst, date).length); - } - }, - - /* Attach an inline date picker to a div. */ - _inlineDatepicker: function(target, inst) { - var divSpan = $(target); - if (divSpan.hasClass(this.markerClassName)) - return; - divSpan.addClass(this.markerClassName).append(inst.dpDiv). - bind("setData.datepicker", function(event, key, value){ - inst.settings[key] = value; - }).bind("getData.datepicker", function(event, key){ - return this._get(inst, key); - }); - $.data(target, PROP_NAME, inst); - this._setDate(inst, this._getDefaultDate(inst), true); - this._updateDatepicker(inst); - this._updateAlternate(inst); - //If disabled option is true, disable the datepicker before showing it (see ticket #5665) - if( inst.settings.disabled ) { - this._disableDatepicker( target ); - } - // Set display:block in place of inst.dpDiv.show() which won't work on disconnected elements - // http://bugs.jqueryui.com/ticket/7552 - A Datepicker created on a detached div has zero height - inst.dpDiv.css( "display", "block" ); - }, - - /* Pop-up the date picker in a "dialog" box. - @param input element - ignored - @param date string or Date - the initial date to display - @param onSelect function - the function to call when a date is selected - @param settings object - update the dialog date picker instance's settings (anonymous object) - @param pos int[2] - coordinates for the dialog's position within the screen or - event - with x/y coordinates or - leave empty for default (screen centre) - @return the manager object */ - _dialogDatepicker: function(input, date, onSelect, settings, pos) { - var inst = this._dialogInst; // internal instance - if (!inst) { - this.uuid += 1; - var id = 'dp' + this.uuid; - this._dialogInput = $(''); - this._dialogInput.keydown(this._doKeyDown); - $('body').append(this._dialogInput); - inst = this._dialogInst = this._newInst(this._dialogInput, false); - inst.settings = {}; - $.data(this._dialogInput[0], PROP_NAME, inst); - } - extendRemove(inst.settings, settings || {}); - date = (date && date.constructor == Date ? this._formatDate(inst, date) : date); - this._dialogInput.val(date); - - this._pos = (pos ? (pos.length ? pos : [pos.pageX, pos.pageY]) : null); - if (!this._pos) { - var browserWidth = document.documentElement.clientWidth; - var browserHeight = document.documentElement.clientHeight; - var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft; - var scrollY = document.documentElement.scrollTop || document.body.scrollTop; - this._pos = // should use actual width/height below - [(browserWidth / 2) - 100 + scrollX, (browserHeight / 2) - 150 + scrollY]; - } - - // move input on screen for focus, but hidden behind dialog - this._dialogInput.css('left', (this._pos[0] + 20) + 'px').css('top', this._pos[1] + 'px'); - inst.settings.onSelect = onSelect; - this._inDialog = true; - this.dpDiv.addClass(this._dialogClass); - this._showDatepicker(this._dialogInput[0]); - if ($.blockUI) - $.blockUI(this.dpDiv); - $.data(this._dialogInput[0], PROP_NAME, inst); - return this; - }, - - /* Detach a datepicker from its control. - @param target element - the target input field or division or span */ - _destroyDatepicker: function(target) { - var $target = $(target); - var inst = $.data(target, PROP_NAME); - if (!$target.hasClass(this.markerClassName)) { - return; - } - var nodeName = target.nodeName.toLowerCase(); - $.removeData(target, PROP_NAME); - if (nodeName == 'input') { - inst.append.remove(); - inst.trigger.remove(); - $target.removeClass(this.markerClassName). - unbind('focus', this._showDatepicker). - unbind('keydown', this._doKeyDown). - unbind('keypress', this._doKeyPress). - unbind('keyup', this._doKeyUp); - } else if (nodeName == 'div' || nodeName == 'span') - $target.removeClass(this.markerClassName).empty(); - }, - - /* Enable the date picker to a jQuery selection. - @param target element - the target input field or division or span */ - _enableDatepicker: function(target) { - var $target = $(target); - var inst = $.data(target, PROP_NAME); - if (!$target.hasClass(this.markerClassName)) { - return; - } - var nodeName = target.nodeName.toLowerCase(); - if (nodeName == 'input') { - target.disabled = false; - inst.trigger.filter('button'). - each(function() { this.disabled = false; }).end(). - filter('img').css({opacity: '1.0', cursor: ''}); - } - else if (nodeName == 'div' || nodeName == 'span') { - var inline = $target.children('.' + this._inlineClass); - inline.children().removeClass('ui-state-disabled'); - inline.find("select.ui-datepicker-month, select.ui-datepicker-year"). - removeAttr("disabled"); - } - this._disabledInputs = $.map(this._disabledInputs, - function(value) { return (value == target ? null : value); }); // delete entry - }, - - /* Disable the date picker to a jQuery selection. - @param target element - the target input field or division or span */ - _disableDatepicker: function(target) { - var $target = $(target); - var inst = $.data(target, PROP_NAME); - if (!$target.hasClass(this.markerClassName)) { - return; - } - var nodeName = target.nodeName.toLowerCase(); - if (nodeName == 'input') { - target.disabled = true; - inst.trigger.filter('button'). - each(function() { this.disabled = true; }).end(). - filter('img').css({opacity: '0.5', cursor: 'default'}); - } - else if (nodeName == 'div' || nodeName == 'span') { - var inline = $target.children('.' + this._inlineClass); - inline.children().addClass('ui-state-disabled'); - inline.find("select.ui-datepicker-month, select.ui-datepicker-year"). - attr("disabled", "disabled"); - } - this._disabledInputs = $.map(this._disabledInputs, - function(value) { return (value == target ? null : value); }); // delete entry - this._disabledInputs[this._disabledInputs.length] = target; - }, - - /* Is the first field in a jQuery collection disabled as a datepicker? - @param target element - the target input field or division or span - @return boolean - true if disabled, false if enabled */ - _isDisabledDatepicker: function(target) { - if (!target) { - return false; - } - for (var i = 0; i < this._disabledInputs.length; i++) { - if (this._disabledInputs[i] == target) - return true; - } - return false; - }, - - /* Retrieve the instance data for the target control. - @param target element - the target input field or division or span - @return object - the associated instance data - @throws error if a jQuery problem getting data */ - _getInst: function(target) { - try { - return $.data(target, PROP_NAME); - } - catch (err) { - throw 'Missing instance data for this datepicker'; - } - }, - - /* Update or retrieve the settings for a date picker attached to an input field or division. - @param target element - the target input field or division or span - @param name object - the new settings to update or - string - the name of the setting to change or retrieve, - when retrieving also 'all' for all instance settings or - 'defaults' for all global defaults - @param value any - the new value for the setting - (omit if above is an object or to retrieve a value) */ - _optionDatepicker: function(target, name, value) { - var inst = this._getInst(target); - if (arguments.length == 2 && typeof name == 'string') { - return (name == 'defaults' ? $.extend({}, $.datepicker._defaults) : - (inst ? (name == 'all' ? $.extend({}, inst.settings) : - this._get(inst, name)) : null)); - } - var settings = name || {}; - if (typeof name == 'string') { - settings = {}; - settings[name] = value; - } - if (inst) { - if (this._curInst == inst) { - this._hideDatepicker(); - } - var date = this._getDateDatepicker(target, true); - var minDate = this._getMinMaxDate(inst, 'min'); - var maxDate = this._getMinMaxDate(inst, 'max'); - extendRemove(inst.settings, settings); - // reformat the old minDate/maxDate values if dateFormat changes and a new minDate/maxDate isn't provided - if (minDate !== null && settings['dateFormat'] !== undefined && settings['minDate'] === undefined) - inst.settings.minDate = this._formatDate(inst, minDate); - if (maxDate !== null && settings['dateFormat'] !== undefined && settings['maxDate'] === undefined) - inst.settings.maxDate = this._formatDate(inst, maxDate); - this._attachments($(target), inst); - this._autoSize(inst); - this._setDate(inst, date); - this._updateAlternate(inst); - this._updateDatepicker(inst); - } - }, - - // change method deprecated - _changeDatepicker: function(target, name, value) { - this._optionDatepicker(target, name, value); - }, - - /* Redraw the date picker attached to an input field or division. - @param target element - the target input field or division or span */ - _refreshDatepicker: function(target) { - var inst = this._getInst(target); - if (inst) { - this._updateDatepicker(inst); - } - }, - - /* Set the dates for a jQuery selection. - @param target element - the target input field or division or span - @param date Date - the new date */ - _setDateDatepicker: function(target, date) { - var inst = this._getInst(target); - if (inst) { - this._setDate(inst, date); - this._updateDatepicker(inst); - this._updateAlternate(inst); - } - }, - - /* Get the date(s) for the first entry in a jQuery selection. - @param target element - the target input field or division or span - @param noDefault boolean - true if no default date is to be used - @return Date - the current date */ - _getDateDatepicker: function(target, noDefault) { - var inst = this._getInst(target); - if (inst && !inst.inline) - this._setDateFromField(inst, noDefault); - return (inst ? this._getDate(inst) : null); - }, - - /* Handle keystrokes. */ - _doKeyDown: function(event) { - var inst = $.datepicker._getInst(event.target); - var handled = true; - var isRTL = inst.dpDiv.is('.ui-datepicker-rtl'); - inst._keyEvent = true; - if ($.datepicker._datepickerShowing) - switch (event.keyCode) { - case 9: $.datepicker._hideDatepicker(); - handled = false; - break; // hide on tab out - case 13: var sel = $('td.' + $.datepicker._dayOverClass + ':not(.' + - $.datepicker._currentClass + ')', inst.dpDiv); - if (sel[0]) - $.datepicker._selectDay(event.target, inst.selectedMonth, inst.selectedYear, sel[0]); - var onSelect = $.datepicker._get(inst, 'onSelect'); - if (onSelect) { - var dateStr = $.datepicker._formatDate(inst); - - // trigger custom callback - onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]); - } - else - $.datepicker._hideDatepicker(); - return false; // don't submit the form - break; // select the value on enter - case 27: $.datepicker._hideDatepicker(); - break; // hide on escape - case 33: $.datepicker._adjustDate(event.target, (event.ctrlKey ? - -$.datepicker._get(inst, 'stepBigMonths') : - -$.datepicker._get(inst, 'stepMonths')), 'M'); - break; // previous month/year on page up/+ ctrl - case 34: $.datepicker._adjustDate(event.target, (event.ctrlKey ? - +$.datepicker._get(inst, 'stepBigMonths') : - +$.datepicker._get(inst, 'stepMonths')), 'M'); - break; // next month/year on page down/+ ctrl - case 35: if (event.ctrlKey || event.metaKey) $.datepicker._clearDate(event.target); - handled = event.ctrlKey || event.metaKey; - break; // clear on ctrl or command +end - case 36: if (event.ctrlKey || event.metaKey) $.datepicker._gotoToday(event.target); - handled = event.ctrlKey || event.metaKey; - break; // current on ctrl or command +home - case 37: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, (isRTL ? +1 : -1), 'D'); - handled = event.ctrlKey || event.metaKey; - // -1 day on ctrl or command +left - if (event.originalEvent.altKey) $.datepicker._adjustDate(event.target, (event.ctrlKey ? - -$.datepicker._get(inst, 'stepBigMonths') : - -$.datepicker._get(inst, 'stepMonths')), 'M'); - // next month/year on alt +left on Mac - break; - case 38: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, -7, 'D'); - handled = event.ctrlKey || event.metaKey; - break; // -1 week on ctrl or command +up - case 39: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, (isRTL ? -1 : +1), 'D'); - handled = event.ctrlKey || event.metaKey; - // +1 day on ctrl or command +right - if (event.originalEvent.altKey) $.datepicker._adjustDate(event.target, (event.ctrlKey ? - +$.datepicker._get(inst, 'stepBigMonths') : - +$.datepicker._get(inst, 'stepMonths')), 'M'); - // next month/year on alt +right - break; - case 40: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, +7, 'D'); - handled = event.ctrlKey || event.metaKey; - break; // +1 week on ctrl or command +down - default: handled = false; - } - else if (event.keyCode == 36 && event.ctrlKey) // display the date picker on ctrl+home - $.datepicker._showDatepicker(this); - else { - handled = false; - } - if (handled) { - event.preventDefault(); - event.stopPropagation(); - } - }, - - /* Filter entered characters - based on date format. */ - _doKeyPress: function(event) { - var inst = $.datepicker._getInst(event.target); - if ($.datepicker._get(inst, 'constrainInput')) { - var chars = $.datepicker._possibleChars($.datepicker._get(inst, 'dateFormat')); - var chr = String.fromCharCode(event.charCode == undefined ? event.keyCode : event.charCode); - return event.ctrlKey || event.metaKey || (chr < ' ' || !chars || chars.indexOf(chr) > -1); - } - }, - - /* Synchronise manual entry and field/alternate field. */ - _doKeyUp: function(event) { - var inst = $.datepicker._getInst(event.target); - if (inst.input.val() != inst.lastVal) { - try { - var date = $.datepicker.parseDate($.datepicker._get(inst, 'dateFormat'), - (inst.input ? inst.input.val() : null), - $.datepicker._getFormatConfig(inst)); - if (date) { // only if valid - $.datepicker._setDateFromField(inst); - $.datepicker._updateAlternate(inst); - $.datepicker._updateDatepicker(inst); - } - } - catch (err) { - $.datepicker.log(err); - } - } - return true; - }, - - /* Pop-up the date picker for a given input field. - If false returned from beforeShow event handler do not show. - @param input element - the input field attached to the date picker or - event - if triggered by focus */ - _showDatepicker: function(input) { - input = input.target || input; - if (input.nodeName.toLowerCase() != 'input') // find from button/image trigger - input = $('input', input.parentNode)[0]; - if ($.datepicker._isDisabledDatepicker(input) || $.datepicker._lastInput == input) // already here - return; - var inst = $.datepicker._getInst(input); - if ($.datepicker._curInst && $.datepicker._curInst != inst) { - $.datepicker._curInst.dpDiv.stop(true, true); - if ( inst && $.datepicker._datepickerShowing ) { - $.datepicker._hideDatepicker( $.datepicker._curInst.input[0] ); - } - } - var beforeShow = $.datepicker._get(inst, 'beforeShow'); - var beforeShowSettings = beforeShow ? beforeShow.apply(input, [input, inst]) : {}; - if(beforeShowSettings === false){ - //false - return; - } - extendRemove(inst.settings, beforeShowSettings); - inst.lastVal = null; - $.datepicker._lastInput = input; - $.datepicker._setDateFromField(inst); - if ($.datepicker._inDialog) // hide cursor - input.value = ''; - if (!$.datepicker._pos) { // position below input - $.datepicker._pos = $.datepicker._findPos(input); - $.datepicker._pos[1] += input.offsetHeight; // add the height - } - var isFixed = false; - $(input).parents().each(function() { - isFixed |= $(this).css('position') == 'fixed'; - return !isFixed; - }); - if (isFixed && $.browser.opera) { // correction for Opera when fixed and scrolled - $.datepicker._pos[0] -= document.documentElement.scrollLeft; - $.datepicker._pos[1] -= document.documentElement.scrollTop; - } - var offset = {left: $.datepicker._pos[0], top: $.datepicker._pos[1]}; - $.datepicker._pos = null; - //to avoid flashes on Firefox - inst.dpDiv.empty(); - // determine sizing offscreen - inst.dpDiv.css({position: 'absolute', display: 'block', top: '-1000px'}); - $.datepicker._updateDatepicker(inst); - // fix width for dynamic number of date pickers - // and adjust position before showing - offset = $.datepicker._checkOffset(inst, offset, isFixed); - inst.dpDiv.css({position: ($.datepicker._inDialog && $.blockUI ? - 'static' : (isFixed ? 'fixed' : 'absolute')), display: 'none', - left: offset.left + 'px', top: offset.top + 'px'}); - if (!inst.inline) { - var showAnim = $.datepicker._get(inst, 'showAnim'); - var duration = $.datepicker._get(inst, 'duration'); - var postProcess = function() { - var cover = inst.dpDiv.find('iframe.ui-datepicker-cover'); // IE6- only - if( !! cover.length ){ - var borders = $.datepicker._getBorders(inst.dpDiv); - cover.css({left: -borders[0], top: -borders[1], - width: inst.dpDiv.outerWidth(), height: inst.dpDiv.outerHeight()}); - } - }; - inst.dpDiv.zIndex($(input).zIndex()+1); - $.datepicker._datepickerShowing = true; - if ($.effects && $.effects[showAnim]) - inst.dpDiv.show(showAnim, $.datepicker._get(inst, 'showOptions'), duration, postProcess); - else - inst.dpDiv[showAnim || 'show']((showAnim ? duration : null), postProcess); - if (!showAnim || !duration) - postProcess(); - if (inst.input.is(':visible') && !inst.input.is(':disabled')) - inst.input.focus(); - $.datepicker._curInst = inst; - } - }, - - /* Generate the date picker content. */ - _updateDatepicker: function(inst) { - var self = this; - self.maxRows = 4; //Reset the max number of rows being displayed (see #7043) - var borders = $.datepicker._getBorders(inst.dpDiv); - instActive = inst; // for delegate hover events - inst.dpDiv.empty().append(this._generateHTML(inst)); - this._attachHandlers(inst); - var cover = inst.dpDiv.find('iframe.ui-datepicker-cover'); // IE6- only - if( !!cover.length ){ //avoid call to outerXXXX() when not in IE6 - cover.css({left: -borders[0], top: -borders[1], width: inst.dpDiv.outerWidth(), height: inst.dpDiv.outerHeight()}) - } - inst.dpDiv.find('.' + this._dayOverClass + ' a').mouseover(); - var numMonths = this._getNumberOfMonths(inst); - var cols = numMonths[1]; - var width = 17; - inst.dpDiv.removeClass('ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4').width(''); - if (cols > 1) - inst.dpDiv.addClass('ui-datepicker-multi-' + cols).css('width', (width * cols) + 'em'); - inst.dpDiv[(numMonths[0] != 1 || numMonths[1] != 1 ? 'add' : 'remove') + - 'Class']('ui-datepicker-multi'); - inst.dpDiv[(this._get(inst, 'isRTL') ? 'add' : 'remove') + - 'Class']('ui-datepicker-rtl'); - if (inst == $.datepicker._curInst && $.datepicker._datepickerShowing && inst.input && - // #6694 - don't focus the input if it's already focused - // this breaks the change event in IE - inst.input.is(':visible') && !inst.input.is(':disabled') && inst.input[0] != document.activeElement) - inst.input.focus(); - // deffered render of the years select (to avoid flashes on Firefox) - if( inst.yearshtml ){ - var origyearshtml = inst.yearshtml; - setTimeout(function(){ - //assure that inst.yearshtml didn't change. - if( origyearshtml === inst.yearshtml && inst.yearshtml ){ - inst.dpDiv.find('select.ui-datepicker-year:first').replaceWith(inst.yearshtml); - } - origyearshtml = inst.yearshtml = null; - }, 0); - } - }, - - /* Retrieve the size of left and top borders for an element. - @param elem (jQuery object) the element of interest - @return (number[2]) the left and top borders */ - _getBorders: function(elem) { - var convert = function(value) { - return {thin: 1, medium: 2, thick: 3}[value] || value; - }; - return [parseFloat(convert(elem.css('border-left-width'))), - parseFloat(convert(elem.css('border-top-width')))]; - }, - - /* Check positioning to remain on screen. */ - _checkOffset: function(inst, offset, isFixed) { - var dpWidth = inst.dpDiv.outerWidth(); - var dpHeight = inst.dpDiv.outerHeight(); - var inputWidth = inst.input ? inst.input.outerWidth() : 0; - var inputHeight = inst.input ? inst.input.outerHeight() : 0; - var viewWidth = document.documentElement.clientWidth + (isFixed ? 0 : $(document).scrollLeft()); - var viewHeight = document.documentElement.clientHeight + (isFixed ? 0 : $(document).scrollTop()); - - offset.left -= (this._get(inst, 'isRTL') ? (dpWidth - inputWidth) : 0); - offset.left -= (isFixed && offset.left == inst.input.offset().left) ? $(document).scrollLeft() : 0; - offset.top -= (isFixed && offset.top == (inst.input.offset().top + inputHeight)) ? $(document).scrollTop() : 0; - - // now check if datepicker is showing outside window viewport - move to a better place if so. - offset.left -= Math.min(offset.left, (offset.left + dpWidth > viewWidth && viewWidth > dpWidth) ? - Math.abs(offset.left + dpWidth - viewWidth) : 0); - offset.top -= Math.min(offset.top, (offset.top + dpHeight > viewHeight && viewHeight > dpHeight) ? - Math.abs(dpHeight + inputHeight) : 0); - - return offset; - }, - - /* Find an object's position on the screen. */ - _findPos: function(obj) { - var inst = this._getInst(obj); - var isRTL = this._get(inst, 'isRTL'); - while (obj && (obj.type == 'hidden' || obj.nodeType != 1 || $.expr.filters.hidden(obj))) { - obj = obj[isRTL ? 'previousSibling' : 'nextSibling']; - } - var position = $(obj).offset(); - return [position.left, position.top]; - }, - - /* Hide the date picker from view. - @param input element - the input field attached to the date picker */ - _hideDatepicker: function(input) { - var inst = this._curInst; - if (!inst || (input && inst != $.data(input, PROP_NAME))) - return; - if (this._datepickerShowing) { - var showAnim = this._get(inst, 'showAnim'); - var duration = this._get(inst, 'duration'); - var postProcess = function() { - $.datepicker._tidyDialog(inst); - }; - if ($.effects && $.effects[showAnim]) - inst.dpDiv.hide(showAnim, $.datepicker._get(inst, 'showOptions'), duration, postProcess); - else - inst.dpDiv[(showAnim == 'slideDown' ? 'slideUp' : - (showAnim == 'fadeIn' ? 'fadeOut' : 'hide'))]((showAnim ? duration : null), postProcess); - if (!showAnim) - postProcess(); - this._datepickerShowing = false; - var onClose = this._get(inst, 'onClose'); - if (onClose) - onClose.apply((inst.input ? inst.input[0] : null), - [(inst.input ? inst.input.val() : ''), inst]); - this._lastInput = null; - if (this._inDialog) { - this._dialogInput.css({ position: 'absolute', left: '0', top: '-100px' }); - if ($.blockUI) { - $.unblockUI(); - $('body').append(this.dpDiv); - } - } - this._inDialog = false; - } - }, - - /* Tidy up after a dialog display. */ - _tidyDialog: function(inst) { - inst.dpDiv.removeClass(this._dialogClass).unbind('.ui-datepicker-calendar'); - }, - - /* Close date picker if clicked elsewhere. */ - _checkExternalClick: function(event) { - if (!$.datepicker._curInst) - return; - - var $target = $(event.target), - inst = $.datepicker._getInst($target[0]); - - if ( ( ( $target[0].id != $.datepicker._mainDivId && - $target.parents('#' + $.datepicker._mainDivId).length == 0 && - !$target.hasClass($.datepicker.markerClassName) && - !$target.closest("." + $.datepicker._triggerClass).length && - $.datepicker._datepickerShowing && !($.datepicker._inDialog && $.blockUI) ) ) || - ( $target.hasClass($.datepicker.markerClassName) && $.datepicker._curInst != inst ) ) - $.datepicker._hideDatepicker(); - }, - - /* Adjust one of the date sub-fields. */ - _adjustDate: function(id, offset, period) { - var target = $(id); - var inst = this._getInst(target[0]); - if (this._isDisabledDatepicker(target[0])) { - return; - } - this._adjustInstDate(inst, offset + - (period == 'M' ? this._get(inst, 'showCurrentAtPos') : 0), // undo positioning - period); - this._updateDatepicker(inst); - }, - - /* Action for current link. */ - _gotoToday: function(id) { - var target = $(id); - var inst = this._getInst(target[0]); - if (this._get(inst, 'gotoCurrent') && inst.currentDay) { - inst.selectedDay = inst.currentDay; - inst.drawMonth = inst.selectedMonth = inst.currentMonth; - inst.drawYear = inst.selectedYear = inst.currentYear; - } - else { - var date = new Date(); - inst.selectedDay = date.getDate(); - inst.drawMonth = inst.selectedMonth = date.getMonth(); - inst.drawYear = inst.selectedYear = date.getFullYear(); - } - this._notifyChange(inst); - this._adjustDate(target); - }, - - /* Action for selecting a new month/year. */ - _selectMonthYear: function(id, select, period) { - var target = $(id); - var inst = this._getInst(target[0]); - inst['selected' + (period == 'M' ? 'Month' : 'Year')] = - inst['draw' + (period == 'M' ? 'Month' : 'Year')] = - parseInt(select.options[select.selectedIndex].value,10); - this._notifyChange(inst); - this._adjustDate(target); - }, - - /* Action for selecting a day. */ - _selectDay: function(id, month, year, td) { - var target = $(id); - if ($(td).hasClass(this._unselectableClass) || this._isDisabledDatepicker(target[0])) { - return; - } - var inst = this._getInst(target[0]); - inst.selectedDay = inst.currentDay = $('a', td).html(); - inst.selectedMonth = inst.currentMonth = month; - inst.selectedYear = inst.currentYear = year; - this._selectDate(id, this._formatDate(inst, - inst.currentDay, inst.currentMonth, inst.currentYear)); - }, - - /* Erase the input field and hide the date picker. */ - _clearDate: function(id) { - var target = $(id); - var inst = this._getInst(target[0]); - this._selectDate(target, ''); - }, - - /* Update the input field with the selected date. */ - _selectDate: function(id, dateStr) { - var target = $(id); - var inst = this._getInst(target[0]); - dateStr = (dateStr != null ? dateStr : this._formatDate(inst)); - if (inst.input) - inst.input.val(dateStr); - this._updateAlternate(inst); - var onSelect = this._get(inst, 'onSelect'); - if (onSelect) - onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]); // trigger custom callback - else if (inst.input) - inst.input.trigger('change'); // fire the change event - if (inst.inline) - this._updateDatepicker(inst); - else { - this._hideDatepicker(); - this._lastInput = inst.input[0]; - if (typeof(inst.input[0]) != 'object') - inst.input.focus(); // restore focus - this._lastInput = null; - } - }, - - /* Update any alternate field to synchronise with the main field. */ - _updateAlternate: function(inst) { - var altField = this._get(inst, 'altField'); - if (altField) { // update alternate field too - var altFormat = this._get(inst, 'altFormat') || this._get(inst, 'dateFormat'); - var date = this._getDate(inst); - var dateStr = this.formatDate(altFormat, date, this._getFormatConfig(inst)); - $(altField).each(function() { $(this).val(dateStr); }); - } - }, - - /* Set as beforeShowDay function to prevent selection of weekends. - @param date Date - the date to customise - @return [boolean, string] - is this date selectable?, what is its CSS class? */ - noWeekends: function(date) { - var day = date.getDay(); - return [(day > 0 && day < 6), '']; - }, - - /* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition. - @param date Date - the date to get the week for - @return number - the number of the week within the year that contains this date */ - iso8601Week: function(date) { - var checkDate = new Date(date.getTime()); - // Find Thursday of this week starting on Monday - checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7)); - var time = checkDate.getTime(); - checkDate.setMonth(0); // Compare with Jan 1 - checkDate.setDate(1); - return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1; - }, - - /* Parse a string value into a date object. - See formatDate below for the possible formats. - - @param format string - the expected format of the date - @param value string - the date in the above format - @param settings Object - attributes include: - shortYearCutoff number - the cutoff year for determining the century (optional) - dayNamesShort string[7] - abbreviated names of the days from Sunday (optional) - dayNames string[7] - names of the days from Sunday (optional) - monthNamesShort string[12] - abbreviated names of the months (optional) - monthNames string[12] - names of the months (optional) - @return Date - the extracted date value or null if value is blank */ - parseDate: function (format, value, settings) { - if (format == null || value == null) - throw 'Invalid arguments'; - value = (typeof value == 'object' ? value.toString() : value + ''); - if (value == '') - return null; - var shortYearCutoff = (settings ? settings.shortYearCutoff : null) || this._defaults.shortYearCutoff; - shortYearCutoff = (typeof shortYearCutoff != 'string' ? shortYearCutoff : - new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10)); - var dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort; - var dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames; - var monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort; - var monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames; - var year = -1; - var month = -1; - var day = -1; - var doy = -1; - var literal = false; - // Check whether a format character is doubled - var lookAhead = function(match) { - var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match); - if (matches) - iFormat++; - return matches; - }; - // Extract a number from the string value - var getNumber = function(match) { - var isDoubled = lookAhead(match); - var size = (match == '@' ? 14 : (match == '!' ? 20 : - (match == 'y' && isDoubled ? 4 : (match == 'o' ? 3 : 2)))); - var digits = new RegExp('^\\d{1,' + size + '}'); - var num = value.substring(iValue).match(digits); - if (!num) - throw 'Missing number at position ' + iValue; - iValue += num[0].length; - return parseInt(num[0], 10); - }; - // Extract a name from the string value and convert to an index - var getName = function(match, shortNames, longNames) { - var names = $.map(lookAhead(match) ? longNames : shortNames, function (v, k) { - return [ [k, v] ]; - }).sort(function (a, b) { - return -(a[1].length - b[1].length); - }); - var index = -1; - $.each(names, function (i, pair) { - var name = pair[1]; - if (value.substr(iValue, name.length).toLowerCase() == name.toLowerCase()) { - index = pair[0]; - iValue += name.length; - return false; - } - }); - if (index != -1) - return index + 1; - else - throw 'Unknown name at position ' + iValue; - }; - // Confirm that a literal character matches the string value - var checkLiteral = function() { - if (value.charAt(iValue) != format.charAt(iFormat)) - throw 'Unexpected literal at position ' + iValue; - iValue++; - }; - var iValue = 0; - for (var iFormat = 0; iFormat < format.length; iFormat++) { - if (literal) - if (format.charAt(iFormat) == "'" && !lookAhead("'")) - literal = false; - else - checkLiteral(); - else - switch (format.charAt(iFormat)) { - case 'd': - day = getNumber('d'); - break; - case 'D': - getName('D', dayNamesShort, dayNames); - break; - case 'o': - doy = getNumber('o'); - break; - case 'm': - month = getNumber('m'); - break; - case 'M': - month = getName('M', monthNamesShort, monthNames); - break; - case 'y': - year = getNumber('y'); - break; - case '@': - var date = new Date(getNumber('@')); - year = date.getFullYear(); - month = date.getMonth() + 1; - day = date.getDate(); - break; - case '!': - var date = new Date((getNumber('!') - this._ticksTo1970) / 10000); - year = date.getFullYear(); - month = date.getMonth() + 1; - day = date.getDate(); - break; - case "'": - if (lookAhead("'")) - checkLiteral(); - else - literal = true; - break; - default: - checkLiteral(); - } - } - if (iValue < value.length){ - throw "Extra/unparsed characters found in date: " + value.substring(iValue); - } - if (year == -1) - year = new Date().getFullYear(); - else if (year < 100) - year += new Date().getFullYear() - new Date().getFullYear() % 100 + - (year <= shortYearCutoff ? 0 : -100); - if (doy > -1) { - month = 1; - day = doy; - do { - var dim = this._getDaysInMonth(year, month - 1); - if (day <= dim) - break; - month++; - day -= dim; - } while (true); - } - var date = this._daylightSavingAdjust(new Date(year, month - 1, day)); - if (date.getFullYear() != year || date.getMonth() + 1 != month || date.getDate() != day) - throw 'Invalid date'; // E.g. 31/02/00 - return date; - }, - - /* Standard date formats. */ - ATOM: 'yy-mm-dd', // RFC 3339 (ISO 8601) - COOKIE: 'D, dd M yy', - ISO_8601: 'yy-mm-dd', - RFC_822: 'D, d M y', - RFC_850: 'DD, dd-M-y', - RFC_1036: 'D, d M y', - RFC_1123: 'D, d M yy', - RFC_2822: 'D, d M yy', - RSS: 'D, d M y', // RFC 822 - TICKS: '!', - TIMESTAMP: '@', - W3C: 'yy-mm-dd', // ISO 8601 - - _ticksTo1970: (((1970 - 1) * 365 + Math.floor(1970 / 4) - Math.floor(1970 / 100) + - Math.floor(1970 / 400)) * 24 * 60 * 60 * 10000000), - - /* Format a date object into a string value. - The format can be combinations of the following: - d - day of month (no leading zero) - dd - day of month (two digit) - o - day of year (no leading zeros) - oo - day of year (three digit) - D - day name short - DD - day name long - m - month of year (no leading zero) - mm - month of year (two digit) - M - month name short - MM - month name long - y - year (two digit) - yy - year (four digit) - @ - Unix timestamp (ms since 01/01/1970) - ! - Windows ticks (100ns since 01/01/0001) - '...' - literal text - '' - single quote - - @param format string - the desired format of the date - @param date Date - the date value to format - @param settings Object - attributes include: - dayNamesShort string[7] - abbreviated names of the days from Sunday (optional) - dayNames string[7] - names of the days from Sunday (optional) - monthNamesShort string[12] - abbreviated names of the months (optional) - monthNames string[12] - names of the months (optional) - @return string - the date in the above format */ - formatDate: function (format, date, settings) { - if (!date) - return ''; - var dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort; - var dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames; - var monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort; - var monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames; - // Check whether a format character is doubled - var lookAhead = function(match) { - var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match); - if (matches) - iFormat++; - return matches; - }; - // Format a number, with leading zero if necessary - var formatNumber = function(match, value, len) { - var num = '' + value; - if (lookAhead(match)) - while (num.length < len) - num = '0' + num; - return num; - }; - // Format a name, short or long as requested - var formatName = function(match, value, shortNames, longNames) { - return (lookAhead(match) ? longNames[value] : shortNames[value]); - }; - var output = ''; - var literal = false; - if (date) - for (var iFormat = 0; iFormat < format.length; iFormat++) { - if (literal) - if (format.charAt(iFormat) == "'" && !lookAhead("'")) - literal = false; - else - output += format.charAt(iFormat); - else - switch (format.charAt(iFormat)) { - case 'd': - output += formatNumber('d', date.getDate(), 2); - break; - case 'D': - output += formatName('D', date.getDay(), dayNamesShort, dayNames); - break; - case 'o': - output += formatNumber('o', - Math.round((new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 86400000), 3); - break; - case 'm': - output += formatNumber('m', date.getMonth() + 1, 2); - break; - case 'M': - output += formatName('M', date.getMonth(), monthNamesShort, monthNames); - break; - case 'y': - output += (lookAhead('y') ? date.getFullYear() : - (date.getYear() % 100 < 10 ? '0' : '') + date.getYear() % 100); - break; - case '@': - output += date.getTime(); - break; - case '!': - output += date.getTime() * 10000 + this._ticksTo1970; - break; - case "'": - if (lookAhead("'")) - output += "'"; - else - literal = true; - break; - default: - output += format.charAt(iFormat); - } - } - return output; - }, - - /* Extract all possible characters from the date format. */ - _possibleChars: function (format) { - var chars = ''; - var literal = false; - // Check whether a format character is doubled - var lookAhead = function(match) { - var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match); - if (matches) - iFormat++; - return matches; - }; - for (var iFormat = 0; iFormat < format.length; iFormat++) - if (literal) - if (format.charAt(iFormat) == "'" && !lookAhead("'")) - literal = false; - else - chars += format.charAt(iFormat); - else - switch (format.charAt(iFormat)) { - case 'd': case 'm': case 'y': case '@': - chars += '0123456789'; - break; - case 'D': case 'M': - return null; // Accept anything - case "'": - if (lookAhead("'")) - chars += "'"; - else - literal = true; - break; - default: - chars += format.charAt(iFormat); - } - return chars; - }, - - /* Get a setting value, defaulting if necessary. */ - _get: function(inst, name) { - return inst.settings[name] !== undefined ? - inst.settings[name] : this._defaults[name]; - }, - - /* Parse existing date and initialise date picker. */ - _setDateFromField: function(inst, noDefault) { - if (inst.input.val() == inst.lastVal) { - return; - } - var dateFormat = this._get(inst, 'dateFormat'); - var dates = inst.lastVal = inst.input ? inst.input.val() : null; - var date, defaultDate; - date = defaultDate = this._getDefaultDate(inst); - var settings = this._getFormatConfig(inst); - try { - date = this.parseDate(dateFormat, dates, settings) || defaultDate; - } catch (event) { - this.log(event); - dates = (noDefault ? '' : dates); - } - inst.selectedDay = date.getDate(); - inst.drawMonth = inst.selectedMonth = date.getMonth(); - inst.drawYear = inst.selectedYear = date.getFullYear(); - inst.currentDay = (dates ? date.getDate() : 0); - inst.currentMonth = (dates ? date.getMonth() : 0); - inst.currentYear = (dates ? date.getFullYear() : 0); - this._adjustInstDate(inst); - }, - - /* Retrieve the default date shown on opening. */ - _getDefaultDate: function(inst) { - return this._restrictMinMax(inst, - this._determineDate(inst, this._get(inst, 'defaultDate'), new Date())); - }, - - /* A date may be specified as an exact value or a relative one. */ - _determineDate: function(inst, date, defaultDate) { - var offsetNumeric = function(offset) { - var date = new Date(); - date.setDate(date.getDate() + offset); - return date; - }; - var offsetString = function(offset) { - try { - return $.datepicker.parseDate($.datepicker._get(inst, 'dateFormat'), - offset, $.datepicker._getFormatConfig(inst)); - } - catch (e) { - // Ignore - } - var date = (offset.toLowerCase().match(/^c/) ? - $.datepicker._getDate(inst) : null) || new Date(); - var year = date.getFullYear(); - var month = date.getMonth(); - var day = date.getDate(); - var pattern = /([+-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g; - var matches = pattern.exec(offset); - while (matches) { - switch (matches[2] || 'd') { - case 'd' : case 'D' : - day += parseInt(matches[1],10); break; - case 'w' : case 'W' : - day += parseInt(matches[1],10) * 7; break; - case 'm' : case 'M' : - month += parseInt(matches[1],10); - day = Math.min(day, $.datepicker._getDaysInMonth(year, month)); - break; - case 'y': case 'Y' : - year += parseInt(matches[1],10); - day = Math.min(day, $.datepicker._getDaysInMonth(year, month)); - break; - } - matches = pattern.exec(offset); - } - return new Date(year, month, day); - }; - var newDate = (date == null || date === '' ? defaultDate : (typeof date == 'string' ? offsetString(date) : - (typeof date == 'number' ? (isNaN(date) ? defaultDate : offsetNumeric(date)) : new Date(date.getTime())))); - newDate = (newDate && newDate.toString() == 'Invalid Date' ? defaultDate : newDate); - if (newDate) { - newDate.setHours(0); - newDate.setMinutes(0); - newDate.setSeconds(0); - newDate.setMilliseconds(0); - } - return this._daylightSavingAdjust(newDate); - }, - - /* Handle switch to/from daylight saving. - Hours may be non-zero on daylight saving cut-over: - > 12 when midnight changeover, but then cannot generate - midnight datetime, so jump to 1AM, otherwise reset. - @param date (Date) the date to check - @return (Date) the corrected date */ - _daylightSavingAdjust: function(date) { - if (!date) return null; - date.setHours(date.getHours() > 12 ? date.getHours() + 2 : 0); - return date; - }, - - /* Set the date(s) directly. */ - _setDate: function(inst, date, noChange) { - var clear = !date; - var origMonth = inst.selectedMonth; - var origYear = inst.selectedYear; - var newDate = this._restrictMinMax(inst, this._determineDate(inst, date, new Date())); - inst.selectedDay = inst.currentDay = newDate.getDate(); - inst.drawMonth = inst.selectedMonth = inst.currentMonth = newDate.getMonth(); - inst.drawYear = inst.selectedYear = inst.currentYear = newDate.getFullYear(); - if ((origMonth != inst.selectedMonth || origYear != inst.selectedYear) && !noChange) - this._notifyChange(inst); - this._adjustInstDate(inst); - if (inst.input) { - inst.input.val(clear ? '' : this._formatDate(inst)); - } - }, - - /* Retrieve the date(s) directly. */ - _getDate: function(inst) { - var startDate = (!inst.currentYear || (inst.input && inst.input.val() == '') ? null : - this._daylightSavingAdjust(new Date( - inst.currentYear, inst.currentMonth, inst.currentDay))); - return startDate; - }, - - /* Attach the onxxx handlers. These are declared statically so - * they work with static code transformers like Caja. - */ - _attachHandlers: function(inst) { - var stepMonths = this._get(inst, 'stepMonths'); - var id = '#' + inst.id.replace( /\\\\/g, "\\" ); - inst.dpDiv.find('[data-handler]').map(function () { - var handler = { - prev: function () { - window['DP_jQuery_' + dpuuid].datepicker._adjustDate(id, -stepMonths, 'M'); - }, - next: function () { - window['DP_jQuery_' + dpuuid].datepicker._adjustDate(id, +stepMonths, 'M'); - }, - hide: function () { - window['DP_jQuery_' + dpuuid].datepicker._hideDatepicker(); - }, - today: function () { - window['DP_jQuery_' + dpuuid].datepicker._gotoToday(id); - }, - selectDay: function () { - window['DP_jQuery_' + dpuuid].datepicker._selectDay(id, +this.getAttribute('data-month'), +this.getAttribute('data-year'), this); - return false; - }, - selectMonth: function () { - window['DP_jQuery_' + dpuuid].datepicker._selectMonthYear(id, this, 'M'); - return false; - }, - selectYear: function () { - window['DP_jQuery_' + dpuuid].datepicker._selectMonthYear(id, this, 'Y'); - return false; - } - }; - $(this).bind(this.getAttribute('data-event'), handler[this.getAttribute('data-handler')]); - }); - }, - - /* Generate the HTML for the current state of the date picker. */ - _generateHTML: function(inst) { - var today = new Date(); - today = this._daylightSavingAdjust( - new Date(today.getFullYear(), today.getMonth(), today.getDate())); // clear time - var isRTL = this._get(inst, 'isRTL'); - var showButtonPanel = this._get(inst, 'showButtonPanel'); - var hideIfNoPrevNext = this._get(inst, 'hideIfNoPrevNext'); - var navigationAsDateFormat = this._get(inst, 'navigationAsDateFormat'); - var numMonths = this._getNumberOfMonths(inst); - var showCurrentAtPos = this._get(inst, 'showCurrentAtPos'); - var stepMonths = this._get(inst, 'stepMonths'); - var isMultiMonth = (numMonths[0] != 1 || numMonths[1] != 1); - var currentDate = this._daylightSavingAdjust((!inst.currentDay ? new Date(9999, 9, 9) : - new Date(inst.currentYear, inst.currentMonth, inst.currentDay))); - var minDate = this._getMinMaxDate(inst, 'min'); - var maxDate = this._getMinMaxDate(inst, 'max'); - var drawMonth = inst.drawMonth - showCurrentAtPos; - var drawYear = inst.drawYear; - if (drawMonth < 0) { - drawMonth += 12; - drawYear--; - } - if (maxDate) { - var maxDraw = this._daylightSavingAdjust(new Date(maxDate.getFullYear(), - maxDate.getMonth() - (numMonths[0] * numMonths[1]) + 1, maxDate.getDate())); - maxDraw = (minDate && maxDraw < minDate ? minDate : maxDraw); - while (this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1)) > maxDraw) { - drawMonth--; - if (drawMonth < 0) { - drawMonth = 11; - drawYear--; - } - } - } - inst.drawMonth = drawMonth; - inst.drawYear = drawYear; - var prevText = this._get(inst, 'prevText'); - prevText = (!navigationAsDateFormat ? prevText : this.formatDate(prevText, - this._daylightSavingAdjust(new Date(drawYear, drawMonth - stepMonths, 1)), - this._getFormatConfig(inst))); - var prev = (this._canAdjustMonth(inst, -1, drawYear, drawMonth) ? - '' + prevText + '' : - (hideIfNoPrevNext ? '' : '' + prevText + '')); - var nextText = this._get(inst, 'nextText'); - nextText = (!navigationAsDateFormat ? nextText : this.formatDate(nextText, - this._daylightSavingAdjust(new Date(drawYear, drawMonth + stepMonths, 1)), - this._getFormatConfig(inst))); - var next = (this._canAdjustMonth(inst, +1, drawYear, drawMonth) ? - '' + nextText + '' : - (hideIfNoPrevNext ? '' : '' + nextText + '')); - var currentText = this._get(inst, 'currentText'); - var gotoDate = (this._get(inst, 'gotoCurrent') && inst.currentDay ? currentDate : today); - currentText = (!navigationAsDateFormat ? currentText : - this.formatDate(currentText, gotoDate, this._getFormatConfig(inst))); - var controls = (!inst.inline ? '' : ''); - var buttonPanel = (showButtonPanel) ? '
      ' + (isRTL ? controls : '') + - (this._isInRange(inst, gotoDate) ? '' : '') + (isRTL ? '' : controls) + '
      ' : ''; - var firstDay = parseInt(this._get(inst, 'firstDay'),10); - firstDay = (isNaN(firstDay) ? 0 : firstDay); - var showWeek = this._get(inst, 'showWeek'); - var dayNames = this._get(inst, 'dayNames'); - var dayNamesShort = this._get(inst, 'dayNamesShort'); - var dayNamesMin = this._get(inst, 'dayNamesMin'); - var monthNames = this._get(inst, 'monthNames'); - var monthNamesShort = this._get(inst, 'monthNamesShort'); - var beforeShowDay = this._get(inst, 'beforeShowDay'); - var showOtherMonths = this._get(inst, 'showOtherMonths'); - var selectOtherMonths = this._get(inst, 'selectOtherMonths'); - var calculateWeek = this._get(inst, 'calculateWeek') || this.iso8601Week; - var defaultDate = this._getDefaultDate(inst); - var html = ''; - for (var row = 0; row < numMonths[0]; row++) { - var group = ''; - this.maxRows = 4; - for (var col = 0; col < numMonths[1]; col++) { - var selectedDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, inst.selectedDay)); - var cornerClass = ' ui-corner-all'; - var calender = ''; - if (isMultiMonth) { - calender += '
      '; - } - calender += '
      ' + - (/all|left/.test(cornerClass) && row == 0 ? (isRTL ? next : prev) : '') + - (/all|right/.test(cornerClass) && row == 0 ? (isRTL ? prev : next) : '') + - this._generateMonthYearHeader(inst, drawMonth, drawYear, minDate, maxDate, - row > 0 || col > 0, monthNames, monthNamesShort) + // draw month headers - '
    • ' + - ''; - var thead = (showWeek ? '' : ''); - for (var dow = 0; dow < 7; dow++) { // days of the week - var day = (dow + firstDay) % 7; - thead += '= 5 ? ' class="ui-datepicker-week-end"' : '') + '>' + - '' + dayNamesMin[day] + ''; - } - calender += thead + ''; - var daysInMonth = this._getDaysInMonth(drawYear, drawMonth); - if (drawYear == inst.selectedYear && drawMonth == inst.selectedMonth) - inst.selectedDay = Math.min(inst.selectedDay, daysInMonth); - var leadDays = (this._getFirstDayOfMonth(drawYear, drawMonth) - firstDay + 7) % 7; - var curRows = Math.ceil((leadDays + daysInMonth) / 7); // calculate the number of rows to generate - var numRows = (isMultiMonth ? this.maxRows > curRows ? this.maxRows : curRows : curRows); //If multiple months, use the higher number of rows (see #7043) - this.maxRows = numRows; - var printDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1 - leadDays)); - for (var dRow = 0; dRow < numRows; dRow++) { // create date picker rows - calender += ''; - var tbody = (!showWeek ? '' : ''); - for (var dow = 0; dow < 7; dow++) { // create date picker days - var daySettings = (beforeShowDay ? - beforeShowDay.apply((inst.input ? inst.input[0] : null), [printDate]) : [true, '']); - var otherMonth = (printDate.getMonth() != drawMonth); - var unselectable = (otherMonth && !selectOtherMonths) || !daySettings[0] || - (minDate && printDate < minDate) || (maxDate && printDate > maxDate); - tbody += ''; // display selectable date - printDate.setDate(printDate.getDate() + 1); - printDate = this._daylightSavingAdjust(printDate); - } - calender += tbody + ''; - } - drawMonth++; - if (drawMonth > 11) { - drawMonth = 0; - drawYear++; - } - calender += '
      ' + this._get(inst, 'weekHeader') + '
      ' + - this._get(inst, 'calculateWeek')(printDate) + '' + // actions - (otherMonth && !showOtherMonths ? ' ' : // display for other months - (unselectable ? '' + printDate.getDate() + '' : '' + printDate.getDate() + '')) + '
      ' + (isMultiMonth ? '' + - ((numMonths[0] > 0 && col == numMonths[1]-1) ? '
      ' : '') : ''); - group += calender; - } - html += group; - } - html += buttonPanel + ($.browser.msie && parseInt($.browser.version,10) < 7 && !inst.inline ? - '' : ''); - inst._keyEvent = false; - return html; - }, - - /* Generate the month and year header. */ - _generateMonthYearHeader: function(inst, drawMonth, drawYear, minDate, maxDate, - secondary, monthNames, monthNamesShort) { - var changeMonth = this._get(inst, 'changeMonth'); - var changeYear = this._get(inst, 'changeYear'); - var showMonthAfterYear = this._get(inst, 'showMonthAfterYear'); - var html = '
      '; - var monthHtml = ''; - // month selection - if (secondary || !changeMonth) - monthHtml += '' + monthNames[drawMonth] + ''; - else { - var inMinYear = (minDate && minDate.getFullYear() == drawYear); - var inMaxYear = (maxDate && maxDate.getFullYear() == drawYear); - monthHtml += ''; - } - if (!showMonthAfterYear) - html += monthHtml + (secondary || !(changeMonth && changeYear) ? ' ' : ''); - // year selection - if ( !inst.yearshtml ) { - inst.yearshtml = ''; - if (secondary || !changeYear) - html += '' + drawYear + ''; - else { - // determine range of years to display - var years = this._get(inst, 'yearRange').split(':'); - var thisYear = new Date().getFullYear(); - var determineYear = function(value) { - var year = (value.match(/c[+-].*/) ? drawYear + parseInt(value.substring(1), 10) : - (value.match(/[+-].*/) ? thisYear + parseInt(value, 10) : - parseInt(value, 10))); - return (isNaN(year) ? thisYear : year); - }; - var year = determineYear(years[0]); - var endYear = Math.max(year, determineYear(years[1] || '')); - year = (minDate ? Math.max(year, minDate.getFullYear()) : year); - endYear = (maxDate ? Math.min(endYear, maxDate.getFullYear()) : endYear); - inst.yearshtml += ''; - - html += inst.yearshtml; - inst.yearshtml = null; - } - } - html += this._get(inst, 'yearSuffix'); - if (showMonthAfterYear) - html += (secondary || !(changeMonth && changeYear) ? ' ' : '') + monthHtml; - html += '
      '; // Close datepicker_header - return html; - }, - - /* Adjust one of the date sub-fields. */ - _adjustInstDate: function(inst, offset, period) { - var year = inst.drawYear + (period == 'Y' ? offset : 0); - var month = inst.drawMonth + (period == 'M' ? offset : 0); - var day = Math.min(inst.selectedDay, this._getDaysInMonth(year, month)) + - (period == 'D' ? offset : 0); - var date = this._restrictMinMax(inst, - this._daylightSavingAdjust(new Date(year, month, day))); - inst.selectedDay = date.getDate(); - inst.drawMonth = inst.selectedMonth = date.getMonth(); - inst.drawYear = inst.selectedYear = date.getFullYear(); - if (period == 'M' || period == 'Y') - this._notifyChange(inst); - }, - - /* Ensure a date is within any min/max bounds. */ - _restrictMinMax: function(inst, date) { - var minDate = this._getMinMaxDate(inst, 'min'); - var maxDate = this._getMinMaxDate(inst, 'max'); - var newDate = (minDate && date < minDate ? minDate : date); - newDate = (maxDate && newDate > maxDate ? maxDate : newDate); - return newDate; - }, - - /* Notify change of month/year. */ - _notifyChange: function(inst) { - var onChange = this._get(inst, 'onChangeMonthYear'); - if (onChange) - onChange.apply((inst.input ? inst.input[0] : null), - [inst.selectedYear, inst.selectedMonth + 1, inst]); - }, - - /* Determine the number of months to show. */ - _getNumberOfMonths: function(inst) { - var numMonths = this._get(inst, 'numberOfMonths'); - return (numMonths == null ? [1, 1] : (typeof numMonths == 'number' ? [1, numMonths] : numMonths)); - }, - - /* Determine the current maximum date - ensure no time components are set. */ - _getMinMaxDate: function(inst, minMax) { - return this._determineDate(inst, this._get(inst, minMax + 'Date'), null); - }, - - /* Find the number of days in a given month. */ - _getDaysInMonth: function(year, month) { - return 32 - this._daylightSavingAdjust(new Date(year, month, 32)).getDate(); - }, - - /* Find the day of the week of the first of a month. */ - _getFirstDayOfMonth: function(year, month) { - return new Date(year, month, 1).getDay(); - }, - - /* Determines if we should allow a "next/prev" month display change. */ - _canAdjustMonth: function(inst, offset, curYear, curMonth) { - var numMonths = this._getNumberOfMonths(inst); - var date = this._daylightSavingAdjust(new Date(curYear, - curMonth + (offset < 0 ? offset : numMonths[0] * numMonths[1]), 1)); - if (offset < 0) - date.setDate(this._getDaysInMonth(date.getFullYear(), date.getMonth())); - return this._isInRange(inst, date); - }, - - /* Is the given date in the accepted range? */ - _isInRange: function(inst, date) { - var minDate = this._getMinMaxDate(inst, 'min'); - var maxDate = this._getMinMaxDate(inst, 'max'); - return ((!minDate || date.getTime() >= minDate.getTime()) && - (!maxDate || date.getTime() <= maxDate.getTime())); - }, - - /* Provide the configuration settings for formatting/parsing. */ - _getFormatConfig: function(inst) { - var shortYearCutoff = this._get(inst, 'shortYearCutoff'); - shortYearCutoff = (typeof shortYearCutoff != 'string' ? shortYearCutoff : - new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10)); - return {shortYearCutoff: shortYearCutoff, - dayNamesShort: this._get(inst, 'dayNamesShort'), dayNames: this._get(inst, 'dayNames'), - monthNamesShort: this._get(inst, 'monthNamesShort'), monthNames: this._get(inst, 'monthNames')}; - }, - - /* Format the given date for display. */ - _formatDate: function(inst, day, month, year) { - if (!day) { - inst.currentDay = inst.selectedDay; - inst.currentMonth = inst.selectedMonth; - inst.currentYear = inst.selectedYear; - } - var date = (day ? (typeof day == 'object' ? day : - this._daylightSavingAdjust(new Date(year, month, day))) : - this._daylightSavingAdjust(new Date(inst.currentYear, inst.currentMonth, inst.currentDay))); - return this.formatDate(this._get(inst, 'dateFormat'), date, this._getFormatConfig(inst)); - } -}); - -/* - * Bind hover events for datepicker elements. - * Done via delegate so the binding only occurs once in the lifetime of the parent div. - * Global instActive, set by _updateDatepicker allows the handlers to find their way back to the active picker. - */ -function bindHover(dpDiv) { - var selector = 'button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a'; - return dpDiv.bind('mouseout', function(event) { - var elem = $( event.target ).closest( selector ); - if ( !elem.length ) { - return; - } - elem.removeClass( "ui-state-hover ui-datepicker-prev-hover ui-datepicker-next-hover" ); - }) - .bind('mouseover', function(event) { - var elem = $( event.target ).closest( selector ); - if ($.datepicker._isDisabledDatepicker( instActive.inline ? dpDiv.parent()[0] : instActive.input[0]) || - !elem.length ) { - return; - } - elem.parents('.ui-datepicker-calendar').find('a').removeClass('ui-state-hover'); - elem.addClass('ui-state-hover'); - if (elem.hasClass('ui-datepicker-prev')) elem.addClass('ui-datepicker-prev-hover'); - if (elem.hasClass('ui-datepicker-next')) elem.addClass('ui-datepicker-next-hover'); - }); -} - -/* jQuery extend now ignores nulls! */ -function extendRemove(target, props) { - $.extend(target, props); - for (var name in props) - if (props[name] == null || props[name] == undefined) - target[name] = props[name]; - return target; -}; - -/* Determine whether an object is an array. */ -function isArray(a) { - return (a && (($.browser.safari && typeof a == 'object' && a.length) || - (a.constructor && a.constructor.toString().match(/\Array\(\)/)))); -}; - -/* Invoke the datepicker functionality. - @param options string - a command, optionally followed by additional parameters or - Object - settings for attaching new datepicker functionality - @return jQuery object */ -$.fn.datepicker = function(options){ - - /* Verify an empty collection wasn't passed - Fixes #6976 */ - if ( !this.length ) { - return this; - } - - /* Initialise the date picker. */ - if (!$.datepicker.initialized) { - $(document).mousedown($.datepicker._checkExternalClick). - find('body').append($.datepicker.dpDiv); - $.datepicker.initialized = true; - } - - var otherArgs = Array.prototype.slice.call(arguments, 1); - if (typeof options == 'string' && (options == 'isDisabled' || options == 'getDate' || options == 'widget')) - return $.datepicker['_' + options + 'Datepicker']. - apply($.datepicker, [this[0]].concat(otherArgs)); - if (options == 'option' && arguments.length == 2 && typeof arguments[1] == 'string') - return $.datepicker['_' + options + 'Datepicker']. - apply($.datepicker, [this[0]].concat(otherArgs)); - return this.each(function() { - typeof options == 'string' ? - $.datepicker['_' + options + 'Datepicker']. - apply($.datepicker, [this].concat(otherArgs)) : - $.datepicker._attachDatepicker(this, options); - }); -}; - -$.datepicker = new Datepicker(); // singleton instance -$.datepicker.initialized = false; -$.datepicker.uuid = new Date().getTime(); -$.datepicker.version = "1.8.23"; - -// Workaround for #4055 -// Add another global to avoid noConflict issues with inline event handlers -window['DP_jQuery_' + dpuuid] = $; - -})(jQuery); -/*! - * jQuery UI Progressbar 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Progressbar - * - * Depends: - * jquery.ui.core.js - * jquery.ui.widget.js - */ -(function( $, undefined ) { - -$.widget( "ui.progressbar", { - options: { - value: 0, - max: 100 - }, - - min: 0, - - _create: function() { - this.element - .addClass( "ui-progressbar ui-widget ui-widget-content ui-corner-all" ) - .attr({ - role: "progressbar", - "aria-valuemin": this.min, - "aria-valuemax": this.options.max, - "aria-valuenow": this._value() - }); - - this.valueDiv = $( "
      " ) - .appendTo( this.element ); - - this.oldValue = this._value(); - this._refreshValue(); - }, - - destroy: function() { - this.element - .removeClass( "ui-progressbar ui-widget ui-widget-content ui-corner-all" ) - .removeAttr( "role" ) - .removeAttr( "aria-valuemin" ) - .removeAttr( "aria-valuemax" ) - .removeAttr( "aria-valuenow" ); - - this.valueDiv.remove(); - - $.Widget.prototype.destroy.apply( this, arguments ); - }, - - value: function( newValue ) { - if ( newValue === undefined ) { - return this._value(); - } - - this._setOption( "value", newValue ); - return this; - }, - - _setOption: function( key, value ) { - if ( key === "value" ) { - this.options.value = value; - this._refreshValue(); - if ( this._value() === this.options.max ) { - this._trigger( "complete" ); - } - } - - $.Widget.prototype._setOption.apply( this, arguments ); - }, - - _value: function() { - var val = this.options.value; - // normalize invalid value - if ( typeof val !== "number" ) { - val = 0; - } - return Math.min( this.options.max, Math.max( this.min, val ) ); - }, - - _percentage: function() { - return 100 * this._value() / this.options.max; - }, - - _refreshValue: function() { - var value = this.value(); - var percentage = this._percentage(); - - if ( this.oldValue !== value ) { - this.oldValue = value; - this._trigger( "change" ); - } - - this.valueDiv - .toggle( value > this.min ) - .toggleClass( "ui-corner-right", value === this.options.max ) - .width( percentage.toFixed(0) + "%" ); - this.element.attr( "aria-valuenow", value ); - } -}); - -$.extend( $.ui.progressbar, { - version: "1.8.23" -}); - -})( jQuery ); -/*! - * jQuery UI Effects 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Effects/ - */ -;jQuery.effects || (function($, undefined) { - -$.effects = {}; - - - -/******************************************************************************/ -/****************************** COLOR ANIMATIONS ******************************/ -/******************************************************************************/ - -// override the animation for color styles -$.each(['backgroundColor', 'borderBottomColor', 'borderLeftColor', - 'borderRightColor', 'borderTopColor', 'borderColor', 'color', 'outlineColor'], -function(i, attr) { - $.fx.step[attr] = function(fx) { - if (!fx.colorInit) { - fx.start = getColor(fx.elem, attr); - fx.end = getRGB(fx.end); - fx.colorInit = true; - } - - fx.elem.style[attr] = 'rgb(' + - Math.max(Math.min(parseInt((fx.pos * (fx.end[0] - fx.start[0])) + fx.start[0], 10), 255), 0) + ',' + - Math.max(Math.min(parseInt((fx.pos * (fx.end[1] - fx.start[1])) + fx.start[1], 10), 255), 0) + ',' + - Math.max(Math.min(parseInt((fx.pos * (fx.end[2] - fx.start[2])) + fx.start[2], 10), 255), 0) + ')'; - }; -}); - -// Color Conversion functions from highlightFade -// By Blair Mitchelmore -// http://jquery.offput.ca/highlightFade/ - -// Parse strings looking for color tuples [255,255,255] -function getRGB(color) { - var result; - - // Check if we're already dealing with an array of colors - if ( color && color.constructor == Array && color.length == 3 ) - return color; - - // Look for rgb(num,num,num) - if (result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color)) - return [parseInt(result[1],10), parseInt(result[2],10), parseInt(result[3],10)]; - - // Look for rgb(num%,num%,num%) - if (result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(color)) - return [parseFloat(result[1])*2.55, parseFloat(result[2])*2.55, parseFloat(result[3])*2.55]; - - // Look for #a0b1c2 - if (result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(color)) - return [parseInt(result[1],16), parseInt(result[2],16), parseInt(result[3],16)]; - - // Look for #fff - if (result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(color)) - return [parseInt(result[1]+result[1],16), parseInt(result[2]+result[2],16), parseInt(result[3]+result[3],16)]; - - // Look for rgba(0, 0, 0, 0) == transparent in Safari 3 - if (result = /rgba\(0, 0, 0, 0\)/.exec(color)) - return colors['transparent']; - - // Otherwise, we're most likely dealing with a named color - return colors[$.trim(color).toLowerCase()]; -} - -function getColor(elem, attr) { - var color; - - do { - // jQuery <1.4.3 uses curCSS, in 1.4.3 - 1.7.2 curCSS = css, 1.8+ only has css - color = ($.curCSS || $.css)(elem, attr); - - // Keep going until we find an element that has color, or we hit the body - if ( color != '' && color != 'transparent' || $.nodeName(elem, "body") ) - break; - - attr = "backgroundColor"; - } while ( elem = elem.parentNode ); - - return getRGB(color); -}; - -// Some named colors to work with -// From Interface by Stefan Petre -// http://interface.eyecon.ro/ - -var colors = { - aqua:[0,255,255], - azure:[240,255,255], - beige:[245,245,220], - black:[0,0,0], - blue:[0,0,255], - brown:[165,42,42], - cyan:[0,255,255], - darkblue:[0,0,139], - darkcyan:[0,139,139], - darkgrey:[169,169,169], - darkgreen:[0,100,0], - darkkhaki:[189,183,107], - darkmagenta:[139,0,139], - darkolivegreen:[85,107,47], - darkorange:[255,140,0], - darkorchid:[153,50,204], - darkred:[139,0,0], - darksalmon:[233,150,122], - darkviolet:[148,0,211], - fuchsia:[255,0,255], - gold:[255,215,0], - green:[0,128,0], - indigo:[75,0,130], - khaki:[240,230,140], - lightblue:[173,216,230], - lightcyan:[224,255,255], - lightgreen:[144,238,144], - lightgrey:[211,211,211], - lightpink:[255,182,193], - lightyellow:[255,255,224], - lime:[0,255,0], - magenta:[255,0,255], - maroon:[128,0,0], - navy:[0,0,128], - olive:[128,128,0], - orange:[255,165,0], - pink:[255,192,203], - purple:[128,0,128], - violet:[128,0,128], - red:[255,0,0], - silver:[192,192,192], - white:[255,255,255], - yellow:[255,255,0], - transparent: [255,255,255] -}; - - - -/******************************************************************************/ -/****************************** CLASS ANIMATIONS ******************************/ -/******************************************************************************/ - -var classAnimationActions = ['add', 'remove', 'toggle'], - shorthandStyles = { - border: 1, - borderBottom: 1, - borderColor: 1, - borderLeft: 1, - borderRight: 1, - borderTop: 1, - borderWidth: 1, - margin: 1, - padding: 1 - }; - -function getElementStyles() { - var style = document.defaultView - ? document.defaultView.getComputedStyle(this, null) - : this.currentStyle, - newStyle = {}, - key, - camelCase; - - // webkit enumerates style porperties - if (style && style.length && style[0] && style[style[0]]) { - var len = style.length; - while (len--) { - key = style[len]; - if (typeof style[key] == 'string') { - camelCase = key.replace(/\-(\w)/g, function(all, letter){ - return letter.toUpperCase(); - }); - newStyle[camelCase] = style[key]; - } - } - } else { - for (key in style) { - if (typeof style[key] === 'string') { - newStyle[key] = style[key]; - } - } - } - - return newStyle; -} - -function filterStyles(styles) { - var name, value; - for (name in styles) { - value = styles[name]; - if ( - // ignore null and undefined values - value == null || - // ignore functions (when does this occur?) - $.isFunction(value) || - // shorthand styles that need to be expanded - name in shorthandStyles || - // ignore scrollbars (break in IE) - (/scrollbar/).test(name) || - - // only colors or values that can be converted to numbers - (!(/color/i).test(name) && isNaN(parseFloat(value))) - ) { - delete styles[name]; - } - } - - return styles; -} - -function styleDifference(oldStyle, newStyle) { - var diff = { _: 0 }, // http://dev.jquery.com/ticket/5459 - name; - - for (name in newStyle) { - if (oldStyle[name] != newStyle[name]) { - diff[name] = newStyle[name]; - } - } - - return diff; -} - -$.effects.animateClass = function(value, duration, easing, callback) { - if ($.isFunction(easing)) { - callback = easing; - easing = null; - } - - return this.queue(function() { - var that = $(this), - originalStyleAttr = that.attr('style') || ' ', - originalStyle = filterStyles(getElementStyles.call(this)), - newStyle, - className = that.attr('class') || ""; - - $.each(classAnimationActions, function(i, action) { - if (value[action]) { - that[action + 'Class'](value[action]); - } - }); - newStyle = filterStyles(getElementStyles.call(this)); - that.attr('class', className); - - that.animate(styleDifference(originalStyle, newStyle), { - queue: false, - duration: duration, - easing: easing, - complete: function() { - $.each(classAnimationActions, function(i, action) { - if (value[action]) { that[action + 'Class'](value[action]); } - }); - // work around bug in IE by clearing the cssText before setting it - if (typeof that.attr('style') == 'object') { - that.attr('style').cssText = ''; - that.attr('style').cssText = originalStyleAttr; - } else { - that.attr('style', originalStyleAttr); - } - if (callback) { callback.apply(this, arguments); } - $.dequeue( this ); - } - }); - }); -}; - -$.fn.extend({ - _addClass: $.fn.addClass, - addClass: function(classNames, speed, easing, callback) { - return speed ? $.effects.animateClass.apply(this, [{ add: classNames },speed,easing,callback]) : this._addClass(classNames); - }, - - _removeClass: $.fn.removeClass, - removeClass: function(classNames,speed,easing,callback) { - return speed ? $.effects.animateClass.apply(this, [{ remove: classNames },speed,easing,callback]) : this._removeClass(classNames); - }, - - _toggleClass: $.fn.toggleClass, - toggleClass: function(classNames, force, speed, easing, callback) { - if ( typeof force == "boolean" || force === undefined ) { - if ( !speed ) { - // without speed parameter; - return this._toggleClass(classNames, force); - } else { - return $.effects.animateClass.apply(this, [(force?{add:classNames}:{remove:classNames}),speed,easing,callback]); - } - } else { - // without switch parameter; - return $.effects.animateClass.apply(this, [{ toggle: classNames },force,speed,easing]); - } - }, - - switchClass: function(remove,add,speed,easing,callback) { - return $.effects.animateClass.apply(this, [{ add: add, remove: remove },speed,easing,callback]); - } -}); - - - -/******************************************************************************/ -/*********************************** EFFECTS **********************************/ -/******************************************************************************/ - -$.extend($.effects, { - version: "1.8.23", - - // Saves a set of properties in a data storage - save: function(element, set) { - for(var i=0; i < set.length; i++) { - if(set[i] !== null) element.data("ec.storage."+set[i], element[0].style[set[i]]); - } - }, - - // Restores a set of previously saved properties from a data storage - restore: function(element, set) { - for(var i=0; i < set.length; i++) { - if(set[i] !== null) element.css(set[i], element.data("ec.storage."+set[i])); - } - }, - - setMode: function(el, mode) { - if (mode == 'toggle') mode = el.is(':hidden') ? 'show' : 'hide'; // Set for toggle - return mode; - }, - - getBaseline: function(origin, original) { // Translates a [top,left] array into a baseline value - // this should be a little more flexible in the future to handle a string & hash - var y, x; - switch (origin[0]) { - case 'top': y = 0; break; - case 'middle': y = 0.5; break; - case 'bottom': y = 1; break; - default: y = origin[0] / original.height; - }; - switch (origin[1]) { - case 'left': x = 0; break; - case 'center': x = 0.5; break; - case 'right': x = 1; break; - default: x = origin[1] / original.width; - }; - return {x: x, y: y}; - }, - - // Wraps the element around a wrapper that copies position properties - createWrapper: function(element) { - - // if the element is already wrapped, return it - if (element.parent().is('.ui-effects-wrapper')) { - return element.parent(); - } - - // wrap the element - var props = { - width: element.outerWidth(true), - height: element.outerHeight(true), - 'float': element.css('float') - }, - wrapper = $('
      ') - .addClass('ui-effects-wrapper') - .css({ - fontSize: '100%', - background: 'transparent', - border: 'none', - margin: 0, - padding: 0 - }), - active = document.activeElement; - - // support: Firefox - // Firefox incorrectly exposes anonymous content - // https://bugzilla.mozilla.org/show_bug.cgi?id=561664 - try { - active.id; - } catch( e ) { - active = document.body; - } - - element.wrap( wrapper ); - - // Fixes #7595 - Elements lose focus when wrapped. - if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) { - $( active ).focus(); - } - - wrapper = element.parent(); //Hotfix for jQuery 1.4 since some change in wrap() seems to actually loose the reference to the wrapped element - - // transfer positioning properties to the wrapper - if (element.css('position') == 'static') { - wrapper.css({ position: 'relative' }); - element.css({ position: 'relative' }); - } else { - $.extend(props, { - position: element.css('position'), - zIndex: element.css('z-index') - }); - $.each(['top', 'left', 'bottom', 'right'], function(i, pos) { - props[pos] = element.css(pos); - if (isNaN(parseInt(props[pos], 10))) { - props[pos] = 'auto'; - } - }); - element.css({position: 'relative', top: 0, left: 0, right: 'auto', bottom: 'auto' }); - } - - return wrapper.css(props).show(); - }, - - removeWrapper: function(element) { - var parent, - active = document.activeElement; - - if (element.parent().is('.ui-effects-wrapper')) { - parent = element.parent().replaceWith(element); - // Fixes #7595 - Elements lose focus when wrapped. - if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) { - $( active ).focus(); - } - return parent; - } - - return element; - }, - - setTransition: function(element, list, factor, value) { - value = value || {}; - $.each(list, function(i, x){ - var unit = element.cssUnit(x); - if (unit[0] > 0) value[x] = unit[0] * factor + unit[1]; - }); - return value; - } -}); - - -function _normalizeArguments(effect, options, speed, callback) { - // shift params for method overloading - if (typeof effect == 'object') { - callback = options; - speed = null; - options = effect; - effect = options.effect; - } - if ($.isFunction(options)) { - callback = options; - speed = null; - options = {}; - } - if (typeof options == 'number' || $.fx.speeds[options]) { - callback = speed; - speed = options; - options = {}; - } - if ($.isFunction(speed)) { - callback = speed; - speed = null; - } - - options = options || {}; - - speed = speed || options.duration; - speed = $.fx.off ? 0 : typeof speed == 'number' - ? speed : speed in $.fx.speeds ? $.fx.speeds[speed] : $.fx.speeds._default; - - callback = callback || options.complete; - - return [effect, options, speed, callback]; -} - -function standardSpeed( speed ) { - // valid standard speeds - if ( !speed || typeof speed === "number" || $.fx.speeds[ speed ] ) { - return true; - } - - // invalid strings - treat as "normal" speed - if ( typeof speed === "string" && !$.effects[ speed ] ) { - return true; - } - - return false; -} - -$.fn.extend({ - effect: function(effect, options, speed, callback) { - var args = _normalizeArguments.apply(this, arguments), - // TODO: make effects take actual parameters instead of a hash - args2 = { - options: args[1], - duration: args[2], - callback: args[3] - }, - mode = args2.options.mode, - effectMethod = $.effects[effect]; - - if ( $.fx.off || !effectMethod ) { - // delegate to the original method (e.g., .show()) if possible - if ( mode ) { - return this[ mode ]( args2.duration, args2.callback ); - } else { - return this.each(function() { - if ( args2.callback ) { - args2.callback.call( this ); - } - }); - } - } - - return effectMethod.call(this, args2); - }, - - _show: $.fn.show, - show: function(speed) { - if ( standardSpeed( speed ) ) { - return this._show.apply(this, arguments); - } else { - var args = _normalizeArguments.apply(this, arguments); - args[1].mode = 'show'; - return this.effect.apply(this, args); - } - }, - - _hide: $.fn.hide, - hide: function(speed) { - if ( standardSpeed( speed ) ) { - return this._hide.apply(this, arguments); - } else { - var args = _normalizeArguments.apply(this, arguments); - args[1].mode = 'hide'; - return this.effect.apply(this, args); - } - }, - - // jQuery core overloads toggle and creates _toggle - __toggle: $.fn.toggle, - toggle: function(speed) { - if ( standardSpeed( speed ) || typeof speed === "boolean" || $.isFunction( speed ) ) { - return this.__toggle.apply(this, arguments); - } else { - var args = _normalizeArguments.apply(this, arguments); - args[1].mode = 'toggle'; - return this.effect.apply(this, args); - } - }, - - // helper functions - cssUnit: function(key) { - var style = this.css(key), val = []; - $.each( ['em','px','%','pt'], function(i, unit){ - if(style.indexOf(unit) > 0) - val = [parseFloat(style), unit]; - }); - return val; - } -}); - - - -/******************************************************************************/ -/*********************************** EASING ***********************************/ -/******************************************************************************/ - -// based on easing equations from Robert Penner (http://www.robertpenner.com/easing) - -var baseEasings = {}; - -$.each( [ "Quad", "Cubic", "Quart", "Quint", "Expo" ], function( i, name ) { - baseEasings[ name ] = function( p ) { - return Math.pow( p, i + 2 ); - }; -}); - -$.extend( baseEasings, { - Sine: function ( p ) { - return 1 - Math.cos( p * Math.PI / 2 ); - }, - Circ: function ( p ) { - return 1 - Math.sqrt( 1 - p * p ); - }, - Elastic: function( p ) { - return p === 0 || p === 1 ? p : - -Math.pow( 2, 8 * (p - 1) ) * Math.sin( ( (p - 1) * 80 - 7.5 ) * Math.PI / 15 ); - }, - Back: function( p ) { - return p * p * ( 3 * p - 2 ); - }, - Bounce: function ( p ) { - var pow2, - bounce = 4; - - while ( p < ( ( pow2 = Math.pow( 2, --bounce ) ) - 1 ) / 11 ) {} - return 1 / Math.pow( 4, 3 - bounce ) - 7.5625 * Math.pow( ( pow2 * 3 - 2 ) / 22 - p, 2 ); - } -}); - -$.each( baseEasings, function( name, easeIn ) { - $.easing[ "easeIn" + name ] = easeIn; - $.easing[ "easeOut" + name ] = function( p ) { - return 1 - easeIn( 1 - p ); - }; - $.easing[ "easeInOut" + name ] = function( p ) { - return p < .5 ? - easeIn( p * 2 ) / 2 : - easeIn( p * -2 + 2 ) / -2 + 1; - }; -}); - -})(jQuery); -/*! - * jQuery UI Effects Blind 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Effects/Blind - * - * Depends: - * jquery.effects.core.js - */ -(function( $, undefined ) { - -$.effects.blind = function(o) { - - return this.queue(function() { - - // Create element - var el = $(this), props = ['position','top','bottom','left','right']; - - // Set options - var mode = $.effects.setMode(el, o.options.mode || 'hide'); // Set Mode - var direction = o.options.direction || 'vertical'; // Default direction - - // Adjust - $.effects.save(el, props); el.show(); // Save & Show - var wrapper = $.effects.createWrapper(el).css({overflow:'hidden'}); // Create Wrapper - var ref = (direction == 'vertical') ? 'height' : 'width'; - var distance = (direction == 'vertical') ? wrapper.height() : wrapper.width(); - if(mode == 'show') wrapper.css(ref, 0); // Shift - - // Animation - var animation = {}; - animation[ref] = mode == 'show' ? distance : 0; - - // Animate - wrapper.animate(animation, o.duration, o.options.easing, function() { - if(mode == 'hide') el.hide(); // Hide - $.effects.restore(el, props); $.effects.removeWrapper(el); // Restore - if(o.callback) o.callback.apply(el[0], arguments); // Callback - el.dequeue(); - }); - - }); - -}; - -})(jQuery); -/*! - * jQuery UI Effects Bounce 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Effects/Bounce - * - * Depends: - * jquery.effects.core.js - */ -(function( $, undefined ) { - -$.effects.bounce = function(o) { - - return this.queue(function() { - - // Create element - var el = $(this), props = ['position','top','bottom','left','right']; - - // Set options - var mode = $.effects.setMode(el, o.options.mode || 'effect'); // Set Mode - var direction = o.options.direction || 'up'; // Default direction - var distance = o.options.distance || 20; // Default distance - var times = o.options.times || 5; // Default # of times - var speed = o.duration || 250; // Default speed per bounce - if (/show|hide/.test(mode)) props.push('opacity'); // Avoid touching opacity to prevent clearType and PNG issues in IE - - // Adjust - $.effects.save(el, props); el.show(); // Save & Show - $.effects.createWrapper(el); // Create Wrapper - var ref = (direction == 'up' || direction == 'down') ? 'top' : 'left'; - var motion = (direction == 'up' || direction == 'left') ? 'pos' : 'neg'; - var distance = o.options.distance || (ref == 'top' ? el.outerHeight(true) / 3 : el.outerWidth(true) / 3); - if (mode == 'show') el.css('opacity', 0).css(ref, motion == 'pos' ? -distance : distance); // Shift - if (mode == 'hide') distance = distance / (times * 2); - if (mode != 'hide') times--; - - // Animate - if (mode == 'show') { // Show Bounce - var animation = {opacity: 1}; - animation[ref] = (motion == 'pos' ? '+=' : '-=') + distance; - el.animate(animation, speed / 2, o.options.easing); - distance = distance / 2; - times--; - }; - for (var i = 0; i < times; i++) { // Bounces - var animation1 = {}, animation2 = {}; - animation1[ref] = (motion == 'pos' ? '-=' : '+=') + distance; - animation2[ref] = (motion == 'pos' ? '+=' : '-=') + distance; - el.animate(animation1, speed / 2, o.options.easing).animate(animation2, speed / 2, o.options.easing); - distance = (mode == 'hide') ? distance * 2 : distance / 2; - }; - if (mode == 'hide') { // Last Bounce - var animation = {opacity: 0}; - animation[ref] = (motion == 'pos' ? '-=' : '+=') + distance; - el.animate(animation, speed / 2, o.options.easing, function(){ - el.hide(); // Hide - $.effects.restore(el, props); $.effects.removeWrapper(el); // Restore - if(o.callback) o.callback.apply(this, arguments); // Callback - }); - } else { - var animation1 = {}, animation2 = {}; - animation1[ref] = (motion == 'pos' ? '-=' : '+=') + distance; - animation2[ref] = (motion == 'pos' ? '+=' : '-=') + distance; - el.animate(animation1, speed / 2, o.options.easing).animate(animation2, speed / 2, o.options.easing, function(){ - $.effects.restore(el, props); $.effects.removeWrapper(el); // Restore - if(o.callback) o.callback.apply(this, arguments); // Callback - }); - }; - el.queue('fx', function() { el.dequeue(); }); - el.dequeue(); - }); - -}; - -})(jQuery); -/*! - * jQuery UI Effects Clip 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Effects/Clip - * - * Depends: - * jquery.effects.core.js - */ -(function( $, undefined ) { - -$.effects.clip = function(o) { - - return this.queue(function() { - - // Create element - var el = $(this), props = ['position','top','bottom','left','right','height','width']; - - // Set options - var mode = $.effects.setMode(el, o.options.mode || 'hide'); // Set Mode - var direction = o.options.direction || 'vertical'; // Default direction - - // Adjust - $.effects.save(el, props); el.show(); // Save & Show - var wrapper = $.effects.createWrapper(el).css({overflow:'hidden'}); // Create Wrapper - var animate = el[0].tagName == 'IMG' ? wrapper : el; - var ref = { - size: (direction == 'vertical') ? 'height' : 'width', - position: (direction == 'vertical') ? 'top' : 'left' - }; - var distance = (direction == 'vertical') ? animate.height() : animate.width(); - if(mode == 'show') { animate.css(ref.size, 0); animate.css(ref.position, distance / 2); } // Shift - - // Animation - var animation = {}; - animation[ref.size] = mode == 'show' ? distance : 0; - animation[ref.position] = mode == 'show' ? 0 : distance / 2; - - // Animate - animate.animate(animation, { queue: false, duration: o.duration, easing: o.options.easing, complete: function() { - if(mode == 'hide') el.hide(); // Hide - $.effects.restore(el, props); $.effects.removeWrapper(el); // Restore - if(o.callback) o.callback.apply(el[0], arguments); // Callback - el.dequeue(); - }}); - - }); - -}; - -})(jQuery); -/*! - * jQuery UI Effects Drop 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Effects/Drop - * - * Depends: - * jquery.effects.core.js - */ -(function( $, undefined ) { - -$.effects.drop = function(o) { - - return this.queue(function() { - - // Create element - var el = $(this), props = ['position','top','bottom','left','right','opacity']; - - // Set options - var mode = $.effects.setMode(el, o.options.mode || 'hide'); // Set Mode - var direction = o.options.direction || 'left'; // Default Direction - - // Adjust - $.effects.save(el, props); el.show(); // Save & Show - $.effects.createWrapper(el); // Create Wrapper - var ref = (direction == 'up' || direction == 'down') ? 'top' : 'left'; - var motion = (direction == 'up' || direction == 'left') ? 'pos' : 'neg'; - var distance = o.options.distance || (ref == 'top' ? el.outerHeight( true ) / 2 : el.outerWidth( true ) / 2); - if (mode == 'show') el.css('opacity', 0).css(ref, motion == 'pos' ? -distance : distance); // Shift - - // Animation - var animation = {opacity: mode == 'show' ? 1 : 0}; - animation[ref] = (mode == 'show' ? (motion == 'pos' ? '+=' : '-=') : (motion == 'pos' ? '-=' : '+=')) + distance; - - // Animate - el.animate(animation, { queue: false, duration: o.duration, easing: o.options.easing, complete: function() { - if(mode == 'hide') el.hide(); // Hide - $.effects.restore(el, props); $.effects.removeWrapper(el); // Restore - if(o.callback) o.callback.apply(this, arguments); // Callback - el.dequeue(); - }}); - - }); - -}; - -})(jQuery); -/*! - * jQuery UI Effects Explode 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Effects/Explode - * - * Depends: - * jquery.effects.core.js - */ -(function( $, undefined ) { - -$.effects.explode = function(o) { - - return this.queue(function() { - - var rows = o.options.pieces ? Math.round(Math.sqrt(o.options.pieces)) : 3; - var cells = o.options.pieces ? Math.round(Math.sqrt(o.options.pieces)) : 3; - - o.options.mode = o.options.mode == 'toggle' ? ($(this).is(':visible') ? 'hide' : 'show') : o.options.mode; - var el = $(this).show().css('visibility', 'hidden'); - var offset = el.offset(); - - //Substract the margins - not fixing the problem yet. - offset.top -= parseInt(el.css("marginTop"),10) || 0; - offset.left -= parseInt(el.css("marginLeft"),10) || 0; - - var width = el.outerWidth(true); - var height = el.outerHeight(true); - - for(var i=0;i') - .css({ - position: 'absolute', - visibility: 'visible', - left: -j*(width/cells), - top: -i*(height/rows) - }) - .parent() - .addClass('ui-effects-explode') - .css({ - position: 'absolute', - overflow: 'hidden', - width: width/cells, - height: height/rows, - left: offset.left + j*(width/cells) + (o.options.mode == 'show' ? (j-Math.floor(cells/2))*(width/cells) : 0), - top: offset.top + i*(height/rows) + (o.options.mode == 'show' ? (i-Math.floor(rows/2))*(height/rows) : 0), - opacity: o.options.mode == 'show' ? 0 : 1 - }).animate({ - left: offset.left + j*(width/cells) + (o.options.mode == 'show' ? 0 : (j-Math.floor(cells/2))*(width/cells)), - top: offset.top + i*(height/rows) + (o.options.mode == 'show' ? 0 : (i-Math.floor(rows/2))*(height/rows)), - opacity: o.options.mode == 'show' ? 1 : 0 - }, o.duration || 500); - } - } - - // Set a timeout, to call the callback approx. when the other animations have finished - setTimeout(function() { - - o.options.mode == 'show' ? el.css({ visibility: 'visible' }) : el.css({ visibility: 'visible' }).hide(); - if(o.callback) o.callback.apply(el[0]); // Callback - el.dequeue(); - - $('div.ui-effects-explode').remove(); - - }, o.duration || 500); - - - }); - -}; - -})(jQuery); -/*! - * jQuery UI Effects Fade 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Effects/Fade - * - * Depends: - * jquery.effects.core.js - */ -(function( $, undefined ) { - -$.effects.fade = function(o) { - return this.queue(function() { - var elem = $(this), - mode = $.effects.setMode(elem, o.options.mode || 'hide'); - - elem.animate({ opacity: mode }, { - queue: false, - duration: o.duration, - easing: o.options.easing, - complete: function() { - (o.callback && o.callback.apply(this, arguments)); - elem.dequeue(); - } - }); - }); -}; - -})(jQuery); -/*! - * jQuery UI Effects Fold 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Effects/Fold - * - * Depends: - * jquery.effects.core.js - */ -(function( $, undefined ) { - -$.effects.fold = function(o) { - - return this.queue(function() { - - // Create element - var el = $(this), props = ['position','top','bottom','left','right']; - - // Set options - var mode = $.effects.setMode(el, o.options.mode || 'hide'); // Set Mode - var size = o.options.size || 15; // Default fold size - var horizFirst = !(!o.options.horizFirst); // Ensure a boolean value - var duration = o.duration ? o.duration / 2 : $.fx.speeds._default / 2; - - // Adjust - $.effects.save(el, props); el.show(); // Save & Show - var wrapper = $.effects.createWrapper(el).css({overflow:'hidden'}); // Create Wrapper - var widthFirst = ((mode == 'show') != horizFirst); - var ref = widthFirst ? ['width', 'height'] : ['height', 'width']; - var distance = widthFirst ? [wrapper.width(), wrapper.height()] : [wrapper.height(), wrapper.width()]; - var percent = /([0-9]+)%/.exec(size); - if(percent) size = parseInt(percent[1],10) / 100 * distance[mode == 'hide' ? 0 : 1]; - if(mode == 'show') wrapper.css(horizFirst ? {height: 0, width: size} : {height: size, width: 0}); // Shift - - // Animation - var animation1 = {}, animation2 = {}; - animation1[ref[0]] = mode == 'show' ? distance[0] : size; - animation2[ref[1]] = mode == 'show' ? distance[1] : 0; - - // Animate - wrapper.animate(animation1, duration, o.options.easing) - .animate(animation2, duration, o.options.easing, function() { - if(mode == 'hide') el.hide(); // Hide - $.effects.restore(el, props); $.effects.removeWrapper(el); // Restore - if(o.callback) o.callback.apply(el[0], arguments); // Callback - el.dequeue(); - }); - - }); - -}; - -})(jQuery); -/*! - * jQuery UI Effects Highlight 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Effects/Highlight - * - * Depends: - * jquery.effects.core.js - */ -(function( $, undefined ) { - -$.effects.highlight = function(o) { - return this.queue(function() { - var elem = $(this), - props = ['backgroundImage', 'backgroundColor', 'opacity'], - mode = $.effects.setMode(elem, o.options.mode || 'show'), - animation = { - backgroundColor: elem.css('backgroundColor') - }; - - if (mode == 'hide') { - animation.opacity = 0; - } - - $.effects.save(elem, props); - elem - .show() - .css({ - backgroundImage: 'none', - backgroundColor: o.options.color || '#ffff99' - }) - .animate(animation, { - queue: false, - duration: o.duration, - easing: o.options.easing, - complete: function() { - (mode == 'hide' && elem.hide()); - $.effects.restore(elem, props); - (mode == 'show' && !$.support.opacity && this.style.removeAttribute('filter')); - (o.callback && o.callback.apply(this, arguments)); - elem.dequeue(); - } - }); - }); -}; - -})(jQuery); -/*! - * jQuery UI Effects Pulsate 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Effects/Pulsate - * - * Depends: - * jquery.effects.core.js - */ -(function( $, undefined ) { - -$.effects.pulsate = function(o) { - return this.queue(function() { - var elem = $(this), - mode = $.effects.setMode(elem, o.options.mode || 'show'), - times = ((o.options.times || 5) * 2) - 1, - duration = o.duration ? o.duration / 2 : $.fx.speeds._default / 2, - isVisible = elem.is(':visible'), - animateTo = 0; - - if (!isVisible) { - elem.css('opacity', 0).show(); - animateTo = 1; - } - - if ((mode == 'hide' && isVisible) || (mode == 'show' && !isVisible)) { - times--; - } - - for (var i = 0; i < times; i++) { - elem.animate({ opacity: animateTo }, duration, o.options.easing); - animateTo = (animateTo + 1) % 2; - } - - elem.animate({ opacity: animateTo }, duration, o.options.easing, function() { - if (animateTo == 0) { - elem.hide(); - } - (o.callback && o.callback.apply(this, arguments)); - }); - - elem - .queue('fx', function() { elem.dequeue(); }) - .dequeue(); - }); -}; - -})(jQuery); -/*! - * jQuery UI Effects Scale 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Effects/Scale - * - * Depends: - * jquery.effects.core.js - */ -(function( $, undefined ) { - -$.effects.puff = function(o) { - return this.queue(function() { - var elem = $(this), - mode = $.effects.setMode(elem, o.options.mode || 'hide'), - percent = parseInt(o.options.percent, 10) || 150, - factor = percent / 100, - original = { height: elem.height(), width: elem.width() }; - - $.extend(o.options, { - fade: true, - mode: mode, - percent: mode == 'hide' ? percent : 100, - from: mode == 'hide' - ? original - : { - height: original.height * factor, - width: original.width * factor - } - }); - - elem.effect('scale', o.options, o.duration, o.callback); - elem.dequeue(); - }); -}; - -$.effects.scale = function(o) { - - return this.queue(function() { - - // Create element - var el = $(this); - - // Set options - var options = $.extend(true, {}, o.options); - var mode = $.effects.setMode(el, o.options.mode || 'effect'); // Set Mode - var percent = parseInt(o.options.percent,10) || (parseInt(o.options.percent,10) == 0 ? 0 : (mode == 'hide' ? 0 : 100)); // Set default scaling percent - var direction = o.options.direction || 'both'; // Set default axis - var origin = o.options.origin; // The origin of the scaling - if (mode != 'effect') { // Set default origin and restore for show/hide - options.origin = origin || ['middle','center']; - options.restore = true; - } - var original = {height: el.height(), width: el.width()}; // Save original - el.from = o.options.from || (mode == 'show' ? {height: 0, width: 0} : original); // Default from state - - // Adjust - var factor = { // Set scaling factor - y: direction != 'horizontal' ? (percent / 100) : 1, - x: direction != 'vertical' ? (percent / 100) : 1 - }; - el.to = {height: original.height * factor.y, width: original.width * factor.x}; // Set to state - - if (o.options.fade) { // Fade option to support puff - if (mode == 'show') {el.from.opacity = 0; el.to.opacity = 1;}; - if (mode == 'hide') {el.from.opacity = 1; el.to.opacity = 0;}; - }; - - // Animation - options.from = el.from; options.to = el.to; options.mode = mode; - - // Animate - el.effect('size', options, o.duration, o.callback); - el.dequeue(); - }); - -}; - -$.effects.size = function(o) { - - return this.queue(function() { - - // Create element - var el = $(this), props = ['position','top','bottom','left','right','width','height','overflow','opacity']; - var props1 = ['position','top','bottom','left','right','overflow','opacity']; // Always restore - var props2 = ['width','height','overflow']; // Copy for children - var cProps = ['fontSize']; - var vProps = ['borderTopWidth', 'borderBottomWidth', 'paddingTop', 'paddingBottom']; - var hProps = ['borderLeftWidth', 'borderRightWidth', 'paddingLeft', 'paddingRight']; - - // Set options - var mode = $.effects.setMode(el, o.options.mode || 'effect'); // Set Mode - var restore = o.options.restore || false; // Default restore - var scale = o.options.scale || 'both'; // Default scale mode - var origin = o.options.origin; // The origin of the sizing - var original = {height: el.height(), width: el.width()}; // Save original - el.from = o.options.from || original; // Default from state - el.to = o.options.to || original; // Default to state - // Adjust - if (origin) { // Calculate baseline shifts - var baseline = $.effects.getBaseline(origin, original); - el.from.top = (original.height - el.from.height) * baseline.y; - el.from.left = (original.width - el.from.width) * baseline.x; - el.to.top = (original.height - el.to.height) * baseline.y; - el.to.left = (original.width - el.to.width) * baseline.x; - }; - var factor = { // Set scaling factor - from: {y: el.from.height / original.height, x: el.from.width / original.width}, - to: {y: el.to.height / original.height, x: el.to.width / original.width} - }; - if (scale == 'box' || scale == 'both') { // Scale the css box - if (factor.from.y != factor.to.y) { // Vertical props scaling - props = props.concat(vProps); - el.from = $.effects.setTransition(el, vProps, factor.from.y, el.from); - el.to = $.effects.setTransition(el, vProps, factor.to.y, el.to); - }; - if (factor.from.x != factor.to.x) { // Horizontal props scaling - props = props.concat(hProps); - el.from = $.effects.setTransition(el, hProps, factor.from.x, el.from); - el.to = $.effects.setTransition(el, hProps, factor.to.x, el.to); - }; - }; - if (scale == 'content' || scale == 'both') { // Scale the content - if (factor.from.y != factor.to.y) { // Vertical props scaling - props = props.concat(cProps); - el.from = $.effects.setTransition(el, cProps, factor.from.y, el.from); - el.to = $.effects.setTransition(el, cProps, factor.to.y, el.to); - }; - }; - $.effects.save(el, restore ? props : props1); el.show(); // Save & Show - $.effects.createWrapper(el); // Create Wrapper - el.css('overflow','hidden').css(el.from); // Shift - - // Animate - if (scale == 'content' || scale == 'both') { // Scale the children - vProps = vProps.concat(['marginTop','marginBottom']).concat(cProps); // Add margins/font-size - hProps = hProps.concat(['marginLeft','marginRight']); // Add margins - props2 = props.concat(vProps).concat(hProps); // Concat - el.find("*[width]").each(function(){ - var child = $(this); - if (restore) $.effects.save(child, props2); - var c_original = {height: child.height(), width: child.width()}; // Save original - child.from = {height: c_original.height * factor.from.y, width: c_original.width * factor.from.x}; - child.to = {height: c_original.height * factor.to.y, width: c_original.width * factor.to.x}; - if (factor.from.y != factor.to.y) { // Vertical props scaling - child.from = $.effects.setTransition(child, vProps, factor.from.y, child.from); - child.to = $.effects.setTransition(child, vProps, factor.to.y, child.to); - }; - if (factor.from.x != factor.to.x) { // Horizontal props scaling - child.from = $.effects.setTransition(child, hProps, factor.from.x, child.from); - child.to = $.effects.setTransition(child, hProps, factor.to.x, child.to); - }; - child.css(child.from); // Shift children - child.animate(child.to, o.duration, o.options.easing, function(){ - if (restore) $.effects.restore(child, props2); // Restore children - }); // Animate children - }); - }; - - // Animate - el.animate(el.to, { queue: false, duration: o.duration, easing: o.options.easing, complete: function() { - if (el.to.opacity === 0) { - el.css('opacity', el.from.opacity); - } - if(mode == 'hide') el.hide(); // Hide - $.effects.restore(el, restore ? props : props1); $.effects.removeWrapper(el); // Restore - if(o.callback) o.callback.apply(this, arguments); // Callback - el.dequeue(); - }}); - - }); - -}; - -})(jQuery); -/*! - * jQuery UI Effects Shake 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Effects/Shake - * - * Depends: - * jquery.effects.core.js - */ -(function( $, undefined ) { - -$.effects.shake = function(o) { - - return this.queue(function() { - - // Create element - var el = $(this), props = ['position','top','bottom','left','right']; - - // Set options - var mode = $.effects.setMode(el, o.options.mode || 'effect'); // Set Mode - var direction = o.options.direction || 'left'; // Default direction - var distance = o.options.distance || 20; // Default distance - var times = o.options.times || 3; // Default # of times - var speed = o.duration || o.options.duration || 140; // Default speed per shake - - // Adjust - $.effects.save(el, props); el.show(); // Save & Show - $.effects.createWrapper(el); // Create Wrapper - var ref = (direction == 'up' || direction == 'down') ? 'top' : 'left'; - var motion = (direction == 'up' || direction == 'left') ? 'pos' : 'neg'; - - // Animation - var animation = {}, animation1 = {}, animation2 = {}; - animation[ref] = (motion == 'pos' ? '-=' : '+=') + distance; - animation1[ref] = (motion == 'pos' ? '+=' : '-=') + distance * 2; - animation2[ref] = (motion == 'pos' ? '-=' : '+=') + distance * 2; - - // Animate - el.animate(animation, speed, o.options.easing); - for (var i = 1; i < times; i++) { // Shakes - el.animate(animation1, speed, o.options.easing).animate(animation2, speed, o.options.easing); - }; - el.animate(animation1, speed, o.options.easing). - animate(animation, speed / 2, o.options.easing, function(){ // Last shake - $.effects.restore(el, props); $.effects.removeWrapper(el); // Restore - if(o.callback) o.callback.apply(this, arguments); // Callback - }); - el.queue('fx', function() { el.dequeue(); }); - el.dequeue(); - }); - -}; - -})(jQuery); -/*! - * jQuery UI Effects Slide 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Effects/Slide - * - * Depends: - * jquery.effects.core.js - */ -(function( $, undefined ) { - -$.effects.slide = function(o) { - - return this.queue(function() { - - // Create element - var el = $(this), props = ['position','top','bottom','left','right']; - - // Set options - var mode = $.effects.setMode(el, o.options.mode || 'show'); // Set Mode - var direction = o.options.direction || 'left'; // Default Direction - - // Adjust - $.effects.save(el, props); el.show(); // Save & Show - $.effects.createWrapper(el).css({overflow:'hidden'}); // Create Wrapper - var ref = (direction == 'up' || direction == 'down') ? 'top' : 'left'; - var motion = (direction == 'up' || direction == 'left') ? 'pos' : 'neg'; - var distance = o.options.distance || (ref == 'top' ? el.outerHeight( true ) : el.outerWidth( true )); - if (mode == 'show') el.css(ref, motion == 'pos' ? (isNaN(distance) ? "-" + distance : -distance) : distance); // Shift - - // Animation - var animation = {}; - animation[ref] = (mode == 'show' ? (motion == 'pos' ? '+=' : '-=') : (motion == 'pos' ? '-=' : '+=')) + distance; - - // Animate - el.animate(animation, { queue: false, duration: o.duration, easing: o.options.easing, complete: function() { - if(mode == 'hide') el.hide(); // Hide - $.effects.restore(el, props); $.effects.removeWrapper(el); // Restore - if(o.callback) o.callback.apply(this, arguments); // Callback - el.dequeue(); - }}); - - }); - -}; - -})(jQuery); -/*! - * jQuery UI Effects Transfer 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Effects/Transfer - * - * Depends: - * jquery.effects.core.js - */ -(function( $, undefined ) { - -$.effects.transfer = function(o) { - return this.queue(function() { - var elem = $(this), - target = $(o.options.to), - endPosition = target.offset(), - animation = { - top: endPosition.top, - left: endPosition.left, - height: target.innerHeight(), - width: target.innerWidth() - }, - startPosition = elem.offset(), - transfer = $('
      ') - .appendTo(document.body) - .addClass(o.options.className) - .css({ - top: startPosition.top, - left: startPosition.left, - height: elem.innerHeight(), - width: elem.innerWidth(), - position: 'absolute' - }) - .animate(animation, o.duration, o.options.easing, function() { - transfer.remove(); - (o.callback && o.callback.apply(elem[0], arguments)); - elem.dequeue(); - }); - }); -}; - -})(jQuery); diff --git a/public/js/220-jquery.dataTables-1.9.3.js b/public/js/220-jquery.dataTables-1.9.3.js deleted file mode 100644 index 2c28b672e..000000000 --- a/public/js/220-jquery.dataTables-1.9.3.js +++ /dev/null @@ -1,12092 +0,0 @@ -/** - * @summary DataTables - * @description Paginate, search and sort HTML tables - * @version 1.9.3 - * @file jquery.dataTables.js - * @author Allan Jardine (www.sprymedia.co.uk) - * @contact www.sprymedia.co.uk/contact - * - * @copyright Copyright 2008-2012 Allan Jardine, all rights reserved. - * - * This source file is free software, under either the GPL v2 license or a - * BSD style license, available at: - * http://datatables.net/license_gpl2 - * http://datatables.net/license_bsd - * - * This source file is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details. - * - * For details please refer to: http://www.datatables.net - */ - -/*jslint evil: true, undef: true, browser: true */ -/*globals $, jQuery,_fnExternApiFunc,_fnInitialise,_fnInitComplete,_fnLanguageCompat,_fnAddColumn,_fnColumnOptions,_fnAddData,_fnCreateTr,_fnGatherData,_fnBuildHead,_fnDrawHead,_fnDraw,_fnReDraw,_fnAjaxUpdate,_fnAjaxParameters,_fnAjaxUpdateDraw,_fnServerParams,_fnAddOptionsHtml,_fnFeatureHtmlTable,_fnScrollDraw,_fnAdjustColumnSizing,_fnFeatureHtmlFilter,_fnFilterComplete,_fnFilterCustom,_fnFilterColumn,_fnFilter,_fnBuildSearchArray,_fnBuildSearchRow,_fnFilterCreateSearch,_fnDataToSearch,_fnSort,_fnSortAttachListener,_fnSortingClasses,_fnFeatureHtmlPaginate,_fnPageChange,_fnFeatureHtmlInfo,_fnUpdateInfo,_fnFeatureHtmlLength,_fnFeatureHtmlProcessing,_fnProcessingDisplay,_fnVisibleToColumnIndex,_fnColumnIndexToVisible,_fnNodeToDataIndex,_fnVisbleColumns,_fnCalculateEnd,_fnConvertToWidth,_fnCalculateColumnWidths,_fnScrollingWidthAdjust,_fnGetWidestNode,_fnGetMaxLenString,_fnStringToCss,_fnDetectType,_fnSettingsFromNode,_fnGetDataMaster,_fnGetTrNodes,_fnGetTdNodes,_fnEscapeRegex,_fnDeleteIndex,_fnReOrderIndex,_fnColumnOrdering,_fnLog,_fnClearTable,_fnSaveState,_fnLoadState,_fnCreateCookie,_fnReadCookie,_fnDetectHeader,_fnGetUniqueThs,_fnScrollBarWidth,_fnApplyToChildren,_fnMap,_fnGetRowData,_fnGetCellData,_fnSetCellData,_fnGetObjectDataFn,_fnSetObjectDataFn,_fnApplyColumnDefs,_fnBindAction,_fnCallbackReg,_fnCallbackFire,_fnJsonString,_fnRender,_fnNodeToColumnIndex,_fnInfoMacros,_fnBrowserDetect,_fnGetColumns*/ - -(/** @lends */function($, window, document, undefined) { - /** - * DataTables is a plug-in for the jQuery Javascript library. It is a - * highly flexible tool, based upon the foundations of progressive - * enhancement, which will add advanced interaction controls to any - * HTML table. For a full list of features please refer to - * DataTables.net. - * - * Note that the DataTable object is not a global variable but is - * aliased to jQuery.fn.DataTable and jQuery.fn.dataTable through which - * it may be accessed. - * - * @class - * @param {object} [oInit={}] Configuration object for DataTables. Options - * are defined by {@link DataTable.defaults} - * @requires jQuery 1.3+ - * - * @example - * // Basic initialisation - * $(document).ready( function { - * $('#example').dataTable(); - * } ); - * - * @example - * // Initialisation with configuration options - in this case, disable - * // pagination and sorting. - * $(document).ready( function { - * $('#example').dataTable( { - * "bPaginate": false, - * "bSort": false - * } ); - * } ); - */ - var DataTable = function( oInit ) - { - - - /** - * Add a column to the list used for the table with default values - * @param {object} oSettings dataTables settings object - * @param {node} nTh The th element for this column - * @memberof DataTable#oApi - */ - function _fnAddColumn( oSettings, nTh ) - { - var oDefaults = DataTable.defaults.columns; - var iCol = oSettings.aoColumns.length; - var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, { - "sSortingClass": oSettings.oClasses.sSortable, - "sSortingClassJUI": oSettings.oClasses.sSortJUI, - "nTh": nTh ? nTh : document.createElement('th'), - "sTitle": oDefaults.sTitle ? oDefaults.sTitle : nTh ? nTh.innerHTML : '', - "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol], - "mData": oDefaults.mData ? oDefaults.oDefaults : iCol - } ); - oSettings.aoColumns.push( oCol ); - - /* Add a column specific filter */ - if ( oSettings.aoPreSearchCols[ iCol ] === undefined || oSettings.aoPreSearchCols[ iCol ] === null ) - { - oSettings.aoPreSearchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch ); - } - else - { - var oPre = oSettings.aoPreSearchCols[ iCol ]; - - /* Don't require that the user must specify bRegex, bSmart or bCaseInsensitive */ - if ( oPre.bRegex === undefined ) - { - oPre.bRegex = true; - } - - if ( oPre.bSmart === undefined ) - { - oPre.bSmart = true; - } - - if ( oPre.bCaseInsensitive === undefined ) - { - oPre.bCaseInsensitive = true; - } - } - - /* Use the column options function to initialise classes etc */ - _fnColumnOptions( oSettings, iCol, null ); - } - - - /** - * Apply options for a column - * @param {object} oSettings dataTables settings object - * @param {int} iCol column index to consider - * @param {object} oOptions object with sType, bVisible and bSearchable etc - * @memberof DataTable#oApi - */ - function _fnColumnOptions( oSettings, iCol, oOptions ) - { - var oCol = oSettings.aoColumns[ iCol ]; - - /* User specified column options */ - if ( oOptions !== undefined && oOptions !== null ) - { - /* Backwards compatibility for mDataProp */ - if ( oOptions.mDataProp && !oOptions.mData ) - { - oOptions.mData = oOptions.mDataProp; - } - - if ( oOptions.sType !== undefined ) - { - oCol.sType = oOptions.sType; - oCol._bAutoType = false; - } - - $.extend( oCol, oOptions ); - _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" ); - - /* iDataSort to be applied (backwards compatibility), but aDataSort will take - * priority if defined - */ - if ( oOptions.iDataSort !== undefined ) - { - oCol.aDataSort = [ oOptions.iDataSort ]; - } - _fnMap( oCol, oOptions, "aDataSort" ); - } - - /* Cache the data get and set functions for speed */ - var mRender = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null; - var mData = _fnGetObjectDataFn( oCol.mData ); - - oCol.fnGetData = function (oData, sSpecific) { - var innerData = mData( oData, sSpecific ); - - if ( oCol.mRender && (sSpecific && sSpecific !== '') ) - { - return mRender( innerData, sSpecific, oData ); - } - return innerData; - }; - oCol.fnSetData = _fnSetObjectDataFn( oCol.mData ); - - /* Feature sorting overrides column specific when off */ - if ( !oSettings.oFeatures.bSort ) - { - oCol.bSortable = false; - } - - /* Check that the class assignment is correct for sorting */ - if ( !oCol.bSortable || - ($.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) == -1) ) - { - oCol.sSortingClass = oSettings.oClasses.sSortableNone; - oCol.sSortingClassJUI = ""; - } - else if ( oCol.bSortable || - ($.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) == -1) ) - { - oCol.sSortingClass = oSettings.oClasses.sSortable; - oCol.sSortingClassJUI = oSettings.oClasses.sSortJUI; - } - else if ( $.inArray('asc', oCol.asSorting) != -1 && $.inArray('desc', oCol.asSorting) == -1 ) - { - oCol.sSortingClass = oSettings.oClasses.sSortableAsc; - oCol.sSortingClassJUI = oSettings.oClasses.sSortJUIAscAllowed; - } - else if ( $.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) != -1 ) - { - oCol.sSortingClass = oSettings.oClasses.sSortableDesc; - oCol.sSortingClassJUI = oSettings.oClasses.sSortJUIDescAllowed; - } - } - - - /** - * Adjust the table column widths for new data. Note: you would probably want to - * do a redraw after calling this function! - * @param {object} oSettings dataTables settings object - * @memberof DataTable#oApi - */ - function _fnAdjustColumnSizing ( oSettings ) - { - /* Not interested in doing column width calculation if auto-width is disabled */ - if ( oSettings.oFeatures.bAutoWidth === false ) - { - return false; - } - - _fnCalculateColumnWidths( oSettings ); - for ( var i=0 , iLen=oSettings.aoColumns.length ; i