diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..7ab91dc3 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +dockerize/postgres_data diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index afc17f03..498e37be 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -47,15 +47,23 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Run docker-compose build - run: docker-compose build + - name: Generate the .env file + run: cp .env.template .env - - name: Run the containers - run: docker-compose up -d db devweb + - name: Run docker compose build + run: docker compose build devweb + + - name: Run docker compose services + working-directory: dockerize + run: | + cp docker-compose.override.test.yml docker-compose.override.yml + make devweb-test + make wait-db + make create-test-db - name: Run Coverage test run: | - cat << EOF | docker-compose exec -T devweb bash + cat << EOF | docker compose exec -T devweb bash pip install coverage python manage.py makemigrations python manage.py migrate @@ -64,7 +72,7 @@ jobs: EOF - name: Upload coverage to codecov - uses: codecov/codecov-action@v2 + uses: codecov/codecov-action@v3 with: fail_ci_if_error: true token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.gitignore b/.gitignore index 4267cc6f..57766a39 100644 --- a/.gitignore +++ b/.gitignore @@ -43,6 +43,8 @@ static_media dockerize/static dockerize/media dockerize/logs +dockerize/certbot-etc +dockerize/webroot .editorconfig # test cache @@ -51,3 +53,11 @@ qgis-app/api/tests/*/ # whoosh_index qgis-app/whoosh_index/ +docker-compose.override.yml +.env + +# Webpack +qgis-app/node_modules/ +qgis-app/package-lock.json +qgis-app/webpack-stats.json +qgis-app/static/webpack-bundles \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..b189a5aa --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,19 @@ +{ + "files.associations": { + "**/*.html": "html", + "**/templates/*/*.html": "django-html", + "**/templates/*": "django-txt", + "**/requirements{/**,*}.{txt,in}": "pip-requirements" + }, + + "emmet.includeLanguages": { + "django-html": "html" + }, + "beautify.language": { + "html": [ + "htm", + "html", + "django-html" + ] + } +} \ No newline at end of file diff --git a/INSTALL.md b/INSTALL.md index 1a66d939..3a2f7421 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -196,6 +196,8 @@ $ make dbrestore ## Deploy Update with Rancher +**TO BE DEPRECATED** + - Go to [https://rancher.qgis.org](https://rancher.qgis.org) - Click the home icon in the top left corner, and choose the environment: `plugins.qgis.org` - You will be redirected to User Stacks page, choose the plugins stack by clicking on `plugins` link diff --git a/REQUIREMENTS.txt b/REQUIREMENTS.txt index 2e741ec8..a92fb708 100644 --- a/REQUIREMENTS.txt +++ b/REQUIREMENTS.txt @@ -1,17 +1,21 @@ -Django==2.2.25 +Django==3.2.11 + # Currently broken with 'no module named defaults' error #Feedjack==0.9.18 # So use George's fork rather # git+https://github.com/Erve1879/feedjack.git -# George's is also broken: use my fork (django 1.8 ready) -git+https://github.com/elpaso/feedjack.git +# George's is also broken: use elpaso fork (django 1.8 ready) +# git+https://github.com/elpaso/feedjack.git +# His is also broken, use dimasciput (django 3.2 ready) +git+https://github.com/dimasciput/feedjack.git + Markdown==2.3.1 #PIL==1.1.7 Pillow Pygments==2.7.4 # Updates for Django 2 & Python 3.7 -git+https://github.com/Xpirix/whoosh.git@a306553 +git+https://github.com/Xpirix/whoosh.git@main pickle5==0.0.12 django-haystack==3.2.1 @@ -21,16 +25,18 @@ argparse==1.2.1 django-annoying==0.7.7 django-auth-ldap==1.2.6 django-autoslug==1.7.1 -django-debug-toolbar==1.11.1 +django-debug-toolbar==3.2.4 django-endless-pagination==2.0 django-extensions==1.2.0 django-generic-aggregation==0.3.2 #django-olwidget==0.61.0 unmaintained, use this fork git+https://github.com/Christophe31/olwidget.git django-pagination==1.0.7 + # Unmaintained! #django-ratings==0.3.7 -git+https://github.com/enikesha/django-ratings.git +git+https://github.com/gelo-zhukov/django-ratings.git + django-simple-ratings==0.3.2 # SIMPLEMENU git+https://github.com/elpaso/django-simplemenu.git diff --git a/dockerize/.env.template b/dockerize/.env.template new file mode 100644 index 00000000..23981fe9 --- /dev/null +++ b/dockerize/.env.template @@ -0,0 +1,31 @@ +# RabbitMQ host +RABBITMQ_HOST=rabbitmq + +# Database variables +DATABASE_NAME=gis +DATABASE_USERNAME=docker +DATABASE_PASSWORD=docker +DATABASE_HOST=db + +# Django settings +DJANGO_SETTINGS_MODULE=settings_docker +DEBUG=False + +# Docker volumes +QGISPLUGINS_STATIC_VOLUME=static-data +QGISPLUGINS_MEDIA_VOLUME=media-data +QGISPLUGINS_BACKUP_VOLUME=backups-data + +# Email variables +EMAIL_BACKEND='django.core.mail.backends.smtp.EmailBackend' +EMAIL_HOST='' +EMAIL_PORT=587 +EMAIL_USE_TLS=True +EMAIL_HOST_USER='' +EMAIL_HOST_PASSWORD='' + +# URL +DEFAULT_PLUGINS_SITE='https://plugins.qgis.org/' + +# ENV: debug or prod +QGISPLUGINS_ENV=debug \ No newline at end of file diff --git a/dockerize/Makefile b/dockerize/Makefile index 21838313..aa904327 100644 --- a/dockerize/Makefile +++ b/dockerize/Makefile @@ -13,35 +13,70 @@ build: @echo "------------------------------------------------------------------" @echo "Building in production and development mode" @echo "------------------------------------------------------------------" - @docker-compose -p $(PROJECT_ID) build + @docker compose -p $(PROJECT_ID) build + +build-dev: + @echo + @echo "------------------------------------------------------------------" + @echo "Building in development mode only" + @echo "------------------------------------------------------------------" + @docker compose -p $(PROJECT_ID) build devweb db: @echo @echo "------------------------------------------------------------------" @echo "Running db in production mode" @echo "------------------------------------------------------------------" - @docker-compose -p $(PROJECT_ID) up -d db + @docker compose -p $(PROJECT_ID) up -d db + +metabase: db + @echo + @echo "------------------------------------------------------------------" + @echo "Running metabase in production mode" + @echo "------------------------------------------------------------------" + @docker compose -p $(PROJECT_ID) up -d metabase web: db @echo @echo "------------------------------------------------------------------" @echo "Running in production mode" @echo "------------------------------------------------------------------" - @docker-compose -p $(PROJECT_ID) up -d web + @docker compose -p $(PROJECT_ID) up -d uwsgi web worker beat + +dbbackups: db + @echo + @echo "------------------------------------------------------------------" + @echo "Running dbbackups in production mode" + @echo "------------------------------------------------------------------" + @docker compose -p $(PROJECT_ID) up -d dbbackups + +certbot: web + @echo + @echo "------------------------------------------------------------------" + @echo "Running cerbot in production mode" + @echo "------------------------------------------------------------------" + @docker compose -p $(PROJECT_ID) up -d certbot + +devweb-test: db + @echo + @echo "------------------------------------------------------------------" + @echo "Running in TESTING mode" + @echo "------------------------------------------------------------------" + @docker compose up --no-deps -d devweb devweb: db @echo @echo "------------------------------------------------------------------" @echo "Running in DEVELOPMENT mode" @echo "------------------------------------------------------------------" - @docker-compose -p $(PROJECT_ID) up --no-deps -d devweb + @docker compose -p $(PROJECT_ID) up --no-deps -d rabbitmq worker beat devweb-runserver: devweb @echo @echo "------------------------------------------------------------------" @echo "Running in DEVELOPMENT mode" @echo "------------------------------------------------------------------" - @docker-compose -p $(PROJECT_ID) exec devweb python manage.py runserver 0.0.0.0:8080 + @docker compose -p $(PROJECT_ID) up devweb webpack migrate: @echo @@ -54,26 +89,26 @@ migrate: @# We add the '-' prefix to the next line as the migration may fail @# but we want to continue anyway. @#We need to migrate accounts first as it has a reference to user model - -@docker-compose -p $(PROJECT_ID) exec web python manage.py migrate auth - @docker-compose -p $(PROJECT_ID) exec web python manage.py migrate + -@docker compose -p $(PROJECT_ID) exec uwsgi python manage.py migrate auth + @docker compose -p $(PROJECT_ID) exec uwsgi python manage.py migrate update-migrations: @echo @echo "------------------------------------------------------------------" @echo "Running update migrations in production mode" @echo "------------------------------------------------------------------" - @docker-compose -p $(PROJECT_ID) exec web python manage.py makemigrations + @docker compose -p $(PROJECT_ID) exec uwsgi python manage.py makemigrations collectstatic: @echo @echo "------------------------------------------------------------------" @echo "Collecting static in production mode" @echo "------------------------------------------------------------------" - #@docker-compose -p $(PROJECT_ID) run uwsgi python manage.py collectstatic --noinput + @docker compose -p $(PROJECT_ID) run uwsgi python manage.py collectstatic --noinput #We need to run collect static in the same context as the running # uwsgi container it seems so I use docker exec here # no -it flag so we can run over remote shell - @docker exec $(PROJECT_ID)-web python manage.py collectstatic --noinput + # @docker exec $(PROJECT_ID)-web python manage.py collectstatic --noinput reload: @echo @@ -89,7 +124,7 @@ kill: @echo "------------------------------------------------------------------" @echo "Killing in production mode" @echo "------------------------------------------------------------------" - @docker-compose -p $(PROJECT_ID) kill + @docker compose -p $(PROJECT_ID) kill rm: rm-only @@ -98,7 +133,21 @@ rm-only: kill @echo "------------------------------------------------------------------" @echo "Removing production instance!!! " @echo "------------------------------------------------------------------" - @docker-compose -p $(PROJECT_ID) rm + @docker compose -p $(PROJECT_ID) rm + +maillogs: + @echo + @echo "------------------------------------------------------------------" + @echo "Showing smtp logs in production mode" + @echo "------------------------------------------------------------------" + @docker compose exec smtp tail -f /var/log/mail.log + +mailerrorlogs: + @echo + @echo "------------------------------------------------------------------" + @echo "Showing smtp error logs in production mode" + @echo "------------------------------------------------------------------" + @docker compose exec smtp tail -f /var/log/mail.err dbrestore: @echo @@ -106,30 +155,37 @@ dbrestore: @echo "Restore dump from backups/latest.dmp in production mode" @echo "------------------------------------------------------------------" @# - prefix causes command to continue even if it fails - @echo "stopping web container" - @docker-compose -p $(PROJECT_ID) stop web - @echo "dropping gis" - @docker exec -t $(PROJECT_ID)-db su - postgres -c "dropdb gis" - @echo "creating gis" - @docker exec -t $(PROJECT_ID)-db su - postgres -c "createdb -O docker -T template_postgis gis" - @echo "restoring gis" - @# Because we pipe from one docker command to another and we are going - @# to execute this Make command from a remote server at times, we need to using use interactive mode (-i) - @# in the first command and not use terminal (-t) in the second. Please do not change these! - @docker exec -t $(PROJECT_ID)-db pg_restore /backups/latest.dmp | docker exec -i $(PROJECT_ID)-db su - postgres -c "psql gis" - @docker-compose -p $(PROJECT_ID) start web - @echo "starting web container" + @echo "stopping uwsgi container" + @docker compose -p $(PROJECT_ID) stop uwsgi + @echo "Dropping the gis and metabase databases" + -@docker compose -p $(PROJECT_ID) exec db su - postgres -c "dropdb --force gis" + -@docker compose -p $(PROJECT_ID) exec db su - postgres -c "dropdb --force metabase" + @echo "Creating the gis and metabase databases" + -@docker compose -p $(PROJECT_ID) exec db su - postgres -c "createdb -O docker -T template1 gis" + -@docker compose -p $(PROJECT_ID) exec db su - postgres -c "createdb -O docker -T template1 metabase" + @echo "Restore database from backups/latest-gis.dmp" + -@docker compose -p $(PROJECT_ID) exec db su - postgres -c "pg_restore -c /backups/latest-gis.dmp -d gis" + -@docker compose -p $(PROJECT_ID) exec db su - postgres -c "pg_restore -c /backups/latest-metabase.dmp -d metabase" + @echo "starting uwsgi container" + @docker compose -p $(PROJECT_ID) up -d uwsgi + +wait-db: + @docker compose -p $(PROJECT_ID) exec db su - postgres -c "until pg_isready; do sleep 5; done" + +create-test-db: + @docker compose -p $(PROJECT_ID) exec db su - postgres -c "psql -c 'create database test_db;'" + @docker compose -p $(PROJECT_ID) exec db su - postgres -c "psql -d test_db -c 'create extension postgis;'" dbseed: @echo @echo "------------------------------------------------------------------" @echo "Seed db with JSON data from /fixtures/*.json" @echo "------------------------------------------------------------------" - @docker-compose -p $(PROJECT_ID) exec devweb bash -c 'python manage.py loaddata fixtures/*.json' + @docker compose -p $(PROJECT_ID) exec devweb bash -c 'python manage.py loaddata fixtures/*.json' rebuild_index: @echo @echo "------------------------------------------------------------------" @echo "Rebuild search index in PRODUCTION mode" @echo "------------------------------------------------------------------" - @docker-compose -p $(PROJECT_ID) exec web bash -c 'python manage.py rebuild_index' + @docker compose -p $(PROJECT_ID) exec uwsgi bash -c 'python manage.py rebuild_index' diff --git a/dockerize/docker-compose.override.template.yml b/dockerize/docker-compose.override.template.yml new file mode 100644 index 00000000..bb0fdd8a --- /dev/null +++ b/dockerize/docker-compose.override.template.yml @@ -0,0 +1,55 @@ +version: '3' +services: + devweb: + # Note you cannot scale if you use container_name + image: kartoza/qgis-plugins-uwsgi:dev-latest + container_name: qgis-plugins-devweb + volumes: + - ../qgis-app:/home/web/django_project + - ./static:/home/web/static:rw + - ./media:/home/web/media:rw + build: + context: ${PWD}/../ + dockerfile: dockerize/docker/Dockerfile + target: dev + ports: + # for django test server + - "62202:8080" + # for ssh + - "62203:22" + + beat: + volumes: + - ../qgis-app:/home/web/django_project + - ./static:/home/web/static:rw + - ./media:/home/web/media:rw + + worker: + volumes: + - ../qgis-app:/home/web/django_project + - ./static:/home/web/static:rw + - ./media:/home/web/media:rw + + uwsgi: + container_name: qgis-plugins-uwsgi + volumes: + - ../qgis-app:/home/web/django_project + - ./static:/home/web/static:rw + - ./media:/home/web/media:rw + build: + context: ${PWD}/../ + dockerfile: dockerize/docker/Dockerfile + target: prod + + db: + volumes: + - ./postgres_data:/var/lib/postgresql + - ./backups:/backups + + web: + volumes: + - ./sites-enabled:/etc/nginx/conf.d:ro + - ./static:/home/web/static:ro + - ./media:/home/web/media:ro + ports: + - "62201:8080" diff --git a/dockerize/docker-compose.override.test.yml b/dockerize/docker-compose.override.test.yml new file mode 100644 index 00000000..e10fe9a1 --- /dev/null +++ b/dockerize/docker-compose.override.test.yml @@ -0,0 +1,19 @@ +version: '3' +services: + devweb: + # Note you cannot scale if you use container_name + image: kartoza/qgis-plugins-uwsgi:dev-latest + container_name: qgis-plugins-devweb + volumes: + - ../qgis-app:/home/web/django_project + - ./static:/home/web/static:rw + - ./media:/home/web/media:rw + build: + context: ${PWD}/../ + dockerfile: dockerize/docker/Dockerfile + target: dev + ports: + # for django test server + - "62202:8080" + # for ssh + - "62203:22" diff --git a/dockerize/docker-compose.yml b/dockerize/docker-compose.yml index 80578f9f..2d6019ac 100644 --- a/dockerize/docker-compose.yml +++ b/dockerize/docker-compose.yml @@ -1,121 +1,188 @@ -version: "3.8" +version: '3.8' volumes: - django-statics-data: {} - django-media-data: {} + postgres_data: + rabbitmq: + celerybeat-schedule: + static-data: + media-data: + backups-data: services: + db: container_name: qgis-plugins-db - image: kartoza/postgis:9.6-2.4 + image: kartoza/postgis:16-3.4 environment: - ALLOW_IP_RANGE=0.0.0.0/0 - - POSTGRES_USER=docker - - POSTGRES_PASS=docker + - POSTGRES_USER=${DATABASE_USERNAME:-docker} + - POSTGRES_PASS=${DATABASE_PASSWORD:-docker} + - PASSWORD_AUTHENTICATION=${PASSWORD_AUTHENTICATION:-md5} volumes: - - ./backups:/backups + - postgres_data:/var/lib/postgresql + - ${QGISPLUGINS_BACKUP_VOLUME}:/backups restart: unless-stopped - web: - # Note you cannot scale if you use container_name - container_name: qgis-plugins-web - build: docker + uwsgi: &uwsgi-common + container_name: qgis-plugins-uwsgi-common + build: + context: ${PWD}/../ + dockerfile: dockerize/docker/Dockerfile + target: prod hostname: uwsgi + expose: + - "8080" environment: - - DATABASE_NAME=gis - - DATABASE_USERNAME=docker - - DATABASE_PASSWORD=docker - - DATABASE_HOST=db - - DJANGO_SETTINGS_MODULE=settings_docker - - VIRTUAL_HOST=plugins.kartoza.com - - VIRTUAL_PORT=8080 - - DEBUG=False - - RABBITMQ_HOST=rabbitmq + - DATABASE_NAME=${DATABASE_NAME:-gis} + - DATABASE_USERNAME=${DATABASE_USERNAME:-docker} + - DATABASE_PASSWORD=${DATABASE_PASSWORD:-docker} + - DATABASE_HOST=${DATABASE_HOST:-db} + - DJANGO_SETTINGS_MODULE=${DJANGO_SETTINGS_MODULE:-settings_docker} + - VIRTUAL_HOST=${VIRTUAL_HOST:-plugins.kartoza.com} + - VIRTUAL_PORT=${VIRTUAL_PORT:-8080} + - DEBUG=${DEBUG:-False} + - RABBITMQ_HOST=${RABBITMQ_HOST:-rabbitmq} + - BROKER_URL=amqp://rabbitmq:5672 + - EMAIL_BACKEND=${EMAIL_BACKEND} + - EMAIL_HOST=${EMAIL_HOST} + - EMAIL_PORT=${EMAIL_PORT} + - EMAIL_USE_TLS=${EMAIL_USE_TLS} + - EMAIL_HOST_USER=${EMAIL_HOST_USER:-automation} + - EMAIL_HOST_PASSWORD=${EMAIL_HOST_PASSWORD} + - DEFAULT_PLUGINS_SITE=${DEFAULT_PLUGINS_SITE:-https://plugins.qgis.org/} volumes: - ../qgis-app:/home/web/django_project - - django-statics-data:/home/web/static:rw - - django-media-data:/home/web/media:rw + - ${QGISPLUGINS_STATIC_VOLUME}:/home/web/static:rw + - ${QGISPLUGINS_MEDIA_VOLUME}:/home/web/media:rw + - celerybeat-schedule:/home/web/celerybeat-schedule:rw links: - db:db - rabbitmq:rabbitmq - - worker:worker restart: unless-stopped user: root - command: uwsgi --ini /uwsgi.conf + # This is the entry point for a development server. + # Run with --no-deps to run attached to the services + # from prod environment if wanted devweb: - # Note you cannot scale if you use container_name + <<: *uwsgi-common container_name: qgis-plugins-devweb - build: docker - hostname: uwsgi - environment: - - DATABASE_NAME=gis - - DATABASE_USERNAME=docker - - DATABASE_PASSWORD=docker - - DATABASE_HOST=db - - DJANGO_SETTINGS_MODULE=settings_docker - - VIRTUAL_HOST=plugins.kartoza.com - - VIRTUAL_PORT=8080 - - RABBITMQ_HOST=rabbitmq + build: + context: ${PWD}/../ + dockerfile: dockerize/docker/Dockerfile + target: dev + working_dir: /home/web/django_project + command: python manage.py runserver 0.0.0.0:8080 volumes: - ../qgis-app:/home/web/django_project - - django-statics-data:/home/web/static:rw - - django-media-data:/home/web/media:rw - links: - - db:db - - rabbitmq:rabbitmq - - worker:worker - restart: unless-stopped - user: root + - ${QGISPLUGINS_STATIC_VOLUME}:/home/web/static:rw + - ${QGISPLUGINS_MEDIA_VOLUME}:/home/web/media:rw ports: # for django test server - "62202:8080" # for ssh - "62203:22" + restart: always + + webpack: + container_name: qgis-plugins-webpack + build: + context: ${PWD}/../ + dockerfile: dockerize/docker/Dockerfile + target: dev + working_dir: /home/web/django_project + command: npm start + volumes: + - ../qgis-app:/home/web/django_project + - ${QGISPLUGINS_STATIC_VOLUME}:/home/web/static:rw + - ${QGISPLUGINS_MEDIA_VOLUME}:/home/web/media:rw + rabbitmq: - image: library/rabbitmq:3.6 + image: rabbitmq:3.7-alpine hostname: rabbitmq - environment: - - RABBIT_PASSWORD=rabbit_test_password - - USER=rabbit_user - - RABBITMQ_NODENAME=rabbit + volumes: + - rabbitmq:/var/lib/rabbitmq restart: unless-stopped + beat: + <<: *uwsgi-common + container_name: qgis-plugins-beat + working_dir: /home/web/django_project + entrypoint: [ ] + command: celery --app=plugins.celery:app beat -s /home/web/celerybeat-schedule/schedule -l INFO + worker: - # Note you cannot scale if you use container_name + <<: *uwsgi-common container_name: qgis-plugins-worker - build: docker - hostname: uwsgi - working_dir: /home/web/django_project - command: celery -A plugins worker -l info - environment: - - DATABASE_NAME=gis - - DATABASE_USERNAME=docker - - DATABASE_PASSWORD=docker - - DATABASE_HOST=db - - DJANGO_SETTINGS_MODULE=settings_docker - - VIRTUAL_HOST=plugins.kartoza.com - - VIRTUAL_PORT=8080 - - RABBITMQ_HOST=rabbitmq - volumes: - - ../qgis-app:/home/web/django_project - - django-statics-data:/home/web/static:rw - - django-media-data:/home/web/media:rw links: - - db:db - - rabbitmq:rabbitmq + - db + - rabbitmq + - beat + working_dir: /home/web/django_project + entrypoint: [] + command: celery -A plugins worker -l INFO - nginx: + web: # Note you cannot scale if you use container_name - container_name: qgis-plugins-nginx + container_name: qgis-plugins-web image: nginx - hostname: nginx + hostname: web + entrypoint: + - /etc/nginx/sites-available/docker-entrypoint.sh + ports: + - "80:80" + - "443:443" volumes: - - ./sites-enabled:/etc/nginx/conf.d:ro - - django-statics-data:/home/web/static:ro - - django-media-data:/home/web/media:ro - - ./logs:/var/log/nginx + - ./sites-enabled:/etc/nginx/sites-available/:ro + - ${QGISPLUGINS_STATIC_VOLUME}:/home/web/static:ro + - ${QGISPLUGINS_MEDIA_VOLUME}:/home/web/media:ro + - ./webroot:/var/www/webroot + - ./certbot-etc:/etc/letsencrypt links: - - web:uwsgi - ports: - - "62201:8080" + - uwsgi:uwsgi + - metabase:metabase + logging: + driver: "json-file" + options: + max-size: "200k" + max-file: "10" restart: unless-stopped + command: + - ${QGISPLUGINS_ENV} + + dbbackups: + image: kartoza/pg-backup:16-3.4 + hostname: pg-backups + volumes: + - ${QGISPLUGINS_BACKUP_VOLUME}:/backups + links: + - db:db + environment: + # take care to let the project name below match that + # declared in the top of the makefile + - DUMPPREFIX=${DUMPPREFIX:-QGIS_PLUGINS} + - POSTGRES_USER=${DATABASE_USERNAME:-docker} + - POSTGRES_PASS=${DATABASE_PASSWORD:-docker} + - POSTGRES_PORT=${POSTGRES_PORT:-5432} + - POSTGRES_HOST=${DATABASE_HOST:-db} + - PGDATABASE=${DATABASE_NAME:-gis} + restart: unless-stopped + + metabase: + image: metabase/metabase:latest + environment: + - MB_DB_TYPE=postgres + - MB_DB_CONNECTION_URI=jdbc:postgresql://${DATABASE_HOST:-db}:5432/metabase?user=${DATABASE_USERNAME:-docker}&password=${DATABASE_PASSWORD:-docker} + links: + - db + expose: + - "3000" + + certbot: + image: certbot/certbot + container_name: certbot + volumes: + - ./webroot:/var/www/webroot + - ./certbot-etc:/etc/letsencrypt + depends_on: + - web + command: certonly --webroot --webroot-path=/var/www/webroot --email admin@qgis.org --agree-tos --no-eff-email --force-renewal -d plugins.qgis.org \ No newline at end of file diff --git a/dockerize/docker/Dockerfile b/dockerize/docker/Dockerfile index 3bd21f73..9669fd69 100644 --- a/dockerize/docker/Dockerfile +++ b/dockerize/docker/Dockerfile @@ -1,22 +1,51 @@ -#--------- Generic stuff all our Dockerfiles should start with so we get caching ------------ -# Note this base image is based on debian -FROM kartoza/django-base:3.7 -MAINTAINER Dimas Ciputra +# For more information, please refer to https://aka.ms/vscode-docker-python +FROM python:3.12-slim as prod -#RUN ln -s /bin/true /sbin/initctl -RUN apt-get clean all +EXPOSE 8000 -# Debian stretch/updates release issue. please see https://serverfault.com/a/1130167 -RUN echo "deb http://archive.debian.org/debian stretch main contrib non-free" > /etc/apt/sources.list +# Keeps Python from generating .pyc files in the container +ENV PYTHONDONTWRITEBYTECODE=1 -RUN apt-get update && apt-get install -y libsasl2-dev python-dev libldap2-dev libssl-dev -ADD REQUIREMENTS.txt /REQUIREMENTS.txt -RUN pip install -r /REQUIREMENTS.txt -RUN pip install uwsgi freezegun==1.3.1 +# Turns off buffering for easier container logging +ENV PYTHONUNBUFFERED=1 +RUN apt-get update && apt-get install -y \ + git python3-dev libxml2-dev \ + libsasl2-dev libldap2-dev libssl-dev \ + libxslt1-dev zlib1g-dev \ + build-essential \ + libffi-dev gdal-bin\ + libjpeg-dev libpq-dev \ + liblcms2-dev libblas-dev libatlas-base-dev + +RUN rm -rf /uwsgi.conf +ADD dockerize/docker/uwsgi.conf /uwsgi.conf +ADD qgis-app /home/web/django_project +ADD dockerize/docker/REQUIREMENTS.txt /REQUIREMENTS.txt + +RUN pip install --upgrade pip && pip install -r /REQUIREMENTS.txt + +# Install NodeJS and bulma css webpack +RUN apt-get -qq update && apt-get -qq install -y --no-install-recommends wget && \ + wget --no-check-certificate https://deb.nodesource.com/setup_20.x -O /tmp/node.sh && bash /tmp/node.sh && \ + apt-get -qq update && apt-get -qq install -y nodejs build-essential + +WORKDIR /home/web/django_project +COPY qgis-app/package.json /home/web/django_project/package.json +RUN npm install -g npm@10.2.1 && npm install -g webpack@5.89.0 && npm install -g webpack-cli@5.1.4 && npm install + +RUN mkdir -p /var/log/uwsgi + +WORKDIR /home/web/django_project +CMD ["uwsgi", "--ini", "/uwsgi.conf"] + + +FROM prod as dev + +# This section taken on 2 July 2015 from # https://docs.docker.com/examples/running_ssh_service/ # Sudo is needed by pycharm when it tries to pip install packages -RUN apt-get install -y openssh-server sudo +RUN apt-get update && apt-get install -y openssh-server sudo RUN mkdir /var/run/sshd RUN echo 'root:docker' | chpasswd RUN echo "PermitRootLogin yes" >> /etc/ssh/sshd_config @@ -27,15 +56,16 @@ RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so ENV NOTVISIBLE "in users profile" RUN echo "export VISIBLE=now" >> /etc/profile -RUN rm -rf /uwsgi.conf -ADD uwsgi.conf /uwsgi.conf +# Install freezegun for feedback test +RUN pip install freezegun -# Open port 8080 as we will be running our uwsgi socket on that -EXPOSE 8080 +# -------------------------------------------------------- +# Open ports as needed +# -------------------------------------------------------- +# Open port 8080 as we will be running our django dev server on +EXPOSE 8080 # Open port 22 as we will be using a remote interpreter from pycharm EXPOSE 22 -RUN mkdir -p /var/log/uwsgi -WORKDIR /home/web/django_project CMD ["/usr/sbin/sshd", "-D"] diff --git a/dockerize/docker/REQUIREMENTS.txt b/dockerize/docker/REQUIREMENTS.txt index 4de38450..1b4f4394 100644 --- a/dockerize/docker/REQUIREMENTS.txt +++ b/dockerize/docker/REQUIREMENTS.txt @@ -1,59 +1,63 @@ -django==2.2.25 -django-auth-ldap -python-ldap -django-taggit==2.0.0 +django~=4.2 +django-auth-ldap~=4.6 +python-ldap~=3.4 +django-taggit~=5.0 django-tinymce==3.4.0 -psycopg2 +psycopg2-binary~=2.9 # Updates for Django 2 git+https://github.com/metamatik/django-templatetag-sugar.git +# Updates for Django 4 +git+https://github.com/Xpirix/django-ratings.git@modernize +django-taggit-autosuggest~=0.4 +django-annoying~=0.10 + # Updates for Django 2 -git+https://github.com/elpaso/django-ratings.git@modernize -django-taggit-autosuggest -django-annoying -# Updates for Django 2 -git+https://github.com/elpaso/rpc4django.git@modernize -Pillow +# git+https://github.com/elpaso/rpc4django.git@modernize +rpc4django~=0.6 +Pillow~=10.1 django-taggit-templatetags -# Updates for Django 2 -git+https://github.com/elpaso/django-simplemenu.git@modernize -django-bootstrap-pagination -django-sortable-listview -sorl-thumbnail -django-extensions -django-debug-toolbar==1.11.1 - -# Updates for Django 2 & Python 3.7 -git+https://github.com/Xpirix/whoosh.git@a306553 -pickle5==0.0.12 -django-haystack==3.2.1 +# Updates for Django 4 +git+https://github.com/Xpirix/django-simplemenu.git@modernize +django-bootstrap-pagination-forked~=1.7 +django-sortable-listview~=0.43 +sorl-thumbnail~=12.10 +django-extensions~=3.2 +django-debug-toolbar~=4.2 +whoosh~=2.7 +django-haystack~=3.2 # Feedjack==0.9.18 # So use George's fork rather # git+https://github.com/Erve1879/feedjack.git # George's is also broken: use my fork (django 1.8 ready) # git+https://github.com/elpaso/feedjack.git -# His is also broken, use mine (django 2.2 ready) -git+https://github.com/dimasciput/feedjack.git -feedparser==5.2.1 -celery==4.3.1 +# His is also broken, use dimasciput (django 2.2 ready) +# git+https://github.com/dimasciput/feedjack.git +# For django 4, use Xpirix (django 4.2 ready) +git+https://github.com/Xpirix/feedjack.git +feedparser~=6.0 +celery~=5.3 # pin due to issues with a breaking change # https://github.com/celery/celery/issues/7783 importlib_metadata<5 -requests==2.23.0 +requests~=2.31 -markdown==3.2.1 - -djangorestframework==3.11.2 -pyjwt==1.7.1 -djangorestframework-simplejwt==4.4 +markdown~=3.5 +djangorestframework~=3.14 +pyjwt~=2.8 +djangorestframework-simplejwt~=5.3 sorl-thumbnail-serializer-field==0.2.1 django-rest-auth==0.9.5 -drf-yasg==1.17.1 +drf-yasg~=1.21 django-rest-multiple-models==2.1.3 django-preferences==1.0.0 PyWavefront==1.3.3 django-matomo==0.1.6 +uwsgi~=2.0 +freezegun~=1.4 + +django-webpack-loader~=3.1 \ No newline at end of file diff --git a/dockerize/docker/uwsgi.conf b/dockerize/docker/uwsgi.conf index 50a80d0d..7af0f302 100644 --- a/dockerize/docker/uwsgi.conf +++ b/dockerize/docker/uwsgi.conf @@ -14,7 +14,7 @@ env = DJANGO_SETTINGS_MODULE=settings_docker #daemonize = /tmp/django.log req-logger = file:/var/log/uwsgi-requests.log logger = file:/var/log/uwsgi-errors.log -reload-os-env +# reload-os-env #uid = 1000 #gid = 1000 memory-report = true diff --git a/dockerize/production/Dockerfile b/dockerize/production/Dockerfile index 7a57d864..9453fffc 100644 --- a/dockerize/production/Dockerfile +++ b/dockerize/production/Dockerfile @@ -5,6 +5,10 @@ MAINTAINER Dimas Ciputra #RUN ln -s /bin/true /sbin/initctl RUN apt-get clean all + +# Debian stretch/updates release issue. please see https://serverfault.com/a/1130167 +RUN echo "deb http://archive.debian.org/debian stretch main contrib non-free" > /etc/apt/sources.list + RUN apt-get update && apt-get install -y libsasl2-dev python-dev libldap2-dev libssl-dev ARG BRANCH_TAG=develop @@ -14,6 +18,7 @@ RUN mkdir -p /usr/src; mkdir -p /home/web && \ ln -s /usr/src/plugins/qgis-app /home/web/django_project RUN cd /usr/src/plugins/dockerize/docker && \ + pip install --upgrade pip && \ pip install -r REQUIREMENTS.txt && \ pip install uwsgi && \ rm -rf /uwsgi.conf && \ diff --git a/dockerize/scripts/renew-ssl.sh b/dockerize/scripts/renew-ssl.sh new file mode 100644 index 00000000..7004ead6 --- /dev/null +++ b/dockerize/scripts/renew-ssl.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + + +# Run daily on crontab e.g. +# Your cron job will be run at: (5 times displayed) +# +# 2021-11-08 11:10:00 UTC +# 2021-11-09 11:10:00 UTC +# 2021-11-10 11:10:00 UTC +# 2021-11-11 11:10:00 UTC +# 2021-11-12 11:10:00 UTC +# ...etc + +#25 11 * * * /bin/bash /home/web/QGIS-Django/dockerize/scripts/renew_ssl.sh > /tmp/ssl-renewal-logs.txt + + +docker compose -f /home/web/QGIS-Django/dockerize/docker-compose.yml -p qgis-plugins run certbot renew \ No newline at end of file diff --git a/dockerize/sites-enabled/dev.conf b/dockerize/sites-enabled/dev.conf new file mode 100644 index 00000000..ef93e13c --- /dev/null +++ b/dockerize/sites-enabled/dev.conf @@ -0,0 +1,31 @@ +# Define connection details for connecting to django running in +# a docker container. +upstream uwsgi { + server uwsgi:8080; +} +server { + # OTF gzip compression + gzip on; + gzip_min_length 860; + gzip_comp_level 5; + gzip_proxied expired no-cache no-store private auth; + gzip_types text/plain application/xml application/x-javascript text/xml text/css application/json; + gzip_disable “MSIE [1-6].(?!.*SV1)”; + + access_log /var/log/nginx/access.log; + error_log /var/log/nginx/error.log; + + # the port your site will be served on + listen 80; + # the domain name it will serve for + server_name ""; + charset utf-8; + + # Send all requests to the Django server. + location / { + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $http_host; + proxy_redirect off; + proxy_pass http://uwsgi; + } +} diff --git a/dockerize/sites-enabled/docker-entrypoint.sh b/dockerize/sites-enabled/docker-entrypoint.sh new file mode 100755 index 00000000..fbc27084 --- /dev/null +++ b/dockerize/sites-enabled/docker-entrypoint.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash + +# Clean up sites-enabled +echo "Clean sites-enabled" +rm -rf /etc/nginx/conf.d/*.conf +mkdir -p /etc/nginx/conf.d + +if [ $# -eq 1 ]; then + case $1 in + # Debug mode, enable dev.conf + [Dd][Ee][Bb][Uu][Gg]) + echo "Run in debug mode" + CONF_FILE=dev.conf + ln -s /etc/nginx/sites-available/$CONF_FILE /etc/nginx/conf.d/$CONF_FILE + exec nginx -g "daemon off;" + ;; + # Production mode, run using uwsgi + [Pp][Rr][Oo][Dd]) + echo "Run in prod mode" + CONF_FILE=prod.conf + ln -s /etc/nginx/sites-available/$CONF_FILE /etc/nginx/conf.d/$CONF_FILE + exec nginx -g "daemon off;" + ;; + # Production SSL mode, run using uwsgi + [Pp][Rr][Oo][Dd][-][Ss][Ss][Ll]) + echo "Run in prod SSL mode" + CONF_FILE=prod-ssl.conf + ln -s /etc/nginx/sites-available/$CONF_FILE /etc/nginx/conf.d/$CONF_FILE + exec nginx -g "daemon off;" + ;; + esac +fi + +# Run as bash entrypoint +exec "$@" diff --git a/dockerize/sites-enabled/prod-ssl.conf b/dockerize/sites-enabled/prod-ssl.conf new file mode 100644 index 00000000..c8100ef6 --- /dev/null +++ b/dockerize/sites-enabled/prod-ssl.conf @@ -0,0 +1,210 @@ +# Define connection details for connecting to django running in +# a docker container. +upstream uwsgi { + server uwsgi:8080; +} + +server { + # OTF gzip compression + gzip on; + gzip_min_length 860; + gzip_comp_level 5; + gzip_proxied expired no-cache no-store private auth; + gzip_types text/plain application/xml application/x-javascript text/xml text/css application/json; + gzip_disable “MSIE [1-6].(?!.*SV1)”; + + access_log /var/log/nginx/access.log; + + client_max_body_size 20M; + error_log /var/log/nginx/error.log; + + # the port your site will be served on + listen 80; + # the domain name it will serve for + server_name ""; + + charset utf-8; + + # Drop any non django related requests + # Its probably someone nefarious probing for vulnerabilities... + location ~ (\.php|\.asp|myadmin) { + return 404; + } + + # Django media + location /media { + # your Django project's media files - amend as required + alias /home/web/media; + expires 21d; # cache for 71 days + } + location /static { + # your Django project's static files - amend as required + alias /home/web/static; + expires 21d; # cache for 21 days + } + location /plugins/plugins.xml { + if ($request_uri !~ "&package_name(.*)") { + rewrite ^/plugins/plugins.xml /web/media/cached_xmls/plugins_$arg_qgis.xml break; + root /home; + expires 600s; + } + } + location /archive { + # Changed from http_host to host because of error messages when + # bots hit urls like this: + # 'REQUEST_URI': '/phpmyadmin/scripts/setup.php', + # See https://snakeycode.wordpress.com/2016/11/21/django-nginx-invalid-http_host-header/ + # for more details. + #proxy_set_header Host $http_host; + proxy_set_header Host $host; + autoindex on; + # your Django project's static files - amend as required + alias /home/web/archive; + expires 21d; # cache for 6h + } + # Finally, send all non-media requests to the Django server. + location / { + uwsgi_pass uwsgi; + # the uwsgi_params file you installed needs to be passed with each + # request. + # the uwsgi_params need to be passed with each uwsgi request + uwsgi_param QUERY_STRING $query_string; + uwsgi_param REQUEST_METHOD $request_method; + uwsgi_param CONTENT_TYPE $content_type; + uwsgi_param CONTENT_LENGTH $content_length; + + uwsgi_param REQUEST_URI $request_uri; + uwsgi_param PATH_INFO $document_uri; + uwsgi_param DOCUMENT_ROOT $document_root; + uwsgi_param SERVER_PROTOCOL $server_protocol; + uwsgi_param HTTPS $https if_not_empty; + + uwsgi_param REMOTE_ADDR $remote_addr; + uwsgi_param REMOTE_PORT $remote_port; + uwsgi_param SERVER_PORT $server_port; + uwsgi_param SERVER_NAME $server_name; + } + + location /metabase/ { + # set to webroot path + proxy_pass http://metabase:3000/; + } + + location ~ /.well-known/acme-challenge { + # set to webroot path + root /var/www/webroot; + default_type "text/plain"; + allow all; + } +} + + +server { + # SSL Cert + listen 443 ssl http2; + listen [::]:443 ssl http2; + server_name plugins.qgis.org; + + server_tokens off; + + ssl_certificate /etc/letsencrypt/live/plugins.qgis.org/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/plugins.qgis.org/privkey.pem; + + ssl_buffer_size 8k; + + # ssl_dhparam /etc/ssl/certs/dhparam-2048.pem; + + ssl_protocols TLSv1.2 TLSv1.1 TLSv1; + ssl_prefer_server_ciphers on; + + ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5; + + ssl_ecdh_curve secp384r1; + ssl_session_tickets off; + + ssl_stapling on; + ssl_stapling_verify on; + resolver 8.8.8.8; + + # OTF gzip compression + gzip on; + gzip_min_length 860; + gzip_comp_level 5; + gzip_proxied expired no-cache no-store private auth; + gzip_types text/plain application/xml application/x-javascript text/xml text/css application/json; + gzip_disable “MSIE [1-6].(?!.*SV1)”; + + access_log /var/log/nginx/access.log; + + client_max_body_size 20M; + error_log /var/log/nginx/error.log; + + charset utf-8; + + # Drop any non django related requests + # Its probably someone nefarious probing for vulnerabilities... + location ~ (\.php|\.asp|myadmin) { + return 404; + } + + # Django media + location /media { + # your Django project's media files - amend as required + alias /home/web/media; + expires 21d; # cache for 71 days + } + location /static { + # your Django project's static files - amend as required + alias /home/web/static; + expires 21d; # cache for 21 days + } + location /plugins/plugins.xml { + if ($request_uri !~ "&package_name(.*)") { + rewrite ^/plugins/plugins.xml /web/media/cached_xmls/plugins_$arg_qgis.xml break; + root /home; + expires 600s; + } + } + location /archive { + # Changed from http_host to host because of error messages when + # bots hit urls like this: + # 'REQUEST_URI': '/phpmyadmin/scripts/setup.php', + # See https://snakeycode.wordpress.com/2016/11/21/django-nginx-invalid-http_host-header/ + # for more details. + #proxy_set_header Host $http_host; + proxy_set_header Host $host; + autoindex on; + # your Django project's static files - amend as required + alias /home/web/archive; + expires 21d; # cache for 6h + } + # Finally, send all non-media requests to the Django server. + location / { + uwsgi_pass uwsgi; + # the uwsgi_params file you installed needs to be passed with each + # request. + # the uwsgi_params need to be passed with each uwsgi request + uwsgi_param QUERY_STRING $query_string; + uwsgi_param REQUEST_METHOD $request_method; + uwsgi_param CONTENT_TYPE $content_type; + uwsgi_param CONTENT_LENGTH $content_length; + + uwsgi_param REQUEST_URI $request_uri; + uwsgi_param PATH_INFO $document_uri; + uwsgi_param DOCUMENT_ROOT $document_root; + uwsgi_param SERVER_PROTOCOL $server_protocol; + uwsgi_param HTTPS $https if_not_empty; + + uwsgi_param REMOTE_ADDR $remote_addr; + uwsgi_param REMOTE_PORT $remote_port; + uwsgi_param SERVER_PORT $server_port; + uwsgi_param SERVER_NAME $server_name; + } + + location /metabase/ { + # set to webroot path + proxy_pass http://metabase:3000/; + } + + root /var/www/webroot; +} \ No newline at end of file diff --git a/dockerize/sites-enabled/default.conf b/dockerize/sites-enabled/prod.conf similarity index 81% rename from dockerize/sites-enabled/default.conf rename to dockerize/sites-enabled/prod.conf index e870a2a6..84f9d8aa 100644 --- a/dockerize/sites-enabled/default.conf +++ b/dockerize/sites-enabled/prod.conf @@ -3,6 +3,7 @@ upstream uwsgi { server uwsgi:8080; } + server { # OTF gzip compression gzip on; @@ -13,21 +14,23 @@ server { gzip_disable “MSIE [1-6].(?!.*SV1)”; access_log /var/log/nginx/access.log; + + client_max_body_size 20M; error_log /var/log/nginx/error.log; # the port your site will be served on - listen 8080; + listen 80; # the domain name it will serve for server_name ""; + charset utf-8; # Drop any non django related requests # Its probably someone nefarious probing for vulnerabilities... - location ~ (\.php|.aspx|.asp|myadmin) { + location ~ (\.php|\.asp|myadmin) { return 404; } - # max upload size, adjust to taste - client_max_body_size 15M; + # Django media location /media { # your Django project's media files - amend as required @@ -39,6 +42,13 @@ server { alias /home/web/static; expires 21d; # cache for 21 days } + location /plugins/plugins.xml { + if ($request_uri !~ "&package_name(.*)") { + rewrite ^/plugins/plugins.xml /web/media/cached_xmls/plugins_$arg_qgis.xml break; + root /home; + expires 600s; + } + } location /archive { # Changed from http_host to host because of error messages when # bots hit urls like this: @@ -74,4 +84,16 @@ server { uwsgi_param SERVER_PORT $server_port; uwsgi_param SERVER_NAME $server_name; } + + location /metabase/ { + # set to webroot path + proxy_pass http://metabase:3000/; + } + + location ~ /.well-known/acme-challenge { + # set to webroot path + root /var/www/webroot; + default_type "text/plain"; + allow all; + } } diff --git a/qgis-app/REQUIREMENTS_plugins.txt b/qgis-app/REQUIREMENTS_plugins.txt index fa9c03ac..ebb39373 100644 --- a/qgis-app/REQUIREMENTS_plugins.txt +++ b/qgis-app/REQUIREMENTS_plugins.txt @@ -1,4 +1,4 @@ -django==2.2.25 +django==3.2.11 django-auth-ldap python-ldap django-taggit==2.0.0 @@ -6,8 +6,10 @@ django-tinymce==3.4.0 psycopg2 # Updates for Django 2 git+https://github.com/metamatik/django-templatetag-sugar.git -# Updates for Django 2 -git+https://github.com/elpaso/django-ratings.git@modernize + +# Updates for Django 3 +git+https://github.com/gelo-zhukov/django-ratings.git + django-taggit-autosuggest django-annoying # Updates for Django 2 @@ -20,9 +22,9 @@ django-bootstrap-pagination django-sortable-listview sorl-thumbnail django-extensions -django-debug-toolbar==1.11.1 +django-debug-toolbar==3.2.4 # Updates for Django 2 & Python 3.7 -git+https://github.com/Xpirix/whoosh.git@a306553 +git+https://github.com/Xpirix/whoosh.git@main pickle5==0.0.12 django-haystack==3.2.1 diff --git a/qgis-app/api/tests/test_views.py b/qgis-app/api/tests/test_views.py index 9f0ff30b..acb2cb30 100644 --- a/qgis-app/api/tests/test_views.py +++ b/qgis-app/api/tests/test_views.py @@ -216,7 +216,7 @@ def test_download_resource_should_be_a_file_in_a_zip(self): url = reverse("resource-download", kwargs={"uuid": self.style.uuid}) response = self.client.get(url) self.assertEqual(response.status_code, 200) - self.assertEquals( + self.assertEqual( response.get("Content-Disposition"), "attachment; filename=style_zero.zip" ) with io.BytesIO(response.content) as file: diff --git a/qgis-app/base/forms/processing_forms.py b/qgis-app/base/forms/processing_forms.py index 771eb3eb..cdf85a7f 100644 --- a/qgis-app/base/forms/processing_forms.py +++ b/qgis-app/base/forms/processing_forms.py @@ -1,6 +1,6 @@ from base.validator import filesize_validator from django import forms -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ class ResourceBaseReviewForm(forms.Form): diff --git a/qgis-app/base/models/processing_models.py b/qgis-app/base/models/processing_models.py index 0d6590c5..9b53c625 100644 --- a/qgis-app/base/models/processing_models.py +++ b/qgis-app/base/models/processing_models.py @@ -7,7 +7,7 @@ from django.contrib.auth.models import User from django.db import models -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ class UnapprovedManager(models.Manager): diff --git a/qgis-app/base/validator.py b/qgis-app/base/validator.py index 534c4c08..4a491e9e 100644 --- a/qgis-app/base/validator.py +++ b/qgis-app/base/validator.py @@ -2,7 +2,7 @@ from django.conf import settings from django.core.exceptions import ValidationError -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ RESOURCE_MAX_SIZE = getattr(settings, "RESOURCE_MAX_SIZE", 1000000) # 1MB ERROR_FILESIZE_TOO_BIG = ValidationError( diff --git a/qgis-app/base/views/processing_view.py b/qgis-app/base/views/processing_view.py index bddb4fb5..10087884 100644 --- a/qgis-app/base/views/processing_view.py +++ b/qgis-app/base/views/processing_view.py @@ -17,7 +17,7 @@ from django.urls import reverse, reverse_lazy from django.utils.decorators import method_decorator from django.utils.text import slugify -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from django.views.decorators.cache import never_cache from django.views.generic import ( CreateView, diff --git a/qgis-app/custom_haystack_urls.py b/qgis-app/custom_haystack_urls.py index a0ab8c23..c14fa7f5 100755 --- a/qgis-app/custom_haystack_urls.py +++ b/qgis-app/custom_haystack_urls.py @@ -1,6 +1,6 @@ # Custom haystack search to match partial strings -from django.conf.urls import include, url +from django.urls import re_path as url from haystack.query import SearchQuerySet from haystack.views import SearchView diff --git a/qgis-app/geopackages/migrations/0009_alter_review_reviewer.py b/qgis-app/geopackages/migrations/0009_alter_review_reviewer.py new file mode 100644 index 00000000..0fc4f397 --- /dev/null +++ b/qgis-app/geopackages/migrations/0009_alter_review_reviewer.py @@ -0,0 +1,21 @@ +# Generated by Django 4.2.11 on 2024-03-22 00:02 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('geopackages', '0008_remove_uuid_null'), + ] + + operations = [ + migrations.AlterField( + model_name='review', + name='reviewer', + field=models.ForeignKey(help_text='The user who reviewed this %(app_label)s.', on_delete=django.db.models.deletion.CASCADE, related_name='%(app_label)s_%(class)s_related', to=settings.AUTH_USER_MODEL, verbose_name='Reviewed by'), + ), + ] diff --git a/qgis-app/geopackages/models.py b/qgis-app/geopackages/models.py index 0cfc14e2..3475f08a 100644 --- a/qgis-app/geopackages/models.py +++ b/qgis-app/geopackages/models.py @@ -5,7 +5,7 @@ from django.core.validators import FileExtensionValidator from django.db import models from django.urls import reverse -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ GEOPACKAGES_STORAGE_PATH = getattr( settings, "GEOPACKAGE_STORAGE_PATH", "geopackages/%Y" diff --git a/qgis-app/homepage.py b/qgis-app/homepage.py index 7bf3d7c2..a7e9e51f 100644 --- a/qgis-app/homepage.py +++ b/qgis-app/homepage.py @@ -1,7 +1,7 @@ from django.contrib.flatpages.models import FlatPage from django.shortcuts import render from django.template import RequestContext -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from plugins.models import Plugin # from feedjack.models import Post diff --git a/qgis-app/layerdefinitions/file_handler.py b/qgis-app/layerdefinitions/file_handler.py index ddacc02d..ffc196c1 100644 --- a/qgis-app/layerdefinitions/file_handler.py +++ b/qgis-app/layerdefinitions/file_handler.py @@ -6,7 +6,7 @@ import xml.etree.ElementTree as ET from django.core.exceptions import ValidationError -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ def parse_qlr(xmlfile): diff --git a/qgis-app/layerdefinitions/migrations/0002_alter_review_reviewer.py b/qgis-app/layerdefinitions/migrations/0002_alter_review_reviewer.py new file mode 100644 index 00000000..97576ae6 --- /dev/null +++ b/qgis-app/layerdefinitions/migrations/0002_alter_review_reviewer.py @@ -0,0 +1,21 @@ +# Generated by Django 4.2.11 on 2024-03-22 00:02 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('layerdefinitions', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='review', + name='reviewer', + field=models.ForeignKey(help_text='The user who reviewed this %(app_label)s.', on_delete=django.db.models.deletion.CASCADE, related_name='%(app_label)s_%(class)s_related', to=settings.AUTH_USER_MODEL, verbose_name='Reviewed by'), + ), + ] diff --git a/qgis-app/layerdefinitions/models.py b/qgis-app/layerdefinitions/models.py index 1c6be6e4..2383b5be 100644 --- a/qgis-app/layerdefinitions/models.py +++ b/qgis-app/layerdefinitions/models.py @@ -5,7 +5,7 @@ from django.core.validators import FileExtensionValidator from django.db import models from django.urls import reverse -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ LAYERDEFINITIONS_STORAGE_PATH = getattr( settings, "LAYERDEFINITION_STORAGE_PATH", "layerdefinitions" diff --git a/qgis-app/layerdefinitions/tests/test_views.py b/qgis-app/layerdefinitions/tests/test_views.py index f46119b2..44f8dd28 100644 --- a/qgis-app/layerdefinitions/tests/test_views.py +++ b/qgis-app/layerdefinitions/tests/test_views.py @@ -193,7 +193,7 @@ def test_download_should_return_zipfile_with_custom_license(self): url = reverse("layerdefinition_download", kwargs={"pk": qlr.id}) response = self.client.get(url) self.assertEqual(response.status_code, 200) - self.assertEquals( + self.assertEqual( response.get("Content-Disposition"), "attachment; filename=test-qlr-file.zip", ) diff --git a/qgis-app/models/migrations/0007_alter_review_reviewer.py b/qgis-app/models/migrations/0007_alter_review_reviewer.py new file mode 100644 index 00000000..ad03f8c2 --- /dev/null +++ b/qgis-app/models/migrations/0007_alter_review_reviewer.py @@ -0,0 +1,21 @@ +# Generated by Django 4.2.11 on 2024-03-22 00:02 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('models', '0006_remove_uuid_null'), + ] + + operations = [ + migrations.AlterField( + model_name='review', + name='reviewer', + field=models.ForeignKey(help_text='The user who reviewed this %(app_label)s.', on_delete=django.db.models.deletion.CASCADE, related_name='%(app_label)s_%(class)s_related', to=settings.AUTH_USER_MODEL, verbose_name='Reviewed by'), + ), + ] diff --git a/qgis-app/models/models.py b/qgis-app/models/models.py index 1786bd9e..4aa2809f 100644 --- a/qgis-app/models/models.py +++ b/qgis-app/models/models.py @@ -5,7 +5,7 @@ from django.core.validators import FileExtensionValidator from django.db import models from django.urls import reverse -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ MODELS_STORAGE_PATH = getattr(settings, "MODELS_STORAGE_PATH", "models/%Y") diff --git a/qgis-app/models/validator.py b/qgis-app/models/validator.py index e4d41829..6241633d 100644 --- a/qgis-app/models/validator.py +++ b/qgis-app/models/validator.py @@ -2,7 +2,7 @@ from django.conf import settings from django.core.exceptions import ValidationError -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ MODEL_MAX_SIZE = getattr(settings, "MODEL_MAX_SIZE", 1000000) # 1MB diff --git a/qgis-app/package.json b/qgis-app/package.json new file mode 100644 index 00000000..c38f9eef --- /dev/null +++ b/qgis-app/package.json @@ -0,0 +1,38 @@ +{ + "name": "qgis-plugins-website", + "version": "1.0.0", + "description": "![image](https://user-images.githubusercontent.com/178003/91536115-2356ec80-e90c-11ea-971b-f23ac72d3aea.png)", + "main": "webpack.config.js", + "directories": { + "doc": "doc" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "npx webpack --config webpack.config.js --mode development --watch", + "dev": "webpack --mode development", + "build": "webpack --mode production" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/qgis/QGIS-Django.git" + }, + "author": "", + "license": "GPL-2.0", + "bugs": { + "url": "https://github.com/qgis/QGIS-Django/issues" + }, + "homepage": "https://github.com/qgis/QGIS-Django#readme", + "devDependencies": { + "bulma": "^0.9.4", + "css-loader": "^6.8.1", + "mini-css-extract-plugin": "^2.7.6", + "node-sass": "^9.0.0", + "sass-loader": "^13.3.2", + "style-loader": "^3.3.3", + "webpack": "^5.89.0", + "webpack-bundle-tracker": "^2.0.1", + "webpack-cli": "^5.1.4", + "webpack-dev-server": "^4.15.1", + "webpack-livereload-plugin": "^3.0.2" + } +} diff --git a/qgis-app/plugins/api.py b/qgis-app/plugins/api.py index 62ff6b8e..72615d68 100644 --- a/qgis-app/plugins/api.py +++ b/qgis-app/plugins/api.py @@ -12,7 +12,7 @@ # Transaction from django.db import IntegrityError, connection -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from plugins.models import * from plugins.validator import validator from plugins.views import plugin_notify diff --git a/qgis-app/plugins/celery.py b/qgis-app/plugins/celery.py index 3ec72655..cdc9b450 100644 --- a/qgis-app/plugins/celery.py +++ b/qgis-app/plugins/celery.py @@ -3,6 +3,10 @@ import os from celery import Celery +import logging + +logger = logging.getLogger('plugins') + # set the default Django settings module for the 'celery' program. os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings_docker") diff --git a/qgis-app/plugins/forms.py b/qgis-app/plugins/forms.py index 40c8ab5a..9f98f03f 100644 --- a/qgis-app/plugins/forms.py +++ b/qgis-app/plugins/forms.py @@ -5,7 +5,7 @@ from django.contrib.auth.models import User from django.forms import CharField, ModelForm, ValidationError from django.utils.safestring import mark_safe -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from plugins.models import Plugin, PluginOutstandingToken, PluginVersion, PluginVersionFeedback from plugins.validator import validator from taggit.forms import * diff --git a/qgis-app/plugins/management/commands/generate_plugins_xml.py b/qgis-app/plugins/management/commands/generate_plugins_xml.py index 4efd70e0..1795dcf9 100644 --- a/qgis-app/plugins/management/commands/generate_plugins_xml.py +++ b/qgis-app/plugins/management/commands/generate_plugins_xml.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from django.core.management.base import BaseCommand from plugins.tasks.generate_plugins_xml import generate_plugins_xml - +from django.conf import settings class Command(BaseCommand): @@ -12,7 +12,7 @@ def add_arguments(self, parser): "-s", "--site", dest="site", - default="http://plugins.qgis.org", + default=settings.DEFAULT_PLUGINS_SITE, help="Site url to get the source of plugins", ) diff --git a/qgis-app/plugins/migrations/0004_merge_20231123_0018.py b/qgis-app/plugins/migrations/0004_merge_20231123_0018.py new file mode 100644 index 00000000..8e18926d --- /dev/null +++ b/qgis-app/plugins/migrations/0004_merge_20231123_0018.py @@ -0,0 +1,14 @@ +# Generated by Django 3.2.11 on 2023-11-23 00:18 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('plugins', '0002_plugins_feedback'), + ('plugins', '0003_plugin_allow_update_name'), + ] + + operations = [ + ] diff --git a/qgis-app/plugins/migrations/0009_merge_20240321_0207.py b/qgis-app/plugins/migrations/0009_merge_20240321_0207.py new file mode 100644 index 00000000..45cd685a --- /dev/null +++ b/qgis-app/plugins/migrations/0009_merge_20240321_0207.py @@ -0,0 +1,14 @@ +# Generated by Django 4.2.11 on 2024-03-21 02:07 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('plugins', '0004_merge_20231123_0018'), + ('plugins', '0008_merge_20240206_0448'), + ] + + operations = [ + ] diff --git a/qgis-app/plugins/models.py b/qgis-app/plugins/models.py index 9f9cc209..72e8cc5a 100644 --- a/qgis-app/plugins/models.py +++ b/qgis-app/plugins/models.py @@ -8,7 +8,7 @@ from django.contrib.auth.models import User from django.db import models from django.urls import reverse -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from django.utils import timezone from djangoratings.fields import AnonymousRatingField from taggit_autosuggest.managers import TaggableManager diff --git a/qgis-app/plugins/tasks/__init__.py b/qgis-app/plugins/tasks/__init__.py index 7987374c..a0df9010 100644 --- a/qgis-app/plugins/tasks/__init__.py +++ b/qgis-app/plugins/tasks/__init__.py @@ -1 +1,2 @@ -from plugins.tasks.generate_plugins_xml import * +from plugins.tasks.generate_plugins_xml import * # noqa +from plugins.tasks.update_feedjack import * # noqa diff --git a/qgis-app/plugins/tasks/generate_plugins_xml.py b/qgis-app/plugins/tasks/generate_plugins_xml.py index b1e60cef..a7415b60 100644 --- a/qgis-app/plugins/tasks/generate_plugins_xml.py +++ b/qgis-app/plugins/tasks/generate_plugins_xml.py @@ -2,10 +2,15 @@ import requests from celery import shared_task +from celery.utils.log import get_task_logger +from preferences import preferences from django.conf import settings from preferences import preferences +logger = get_task_logger(__name__) + + @shared_task def generate_plugins_xml(site=""): """ @@ -13,6 +18,8 @@ def generate_plugins_xml(site=""): :param site: site domain where the plugins will be fetched, default to http://plugins.qgis.org """ + logger.info('generate_plugins_xml : {}'.format(site)) + if not site: if settings.DEFAULT_PLUGINS_SITE: site = settings.DEFAULT_PLUGINS_SITE diff --git a/qgis-app/plugins/tasks/update_feedjack.py b/qgis-app/plugins/tasks/update_feedjack.py new file mode 100644 index 00000000..e12d14bd --- /dev/null +++ b/qgis-app/plugins/tasks/update_feedjack.py @@ -0,0 +1,10 @@ +from celery import shared_task +from celery.utils.log import get_task_logger + +logger = get_task_logger(__name__) + + +@shared_task +def update_feedjack(): + import subprocess + subprocess.call(['python', 'manage.py', 'feedjackupdate']) diff --git a/qgis-app/plugins/templates/plugins/form_snippet.html b/qgis-app/plugins/templates/plugins/form_snippet.html index f33ec0ce..ae794508 100755 --- a/qgis-app/plugins/templates/plugins/form_snippet.html +++ b/qgis-app/plugins/templates/plugins/form_snippet.html @@ -2,7 +2,7 @@
{% for field in form %}
- {% ifequal field.field.widget|klass 'CheckboxInput' %} + {% if field.field.widget|klass == 'CheckboxInput' %}
{% endif %} {{ field }} - {% endifequal %} + {% endif %}
{{ field.help_text }}
{% endfor %} diff --git a/qgis-app/plugins/templates/plugins/pagination.html b/qgis-app/plugins/templates/plugins/pagination.html index fc7e65c7..99bec111 100644 --- a/qgis-app/plugins/templates/plugins/pagination.html +++ b/qgis-app/plugins/templates/plugins/pagination.html @@ -8,11 +8,11 @@ {% endif %} {% for page in pages %} {% if page %} - {% ifequal page page_obj.number %} + {% if page == page_obj.number %} {{ page }} {% else %} {{ page }} - {% endifequal %} + {% endif %} {% else %} ... {% endif %} diff --git a/qgis-app/plugins/tests/test_change_maintainer.py b/qgis-app/plugins/tests/test_change_maintainer.py index a9357f6f..515b5c46 100644 --- a/qgis-app/plugins/tests/test_change_maintainer.py +++ b/qgis-app/plugins/tests/test_change_maintainer.py @@ -50,7 +50,7 @@ def setUp(self): self.plugin = Plugin.objects.get(name='Test Plugin') self.plugin.save() - @patch("plugins.tasks.generate_plugins_xml.delay", new=do_nothing) + @patch("plugins.tasks.generate_plugins_xml", new=do_nothing) @patch("plugins.validator._check_url_link", new=do_nothing) def test_change_maintainer(self): """ diff --git a/qgis-app/plugins/tests/test_plugin_update.py b/qgis-app/plugins/tests/test_plugin_update.py index 2bc5979a..bdb700e9 100644 --- a/qgis-app/plugins/tests/test_plugin_update.py +++ b/qgis-app/plugins/tests/test_plugin_update.py @@ -51,7 +51,7 @@ def setUp(self): self.plugin = Plugin.objects.get(name='Test Plugin') - @patch("plugins.tasks.generate_plugins_xml.delay", new=do_nothing) + @patch("plugins.tasks.generate_plugins_xml", new=do_nothing) @patch("plugins.validator._check_url_link", new=do_nothing) def test_plugin_new_version(self): """ @@ -103,7 +103,7 @@ def test_plugin_new_version(self): settings.EMAIL_HOST_USER ) - @patch("plugins.tasks.generate_plugins_xml.delay", new=do_nothing) + @patch("plugins.tasks.generate_plugins_xml", new=do_nothing) @patch("plugins.validator._check_url_link", new=do_nothing) def test_plugin_version_update(self): """ diff --git a/qgis-app/plugins/tests/test_plugin_upload.py b/qgis-app/plugins/tests/test_plugin_upload.py index 3539701a..9b3d2814 100644 --- a/qgis-app/plugins/tests/test_plugin_upload.py +++ b/qgis-app/plugins/tests/test_plugin_upload.py @@ -34,7 +34,7 @@ def setUp(self): email='test@example.com' ) - @patch("plugins.tasks.generate_plugins_xml.delay", new=do_nothing) + @patch("plugins.tasks.generate_plugins_xml", new=do_nothing) @patch("plugins.validator._check_url_link", new=do_nothing) def test_plugin_upload_form(self): # Log in the test user diff --git a/qgis-app/plugins/tests/test_rename_plugin.py b/qgis-app/plugins/tests/test_rename_plugin.py index d1eb890b..78ff7d69 100644 --- a/qgis-app/plugins/tests/test_rename_plugin.py +++ b/qgis-app/plugins/tests/test_rename_plugin.py @@ -51,7 +51,7 @@ def setUp(self): self.plugin.name = "New name Test Plugin" self.plugin.save() - @patch("plugins.tasks.generate_plugins_xml.delay", new=do_nothing) + @patch("plugins.tasks.generate_plugins_xml", new=do_nothing) @patch("plugins.validator._check_url_link", new=do_nothing) def test_plugin_rename(self): """ diff --git a/qgis-app/plugins/tests/test_validator.py b/qgis-app/plugins/tests/test_validator.py index 7a0e7e4e..6b1e9051 100644 --- a/qgis-app/plugins/tests/test_validator.py +++ b/qgis-app/plugins/tests/test_validator.py @@ -120,10 +120,7 @@ def test_check_url_link_ssl_error(self, mock_request): @mock.patch("requests.get", side_effect=requests.exceptions.HTTPError()) def test_check_url_link_does_not_exist(self, mock_request): url = "http://example.com/" - self.assertRaises( - ValidationError, - _check_url_link(url, "forbidden_url", "metadata attribute"), - ) + self.assertIsNone(_check_url_link(url, "forbidden_url", "metadata attribute")) class TestValidatorForbiddenFileFolder(TestCase): diff --git a/qgis-app/plugins/tests/testfiles/valid_plugin_0.0.2.zip_ b/qgis-app/plugins/tests/testfiles/valid_plugin_0.0.2.zip_ index c694809e..f5ba6212 100644 Binary files a/qgis-app/plugins/tests/testfiles/valid_plugin_0.0.2.zip_ and b/qgis-app/plugins/tests/testfiles/valid_plugin_0.0.2.zip_ differ diff --git a/qgis-app/plugins/tests/testfiles/valid_plugin_0.0.3.zip_ b/qgis-app/plugins/tests/testfiles/valid_plugin_0.0.3.zip_ index f838d489..613f0f1f 100644 Binary files a/qgis-app/plugins/tests/testfiles/valid_plugin_0.0.3.zip_ and b/qgis-app/plugins/tests/testfiles/valid_plugin_0.0.3.zip_ differ diff --git a/qgis-app/plugins/tests/tests.py b/qgis-app/plugins/tests/tests.py index 3748f41b..f51d798f 100644 --- a/qgis-app/plugins/tests/tests.py +++ b/qgis-app/plugins/tests/tests.py @@ -13,7 +13,7 @@ def test_basic_addition(self): """ Tests that 1 + 1 always equals 2. """ - self.failUnlessEqual(1 + 1, 2) + self.assertEqual(1 + 1, 2) __test__ = { diff --git a/qgis-app/plugins/urls.py b/qgis-app/plugins/urls.py index dd021892..3d1c5da3 100644 --- a/qgis-app/plugins/urls.py +++ b/qgis-app/plugins/urls.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -from django.conf.urls import include, url +from django.urls import re_path as url from django.contrib.auth.decorators import login_required, user_passes_test -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from plugins.models import Plugin, PluginVersion from plugins.views import * from rpc4django.views import serve_rpc_request diff --git a/qgis-app/plugins/validator.py b/qgis-app/plugins/validator.py index e1482bd4..f8ebd6ad 100644 --- a/qgis-app/plugins/validator.py +++ b/qgis-app/plugins/validator.py @@ -15,7 +15,7 @@ from django.conf import settings from django.core.files.uploadedfile import SimpleUploadedFile from django.forms import ValidationError -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ PLUGIN_MAX_UPLOAD_SIZE = getattr(settings, "PLUGIN_MAX_UPLOAD_SIZE", 25000000) # 25 mb PLUGIN_REQUIRED_METADATA = getattr( @@ -61,16 +61,16 @@ def _read_from_init(initcontent, initname): i = 0 lines = initcontent.split("\n") while i < len(lines): - if re.search("def\s+([^\(]+)", lines[i]): - k = re.search("def\s+([^\(]+)", lines[i]).groups()[0] + if re.search(r"def\s+([^\(]+)", lines[i]): + k = re.search(r"def\s+([^\(]+)", lines[i]).groups()[0] i += 1 while i < len(lines) and lines[i] != "": - if re.search("return\s+[\"']?([^\"']+)[\"']?", lines[i]): + if re.search(r"return\s+[\"']?([^\"']+)[\"']?", lines[i]): metadata.append( ( k, re.search( - "return\s+[\"']?([^\"']+)[\"']?", lines[i] + r"return\s+[\"']?([^\"']+)[\"']?", lines[i] ).groups()[0], ) ) @@ -255,7 +255,7 @@ def validator(package): try: parser = configparser.ConfigParser() parser.optionxform = str - parser.readfp(StringIO(codecs.decode(zip.read(metadataname), "utf8"))) + parser.read_file(StringIO(codecs.decode(zip.read(metadataname), "utf8"))) if not parser.has_section("general"): raise ValidationError( _("Cannot find a section named 'general' in %s") % metadataname diff --git a/qgis-app/plugins/views.py b/qgis-app/plugins/views.py index d1897117..20dfca1c 100644 --- a/qgis-app/plugins/views.py +++ b/qgis-app/plugins/views.py @@ -21,7 +21,7 @@ from django.utils.timezone import now from django.utils.decorators import method_decorator from django.utils.encoding import DjangoUnicodeDecodeError -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from django.views.decorators.cache import never_cache from django.views.decorators.csrf import ensure_csrf_cookie, csrf_exempt, csrf_protect from django.views.decorators.http import require_POST diff --git a/qgis-app/qgis_context_processor.py b/qgis-app/qgis_context_processor.py index 11ff0481..8b8de12c 100644 --- a/qgis-app/qgis_context_processor.py +++ b/qgis-app/qgis_context_processor.py @@ -2,12 +2,14 @@ from django.contrib.sites.models import Site from django.core.exceptions import ImproperlyConfigured +def is_ajax(request): + return request.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest' def additions(request): """Insert some additional information into the template context from the settings and set the base template according to qs. """ - if request.is_ajax() or request.GET.get("ajax"): + if is_ajax(request=request) or request.GET.get("ajax"): base_template = "ajax_base.html" is_naked = True else: diff --git a/qgis-app/settings.py b/qgis-app/settings.py index c3e7a79b..23d50828 100644 --- a/qgis-app/settings.py +++ b/qgis-app/settings.py @@ -253,7 +253,7 @@ DEBUG_TOOLBAR_CONFIG = {"INTERCEPT_REDIRECTS": False} -THUMBNAIL_ENGINE = "sorl.thumbnail.engines.convert_engine.Engine" +# THUMBNAIL_ENGINE = "sorl.thumbnail.engines.convert_engine.Engine" USER_MAP = { "project_name": "QGIS", diff --git a/qgis-app/settings_docker.py b/qgis-app/settings_docker.py index f51dc50b..8154e819 100644 --- a/qgis-app/settings_docker.py +++ b/qgis-app/settings_docker.py @@ -1,3 +1,6 @@ +from celery.schedules import crontab + +from settings import * import ast import os @@ -13,7 +16,7 @@ # Absolute filesystem path to the directory that will hold user-uploaded files. # Example: "/var/www/example.com/media/" -MEDIA_ROOT = os.environ.get("MEDIA_ROOT", "/home/web/media") +MEDIA_ROOT = os.environ.get("MEDIA_ROOT", "/home/web/media/") # URL that handles the media served from MEDIA_ROOT. Make sure to use a # trailing slash. @@ -26,7 +29,7 @@ # Don't put anything in this directory yourself; store your static files # in apps' "static/" subdirectories and in STATICFILES_DIRS. # Example: "/var/www/example.com/static/" -STATIC_ROOT = os.environ.get("STATIC_ROOT", "/home/web/static") +STATIC_ROOT = os.environ.get("STATIC_ROOT", "/home/web/static/") # URL prefix for static files. # Example: "http://example.com/static/", "http://static.example.com/" @@ -85,7 +88,10 @@ # models (sharing .model3 file feature) "models", "wavefronts", - "matomo" + "matomo", + + # Webpack + 'webpack_loader' ] DATABASES = { @@ -106,7 +112,7 @@ PAGINATION_DEFAULT_PAGINATION_HUB = 30 LOGIN_REDIRECT_URL = "/" SERVE_STATIC_MEDIA = DEBUG -DEFAULT_PLUGINS_SITE = os.environ.get("DEFAULT_PLUGINS_SITE", "") +DEFAULT_PLUGINS_SITE = os.environ.get("DEFAULT_PLUGINS_SITE", "https://plugins.qgis.org/") # See fig.yml file for postfix container definition # @@ -131,6 +137,21 @@ "TEST_REQUEST_DEFAULT_FORMAT": "json", } +CELERY_RESULT_BACKEND = 'rpc://' +CELERY_BROKER_URL = os.environ.get('BROKER_URL', 'amqp://rabbitmq:5672') +CELERY_BEAT_SCHEDULE = { + 'generate_plugins_xml': { + 'task': 'plugins.tasks.generate_plugins_xml.generate_plugins_xml', + 'schedule': crontab(minute='*/10'), # Execute every 10 minutes. + 'kwargs': { + 'site': DEFAULT_PLUGINS_SITE + } + }, + 'update_feedjack': { + 'task': 'plugins.tasks.update_feedjack.update_feedjack', + 'schedule': crontab(minute='*/30'), # Execute every 30 minutes. + } +} # Set plugin token access and refresh validity to a very long duration SIMPLE_JWT = { 'ACCESS_TOKEN_LIFETIME': timedelta(days=365*1000), @@ -139,3 +160,17 @@ MATOMO_SITE_ID="1" MATOMO_URL="//matomo.qgis.org/" + +# Default primary key type +DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' + + +print(SITE_ROOT) + +# Webpack +WEBPACK_LOADER = { + 'DEFAULT': { + 'BUNDLE_DIR_NAME': 'webpack-bundles', + 'STATS_FILE': os.path.join(SITE_ROOT, 'webpack-stats.json'), + } +} diff --git a/qgis-app/static/images/footlogo.svg b/qgis-app/static/images/footlogo.svg new file mode 100644 index 00000000..d4c103b6 --- /dev/null +++ b/qgis-app/static/images/footlogo.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/qgis-app/static/images/mastodon.svg b/qgis-app/static/images/mastodon.svg new file mode 100644 index 00000000..b22cab12 --- /dev/null +++ b/qgis-app/static/images/mastodon.svg @@ -0,0 +1,3 @@ + + + diff --git a/qgis-app/static/js/webpack.js b/qgis-app/static/js/webpack.js new file mode 100644 index 00000000..a0caea83 --- /dev/null +++ b/qgis-app/static/js/webpack.js @@ -0,0 +1,24 @@ +require('../style/scss/style.scss'); + +document.addEventListener('DOMContentLoaded', () => { + + // Get all "navbar-burger" elements + const $navbarBurgers = Array.prototype.slice.call( + document.querySelectorAll('.navbar-burger'), 0); + + // Add a click event on each of them + $navbarBurgers.forEach( el => { + el.addEventListener('click', () => { + + // Get the target from the "data-target" attribute + const target = el.dataset.target; + const $target = document.getElementById(target); + + // Toggle the "is-active" class on both the "navbar-burger" and the "navbar-menu" + el.classList.toggle('is-active'); + $target.classList.toggle('is-active'); + + }); + }); + + }); \ No newline at end of file diff --git a/qgis-app/static/style/fontawesome.css b/qgis-app/static/style/fontawesome.css new file mode 100644 index 00000000..2b64140e --- /dev/null +++ b/qgis-app/static/style/fontawesome.css @@ -0,0 +1,7946 @@ +/*! + * Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + * Copyright 2022 Fonticons, Inc. + */ +.fa { + font-family: var(--fa-style-family, "Font Awesome 6 Free"); + font-weight: var(--fa-style, 900); } + +.fa, +.fa-classic, +.fa-sharp, +.fas, +.fa-solid, +.far, +.fa-regular, +.fab, +.fa-brands { + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + display: var(--fa-display, inline-block); + font-style: normal; + font-variant: normal; + line-height: 1; + text-rendering: auto; } + +.fas, +.fa-classic, +.fa-solid, +.far, +.fa-regular { + font-family: 'Font Awesome 6 Free'; } + +.fab, +.fa-brands { + font-family: 'Font Awesome 6 Brands'; } + +.fa-1x { + font-size: 1em; } + +.fa-2x { + font-size: 2em; } + +.fa-3x { + font-size: 3em; } + +.fa-4x { + font-size: 4em; } + +.fa-5x { + font-size: 5em; } + +.fa-6x { + font-size: 6em; } + +.fa-7x { + font-size: 7em; } + +.fa-8x { + font-size: 8em; } + +.fa-9x { + font-size: 9em; } + +.fa-10x { + font-size: 10em; } + +.fa-2xs { + font-size: 0.625em; + line-height: 0.1em; + vertical-align: 0.225em; } + +.fa-xs { + font-size: 0.75em; + line-height: 0.08333em; + vertical-align: 0.125em; } + +.fa-sm { + font-size: 0.875em; + line-height: 0.07143em; + vertical-align: 0.05357em; } + +.fa-lg { + font-size: 1.25em; + line-height: 0.05em; + vertical-align: -0.075em; } + +.fa-xl { + font-size: 1.5em; + line-height: 0.04167em; + vertical-align: -0.125em; } + +.fa-2xl { + font-size: 2em; + line-height: 0.03125em; + vertical-align: -0.1875em; } + +.fa-fw { + text-align: center; + width: 1.25em; } + +.fa-ul { + list-style-type: none; + margin-left: var(--fa-li-margin, 2.5em); + padding-left: 0; } + .fa-ul > li { + position: relative; } + +.fa-li { + left: calc(var(--fa-li-width, 2em) * -1); + position: absolute; + text-align: center; + width: var(--fa-li-width, 2em); + line-height: inherit; } + +.fa-border { + border-color: var(--fa-border-color, #eee); + border-radius: var(--fa-border-radius, 0.1em); + border-style: var(--fa-border-style, solid); + border-width: var(--fa-border-width, 0.08em); + padding: var(--fa-border-padding, 0.2em 0.25em 0.15em); } + +.fa-pull-left { + float: left; + margin-right: var(--fa-pull-margin, 0.3em); } + +.fa-pull-right { + float: right; + margin-left: var(--fa-pull-margin, 0.3em); } + +.fa-beat { + -webkit-animation-name: fa-beat; + animation-name: fa-beat; + -webkit-animation-delay: var(--fa-animation-delay, 0s); + animation-delay: var(--fa-animation-delay, 0s); + -webkit-animation-direction: var(--fa-animation-direction, normal); + animation-direction: var(--fa-animation-direction, normal); + -webkit-animation-duration: var(--fa-animation-duration, 1s); + animation-duration: var(--fa-animation-duration, 1s); + -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); + animation-iteration-count: var(--fa-animation-iteration-count, infinite); + -webkit-animation-timing-function: var(--fa-animation-timing, ease-in-out); + animation-timing-function: var(--fa-animation-timing, ease-in-out); } + +.fa-bounce { + -webkit-animation-name: fa-bounce; + animation-name: fa-bounce; + -webkit-animation-delay: var(--fa-animation-delay, 0s); + animation-delay: var(--fa-animation-delay, 0s); + -webkit-animation-direction: var(--fa-animation-direction, normal); + animation-direction: var(--fa-animation-direction, normal); + -webkit-animation-duration: var(--fa-animation-duration, 1s); + animation-duration: var(--fa-animation-duration, 1s); + -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); + animation-iteration-count: var(--fa-animation-iteration-count, infinite); + -webkit-animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.28, 0.84, 0.42, 1)); + animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.28, 0.84, 0.42, 1)); } + +.fa-fade { + -webkit-animation-name: fa-fade; + animation-name: fa-fade; + -webkit-animation-delay: var(--fa-animation-delay, 0s); + animation-delay: var(--fa-animation-delay, 0s); + -webkit-animation-direction: var(--fa-animation-direction, normal); + animation-direction: var(--fa-animation-direction, normal); + -webkit-animation-duration: var(--fa-animation-duration, 1s); + animation-duration: var(--fa-animation-duration, 1s); + -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); + animation-iteration-count: var(--fa-animation-iteration-count, infinite); + -webkit-animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1)); + animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1)); } + +.fa-beat-fade { + -webkit-animation-name: fa-beat-fade; + animation-name: fa-beat-fade; + -webkit-animation-delay: var(--fa-animation-delay, 0s); + animation-delay: var(--fa-animation-delay, 0s); + -webkit-animation-direction: var(--fa-animation-direction, normal); + animation-direction: var(--fa-animation-direction, normal); + -webkit-animation-duration: var(--fa-animation-duration, 1s); + animation-duration: var(--fa-animation-duration, 1s); + -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); + animation-iteration-count: var(--fa-animation-iteration-count, infinite); + -webkit-animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1)); + animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1)); } + +.fa-flip { + -webkit-animation-name: fa-flip; + animation-name: fa-flip; + -webkit-animation-delay: var(--fa-animation-delay, 0s); + animation-delay: var(--fa-animation-delay, 0s); + -webkit-animation-direction: var(--fa-animation-direction, normal); + animation-direction: var(--fa-animation-direction, normal); + -webkit-animation-duration: var(--fa-animation-duration, 1s); + animation-duration: var(--fa-animation-duration, 1s); + -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); + animation-iteration-count: var(--fa-animation-iteration-count, infinite); + -webkit-animation-timing-function: var(--fa-animation-timing, ease-in-out); + animation-timing-function: var(--fa-animation-timing, ease-in-out); } + +.fa-shake { + -webkit-animation-name: fa-shake; + animation-name: fa-shake; + -webkit-animation-delay: var(--fa-animation-delay, 0s); + animation-delay: var(--fa-animation-delay, 0s); + -webkit-animation-direction: var(--fa-animation-direction, normal); + animation-direction: var(--fa-animation-direction, normal); + -webkit-animation-duration: var(--fa-animation-duration, 1s); + animation-duration: var(--fa-animation-duration, 1s); + -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); + animation-iteration-count: var(--fa-animation-iteration-count, infinite); + -webkit-animation-timing-function: var(--fa-animation-timing, linear); + animation-timing-function: var(--fa-animation-timing, linear); } + +.fa-spin { + -webkit-animation-name: fa-spin; + animation-name: fa-spin; + -webkit-animation-delay: var(--fa-animation-delay, 0s); + animation-delay: var(--fa-animation-delay, 0s); + -webkit-animation-direction: var(--fa-animation-direction, normal); + animation-direction: var(--fa-animation-direction, normal); + -webkit-animation-duration: var(--fa-animation-duration, 2s); + animation-duration: var(--fa-animation-duration, 2s); + -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); + animation-iteration-count: var(--fa-animation-iteration-count, infinite); + -webkit-animation-timing-function: var(--fa-animation-timing, linear); + animation-timing-function: var(--fa-animation-timing, linear); } + +.fa-spin-reverse { + --fa-animation-direction: reverse; } + +.fa-pulse, +.fa-spin-pulse { + -webkit-animation-name: fa-spin; + animation-name: fa-spin; + -webkit-animation-direction: var(--fa-animation-direction, normal); + animation-direction: var(--fa-animation-direction, normal); + -webkit-animation-duration: var(--fa-animation-duration, 1s); + animation-duration: var(--fa-animation-duration, 1s); + -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); + animation-iteration-count: var(--fa-animation-iteration-count, infinite); + -webkit-animation-timing-function: var(--fa-animation-timing, steps(8)); + animation-timing-function: var(--fa-animation-timing, steps(8)); } + +@media (prefers-reduced-motion: reduce) { + .fa-beat, + .fa-bounce, + .fa-fade, + .fa-beat-fade, + .fa-flip, + .fa-pulse, + .fa-shake, + .fa-spin, + .fa-spin-pulse { + -webkit-animation-delay: -1ms; + animation-delay: -1ms; + -webkit-animation-duration: 1ms; + animation-duration: 1ms; + -webkit-animation-iteration-count: 1; + animation-iteration-count: 1; + transition-delay: 0s; + transition-duration: 0s; } } + +@-webkit-keyframes fa-beat { + 0%, 90% { + -webkit-transform: scale(1); + transform: scale(1); } + 45% { + -webkit-transform: scale(var(--fa-beat-scale, 1.25)); + transform: scale(var(--fa-beat-scale, 1.25)); } } + +@keyframes fa-beat { + 0%, 90% { + -webkit-transform: scale(1); + transform: scale(1); } + 45% { + -webkit-transform: scale(var(--fa-beat-scale, 1.25)); + transform: scale(var(--fa-beat-scale, 1.25)); } } + +@-webkit-keyframes fa-bounce { + 0% { + -webkit-transform: scale(1, 1) translateY(0); + transform: scale(1, 1) translateY(0); } + 10% { + -webkit-transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0); + transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0); } + 30% { + -webkit-transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em)); + transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em)); } + 50% { + -webkit-transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0); + transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0); } + 57% { + -webkit-transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em)); + transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em)); } + 64% { + -webkit-transform: scale(1, 1) translateY(0); + transform: scale(1, 1) translateY(0); } + 100% { + -webkit-transform: scale(1, 1) translateY(0); + transform: scale(1, 1) translateY(0); } } + +@keyframes fa-bounce { + 0% { + -webkit-transform: scale(1, 1) translateY(0); + transform: scale(1, 1) translateY(0); } + 10% { + -webkit-transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0); + transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0); } + 30% { + -webkit-transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em)); + transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em)); } + 50% { + -webkit-transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0); + transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0); } + 57% { + -webkit-transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em)); + transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em)); } + 64% { + -webkit-transform: scale(1, 1) translateY(0); + transform: scale(1, 1) translateY(0); } + 100% { + -webkit-transform: scale(1, 1) translateY(0); + transform: scale(1, 1) translateY(0); } } + +@-webkit-keyframes fa-fade { + 50% { + opacity: var(--fa-fade-opacity, 0.4); } } + +@keyframes fa-fade { + 50% { + opacity: var(--fa-fade-opacity, 0.4); } } + +@-webkit-keyframes fa-beat-fade { + 0%, 100% { + opacity: var(--fa-beat-fade-opacity, 0.4); + -webkit-transform: scale(1); + transform: scale(1); } + 50% { + opacity: 1; + -webkit-transform: scale(var(--fa-beat-fade-scale, 1.125)); + transform: scale(var(--fa-beat-fade-scale, 1.125)); } } + +@keyframes fa-beat-fade { + 0%, 100% { + opacity: var(--fa-beat-fade-opacity, 0.4); + -webkit-transform: scale(1); + transform: scale(1); } + 50% { + opacity: 1; + -webkit-transform: scale(var(--fa-beat-fade-scale, 1.125)); + transform: scale(var(--fa-beat-fade-scale, 1.125)); } } + +@-webkit-keyframes fa-flip { + 50% { + -webkit-transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)); + transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)); } } + +@keyframes fa-flip { + 50% { + -webkit-transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)); + transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)); } } + +@-webkit-keyframes fa-shake { + 0% { + -webkit-transform: rotate(-15deg); + transform: rotate(-15deg); } + 4% { + -webkit-transform: rotate(15deg); + transform: rotate(15deg); } + 8%, 24% { + -webkit-transform: rotate(-18deg); + transform: rotate(-18deg); } + 12%, 28% { + -webkit-transform: rotate(18deg); + transform: rotate(18deg); } + 16% { + -webkit-transform: rotate(-22deg); + transform: rotate(-22deg); } + 20% { + -webkit-transform: rotate(22deg); + transform: rotate(22deg); } + 32% { + -webkit-transform: rotate(-12deg); + transform: rotate(-12deg); } + 36% { + -webkit-transform: rotate(12deg); + transform: rotate(12deg); } + 40%, 100% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); } } + +@keyframes fa-shake { + 0% { + -webkit-transform: rotate(-15deg); + transform: rotate(-15deg); } + 4% { + -webkit-transform: rotate(15deg); + transform: rotate(15deg); } + 8%, 24% { + -webkit-transform: rotate(-18deg); + transform: rotate(-18deg); } + 12%, 28% { + -webkit-transform: rotate(18deg); + transform: rotate(18deg); } + 16% { + -webkit-transform: rotate(-22deg); + transform: rotate(-22deg); } + 20% { + -webkit-transform: rotate(22deg); + transform: rotate(22deg); } + 32% { + -webkit-transform: rotate(-12deg); + transform: rotate(-12deg); } + 36% { + -webkit-transform: rotate(12deg); + transform: rotate(12deg); } + 40%, 100% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); } } + +@-webkit-keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); } } + +@keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); } } + +.fa-rotate-90 { + -webkit-transform: rotate(90deg); + transform: rotate(90deg); } + +.fa-rotate-180 { + -webkit-transform: rotate(180deg); + transform: rotate(180deg); } + +.fa-rotate-270 { + -webkit-transform: rotate(270deg); + transform: rotate(270deg); } + +.fa-flip-horizontal { + -webkit-transform: scale(-1, 1); + transform: scale(-1, 1); } + +.fa-flip-vertical { + -webkit-transform: scale(1, -1); + transform: scale(1, -1); } + +.fa-flip-both, +.fa-flip-horizontal.fa-flip-vertical { + -webkit-transform: scale(-1, -1); + transform: scale(-1, -1); } + +.fa-rotate-by { + -webkit-transform: rotate(var(--fa-rotate-angle, none)); + transform: rotate(var(--fa-rotate-angle, none)); } + +.fa-stack { + display: inline-block; + height: 2em; + line-height: 2em; + position: relative; + vertical-align: middle; + width: 2.5em; } + +.fa-stack-1x, +.fa-stack-2x { + left: 0; + position: absolute; + text-align: center; + width: 100%; + z-index: var(--fa-stack-z-index, auto); } + +.fa-stack-1x { + line-height: inherit; } + +.fa-stack-2x { + font-size: 2em; } + +.fa-inverse { + color: var(--fa-inverse, #fff); } + +/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen +readers do not read off random characters that represent icons */ +.fa-0::before { + content: "\30"; } + +.fa-1::before { + content: "\31"; } + +.fa-2::before { + content: "\32"; } + +.fa-3::before { + content: "\33"; } + +.fa-4::before { + content: "\34"; } + +.fa-5::before { + content: "\35"; } + +.fa-6::before { + content: "\36"; } + +.fa-7::before { + content: "\37"; } + +.fa-8::before { + content: "\38"; } + +.fa-9::before { + content: "\39"; } + +.fa-fill-drip::before { + content: "\f576"; } + +.fa-arrows-to-circle::before { + content: "\e4bd"; } + +.fa-circle-chevron-right::before { + content: "\f138"; } + +.fa-chevron-circle-right::before { + content: "\f138"; } + +.fa-at::before { + content: "\40"; } + +.fa-trash-can::before { + content: "\f2ed"; } + +.fa-trash-alt::before { + content: "\f2ed"; } + +.fa-text-height::before { + content: "\f034"; } + +.fa-user-xmark::before { + content: "\f235"; } + +.fa-user-times::before { + content: "\f235"; } + +.fa-stethoscope::before { + content: "\f0f1"; } + +.fa-message::before { + content: "\f27a"; } + +.fa-comment-alt::before { + content: "\f27a"; } + +.fa-info::before { + content: "\f129"; } + +.fa-down-left-and-up-right-to-center::before { + content: "\f422"; } + +.fa-compress-alt::before { + content: "\f422"; } + +.fa-explosion::before { + content: "\e4e9"; } + +.fa-file-lines::before { + content: "\f15c"; } + +.fa-file-alt::before { + content: "\f15c"; } + +.fa-file-text::before { + content: "\f15c"; } + +.fa-wave-square::before { + content: "\f83e"; } + +.fa-ring::before { + content: "\f70b"; } + +.fa-building-un::before { + content: "\e4d9"; } + +.fa-dice-three::before { + content: "\f527"; } + +.fa-calendar-days::before { + content: "\f073"; } + +.fa-calendar-alt::before { + content: "\f073"; } + +.fa-anchor-circle-check::before { + content: "\e4aa"; } + +.fa-building-circle-arrow-right::before { + content: "\e4d1"; } + +.fa-volleyball::before { + content: "\f45f"; } + +.fa-volleyball-ball::before { + content: "\f45f"; } + +.fa-arrows-up-to-line::before { + content: "\e4c2"; } + +.fa-sort-down::before { + content: "\f0dd"; } + +.fa-sort-desc::before { + content: "\f0dd"; } + +.fa-circle-minus::before { + content: "\f056"; } + +.fa-minus-circle::before { + content: "\f056"; } + +.fa-door-open::before { + content: "\f52b"; } + +.fa-right-from-bracket::before { + content: "\f2f5"; } + +.fa-sign-out-alt::before { + content: "\f2f5"; } + +.fa-atom::before { + content: "\f5d2"; } + +.fa-soap::before { + content: "\e06e"; } + +.fa-icons::before { + content: "\f86d"; } + +.fa-heart-music-camera-bolt::before { + content: "\f86d"; } + +.fa-microphone-lines-slash::before { + content: "\f539"; } + +.fa-microphone-alt-slash::before { + content: "\f539"; } + +.fa-bridge-circle-check::before { + content: "\e4c9"; } + +.fa-pump-medical::before { + content: "\e06a"; } + +.fa-fingerprint::before { + content: "\f577"; } + +.fa-hand-point-right::before { + content: "\f0a4"; } + +.fa-magnifying-glass-location::before { + content: "\f689"; } + +.fa-search-location::before { + content: "\f689"; } + +.fa-forward-step::before { + content: "\f051"; } + +.fa-step-forward::before { + content: "\f051"; } + +.fa-face-smile-beam::before { + content: "\f5b8"; } + +.fa-smile-beam::before { + content: "\f5b8"; } + +.fa-flag-checkered::before { + content: "\f11e"; } + +.fa-football::before { + content: "\f44e"; } + +.fa-football-ball::before { + content: "\f44e"; } + +.fa-school-circle-exclamation::before { + content: "\e56c"; } + +.fa-crop::before { + content: "\f125"; } + +.fa-angles-down::before { + content: "\f103"; } + +.fa-angle-double-down::before { + content: "\f103"; } + +.fa-users-rectangle::before { + content: "\e594"; } + +.fa-people-roof::before { + content: "\e537"; } + +.fa-people-line::before { + content: "\e534"; } + +.fa-beer-mug-empty::before { + content: "\f0fc"; } + +.fa-beer::before { + content: "\f0fc"; } + +.fa-diagram-predecessor::before { + content: "\e477"; } + +.fa-arrow-up-long::before { + content: "\f176"; } + +.fa-long-arrow-up::before { + content: "\f176"; } + +.fa-fire-flame-simple::before { + content: "\f46a"; } + +.fa-burn::before { + content: "\f46a"; } + +.fa-person::before { + content: "\f183"; } + +.fa-male::before { + content: "\f183"; } + +.fa-laptop::before { + content: "\f109"; } + +.fa-file-csv::before { + content: "\f6dd"; } + +.fa-menorah::before { + content: "\f676"; } + +.fa-truck-plane::before { + content: "\e58f"; } + +.fa-record-vinyl::before { + content: "\f8d9"; } + +.fa-face-grin-stars::before { + content: "\f587"; } + +.fa-grin-stars::before { + content: "\f587"; } + +.fa-bong::before { + content: "\f55c"; } + +.fa-spaghetti-monster-flying::before { + content: "\f67b"; } + +.fa-pastafarianism::before { + content: "\f67b"; } + +.fa-arrow-down-up-across-line::before { + content: "\e4af"; } + +.fa-spoon::before { + content: "\f2e5"; } + +.fa-utensil-spoon::before { + content: "\f2e5"; } + +.fa-jar-wheat::before { + content: "\e517"; } + +.fa-envelopes-bulk::before { + content: "\f674"; } + +.fa-mail-bulk::before { + content: "\f674"; } + +.fa-file-circle-exclamation::before { + content: "\e4eb"; } + +.fa-circle-h::before { + content: "\f47e"; } + +.fa-hospital-symbol::before { + content: "\f47e"; } + +.fa-pager::before { + content: "\f815"; } + +.fa-address-book::before { + content: "\f2b9"; } + +.fa-contact-book::before { + content: "\f2b9"; } + +.fa-strikethrough::before { + content: "\f0cc"; } + +.fa-k::before { + content: "\4b"; } + +.fa-landmark-flag::before { + content: "\e51c"; } + +.fa-pencil::before { + content: "\f303"; } + +.fa-pencil-alt::before { + content: "\f303"; } + +.fa-backward::before { + content: "\f04a"; } + +.fa-caret-right::before { + content: "\f0da"; } + +.fa-comments::before { + content: "\f086"; } + +.fa-paste::before { + content: "\f0ea"; } + +.fa-file-clipboard::before { + content: "\f0ea"; } + +.fa-code-pull-request::before { + content: "\e13c"; } + +.fa-clipboard-list::before { + content: "\f46d"; } + +.fa-truck-ramp-box::before { + content: "\f4de"; } + +.fa-truck-loading::before { + content: "\f4de"; } + +.fa-user-check::before { + content: "\f4fc"; } + +.fa-vial-virus::before { + content: "\e597"; } + +.fa-sheet-plastic::before { + content: "\e571"; } + +.fa-blog::before { + content: "\f781"; } + +.fa-user-ninja::before { + content: "\f504"; } + +.fa-person-arrow-up-from-line::before { + content: "\e539"; } + +.fa-scroll-torah::before { + content: "\f6a0"; } + +.fa-torah::before { + content: "\f6a0"; } + +.fa-broom-ball::before { + content: "\f458"; } + +.fa-quidditch::before { + content: "\f458"; } + +.fa-quidditch-broom-ball::before { + content: "\f458"; } + +.fa-toggle-off::before { + content: "\f204"; } + +.fa-box-archive::before { + content: "\f187"; } + +.fa-archive::before { + content: "\f187"; } + +.fa-person-drowning::before { + content: "\e545"; } + +.fa-arrow-down-9-1::before { + content: "\f886"; } + +.fa-sort-numeric-desc::before { + content: "\f886"; } + +.fa-sort-numeric-down-alt::before { + content: "\f886"; } + +.fa-face-grin-tongue-squint::before { + content: "\f58a"; } + +.fa-grin-tongue-squint::before { + content: "\f58a"; } + +.fa-spray-can::before { + content: "\f5bd"; } + +.fa-truck-monster::before { + content: "\f63b"; } + +.fa-w::before { + content: "\57"; } + +.fa-earth-africa::before { + content: "\f57c"; } + +.fa-globe-africa::before { + content: "\f57c"; } + +.fa-rainbow::before { + content: "\f75b"; } + +.fa-circle-notch::before { + content: "\f1ce"; } + +.fa-tablet-screen-button::before { + content: "\f3fa"; } + +.fa-tablet-alt::before { + content: "\f3fa"; } + +.fa-paw::before { + content: "\f1b0"; } + +.fa-cloud::before { + content: "\f0c2"; } + +.fa-trowel-bricks::before { + content: "\e58a"; } + +.fa-face-flushed::before { + content: "\f579"; } + +.fa-flushed::before { + content: "\f579"; } + +.fa-hospital-user::before { + content: "\f80d"; } + +.fa-tent-arrow-left-right::before { + content: "\e57f"; } + +.fa-gavel::before { + content: "\f0e3"; } + +.fa-legal::before { + content: "\f0e3"; } + +.fa-binoculars::before { + content: "\f1e5"; } + +.fa-microphone-slash::before { + content: "\f131"; } + +.fa-box-tissue::before { + content: "\e05b"; } + +.fa-motorcycle::before { + content: "\f21c"; } + +.fa-bell-concierge::before { + content: "\f562"; } + +.fa-concierge-bell::before { + content: "\f562"; } + +.fa-pen-ruler::before { + content: "\f5ae"; } + +.fa-pencil-ruler::before { + content: "\f5ae"; } + +.fa-people-arrows::before { + content: "\e068"; } + +.fa-people-arrows-left-right::before { + content: "\e068"; } + +.fa-mars-and-venus-burst::before { + content: "\e523"; } + +.fa-square-caret-right::before { + content: "\f152"; } + +.fa-caret-square-right::before { + content: "\f152"; } + +.fa-scissors::before { + content: "\f0c4"; } + +.fa-cut::before { + content: "\f0c4"; } + +.fa-sun-plant-wilt::before { + content: "\e57a"; } + +.fa-toilets-portable::before { + content: "\e584"; } + +.fa-hockey-puck::before { + content: "\f453"; } + +.fa-table::before { + content: "\f0ce"; } + +.fa-magnifying-glass-arrow-right::before { + content: "\e521"; } + +.fa-tachograph-digital::before { + content: "\f566"; } + +.fa-digital-tachograph::before { + content: "\f566"; } + +.fa-users-slash::before { + content: "\e073"; } + +.fa-clover::before { + content: "\e139"; } + +.fa-reply::before { + content: "\f3e5"; } + +.fa-mail-reply::before { + content: "\f3e5"; } + +.fa-star-and-crescent::before { + content: "\f699"; } + +.fa-house-fire::before { + content: "\e50c"; } + +.fa-square-minus::before { + content: "\f146"; } + +.fa-minus-square::before { + content: "\f146"; } + +.fa-helicopter::before { + content: "\f533"; } + +.fa-compass::before { + content: "\f14e"; } + +.fa-square-caret-down::before { + content: "\f150"; } + +.fa-caret-square-down::before { + content: "\f150"; } + +.fa-file-circle-question::before { + content: "\e4ef"; } + +.fa-laptop-code::before { + content: "\f5fc"; } + +.fa-swatchbook::before { + content: "\f5c3"; } + +.fa-prescription-bottle::before { + content: "\f485"; } + +.fa-bars::before { + content: "\f0c9"; } + +.fa-navicon::before { + content: "\f0c9"; } + +.fa-people-group::before { + content: "\e533"; } + +.fa-hourglass-end::before { + content: "\f253"; } + +.fa-hourglass-3::before { + content: "\f253"; } + +.fa-heart-crack::before { + content: "\f7a9"; } + +.fa-heart-broken::before { + content: "\f7a9"; } + +.fa-square-up-right::before { + content: "\f360"; } + +.fa-external-link-square-alt::before { + content: "\f360"; } + +.fa-face-kiss-beam::before { + content: "\f597"; } + +.fa-kiss-beam::before { + content: "\f597"; } + +.fa-film::before { + content: "\f008"; } + +.fa-ruler-horizontal::before { + content: "\f547"; } + +.fa-people-robbery::before { + content: "\e536"; } + +.fa-lightbulb::before { + content: "\f0eb"; } + +.fa-caret-left::before { + content: "\f0d9"; } + +.fa-circle-exclamation::before { + content: "\f06a"; } + +.fa-exclamation-circle::before { + content: "\f06a"; } + +.fa-school-circle-xmark::before { + content: "\e56d"; } + +.fa-arrow-right-from-bracket::before { + content: "\f08b"; } + +.fa-sign-out::before { + content: "\f08b"; } + +.fa-circle-chevron-down::before { + content: "\f13a"; } + +.fa-chevron-circle-down::before { + content: "\f13a"; } + +.fa-unlock-keyhole::before { + content: "\f13e"; } + +.fa-unlock-alt::before { + content: "\f13e"; } + +.fa-cloud-showers-heavy::before { + content: "\f740"; } + +.fa-headphones-simple::before { + content: "\f58f"; } + +.fa-headphones-alt::before { + content: "\f58f"; } + +.fa-sitemap::before { + content: "\f0e8"; } + +.fa-circle-dollar-to-slot::before { + content: "\f4b9"; } + +.fa-donate::before { + content: "\f4b9"; } + +.fa-memory::before { + content: "\f538"; } + +.fa-road-spikes::before { + content: "\e568"; } + +.fa-fire-burner::before { + content: "\e4f1"; } + +.fa-flag::before { + content: "\f024"; } + +.fa-hanukiah::before { + content: "\f6e6"; } + +.fa-feather::before { + content: "\f52d"; } + +.fa-volume-low::before { + content: "\f027"; } + +.fa-volume-down::before { + content: "\f027"; } + +.fa-comment-slash::before { + content: "\f4b3"; } + +.fa-cloud-sun-rain::before { + content: "\f743"; } + +.fa-compress::before { + content: "\f066"; } + +.fa-wheat-awn::before { + content: "\e2cd"; } + +.fa-wheat-alt::before { + content: "\e2cd"; } + +.fa-ankh::before { + content: "\f644"; } + +.fa-hands-holding-child::before { + content: "\e4fa"; } + +.fa-asterisk::before { + content: "\2a"; } + +.fa-square-check::before { + content: "\f14a"; } + +.fa-check-square::before { + content: "\f14a"; } + +.fa-peseta-sign::before { + content: "\e221"; } + +.fa-heading::before { + content: "\f1dc"; } + +.fa-header::before { + content: "\f1dc"; } + +.fa-ghost::before { + content: "\f6e2"; } + +.fa-list::before { + content: "\f03a"; } + +.fa-list-squares::before { + content: "\f03a"; } + +.fa-square-phone-flip::before { + content: "\f87b"; } + +.fa-phone-square-alt::before { + content: "\f87b"; } + +.fa-cart-plus::before { + content: "\f217"; } + +.fa-gamepad::before { + content: "\f11b"; } + +.fa-circle-dot::before { + content: "\f192"; } + +.fa-dot-circle::before { + content: "\f192"; } + +.fa-face-dizzy::before { + content: "\f567"; } + +.fa-dizzy::before { + content: "\f567"; } + +.fa-egg::before { + content: "\f7fb"; } + +.fa-house-medical-circle-xmark::before { + content: "\e513"; } + +.fa-campground::before { + content: "\f6bb"; } + +.fa-folder-plus::before { + content: "\f65e"; } + +.fa-futbol::before { + content: "\f1e3"; } + +.fa-futbol-ball::before { + content: "\f1e3"; } + +.fa-soccer-ball::before { + content: "\f1e3"; } + +.fa-paintbrush::before { + content: "\f1fc"; } + +.fa-paint-brush::before { + content: "\f1fc"; } + +.fa-lock::before { + content: "\f023"; } + +.fa-gas-pump::before { + content: "\f52f"; } + +.fa-hot-tub-person::before { + content: "\f593"; } + +.fa-hot-tub::before { + content: "\f593"; } + +.fa-map-location::before { + content: "\f59f"; } + +.fa-map-marked::before { + content: "\f59f"; } + +.fa-house-flood-water::before { + content: "\e50e"; } + +.fa-tree::before { + content: "\f1bb"; } + +.fa-bridge-lock::before { + content: "\e4cc"; } + +.fa-sack-dollar::before { + content: "\f81d"; } + +.fa-pen-to-square::before { + content: "\f044"; } + +.fa-edit::before { + content: "\f044"; } + +.fa-car-side::before { + content: "\f5e4"; } + +.fa-share-nodes::before { + content: "\f1e0"; } + +.fa-share-alt::before { + content: "\f1e0"; } + +.fa-heart-circle-minus::before { + content: "\e4ff"; } + +.fa-hourglass-half::before { + content: "\f252"; } + +.fa-hourglass-2::before { + content: "\f252"; } + +.fa-microscope::before { + content: "\f610"; } + +.fa-sink::before { + content: "\e06d"; } + +.fa-bag-shopping::before { + content: "\f290"; } + +.fa-shopping-bag::before { + content: "\f290"; } + +.fa-arrow-down-z-a::before { + content: "\f881"; } + +.fa-sort-alpha-desc::before { + content: "\f881"; } + +.fa-sort-alpha-down-alt::before { + content: "\f881"; } + +.fa-mitten::before { + content: "\f7b5"; } + +.fa-person-rays::before { + content: "\e54d"; } + +.fa-users::before { + content: "\f0c0"; } + +.fa-eye-slash::before { + content: "\f070"; } + +.fa-flask-vial::before { + content: "\e4f3"; } + +.fa-hand::before { + content: "\f256"; } + +.fa-hand-paper::before { + content: "\f256"; } + +.fa-om::before { + content: "\f679"; } + +.fa-worm::before { + content: "\e599"; } + +.fa-house-circle-xmark::before { + content: "\e50b"; } + +.fa-plug::before { + content: "\f1e6"; } + +.fa-chevron-up::before { + content: "\f077"; } + +.fa-hand-spock::before { + content: "\f259"; } + +.fa-stopwatch::before { + content: "\f2f2"; } + +.fa-face-kiss::before { + content: "\f596"; } + +.fa-kiss::before { + content: "\f596"; } + +.fa-bridge-circle-xmark::before { + content: "\e4cb"; } + +.fa-face-grin-tongue::before { + content: "\f589"; } + +.fa-grin-tongue::before { + content: "\f589"; } + +.fa-chess-bishop::before { + content: "\f43a"; } + +.fa-face-grin-wink::before { + content: "\f58c"; } + +.fa-grin-wink::before { + content: "\f58c"; } + +.fa-ear-deaf::before { + content: "\f2a4"; } + +.fa-deaf::before { + content: "\f2a4"; } + +.fa-deafness::before { + content: "\f2a4"; } + +.fa-hard-of-hearing::before { + content: "\f2a4"; } + +.fa-road-circle-check::before { + content: "\e564"; } + +.fa-dice-five::before { + content: "\f523"; } + +.fa-square-rss::before { + content: "\f143"; } + +.fa-rss-square::before { + content: "\f143"; } + +.fa-land-mine-on::before { + content: "\e51b"; } + +.fa-i-cursor::before { + content: "\f246"; } + +.fa-stamp::before { + content: "\f5bf"; } + +.fa-stairs::before { + content: "\e289"; } + +.fa-i::before { + content: "\49"; } + +.fa-hryvnia-sign::before { + content: "\f6f2"; } + +.fa-hryvnia::before { + content: "\f6f2"; } + +.fa-pills::before { + content: "\f484"; } + +.fa-face-grin-wide::before { + content: "\f581"; } + +.fa-grin-alt::before { + content: "\f581"; } + +.fa-tooth::before { + content: "\f5c9"; } + +.fa-v::before { + content: "\56"; } + +.fa-bangladeshi-taka-sign::before { + content: "\e2e6"; } + +.fa-bicycle::before { + content: "\f206"; } + +.fa-staff-snake::before { + content: "\e579"; } + +.fa-rod-asclepius::before { + content: "\e579"; } + +.fa-rod-snake::before { + content: "\e579"; } + +.fa-staff-aesculapius::before { + content: "\e579"; } + +.fa-head-side-cough-slash::before { + content: "\e062"; } + +.fa-truck-medical::before { + content: "\f0f9"; } + +.fa-ambulance::before { + content: "\f0f9"; } + +.fa-wheat-awn-circle-exclamation::before { + content: "\e598"; } + +.fa-snowman::before { + content: "\f7d0"; } + +.fa-mortar-pestle::before { + content: "\f5a7"; } + +.fa-road-barrier::before { + content: "\e562"; } + +.fa-school::before { + content: "\f549"; } + +.fa-igloo::before { + content: "\f7ae"; } + +.fa-joint::before { + content: "\f595"; } + +.fa-angle-right::before { + content: "\f105"; } + +.fa-horse::before { + content: "\f6f0"; } + +.fa-q::before { + content: "\51"; } + +.fa-g::before { + content: "\47"; } + +.fa-notes-medical::before { + content: "\f481"; } + +.fa-temperature-half::before { + content: "\f2c9"; } + +.fa-temperature-2::before { + content: "\f2c9"; } + +.fa-thermometer-2::before { + content: "\f2c9"; } + +.fa-thermometer-half::before { + content: "\f2c9"; } + +.fa-dong-sign::before { + content: "\e169"; } + +.fa-capsules::before { + content: "\f46b"; } + +.fa-poo-storm::before { + content: "\f75a"; } + +.fa-poo-bolt::before { + content: "\f75a"; } + +.fa-face-frown-open::before { + content: "\f57a"; } + +.fa-frown-open::before { + content: "\f57a"; } + +.fa-hand-point-up::before { + content: "\f0a6"; } + +.fa-money-bill::before { + content: "\f0d6"; } + +.fa-bookmark::before { + content: "\f02e"; } + +.fa-align-justify::before { + content: "\f039"; } + +.fa-umbrella-beach::before { + content: "\f5ca"; } + +.fa-helmet-un::before { + content: "\e503"; } + +.fa-bullseye::before { + content: "\f140"; } + +.fa-bacon::before { + content: "\f7e5"; } + +.fa-hand-point-down::before { + content: "\f0a7"; } + +.fa-arrow-up-from-bracket::before { + content: "\e09a"; } + +.fa-folder::before { + content: "\f07b"; } + +.fa-folder-blank::before { + content: "\f07b"; } + +.fa-file-waveform::before { + content: "\f478"; } + +.fa-file-medical-alt::before { + content: "\f478"; } + +.fa-radiation::before { + content: "\f7b9"; } + +.fa-chart-simple::before { + content: "\e473"; } + +.fa-mars-stroke::before { + content: "\f229"; } + +.fa-vial::before { + content: "\f492"; } + +.fa-gauge::before { + content: "\f624"; } + +.fa-dashboard::before { + content: "\f624"; } + +.fa-gauge-med::before { + content: "\f624"; } + +.fa-tachometer-alt-average::before { + content: "\f624"; } + +.fa-wand-magic-sparkles::before { + content: "\e2ca"; } + +.fa-magic-wand-sparkles::before { + content: "\e2ca"; } + +.fa-e::before { + content: "\45"; } + +.fa-pen-clip::before { + content: "\f305"; } + +.fa-pen-alt::before { + content: "\f305"; } + +.fa-bridge-circle-exclamation::before { + content: "\e4ca"; } + +.fa-user::before { + content: "\f007"; } + +.fa-school-circle-check::before { + content: "\e56b"; } + +.fa-dumpster::before { + content: "\f793"; } + +.fa-van-shuttle::before { + content: "\f5b6"; } + +.fa-shuttle-van::before { + content: "\f5b6"; } + +.fa-building-user::before { + content: "\e4da"; } + +.fa-square-caret-left::before { + content: "\f191"; } + +.fa-caret-square-left::before { + content: "\f191"; } + +.fa-highlighter::before { + content: "\f591"; } + +.fa-key::before { + content: "\f084"; } + +.fa-bullhorn::before { + content: "\f0a1"; } + +.fa-globe::before { + content: "\f0ac"; } + +.fa-synagogue::before { + content: "\f69b"; } + +.fa-person-half-dress::before { + content: "\e548"; } + +.fa-road-bridge::before { + content: "\e563"; } + +.fa-location-arrow::before { + content: "\f124"; } + +.fa-c::before { + content: "\43"; } + +.fa-tablet-button::before { + content: "\f10a"; } + +.fa-building-lock::before { + content: "\e4d6"; } + +.fa-pizza-slice::before { + content: "\f818"; } + +.fa-money-bill-wave::before { + content: "\f53a"; } + +.fa-chart-area::before { + content: "\f1fe"; } + +.fa-area-chart::before { + content: "\f1fe"; } + +.fa-house-flag::before { + content: "\e50d"; } + +.fa-person-circle-minus::before { + content: "\e540"; } + +.fa-ban::before { + content: "\f05e"; } + +.fa-cancel::before { + content: "\f05e"; } + +.fa-camera-rotate::before { + content: "\e0d8"; } + +.fa-spray-can-sparkles::before { + content: "\f5d0"; } + +.fa-air-freshener::before { + content: "\f5d0"; } + +.fa-star::before { + content: "\f005"; } + +.fa-repeat::before { + content: "\f363"; } + +.fa-cross::before { + content: "\f654"; } + +.fa-box::before { + content: "\f466"; } + +.fa-venus-mars::before { + content: "\f228"; } + +.fa-arrow-pointer::before { + content: "\f245"; } + +.fa-mouse-pointer::before { + content: "\f245"; } + +.fa-maximize::before { + content: "\f31e"; } + +.fa-expand-arrows-alt::before { + content: "\f31e"; } + +.fa-charging-station::before { + content: "\f5e7"; } + +.fa-shapes::before { + content: "\f61f"; } + +.fa-triangle-circle-square::before { + content: "\f61f"; } + +.fa-shuffle::before { + content: "\f074"; } + +.fa-random::before { + content: "\f074"; } + +.fa-person-running::before { + content: "\f70c"; } + +.fa-running::before { + content: "\f70c"; } + +.fa-mobile-retro::before { + content: "\e527"; } + +.fa-grip-lines-vertical::before { + content: "\f7a5"; } + +.fa-spider::before { + content: "\f717"; } + +.fa-hands-bound::before { + content: "\e4f9"; } + +.fa-file-invoice-dollar::before { + content: "\f571"; } + +.fa-plane-circle-exclamation::before { + content: "\e556"; } + +.fa-x-ray::before { + content: "\f497"; } + +.fa-spell-check::before { + content: "\f891"; } + +.fa-slash::before { + content: "\f715"; } + +.fa-computer-mouse::before { + content: "\f8cc"; } + +.fa-mouse::before { + content: "\f8cc"; } + +.fa-arrow-right-to-bracket::before { + content: "\f090"; } + +.fa-sign-in::before { + content: "\f090"; } + +.fa-shop-slash::before { + content: "\e070"; } + +.fa-store-alt-slash::before { + content: "\e070"; } + +.fa-server::before { + content: "\f233"; } + +.fa-virus-covid-slash::before { + content: "\e4a9"; } + +.fa-shop-lock::before { + content: "\e4a5"; } + +.fa-hourglass-start::before { + content: "\f251"; } + +.fa-hourglass-1::before { + content: "\f251"; } + +.fa-blender-phone::before { + content: "\f6b6"; } + +.fa-building-wheat::before { + content: "\e4db"; } + +.fa-person-breastfeeding::before { + content: "\e53a"; } + +.fa-right-to-bracket::before { + content: "\f2f6"; } + +.fa-sign-in-alt::before { + content: "\f2f6"; } + +.fa-venus::before { + content: "\f221"; } + +.fa-passport::before { + content: "\f5ab"; } + +.fa-heart-pulse::before { + content: "\f21e"; } + +.fa-heartbeat::before { + content: "\f21e"; } + +.fa-people-carry-box::before { + content: "\f4ce"; } + +.fa-people-carry::before { + content: "\f4ce"; } + +.fa-temperature-high::before { + content: "\f769"; } + +.fa-microchip::before { + content: "\f2db"; } + +.fa-crown::before { + content: "\f521"; } + +.fa-weight-hanging::before { + content: "\f5cd"; } + +.fa-xmarks-lines::before { + content: "\e59a"; } + +.fa-file-prescription::before { + content: "\f572"; } + +.fa-weight-scale::before { + content: "\f496"; } + +.fa-weight::before { + content: "\f496"; } + +.fa-user-group::before { + content: "\f500"; } + +.fa-user-friends::before { + content: "\f500"; } + +.fa-arrow-up-a-z::before { + content: "\f15e"; } + +.fa-sort-alpha-up::before { + content: "\f15e"; } + +.fa-chess-knight::before { + content: "\f441"; } + +.fa-face-laugh-squint::before { + content: "\f59b"; } + +.fa-laugh-squint::before { + content: "\f59b"; } + +.fa-wheelchair::before { + content: "\f193"; } + +.fa-circle-arrow-up::before { + content: "\f0aa"; } + +.fa-arrow-circle-up::before { + content: "\f0aa"; } + +.fa-toggle-on::before { + content: "\f205"; } + +.fa-person-walking::before { + content: "\f554"; } + +.fa-walking::before { + content: "\f554"; } + +.fa-l::before { + content: "\4c"; } + +.fa-fire::before { + content: "\f06d"; } + +.fa-bed-pulse::before { + content: "\f487"; } + +.fa-procedures::before { + content: "\f487"; } + +.fa-shuttle-space::before { + content: "\f197"; } + +.fa-space-shuttle::before { + content: "\f197"; } + +.fa-face-laugh::before { + content: "\f599"; } + +.fa-laugh::before { + content: "\f599"; } + +.fa-folder-open::before { + content: "\f07c"; } + +.fa-heart-circle-plus::before { + content: "\e500"; } + +.fa-code-fork::before { + content: "\e13b"; } + +.fa-city::before { + content: "\f64f"; } + +.fa-microphone-lines::before { + content: "\f3c9"; } + +.fa-microphone-alt::before { + content: "\f3c9"; } + +.fa-pepper-hot::before { + content: "\f816"; } + +.fa-unlock::before { + content: "\f09c"; } + +.fa-colon-sign::before { + content: "\e140"; } + +.fa-headset::before { + content: "\f590"; } + +.fa-store-slash::before { + content: "\e071"; } + +.fa-road-circle-xmark::before { + content: "\e566"; } + +.fa-user-minus::before { + content: "\f503"; } + +.fa-mars-stroke-up::before { + content: "\f22a"; } + +.fa-mars-stroke-v::before { + content: "\f22a"; } + +.fa-champagne-glasses::before { + content: "\f79f"; } + +.fa-glass-cheers::before { + content: "\f79f"; } + +.fa-clipboard::before { + content: "\f328"; } + +.fa-house-circle-exclamation::before { + content: "\e50a"; } + +.fa-file-arrow-up::before { + content: "\f574"; } + +.fa-file-upload::before { + content: "\f574"; } + +.fa-wifi::before { + content: "\f1eb"; } + +.fa-wifi-3::before { + content: "\f1eb"; } + +.fa-wifi-strong::before { + content: "\f1eb"; } + +.fa-bath::before { + content: "\f2cd"; } + +.fa-bathtub::before { + content: "\f2cd"; } + +.fa-underline::before { + content: "\f0cd"; } + +.fa-user-pen::before { + content: "\f4ff"; } + +.fa-user-edit::before { + content: "\f4ff"; } + +.fa-signature::before { + content: "\f5b7"; } + +.fa-stroopwafel::before { + content: "\f551"; } + +.fa-bold::before { + content: "\f032"; } + +.fa-anchor-lock::before { + content: "\e4ad"; } + +.fa-building-ngo::before { + content: "\e4d7"; } + +.fa-manat-sign::before { + content: "\e1d5"; } + +.fa-not-equal::before { + content: "\f53e"; } + +.fa-border-top-left::before { + content: "\f853"; } + +.fa-border-style::before { + content: "\f853"; } + +.fa-map-location-dot::before { + content: "\f5a0"; } + +.fa-map-marked-alt::before { + content: "\f5a0"; } + +.fa-jedi::before { + content: "\f669"; } + +.fa-square-poll-vertical::before { + content: "\f681"; } + +.fa-poll::before { + content: "\f681"; } + +.fa-mug-hot::before { + content: "\f7b6"; } + +.fa-car-battery::before { + content: "\f5df"; } + +.fa-battery-car::before { + content: "\f5df"; } + +.fa-gift::before { + content: "\f06b"; } + +.fa-dice-two::before { + content: "\f528"; } + +.fa-chess-queen::before { + content: "\f445"; } + +.fa-glasses::before { + content: "\f530"; } + +.fa-chess-board::before { + content: "\f43c"; } + +.fa-building-circle-check::before { + content: "\e4d2"; } + +.fa-person-chalkboard::before { + content: "\e53d"; } + +.fa-mars-stroke-right::before { + content: "\f22b"; } + +.fa-mars-stroke-h::before { + content: "\f22b"; } + +.fa-hand-back-fist::before { + content: "\f255"; } + +.fa-hand-rock::before { + content: "\f255"; } + +.fa-square-caret-up::before { + content: "\f151"; } + +.fa-caret-square-up::before { + content: "\f151"; } + +.fa-cloud-showers-water::before { + content: "\e4e4"; } + +.fa-chart-bar::before { + content: "\f080"; } + +.fa-bar-chart::before { + content: "\f080"; } + +.fa-hands-bubbles::before { + content: "\e05e"; } + +.fa-hands-wash::before { + content: "\e05e"; } + +.fa-less-than-equal::before { + content: "\f537"; } + +.fa-train::before { + content: "\f238"; } + +.fa-eye-low-vision::before { + content: "\f2a8"; } + +.fa-low-vision::before { + content: "\f2a8"; } + +.fa-crow::before { + content: "\f520"; } + +.fa-sailboat::before { + content: "\e445"; } + +.fa-window-restore::before { + content: "\f2d2"; } + +.fa-square-plus::before { + content: "\f0fe"; } + +.fa-plus-square::before { + content: "\f0fe"; } + +.fa-torii-gate::before { + content: "\f6a1"; } + +.fa-frog::before { + content: "\f52e"; } + +.fa-bucket::before { + content: "\e4cf"; } + +.fa-image::before { + content: "\f03e"; } + +.fa-microphone::before { + content: "\f130"; } + +.fa-cow::before { + content: "\f6c8"; } + +.fa-caret-up::before { + content: "\f0d8"; } + +.fa-screwdriver::before { + content: "\f54a"; } + +.fa-folder-closed::before { + content: "\e185"; } + +.fa-house-tsunami::before { + content: "\e515"; } + +.fa-square-nfi::before { + content: "\e576"; } + +.fa-arrow-up-from-ground-water::before { + content: "\e4b5"; } + +.fa-martini-glass::before { + content: "\f57b"; } + +.fa-glass-martini-alt::before { + content: "\f57b"; } + +.fa-rotate-left::before { + content: "\f2ea"; } + +.fa-rotate-back::before { + content: "\f2ea"; } + +.fa-rotate-backward::before { + content: "\f2ea"; } + +.fa-undo-alt::before { + content: "\f2ea"; } + +.fa-table-columns::before { + content: "\f0db"; } + +.fa-columns::before { + content: "\f0db"; } + +.fa-lemon::before { + content: "\f094"; } + +.fa-head-side-mask::before { + content: "\e063"; } + +.fa-handshake::before { + content: "\f2b5"; } + +.fa-gem::before { + content: "\f3a5"; } + +.fa-dolly::before { + content: "\f472"; } + +.fa-dolly-box::before { + content: "\f472"; } + +.fa-smoking::before { + content: "\f48d"; } + +.fa-minimize::before { + content: "\f78c"; } + +.fa-compress-arrows-alt::before { + content: "\f78c"; } + +.fa-monument::before { + content: "\f5a6"; } + +.fa-snowplow::before { + content: "\f7d2"; } + +.fa-angles-right::before { + content: "\f101"; } + +.fa-angle-double-right::before { + content: "\f101"; } + +.fa-cannabis::before { + content: "\f55f"; } + +.fa-circle-play::before { + content: "\f144"; } + +.fa-play-circle::before { + content: "\f144"; } + +.fa-tablets::before { + content: "\f490"; } + +.fa-ethernet::before { + content: "\f796"; } + +.fa-euro-sign::before { + content: "\f153"; } + +.fa-eur::before { + content: "\f153"; } + +.fa-euro::before { + content: "\f153"; } + +.fa-chair::before { + content: "\f6c0"; } + +.fa-circle-check::before { + content: "\f058"; } + +.fa-check-circle::before { + content: "\f058"; } + +.fa-circle-stop::before { + content: "\f28d"; } + +.fa-stop-circle::before { + content: "\f28d"; } + +.fa-compass-drafting::before { + content: "\f568"; } + +.fa-drafting-compass::before { + content: "\f568"; } + +.fa-plate-wheat::before { + content: "\e55a"; } + +.fa-icicles::before { + content: "\f7ad"; } + +.fa-person-shelter::before { + content: "\e54f"; } + +.fa-neuter::before { + content: "\f22c"; } + +.fa-id-badge::before { + content: "\f2c1"; } + +.fa-marker::before { + content: "\f5a1"; } + +.fa-face-laugh-beam::before { + content: "\f59a"; } + +.fa-laugh-beam::before { + content: "\f59a"; } + +.fa-helicopter-symbol::before { + content: "\e502"; } + +.fa-universal-access::before { + content: "\f29a"; } + +.fa-circle-chevron-up::before { + content: "\f139"; } + +.fa-chevron-circle-up::before { + content: "\f139"; } + +.fa-lari-sign::before { + content: "\e1c8"; } + +.fa-volcano::before { + content: "\f770"; } + +.fa-person-walking-dashed-line-arrow-right::before { + content: "\e553"; } + +.fa-sterling-sign::before { + content: "\f154"; } + +.fa-gbp::before { + content: "\f154"; } + +.fa-pound-sign::before { + content: "\f154"; } + +.fa-viruses::before { + content: "\e076"; } + +.fa-square-person-confined::before { + content: "\e577"; } + +.fa-user-tie::before { + content: "\f508"; } + +.fa-arrow-down-long::before { + content: "\f175"; } + +.fa-long-arrow-down::before { + content: "\f175"; } + +.fa-tent-arrow-down-to-line::before { + content: "\e57e"; } + +.fa-certificate::before { + content: "\f0a3"; } + +.fa-reply-all::before { + content: "\f122"; } + +.fa-mail-reply-all::before { + content: "\f122"; } + +.fa-suitcase::before { + content: "\f0f2"; } + +.fa-person-skating::before { + content: "\f7c5"; } + +.fa-skating::before { + content: "\f7c5"; } + +.fa-filter-circle-dollar::before { + content: "\f662"; } + +.fa-funnel-dollar::before { + content: "\f662"; } + +.fa-camera-retro::before { + content: "\f083"; } + +.fa-circle-arrow-down::before { + content: "\f0ab"; } + +.fa-arrow-circle-down::before { + content: "\f0ab"; } + +.fa-file-import::before { + content: "\f56f"; } + +.fa-arrow-right-to-file::before { + content: "\f56f"; } + +.fa-square-arrow-up-right::before { + content: "\f14c"; } + +.fa-external-link-square::before { + content: "\f14c"; } + +.fa-box-open::before { + content: "\f49e"; } + +.fa-scroll::before { + content: "\f70e"; } + +.fa-spa::before { + content: "\f5bb"; } + +.fa-location-pin-lock::before { + content: "\e51f"; } + +.fa-pause::before { + content: "\f04c"; } + +.fa-hill-avalanche::before { + content: "\e507"; } + +.fa-temperature-empty::before { + content: "\f2cb"; } + +.fa-temperature-0::before { + content: "\f2cb"; } + +.fa-thermometer-0::before { + content: "\f2cb"; } + +.fa-thermometer-empty::before { + content: "\f2cb"; } + +.fa-bomb::before { + content: "\f1e2"; } + +.fa-registered::before { + content: "\f25d"; } + +.fa-address-card::before { + content: "\f2bb"; } + +.fa-contact-card::before { + content: "\f2bb"; } + +.fa-vcard::before { + content: "\f2bb"; } + +.fa-scale-unbalanced-flip::before { + content: "\f516"; } + +.fa-balance-scale-right::before { + content: "\f516"; } + +.fa-subscript::before { + content: "\f12c"; } + +.fa-diamond-turn-right::before { + content: "\f5eb"; } + +.fa-directions::before { + content: "\f5eb"; } + +.fa-burst::before { + content: "\e4dc"; } + +.fa-house-laptop::before { + content: "\e066"; } + +.fa-laptop-house::before { + content: "\e066"; } + +.fa-face-tired::before { + content: "\f5c8"; } + +.fa-tired::before { + content: "\f5c8"; } + +.fa-money-bills::before { + content: "\e1f3"; } + +.fa-smog::before { + content: "\f75f"; } + +.fa-crutch::before { + content: "\f7f7"; } + +.fa-cloud-arrow-up::before { + content: "\f0ee"; } + +.fa-cloud-upload::before { + content: "\f0ee"; } + +.fa-cloud-upload-alt::before { + content: "\f0ee"; } + +.fa-palette::before { + content: "\f53f"; } + +.fa-arrows-turn-right::before { + content: "\e4c0"; } + +.fa-vest::before { + content: "\e085"; } + +.fa-ferry::before { + content: "\e4ea"; } + +.fa-arrows-down-to-people::before { + content: "\e4b9"; } + +.fa-seedling::before { + content: "\f4d8"; } + +.fa-sprout::before { + content: "\f4d8"; } + +.fa-left-right::before { + content: "\f337"; } + +.fa-arrows-alt-h::before { + content: "\f337"; } + +.fa-boxes-packing::before { + content: "\e4c7"; } + +.fa-circle-arrow-left::before { + content: "\f0a8"; } + +.fa-arrow-circle-left::before { + content: "\f0a8"; } + +.fa-group-arrows-rotate::before { + content: "\e4f6"; } + +.fa-bowl-food::before { + content: "\e4c6"; } + +.fa-candy-cane::before { + content: "\f786"; } + +.fa-arrow-down-wide-short::before { + content: "\f160"; } + +.fa-sort-amount-asc::before { + content: "\f160"; } + +.fa-sort-amount-down::before { + content: "\f160"; } + +.fa-cloud-bolt::before { + content: "\f76c"; } + +.fa-thunderstorm::before { + content: "\f76c"; } + +.fa-text-slash::before { + content: "\f87d"; } + +.fa-remove-format::before { + content: "\f87d"; } + +.fa-face-smile-wink::before { + content: "\f4da"; } + +.fa-smile-wink::before { + content: "\f4da"; } + +.fa-file-word::before { + content: "\f1c2"; } + +.fa-file-powerpoint::before { + content: "\f1c4"; } + +.fa-arrows-left-right::before { + content: "\f07e"; } + +.fa-arrows-h::before { + content: "\f07e"; } + +.fa-house-lock::before { + content: "\e510"; } + +.fa-cloud-arrow-down::before { + content: "\f0ed"; } + +.fa-cloud-download::before { + content: "\f0ed"; } + +.fa-cloud-download-alt::before { + content: "\f0ed"; } + +.fa-children::before { + content: "\e4e1"; } + +.fa-chalkboard::before { + content: "\f51b"; } + +.fa-blackboard::before { + content: "\f51b"; } + +.fa-user-large-slash::before { + content: "\f4fa"; } + +.fa-user-alt-slash::before { + content: "\f4fa"; } + +.fa-envelope-open::before { + content: "\f2b6"; } + +.fa-handshake-simple-slash::before { + content: "\e05f"; } + +.fa-handshake-alt-slash::before { + content: "\e05f"; } + +.fa-mattress-pillow::before { + content: "\e525"; } + +.fa-guarani-sign::before { + content: "\e19a"; } + +.fa-arrows-rotate::before { + content: "\f021"; } + +.fa-refresh::before { + content: "\f021"; } + +.fa-sync::before { + content: "\f021"; } + +.fa-fire-extinguisher::before { + content: "\f134"; } + +.fa-cruzeiro-sign::before { + content: "\e152"; } + +.fa-greater-than-equal::before { + content: "\f532"; } + +.fa-shield-halved::before { + content: "\f3ed"; } + +.fa-shield-alt::before { + content: "\f3ed"; } + +.fa-book-atlas::before { + content: "\f558"; } + +.fa-atlas::before { + content: "\f558"; } + +.fa-virus::before { + content: "\e074"; } + +.fa-envelope-circle-check::before { + content: "\e4e8"; } + +.fa-layer-group::before { + content: "\f5fd"; } + +.fa-arrows-to-dot::before { + content: "\e4be"; } + +.fa-archway::before { + content: "\f557"; } + +.fa-heart-circle-check::before { + content: "\e4fd"; } + +.fa-house-chimney-crack::before { + content: "\f6f1"; } + +.fa-house-damage::before { + content: "\f6f1"; } + +.fa-file-zipper::before { + content: "\f1c6"; } + +.fa-file-archive::before { + content: "\f1c6"; } + +.fa-square::before { + content: "\f0c8"; } + +.fa-martini-glass-empty::before { + content: "\f000"; } + +.fa-glass-martini::before { + content: "\f000"; } + +.fa-couch::before { + content: "\f4b8"; } + +.fa-cedi-sign::before { + content: "\e0df"; } + +.fa-italic::before { + content: "\f033"; } + +.fa-church::before { + content: "\f51d"; } + +.fa-comments-dollar::before { + content: "\f653"; } + +.fa-democrat::before { + content: "\f747"; } + +.fa-z::before { + content: "\5a"; } + +.fa-person-skiing::before { + content: "\f7c9"; } + +.fa-skiing::before { + content: "\f7c9"; } + +.fa-road-lock::before { + content: "\e567"; } + +.fa-a::before { + content: "\41"; } + +.fa-temperature-arrow-down::before { + content: "\e03f"; } + +.fa-temperature-down::before { + content: "\e03f"; } + +.fa-feather-pointed::before { + content: "\f56b"; } + +.fa-feather-alt::before { + content: "\f56b"; } + +.fa-p::before { + content: "\50"; } + +.fa-snowflake::before { + content: "\f2dc"; } + +.fa-newspaper::before { + content: "\f1ea"; } + +.fa-rectangle-ad::before { + content: "\f641"; } + +.fa-ad::before { + content: "\f641"; } + +.fa-circle-arrow-right::before { + content: "\f0a9"; } + +.fa-arrow-circle-right::before { + content: "\f0a9"; } + +.fa-filter-circle-xmark::before { + content: "\e17b"; } + +.fa-locust::before { + content: "\e520"; } + +.fa-sort::before { + content: "\f0dc"; } + +.fa-unsorted::before { + content: "\f0dc"; } + +.fa-list-ol::before { + content: "\f0cb"; } + +.fa-list-1-2::before { + content: "\f0cb"; } + +.fa-list-numeric::before { + content: "\f0cb"; } + +.fa-person-dress-burst::before { + content: "\e544"; } + +.fa-money-check-dollar::before { + content: "\f53d"; } + +.fa-money-check-alt::before { + content: "\f53d"; } + +.fa-vector-square::before { + content: "\f5cb"; } + +.fa-bread-slice::before { + content: "\f7ec"; } + +.fa-language::before { + content: "\f1ab"; } + +.fa-face-kiss-wink-heart::before { + content: "\f598"; } + +.fa-kiss-wink-heart::before { + content: "\f598"; } + +.fa-filter::before { + content: "\f0b0"; } + +.fa-question::before { + content: "\3f"; } + +.fa-file-signature::before { + content: "\f573"; } + +.fa-up-down-left-right::before { + content: "\f0b2"; } + +.fa-arrows-alt::before { + content: "\f0b2"; } + +.fa-house-chimney-user::before { + content: "\e065"; } + +.fa-hand-holding-heart::before { + content: "\f4be"; } + +.fa-puzzle-piece::before { + content: "\f12e"; } + +.fa-money-check::before { + content: "\f53c"; } + +.fa-star-half-stroke::before { + content: "\f5c0"; } + +.fa-star-half-alt::before { + content: "\f5c0"; } + +.fa-code::before { + content: "\f121"; } + +.fa-whiskey-glass::before { + content: "\f7a0"; } + +.fa-glass-whiskey::before { + content: "\f7a0"; } + +.fa-building-circle-exclamation::before { + content: "\e4d3"; } + +.fa-magnifying-glass-chart::before { + content: "\e522"; } + +.fa-arrow-up-right-from-square::before { + content: "\f08e"; } + +.fa-external-link::before { + content: "\f08e"; } + +.fa-cubes-stacked::before { + content: "\e4e6"; } + +.fa-won-sign::before { + content: "\f159"; } + +.fa-krw::before { + content: "\f159"; } + +.fa-won::before { + content: "\f159"; } + +.fa-virus-covid::before { + content: "\e4a8"; } + +.fa-austral-sign::before { + content: "\e0a9"; } + +.fa-f::before { + content: "\46"; } + +.fa-leaf::before { + content: "\f06c"; } + +.fa-road::before { + content: "\f018"; } + +.fa-taxi::before { + content: "\f1ba"; } + +.fa-cab::before { + content: "\f1ba"; } + +.fa-person-circle-plus::before { + content: "\e541"; } + +.fa-chart-pie::before { + content: "\f200"; } + +.fa-pie-chart::before { + content: "\f200"; } + +.fa-bolt-lightning::before { + content: "\e0b7"; } + +.fa-sack-xmark::before { + content: "\e56a"; } + +.fa-file-excel::before { + content: "\f1c3"; } + +.fa-file-contract::before { + content: "\f56c"; } + +.fa-fish-fins::before { + content: "\e4f2"; } + +.fa-building-flag::before { + content: "\e4d5"; } + +.fa-face-grin-beam::before { + content: "\f582"; } + +.fa-grin-beam::before { + content: "\f582"; } + +.fa-object-ungroup::before { + content: "\f248"; } + +.fa-poop::before { + content: "\f619"; } + +.fa-location-pin::before { + content: "\f041"; } + +.fa-map-marker::before { + content: "\f041"; } + +.fa-kaaba::before { + content: "\f66b"; } + +.fa-toilet-paper::before { + content: "\f71e"; } + +.fa-helmet-safety::before { + content: "\f807"; } + +.fa-hard-hat::before { + content: "\f807"; } + +.fa-hat-hard::before { + content: "\f807"; } + +.fa-eject::before { + content: "\f052"; } + +.fa-circle-right::before { + content: "\f35a"; } + +.fa-arrow-alt-circle-right::before { + content: "\f35a"; } + +.fa-plane-circle-check::before { + content: "\e555"; } + +.fa-face-rolling-eyes::before { + content: "\f5a5"; } + +.fa-meh-rolling-eyes::before { + content: "\f5a5"; } + +.fa-object-group::before { + content: "\f247"; } + +.fa-chart-line::before { + content: "\f201"; } + +.fa-line-chart::before { + content: "\f201"; } + +.fa-mask-ventilator::before { + content: "\e524"; } + +.fa-arrow-right::before { + content: "\f061"; } + +.fa-signs-post::before { + content: "\f277"; } + +.fa-map-signs::before { + content: "\f277"; } + +.fa-cash-register::before { + content: "\f788"; } + +.fa-person-circle-question::before { + content: "\e542"; } + +.fa-h::before { + content: "\48"; } + +.fa-tarp::before { + content: "\e57b"; } + +.fa-screwdriver-wrench::before { + content: "\f7d9"; } + +.fa-tools::before { + content: "\f7d9"; } + +.fa-arrows-to-eye::before { + content: "\e4bf"; } + +.fa-plug-circle-bolt::before { + content: "\e55b"; } + +.fa-heart::before { + content: "\f004"; } + +.fa-mars-and-venus::before { + content: "\f224"; } + +.fa-house-user::before { + content: "\e1b0"; } + +.fa-home-user::before { + content: "\e1b0"; } + +.fa-dumpster-fire::before { + content: "\f794"; } + +.fa-house-crack::before { + content: "\e3b1"; } + +.fa-martini-glass-citrus::before { + content: "\f561"; } + +.fa-cocktail::before { + content: "\f561"; } + +.fa-face-surprise::before { + content: "\f5c2"; } + +.fa-surprise::before { + content: "\f5c2"; } + +.fa-bottle-water::before { + content: "\e4c5"; } + +.fa-circle-pause::before { + content: "\f28b"; } + +.fa-pause-circle::before { + content: "\f28b"; } + +.fa-toilet-paper-slash::before { + content: "\e072"; } + +.fa-apple-whole::before { + content: "\f5d1"; } + +.fa-apple-alt::before { + content: "\f5d1"; } + +.fa-kitchen-set::before { + content: "\e51a"; } + +.fa-r::before { + content: "\52"; } + +.fa-temperature-quarter::before { + content: "\f2ca"; } + +.fa-temperature-1::before { + content: "\f2ca"; } + +.fa-thermometer-1::before { + content: "\f2ca"; } + +.fa-thermometer-quarter::before { + content: "\f2ca"; } + +.fa-cube::before { + content: "\f1b2"; } + +.fa-bitcoin-sign::before { + content: "\e0b4"; } + +.fa-shield-dog::before { + content: "\e573"; } + +.fa-solar-panel::before { + content: "\f5ba"; } + +.fa-lock-open::before { + content: "\f3c1"; } + +.fa-elevator::before { + content: "\e16d"; } + +.fa-money-bill-transfer::before { + content: "\e528"; } + +.fa-money-bill-trend-up::before { + content: "\e529"; } + +.fa-house-flood-water-circle-arrow-right::before { + content: "\e50f"; } + +.fa-square-poll-horizontal::before { + content: "\f682"; } + +.fa-poll-h::before { + content: "\f682"; } + +.fa-circle::before { + content: "\f111"; } + +.fa-backward-fast::before { + content: "\f049"; } + +.fa-fast-backward::before { + content: "\f049"; } + +.fa-recycle::before { + content: "\f1b8"; } + +.fa-user-astronaut::before { + content: "\f4fb"; } + +.fa-plane-slash::before { + content: "\e069"; } + +.fa-trademark::before { + content: "\f25c"; } + +.fa-basketball::before { + content: "\f434"; } + +.fa-basketball-ball::before { + content: "\f434"; } + +.fa-satellite-dish::before { + content: "\f7c0"; } + +.fa-circle-up::before { + content: "\f35b"; } + +.fa-arrow-alt-circle-up::before { + content: "\f35b"; } + +.fa-mobile-screen-button::before { + content: "\f3cd"; } + +.fa-mobile-alt::before { + content: "\f3cd"; } + +.fa-volume-high::before { + content: "\f028"; } + +.fa-volume-up::before { + content: "\f028"; } + +.fa-users-rays::before { + content: "\e593"; } + +.fa-wallet::before { + content: "\f555"; } + +.fa-clipboard-check::before { + content: "\f46c"; } + +.fa-file-audio::before { + content: "\f1c7"; } + +.fa-burger::before { + content: "\f805"; } + +.fa-hamburger::before { + content: "\f805"; } + +.fa-wrench::before { + content: "\f0ad"; } + +.fa-bugs::before { + content: "\e4d0"; } + +.fa-rupee-sign::before { + content: "\f156"; } + +.fa-rupee::before { + content: "\f156"; } + +.fa-file-image::before { + content: "\f1c5"; } + +.fa-circle-question::before { + content: "\f059"; } + +.fa-question-circle::before { + content: "\f059"; } + +.fa-plane-departure::before { + content: "\f5b0"; } + +.fa-handshake-slash::before { + content: "\e060"; } + +.fa-book-bookmark::before { + content: "\e0bb"; } + +.fa-code-branch::before { + content: "\f126"; } + +.fa-hat-cowboy::before { + content: "\f8c0"; } + +.fa-bridge::before { + content: "\e4c8"; } + +.fa-phone-flip::before { + content: "\f879"; } + +.fa-phone-alt::before { + content: "\f879"; } + +.fa-truck-front::before { + content: "\e2b7"; } + +.fa-cat::before { + content: "\f6be"; } + +.fa-anchor-circle-exclamation::before { + content: "\e4ab"; } + +.fa-truck-field::before { + content: "\e58d"; } + +.fa-route::before { + content: "\f4d7"; } + +.fa-clipboard-question::before { + content: "\e4e3"; } + +.fa-panorama::before { + content: "\e209"; } + +.fa-comment-medical::before { + content: "\f7f5"; } + +.fa-teeth-open::before { + content: "\f62f"; } + +.fa-file-circle-minus::before { + content: "\e4ed"; } + +.fa-tags::before { + content: "\f02c"; } + +.fa-wine-glass::before { + content: "\f4e3"; } + +.fa-forward-fast::before { + content: "\f050"; } + +.fa-fast-forward::before { + content: "\f050"; } + +.fa-face-meh-blank::before { + content: "\f5a4"; } + +.fa-meh-blank::before { + content: "\f5a4"; } + +.fa-square-parking::before { + content: "\f540"; } + +.fa-parking::before { + content: "\f540"; } + +.fa-house-signal::before { + content: "\e012"; } + +.fa-bars-progress::before { + content: "\f828"; } + +.fa-tasks-alt::before { + content: "\f828"; } + +.fa-faucet-drip::before { + content: "\e006"; } + +.fa-cart-flatbed::before { + content: "\f474"; } + +.fa-dolly-flatbed::before { + content: "\f474"; } + +.fa-ban-smoking::before { + content: "\f54d"; } + +.fa-smoking-ban::before { + content: "\f54d"; } + +.fa-terminal::before { + content: "\f120"; } + +.fa-mobile-button::before { + content: "\f10b"; } + +.fa-house-medical-flag::before { + content: "\e514"; } + +.fa-basket-shopping::before { + content: "\f291"; } + +.fa-shopping-basket::before { + content: "\f291"; } + +.fa-tape::before { + content: "\f4db"; } + +.fa-bus-simple::before { + content: "\f55e"; } + +.fa-bus-alt::before { + content: "\f55e"; } + +.fa-eye::before { + content: "\f06e"; } + +.fa-face-sad-cry::before { + content: "\f5b3"; } + +.fa-sad-cry::before { + content: "\f5b3"; } + +.fa-audio-description::before { + content: "\f29e"; } + +.fa-person-military-to-person::before { + content: "\e54c"; } + +.fa-file-shield::before { + content: "\e4f0"; } + +.fa-user-slash::before { + content: "\f506"; } + +.fa-pen::before { + content: "\f304"; } + +.fa-tower-observation::before { + content: "\e586"; } + +.fa-file-code::before { + content: "\f1c9"; } + +.fa-signal::before { + content: "\f012"; } + +.fa-signal-5::before { + content: "\f012"; } + +.fa-signal-perfect::before { + content: "\f012"; } + +.fa-bus::before { + content: "\f207"; } + +.fa-heart-circle-xmark::before { + content: "\e501"; } + +.fa-house-chimney::before { + content: "\e3af"; } + +.fa-home-lg::before { + content: "\e3af"; } + +.fa-window-maximize::before { + content: "\f2d0"; } + +.fa-face-frown::before { + content: "\f119"; } + +.fa-frown::before { + content: "\f119"; } + +.fa-prescription::before { + content: "\f5b1"; } + +.fa-shop::before { + content: "\f54f"; } + +.fa-store-alt::before { + content: "\f54f"; } + +.fa-floppy-disk::before { + content: "\f0c7"; } + +.fa-save::before { + content: "\f0c7"; } + +.fa-vihara::before { + content: "\f6a7"; } + +.fa-scale-unbalanced::before { + content: "\f515"; } + +.fa-balance-scale-left::before { + content: "\f515"; } + +.fa-sort-up::before { + content: "\f0de"; } + +.fa-sort-asc::before { + content: "\f0de"; } + +.fa-comment-dots::before { + content: "\f4ad"; } + +.fa-commenting::before { + content: "\f4ad"; } + +.fa-plant-wilt::before { + content: "\e5aa"; } + +.fa-diamond::before { + content: "\f219"; } + +.fa-face-grin-squint::before { + content: "\f585"; } + +.fa-grin-squint::before { + content: "\f585"; } + +.fa-hand-holding-dollar::before { + content: "\f4c0"; } + +.fa-hand-holding-usd::before { + content: "\f4c0"; } + +.fa-bacterium::before { + content: "\e05a"; } + +.fa-hand-pointer::before { + content: "\f25a"; } + +.fa-drum-steelpan::before { + content: "\f56a"; } + +.fa-hand-scissors::before { + content: "\f257"; } + +.fa-hands-praying::before { + content: "\f684"; } + +.fa-praying-hands::before { + content: "\f684"; } + +.fa-arrow-rotate-right::before { + content: "\f01e"; } + +.fa-arrow-right-rotate::before { + content: "\f01e"; } + +.fa-arrow-rotate-forward::before { + content: "\f01e"; } + +.fa-redo::before { + content: "\f01e"; } + +.fa-biohazard::before { + content: "\f780"; } + +.fa-location-crosshairs::before { + content: "\f601"; } + +.fa-location::before { + content: "\f601"; } + +.fa-mars-double::before { + content: "\f227"; } + +.fa-child-dress::before { + content: "\e59c"; } + +.fa-users-between-lines::before { + content: "\e591"; } + +.fa-lungs-virus::before { + content: "\e067"; } + +.fa-face-grin-tears::before { + content: "\f588"; } + +.fa-grin-tears::before { + content: "\f588"; } + +.fa-phone::before { + content: "\f095"; } + +.fa-calendar-xmark::before { + content: "\f273"; } + +.fa-calendar-times::before { + content: "\f273"; } + +.fa-child-reaching::before { + content: "\e59d"; } + +.fa-head-side-virus::before { + content: "\e064"; } + +.fa-user-gear::before { + content: "\f4fe"; } + +.fa-user-cog::before { + content: "\f4fe"; } + +.fa-arrow-up-1-9::before { + content: "\f163"; } + +.fa-sort-numeric-up::before { + content: "\f163"; } + +.fa-door-closed::before { + content: "\f52a"; } + +.fa-shield-virus::before { + content: "\e06c"; } + +.fa-dice-six::before { + content: "\f526"; } + +.fa-mosquito-net::before { + content: "\e52c"; } + +.fa-bridge-water::before { + content: "\e4ce"; } + +.fa-person-booth::before { + content: "\f756"; } + +.fa-text-width::before { + content: "\f035"; } + +.fa-hat-wizard::before { + content: "\f6e8"; } + +.fa-pen-fancy::before { + content: "\f5ac"; } + +.fa-person-digging::before { + content: "\f85e"; } + +.fa-digging::before { + content: "\f85e"; } + +.fa-trash::before { + content: "\f1f8"; } + +.fa-gauge-simple::before { + content: "\f629"; } + +.fa-gauge-simple-med::before { + content: "\f629"; } + +.fa-tachometer-average::before { + content: "\f629"; } + +.fa-book-medical::before { + content: "\f7e6"; } + +.fa-poo::before { + content: "\f2fe"; } + +.fa-quote-right::before { + content: "\f10e"; } + +.fa-quote-right-alt::before { + content: "\f10e"; } + +.fa-shirt::before { + content: "\f553"; } + +.fa-t-shirt::before { + content: "\f553"; } + +.fa-tshirt::before { + content: "\f553"; } + +.fa-cubes::before { + content: "\f1b3"; } + +.fa-divide::before { + content: "\f529"; } + +.fa-tenge-sign::before { + content: "\f7d7"; } + +.fa-tenge::before { + content: "\f7d7"; } + +.fa-headphones::before { + content: "\f025"; } + +.fa-hands-holding::before { + content: "\f4c2"; } + +.fa-hands-clapping::before { + content: "\e1a8"; } + +.fa-republican::before { + content: "\f75e"; } + +.fa-arrow-left::before { + content: "\f060"; } + +.fa-person-circle-xmark::before { + content: "\e543"; } + +.fa-ruler::before { + content: "\f545"; } + +.fa-align-left::before { + content: "\f036"; } + +.fa-dice-d6::before { + content: "\f6d1"; } + +.fa-restroom::before { + content: "\f7bd"; } + +.fa-j::before { + content: "\4a"; } + +.fa-users-viewfinder::before { + content: "\e595"; } + +.fa-file-video::before { + content: "\f1c8"; } + +.fa-up-right-from-square::before { + content: "\f35d"; } + +.fa-external-link-alt::before { + content: "\f35d"; } + +.fa-table-cells::before { + content: "\f00a"; } + +.fa-th::before { + content: "\f00a"; } + +.fa-file-pdf::before { + content: "\f1c1"; } + +.fa-book-bible::before { + content: "\f647"; } + +.fa-bible::before { + content: "\f647"; } + +.fa-o::before { + content: "\4f"; } + +.fa-suitcase-medical::before { + content: "\f0fa"; } + +.fa-medkit::before { + content: "\f0fa"; } + +.fa-user-secret::before { + content: "\f21b"; } + +.fa-otter::before { + content: "\f700"; } + +.fa-person-dress::before { + content: "\f182"; } + +.fa-female::before { + content: "\f182"; } + +.fa-comment-dollar::before { + content: "\f651"; } + +.fa-business-time::before { + content: "\f64a"; } + +.fa-briefcase-clock::before { + content: "\f64a"; } + +.fa-table-cells-large::before { + content: "\f009"; } + +.fa-th-large::before { + content: "\f009"; } + +.fa-book-tanakh::before { + content: "\f827"; } + +.fa-tanakh::before { + content: "\f827"; } + +.fa-phone-volume::before { + content: "\f2a0"; } + +.fa-volume-control-phone::before { + content: "\f2a0"; } + +.fa-hat-cowboy-side::before { + content: "\f8c1"; } + +.fa-clipboard-user::before { + content: "\f7f3"; } + +.fa-child::before { + content: "\f1ae"; } + +.fa-lira-sign::before { + content: "\f195"; } + +.fa-satellite::before { + content: "\f7bf"; } + +.fa-plane-lock::before { + content: "\e558"; } + +.fa-tag::before { + content: "\f02b"; } + +.fa-comment::before { + content: "\f075"; } + +.fa-cake-candles::before { + content: "\f1fd"; } + +.fa-birthday-cake::before { + content: "\f1fd"; } + +.fa-cake::before { + content: "\f1fd"; } + +.fa-envelope::before { + content: "\f0e0"; } + +.fa-angles-up::before { + content: "\f102"; } + +.fa-angle-double-up::before { + content: "\f102"; } + +.fa-paperclip::before { + content: "\f0c6"; } + +.fa-arrow-right-to-city::before { + content: "\e4b3"; } + +.fa-ribbon::before { + content: "\f4d6"; } + +.fa-lungs::before { + content: "\f604"; } + +.fa-arrow-up-9-1::before { + content: "\f887"; } + +.fa-sort-numeric-up-alt::before { + content: "\f887"; } + +.fa-litecoin-sign::before { + content: "\e1d3"; } + +.fa-border-none::before { + content: "\f850"; } + +.fa-circle-nodes::before { + content: "\e4e2"; } + +.fa-parachute-box::before { + content: "\f4cd"; } + +.fa-indent::before { + content: "\f03c"; } + +.fa-truck-field-un::before { + content: "\e58e"; } + +.fa-hourglass::before { + content: "\f254"; } + +.fa-hourglass-empty::before { + content: "\f254"; } + +.fa-mountain::before { + content: "\f6fc"; } + +.fa-user-doctor::before { + content: "\f0f0"; } + +.fa-user-md::before { + content: "\f0f0"; } + +.fa-circle-info::before { + content: "\f05a"; } + +.fa-info-circle::before { + content: "\f05a"; } + +.fa-cloud-meatball::before { + content: "\f73b"; } + +.fa-camera::before { + content: "\f030"; } + +.fa-camera-alt::before { + content: "\f030"; } + +.fa-square-virus::before { + content: "\e578"; } + +.fa-meteor::before { + content: "\f753"; } + +.fa-car-on::before { + content: "\e4dd"; } + +.fa-sleigh::before { + content: "\f7cc"; } + +.fa-arrow-down-1-9::before { + content: "\f162"; } + +.fa-sort-numeric-asc::before { + content: "\f162"; } + +.fa-sort-numeric-down::before { + content: "\f162"; } + +.fa-hand-holding-droplet::before { + content: "\f4c1"; } + +.fa-hand-holding-water::before { + content: "\f4c1"; } + +.fa-water::before { + content: "\f773"; } + +.fa-calendar-check::before { + content: "\f274"; } + +.fa-braille::before { + content: "\f2a1"; } + +.fa-prescription-bottle-medical::before { + content: "\f486"; } + +.fa-prescription-bottle-alt::before { + content: "\f486"; } + +.fa-landmark::before { + content: "\f66f"; } + +.fa-truck::before { + content: "\f0d1"; } + +.fa-crosshairs::before { + content: "\f05b"; } + +.fa-person-cane::before { + content: "\e53c"; } + +.fa-tent::before { + content: "\e57d"; } + +.fa-vest-patches::before { + content: "\e086"; } + +.fa-check-double::before { + content: "\f560"; } + +.fa-arrow-down-a-z::before { + content: "\f15d"; } + +.fa-sort-alpha-asc::before { + content: "\f15d"; } + +.fa-sort-alpha-down::before { + content: "\f15d"; } + +.fa-money-bill-wheat::before { + content: "\e52a"; } + +.fa-cookie::before { + content: "\f563"; } + +.fa-arrow-rotate-left::before { + content: "\f0e2"; } + +.fa-arrow-left-rotate::before { + content: "\f0e2"; } + +.fa-arrow-rotate-back::before { + content: "\f0e2"; } + +.fa-arrow-rotate-backward::before { + content: "\f0e2"; } + +.fa-undo::before { + content: "\f0e2"; } + +.fa-hard-drive::before { + content: "\f0a0"; } + +.fa-hdd::before { + content: "\f0a0"; } + +.fa-face-grin-squint-tears::before { + content: "\f586"; } + +.fa-grin-squint-tears::before { + content: "\f586"; } + +.fa-dumbbell::before { + content: "\f44b"; } + +.fa-rectangle-list::before { + content: "\f022"; } + +.fa-list-alt::before { + content: "\f022"; } + +.fa-tarp-droplet::before { + content: "\e57c"; } + +.fa-house-medical-circle-check::before { + content: "\e511"; } + +.fa-person-skiing-nordic::before { + content: "\f7ca"; } + +.fa-skiing-nordic::before { + content: "\f7ca"; } + +.fa-calendar-plus::before { + content: "\f271"; } + +.fa-plane-arrival::before { + content: "\f5af"; } + +.fa-circle-left::before { + content: "\f359"; } + +.fa-arrow-alt-circle-left::before { + content: "\f359"; } + +.fa-train-subway::before { + content: "\f239"; } + +.fa-subway::before { + content: "\f239"; } + +.fa-chart-gantt::before { + content: "\e0e4"; } + +.fa-indian-rupee-sign::before { + content: "\e1bc"; } + +.fa-indian-rupee::before { + content: "\e1bc"; } + +.fa-inr::before { + content: "\e1bc"; } + +.fa-crop-simple::before { + content: "\f565"; } + +.fa-crop-alt::before { + content: "\f565"; } + +.fa-money-bill-1::before { + content: "\f3d1"; } + +.fa-money-bill-alt::before { + content: "\f3d1"; } + +.fa-left-long::before { + content: "\f30a"; } + +.fa-long-arrow-alt-left::before { + content: "\f30a"; } + +.fa-dna::before { + content: "\f471"; } + +.fa-virus-slash::before { + content: "\e075"; } + +.fa-minus::before { + content: "\f068"; } + +.fa-subtract::before { + content: "\f068"; } + +.fa-chess::before { + content: "\f439"; } + +.fa-arrow-left-long::before { + content: "\f177"; } + +.fa-long-arrow-left::before { + content: "\f177"; } + +.fa-plug-circle-check::before { + content: "\e55c"; } + +.fa-street-view::before { + content: "\f21d"; } + +.fa-franc-sign::before { + content: "\e18f"; } + +.fa-volume-off::before { + content: "\f026"; } + +.fa-hands-asl-interpreting::before { + content: "\f2a3"; } + +.fa-american-sign-language-interpreting::before { + content: "\f2a3"; } + +.fa-asl-interpreting::before { + content: "\f2a3"; } + +.fa-hands-american-sign-language-interpreting::before { + content: "\f2a3"; } + +.fa-gear::before { + content: "\f013"; } + +.fa-cog::before { + content: "\f013"; } + +.fa-droplet-slash::before { + content: "\f5c7"; } + +.fa-tint-slash::before { + content: "\f5c7"; } + +.fa-mosque::before { + content: "\f678"; } + +.fa-mosquito::before { + content: "\e52b"; } + +.fa-star-of-david::before { + content: "\f69a"; } + +.fa-person-military-rifle::before { + content: "\e54b"; } + +.fa-cart-shopping::before { + content: "\f07a"; } + +.fa-shopping-cart::before { + content: "\f07a"; } + +.fa-vials::before { + content: "\f493"; } + +.fa-plug-circle-plus::before { + content: "\e55f"; } + +.fa-place-of-worship::before { + content: "\f67f"; } + +.fa-grip-vertical::before { + content: "\f58e"; } + +.fa-arrow-turn-up::before { + content: "\f148"; } + +.fa-level-up::before { + content: "\f148"; } + +.fa-u::before { + content: "\55"; } + +.fa-square-root-variable::before { + content: "\f698"; } + +.fa-square-root-alt::before { + content: "\f698"; } + +.fa-clock::before { + content: "\f017"; } + +.fa-clock-four::before { + content: "\f017"; } + +.fa-backward-step::before { + content: "\f048"; } + +.fa-step-backward::before { + content: "\f048"; } + +.fa-pallet::before { + content: "\f482"; } + +.fa-faucet::before { + content: "\e005"; } + +.fa-baseball-bat-ball::before { + content: "\f432"; } + +.fa-s::before { + content: "\53"; } + +.fa-timeline::before { + content: "\e29c"; } + +.fa-keyboard::before { + content: "\f11c"; } + +.fa-caret-down::before { + content: "\f0d7"; } + +.fa-house-chimney-medical::before { + content: "\f7f2"; } + +.fa-clinic-medical::before { + content: "\f7f2"; } + +.fa-temperature-three-quarters::before { + content: "\f2c8"; } + +.fa-temperature-3::before { + content: "\f2c8"; } + +.fa-thermometer-3::before { + content: "\f2c8"; } + +.fa-thermometer-three-quarters::before { + content: "\f2c8"; } + +.fa-mobile-screen::before { + content: "\f3cf"; } + +.fa-mobile-android-alt::before { + content: "\f3cf"; } + +.fa-plane-up::before { + content: "\e22d"; } + +.fa-piggy-bank::before { + content: "\f4d3"; } + +.fa-battery-half::before { + content: "\f242"; } + +.fa-battery-3::before { + content: "\f242"; } + +.fa-mountain-city::before { + content: "\e52e"; } + +.fa-coins::before { + content: "\f51e"; } + +.fa-khanda::before { + content: "\f66d"; } + +.fa-sliders::before { + content: "\f1de"; } + +.fa-sliders-h::before { + content: "\f1de"; } + +.fa-folder-tree::before { + content: "\f802"; } + +.fa-network-wired::before { + content: "\f6ff"; } + +.fa-map-pin::before { + content: "\f276"; } + +.fa-hamsa::before { + content: "\f665"; } + +.fa-cent-sign::before { + content: "\e3f5"; } + +.fa-flask::before { + content: "\f0c3"; } + +.fa-person-pregnant::before { + content: "\e31e"; } + +.fa-wand-sparkles::before { + content: "\f72b"; } + +.fa-ellipsis-vertical::before { + content: "\f142"; } + +.fa-ellipsis-v::before { + content: "\f142"; } + +.fa-ticket::before { + content: "\f145"; } + +.fa-power-off::before { + content: "\f011"; } + +.fa-right-long::before { + content: "\f30b"; } + +.fa-long-arrow-alt-right::before { + content: "\f30b"; } + +.fa-flag-usa::before { + content: "\f74d"; } + +.fa-laptop-file::before { + content: "\e51d"; } + +.fa-tty::before { + content: "\f1e4"; } + +.fa-teletype::before { + content: "\f1e4"; } + +.fa-diagram-next::before { + content: "\e476"; } + +.fa-person-rifle::before { + content: "\e54e"; } + +.fa-house-medical-circle-exclamation::before { + content: "\e512"; } + +.fa-closed-captioning::before { + content: "\f20a"; } + +.fa-person-hiking::before { + content: "\f6ec"; } + +.fa-hiking::before { + content: "\f6ec"; } + +.fa-venus-double::before { + content: "\f226"; } + +.fa-images::before { + content: "\f302"; } + +.fa-calculator::before { + content: "\f1ec"; } + +.fa-people-pulling::before { + content: "\e535"; } + +.fa-n::before { + content: "\4e"; } + +.fa-cable-car::before { + content: "\f7da"; } + +.fa-tram::before { + content: "\f7da"; } + +.fa-cloud-rain::before { + content: "\f73d"; } + +.fa-building-circle-xmark::before { + content: "\e4d4"; } + +.fa-ship::before { + content: "\f21a"; } + +.fa-arrows-down-to-line::before { + content: "\e4b8"; } + +.fa-download::before { + content: "\f019"; } + +.fa-face-grin::before { + content: "\f580"; } + +.fa-grin::before { + content: "\f580"; } + +.fa-delete-left::before { + content: "\f55a"; } + +.fa-backspace::before { + content: "\f55a"; } + +.fa-eye-dropper::before { + content: "\f1fb"; } + +.fa-eye-dropper-empty::before { + content: "\f1fb"; } + +.fa-eyedropper::before { + content: "\f1fb"; } + +.fa-file-circle-check::before { + content: "\e5a0"; } + +.fa-forward::before { + content: "\f04e"; } + +.fa-mobile::before { + content: "\f3ce"; } + +.fa-mobile-android::before { + content: "\f3ce"; } + +.fa-mobile-phone::before { + content: "\f3ce"; } + +.fa-face-meh::before { + content: "\f11a"; } + +.fa-meh::before { + content: "\f11a"; } + +.fa-align-center::before { + content: "\f037"; } + +.fa-book-skull::before { + content: "\f6b7"; } + +.fa-book-dead::before { + content: "\f6b7"; } + +.fa-id-card::before { + content: "\f2c2"; } + +.fa-drivers-license::before { + content: "\f2c2"; } + +.fa-outdent::before { + content: "\f03b"; } + +.fa-dedent::before { + content: "\f03b"; } + +.fa-heart-circle-exclamation::before { + content: "\e4fe"; } + +.fa-house::before { + content: "\f015"; } + +.fa-home::before { + content: "\f015"; } + +.fa-home-alt::before { + content: "\f015"; } + +.fa-home-lg-alt::before { + content: "\f015"; } + +.fa-calendar-week::before { + content: "\f784"; } + +.fa-laptop-medical::before { + content: "\f812"; } + +.fa-b::before { + content: "\42"; } + +.fa-file-medical::before { + content: "\f477"; } + +.fa-dice-one::before { + content: "\f525"; } + +.fa-kiwi-bird::before { + content: "\f535"; } + +.fa-arrow-right-arrow-left::before { + content: "\f0ec"; } + +.fa-exchange::before { + content: "\f0ec"; } + +.fa-rotate-right::before { + content: "\f2f9"; } + +.fa-redo-alt::before { + content: "\f2f9"; } + +.fa-rotate-forward::before { + content: "\f2f9"; } + +.fa-utensils::before { + content: "\f2e7"; } + +.fa-cutlery::before { + content: "\f2e7"; } + +.fa-arrow-up-wide-short::before { + content: "\f161"; } + +.fa-sort-amount-up::before { + content: "\f161"; } + +.fa-mill-sign::before { + content: "\e1ed"; } + +.fa-bowl-rice::before { + content: "\e2eb"; } + +.fa-skull::before { + content: "\f54c"; } + +.fa-tower-broadcast::before { + content: "\f519"; } + +.fa-broadcast-tower::before { + content: "\f519"; } + +.fa-truck-pickup::before { + content: "\f63c"; } + +.fa-up-long::before { + content: "\f30c"; } + +.fa-long-arrow-alt-up::before { + content: "\f30c"; } + +.fa-stop::before { + content: "\f04d"; } + +.fa-code-merge::before { + content: "\f387"; } + +.fa-upload::before { + content: "\f093"; } + +.fa-hurricane::before { + content: "\f751"; } + +.fa-mound::before { + content: "\e52d"; } + +.fa-toilet-portable::before { + content: "\e583"; } + +.fa-compact-disc::before { + content: "\f51f"; } + +.fa-file-arrow-down::before { + content: "\f56d"; } + +.fa-file-download::before { + content: "\f56d"; } + +.fa-caravan::before { + content: "\f8ff"; } + +.fa-shield-cat::before { + content: "\e572"; } + +.fa-bolt::before { + content: "\f0e7"; } + +.fa-zap::before { + content: "\f0e7"; } + +.fa-glass-water::before { + content: "\e4f4"; } + +.fa-oil-well::before { + content: "\e532"; } + +.fa-vault::before { + content: "\e2c5"; } + +.fa-mars::before { + content: "\f222"; } + +.fa-toilet::before { + content: "\f7d8"; } + +.fa-plane-circle-xmark::before { + content: "\e557"; } + +.fa-yen-sign::before { + content: "\f157"; } + +.fa-cny::before { + content: "\f157"; } + +.fa-jpy::before { + content: "\f157"; } + +.fa-rmb::before { + content: "\f157"; } + +.fa-yen::before { + content: "\f157"; } + +.fa-ruble-sign::before { + content: "\f158"; } + +.fa-rouble::before { + content: "\f158"; } + +.fa-rub::before { + content: "\f158"; } + +.fa-ruble::before { + content: "\f158"; } + +.fa-sun::before { + content: "\f185"; } + +.fa-guitar::before { + content: "\f7a6"; } + +.fa-face-laugh-wink::before { + content: "\f59c"; } + +.fa-laugh-wink::before { + content: "\f59c"; } + +.fa-horse-head::before { + content: "\f7ab"; } + +.fa-bore-hole::before { + content: "\e4c3"; } + +.fa-industry::before { + content: "\f275"; } + +.fa-circle-down::before { + content: "\f358"; } + +.fa-arrow-alt-circle-down::before { + content: "\f358"; } + +.fa-arrows-turn-to-dots::before { + content: "\e4c1"; } + +.fa-florin-sign::before { + content: "\e184"; } + +.fa-arrow-down-short-wide::before { + content: "\f884"; } + +.fa-sort-amount-desc::before { + content: "\f884"; } + +.fa-sort-amount-down-alt::before { + content: "\f884"; } + +.fa-less-than::before { + content: "\3c"; } + +.fa-angle-down::before { + content: "\f107"; } + +.fa-car-tunnel::before { + content: "\e4de"; } + +.fa-head-side-cough::before { + content: "\e061"; } + +.fa-grip-lines::before { + content: "\f7a4"; } + +.fa-thumbs-down::before { + content: "\f165"; } + +.fa-user-lock::before { + content: "\f502"; } + +.fa-arrow-right-long::before { + content: "\f178"; } + +.fa-long-arrow-right::before { + content: "\f178"; } + +.fa-anchor-circle-xmark::before { + content: "\e4ac"; } + +.fa-ellipsis::before { + content: "\f141"; } + +.fa-ellipsis-h::before { + content: "\f141"; } + +.fa-chess-pawn::before { + content: "\f443"; } + +.fa-kit-medical::before { + content: "\f479"; } + +.fa-first-aid::before { + content: "\f479"; } + +.fa-person-through-window::before { + content: "\e5a9"; } + +.fa-toolbox::before { + content: "\f552"; } + +.fa-hands-holding-circle::before { + content: "\e4fb"; } + +.fa-bug::before { + content: "\f188"; } + +.fa-credit-card::before { + content: "\f09d"; } + +.fa-credit-card-alt::before { + content: "\f09d"; } + +.fa-car::before { + content: "\f1b9"; } + +.fa-automobile::before { + content: "\f1b9"; } + +.fa-hand-holding-hand::before { + content: "\e4f7"; } + +.fa-book-open-reader::before { + content: "\f5da"; } + +.fa-book-reader::before { + content: "\f5da"; } + +.fa-mountain-sun::before { + content: "\e52f"; } + +.fa-arrows-left-right-to-line::before { + content: "\e4ba"; } + +.fa-dice-d20::before { + content: "\f6cf"; } + +.fa-truck-droplet::before { + content: "\e58c"; } + +.fa-file-circle-xmark::before { + content: "\e5a1"; } + +.fa-temperature-arrow-up::before { + content: "\e040"; } + +.fa-temperature-up::before { + content: "\e040"; } + +.fa-medal::before { + content: "\f5a2"; } + +.fa-bed::before { + content: "\f236"; } + +.fa-square-h::before { + content: "\f0fd"; } + +.fa-h-square::before { + content: "\f0fd"; } + +.fa-podcast::before { + content: "\f2ce"; } + +.fa-temperature-full::before { + content: "\f2c7"; } + +.fa-temperature-4::before { + content: "\f2c7"; } + +.fa-thermometer-4::before { + content: "\f2c7"; } + +.fa-thermometer-full::before { + content: "\f2c7"; } + +.fa-bell::before { + content: "\f0f3"; } + +.fa-superscript::before { + content: "\f12b"; } + +.fa-plug-circle-xmark::before { + content: "\e560"; } + +.fa-star-of-life::before { + content: "\f621"; } + +.fa-phone-slash::before { + content: "\f3dd"; } + +.fa-paint-roller::before { + content: "\f5aa"; } + +.fa-handshake-angle::before { + content: "\f4c4"; } + +.fa-hands-helping::before { + content: "\f4c4"; } + +.fa-location-dot::before { + content: "\f3c5"; } + +.fa-map-marker-alt::before { + content: "\f3c5"; } + +.fa-file::before { + content: "\f15b"; } + +.fa-greater-than::before { + content: "\3e"; } + +.fa-person-swimming::before { + content: "\f5c4"; } + +.fa-swimmer::before { + content: "\f5c4"; } + +.fa-arrow-down::before { + content: "\f063"; } + +.fa-droplet::before { + content: "\f043"; } + +.fa-tint::before { + content: "\f043"; } + +.fa-eraser::before { + content: "\f12d"; } + +.fa-earth-americas::before { + content: "\f57d"; } + +.fa-earth::before { + content: "\f57d"; } + +.fa-earth-america::before { + content: "\f57d"; } + +.fa-globe-americas::before { + content: "\f57d"; } + +.fa-person-burst::before { + content: "\e53b"; } + +.fa-dove::before { + content: "\f4ba"; } + +.fa-battery-empty::before { + content: "\f244"; } + +.fa-battery-0::before { + content: "\f244"; } + +.fa-socks::before { + content: "\f696"; } + +.fa-inbox::before { + content: "\f01c"; } + +.fa-section::before { + content: "\e447"; } + +.fa-gauge-high::before { + content: "\f625"; } + +.fa-tachometer-alt::before { + content: "\f625"; } + +.fa-tachometer-alt-fast::before { + content: "\f625"; } + +.fa-envelope-open-text::before { + content: "\f658"; } + +.fa-hospital::before { + content: "\f0f8"; } + +.fa-hospital-alt::before { + content: "\f0f8"; } + +.fa-hospital-wide::before { + content: "\f0f8"; } + +.fa-wine-bottle::before { + content: "\f72f"; } + +.fa-chess-rook::before { + content: "\f447"; } + +.fa-bars-staggered::before { + content: "\f550"; } + +.fa-reorder::before { + content: "\f550"; } + +.fa-stream::before { + content: "\f550"; } + +.fa-dharmachakra::before { + content: "\f655"; } + +.fa-hotdog::before { + content: "\f80f"; } + +.fa-person-walking-with-cane::before { + content: "\f29d"; } + +.fa-blind::before { + content: "\f29d"; } + +.fa-drum::before { + content: "\f569"; } + +.fa-ice-cream::before { + content: "\f810"; } + +.fa-heart-circle-bolt::before { + content: "\e4fc"; } + +.fa-fax::before { + content: "\f1ac"; } + +.fa-paragraph::before { + content: "\f1dd"; } + +.fa-check-to-slot::before { + content: "\f772"; } + +.fa-vote-yea::before { + content: "\f772"; } + +.fa-star-half::before { + content: "\f089"; } + +.fa-boxes-stacked::before { + content: "\f468"; } + +.fa-boxes::before { + content: "\f468"; } + +.fa-boxes-alt::before { + content: "\f468"; } + +.fa-link::before { + content: "\f0c1"; } + +.fa-chain::before { + content: "\f0c1"; } + +.fa-ear-listen::before { + content: "\f2a2"; } + +.fa-assistive-listening-systems::before { + content: "\f2a2"; } + +.fa-tree-city::before { + content: "\e587"; } + +.fa-play::before { + content: "\f04b"; } + +.fa-font::before { + content: "\f031"; } + +.fa-rupiah-sign::before { + content: "\e23d"; } + +.fa-magnifying-glass::before { + content: "\f002"; } + +.fa-search::before { + content: "\f002"; } + +.fa-table-tennis-paddle-ball::before { + content: "\f45d"; } + +.fa-ping-pong-paddle-ball::before { + content: "\f45d"; } + +.fa-table-tennis::before { + content: "\f45d"; } + +.fa-person-dots-from-line::before { + content: "\f470"; } + +.fa-diagnoses::before { + content: "\f470"; } + +.fa-trash-can-arrow-up::before { + content: "\f82a"; } + +.fa-trash-restore-alt::before { + content: "\f82a"; } + +.fa-naira-sign::before { + content: "\e1f6"; } + +.fa-cart-arrow-down::before { + content: "\f218"; } + +.fa-walkie-talkie::before { + content: "\f8ef"; } + +.fa-file-pen::before { + content: "\f31c"; } + +.fa-file-edit::before { + content: "\f31c"; } + +.fa-receipt::before { + content: "\f543"; } + +.fa-square-pen::before { + content: "\f14b"; } + +.fa-pen-square::before { + content: "\f14b"; } + +.fa-pencil-square::before { + content: "\f14b"; } + +.fa-suitcase-rolling::before { + content: "\f5c1"; } + +.fa-person-circle-exclamation::before { + content: "\e53f"; } + +.fa-chevron-down::before { + content: "\f078"; } + +.fa-battery-full::before { + content: "\f240"; } + +.fa-battery::before { + content: "\f240"; } + +.fa-battery-5::before { + content: "\f240"; } + +.fa-skull-crossbones::before { + content: "\f714"; } + +.fa-code-compare::before { + content: "\e13a"; } + +.fa-list-ul::before { + content: "\f0ca"; } + +.fa-list-dots::before { + content: "\f0ca"; } + +.fa-school-lock::before { + content: "\e56f"; } + +.fa-tower-cell::before { + content: "\e585"; } + +.fa-down-long::before { + content: "\f309"; } + +.fa-long-arrow-alt-down::before { + content: "\f309"; } + +.fa-ranking-star::before { + content: "\e561"; } + +.fa-chess-king::before { + content: "\f43f"; } + +.fa-person-harassing::before { + content: "\e549"; } + +.fa-brazilian-real-sign::before { + content: "\e46c"; } + +.fa-landmark-dome::before { + content: "\f752"; } + +.fa-landmark-alt::before { + content: "\f752"; } + +.fa-arrow-up::before { + content: "\f062"; } + +.fa-tv::before { + content: "\f26c"; } + +.fa-television::before { + content: "\f26c"; } + +.fa-tv-alt::before { + content: "\f26c"; } + +.fa-shrimp::before { + content: "\e448"; } + +.fa-list-check::before { + content: "\f0ae"; } + +.fa-tasks::before { + content: "\f0ae"; } + +.fa-jug-detergent::before { + content: "\e519"; } + +.fa-circle-user::before { + content: "\f2bd"; } + +.fa-user-circle::before { + content: "\f2bd"; } + +.fa-user-shield::before { + content: "\f505"; } + +.fa-wind::before { + content: "\f72e"; } + +.fa-car-burst::before { + content: "\f5e1"; } + +.fa-car-crash::before { + content: "\f5e1"; } + +.fa-y::before { + content: "\59"; } + +.fa-person-snowboarding::before { + content: "\f7ce"; } + +.fa-snowboarding::before { + content: "\f7ce"; } + +.fa-truck-fast::before { + content: "\f48b"; } + +.fa-shipping-fast::before { + content: "\f48b"; } + +.fa-fish::before { + content: "\f578"; } + +.fa-user-graduate::before { + content: "\f501"; } + +.fa-circle-half-stroke::before { + content: "\f042"; } + +.fa-adjust::before { + content: "\f042"; } + +.fa-clapperboard::before { + content: "\e131"; } + +.fa-circle-radiation::before { + content: "\f7ba"; } + +.fa-radiation-alt::before { + content: "\f7ba"; } + +.fa-baseball::before { + content: "\f433"; } + +.fa-baseball-ball::before { + content: "\f433"; } + +.fa-jet-fighter-up::before { + content: "\e518"; } + +.fa-diagram-project::before { + content: "\f542"; } + +.fa-project-diagram::before { + content: "\f542"; } + +.fa-copy::before { + content: "\f0c5"; } + +.fa-volume-xmark::before { + content: "\f6a9"; } + +.fa-volume-mute::before { + content: "\f6a9"; } + +.fa-volume-times::before { + content: "\f6a9"; } + +.fa-hand-sparkles::before { + content: "\e05d"; } + +.fa-grip::before { + content: "\f58d"; } + +.fa-grip-horizontal::before { + content: "\f58d"; } + +.fa-share-from-square::before { + content: "\f14d"; } + +.fa-share-square::before { + content: "\f14d"; } + +.fa-child-combatant::before { + content: "\e4e0"; } + +.fa-child-rifle::before { + content: "\e4e0"; } + +.fa-gun::before { + content: "\e19b"; } + +.fa-square-phone::before { + content: "\f098"; } + +.fa-phone-square::before { + content: "\f098"; } + +.fa-plus::before { + content: "\2b"; } + +.fa-add::before { + content: "\2b"; } + +.fa-expand::before { + content: "\f065"; } + +.fa-computer::before { + content: "\e4e5"; } + +.fa-xmark::before { + content: "\f00d"; } + +.fa-close::before { + content: "\f00d"; } + +.fa-multiply::before { + content: "\f00d"; } + +.fa-remove::before { + content: "\f00d"; } + +.fa-times::before { + content: "\f00d"; } + +.fa-arrows-up-down-left-right::before { + content: "\f047"; } + +.fa-arrows::before { + content: "\f047"; } + +.fa-chalkboard-user::before { + content: "\f51c"; } + +.fa-chalkboard-teacher::before { + content: "\f51c"; } + +.fa-peso-sign::before { + content: "\e222"; } + +.fa-building-shield::before { + content: "\e4d8"; } + +.fa-baby::before { + content: "\f77c"; } + +.fa-users-line::before { + content: "\e592"; } + +.fa-quote-left::before { + content: "\f10d"; } + +.fa-quote-left-alt::before { + content: "\f10d"; } + +.fa-tractor::before { + content: "\f722"; } + +.fa-trash-arrow-up::before { + content: "\f829"; } + +.fa-trash-restore::before { + content: "\f829"; } + +.fa-arrow-down-up-lock::before { + content: "\e4b0"; } + +.fa-lines-leaning::before { + content: "\e51e"; } + +.fa-ruler-combined::before { + content: "\f546"; } + +.fa-copyright::before { + content: "\f1f9"; } + +.fa-equals::before { + content: "\3d"; } + +.fa-blender::before { + content: "\f517"; } + +.fa-teeth::before { + content: "\f62e"; } + +.fa-shekel-sign::before { + content: "\f20b"; } + +.fa-ils::before { + content: "\f20b"; } + +.fa-shekel::before { + content: "\f20b"; } + +.fa-sheqel::before { + content: "\f20b"; } + +.fa-sheqel-sign::before { + content: "\f20b"; } + +.fa-map::before { + content: "\f279"; } + +.fa-rocket::before { + content: "\f135"; } + +.fa-photo-film::before { + content: "\f87c"; } + +.fa-photo-video::before { + content: "\f87c"; } + +.fa-folder-minus::before { + content: "\f65d"; } + +.fa-store::before { + content: "\f54e"; } + +.fa-arrow-trend-up::before { + content: "\e098"; } + +.fa-plug-circle-minus::before { + content: "\e55e"; } + +.fa-sign-hanging::before { + content: "\f4d9"; } + +.fa-sign::before { + content: "\f4d9"; } + +.fa-bezier-curve::before { + content: "\f55b"; } + +.fa-bell-slash::before { + content: "\f1f6"; } + +.fa-tablet::before { + content: "\f3fb"; } + +.fa-tablet-android::before { + content: "\f3fb"; } + +.fa-school-flag::before { + content: "\e56e"; } + +.fa-fill::before { + content: "\f575"; } + +.fa-angle-up::before { + content: "\f106"; } + +.fa-drumstick-bite::before { + content: "\f6d7"; } + +.fa-holly-berry::before { + content: "\f7aa"; } + +.fa-chevron-left::before { + content: "\f053"; } + +.fa-bacteria::before { + content: "\e059"; } + +.fa-hand-lizard::before { + content: "\f258"; } + +.fa-notdef::before { + content: "\e1fe"; } + +.fa-disease::before { + content: "\f7fa"; } + +.fa-briefcase-medical::before { + content: "\f469"; } + +.fa-genderless::before { + content: "\f22d"; } + +.fa-chevron-right::before { + content: "\f054"; } + +.fa-retweet::before { + content: "\f079"; } + +.fa-car-rear::before { + content: "\f5de"; } + +.fa-car-alt::before { + content: "\f5de"; } + +.fa-pump-soap::before { + content: "\e06b"; } + +.fa-video-slash::before { + content: "\f4e2"; } + +.fa-battery-quarter::before { + content: "\f243"; } + +.fa-battery-2::before { + content: "\f243"; } + +.fa-radio::before { + content: "\f8d7"; } + +.fa-baby-carriage::before { + content: "\f77d"; } + +.fa-carriage-baby::before { + content: "\f77d"; } + +.fa-traffic-light::before { + content: "\f637"; } + +.fa-thermometer::before { + content: "\f491"; } + +.fa-vr-cardboard::before { + content: "\f729"; } + +.fa-hand-middle-finger::before { + content: "\f806"; } + +.fa-percent::before { + content: "\25"; } + +.fa-percentage::before { + content: "\25"; } + +.fa-truck-moving::before { + content: "\f4df"; } + +.fa-glass-water-droplet::before { + content: "\e4f5"; } + +.fa-display::before { + content: "\e163"; } + +.fa-face-smile::before { + content: "\f118"; } + +.fa-smile::before { + content: "\f118"; } + +.fa-thumbtack::before { + content: "\f08d"; } + +.fa-thumb-tack::before { + content: "\f08d"; } + +.fa-trophy::before { + content: "\f091"; } + +.fa-person-praying::before { + content: "\f683"; } + +.fa-pray::before { + content: "\f683"; } + +.fa-hammer::before { + content: "\f6e3"; } + +.fa-hand-peace::before { + content: "\f25b"; } + +.fa-rotate::before { + content: "\f2f1"; } + +.fa-sync-alt::before { + content: "\f2f1"; } + +.fa-spinner::before { + content: "\f110"; } + +.fa-robot::before { + content: "\f544"; } + +.fa-peace::before { + content: "\f67c"; } + +.fa-gears::before { + content: "\f085"; } + +.fa-cogs::before { + content: "\f085"; } + +.fa-warehouse::before { + content: "\f494"; } + +.fa-arrow-up-right-dots::before { + content: "\e4b7"; } + +.fa-splotch::before { + content: "\f5bc"; } + +.fa-face-grin-hearts::before { + content: "\f584"; } + +.fa-grin-hearts::before { + content: "\f584"; } + +.fa-dice-four::before { + content: "\f524"; } + +.fa-sim-card::before { + content: "\f7c4"; } + +.fa-transgender::before { + content: "\f225"; } + +.fa-transgender-alt::before { + content: "\f225"; } + +.fa-mercury::before { + content: "\f223"; } + +.fa-arrow-turn-down::before { + content: "\f149"; } + +.fa-level-down::before { + content: "\f149"; } + +.fa-person-falling-burst::before { + content: "\e547"; } + +.fa-award::before { + content: "\f559"; } + +.fa-ticket-simple::before { + content: "\f3ff"; } + +.fa-ticket-alt::before { + content: "\f3ff"; } + +.fa-building::before { + content: "\f1ad"; } + +.fa-angles-left::before { + content: "\f100"; } + +.fa-angle-double-left::before { + content: "\f100"; } + +.fa-qrcode::before { + content: "\f029"; } + +.fa-clock-rotate-left::before { + content: "\f1da"; } + +.fa-history::before { + content: "\f1da"; } + +.fa-face-grin-beam-sweat::before { + content: "\f583"; } + +.fa-grin-beam-sweat::before { + content: "\f583"; } + +.fa-file-export::before { + content: "\f56e"; } + +.fa-arrow-right-from-file::before { + content: "\f56e"; } + +.fa-shield::before { + content: "\f132"; } + +.fa-shield-blank::before { + content: "\f132"; } + +.fa-arrow-up-short-wide::before { + content: "\f885"; } + +.fa-sort-amount-up-alt::before { + content: "\f885"; } + +.fa-house-medical::before { + content: "\e3b2"; } + +.fa-golf-ball-tee::before { + content: "\f450"; } + +.fa-golf-ball::before { + content: "\f450"; } + +.fa-circle-chevron-left::before { + content: "\f137"; } + +.fa-chevron-circle-left::before { + content: "\f137"; } + +.fa-house-chimney-window::before { + content: "\e00d"; } + +.fa-pen-nib::before { + content: "\f5ad"; } + +.fa-tent-arrow-turn-left::before { + content: "\e580"; } + +.fa-tents::before { + content: "\e582"; } + +.fa-wand-magic::before { + content: "\f0d0"; } + +.fa-magic::before { + content: "\f0d0"; } + +.fa-dog::before { + content: "\f6d3"; } + +.fa-carrot::before { + content: "\f787"; } + +.fa-moon::before { + content: "\f186"; } + +.fa-wine-glass-empty::before { + content: "\f5ce"; } + +.fa-wine-glass-alt::before { + content: "\f5ce"; } + +.fa-cheese::before { + content: "\f7ef"; } + +.fa-yin-yang::before { + content: "\f6ad"; } + +.fa-music::before { + content: "\f001"; } + +.fa-code-commit::before { + content: "\f386"; } + +.fa-temperature-low::before { + content: "\f76b"; } + +.fa-person-biking::before { + content: "\f84a"; } + +.fa-biking::before { + content: "\f84a"; } + +.fa-broom::before { + content: "\f51a"; } + +.fa-shield-heart::before { + content: "\e574"; } + +.fa-gopuram::before { + content: "\f664"; } + +.fa-earth-oceania::before { + content: "\e47b"; } + +.fa-globe-oceania::before { + content: "\e47b"; } + +.fa-square-xmark::before { + content: "\f2d3"; } + +.fa-times-square::before { + content: "\f2d3"; } + +.fa-xmark-square::before { + content: "\f2d3"; } + +.fa-hashtag::before { + content: "\23"; } + +.fa-up-right-and-down-left-from-center::before { + content: "\f424"; } + +.fa-expand-alt::before { + content: "\f424"; } + +.fa-oil-can::before { + content: "\f613"; } + +.fa-t::before { + content: "\54"; } + +.fa-hippo::before { + content: "\f6ed"; } + +.fa-chart-column::before { + content: "\e0e3"; } + +.fa-infinity::before { + content: "\f534"; } + +.fa-vial-circle-check::before { + content: "\e596"; } + +.fa-person-arrow-down-to-line::before { + content: "\e538"; } + +.fa-voicemail::before { + content: "\f897"; } + +.fa-fan::before { + content: "\f863"; } + +.fa-person-walking-luggage::before { + content: "\e554"; } + +.fa-up-down::before { + content: "\f338"; } + +.fa-arrows-alt-v::before { + content: "\f338"; } + +.fa-cloud-moon-rain::before { + content: "\f73c"; } + +.fa-calendar::before { + content: "\f133"; } + +.fa-trailer::before { + content: "\e041"; } + +.fa-bahai::before { + content: "\f666"; } + +.fa-haykal::before { + content: "\f666"; } + +.fa-sd-card::before { + content: "\f7c2"; } + +.fa-dragon::before { + content: "\f6d5"; } + +.fa-shoe-prints::before { + content: "\f54b"; } + +.fa-circle-plus::before { + content: "\f055"; } + +.fa-plus-circle::before { + content: "\f055"; } + +.fa-face-grin-tongue-wink::before { + content: "\f58b"; } + +.fa-grin-tongue-wink::before { + content: "\f58b"; } + +.fa-hand-holding::before { + content: "\f4bd"; } + +.fa-plug-circle-exclamation::before { + content: "\e55d"; } + +.fa-link-slash::before { + content: "\f127"; } + +.fa-chain-broken::before { + content: "\f127"; } + +.fa-chain-slash::before { + content: "\f127"; } + +.fa-unlink::before { + content: "\f127"; } + +.fa-clone::before { + content: "\f24d"; } + +.fa-person-walking-arrow-loop-left::before { + content: "\e551"; } + +.fa-arrow-up-z-a::before { + content: "\f882"; } + +.fa-sort-alpha-up-alt::before { + content: "\f882"; } + +.fa-fire-flame-curved::before { + content: "\f7e4"; } + +.fa-fire-alt::before { + content: "\f7e4"; } + +.fa-tornado::before { + content: "\f76f"; } + +.fa-file-circle-plus::before { + content: "\e494"; } + +.fa-book-quran::before { + content: "\f687"; } + +.fa-quran::before { + content: "\f687"; } + +.fa-anchor::before { + content: "\f13d"; } + +.fa-border-all::before { + content: "\f84c"; } + +.fa-face-angry::before { + content: "\f556"; } + +.fa-angry::before { + content: "\f556"; } + +.fa-cookie-bite::before { + content: "\f564"; } + +.fa-arrow-trend-down::before { + content: "\e097"; } + +.fa-rss::before { + content: "\f09e"; } + +.fa-feed::before { + content: "\f09e"; } + +.fa-draw-polygon::before { + content: "\f5ee"; } + +.fa-scale-balanced::before { + content: "\f24e"; } + +.fa-balance-scale::before { + content: "\f24e"; } + +.fa-gauge-simple-high::before { + content: "\f62a"; } + +.fa-tachometer::before { + content: "\f62a"; } + +.fa-tachometer-fast::before { + content: "\f62a"; } + +.fa-shower::before { + content: "\f2cc"; } + +.fa-desktop::before { + content: "\f390"; } + +.fa-desktop-alt::before { + content: "\f390"; } + +.fa-m::before { + content: "\4d"; } + +.fa-table-list::before { + content: "\f00b"; } + +.fa-th-list::before { + content: "\f00b"; } + +.fa-comment-sms::before { + content: "\f7cd"; } + +.fa-sms::before { + content: "\f7cd"; } + +.fa-book::before { + content: "\f02d"; } + +.fa-user-plus::before { + content: "\f234"; } + +.fa-check::before { + content: "\f00c"; } + +.fa-battery-three-quarters::before { + content: "\f241"; } + +.fa-battery-4::before { + content: "\f241"; } + +.fa-house-circle-check::before { + content: "\e509"; } + +.fa-angle-left::before { + content: "\f104"; } + +.fa-diagram-successor::before { + content: "\e47a"; } + +.fa-truck-arrow-right::before { + content: "\e58b"; } + +.fa-arrows-split-up-and-left::before { + content: "\e4bc"; } + +.fa-hand-fist::before { + content: "\f6de"; } + +.fa-fist-raised::before { + content: "\f6de"; } + +.fa-cloud-moon::before { + content: "\f6c3"; } + +.fa-briefcase::before { + content: "\f0b1"; } + +.fa-person-falling::before { + content: "\e546"; } + +.fa-image-portrait::before { + content: "\f3e0"; } + +.fa-portrait::before { + content: "\f3e0"; } + +.fa-user-tag::before { + content: "\f507"; } + +.fa-rug::before { + content: "\e569"; } + +.fa-earth-europe::before { + content: "\f7a2"; } + +.fa-globe-europe::before { + content: "\f7a2"; } + +.fa-cart-flatbed-suitcase::before { + content: "\f59d"; } + +.fa-luggage-cart::before { + content: "\f59d"; } + +.fa-rectangle-xmark::before { + content: "\f410"; } + +.fa-rectangle-times::before { + content: "\f410"; } + +.fa-times-rectangle::before { + content: "\f410"; } + +.fa-window-close::before { + content: "\f410"; } + +.fa-baht-sign::before { + content: "\e0ac"; } + +.fa-book-open::before { + content: "\f518"; } + +.fa-book-journal-whills::before { + content: "\f66a"; } + +.fa-journal-whills::before { + content: "\f66a"; } + +.fa-handcuffs::before { + content: "\e4f8"; } + +.fa-triangle-exclamation::before { + content: "\f071"; } + +.fa-exclamation-triangle::before { + content: "\f071"; } + +.fa-warning::before { + content: "\f071"; } + +.fa-database::before { + content: "\f1c0"; } + +.fa-share::before { + content: "\f064"; } + +.fa-arrow-turn-right::before { + content: "\f064"; } + +.fa-mail-forward::before { + content: "\f064"; } + +.fa-bottle-droplet::before { + content: "\e4c4"; } + +.fa-mask-face::before { + content: "\e1d7"; } + +.fa-hill-rockslide::before { + content: "\e508"; } + +.fa-right-left::before { + content: "\f362"; } + +.fa-exchange-alt::before { + content: "\f362"; } + +.fa-paper-plane::before { + content: "\f1d8"; } + +.fa-road-circle-exclamation::before { + content: "\e565"; } + +.fa-dungeon::before { + content: "\f6d9"; } + +.fa-align-right::before { + content: "\f038"; } + +.fa-money-bill-1-wave::before { + content: "\f53b"; } + +.fa-money-bill-wave-alt::before { + content: "\f53b"; } + +.fa-life-ring::before { + content: "\f1cd"; } + +.fa-hands::before { + content: "\f2a7"; } + +.fa-sign-language::before { + content: "\f2a7"; } + +.fa-signing::before { + content: "\f2a7"; } + +.fa-calendar-day::before { + content: "\f783"; } + +.fa-water-ladder::before { + content: "\f5c5"; } + +.fa-ladder-water::before { + content: "\f5c5"; } + +.fa-swimming-pool::before { + content: "\f5c5"; } + +.fa-arrows-up-down::before { + content: "\f07d"; } + +.fa-arrows-v::before { + content: "\f07d"; } + +.fa-face-grimace::before { + content: "\f57f"; } + +.fa-grimace::before { + content: "\f57f"; } + +.fa-wheelchair-move::before { + content: "\e2ce"; } + +.fa-wheelchair-alt::before { + content: "\e2ce"; } + +.fa-turn-down::before { + content: "\f3be"; } + +.fa-level-down-alt::before { + content: "\f3be"; } + +.fa-person-walking-arrow-right::before { + content: "\e552"; } + +.fa-square-envelope::before { + content: "\f199"; } + +.fa-envelope-square::before { + content: "\f199"; } + +.fa-dice::before { + content: "\f522"; } + +.fa-bowling-ball::before { + content: "\f436"; } + +.fa-brain::before { + content: "\f5dc"; } + +.fa-bandage::before { + content: "\f462"; } + +.fa-band-aid::before { + content: "\f462"; } + +.fa-calendar-minus::before { + content: "\f272"; } + +.fa-circle-xmark::before { + content: "\f057"; } + +.fa-times-circle::before { + content: "\f057"; } + +.fa-xmark-circle::before { + content: "\f057"; } + +.fa-gifts::before { + content: "\f79c"; } + +.fa-hotel::before { + content: "\f594"; } + +.fa-earth-asia::before { + content: "\f57e"; } + +.fa-globe-asia::before { + content: "\f57e"; } + +.fa-id-card-clip::before { + content: "\f47f"; } + +.fa-id-card-alt::before { + content: "\f47f"; } + +.fa-magnifying-glass-plus::before { + content: "\f00e"; } + +.fa-search-plus::before { + content: "\f00e"; } + +.fa-thumbs-up::before { + content: "\f164"; } + +.fa-user-clock::before { + content: "\f4fd"; } + +.fa-hand-dots::before { + content: "\f461"; } + +.fa-allergies::before { + content: "\f461"; } + +.fa-file-invoice::before { + content: "\f570"; } + +.fa-window-minimize::before { + content: "\f2d1"; } + +.fa-mug-saucer::before { + content: "\f0f4"; } + +.fa-coffee::before { + content: "\f0f4"; } + +.fa-brush::before { + content: "\f55d"; } + +.fa-mask::before { + content: "\f6fa"; } + +.fa-magnifying-glass-minus::before { + content: "\f010"; } + +.fa-search-minus::before { + content: "\f010"; } + +.fa-ruler-vertical::before { + content: "\f548"; } + +.fa-user-large::before { + content: "\f406"; } + +.fa-user-alt::before { + content: "\f406"; } + +.fa-train-tram::before { + content: "\e5b4"; } + +.fa-user-nurse::before { + content: "\f82f"; } + +.fa-syringe::before { + content: "\f48e"; } + +.fa-cloud-sun::before { + content: "\f6c4"; } + +.fa-stopwatch-20::before { + content: "\e06f"; } + +.fa-square-full::before { + content: "\f45c"; } + +.fa-magnet::before { + content: "\f076"; } + +.fa-jar::before { + content: "\e516"; } + +.fa-note-sticky::before { + content: "\f249"; } + +.fa-sticky-note::before { + content: "\f249"; } + +.fa-bug-slash::before { + content: "\e490"; } + +.fa-arrow-up-from-water-pump::before { + content: "\e4b6"; } + +.fa-bone::before { + content: "\f5d7"; } + +.fa-user-injured::before { + content: "\f728"; } + +.fa-face-sad-tear::before { + content: "\f5b4"; } + +.fa-sad-tear::before { + content: "\f5b4"; } + +.fa-plane::before { + content: "\f072"; } + +.fa-tent-arrows-down::before { + content: "\e581"; } + +.fa-exclamation::before { + content: "\21"; } + +.fa-arrows-spin::before { + content: "\e4bb"; } + +.fa-print::before { + content: "\f02f"; } + +.fa-turkish-lira-sign::before { + content: "\e2bb"; } + +.fa-try::before { + content: "\e2bb"; } + +.fa-turkish-lira::before { + content: "\e2bb"; } + +.fa-dollar-sign::before { + content: "\24"; } + +.fa-dollar::before { + content: "\24"; } + +.fa-usd::before { + content: "\24"; } + +.fa-x::before { + content: "\58"; } + +.fa-magnifying-glass-dollar::before { + content: "\f688"; } + +.fa-search-dollar::before { + content: "\f688"; } + +.fa-users-gear::before { + content: "\f509"; } + +.fa-users-cog::before { + content: "\f509"; } + +.fa-person-military-pointing::before { + content: "\e54a"; } + +.fa-building-columns::before { + content: "\f19c"; } + +.fa-bank::before { + content: "\f19c"; } + +.fa-institution::before { + content: "\f19c"; } + +.fa-museum::before { + content: "\f19c"; } + +.fa-university::before { + content: "\f19c"; } + +.fa-umbrella::before { + content: "\f0e9"; } + +.fa-trowel::before { + content: "\e589"; } + +.fa-d::before { + content: "\44"; } + +.fa-stapler::before { + content: "\e5af"; } + +.fa-masks-theater::before { + content: "\f630"; } + +.fa-theater-masks::before { + content: "\f630"; } + +.fa-kip-sign::before { + content: "\e1c4"; } + +.fa-hand-point-left::before { + content: "\f0a5"; } + +.fa-handshake-simple::before { + content: "\f4c6"; } + +.fa-handshake-alt::before { + content: "\f4c6"; } + +.fa-jet-fighter::before { + content: "\f0fb"; } + +.fa-fighter-jet::before { + content: "\f0fb"; } + +.fa-square-share-nodes::before { + content: "\f1e1"; } + +.fa-share-alt-square::before { + content: "\f1e1"; } + +.fa-barcode::before { + content: "\f02a"; } + +.fa-plus-minus::before { + content: "\e43c"; } + +.fa-video::before { + content: "\f03d"; } + +.fa-video-camera::before { + content: "\f03d"; } + +.fa-graduation-cap::before { + content: "\f19d"; } + +.fa-mortar-board::before { + content: "\f19d"; } + +.fa-hand-holding-medical::before { + content: "\e05c"; } + +.fa-person-circle-check::before { + content: "\e53e"; } + +.fa-turn-up::before { + content: "\f3bf"; } + +.fa-level-up-alt::before { + content: "\f3bf"; } + +.sr-only, +.fa-sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; } + +.sr-only-focusable:not(:focus), +.fa-sr-only-focusable:not(:focus) { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; } +:root, :host { + --fa-style-family-brands: 'Font Awesome 6 Brands'; + --fa-font-brands: normal 400 1em/1 'Font Awesome 6 Brands'; } + +@font-face { + font-family: 'Font Awesome 6 Brands'; + font-style: normal; + font-weight: 400; + font-display: block; + src: url("./webfonts/fa-brands-400.woff2") format("woff2"), url("./webfonts/fa-brands-400.ttf") format("truetype"); } + +.fab, +.fa-brands { + font-weight: 400; } + +.fa-monero:before { + content: "\f3d0"; } + +.fa-hooli:before { + content: "\f427"; } + +.fa-yelp:before { + content: "\f1e9"; } + +.fa-cc-visa:before { + content: "\f1f0"; } + +.fa-lastfm:before { + content: "\f202"; } + +.fa-shopware:before { + content: "\f5b5"; } + +.fa-creative-commons-nc:before { + content: "\f4e8"; } + +.fa-aws:before { + content: "\f375"; } + +.fa-redhat:before { + content: "\f7bc"; } + +.fa-yoast:before { + content: "\f2b1"; } + +.fa-cloudflare:before { + content: "\e07d"; } + +.fa-ups:before { + content: "\f7e0"; } + +.fa-wpexplorer:before { + content: "\f2de"; } + +.fa-dyalog:before { + content: "\f399"; } + +.fa-bity:before { + content: "\f37a"; } + +.fa-stackpath:before { + content: "\f842"; } + +.fa-buysellads:before { + content: "\f20d"; } + +.fa-first-order:before { + content: "\f2b0"; } + +.fa-modx:before { + content: "\f285"; } + +.fa-guilded:before { + content: "\e07e"; } + +.fa-vnv:before { + content: "\f40b"; } + +.fa-square-js:before { + content: "\f3b9"; } + +.fa-js-square:before { + content: "\f3b9"; } + +.fa-microsoft:before { + content: "\f3ca"; } + +.fa-qq:before { + content: "\f1d6"; } + +.fa-orcid:before { + content: "\f8d2"; } + +.fa-java:before { + content: "\f4e4"; } + +.fa-invision:before { + content: "\f7b0"; } + +.fa-creative-commons-pd-alt:before { + content: "\f4ed"; } + +.fa-centercode:before { + content: "\f380"; } + +.fa-glide-g:before { + content: "\f2a6"; } + +.fa-drupal:before { + content: "\f1a9"; } + +.fa-hire-a-helper:before { + content: "\f3b0"; } + +.fa-creative-commons-by:before { + content: "\f4e7"; } + +.fa-unity:before { + content: "\e049"; } + +.fa-whmcs:before { + content: "\f40d"; } + +.fa-rocketchat:before { + content: "\f3e8"; } + +.fa-vk:before { + content: "\f189"; } + +.fa-untappd:before { + content: "\f405"; } + +.fa-mailchimp:before { + content: "\f59e"; } + +.fa-css3-alt:before { + content: "\f38b"; } + +.fa-square-reddit:before { + content: "\f1a2"; } + +.fa-reddit-square:before { + content: "\f1a2"; } + +.fa-vimeo-v:before { + content: "\f27d"; } + +.fa-contao:before { + content: "\f26d"; } + +.fa-square-font-awesome:before { + content: "\e5ad"; } + +.fa-deskpro:before { + content: "\f38f"; } + +.fa-sistrix:before { + content: "\f3ee"; } + +.fa-square-instagram:before { + content: "\e055"; } + +.fa-instagram-square:before { + content: "\e055"; } + +.fa-battle-net:before { + content: "\f835"; } + +.fa-the-red-yeti:before { + content: "\f69d"; } + +.fa-square-hacker-news:before { + content: "\f3af"; } + +.fa-hacker-news-square:before { + content: "\f3af"; } + +.fa-edge:before { + content: "\f282"; } + +.fa-napster:before { + content: "\f3d2"; } + +.fa-square-snapchat:before { + content: "\f2ad"; } + +.fa-snapchat-square:before { + content: "\f2ad"; } + +.fa-google-plus-g:before { + content: "\f0d5"; } + +.fa-artstation:before { + content: "\f77a"; } + +.fa-markdown:before { + content: "\f60f"; } + +.fa-sourcetree:before { + content: "\f7d3"; } + +.fa-google-plus:before { + content: "\f2b3"; } + +.fa-diaspora:before { + content: "\f791"; } + +.fa-foursquare:before { + content: "\f180"; } + +.fa-stack-overflow:before { + content: "\f16c"; } + +.fa-github-alt:before { + content: "\f113"; } + +.fa-phoenix-squadron:before { + content: "\f511"; } + +.fa-pagelines:before { + content: "\f18c"; } + +.fa-algolia:before { + content: "\f36c"; } + +.fa-red-river:before { + content: "\f3e3"; } + +.fa-creative-commons-sa:before { + content: "\f4ef"; } + +.fa-safari:before { + content: "\f267"; } + +.fa-google:before { + content: "\f1a0"; } + +.fa-square-font-awesome-stroke:before { + content: "\f35c"; } + +.fa-font-awesome-alt:before { + content: "\f35c"; } + +.fa-atlassian:before { + content: "\f77b"; } + +.fa-linkedin-in:before { + content: "\f0e1"; } + +.fa-digital-ocean:before { + content: "\f391"; } + +.fa-nimblr:before { + content: "\f5a8"; } + +.fa-chromecast:before { + content: "\f838"; } + +.fa-evernote:before { + content: "\f839"; } + +.fa-hacker-news:before { + content: "\f1d4"; } + +.fa-creative-commons-sampling:before { + content: "\f4f0"; } + +.fa-adversal:before { + content: "\f36a"; } + +.fa-creative-commons:before { + content: "\f25e"; } + +.fa-watchman-monitoring:before { + content: "\e087"; } + +.fa-fonticons:before { + content: "\f280"; } + +.fa-weixin:before { + content: "\f1d7"; } + +.fa-shirtsinbulk:before { + content: "\f214"; } + +.fa-codepen:before { + content: "\f1cb"; } + +.fa-git-alt:before { + content: "\f841"; } + +.fa-lyft:before { + content: "\f3c3"; } + +.fa-rev:before { + content: "\f5b2"; } + +.fa-windows:before { + content: "\f17a"; } + +.fa-wizards-of-the-coast:before { + content: "\f730"; } + +.fa-square-viadeo:before { + content: "\f2aa"; } + +.fa-viadeo-square:before { + content: "\f2aa"; } + +.fa-meetup:before { + content: "\f2e0"; } + +.fa-centos:before { + content: "\f789"; } + +.fa-adn:before { + content: "\f170"; } + +.fa-cloudsmith:before { + content: "\f384"; } + +.fa-pied-piper-alt:before { + content: "\f1a8"; } + +.fa-square-dribbble:before { + content: "\f397"; } + +.fa-dribbble-square:before { + content: "\f397"; } + +.fa-codiepie:before { + content: "\f284"; } + +.fa-node:before { + content: "\f419"; } + +.fa-mix:before { + content: "\f3cb"; } + +.fa-steam:before { + content: "\f1b6"; } + +.fa-cc-apple-pay:before { + content: "\f416"; } + +.fa-scribd:before { + content: "\f28a"; } + +.fa-openid:before { + content: "\f19b"; } + +.fa-instalod:before { + content: "\e081"; } + +.fa-expeditedssl:before { + content: "\f23e"; } + +.fa-sellcast:before { + content: "\f2da"; } + +.fa-square-twitter:before { + content: "\f081"; } + +.fa-twitter-square:before { + content: "\f081"; } + +.fa-r-project:before { + content: "\f4f7"; } + +.fa-delicious:before { + content: "\f1a5"; } + +.fa-freebsd:before { + content: "\f3a4"; } + +.fa-vuejs:before { + content: "\f41f"; } + +.fa-accusoft:before { + content: "\f369"; } + +.fa-ioxhost:before { + content: "\f208"; } + +.fa-fonticons-fi:before { + content: "\f3a2"; } + +.fa-app-store:before { + content: "\f36f"; } + +.fa-cc-mastercard:before { + content: "\f1f1"; } + +.fa-itunes-note:before { + content: "\f3b5"; } + +.fa-golang:before { + content: "\e40f"; } + +.fa-kickstarter:before { + content: "\f3bb"; } + +.fa-grav:before { + content: "\f2d6"; } + +.fa-weibo:before { + content: "\f18a"; } + +.fa-uncharted:before { + content: "\e084"; } + +.fa-firstdraft:before { + content: "\f3a1"; } + +.fa-square-youtube:before { + content: "\f431"; } + +.fa-youtube-square:before { + content: "\f431"; } + +.fa-wikipedia-w:before { + content: "\f266"; } + +.fa-wpressr:before { + content: "\f3e4"; } + +.fa-rendact:before { + content: "\f3e4"; } + +.fa-angellist:before { + content: "\f209"; } + +.fa-galactic-republic:before { + content: "\f50c"; } + +.fa-nfc-directional:before { + content: "\e530"; } + +.fa-skype:before { + content: "\f17e"; } + +.fa-joget:before { + content: "\f3b7"; } + +.fa-fedora:before { + content: "\f798"; } + +.fa-stripe-s:before { + content: "\f42a"; } + +.fa-meta:before { + content: "\e49b"; } + +.fa-laravel:before { + content: "\f3bd"; } + +.fa-hotjar:before { + content: "\f3b1"; } + +.fa-bluetooth-b:before { + content: "\f294"; } + +.fa-sticker-mule:before { + content: "\f3f7"; } + +.fa-creative-commons-zero:before { + content: "\f4f3"; } + +.fa-hips:before { + content: "\f452"; } + +.fa-behance:before { + content: "\f1b4"; } + +.fa-reddit:before { + content: "\f1a1"; } + +.fa-discord:before { + content: "\f392"; } + +.fa-chrome:before { + content: "\f268"; } + +.fa-app-store-ios:before { + content: "\f370"; } + +.fa-cc-discover:before { + content: "\f1f2"; } + +.fa-wpbeginner:before { + content: "\f297"; } + +.fa-confluence:before { + content: "\f78d"; } + +.fa-mdb:before { + content: "\f8ca"; } + +.fa-dochub:before { + content: "\f394"; } + +.fa-accessible-icon:before { + content: "\f368"; } + +.fa-ebay:before { + content: "\f4f4"; } + +.fa-amazon:before { + content: "\f270"; } + +.fa-unsplash:before { + content: "\e07c"; } + +.fa-yarn:before { + content: "\f7e3"; } + +.fa-square-steam:before { + content: "\f1b7"; } + +.fa-steam-square:before { + content: "\f1b7"; } + +.fa-500px:before { + content: "\f26e"; } + +.fa-square-vimeo:before { + content: "\f194"; } + +.fa-vimeo-square:before { + content: "\f194"; } + +.fa-asymmetrik:before { + content: "\f372"; } + +.fa-font-awesome:before { + content: "\f2b4"; } + +.fa-font-awesome-flag:before { + content: "\f2b4"; } + +.fa-font-awesome-logo-full:before { + content: "\f2b4"; } + +.fa-gratipay:before { + content: "\f184"; } + +.fa-apple:before { + content: "\f179"; } + +.fa-hive:before { + content: "\e07f"; } + +.fa-gitkraken:before { + content: "\f3a6"; } + +.fa-keybase:before { + content: "\f4f5"; } + +.fa-apple-pay:before { + content: "\f415"; } + +.fa-padlet:before { + content: "\e4a0"; } + +.fa-amazon-pay:before { + content: "\f42c"; } + +.fa-square-github:before { + content: "\f092"; } + +.fa-github-square:before { + content: "\f092"; } + +.fa-stumbleupon:before { + content: "\f1a4"; } + +.fa-fedex:before { + content: "\f797"; } + +.fa-phoenix-framework:before { + content: "\f3dc"; } + +.fa-shopify:before { + content: "\e057"; } + +.fa-neos:before { + content: "\f612"; } + +.fa-hackerrank:before { + content: "\f5f7"; } + +.fa-researchgate:before { + content: "\f4f8"; } + +.fa-swift:before { + content: "\f8e1"; } + +.fa-angular:before { + content: "\f420"; } + +.fa-speakap:before { + content: "\f3f3"; } + +.fa-angrycreative:before { + content: "\f36e"; } + +.fa-y-combinator:before { + content: "\f23b"; } + +.fa-empire:before { + content: "\f1d1"; } + +.fa-envira:before { + content: "\f299"; } + +.fa-square-gitlab:before { + content: "\e5ae"; } + +.fa-gitlab-square:before { + content: "\e5ae"; } + +.fa-studiovinari:before { + content: "\f3f8"; } + +.fa-pied-piper:before { + content: "\f2ae"; } + +.fa-wordpress:before { + content: "\f19a"; } + +.fa-product-hunt:before { + content: "\f288"; } + +.fa-firefox:before { + content: "\f269"; } + +.fa-linode:before { + content: "\f2b8"; } + +.fa-goodreads:before { + content: "\f3a8"; } + +.fa-square-odnoklassniki:before { + content: "\f264"; } + +.fa-odnoklassniki-square:before { + content: "\f264"; } + +.fa-jsfiddle:before { + content: "\f1cc"; } + +.fa-sith:before { + content: "\f512"; } + +.fa-themeisle:before { + content: "\f2b2"; } + +.fa-page4:before { + content: "\f3d7"; } + +.fa-hashnode:before { + content: "\e499"; } + +.fa-react:before { + content: "\f41b"; } + +.fa-cc-paypal:before { + content: "\f1f4"; } + +.fa-squarespace:before { + content: "\f5be"; } + +.fa-cc-stripe:before { + content: "\f1f5"; } + +.fa-creative-commons-share:before { + content: "\f4f2"; } + +.fa-bitcoin:before { + content: "\f379"; } + +.fa-keycdn:before { + content: "\f3ba"; } + +.fa-opera:before { + content: "\f26a"; } + +.fa-itch-io:before { + content: "\f83a"; } + +.fa-umbraco:before { + content: "\f8e8"; } + +.fa-galactic-senate:before { + content: "\f50d"; } + +.fa-ubuntu:before { + content: "\f7df"; } + +.fa-draft2digital:before { + content: "\f396"; } + +.fa-stripe:before { + content: "\f429"; } + +.fa-houzz:before { + content: "\f27c"; } + +.fa-gg:before { + content: "\f260"; } + +.fa-dhl:before { + content: "\f790"; } + +.fa-square-pinterest:before { + content: "\f0d3"; } + +.fa-pinterest-square:before { + content: "\f0d3"; } + +.fa-xing:before { + content: "\f168"; } + +.fa-blackberry:before { + content: "\f37b"; } + +.fa-creative-commons-pd:before { + content: "\f4ec"; } + +.fa-playstation:before { + content: "\f3df"; } + +.fa-quinscape:before { + content: "\f459"; } + +.fa-less:before { + content: "\f41d"; } + +.fa-blogger-b:before { + content: "\f37d"; } + +.fa-opencart:before { + content: "\f23d"; } + +.fa-vine:before { + content: "\f1ca"; } + +.fa-paypal:before { + content: "\f1ed"; } + +.fa-gitlab:before { + content: "\f296"; } + +.fa-typo3:before { + content: "\f42b"; } + +.fa-reddit-alien:before { + content: "\f281"; } + +.fa-yahoo:before { + content: "\f19e"; } + +.fa-dailymotion:before { + content: "\e052"; } + +.fa-affiliatetheme:before { + content: "\f36b"; } + +.fa-pied-piper-pp:before { + content: "\f1a7"; } + +.fa-bootstrap:before { + content: "\f836"; } + +.fa-odnoklassniki:before { + content: "\f263"; } + +.fa-nfc-symbol:before { + content: "\e531"; } + +.fa-ethereum:before { + content: "\f42e"; } + +.fa-speaker-deck:before { + content: "\f83c"; } + +.fa-creative-commons-nc-eu:before { + content: "\f4e9"; } + +.fa-patreon:before { + content: "\f3d9"; } + +.fa-avianex:before { + content: "\f374"; } + +.fa-ello:before { + content: "\f5f1"; } + +.fa-gofore:before { + content: "\f3a7"; } + +.fa-bimobject:before { + content: "\f378"; } + +.fa-facebook-f:before { + content: "\f39e"; } + +.fa-square-google-plus:before { + content: "\f0d4"; } + +.fa-google-plus-square:before { + content: "\f0d4"; } + +.fa-mandalorian:before { + content: "\f50f"; } + +.fa-first-order-alt:before { + content: "\f50a"; } + +.fa-osi:before { + content: "\f41a"; } + +.fa-google-wallet:before { + content: "\f1ee"; } + +.fa-d-and-d-beyond:before { + content: "\f6ca"; } + +.fa-periscope:before { + content: "\f3da"; } + +.fa-fulcrum:before { + content: "\f50b"; } + +.fa-cloudscale:before { + content: "\f383"; } + +.fa-forumbee:before { + content: "\f211"; } + +.fa-mizuni:before { + content: "\f3cc"; } + +.fa-schlix:before { + content: "\f3ea"; } + +.fa-square-xing:before { + content: "\f169"; } + +.fa-xing-square:before { + content: "\f169"; } + +.fa-bandcamp:before { + content: "\f2d5"; } + +.fa-wpforms:before { + content: "\f298"; } + +.fa-cloudversify:before { + content: "\f385"; } + +.fa-usps:before { + content: "\f7e1"; } + +.fa-megaport:before { + content: "\f5a3"; } + +.fa-magento:before { + content: "\f3c4"; } + +.fa-spotify:before { + content: "\f1bc"; } + +.fa-optin-monster:before { + content: "\f23c"; } + +.fa-fly:before { + content: "\f417"; } + +.fa-aviato:before { + content: "\f421"; } + +.fa-itunes:before { + content: "\f3b4"; } + +.fa-cuttlefish:before { + content: "\f38c"; } + +.fa-blogger:before { + content: "\f37c"; } + +.fa-flickr:before { + content: "\f16e"; } + +.fa-viber:before { + content: "\f409"; } + +.fa-soundcloud:before { + content: "\f1be"; } + +.fa-digg:before { + content: "\f1a6"; } + +.fa-tencent-weibo:before { + content: "\f1d5"; } + +.fa-symfony:before { + content: "\f83d"; } + +.fa-maxcdn:before { + content: "\f136"; } + +.fa-etsy:before { + content: "\f2d7"; } + +.fa-facebook-messenger:before { + content: "\f39f"; } + +.fa-audible:before { + content: "\f373"; } + +.fa-think-peaks:before { + content: "\f731"; } + +.fa-bilibili:before { + content: "\e3d9"; } + +.fa-erlang:before { + content: "\f39d"; } + +.fa-cotton-bureau:before { + content: "\f89e"; } + +.fa-dashcube:before { + content: "\f210"; } + +.fa-42-group:before { + content: "\e080"; } + +.fa-innosoft:before { + content: "\e080"; } + +.fa-stack-exchange:before { + content: "\f18d"; } + +.fa-elementor:before { + content: "\f430"; } + +.fa-square-pied-piper:before { + content: "\e01e"; } + +.fa-pied-piper-square:before { + content: "\e01e"; } + +.fa-creative-commons-nd:before { + content: "\f4eb"; } + +.fa-palfed:before { + content: "\f3d8"; } + +.fa-superpowers:before { + content: "\f2dd"; } + +.fa-resolving:before { + content: "\f3e7"; } + +.fa-xbox:before { + content: "\f412"; } + +.fa-searchengin:before { + content: "\f3eb"; } + +.fa-tiktok:before { + content: "\e07b"; } + +.fa-square-facebook:before { + content: "\f082"; } + +.fa-facebook-square:before { + content: "\f082"; } + +.fa-renren:before { + content: "\f18b"; } + +.fa-linux:before { + content: "\f17c"; } + +.fa-glide:before { + content: "\f2a5"; } + +.fa-linkedin:before { + content: "\f08c"; } + +.fa-hubspot:before { + content: "\f3b2"; } + +.fa-deploydog:before { + content: "\f38e"; } + +.fa-twitch:before { + content: "\f1e8"; } + +.fa-ravelry:before { + content: "\f2d9"; } + +.fa-mixer:before { + content: "\e056"; } + +.fa-square-lastfm:before { + content: "\f203"; } + +.fa-lastfm-square:before { + content: "\f203"; } + +.fa-vimeo:before { + content: "\f40a"; } + +.fa-mendeley:before { + content: "\f7b3"; } + +.fa-uniregistry:before { + content: "\f404"; } + +.fa-figma:before { + content: "\f799"; } + +.fa-creative-commons-remix:before { + content: "\f4ee"; } + +.fa-cc-amazon-pay:before { + content: "\f42d"; } + +.fa-dropbox:before { + content: "\f16b"; } + +.fa-instagram:before { + content: "\f16d"; } + +.fa-cmplid:before { + content: "\e360"; } + +.fa-facebook:before { + content: "\f09a"; } + +.fa-gripfire:before { + content: "\f3ac"; } + +.fa-jedi-order:before { + content: "\f50e"; } + +.fa-uikit:before { + content: "\f403"; } + +.fa-fort-awesome-alt:before { + content: "\f3a3"; } + +.fa-phabricator:before { + content: "\f3db"; } + +.fa-ussunnah:before { + content: "\f407"; } + +.fa-earlybirds:before { + content: "\f39a"; } + +.fa-trade-federation:before { + content: "\f513"; } + +.fa-autoprefixer:before { + content: "\f41c"; } + +.fa-whatsapp:before { + content: "\f232"; } + +.fa-slideshare:before { + content: "\f1e7"; } + +.fa-google-play:before { + content: "\f3ab"; } + +.fa-viadeo:before { + content: "\f2a9"; } + +.fa-line:before { + content: "\f3c0"; } + +.fa-google-drive:before { + content: "\f3aa"; } + +.fa-servicestack:before { + content: "\f3ec"; } + +.fa-simplybuilt:before { + content: "\f215"; } + +.fa-bitbucket:before { + content: "\f171"; } + +.fa-imdb:before { + content: "\f2d8"; } + +.fa-deezer:before { + content: "\e077"; } + +.fa-raspberry-pi:before { + content: "\f7bb"; } + +.fa-jira:before { + content: "\f7b1"; } + +.fa-docker:before { + content: "\f395"; } + +.fa-screenpal:before { + content: "\e570"; } + +.fa-bluetooth:before { + content: "\f293"; } + +.fa-gitter:before { + content: "\f426"; } + +.fa-d-and-d:before { + content: "\f38d"; } + +.fa-microblog:before { + content: "\e01a"; } + +.fa-cc-diners-club:before { + content: "\f24c"; } + +.fa-gg-circle:before { + content: "\f261"; } + +.fa-pied-piper-hat:before { + content: "\f4e5"; } + +.fa-kickstarter-k:before { + content: "\f3bc"; } + +.fa-yandex:before { + content: "\f413"; } + +.fa-readme:before { + content: "\f4d5"; } + +.fa-html5:before { + content: "\f13b"; } + +.fa-sellsy:before { + content: "\f213"; } + +.fa-sass:before { + content: "\f41e"; } + +.fa-wirsindhandwerk:before { + content: "\e2d0"; } + +.fa-wsh:before { + content: "\e2d0"; } + +.fa-buromobelexperte:before { + content: "\f37f"; } + +.fa-salesforce:before { + content: "\f83b"; } + +.fa-octopus-deploy:before { + content: "\e082"; } + +.fa-medapps:before { + content: "\f3c6"; } + +.fa-ns8:before { + content: "\f3d5"; } + +.fa-pinterest-p:before { + content: "\f231"; } + +.fa-apper:before { + content: "\f371"; } + +.fa-fort-awesome:before { + content: "\f286"; } + +.fa-waze:before { + content: "\f83f"; } + +.fa-cc-jcb:before { + content: "\f24b"; } + +.fa-snapchat:before { + content: "\f2ab"; } + +.fa-snapchat-ghost:before { + content: "\f2ab"; } + +.fa-fantasy-flight-games:before { + content: "\f6dc"; } + +.fa-rust:before { + content: "\e07a"; } + +.fa-wix:before { + content: "\f5cf"; } + +.fa-square-behance:before { + content: "\f1b5"; } + +.fa-behance-square:before { + content: "\f1b5"; } + +.fa-supple:before { + content: "\f3f9"; } + +.fa-rebel:before { + content: "\f1d0"; } + +.fa-css3:before { + content: "\f13c"; } + +.fa-staylinked:before { + content: "\f3f5"; } + +.fa-kaggle:before { + content: "\f5fa"; } + +.fa-space-awesome:before { + content: "\e5ac"; } + +.fa-deviantart:before { + content: "\f1bd"; } + +.fa-cpanel:before { + content: "\f388"; } + +.fa-goodreads-g:before { + content: "\f3a9"; } + +.fa-square-git:before { + content: "\f1d2"; } + +.fa-git-square:before { + content: "\f1d2"; } + +.fa-square-tumblr:before { + content: "\f174"; } + +.fa-tumblr-square:before { + content: "\f174"; } + +.fa-trello:before { + content: "\f181"; } + +.fa-creative-commons-nc-jp:before { + content: "\f4ea"; } + +.fa-get-pocket:before { + content: "\f265"; } + +.fa-perbyte:before { + content: "\e083"; } + +.fa-grunt:before { + content: "\f3ad"; } + +.fa-weebly:before { + content: "\f5cc"; } + +.fa-connectdevelop:before { + content: "\f20e"; } + +.fa-leanpub:before { + content: "\f212"; } + +.fa-black-tie:before { + content: "\f27e"; } + +.fa-themeco:before { + content: "\f5c6"; } + +.fa-python:before { + content: "\f3e2"; } + +.fa-android:before { + content: "\f17b"; } + +.fa-bots:before { + content: "\e340"; } + +.fa-free-code-camp:before { + content: "\f2c5"; } + +.fa-hornbill:before { + content: "\f592"; } + +.fa-js:before { + content: "\f3b8"; } + +.fa-ideal:before { + content: "\e013"; } + +.fa-git:before { + content: "\f1d3"; } + +.fa-dev:before { + content: "\f6cc"; } + +.fa-sketch:before { + content: "\f7c6"; } + +.fa-yandex-international:before { + content: "\f414"; } + +.fa-cc-amex:before { + content: "\f1f3"; } + +.fa-uber:before { + content: "\f402"; } + +.fa-github:before { + content: "\f09b"; } + +.fa-php:before { + content: "\f457"; } + +.fa-alipay:before { + content: "\f642"; } + +.fa-youtube:before { + content: "\f167"; } + +.fa-skyatlas:before { + content: "\f216"; } + +.fa-firefox-browser:before { + content: "\e007"; } + +.fa-replyd:before { + content: "\f3e6"; } + +.fa-suse:before { + content: "\f7d6"; } + +.fa-jenkins:before { + content: "\f3b6"; } + +.fa-twitter:before { + content: "\f099"; } + +.fa-rockrms:before { + content: "\f3e9"; } + +.fa-pinterest:before { + content: "\f0d2"; } + +.fa-buffer:before { + content: "\f837"; } + +.fa-npm:before { + content: "\f3d4"; } + +.fa-yammer:before { + content: "\f840"; } + +.fa-btc:before { + content: "\f15a"; } + +.fa-dribbble:before { + content: "\f17d"; } + +.fa-stumbleupon-circle:before { + content: "\f1a3"; } + +.fa-internet-explorer:before { + content: "\f26b"; } + +.fa-telegram:before { + content: "\f2c6"; } + +.fa-telegram-plane:before { + content: "\f2c6"; } + +.fa-old-republic:before { + content: "\f510"; } + +.fa-square-whatsapp:before { + content: "\f40c"; } + +.fa-whatsapp-square:before { + content: "\f40c"; } + +.fa-node-js:before { + content: "\f3d3"; } + +.fa-edge-legacy:before { + content: "\e078"; } + +.fa-slack:before { + content: "\f198"; } + +.fa-slack-hash:before { + content: "\f198"; } + +.fa-medrt:before { + content: "\f3c8"; } + +.fa-usb:before { + content: "\f287"; } + +.fa-tumblr:before { + content: "\f173"; } + +.fa-vaadin:before { + content: "\f408"; } + +.fa-quora:before { + content: "\f2c4"; } + +.fa-reacteurope:before { + content: "\f75d"; } + +.fa-medium:before { + content: "\f23a"; } + +.fa-medium-m:before { + content: "\f23a"; } + +.fa-amilia:before { + content: "\f36d"; } + +.fa-mixcloud:before { + content: "\f289"; } + +.fa-flipboard:before { + content: "\f44d"; } + +.fa-viacoin:before { + content: "\f237"; } + +.fa-critical-role:before { + content: "\f6c9"; } + +.fa-sitrox:before { + content: "\e44a"; } + +.fa-discourse:before { + content: "\f393"; } + +.fa-joomla:before { + content: "\f1aa"; } + +.fa-mastodon:before { + content: "\f4f6"; } + +.fa-airbnb:before { + content: "\f834"; } + +.fa-wolf-pack-battalion:before { + content: "\f514"; } + +.fa-buy-n-large:before { + content: "\f8a6"; } + +.fa-gulp:before { + content: "\f3ae"; } + +.fa-creative-commons-sampling-plus:before { + content: "\f4f1"; } + +.fa-strava:before { + content: "\f428"; } + +.fa-ember:before { + content: "\f423"; } + +.fa-canadian-maple-leaf:before { + content: "\f785"; } + +.fa-teamspeak:before { + content: "\f4f9"; } + +.fa-pushed:before { + content: "\f3e1"; } + +.fa-wordpress-simple:before { + content: "\f411"; } + +.fa-nutritionix:before { + content: "\f3d6"; } + +.fa-wodu:before { + content: "\e088"; } + +.fa-google-pay:before { + content: "\e079"; } + +.fa-intercom:before { + content: "\f7af"; } + +.fa-zhihu:before { + content: "\f63f"; } + +.fa-korvue:before { + content: "\f42f"; } + +.fa-pix:before { + content: "\e43a"; } + +.fa-steam-symbol:before { + content: "\f3f6"; } +:root, :host { + --fa-style-family-classic: 'Font Awesome 6 Free'; + --fa-font-regular: normal 400 1em/1 'Font Awesome 6 Free'; } + +@font-face { + font-family: 'Font Awesome 6 Free'; + font-style: normal; + font-weight: 400; + font-display: block; + src: url("./webfonts/fa-regular-400.woff2") format("woff2"), url("./webfonts/fa-regular-400.ttf") format("truetype"); } + +.far, +.fa-regular { + font-weight: 400; } +:root, :host { + --fa-style-family-classic: 'Font Awesome 6 Free'; + --fa-font-solid: normal 900 1em/1 'Font Awesome 6 Free'; } + +@font-face { + font-family: 'Font Awesome 6 Free'; + font-style: normal; + font-weight: 900; + font-display: block; + src: url("./webfonts/fa-solid-900.woff2") format("woff2"), url("./webfonts/fa-solid-900.ttf") format("truetype"); } + +.fas, +.fa-solid { + font-weight: 900; } +@font-face { + font-family: 'Font Awesome 5 Brands'; + font-display: block; + font-weight: 400; + src: url("./webfonts/fa-brands-400.woff2") format("woff2"), url("./webfonts/fa-brands-400.ttf") format("truetype"); } + +@font-face { + font-family: 'Font Awesome 5 Free'; + font-display: block; + font-weight: 900; + src: url("./webfonts/fa-solid-900.woff2") format("woff2"), url("./webfonts/fa-solid-900.ttf") format("truetype"); } + +@font-face { + font-family: 'Font Awesome 5 Free'; + font-display: block; + font-weight: 400; + src: url("./webfonts/fa-regular-400.woff2") format("woff2"), url("./webfonts/fa-regular-400.ttf") format("truetype"); } +@font-face { + font-family: 'FontAwesome'; + font-display: block; + src: url("./webfonts/fa-solid-900.woff2") format("woff2"), url("./webfonts/fa-solid-900.ttf") format("truetype"); } + +@font-face { + font-family: 'FontAwesome'; + font-display: block; + src: url("./webfonts/fa-brands-400.woff2") format("woff2"), url("./webfonts/fa-brands-400.ttf") format("truetype"); } + +@font-face { + font-family: 'FontAwesome'; + font-display: block; + src: url("./webfonts/fa-regular-400.woff2") format("woff2"), url("./webfonts/fa-regular-400.ttf") format("truetype"); + unicode-range: U+F003,U+F006,U+F014,U+F016-F017,U+F01A-F01B,U+F01D,U+F022,U+F03E,U+F044,U+F046,U+F05C-F05D,U+F06E,U+F070,U+F087-F088,U+F08A,U+F094,U+F096-F097,U+F09D,U+F0A0,U+F0A2,U+F0A4-F0A7,U+F0C5,U+F0C7,U+F0E5-F0E6,U+F0EB,U+F0F6-F0F8,U+F10C,U+F114-F115,U+F118-F11A,U+F11C-F11D,U+F133,U+F147,U+F14E,U+F150-F152,U+F185-F186,U+F18E,U+F190-F192,U+F196,U+F1C1-F1C9,U+F1D9,U+F1DB,U+F1E3,U+F1EA,U+F1F7,U+F1F9,U+F20A,U+F247-F248,U+F24A,U+F24D,U+F255-F25B,U+F25D,U+F271-F274,U+F278,U+F27B,U+F28C,U+F28E,U+F29C,U+F2B5,U+F2B7,U+F2BA,U+F2BC,U+F2BE,U+F2C0-F2C1,U+F2C3,U+F2D0,U+F2D2,U+F2D4,U+F2DC; } + +@font-face { + font-family: 'FontAwesome'; + font-display: block; + src: url("./webfonts/fa-v4compatibility.woff2") format("woff2"), url("./webfonts/fa-v4compatibility.ttf") format("truetype"); + unicode-range: U+F041,U+F047,U+F065-F066,U+F07D-F07E,U+F080,U+F08B,U+F08E,U+F090,U+F09A,U+F0AC,U+F0AE,U+F0B2,U+F0D0,U+F0D6,U+F0E4,U+F0EC,U+F10A-F10B,U+F123,U+F13E,U+F148-F149,U+F14C,U+F156,U+F15E,U+F160-F161,U+F163,U+F175-F178,U+F195,U+F1F8,U+F219,U+F27A; } diff --git a/qgis-app/static/style/scss/_variables.scss b/qgis-app/static/style/scss/_variables.scss new file mode 100644 index 00000000..73d3a766 --- /dev/null +++ b/qgis-app/static/style/scss/_variables.scss @@ -0,0 +1,51 @@ +// Brand colours: +$primary1: #589632; +$primary2: #55828b; +$primary3: #395c6b; +$primary4: #EEE766; +$primary5: #93B023; +$primary6: #ee7913; +$primary1-invert: #fff; +$primary2-invert: #eeeeee; +$primary3-invert: #eeeeee; +$primary4-invert: #363636; +$primary5-invert: #eeeeee; +$primary6-invert: #fff; +$links: #3A9800; +// TODO update these to gradient colours like above +$complementary1: #93B023; +$complementary2: #ee7913; +$complementary3: #EEE766; +$complementary4: #93b023; +$complementary5: #a7cecb; +$complementary6: #589732; +$complementary7: #93B022; +$complementary8: #385C6B; +$light1: #fbfbfb; +$light2: #eeeeee; +$light3: #E7E7E7; +$dark1: #808080; +$dark2: #000; +$text-primary1: #002033; +$text-primary2: #4D6370; +$base: #002033; +// Bulma specific colours based on our branding +$black: #000; +$white: #ffffff; +$grey-dark: #444444; +$grey-darker: #363636; +$grey-light: #d4d4d4; +$primary: #a7cecb; +$danger: #ee7913; +$info: #93b023; +$success: #589632; +$warning: #EEE766; +$link: #3A9800; +// Brand fonts to override bulma font families +$heading-font: 'Trueno', sans-serif; +$text-font: 'Trueno', sans-serif; +$fancy-font: 'Sevillana', cursive; +$menuFontClass: is-size-6; + +// Radius for boxes +$corner-radius: 0px; diff --git a/qgis-app/static/style/scss/bulma/layout/_all.sass b/qgis-app/static/style/scss/bulma/layout/_all.sass new file mode 100644 index 00000000..4d1df5b7 --- /dev/null +++ b/qgis-app/static/style/scss/bulma/layout/_all.sass @@ -0,0 +1,6 @@ +/* Bulma Layout */ +@charset "utf-8" + +@import "hero" +@import "section" +@import "footer" diff --git a/qgis-app/static/style/scss/bulma/layout/footer.sass b/qgis-app/static/style/scss/bulma/layout/footer.sass new file mode 100644 index 00000000..5c693096 --- /dev/null +++ b/qgis-app/static/style/scss/bulma/layout/footer.sass @@ -0,0 +1,75 @@ +@import "../utilities/derived-variables" + +$footer-tablet-breakpoint: 1450px !default +$footer-background-color: #002033 !default +$footer-padding: 4rem 0rem 1rem 0rem !default +$footer-padding-mobile: 4rem 2rem 4rem !default + +.footer + font-weight: 400 + background-color: $footer-background-color + text-align: center + padding: $footer-padding-mobile + + .footertitle + color: #637179 + + .copyright // I can't achieve this with the bulma classes + display: none + +from($desktop) + display: unset + + +from($desktop) + padding: $footer-padding + text-align: unset + + +between($desktop, $footer-tablet-breakpoint) + padding: 4rem 2rem 1rem 2rem + + + figure.image + height: auto + margin: 0 auto + margin-bottom: 4rem + +from($desktop) + margin: unset + + button.button + padding: 40px 87.5px + font-weight: 600 + border-radius: 10px + +from($desktop) + padding: 1rem 2rem + font-size: 20px + font-weight: 400 + + + a > i + font-size: 26px + +from($desktop) + font-size: 32px + + .social-icons-links + display: grid + grid-template-columns: repeat(3, 26px) + gap: 32px + justify-content: center + margin-top: 4rem !important + +from($desktop) + display: grid + grid-template-columns: repeat(auto-fit, 32px) + gap: 34px + justify-content: unset + margin-top: unset !important + + + .privacy-links + margin-top: 4rem + a + color: #637179 + +from($desktop) + margin-top: unset + .fa-foss + background: url('../../images/mastodon.svg') center center no-repeat + width: 32px + height: 34px \ No newline at end of file diff --git a/qgis-app/static/style/scss/bulma/layout/hero.sass b/qgis-app/static/style/scss/bulma/layout/hero.sass new file mode 100644 index 00000000..af7e67c1 --- /dev/null +++ b/qgis-app/static/style/scss/bulma/layout/hero.sass @@ -0,0 +1,223 @@ +@import "../utilities/mixins" + +$hero-body-padding: 3rem 1.5rem !default +$hero-body-padding-tablet: 3rem 3rem !default +$hero-body-padding-small: 1.5rem !default +$hero-body-padding-medium: 9rem 4.5rem !default +$hero-body-padding-large: 14rem 6rem !default +$hero-body-padding-v-large: 7rem 2rem !default + +$hero-colors: $colors !default + +// Main container +.hero + align-items: stretch + display: flex + flex-direction: column + justify-content: space-between + padding-top: 100px + margin-top: -100px + .navbar + background: none + .tabs + ul + border-bottom: none + // Colors + @each $name, $pair in $hero-colors + $color: nth($pair, 1) + $color-invert: nth($pair, 2) + &.is-#{$name} + background-color: $color + color: $color-invert + a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current), + strong + color: inherit + .title + color: $color-invert + .subtitle + font-weight: 300 + color: #ffffff80 + a:not(.button), + strong + color: $color-invert + .navbar-menu + +touch + background-color: $color + .navbar-item, + .navbar-link + color: bulmaRgba($color-invert, 0.7) + a.navbar-item, + .navbar-link + &:hover, + &.is-active + background-color: bulmaDarken($color, 5%) + color: $color-invert + .tabs + a + color: $color-invert + opacity: 0.9 + &:hover + opacity: 1 + li + &.is-active a + color: $color !important + opacity: 1 + &.is-boxed, + &.is-toggle + a + color: $color-invert + &:hover + background-color: bulmaRgba($scheme-invert, 0.1) + li.is-active a + &, + &:hover + background-color: $color-invert + border-color: $color-invert + color: $color + // Modifiers + @if type-of($color) == 'color' + &.is-bold + $gradient-top-left: darken(saturate(adjust-hue($color, -10deg), 10%), 10%) + $gradient-bottom-right: lighten(saturate(adjust-hue($color, 10deg), 5%), 5%) + background-image: linear-gradient(141deg, $gradient-top-left 0%, $color 71%, $gradient-bottom-right 100%) + +mobile + .navbar-menu + background-image: linear-gradient(141deg, $gradient-top-left 0%, $color 71%, $gradient-bottom-right 100%) + // Sizes + &.is-small + .hero-body + padding: $hero-body-padding-small + // min-height: 540px + // display: flex + // align-items: center + // flex-direction: row + &.is-medium + +tablet + .hero-body + padding: $hero-body-padding-medium + &.is-large + +tablet + .hero-body + padding: $hero-body-padding-large + &.is-halfheight, + &.is-fullheight, + &.is-fullheight-with-navbar + .hero-body + align-items: center + display: flex + & > .container + flex-grow: 1 + flex-shrink: 1 + &.is-halfheight + min-height: 50vh + &.is-fullheight + min-height: 100vh + +// Components + +.hero-video + @extend %overlay + overflow: hidden + video + left: 50% + min-height: 100% + min-width: 100% + position: absolute + top: 50% + transform: translate3d(-50%, -50%, 0) + // Modifiers + &.is-transparent + opacity: 0.3 + // Responsiveness + +mobile + display: none + +.hero-buttons + margin-top: 1.5rem + // Responsiveness + +mobile + .button + display: flex + &:not(:last-child) + margin-bottom: 0.75rem + +tablet + display: flex + justify-content: center + .button:not(:last-child) + +ltr-property("margin", 1.5rem) + +// Containers + +.hero-head, +.hero-foot + flex-grow: 0 + flex-shrink: 0 + +.hero-title + font-size: 4.3em + font-weight: 800 + line-height: 1 + +mobile + font-size: 2.7rem + +.hero-subtitle + font-size: 1.5rem + +mobile + margin-top: 1rem + font-size: 1.25rem + +.hero-body + flex-grow: 1 + flex-shrink: 0 + padding: $hero-body-padding + +mobile + height: 100vh + // min-height: 840px + // display: flex + + .container + margin: auto + @media screen and (max-width: 769px) + display: flex + flex-direction: column-reverse + .image + margin: auto + .column + @media screen and (max-width: 1024px) + width: 100% + .logo + @media screen and (max-width: 1024px) + padding: 0rem + .scroll-down + margin-top: 9rem + @media screen and (min-width: 769px) + display: none !important + img + width: 100px + + +.hero-text + display: flex + flex-direction: column + justify-content: space-between + padding: 3rem 0 + +.hero + img + max-height: 380px + @media screen and (max-width: 769px) + max-height: 220px + .title:not(.is-spaced) + .subtitle + margin-top: 0px + .button + margin-right: 2.5rem + .hero-container + margin-bottom: 60px + .label + border: 2px solid #ee7914 + color: white + display: inline-block + padding: 5px 15px + font-weight: 400 + border-radius: 100px + diff --git a/qgis-app/static/style/scss/bulma/layout/section.sass b/qgis-app/static/style/scss/bulma/layout/section.sass new file mode 100644 index 00000000..9c5a9f48 --- /dev/null +++ b/qgis-app/static/style/scss/bulma/layout/section.sass @@ -0,0 +1,17 @@ +@import "../utilities/mixins" + +$section-padding: 3rem 1.5rem !default +$section-padding-desktop: 3rem 3rem !default +$section-padding-medium: 9rem 4.5rem !default +$section-padding-large: 18rem 6rem !default + +.section + padding: $section-padding + // Responsiveness + +desktop + padding: $section-padding-desktop + // Sizes + &.is-medium + padding: $section-padding-medium + &.is-large + padding: $section-padding-large diff --git a/qgis-app/static/style/scss/bulma/utilities/_all.sass b/qgis-app/static/style/scss/bulma/utilities/_all.sass new file mode 100644 index 00000000..51cf348a --- /dev/null +++ b/qgis-app/static/style/scss/bulma/utilities/_all.sass @@ -0,0 +1,9 @@ +/* Bulma Utilities */ +@charset "utf-8" + +@import "initial-variables" +@import "functions" +@import "derived-variables" +@import "mixins" +@import "controls" +@import "extends" diff --git a/qgis-app/static/style/scss/bulma/utilities/animations.sass b/qgis-app/static/style/scss/bulma/utilities/animations.sass new file mode 100644 index 00000000..1872e08c --- /dev/null +++ b/qgis-app/static/style/scss/bulma/utilities/animations.sass @@ -0,0 +1 @@ +@warn "The animations.sass file has MOVED. It is now in the /base folder. Please import sass/base/animations instead." diff --git a/qgis-app/static/style/scss/bulma/utilities/controls.sass b/qgis-app/static/style/scss/bulma/utilities/controls.sass new file mode 100644 index 00000000..4c738c7f --- /dev/null +++ b/qgis-app/static/style/scss/bulma/utilities/controls.sass @@ -0,0 +1,49 @@ +@import "derived-variables" + +$control-radius: $radius !default +$control-radius-small: $radius-small !default + +$control-border-width: 1px !default + +$control-height: 2.5em !default +$control-line-height: 1.5 !default + +$control-padding-vertical: calc(0.5em - #{$control-border-width}) !default +$control-padding-horizontal: calc(0.75em - #{$control-border-width}) !default + +=control + -moz-appearance: none + -webkit-appearance: none + align-items: center + border: $control-border-width solid transparent + border-radius: $control-radius + box-shadow: none + display: inline-flex + font-size: $size-normal + height: $control-height + justify-content: flex-start + line-height: $control-line-height + padding-bottom: $control-padding-vertical + padding-left: $control-padding-horizontal + padding-right: $control-padding-horizontal + padding-top: $control-padding-vertical + position: relative + vertical-align: top + // States + &:focus, + &.is-focused, + &:active, + &.is-active + outline: none + &[disabled], + fieldset[disabled] & + cursor: not-allowed + +// The controls sizes use mixins so they can be used at different breakpoints +=control-small + border-radius: $control-radius-small + font-size: $size-small +=control-medium + font-size: $size-medium +=control-large + font-size: $size-large diff --git a/qgis-app/static/style/scss/bulma/utilities/derived-variables.sass b/qgis-app/static/style/scss/bulma/utilities/derived-variables.sass new file mode 100644 index 00000000..3b142b0c --- /dev/null +++ b/qgis-app/static/style/scss/bulma/utilities/derived-variables.sass @@ -0,0 +1,115 @@ +@import "initial-variables" +@import "functions" + +$primary: $turquoise !default + +$info: $cyan !default +$success: $green !default +$warning: $yellow !default +$danger: $red !default + +$light: $white-ter !default +$dark: $grey-darker !default + +// Invert colors + +$orange-invert: findColorInvert($orange) !default +$yellow-invert: findColorInvert($yellow) !default +$green-invert: findColorInvert($green) !default +$turquoise-invert: findColorInvert($turquoise) !default +$cyan-invert: findColorInvert($cyan) !default +$blue-invert: findColorInvert($blue) !default +$purple-invert: findColorInvert($purple) !default +$red-invert: findColorInvert($red) !default + +$primary-invert: findColorInvert($primary) !default +$primary-light: findLightColor($primary) !default +$primary-dark: findDarkColor($primary) !default +$info-invert: findColorInvert($info) !default +$info-light: findLightColor($info) !default +$info-dark: findDarkColor($info) !default +$success-invert: findColorInvert($success) !default +$success-light: findLightColor($success) !default +$success-dark: findDarkColor($success) !default +$warning-invert: findColorInvert($warning) !default +$warning-light: findLightColor($warning) !default +$warning-dark: findDarkColor($warning) !default +$danger-invert: findColorInvert($danger) !default +$danger-light: findLightColor($danger) !default +$danger-dark: findDarkColor($danger) !default +$light-invert: findColorInvert($light) !default +$dark-invert: findColorInvert($dark) !default + +// General colors + +$scheme-main: $white !default +$scheme-main-bis: $white-bis !default +$scheme-main-ter: $white-ter !default +$scheme-invert: $black !default +$scheme-invert-bis: $black-bis !default +$scheme-invert-ter: $black-ter !default + +$background: $white-ter !default + +$border: $grey-lighter !default +$border-hover: $grey-light !default +$border-light: $grey-lightest !default +$border-light-hover: $grey-light !default + +// Text colors + +$text: $black !default +$text-invert: findColorInvert($text) !default +$text-light: $grey !default +$text-strong: $black !default + +// Code colors + +$code: darken($red, 25%) !default +$code-background: $background !default + +$pre: $text !default +$pre-background: $background !default + +// Link colors + +$link: $blue !default +$link-invert: findColorInvert($link) !default +$link-light: findLightColor($link) !default +$link-dark: findDarkColor($link) !default +$link-visited: $purple !default + +$link-hover: $grey-darker !default +$link-hover-border: $grey-light !default + +$link-focus: $grey-darker !default +$link-focus-border: $blue !default + +$link-active: $grey-darker !default +$link-active-border: $grey-dark !default + +// Typography + +$family-primary: $family-sans-serif !default +$family-secondary: $family-sans-serif !default +$family-code: $family-monospace !default + +$size-small: $size-7 !default +$size-normal: $size-6 !default +$size-medium: $size-5 !default +$size-large: $size-4 !default + +// Effects + +$shadow: 0 0.5em 1em -0.125em rgba($scheme-invert, 0.1), 0 0px 0 1px rgba($scheme-invert, 0.02) !default + +// Lists and maps +$custom-color: null !default + +$custom-shades: null !default + +$colors: mergeColorMaps(("white": ($white, $black), "black": ($black, $white), "light": ($light, $light-invert), "dark": ($dark, $dark-invert), "primary": ($primary, $primary-invert, $primary-light, $primary-dark), "link": ($link, $link-invert, $link-light, $link-dark), "info": ($info, $info-invert, $info-light, $info-dark), "success": ($success, $success-invert, $success-light, $success-dark), "warning": ($warning, $warning-invert, $warning-light, $warning-dark), "danger": ($danger, $danger-invert, $danger-light, $danger-dark)), $custom-colors) !default + +$shades: mergeColorMaps(("black-bis": $black-bis, "black-ter": $black-ter, "grey-darker": $grey-darker, "grey-dark": $grey-dark, "grey": $grey, "grey-light": $grey-light, "grey-lighter": $grey-lighter, "white-ter": $white-ter, "white-bis": $white-bis), $custom-shades) !default + +$sizes: $size-1 $size-2 $size-3 $size-4 $size-5 $size-6 $size-7 !default diff --git a/qgis-app/static/style/scss/bulma/utilities/extends.sass b/qgis-app/static/style/scss/bulma/utilities/extends.sass new file mode 100644 index 00000000..c994fc1a --- /dev/null +++ b/qgis-app/static/style/scss/bulma/utilities/extends.sass @@ -0,0 +1,25 @@ +@import "mixins" + +%control + +control + +%unselectable + +unselectable + +%arrow + +arrow + +%block + +block + +%delete + +delete + +%loader + +loader + +%overlay + +overlay + +%reset + +reset diff --git a/qgis-app/static/style/scss/bulma/utilities/functions.sass b/qgis-app/static/style/scss/bulma/utilities/functions.sass new file mode 100644 index 00000000..eeea6f21 --- /dev/null +++ b/qgis-app/static/style/scss/bulma/utilities/functions.sass @@ -0,0 +1,135 @@ +@function mergeColorMaps($bulma-colors, $custom-colors) + // We return at least Bulma's hard-coded colors + $merged-colors: $bulma-colors + + // We want a map as input + @if type-of($custom-colors) == 'map' + @each $name, $components in $custom-colors + // The color name should be a string + // and the components either a single color + // or a colors list with at least one element + @if type-of($name) == 'string' and (type-of($components) == 'list' or type-of($components) == 'color') and length($components) >= 1 + $color-base: null + $color-invert: null + $color-light: null + $color-dark: null + $value: null + + // The param can either be a single color + // or a list of 2 colors + @if type-of($components) == 'color' + $color-base: $components + $color-invert: findColorInvert($color-base) + $color-light: findLightColor($color-base) + $color-dark: findDarkColor($color-base) + @else if type-of($components) == 'list' + $color-base: nth($components, 1) + // If Invert, Light and Dark are provided + @if length($components) > 3 + $color-invert: nth($components, 2) + $color-light: nth($components, 3) + $color-dark: nth($components, 4) + // If only Invert and Light are provided + @else if length($components) > 2 + $color-invert: nth($components, 2) + $color-light: nth($components, 3) + $color-dark: findDarkColor($color-base) + // If only Invert is provided + @else + $color-invert: nth($components, 2) + $color-light: findLightColor($color-base) + $color-dark: findDarkColor($color-base) + + $value: ($color-base, $color-invert, $color-light, $color-dark) + + // We only want to merge the map if the color base is an actual color + @if type-of($color-base) == 'color' + // We merge this colors elements as map with Bulma's colors map + // (we can override them this way, no multiple definition for the same name) + // $merged-colors: map_merge($merged-colors, ($name: ($color-base, $color-invert, $color-light, $color-dark))) + $merged-colors: map_merge($merged-colors, ($name: $value)) + + @return $merged-colors + +@function powerNumber($number, $exp) + $value: 1 + @if $exp > 0 + @for $i from 1 through $exp + $value: $value * $number + @else if $exp < 0 + @for $i from 1 through -$exp + $value: divide($value, $number) + @return $value + +@function colorLuminance($color) + @if type-of($color) != 'color' + @return 0.55 + $color-rgb: ('red': red($color),'green': green($color),'blue': blue($color)) + @each $name, $value in $color-rgb + $adjusted: 0 + $value: divide($value, 255) + @if $value < 0.03928 + $value: divide($value, 12.92) + @else + $value: divide(($value + .055), 1.055) + $value: powerNumber($value, 2) + $color-rgb: map-merge($color-rgb, ($name: $value)) + @return (map-get($color-rgb, 'red') * .2126) + (map-get($color-rgb, 'green') * .7152) + (map-get($color-rgb, 'blue') * .0722) + +@function findColorInvert($color) + @if (colorLuminance($color) > 0.55) + @return rgba(#000, 0.7) + @else + @return #fff + +@function findLightColor($color, $l: 96%) + @if type-of($color) == 'color' + $l: 96% + @if lightness($color) > 96% + $l: lightness($color) + @return change-color($color, $lightness: $l) + @return $background + +@function findDarkColor($color, $base-l: 29%) + @if type-of($color) == 'color' + $luminance: colorLuminance($color) + $luminance-delta: (0.53 - $luminance) + $target-l: round($base-l + ($luminance-delta * 53)) + @return change-color($color, $lightness: max($base-l, $target-l)) + @return $text-strong + +@function bulmaRgba($color, $alpha) + @if type-of($color) != 'color' + @return $color + @return rgba($color, $alpha) + +@function bulmaDarken($color, $amount) + @if type-of($color) != 'color' + @return $color + @return darken($color, $amount) + +@function bulmaLighten($color, $amount) + @if type-of($color) != 'color' + @return $color + @return lighten($color, $amount) + +// Custom divide function by @mdo from https://github.com/twbs/bootstrap/pull/34245 +// Replaces old slash division deprecated in Dart Sass +@function divide($dividend, $divisor, $precision: 10) + $sign: if($dividend > 0 and $divisor > 0, 1, -1) + $dividend: abs($dividend) + $divisor: abs($divisor) + $quotient: 0 + $remainder: $dividend + @if $dividend == 0 + @return 0 + @if $divisor == 0 + @error "Cannot divide by 0" + @if $divisor == 1 + @return $dividend + @while $remainder >= $divisor + $quotient: $quotient + 1 + $remainder: $remainder - $divisor + @if $remainder > 0 and $precision > 0 + $remainder: divide($remainder * 10, $divisor, $precision - 1) * .1 + @return ($quotient + $remainder) * $sign diff --git a/qgis-app/static/style/scss/bulma/utilities/initial-variables.sass b/qgis-app/static/style/scss/bulma/utilities/initial-variables.sass new file mode 100644 index 00000000..a0cce846 --- /dev/null +++ b/qgis-app/static/style/scss/bulma/utilities/initial-variables.sass @@ -0,0 +1,80 @@ +// Colors + +$black: hsl(0, 0%, 4%) !default +$black-bis: hsl(0, 0%, 7%) !default +$black-ter: hsl(0, 0%, 14%) !default + +$grey-darker: hsl(0, 0%, 21%) !default +$grey-dark: hsl(0, 0%, 29%) !default +$grey: hsl(0, 0%, 48%) !default +$grey-light: hsl(0, 0%, 71%) !default +$grey-lighter: hsl(0, 0%, 86%) !default +$grey-lightest: hsl(0, 0%, 93%) !default + +$white-ter: hsl(0, 0%, 96%) !default +$white-bis: hsl(0, 0%, 98%) !default +$white: hsl(0, 0%, 100%) !default + +$orange: hsl(14, 100%, 53%) !default +$yellow: hsl(44, 100%, 77%) !default +$green: hsl(153, 53%, 53%) !default +$turquoise: hsl(171, 100%, 41%) !default +$cyan: hsl(207, 61%, 53%) !default +$blue: hsl(229, 53%, 53%) !default +$purple: hsl(271, 100%, 71%) !default +$red: hsl(348, 86%, 61%) !default + +// Typography + +$family-sans-serif: BlinkMacSystemFont, -apple-system, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", "Helvetica", "Arial", sans-serif !default +$family-monospace: monospace !default +$render-mode: optimizeLegibility !default + +$size-1: 4rem !default +$size-2: 3.5rem !default +$size-3: 2.2rem !default +$size-4: 1.6rem !default +$size-5: 1.2rem !default +$size-6: 1rem !default +$size-7: 0.75rem !default + +$weight-light: 300 !default +$weight-normal: 400 !default +$weight-medium: 500 !default +$weight-semibold: 600 !default +$weight-bold: 600 !default +$weight-extrabold: 700 !default + +// Spacing + +$block-spacing: 1.5rem !default + +// Responsiveness + +// The container horizontal gap, which acts as the offset for breakpoints +$gap: 32px !default +// 960, 1152, and 1344 have been chosen because they are divisible by both 12 and 16 +$tablet: 769px !default +// 960px container + 4rem +$desktop: 960px + (2 * $gap) !default +// 1152px container + 4rem +$widescreen: 1152px + (2 * $gap) !default +$widescreen-enabled: true !default +// 1344px container + 4rem +$fullhd: 1344px + (2 * $gap) !default +$fullhd-enabled: true !default +$breakpoints: ("mobile": ("until": $tablet), "tablet": ("from": $tablet), "tablet-only": ("from": $tablet, "until": $desktop), "touch": ("from": $desktop), "desktop": ("from": $desktop), "desktop-only": ("from": $desktop, "until": $widescreen), "until-widescreen": ("until": $widescreen), "widescreen": ("from": $widescreen), "widescreen-only": ("from": $widescreen, "until": $fullhd), "until-fullhd": ("until": $fullhd), "fullhd": ("from": $fullhd)) !default + +// Miscellaneous + +$easing: ease-out !default +$radius-small: 2px !default +$radius: 4px !default +$radius-large: 6px !default +$radius-rounded: 9999px !default +$speed: 86ms !default + +// Flags + +$variable-columns: true !default +$rtl: false !default diff --git a/qgis-app/static/style/scss/bulma/utilities/mixins.sass b/qgis-app/static/style/scss/bulma/utilities/mixins.sass new file mode 100644 index 00000000..10cbae30 --- /dev/null +++ b/qgis-app/static/style/scss/bulma/utilities/mixins.sass @@ -0,0 +1,303 @@ +@import "derived-variables" + +=clearfix + &::after + clear: both + content: " " + display: table + +=center($width, $height: 0) + position: absolute + @if $height != 0 + left: calc(50% - (#{$width} * 0.5)) + top: calc(50% - (#{$height} * 0.5)) + @else + left: calc(50% - (#{$width} * 0.5)) + top: calc(50% - (#{$width} * 0.5)) + +=fa($size, $dimensions) + display: inline-block + font-size: $size + height: $dimensions + line-height: $dimensions + text-align: center + vertical-align: top + width: $dimensions + +=hamburger($dimensions) + -moz-appearance: none + -webkit-appearance: none + appearance: none + background: none + border: none + cursor: pointer + display: block + height: $dimensions + position: relative + width: $dimensions + span + background-color: currentColor + display: block + height: 1px + left: calc(50% - 8px) + position: absolute + transform-origin: center + transition-duration: $speed + transition-property: background-color, opacity, transform + transition-timing-function: $easing + width: 16px + &:nth-child(1) + top: calc(50% - 6px) + &:nth-child(2) + top: calc(50% - 1px) + &:nth-child(3) + top: calc(50% + 4px) + &:hover + background-color: bulmaRgba(black, 0.05) + // Modifers + &.is-active + span + &:nth-child(1) + transform: translateY(5px) rotate(45deg) + &:nth-child(2) + opacity: 0 + &:nth-child(3) + transform: translateY(-5px) rotate(-45deg) + +=overflow-touch + -webkit-overflow-scrolling: touch + +=placeholder + $placeholders: ':-moz' ':-webkit-input' '-moz' '-ms-input' + @each $placeholder in $placeholders + &:#{$placeholder}-placeholder + @content + +=reset + -moz-appearance: none + -webkit-appearance: none + appearance: none + background: none + border: none + color: currentColor + font-family: inherit + font-size: 1em + margin: 0 + padding: 0 + +// Responsiveness + +=from($device) + @media screen and (min-width: $device) + @content + +=until($device) + @media screen and (max-width: $device - 1px) + @content + +=between($from, $until) + @media screen and (min-width: $from) and (max-width: $until - 1px) + @content + +=mobile + @media screen and (max-width: $tablet - 1px) + @content + +=tablet + @media screen and (min-width: $tablet), print + @content + +=tablet-only + @media screen and (min-width: $tablet) and (max-width: $desktop - 1px) + @content + +=touch + @media screen and (max-width: $desktop - 1px) + @content + +=desktop + @media screen and (min-width: $desktop) + @content + +=desktop-only + @if $widescreen-enabled + @media screen and (min-width: $desktop) and (max-width: $widescreen - 1px) + @content + +=until-widescreen + @if $widescreen-enabled + @media screen and (max-width: $widescreen - 1px) + @content + +=widescreen + @if $widescreen-enabled + @media screen and (min-width: $widescreen) + @content + +=widescreen-only + @if $widescreen-enabled and $fullhd-enabled + @media screen and (min-width: $widescreen) and (max-width: $fullhd - 1px) + @content + +=until-fullhd + @if $fullhd-enabled + @media screen and (max-width: $fullhd - 1px) + @content + +=fullhd + @if $fullhd-enabled + @media screen and (min-width: $fullhd) + @content + +=breakpoint($name) + $breakpoint: map-get($breakpoints, $name) + @if $breakpoint + $from: map-get($breakpoint, "from") + $until: map-get($breakpoint, "until") + @if $from and $until + +between($from, $until) + @content + @else if $from + +from($from) + @content + @else if $until + +until($until) + @content + +=ltr + @if not $rtl + @content + +=rtl + @if $rtl + @content + +=ltr-property($property, $spacing, $right: true) + $normal: if($right, "right", "left") + $opposite: if($right, "left", "right") + @if $rtl + #{$property}-#{$opposite}: $spacing + @else + #{$property}-#{$normal}: $spacing + +=ltr-position($spacing, $right: true) + $normal: if($right, "right", "left") + $opposite: if($right, "left", "right") + @if $rtl + #{$opposite}: $spacing + @else + #{$normal}: $spacing + +// Placeholders + +=unselectable + -webkit-touch-callout: none + -webkit-user-select: none + -moz-user-select: none + -ms-user-select: none + user-select: none + +=arrow($color: transparent) + border: 3px solid $color + border-radius: 2px + border-right: 0 + border-top: 0 + content: " " + display: block + height: 0.625em + margin-top: -0.4375em + pointer-events: none + position: absolute + top: 50% + transform: rotate(-45deg) + transform-origin: center + width: 0.625em + +=block($spacing: $block-spacing) + &:not(:last-child) + margin-bottom: $spacing + +=delete + +unselectable + -moz-appearance: none + -webkit-appearance: none + background-color: bulmaRgba($scheme-invert, 0.2) + border: none + border-radius: $radius-rounded + cursor: pointer + pointer-events: auto + display: inline-block + flex-grow: 0 + flex-shrink: 0 + font-size: 0 + height: 20px + max-height: 20px + max-width: 20px + min-height: 20px + min-width: 20px + outline: none + position: relative + vertical-align: top + width: 20px + &::before, + &::after + background-color: $scheme-main + content: "" + display: block + left: 50% + position: absolute + top: 50% + transform: translateX(-50%) translateY(-50%) rotate(45deg) + transform-origin: center center + &::before + height: 2px + width: 50% + &::after + height: 50% + width: 2px + &:hover, + &:focus + background-color: bulmaRgba($scheme-invert, 0.3) + &:active + background-color: bulmaRgba($scheme-invert, 0.4) + // Sizes + &.is-small + height: 16px + max-height: 16px + max-width: 16px + min-height: 16px + min-width: 16px + width: 16px + &.is-medium + height: 24px + max-height: 24px + max-width: 24px + min-height: 24px + min-width: 24px + width: 24px + &.is-large + height: 32px + max-height: 32px + max-width: 32px + min-height: 32px + min-width: 32px + width: 32px + +=loader + animation: spinAround 500ms infinite linear + border: 2px solid $grey-lighter + border-radius: $radius-rounded + border-right-color: transparent + border-top-color: transparent + content: "" + display: block + height: 1em + position: relative + width: 1em + +=overlay($offset: 0) + bottom: $offset + left: $offset + position: absolute + right: $offset + top: $offset diff --git a/qgis-app/static/style/scss/custom.bulma.scss b/qgis-app/static/style/scss/custom.bulma.scss new file mode 100644 index 00000000..fddb55a8 --- /dev/null +++ b/qgis-app/static/style/scss/custom.bulma.scss @@ -0,0 +1,189 @@ +@charset "utf-8"; + +@import "variables"; + +@font-face { + font-family: "Montserrat"; + src: url("../webfonts/Montserrat.ttf") format("truetype"); +} + +@font-face { + font-family: "Work Sans"; + src: url("../webfonts/worksans.woff2") format("opentype"); +} + +@font-face { + font-family: "'Sevillana', cursive"; + src: url("../webfonts/sevillana.woff2") format("opentype"); +} + + +@font-face { + font-family: "Trueno"; + src: url("../webfonts/TruenoLt.otf") format("opentype"); + font-weight: 300; +} + +@font-face { + font-family: "Trueno"; + src: url("../webfonts/TruenoRg.otf") format("opentype"); + font-weight: 400; +} + +@font-face { + font-family: "Trueno"; + src: url("../webfonts/TruenoSBd.otf") format("opentype"); + font-weight: 500; +} + +@font-face { + font-family: "Trueno"; + src: url("../webfonts/TruenoBd.otf") format("opentype"); + font-weight: 600; +} + +@font-face { + font-family: "Trueno"; + src: url("../webfonts/TruenoUltBlk.otf") format("opentype"); + font-weight: 700; +} + +// Set your brand colors +$primary1: $primary1; +$primary1-invert: $primary1-invert; +$primary2: $primary2; +$primary2-invert: $primary2-invert; +$primary3: $primary3; +$primary3-invert: $primary3-invert; +$primary4: $primary4; +$primary4-invert: $primary4-invert; +$primary5: $primary5; +$primary5-invert: $primary5-invert; +$complementary1: $complementary1; +$complementary1-invert: findColorInvert($complementary1); +$complementary2: $complementary2; +$complementary2-invert: findColorInvert($complementary2); +$complementary3: $complementary3; +$complementary3-invert: findColorInvert($complementary3); +$complementary4: $complementary4; +$complementary4-invert: findColorInvert($complementary4); +$complementary5: $light1; +$complementary5-invert: findColorInvert($complementary5); +$complementary6: $complementary6; +$complementary7: $complementary7; +$complementary7-invert: findColorInvert($complementary7); +$complementary8: $complementary8; +$complementary8-invert: findColorInvert($complementary8); +$light1: $light1; +$light1-invert: findColorInvert($light1); +$light2: $light2; +$light2-invert: findColorInvert($light2); +$light3: $light3; +$light3-invert: findColorInvert($light3); +$dark1: $dark1; +$dark1-invert: findColorInvert($dark1); +$dark2: $dark2; +$dark2-invert: findColorInvert($dark2); +$white-ter: hsl(0, 0%, 96%) !default; + +// The custom colours are set up so that in +// addition to using bulma's is-primary, is-info +// colours, you can also use e.g. is-primary1, is-primary2 etc. +// see https://stackoverflow.com/a/68419170 +// also dont indent the line starts below, you will break sass compilation +$custom-colors: ( + "primary1": ($primary1, $primary1-invert), + "primary2": ($primary2, $primary2-invert), + "primary3": ($primary3, $primary3-invert), + "primary4": ($primary4, $primary4-invert), + "primary5": ($primary5, $primary5-invert), + "complementary1" : ($complementary1, $complementary1-invert), + "complementary2" : ($complementary2, $complementary2-invert), + "complementary3" : ($complementary3, $complementary3-invert), + "complementary4" : ($complementary4, $complementary4-invert), + "complementary5" : ($complementary5, $complementary5-invert), + "complementary6" : ($complementary6, $complementary5-invert), + "complementary7" : ($complementary7, $complementary7-invert), + "complementary8" : ($complementary8, $complementary8-invert), + "light1": ($light1, $light1-invert), + "light2": ($light2, $light2-invert), + "light3": ($light3, $light3-invert), + "dark1": ($dark1, $dark1-invert), + "dark2": ($dark2, $dark2-invert) +); + +// Brand fonts +$heading-font: $heading-font; +$text-font: $text-font; +//not a brand font, just for testing... +$fancy-font: $fancy-font; + +// Update Bulma's global variables +$family-sans-serif: text-font; +$family-primary: $text-font; +$title-family: $heading-font; + +// Colors +$grey-dark: $grey-dark; +$grey-darker: $grey-darker; +$grey-light: $grey-light; +$primary: $primary; +$danger: $danger; +$info: $info; +$success: $success; +$warning: $warning; +$link: $link; + +// Text colors +$text-strong: $grey-darker !default; + +$widescreen-enabled: false; +//$fullhd-enabled: false; + +// Update some of Bulma's component variables +$background: $white-ter; +$body-background-color: $light1; +$control-border-width: 1px; +$input-border-color: light1; +$input-shadow: none; +$box-shadow: none; +$box-radius: $corner-radius; +$box-background-color: $complementary1; +$box-color: $text-primary1; +$box-link-hover-shadow: none; + +$menu-item-color: $dark2; +// Not really needed since we will hide it +$navbar-dropdown-arrow: $dark2; +$navbar-dropdown-arrow: none; +$navbar-item-hover-color: $dark2; +$navbar-background-color: $grey-light; +$navbar-dropdown-background-color: $grey-light; +// Add more spacing between menu items +.navbar-item, +.navbar-link { + padding: 0.5rem 2rem; +} +// The primary navbar needs to be over the secondary +// so the dropdowns don't get obscured. TS. +$navbar-fixed-z: 1000 !default; + +// Allow navbar logo to be larger than navbar specified height +$navbar-item-img-max-height: 57px;; + +/* We are going for a more boxy look on our site */ +$notification-radius: $corner-radius; + + + +@import "../../../node_modules/bulma/sass/utilities/_all.sass"; +@import "../../../node_modules/bulma/sass/base/_all.sass"; +@import "../../../node_modules/bulma/sass/elements/_all.sass"; +@import "../../../node_modules/bulma/sass/components/_all.sass"; +@import "../../../node_modules/bulma/sass/grid/_all.sass"; +// @import "../../../node_modules/bulma/sass/layout/_all.sass"; +@import "../../../node_modules/bulma/sass/helpers/_all.sass"; +@import "../../../node_modules/bulma/sass/form/_all.sass"; + +// @import "footer.sass" +@import "bulma/layout/_all.sass" \ No newline at end of file diff --git a/qgis-app/static/style/scss/style.scss b/qgis-app/static/style/scss/style.scss new file mode 100644 index 00000000..0aae5b3e --- /dev/null +++ b/qgis-app/static/style/scss/style.scss @@ -0,0 +1,273 @@ +@charset "utf-8"; +@import "custom.bulma.scss"; + +body { + color: $text-primary1; + font-weight: 300; +} + +mark { + background: linear-gradient(-100deg, #93b023, #f0e64a 95%); + border-radius: 0.25em; + padding: 0.05rem 0.25em; +} + +.title { + color: inherit; + font-weight: 500; +} + +.table { + color: inherit; +} + +.label { + color: inherit; + display: block; + font-size: 1rem; + font-weight: 500; +} + +strong { + font-weight: 600; + color: inherit; +} + +th { + font-weight: 500; +} + +p { + color: $text-primary2; +} + +.input, +.textarea, +.select select { + color: $text-primary2; +} + +@media screen and (max-width: 768px) { + .hero-body { + height: unset; + } +} + +.navbar { + background-color: unset; +} +.navbar-brand, +.navbar-tabs { + min-height: unset; + a:hover { + color: $primary1-invert; + } +} + +.gradient-row, +.box-section-footer, +.news-container { + background: linear-gradient(to right, #589732, #93b022); + height: 3em; + display: flex; + align-items: center; + justify-content: flex-end; +} + +.news-text { + padding-right: 3em; + font-size: 14px; + padding-top: 0.3em; + padding-bottom: 0.3em; + letter-spacing: 1px; + font-weight: 500; +} + +.img-grid-1-columns-0 .img-grid-1-column-0 { + display: flex; + justify-content: flex-end; + align-items: end; + padding-right: 0; +} +.img-grid-1-columns-0 .img-grid-1-column-0 img { + height: 80%; +} + +.img-grid-1-columns-0 .img-grid-1-column-1 { + align-items: end; + display: flex; +} + +.img-grid-1-columns-1 { + margin-top: -1.5em; + padding-right: 50px; +} +.img-grid-1-columns-1 .img-grid-1-column-2 { + display: flex; + justify-content: flex-end; + padding-right: 0; +} +.img-grid-1-columns-1 .img-grid-1-column-2 img { + height: 60%; +} + +.box-section-footer { + margin-top: -1em; +} + +.hero-title { + font-size: 4.5vw; +} + +.mkdown strong, +.mkdown h1, +.mkdown h2, +.mkdown h3 { + color: inherit; +} + +.mkdown li { + margin: 20px 0; +} + +.bottom-bar { + border-bottom: 1.5em solid #ee7913; +} + +.pb-7 { + padding-bottom: 4rem !important; +} + +.main-page-content { + background-position: 50% !important; + background-size: cover !important; + height: 100vh; +} + +.navbar-burger { + color: $primary1-invert; +} + +@media screen and (max-width: 1023px) { + .navbar-menu { + background-color: #1d2027; + } + .navbar-menu.is-active { + display: block; + position: fixed; + } +} + +#menu > div > a > span:hover { + border-bottom: solid 1px; + color: black; +} + +a.is-active > span { + border-bottom: solid $primary1-invert 1px; +} + +.login-container { + box-shadow: 0 8px 12px 0 rgba(60, 60, 60, 0.75); + border-radius: 20px; + max-width: 500px; +} + +.login-container hr { + background-color: #dddddd; +} + +.box-container hr { + background-color: #dddddd; +} + +.box-container { + // box-shadow: 0 4px 6px 0 rgba(108, 108, 108, 0.2); + border-radius: 10px; +} + +.is-semi-transparent { + opacity: 0.9; +} + +.form-error { + color: red; + font-size: 10pt; + font-weight: 500; +} + +.gradient-row { + background: #002033; + height: 3em; + display: flex; + align-items: center; + justify-content: flex-end; +} +.context-container { + @extend .gradient-row; + position: fixed; + width: 100%; + top: 4rem; + z-index: 100; + transition: top 0.3s; + + input { + background: #223745; + border: none; + color: $black; + border-radius: 5px !important; + + &::placeholder { + color: $dark1; + } + + &:not(:placeholder-shown):not(:focus) { + color: $white; + } + + &:focus { + background: $white; + } + } + + .navbar-item, + .navbar-link { + color: white; + // border-radius: 5px; + } + + a.navbar-item.is-active, + .navbar-link.is-active { + color: white; + background-color: unset; + &:hover { + color: #002033; + background-color: #fafafa; + } + } + + a.navbar-item:focus, + a.navbar-item:focus-within, + a.navbar-item:hover, + a.navbar-item.is-active, + .navbar-link:focus, + .navbar-link:focus-within, + .navbar-link:hover, + .navbar-link.is-active { + color: #002033 !important; + background-color: #fafafa; + } + + .navbar-item:hover { + .navbar-link { + color: #002033 !important; + } + } + + @media screen and (min-width: 1024px) { + .navbar-dropdown { + background: #002033; + border-top: none; + } + } +} diff --git a/qgis-app/static/style/webfonts/Montserrat.ttf b/qgis-app/static/style/webfonts/Montserrat.ttf new file mode 100644 index 00000000..656db666 Binary files /dev/null and b/qgis-app/static/style/webfonts/Montserrat.ttf differ diff --git a/qgis-app/static/style/webfonts/TruenoBd.otf b/qgis-app/static/style/webfonts/TruenoBd.otf new file mode 100644 index 00000000..fd2c00d3 Binary files /dev/null and b/qgis-app/static/style/webfonts/TruenoBd.otf differ diff --git a/qgis-app/static/style/webfonts/TruenoBd.woff b/qgis-app/static/style/webfonts/TruenoBd.woff new file mode 100644 index 00000000..22090457 Binary files /dev/null and b/qgis-app/static/style/webfonts/TruenoBd.woff differ diff --git a/qgis-app/static/style/webfonts/TruenoExBd.woff b/qgis-app/static/style/webfonts/TruenoExBd.woff new file mode 100644 index 00000000..1f11ea1f Binary files /dev/null and b/qgis-app/static/style/webfonts/TruenoExBd.woff differ diff --git a/qgis-app/static/style/webfonts/TruenoLt.otf b/qgis-app/static/style/webfonts/TruenoLt.otf new file mode 100644 index 00000000..a5ddf781 Binary files /dev/null and b/qgis-app/static/style/webfonts/TruenoLt.otf differ diff --git a/qgis-app/static/style/webfonts/TruenoLt.woff b/qgis-app/static/style/webfonts/TruenoLt.woff new file mode 100644 index 00000000..1d7f3477 Binary files /dev/null and b/qgis-app/static/style/webfonts/TruenoLt.woff differ diff --git a/qgis-app/static/style/webfonts/TruenoRg.otf b/qgis-app/static/style/webfonts/TruenoRg.otf new file mode 100644 index 00000000..82a39527 Binary files /dev/null and b/qgis-app/static/style/webfonts/TruenoRg.otf differ diff --git a/qgis-app/static/style/webfonts/TruenoRg.woff b/qgis-app/static/style/webfonts/TruenoRg.woff new file mode 100644 index 00000000..a4813fde Binary files /dev/null and b/qgis-app/static/style/webfonts/TruenoRg.woff differ diff --git a/qgis-app/static/style/webfonts/TruenoSBd.otf b/qgis-app/static/style/webfonts/TruenoSBd.otf new file mode 100644 index 00000000..e858cde4 Binary files /dev/null and b/qgis-app/static/style/webfonts/TruenoSBd.otf differ diff --git a/qgis-app/static/style/webfonts/TruenoUltBlk.otf b/qgis-app/static/style/webfonts/TruenoUltBlk.otf new file mode 100644 index 00000000..b507d5e2 Binary files /dev/null and b/qgis-app/static/style/webfonts/TruenoUltBlk.otf differ diff --git a/qgis-app/static/style/webfonts/TruenoUltBlk.woff b/qgis-app/static/style/webfonts/TruenoUltBlk.woff new file mode 100644 index 00000000..89a87654 Binary files /dev/null and b/qgis-app/static/style/webfonts/TruenoUltBlk.woff differ diff --git a/qgis-app/static/style/webfonts/TruenoUltLt.woff b/qgis-app/static/style/webfonts/TruenoUltLt.woff new file mode 100644 index 00000000..b3c849fc Binary files /dev/null and b/qgis-app/static/style/webfonts/TruenoUltLt.woff differ diff --git a/qgis-app/static/style/webfonts/fa-brands-400.ttf b/qgis-app/static/style/webfonts/fa-brands-400.ttf new file mode 100644 index 00000000..502f3621 Binary files /dev/null and b/qgis-app/static/style/webfonts/fa-brands-400.ttf differ diff --git a/qgis-app/static/style/webfonts/fa-brands-400.woff2 b/qgis-app/static/style/webfonts/fa-brands-400.woff2 new file mode 100644 index 00000000..d801b51f Binary files /dev/null and b/qgis-app/static/style/webfonts/fa-brands-400.woff2 differ diff --git a/qgis-app/static/style/webfonts/fa-regular-400.ttf b/qgis-app/static/style/webfonts/fa-regular-400.ttf new file mode 100644 index 00000000..e0abe271 Binary files /dev/null and b/qgis-app/static/style/webfonts/fa-regular-400.ttf differ diff --git a/qgis-app/static/style/webfonts/fa-regular-400.woff2 b/qgis-app/static/style/webfonts/fa-regular-400.woff2 new file mode 100644 index 00000000..d736e4b2 Binary files /dev/null and b/qgis-app/static/style/webfonts/fa-regular-400.woff2 differ diff --git a/qgis-app/static/style/webfonts/fa-solid-900.ttf b/qgis-app/static/style/webfonts/fa-solid-900.ttf new file mode 100644 index 00000000..13c94897 Binary files /dev/null and b/qgis-app/static/style/webfonts/fa-solid-900.ttf differ diff --git a/qgis-app/static/style/webfonts/fa-solid-900.woff2 b/qgis-app/static/style/webfonts/fa-solid-900.woff2 new file mode 100644 index 00000000..3516fdbe Binary files /dev/null and b/qgis-app/static/style/webfonts/fa-solid-900.woff2 differ diff --git a/qgis-app/static/style/webfonts/fa-v4compatibility.ttf b/qgis-app/static/style/webfonts/fa-v4compatibility.ttf new file mode 100644 index 00000000..dc298194 Binary files /dev/null and b/qgis-app/static/style/webfonts/fa-v4compatibility.ttf differ diff --git a/qgis-app/static/style/webfonts/fa-v4compatibility.woff2 b/qgis-app/static/style/webfonts/fa-v4compatibility.woff2 new file mode 100644 index 00000000..28d46b15 Binary files /dev/null and b/qgis-app/static/style/webfonts/fa-v4compatibility.woff2 differ diff --git a/qgis-app/static/style/webfonts/sevillana.woff2 b/qgis-app/static/style/webfonts/sevillana.woff2 new file mode 100644 index 00000000..b3f8a9dd Binary files /dev/null and b/qgis-app/static/style/webfonts/sevillana.woff2 differ diff --git a/qgis-app/static/style/webfonts/worksans.woff2 b/qgis-app/static/style/webfonts/worksans.woff2 new file mode 100644 index 00000000..a006c548 Binary files /dev/null and b/qgis-app/static/style/webfonts/worksans.woff2 differ diff --git a/qgis-app/styles/file_handler.py b/qgis-app/styles/file_handler.py index e35e855f..42b3c2de 100644 --- a/qgis-app/styles/file_handler.py +++ b/qgis-app/styles/file_handler.py @@ -4,7 +4,7 @@ import xml.etree.ElementTree as ET from django.core.exceptions import ValidationError -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ def _check_name_type_attribute(element): diff --git a/qgis-app/styles/forms.py b/qgis-app/styles/forms.py index 3fe24047..a23655ff 100644 --- a/qgis-app/styles/forms.py +++ b/qgis-app/styles/forms.py @@ -1,6 +1,6 @@ from django import forms from django.core.exceptions import ValidationError -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from styles.file_handler import validator from styles.models import Style diff --git a/qgis-app/styles/migrations/0015_alter_review_reviewer.py b/qgis-app/styles/migrations/0015_alter_review_reviewer.py new file mode 100644 index 00000000..8c6b0558 --- /dev/null +++ b/qgis-app/styles/migrations/0015_alter_review_reviewer.py @@ -0,0 +1,21 @@ +# Generated by Django 4.2.11 on 2024-03-22 00:02 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('styles', '0014_remove_uuid_null'), + ] + + operations = [ + migrations.AlterField( + model_name='review', + name='reviewer', + field=models.ForeignKey(help_text='The user who reviewed this %(app_label)s.', on_delete=django.db.models.deletion.CASCADE, related_name='%(app_label)s_%(class)s_related', to=settings.AUTH_USER_MODEL, verbose_name='Reviewed by'), + ), + ] diff --git a/qgis-app/styles/models.py b/qgis-app/styles/models.py index f1e4f753..52e62f51 100644 --- a/qgis-app/styles/models.py +++ b/qgis-app/styles/models.py @@ -3,7 +3,7 @@ from django.core.validators import FileExtensionValidator from django.db import models from django.urls import reverse -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ STYLES_STORAGE_PATH = getattr(settings, "PLUGINS_STORAGE_PATH", "styles/%Y") diff --git a/qgis-app/styles/views.py b/qgis-app/styles/views.py index 84cfb874..db156da4 100644 --- a/qgis-app/styles/views.py +++ b/qgis-app/styles/views.py @@ -19,7 +19,7 @@ from django.http import HttpResponse, HttpResponseRedirect from django.urls import reverse, reverse_lazy from django.utils.crypto import get_random_string -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from django.views.decorators.cache import never_cache from styles.file_handler import read_xml_style from styles.forms import UpdateForm, UploadForm diff --git a/qgis-app/templates/base.html b/qgis-app/templates/base.html index a702adc1..fb59ca33 100644 --- a/qgis-app/templates/base.html +++ b/qgis-app/templates/base.html @@ -1,6 +1,10 @@ {% load i18n simplemenu_tags static %} {% load resources_custom_tags %} {% load matomo_tags %} + +{% load render_bundle from webpack_loader %} +{% render_bundle 'main' %} + @@ -10,11 +14,11 @@ - - - + {% comment %} {% endcomment %} + {% comment %} {% endcomment %} + - + {% block extracss %}{% endblock %} @@ -31,66 +35,22 @@ By continuing to visit this site you agree to our use of cookies." data-close-text="Got it!"> + + {% block extrajs %}{% endblock %}
- {% block navigation %} - - {% endblock %} +
+ + {% comment %} The location-prefix will be updated when the QGIS-Hugo site is migrated to qgis.org {% endcomment %} + {% include 'layouts/header.html' %} +
@@ -156,7 +116,8 @@
Silver Sponsors
-
+ {% include 'layouts/footer.html' %} +