diff --git a/.dockerignore b/.dockerignore index fd59b07ac0..2440d71e7f 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,6 +1,7 @@ db logs/* *.tar.gz +.venv src/files/articles src/files/journals src/files/issues 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..8790f41683 100755 --- a/.gitignore +++ b/.gitignore @@ -81,6 +81,7 @@ src/core/settings.py mydatabase node_modules/ src/plugins/* +!src/plugins/ksulcolors/ src/media/profile_images/* # PyCharm diff --git a/dockerfiles/Dockerfile.ksul b/dockerfiles/Dockerfile.ksul new file mode 100644 index 0000000000..7c846c97a8 --- /dev/null +++ b/dockerfiles/Dockerfile.ksul @@ -0,0 +1,53 @@ +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 \ + 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 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..b50286cb4d --- /dev/null +++ b/dockerfiles/entrypoint.sh @@ -0,0 +1,36 @@ +#!/bin/bash +set -e + +# Allow running other commands (e.g., bash for debugging) +if [ "${1:0:1}" = '-' ] || [ -z "${1##*:*}" ]; then + # First arg is a flag or contains ':' (app:callable), run gunicorn + + # 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}}" + + # 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 + + # Append GUNICORN_ARGS if set + if [ -n "$GUNICORN_ARGS" ]; then + exec gunicorn $GUNICORN_ARGS "$@" + fi + + echo "Preparing Janeway assets..." + python /janeway/src/manage.py build_assets + python /janeway/src/manage.py install_plugins ksulcolors + python /janeway/src/manage.py collectstatic --noinput + exec gunicorn "$@" +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 %} +