diff --git a/.dockerignore b/.dockerignore index fd59b07ac0..6aff476666 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,6 +1,7 @@ db logs/* *.tar.gz +.venv src/files/articles src/files/journals src/files/issues @@ -8,3 +9,4 @@ src/files/press src/files/temp src/files/xsl +tallgrass/ diff --git a/.github/workflows/deploy-docker.yml b/.github/workflows/deploy-docker.yml new file mode 100644 index 0000000000..808a8287c1 --- /dev/null +++ b/.github/workflows/deploy-docker.yml @@ -0,0 +1,34 @@ +# Docker image build for hub.docker.com +name: Docker images + +# Run this Build for all pushes to 'main' or tagged releases. +# Also run for PRs to ensure PR doesn't break Docker build process +# NOTE: uses "reusable-docker-build.yml" to actually build each of the Docker images. +on: + push: + branches: + - master + tags: + - 'v**' + pull_request: + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +permissions: + contents: read # to fetch code (actions/checkout) + +jobs: + ################################################################### + # Build/Push the 'kstatelibraries/janeway' image. + ################################################################### + tools: + # Ensure this job never runs on forked repos. It's only executed for 'kstatelibraries/janeway' + if: github.repository == 'kstatelibraries/janeway' + uses: kstatelibraries/.github/.github/workflows/reusable-docker-build.yml@main + with: + build_id: janeway + image_name: kstatelibraries/janeway + dockerfile_path: ./dockerfiles/Dockerfile.ksul + secrets: + DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} + DOCKER_ACCESS_TOKEN: ${{ secrets.DOCKER_ACCESS_TOKEN }} diff --git a/.gitignore b/.gitignore index 3223115ac4..3853b3eff0 100755 --- a/.gitignore +++ b/.gitignore @@ -80,7 +80,6 @@ src/core/settings.py /src/files/sitemaps/** mydatabase node_modules/ -src/plugins/* src/media/profile_images/* # PyCharm @@ -150,3 +149,6 @@ src/utils/management/commands/test_command.py /src/static/admin/hypothesis/** jenkins/test-results jenkins/test_results + +# App Data +/tallgrass/ \ No newline at end of file diff --git a/Caddyfile b/Caddyfile new file mode 100644 index 0000000000..3a0e7b0be6 --- /dev/null +++ b/Caddyfile @@ -0,0 +1,18 @@ +:80 { + respond /health 200 + root * /srv/janeway-tallgrass + file_server + + @notStatic { + not path /static/* + not path /media/* + not path /files/* + } + + reverse_proxy @notStatic janeway-tallgrass-web:8000 { + header_up Host {host} + header_up X-Forwarded-For {remote} + header_up X-Forwarded-Proto {scheme} + } +} + diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 0000000000..2b4f46a67d --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,56 @@ +--- +services: + caddy: + restart: unless-stopped + image: caddy:2 + ports: + - "80:80" + volumes: + - ./Caddyfile:/etc/caddy/Caddyfile:ro + - static-tallgrass:/srv/janeway-tallgrass/static:ro + - ./tallgrass/data/janeway/media:/srv/janeway-tallgrass/media + - ./tallgrass/data/janeway/files:/srv/janeway-tallgrass/files + depends_on: + - janeway-tallgrass-web + + janeway-tallgrass-web: + image: kstatelibraries/janeway:test + build: + context: . + dockerfile: dockerfiles/Dockerfile.ksul + restart: unless-stopped + volumes: + - ./tallgrass/data/logs:/logs + - ./tallgrass/settings.py:/janeway/src/core/settings.py + - ./tallgrass/data/janeway/media:/janeway/src/media + - ./tallgrass/data/janeway/files:/janeway/src/files + - static-tallgrass:/janeway/src/collected-static + environment: + - DB_VENDOR=postgres + - DB_HOST=janeway-tallgrass-postgres + - DB_PORT=5432 + - DB_PASSWORD=${DB_PASSWORD} + - DB_USER=janeway-web + - DB_NAME=janeway-tallgrass + - PYTHONDONTWRITEBYTECODE=yes + - NOSE_INCLUDE_EXE=1 + - JANEWAY_EMAIL_BACKEND + - JANEWAY_EMAIL_HOST + - JANEWAY_EMAIL_PORT + - JANEWAY_EMAIL_USE_TLS + - APP_BUILT=1 # If janeway has already been installed this should be true, otherwise it should be left unset or set to 0 + cap_add: + - SYS_PTRACE + + janeway-tallgrass-postgres: + restart: unless-stopped + image: postgres:15 + volumes: + - ./tallgrass/data/postgres-data:/var/lib/postgresql/data + environment: + - POSTGRES_PASSWORD=${DB_PASSWORD} + - "POSTGRES_USER=janeway-web" + - "POSTGRES_DB=janeway-tallgrass" + +volumes: + static-tallgrass: diff --git a/dockerfiles/Dockerfile.ksul b/dockerfiles/Dockerfile.ksul new file mode 100644 index 0000000000..7e8ec31b41 --- /dev/null +++ b/dockerfiles/Dockerfile.ksul @@ -0,0 +1,54 @@ +FROM python:3.12-slim AS build +RUN apt-get update && \ + apt-get install -y \ + build-essential \ + git \ + gcc \ + g++ \ + libxml2-dev \ + libmagic-dev \ + libxslt1-dev \ + python3-dev \ + zlib1g-dev \ + libffi-dev \ + libssl-dev \ + libjpeg-dev \ + libsass-dev \ + libsass1 + +COPY requirements.txt . +RUN pip install --prefix=/install -r requirements.txt + +FROM ghcr.io/benoitc/gunicorn:24.1.0 +USER root + +RUN apt-get update && \ + apt-get install -y \ + git \ + libmagic-dev \ + libsass1 + +WORKDIR /janeway + +#COPY requirements.txt . +#RUN pip install --no-cache-dir -r requirements.txt +COPY --from=build /install /usr/local + +#RUN find "/vol/janeway/src/plugins/" -print -iname "*requirements.txt" -exec pip3 install -r {} --src /tmp/src \; +#RUN if [ -n "$(ls -A ./lib)" ]; then pip3 install -e lib/*; fi + +COPY --chown=gunicorn:gunicorn . /janeway + +# Copy entrypoint script +COPY --chown=gunicorn:gunicorn dockerfiles/entrypoint.sh /usr/local/bin/docker-entrypoint.sh +RUN chmod +x /usr/local/bin/docker-entrypoint.sh + + +RUN cp src/core/janeway_global_settings.py src/core/settings.py +USER gunicorn + +EXPOSE 8000 +STOPSIGNAL SIGINT +#ENTRYPOINT ["bash"] +ENTRYPOINT ["docker-entrypoint.sh"] +CMD ["gunicorn", "core.wsgi:application", "--bind", "0.0.0.0:8000", "--workers", "3", "--chdir", "/janeway/src"] diff --git a/dockerfiles/entrypoint.sh b/dockerfiles/entrypoint.sh new file mode 100644 index 0000000000..11c9b9a97d --- /dev/null +++ b/dockerfiles/entrypoint.sh @@ -0,0 +1,102 @@ +#!/bin/bash +set -e + +install_plugins() { + local plugin + for plugin in ${JANEWAY_PLUGINS:-customstyling imports books bepress}; do + python /janeway/src/manage.py install_plugins "$plugin" + done +} + +ensure_plugins() { + local customstyling_dir="/janeway/src/plugins/customstyling" + local imports_plugin_dir="/janeway/src/plugins/imports" + local books_dir="/janeway/src/plugins/books" + local bepress_dir="/janeway/src/plugins/bepress" + + if [ -d "$customstyling_dir" ] && [ -d "$imports_plugin_dir" ] && [ -d "$books_dir" ] && [ -d "$bepress_dir" ]; then + return + fi + + if [ ! -d "$customstyling_dir" ]; then + git clone "${CUSTOMSTYLING_REPO:-https://github.com/openlibhums/customstyling.git}" \ + --branch "${CUSTOMSTYLING_REF:-v1.1.1}" \ + "$customstyling_dir" + fi + + if [ ! -d "$imports_plugin_dir" ]; then + git clone "${IMPORTS_REPO:-https://github.com/openlibhums/imports.git}" \ + --branch "${IMPORTS_REF:-main}" \ + "$imports_plugin_dir" + # Install the required pip dependency too + fi + + if [ ! -d "$books_dir" ]; then + git clone "${BOOKS_REPO:-https://github.com/openlibhums/books.git}" \ + --branch "${BOOKS_REF:-master}" \ + "$books_dir" + fi + + if [ ! -d "$bepress_dir" ]; then + git clone "${BEPRESS_REPO:-https://github.com/openlibhums/bepress.git}" \ + --branch "${BEPRESS_REF:-master}" \ + "$bepress_dir" + fi + + + pip install python-wordpress-xmlrpc==2.3 +} + +# Check if APP_BUILT is set to a truthy value (e.g., 1, true, yes) to determine if the app is already built with janeway installed +# if the app is not built with janeway installed, we skip plugin etc installation until janeway is installed +app_built_enabled() { + case "${APP_BUILT:-false}" in + 1|true|TRUE|yes|YES) + return 0 + ;; + *) + return 1 + ;; + esac +} + +# Allow running other commands (e.g., bash for debugging) +if [ $# -eq 0 ] || [ "${1:0:1}" = '-' ] || [ "$1" = 'gunicorn' ] || [ -z "${1##*:*}" ]; then + if ! app_built_enabled; then + exec "$@" + fi + + if [ "$1" = 'gunicorn' ]; then + shift + fi + + # Build bind address from GUNICORN_HOST and GUNICORN_PORT, or use GUNICORN_BIND + PORT="${GUNICORN_PORT:-8000}" + BIND="${GUNICORN_BIND:-${GUNICORN_HOST:-0.0.0.0}:${PORT}}" + + if [ $# -eq 0 ]; then + set -- core.wsgi:application --chdir /janeway/src + fi + + # Add bind if not specified in args or GUNICORN_ARGS + if [[ ! " $* $GUNICORN_ARGS " =~ " --bind " ]] && [[ ! " $* $GUNICORN_ARGS " =~ " -b " ]] && [[ ! "$* $GUNICORN_ARGS" =~ --bind= ]] && [[ ! "$* $GUNICORN_ARGS" =~ -b= ]]; then + set -- --bind "$BIND" "$@" + fi + + # Add workers if not specified - default to (2 * CPU_COUNT) + 1 + if [[ ! " $* $GUNICORN_ARGS " =~ " --workers " ]] && [[ ! " $* $GUNICORN_ARGS " =~ " -w " ]] && [[ ! "$* $GUNICORN_ARGS" =~ --workers= ]] && [[ ! "$* $GUNICORN_ARGS" =~ -w= ]]; then + WORKERS="${GUNICORN_WORKERS:-$(( 2 * $(nproc) + 1 ))}" + set -- --workers "$WORKERS" "$@" + fi + + echo "Preparing Janeway assets..." + ensure_plugins + python /janeway/src/manage.py build_assets + install_plugins + python /janeway/src/manage.py collectstatic --noinput + python /janeway/src/manage.py migrate --noinput + exec gunicorn $GUNICORN_ARGS "$@" +fi + +# Otherwise, run the command as-is (e.g., bash, sh, python) +exec "$@" diff --git a/requirements.txt b/requirements.txt index 957f884dbb..9c391ecb9c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,13 +8,13 @@ crossrefapi==1.5.0 Django==4.2.26 django-bleach==3.1.0 django-bootstrap4==23.2 --e git+https://github.com/openlibhums/django-foundation-form.git@674616f5388fe44c63088671ddea38ed9161a4fd#egg=foundationform +git+https://github.com/openlibhums/django-foundation-form.git@674616f5388fe44c63088671ddea38ed9161a4fd#egg=foundationform django-countries[pyuca]==7.6.1 --e git+https://github.com/openlibhums/django-materializecss-form.git@be094ec283299ee8fd0a1dfe33e73a609fb2f680#egg=django-materializecss-form +git+https://github.com/openlibhums/django-materializecss-form.git@be094ec283299ee8fd0a1dfe33e73a609fb2f680#egg=django-materializecss-form django-debug-toolbar==5.1.0 django-hCaptcha==0.2.0 django-hijack==3.2.1 --e git+https://github.com/BirkbeckCTP/django-mailgun.git@4f01799e01219e83d658f02c876764bf2260337e#egg=django_mailgun +git+https://github.com/BirkbeckCTP/django-mailgun.git@4f01799e01219e83d658f02c876764bf2260337e#egg=django_mailgun django-modeltranslation==0.18.11 django-settings-export==1.2.1 django-recaptcha==3.0.0 @@ -23,8 +23,8 @@ django-simple-history==3.10.1 django-summernote==0.8.20.0 django-tinymce==3.7.1 djangorestframework==3.15.2 --e git+https://github.com/BirkbeckCTP/django-dynamicsites@f439c524cf964d957a75fbec4b23ad03a9a5d159#egg=dynamicsites --e git+https://github.com/BirkbeckCTP/ebooklib@b7005c57602eeea06358ac569a4cf52c630a32ef#egg=ebooklib +git+https://github.com/BirkbeckCTP/django-dynamicsites@f439c524cf964d957a75fbec4b23ad03a9a5d159#egg=dynamicsites +git+https://github.com/BirkbeckCTP/ebooklib@b7005c57602eeea06358ac569a4cf52c630a32ef#egg=ebooklib Faker==17.6.0 freezegun==1.2.0 geoip2==4.8.0 diff --git a/src/plugins/ksulcolors/__init__.py b/src/plugins/ksulcolors/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/plugins/ksulcolors/assets/ksul.css b/src/plugins/ksulcolors/assets/ksul.css new file mode 100644 index 0000000000..dfbad76026 --- /dev/null +++ b/src/plugins/ksulcolors/assets/ksul.css @@ -0,0 +1,41 @@ +/* Insert CSS Below */ +:root { + --primary-dark-color: #22175b; /* Primary colour used by elements such as buttons */ + --very-dark-primary-color: #22175b; /* Darker primary colour used for contrast or hover effects */ + --primary-light-color: #fff; /* lighter colour or secondary colour */ + --topbar-background-color: #24193d; /* background colour for the top bar of the navigation */ + --menu-foreground-color: #512888; /* font colour used in the menu bar */ +} + +.top-bar .dropdown.menu > li.is-dropdown-submenu-parent > a { + color: var(--primary-light-color); +} + +.top-bar .dropdown.menu > li.is-dropdown-submenu-parent > ul.submenu li a { + color: var(--menu-foreground-color); +} + +.dropdown.menu > li.is-dropdown-submenu-parent > a { + color: var(--primary-light-color); + +} + +.top-bar .dropdown.menu > li.is-dropdown-submenu-parent > a::after { + border-color: #FFF transparent transparent; +} + +.dropdown.menu > li.is-dropdown-submenu-parent > ul.submenu > li.is-submenu-item > a { + color: var(--menu-foreground-color); +} + +header #main-menu li a { + font-weight: 600; +} + +footer .large-2.logo-wrapper { + display: none; +} + +footer .large-text-right li a { + margin: 0; +} \ No newline at end of file diff --git a/src/plugins/ksulcolors/forms.py b/src/plugins/ksulcolors/forms.py new file mode 100644 index 0000000000..6459616821 --- /dev/null +++ b/src/plugins/ksulcolors/forms.py @@ -0,0 +1,5 @@ +from django import forms + + +class DummyManagerForm(forms.Form): + dummy_field = forms.CharField() diff --git a/src/plugins/ksulcolors/hooks.py b/src/plugins/ksulcolors/hooks.py new file mode 100644 index 0000000000..444922caef --- /dev/null +++ b/src/plugins/ksulcolors/hooks.py @@ -0,0 +1,7 @@ +from django.conf import settings + + +from django.templatetags.static import static + +def inject_css(context): + return f'' diff --git a/src/plugins/ksulcolors/plugin_settings.py b/src/plugins/ksulcolors/plugin_settings.py new file mode 100644 index 0000000000..a8f386ecc7 --- /dev/null +++ b/src/plugins/ksulcolors/plugin_settings.py @@ -0,0 +1,69 @@ +import os +import shutil +from utils import plugins +from django.conf import settings + +PLUGIN_NAME = 'KsulColors Plugin' +DISPLAY_NAME = 'KsulColors' +DESCRIPTION = 'Overrides colors in OLH theme to match KSUL colors' +AUTHOR = 'Sawyer' +VERSION = '0.1' +SHORT_NAME = 'ksulcolors' +MANAGER_URL = 'ksulcolors_manager' +JANEWAY_VERSION = "1.7.0" + +BASE_CSS_PATH = os.path.join( + settings.MEDIA_ROOT, + 'ksulcolors', +) +KSULCOLORS_ASSETS = os.path.join(os.path.dirname(__file__), 'assets') +STATIC_CSS_PATH = os.path.join( + settings.BASE_DIR, + 'static', + 'plugins', + 'ksulcolors', + 'css', +) + + +class KsulcolorsPlugin(plugins.Plugin): + plugin_name = PLUGIN_NAME + display_name = DISPLAY_NAME + description = DESCRIPTION + author = AUTHOR + short_name = SHORT_NAME + manager_url = MANAGER_URL + + version = VERSION + janeway_version = JANEWAY_VERSION + press_wide = True + plugin_group_name = 'plugin:{plugin_name}'.format(plugin_name=SHORT_NAME) + + + +def install(): + KsulcolorsPlugin.install() + os.makedirs(BASE_CSS_PATH, exist_ok=True) + shutil.copy( + os.path.join(KSULCOLORS_ASSETS, 'ksul.css'), + os.path.join(BASE_CSS_PATH, 'ksul.css') + ) + + os.makedirs(STATIC_CSS_PATH, exist_ok=True) + shutil.copy( + os.path.join(KSULCOLORS_ASSETS, 'ksul.css'), + os.path.join(STATIC_CSS_PATH, 'ksul.css') + ) + print('Installed ksulcolors') + +def hook_registry(): + return { + 'base_head_css': + { + 'module': 'plugins.ksulcolors.hooks', + 'function': 'inject_css' + } + } + +def register_for_events(): + pass diff --git a/src/plugins/ksulcolors/templates/ksulcolors/manager.html b/src/plugins/ksulcolors/templates/ksulcolors/manager.html new file mode 100644 index 0000000000..425522bec6 --- /dev/null +++ b/src/plugins/ksulcolors/templates/ksulcolors/manager.html @@ -0,0 +1,15 @@ +{% extends "admin/core/base.html" %} +{% load foundation %} + +{% block title %}KsulColors Manager{% endblock %} + +{% block body %} +