commit
8dd9eecfa2
51 changed files with 7314 additions and 0 deletions
@ -0,0 +1,37 @@ |
|||||
|
root = true |
||||
|
|
||||
|
[*] |
||||
|
end_of_line = lf |
||||
|
insert_final_newline = true |
||||
|
charset = utf-8 |
||||
|
|
||||
|
[*.py] |
||||
|
indent_style = space |
||||
|
indent_size = 4 |
||||
|
ij_continuation_indent_size = 4 |
||||
|
|
||||
|
[*.js] |
||||
|
indent_style = space |
||||
|
indent_size = 4 |
||||
|
ij_continuation_indent_size = 4 |
||||
|
|
||||
|
[*.scss] |
||||
|
indent_style = space |
||||
|
indent_size = 2 |
||||
|
|
||||
|
[*.html] |
||||
|
indent_style = space |
||||
|
indent_size = 4 |
||||
|
ij_continuation_indent_size = 4 |
||||
|
|
||||
|
[{*.yml, *.yaml}] |
||||
|
indent_style = space |
||||
|
indent_size = 2 |
||||
|
|
||||
|
[*.svg] |
||||
|
indent_style = space |
||||
|
indent_size = 2 |
||||
|
|
||||
|
[{package.json, tsconfig.json}] |
||||
|
indent_style = space |
||||
|
indent_size = 4 |
@ -0,0 +1,4 @@ |
|||||
|
node_modules |
||||
|
db.sqlite3 |
||||
|
static/build |
||||
|
*.mo |
@ -0,0 +1,3 @@ |
|||||
|
# Keep .venv around in the project source to avoid pipenv creating new user-local venvs |
||||
|
* |
||||
|
!.gitignore |
@ -0,0 +1,31 @@ |
|||||
|
# Docker image for local development environment. |
||||
|
# You should NOT publish this image, it is intended for local use only. |
||||
|
|
||||
|
FROM almalinux/almalinux:8 |
||||
|
|
||||
|
ARG LOCAL_UID |
||||
|
ARG LOCAL_GID |
||||
|
|
||||
|
RUN groupadd -g ${LOCAL_GID} web && \ |
||||
|
useradd -ms /bin/bash web -g ${LOCAL_GID} -u ${LOCAL_UID} |
||||
|
|
||||
|
RUN dnf -y install \ |
||||
|
gcc \ |
||||
|
make \ |
||||
|
gettext \ |
||||
|
python38 \ |
||||
|
python38-devel \ |
||||
|
mariadb-devel |
||||
|
|
||||
|
RUN pip3 install pipenv |
||||
|
|
||||
|
# Work around IDEA remote interpreter not able to work with remote virtualenvs |
||||
|
RUN touch /root/.bashrc \ |
||||
|
&& echo 'if [[ -f "/app/.venv/bin/activate" ]]; then source /app/.venv/bin/activate; fi' >> /root/.bashrc |
||||
|
|
||||
|
RUN touch /home/web/.bashrc \ |
||||
|
&& echo 'if [[ -f "/app/.venv/bin/activate" ]]; then source /app/.venv/bin/activate; fi' >> /home/web/.bashrc |
||||
|
|
||||
|
WORKDIR /app |
||||
|
|
||||
|
CMD make runserver |
@ -0,0 +1,25 @@ |
|||||
|
# Docker image for local development environment. |
||||
|
# You should NOT publish this image, it is intended for local use only. |
||||
|
|
||||
|
FROM almalinux/almalinux:8 |
||||
|
|
||||
|
ARG LOCAL_UID |
||||
|
ARG LOCAL_GID |
||||
|
|
||||
|
RUN groupadd -g ${LOCAL_GID} web && \ |
||||
|
useradd -ms /bin/bash web -g ${LOCAL_GID} -u ${LOCAL_UID} |
||||
|
|
||||
|
RUN dnf -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm && \ |
||||
|
dnf -y install yum-utils && \ |
||||
|
curl -fsSL https://rpm.nodesource.com/setup_15.x | bash - && \ |
||||
|
curl -sL https://dl.yarnpkg.com/rpm/yarn.repo | tee /etc/yum.repos.d/yarn.repo |
||||
|
|
||||
|
RUN dnf -y install \ |
||||
|
make \ |
||||
|
gcc-c++ \ |
||||
|
nodejs \ |
||||
|
yarn |
||||
|
|
||||
|
WORKDIR /app |
||||
|
|
||||
|
CMD make frontend-runserver |
@ -0,0 +1,19 @@ |
|||||
|
Copyright (c) 2021 AlmaLinux OS Foundation |
||||
|
|
||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
|
of this software and associated documentation files (the "Software"), to deal |
||||
|
in the Software without restriction, including without limitation the rights |
||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
|
copies of the Software, and to permit persons to whom the Software is |
||||
|
furnished to do so, subject to the following conditions: |
||||
|
|
||||
|
The above copyright notice and this permission notice shall be included in all |
||||
|
copies or substantial portions of the Software. |
||||
|
|
||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
|
SOFTWARE. |
@ -0,0 +1,64 @@ |
|||||
|
ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) |
||||
|
CURRENT_UID := $(shell id -u) |
||||
|
CURRENT_GID := $(shell id -g) |
||||
|
|
||||
|
.PHONY: default |
||||
|
default: dev ; |
||||
|
|
||||
|
dev: #: Start development environment
|
||||
|
LOCAL_UID=${CURRENT_UID} LOCAL_GID=${CURRENT_GID} docker-compose up --abort-on-container-exit |
||||
|
|
||||
|
build-dev: #: Build development Docker images
|
||||
|
LOCAL_UID=${CURRENT_UID} LOCAL_GID=${CURRENT_GID} docker-compose build web |
||||
|
LOCAL_UID=${CURRENT_UID} LOCAL_GID=${CURRENT_GID} docker-compose build frontend |
||||
|
|
||||
|
clean-dev: #: Clean development environment, remove docker images and data
|
||||
|
LOCAL_UID=${CURRENT_UID} LOCAL_GID=${CURRENT_GID} docker-compose down --rmi local -v |
||||
|
|
||||
|
web-shell: #: Enter backend shell within docker, as a regular user, new service instance
|
||||
|
LOCAL_UID=${CURRENT_UID} LOCAL_GID=${CURRENT_GID} docker-compose run -u ${CURRENT_UID} web /bin/bash |
||||
|
|
||||
|
web-root-shell: #: Enter backend shell within docker, as root, new service instance
|
||||
|
LOCAL_UID=${CURRENT_UID} LOCAL_GID=${CURRENT_GID} docker-compose run web /bin/bash |
||||
|
|
||||
|
web-attach: #: Attach to backend shell within docker, as a regular user, running instance
|
||||
|
docker exec -ti -u ${CURRENT_UID} web /bin/bash |
||||
|
|
||||
|
web-root-attach: #: Attach to backend shell within docker, as root, running instance
|
||||
|
docker exec -ti web /bin/bash |
||||
|
|
||||
|
frontend-shell: #: Enter frontend shell within docker, as a regular user, new service instance
|
||||
|
LOCAL_UID=${CURRENT_UID} LOCAL_GID=${CURRENT_GID} docker-compose run -u ${CURRENT_UID} frontend /bin/bash |
||||
|
|
||||
|
frontend-root-shell: #: Enter frontend shell within docker, as root, new service instance
|
||||
|
LOCAL_UID=${CURRENT_UID} LOCAL_GID=${CURRENT_GID} docker-compose run frontend /bin/bash |
||||
|
|
||||
|
frontend-attach: #: Attach to frontend shell within docker, as a regular user, running instance
|
||||
|
docker exec -ti -u ${CURRENT_UID} frontend /bin/bash |
||||
|
|
||||
|
frontend-root-attach: #: Attach to frontend shell within docker, as root, running instance
|
||||
|
docker exec -ti frontend /bin/bash |
||||
|
|
||||
|
dist: #: Assemble a distributable archive
|
||||
|
@echo 'TODO' |
||||
|
|
||||
|
deploy: #: Deploy project
|
||||
|
@echo 'TODO' |
||||
|
|
||||
|
runserver: # INTERNAL - start internal development server. This is supposed to be run inside Docker.
|
||||
|
su - web -c 'cd /app && pipenv install' |
||||
|
su - web -c 'cd /app && pipenv run python3 manage.py migrate' |
||||
|
su - web -c 'cd /app && pipenv run python3 manage.py runserver 0.0.0.0:8080' |
||||
|
|
||||
|
frontend-runserver: # INTERNAL - start internal frontend development server. This is supposed to be run inside Docker.
|
||||
|
su - web -c 'yarn --cwd=/app/frontend/ install' |
||||
|
su - web -c 'yarn --cwd=/app/frontend/ dev-server --host 0.0.0.0 --public http://localhost:8090 --port 8090' |
||||
|
|
||||
|
help: #: Show this help
|
||||
|
@fgrep -h "#:" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/\\$$//' | sed -e 's/#://' |
||||
|
|
||||
|
all: dev |
||||
|
|
||||
|
%: |
||||
|
@echo 'Invalid command' |
||||
|
@make help |
@ -0,0 +1,13 @@ |
|||||
|
[[source]] |
||||
|
url = "https://pypi.org/simple" |
||||
|
verify_ssl = true |
||||
|
name = "pypi" |
||||
|
|
||||
|
[packages] |
||||
|
Django = "~=3.2" |
||||
|
mysqlclient = "~=2.0.3" |
||||
|
|
||||
|
[dev-packages] |
||||
|
|
||||
|
[requires] |
||||
|
python_version = "3.8" |
@ -0,0 +1,63 @@ |
|||||
|
{ |
||||
|
"_meta": { |
||||
|
"hash": { |
||||
|
"sha256": "a6e76f6ff312340357f1c08cde19e71b1c31343b30fe186fa5adad14cd1a0f66" |
||||
|
}, |
||||
|
"pipfile-spec": 6, |
||||
|
"requires": { |
||||
|
"python_version": "3.8" |
||||
|
}, |
||||
|
"sources": [ |
||||
|
{ |
||||
|
"name": "pypi", |
||||
|
"url": "https://pypi.org/simple", |
||||
|
"verify_ssl": true |
||||
|
} |
||||
|
] |
||||
|
}, |
||||
|
"default": { |
||||
|
"asgiref": { |
||||
|
"hashes": [ |
||||
|
"sha256:92906c611ce6c967347bbfea733f13d6313901d54dcca88195eaeb52b2a8e8ee", |
||||
|
"sha256:d1216dfbdfb63826470995d31caed36225dcaf34f182e0fa257a4dd9e86f1b78" |
||||
|
], |
||||
|
"markers": "python_version >= '3.6'", |
||||
|
"version": "==3.3.4" |
||||
|
}, |
||||
|
"django": { |
||||
|
"hashes": [ |
||||
|
"sha256:95c13c750f1f214abadec92b82c2768a5e795e6c2ebd0b4126f895ce9efffcdd", |
||||
|
"sha256:e2f73790c60188d3f94f08f644de249d956b3789161e7604509d128a13fb2fcc" |
||||
|
], |
||||
|
"index": "pypi", |
||||
|
"version": "==3.2.1" |
||||
|
}, |
||||
|
"mysqlclient": { |
||||
|
"hashes": [ |
||||
|
"sha256:0ac0dd759c4ca02c35a9fedc24bc982cf75171651e8187c2495ec957a87dfff7", |
||||
|
"sha256:3381ca1a4f37ff1155fcfde20836b46416d66531add8843f6aa6d968982731c3", |
||||
|
"sha256:71c4b330cf2313bbda0307fc858cc9055e64493ba9bf28454d25cf8b3ee8d7f5", |
||||
|
"sha256:f6ebea7c008f155baeefe16c56cd3ee6239f7a5a9ae42396c2f1860f08a7c432", |
||||
|
"sha256:fc575093cf81b6605bed84653e48b277318b880dc9becf42dd47fa11ffd3e2b6" |
||||
|
], |
||||
|
"index": "pypi", |
||||
|
"version": "==2.0.3" |
||||
|
}, |
||||
|
"pytz": { |
||||
|
"hashes": [ |
||||
|
"sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da", |
||||
|
"sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798" |
||||
|
], |
||||
|
"version": "==2021.1" |
||||
|
}, |
||||
|
"sqlparse": { |
||||
|
"hashes": [ |
||||
|
"sha256:017cde379adbd6a1f15a61873f43e8274179378e95ef3fede90b5aa64d304ed0", |
||||
|
"sha256:0f91fd2e829c44362cbcfab3e9ae12e22badaa8a29ad5ff599f9ec109f0454e8" |
||||
|
], |
||||
|
"markers": "python_version >= '3.5'", |
||||
|
"version": "==0.4.1" |
||||
|
} |
||||
|
}, |
||||
|
"develop": {} |
||||
|
} |
@ -0,0 +1,37 @@ |
|||||
|
# almalinux.org website |
||||
|
|
||||
|
This repository contains website source code for future https://almalinux.org. |
||||
|
|
||||
|
This website is built with Python using Django web framework. It uses MariaDB as database backend, |
||||
|
Docker and docker-compose for development environment deployment, and Pipenv is used to track project |
||||
|
requirements, and manage Python dependencies. |
||||
|
|
||||
|
JavaScript and SCSS is used for frontend, and is managed/built using Webpack Encore. See |
||||
|
[frontend/README](./frontend/README.md) for more details. |
||||
|
|
||||
|
## For developers |
||||
|
|
||||
|
To deploy local development environment, you will need following dependencies installed |
||||
|
on your development host: |
||||
|
|
||||
|
- Docker |
||||
|
- docker-compose |
||||
|
- make |
||||
|
|
||||
|
Common development commands and automation related commands are listed in Makefile. Execute `make help` |
||||
|
for a complete list of available commands. |
||||
|
|
||||
|
Executing `make dev` will deploy a complete, ready to go development environment. |
||||
|
|
||||
|
### Directories and modules |
||||
|
|
||||
|
- `/almalinux/` - Django project root. |
||||
|
- `/commons/` - A Django app-module with reusable utilities for app support. |
||||
|
- `/locale/` - Django locale files. |
||||
|
- `/media/` - Django file uploads. |
||||
|
- `/static/` - Static files and build output for frontend code. |
||||
|
- `/www/` - Django app that contains all logic for the website. |
||||
|
- `/frontend/` - JavaScript and SCSS frontend code. |
||||
|
|
||||
|
|
||||
|
Copyright (c) 2021 AlmaLinux OS Foundation |
@ -0,0 +1,9 @@ |
|||||
|
from django.contrib.admin import AdminSite |
||||
|
|
||||
|
|
||||
|
class AlmaLinuxAdminSite(AdminSite): |
||||
|
site_title = 'AlmaLinux' |
||||
|
site_header = 'AlmaLinux' |
||||
|
|
||||
|
def get_urls(self): |
||||
|
return super().get_urls() |
@ -0,0 +1,5 @@ |
|||||
|
from django.contrib.admin.apps import AdminConfig |
||||
|
|
||||
|
|
||||
|
class AlmaLinuxAdminConfig(AdminConfig): |
||||
|
default_site = 'almalinux.admin.AlmaLinuxAdminSite' |
@ -0,0 +1,16 @@ |
|||||
|
""" |
||||
|
ASGI config for almalinux project. |
||||
|
|
||||
|
It exposes the ASGI callable as a module-level variable named ``application``. |
||||
|
|
||||
|
For more information on this file, see |
||||
|
https://docs.djangoproject.com/en/3.1/howto/deployment/asgi/ |
||||
|
""" |
||||
|
|
||||
|
import os |
||||
|
|
||||
|
from django.core.asgi import get_asgi_application |
||||
|
|
||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'almalinux.settings') |
||||
|
|
||||
|
application = get_asgi_application() |
@ -0,0 +1,158 @@ |
|||||
|
""" |
||||
|
Django settings for almalinux project. |
||||
|
|
||||
|
Generated by 'django-admin startproject' using Django 3.1.4. |
||||
|
|
||||
|
For more information on this file, see |
||||
|
https://docs.djangoproject.com/en/3.1/topics/settings/ |
||||
|
|
||||
|
For the full list of settings and their values, see |
||||
|
https://docs.djangoproject.com/en/3.1/ref/settings/ |
||||
|
""" |
||||
|
|
||||
|
# TODO: .env support for all major settings that could change between envs |
||||
|
|
||||
|
from pathlib import Path |
||||
|
|
||||
|
# Build paths inside the project like this: BASE_DIR / 'subdir'. |
||||
|
BASE_DIR = Path(__file__).resolve().parent.parent |
||||
|
|
||||
|
# Quick-start development settings - unsuitable for production |
||||
|
# See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/ |
||||
|
|
||||
|
# SECURITY WARNING: keep the secret key used in production secret! |
||||
|
SECRET_KEY = 'fm31y+qa5eg74*frj#37_6*cc^g)xg(x(@u(^5m*_qam@_74p!' |
||||
|
|
||||
|
# SECURITY WARNING: don't run with debug turned on in production! |
||||
|
DEBUG = True |
||||
|
|
||||
|
ALLOWED_HOSTS = [ |
||||
|
'127.0.0.1', |
||||
|
'localhost' |
||||
|
] |
||||
|
|
||||
|
# Application definition |
||||
|
|
||||
|
INSTALLED_APPS = [ |
||||
|
'django.contrib.auth', |
||||
|
'django.contrib.contenttypes', |
||||
|
'django.contrib.sessions', |
||||
|
'django.contrib.messages', |
||||
|
'django.contrib.staticfiles', |
||||
|
'commons', |
||||
|
'almalinux.apps.AlmaLinuxAdminConfig', |
||||
|
'www' |
||||
|
] |
||||
|
|
||||
|
MIDDLEWARE = [ |
||||
|
'django.middleware.locale.LocaleMiddleware', |
||||
|
'django.middleware.security.SecurityMiddleware', |
||||
|
'django.contrib.sessions.middleware.SessionMiddleware', |
||||
|
'django.middleware.common.CommonMiddleware', |
||||
|
'django.middleware.csrf.CsrfViewMiddleware', |
||||
|
'django.contrib.auth.middleware.AuthenticationMiddleware', |
||||
|
'django.contrib.messages.middleware.MessageMiddleware', |
||||
|
'django.middleware.clickjacking.XFrameOptionsMiddleware', |
||||
|
] |
||||
|
|
||||
|
ROOT_URLCONF = 'almalinux.urls' |
||||
|
|
||||
|
template_loaders = [ |
||||
|
'django.template.loaders.filesystem.Loader', |
||||
|
'django.template.loaders.app_directories.Loader' |
||||
|
] |
||||
|
|
||||
|
if DEBUG: |
||||
|
effective_loaders = template_loaders |
||||
|
else: |
||||
|
effective_loaders = [ |
||||
|
('django.template.loaders.cached.Loader', template_loaders), |
||||
|
] |
||||
|
|
||||
|
TEMPLATES = [ |
||||
|
{ |
||||
|
'BACKEND': 'django.template.backends.django.DjangoTemplates', |
||||
|
'DIRS': [], |
||||
|
'APP_DIRS': [], |
||||
|
'OPTIONS': { |
||||
|
'loaders': effective_loaders, |
||||
|
'context_processors': [ |
||||
|
'django.template.context_processors.debug', |
||||
|
'django.template.context_processors.request', |
||||
|
'django.contrib.auth.context_processors.auth', |
||||
|
'django.contrib.messages.context_processors.messages', |
||||
|
], |
||||
|
}, |
||||
|
}, |
||||
|
] |
||||
|
|
||||
|
WSGI_APPLICATION = 'almalinux.wsgi.application' |
||||
|
|
||||
|
# Database |
||||
|
# https://docs.djangoproject.com/en/3.1/ref/settings/#databases |
||||
|
|
||||
|
DATABASES = { |
||||
|
'default': { |
||||
|
'ENGINE': 'django.db.backends.mysql', |
||||
|
'NAME': 'devel', |
||||
|
'USER': 'devel', |
||||
|
'PASSWORD': 'devel', |
||||
|
'HOST': 'mariadb', |
||||
|
'PORT': 3306, |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
# Password validation |
||||
|
# https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators |
||||
|
|
||||
|
AUTH_PASSWORD_VALIDATORS = [ |
||||
|
{ |
||||
|
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', |
||||
|
}, |
||||
|
{ |
||||
|
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', |
||||
|
}, |
||||
|
{ |
||||
|
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', |
||||
|
}, |
||||
|
{ |
||||
|
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', |
||||
|
}, |
||||
|
] |
||||
|
|
||||
|
# Internationalization |
||||
|
# https://docs.djangoproject.com/en/3.1/topics/i18n/ |
||||
|
|
||||
|
LANGUAGE_CODE = 'en' |
||||
|
|
||||
|
TIME_ZONE = 'UTC' |
||||
|
|
||||
|
USE_I18N = True |
||||
|
|
||||
|
USE_L10N = True |
||||
|
|
||||
|
USE_TZ = True |
||||
|
|
||||
|
LANGUAGES = ( |
||||
|
('en', 'English (US)'), |
||||
|
('es', 'Español (España)'), |
||||
|
('ru', 'Русский'), |
||||
|
) |
||||
|
|
||||
|
LOCALE_PATHS = [ |
||||
|
BASE_DIR / 'locale' |
||||
|
] |
||||
|
|
||||
|
# Static files (CSS, JavaScript, Images) |
||||
|
# https://docs.djangoproject.com/en/3.1/howto/static-files/ |
||||
|
|
||||
|
STATIC_URL = '/static/' |
||||
|
|
||||
|
STATICFILES_DIRS = [ |
||||
|
BASE_DIR / 'static', |
||||
|
] |
||||
|
|
||||
|
ENCORE_BUILD_DIR = BASE_DIR / 'static/build/' |
||||
|
|
||||
|
MEDIA_ROOT = BASE_DIR / 'media' |
||||
|
MEDIA_URL = 'media/' |
@ -0,0 +1,36 @@ |
|||||
|
"""almalinux URL Configuration |
||||
|
|
||||
|
The `urlpatterns` list routes URLs to views. For more information please see: |
||||
|
https://docs.djangoproject.com/en/3.1/topics/http/urls/ |
||||
|
Examples: |
||||
|
Function views |
||||
|
1. Add an import: from my_app import views |
||||
|
2. Add a URL to urlpatterns: path('', views.home, name='home') |
||||
|
Class-based views |
||||
|
1. Add an import: from other_app.views import Home |
||||
|
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') |
||||
|
Including another URLconf |
||||
|
1. Import the include() function: from django.urls import include, path |
||||
|
2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) |
||||
|
""" |
||||
|
from django.conf import settings |
||||
|
from django.conf.urls.i18n import i18n_patterns |
||||
|
from django.conf.urls.static import static |
||||
|
from django.contrib import admin |
||||
|
from django.urls import path, include |
||||
|
from django.views.i18n import JavaScriptCatalog |
||||
|
|
||||
|
urlpatterns = [] |
||||
|
urlpatterns += i18n_patterns(path('', include('www.urls')), prefix_default_language=False) |
||||
|
|
||||
|
urlpatterns += [ |
||||
|
path('admin/', admin.site.urls), |
||||
|
] |
||||
|
|
||||
|
urlpatterns += i18n_patterns(path( |
||||
|
'jsi18n/', |
||||
|
JavaScriptCatalog.as_view(), |
||||
|
name='javascript-catalog' |
||||
|
), prefix_default_language=False) |
||||
|
|
||||
|
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) |
@ -0,0 +1,16 @@ |
|||||
|
""" |
||||
|
WSGI config for almalinux project. |
||||
|
|
||||
|
It exposes the WSGI callable as a module-level variable named ``application``. |
||||
|
|
||||
|
For more information on this file, see |
||||
|
https://docs.djangoproject.com/en/3.1/howto/deployment/wsgi/ |
||||
|
""" |
||||
|
|
||||
|
import os |
||||
|
|
||||
|
from django.core.wsgi import get_wsgi_application |
||||
|
|
||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'almalinux.settings') |
||||
|
|
||||
|
application = get_wsgi_application() |
@ -0,0 +1,3 @@ |
|||||
|
from commons.encore import Encore |
||||
|
|
||||
|
encore = Encore() |
@ -0,0 +1,5 @@ |
|||||
|
from django.apps import AppConfig |
||||
|
|
||||
|
|
||||
|
class CommonsConfig(AppConfig): |
||||
|
name = 'commons' |
@ -0,0 +1,48 @@ |
|||||
|
import json |
||||
|
from os import path |
||||
|
|
||||
|
from django.conf import settings |
||||
|
|
||||
|
|
||||
|
# Methods in this class should remain non-static as later there might be some instance level cache here. |
||||
|
class Encore: |
||||
|
# noinspection PyMethodMayBeStatic |
||||
|
def load_json(self): |
||||
|
entry_points_path = settings.ENCORE_BUILD_DIR / 'entrypoints.json' |
||||
|
if not path.isfile(entry_points_path): |
||||
|
raise RuntimeError('Missing entry points - %s' % entry_points_path) |
||||
|
|
||||
|
with open(entry_points_path) as it: |
||||
|
return json.load(it) |
||||
|
|
||||
|
def get_js(self, entrypoint): |
||||
|
data = self.load_json() |
||||
|
|
||||
|
if 'entrypoints' not in data: |
||||
|
raise RuntimeError('Invalid entry points manifest') |
||||
|
|
||||
|
manifest = data['entrypoints'] |
||||
|
|
||||
|
if entrypoint not in manifest: |
||||
|
raise RuntimeError('Unknown entrypoint - %s' % entrypoint) |
||||
|
|
||||
|
if 'js' not in manifest[entrypoint]: |
||||
|
return [] |
||||
|
|
||||
|
return manifest[entrypoint]['js'] |
||||
|
|
||||
|
def get_css(self, entrypoint): |
||||
|
data = self.load_json() |
||||
|
|
||||
|
if 'entrypoints' not in data: |
||||
|
raise RuntimeError('Invalid entry points manifest') |
||||
|
|
||||
|
manifest = data['entrypoints'] |
||||
|
|
||||
|
if entrypoint not in manifest: |
||||
|
raise RuntimeError('Unknown entry point - %s' % entrypoint) |
||||
|
|
||||
|
if 'css' not in manifest[entrypoint]: |
||||
|
return [] |
||||
|
|
||||
|
return manifest[entrypoint]['css'] |
@ -0,0 +1,34 @@ |
|||||
|
from django import template |
||||
|
from django.utils.safestring import SafeString |
||||
|
|
||||
|
from commons import encore |
||||
|
|
||||
|
register = template.Library() |
||||
|
|
||||
|
|
||||
|
@register.simple_tag() |
||||
|
def encore_entrypoint_js(entrypoint): |
||||
|
js = encore.get_js(entrypoint) |
||||
|
|
||||
|
if 0 == len(js): |
||||
|
return '' |
||||
|
|
||||
|
buffer = '' |
||||
|
for script in js: |
||||
|
buffer += '<script src="%s" defer></script>\n' % script |
||||
|
|
||||
|
return SafeString(buffer.strip()) |
||||
|
|
||||
|
|
||||
|
@register.simple_tag |
||||
|
def encore_entrypoint_css(entrypoint): |
||||
|
css = encore.get_css(entrypoint) |
||||
|
|
||||
|
if 0 == len(css): |
||||
|
return '' |
||||
|
|
||||
|
buffer = '' |
||||
|
for stylesheet in css: |
||||
|
buffer += '<link rel="stylesheet" href="%s">' % stylesheet |
||||
|
|
||||
|
return SafeString(buffer.strip()) |
@ -0,0 +1,23 @@ |
|||||
|
import hashlib |
||||
|
from os import path |
||||
|
|
||||
|
|
||||
|
# This method allows for file uploads to live in segmented folders, |
||||
|
# instead of placing all uploads in a single folder. |
||||
|
# noinspection PyUnusedLocal |
||||
|
def segmented_upload_to(instance, file_name) -> str: |
||||
|
""" |
||||
|
:param instance: |
||||
|
:param file_name: string |
||||
|
""" |
||||
|
filename_base, filename_ext = path.splitext(file_name) |
||||
|
filename_base = filename_base.replace('/', '_') |
||||
|
hashcode = hashlib.md5(filename_base.encode()).hexdigest() |
||||
|
|
||||
|
return '{0}/{1}/{2}/{3}{4}'.format( |
||||
|
hashcode[0:2], |
||||
|
hashcode[2:4], |
||||
|
hashcode[4:6], |
||||
|
filename_base, |
||||
|
filename_ext |
||||
|
) |
@ -0,0 +1,50 @@ |
|||||
|
version: '3' |
||||
|
|
||||
|
services: |
||||
|
web: |
||||
|
container_name: web |
||||
|
build: |
||||
|
context: . |
||||
|
args: |
||||
|
LOCAL_UID: ${LOCAL_UID} |
||||
|
LOCAL_GID: ${LOCAL_GID} |
||||
|
restart: 'no' |
||||
|
links: |
||||
|
- mariadb |
||||
|
- frontend |
||||
|
volumes: |
||||
|
- .:/app/ |
||||
|
ports: |
||||
|
- 8080:8080 # HTTP |
||||
|
|
||||
|
frontend: |
||||
|
container_name: frontend |
||||
|
build: |
||||
|
context: . |
||||
|
dockerfile: Dockerfile-frontend |
||||
|
args: |
||||
|
LOCAL_UID: ${LOCAL_UID} |
||||
|
LOCAL_GID: ${LOCAL_GID} |
||||
|
restart: 'no' |
||||
|
volumes: |
||||
|
- .:/app/ |
||||
|
ports: |
||||
|
- 8090:8090 # HTTP devserver |
||||
|
|
||||
|
mariadb: |
||||
|
image: mariadb:10 |
||||
|
container_name: mariadb |
||||
|
restart: 'no' |
||||
|
environment: |
||||
|
MYSQL_ROOT_PASSWORD: devel |
||||
|
MYSQL_DATABASE: devel |
||||
|
MYSQL_USER: devel |
||||
|
MYSQL_PASSWORD: devel |
||||
|
volumes: |
||||
|
- mariadb:/var/lib/mysql |
||||
|
ports: |
||||
|
- 3306:3306 |
||||
|
|
||||
|
volumes: |
||||
|
mariadb: |
||||
|
driver: local |
@ -0,0 +1 @@ |
|||||
|
node_modules |
@ -0,0 +1,41 @@ |
|||||
|
# almalinux.org JavaScript and SCSS modules |
||||
|
|
||||
|
This source module contains frontend specific source code for almalinux.org. |
||||
|
|
||||
|
- NodeJS v15. |
||||
|
- Yarn for dependency management. |
||||
|
- Modern JavaScript. |
||||
|
- SCSS for stylesheets. |
||||
|
|
||||
|
## Source structure |
||||
|
|
||||
|
All CSS/JS code can be found in `src/` directory. |
||||
|
|
||||
|
### `src/common` |
||||
|
|
||||
|
Common entry point is used for JavaScript and SCSS code that is expected to be used by all or most of the website pages, |
||||
|
and for otherwise reusable code. |
||||
|
|
||||
|
### `src/modules` |
||||
|
|
||||
|
Modules root directory contains modular JS/SCSS for features isolate to some specific domain, such as a specific feature |
||||
|
or page. Features should be generally organized following this convention: |
||||
|
|
||||
|
``` |
||||
|
src/modules/feature_<NAME> |
||||
|
src/modules/page_<NAME> |
||||
|
``` |
||||
|
|
||||
|
The difference between `page_*` and `feature_*` is that page specific code is limited to be used within a single view, |
||||
|
while features should be reusable. |
||||
|
|
||||
|
Each module should contain a README.md file to document features, use and dependencies for said specific code module. |
||||
|
|
||||
|
`<NAME>` identifiers should be short, but descriptive, and should follow pattern `[a-z_]+`. |
||||
|
|
||||
|
#### Dependencies within modules |
||||
|
|
||||
|
- `page_*` is allowed to have zero or more dependencies on `feature_*` modules, or code in `src/common` |
||||
|
- `page_*` must not depend on code in another `_page*` |
||||
|
- `feature_*` can depend on another `feature_*`. |
||||
|
|
@ -0,0 +1,29 @@ |
|||||
|
{ |
||||
|
"license": "MIT", |
||||
|
"private": true, |
||||
|
"scripts": { |
||||
|
"dev-server": "encore dev-server --hot", |
||||
|
"dev": "encore dev", |
||||
|
"build": "encore production --progress" |
||||
|
}, |
||||
|
"browserslist": [ |
||||
|
"> 0.5%", |
||||
|
"last 2 versions", |
||||
|
"Firefox ESR", |
||||
|
"not dead" |
||||
|
], |
||||
|
"devDependencies": { |
||||
|
"@babel/preset-env": "^7.14.1", |
||||
|
"@symfony/webpack-encore": "^1.2.0", |
||||
|
"autoprefixer": "^10.2.5", |
||||
|
"babel-core": "^6.26.3", |
||||
|
"babel-loader": "^8.2.2", |
||||
|
"core-js": "^3.11.2", |
||||
|
"eslint": "^7.25.0", |
||||
|
"node-sass": "^5.0.0", |
||||
|
"normalize.css": "^8.0.1", |
||||
|
"postcss-loader": "^5.2.0", |
||||
|
"sass-loader": "^11.0.1" |
||||
|
}, |
||||
|
"dependencies": {} |
||||
|
} |
@ -0,0 +1,5 @@ |
|||||
|
module.exports = { |
||||
|
plugins: { |
||||
|
autoprefixer: {}, |
||||
|
} |
||||
|
} |
@ -0,0 +1,3 @@ |
|||||
|
import '@common/scss/main.scss'; |
||||
|
|
||||
|
console.log(gettext('Hello World')); |
@ -0,0 +1,2 @@ |
|||||
|
@import "~normalize.css"; |
||||
|
|
@ -0,0 +1,18 @@ |
|||||
|
// This file is to mock django frontend localization API for intellisense. You do not need to include it in source.
|
||||
|
// noinspection JSUnusedLocalSymbols
|
||||
|
window.pluralidx = window.pluralidx || function (n) { |
||||
|
}; |
||||
|
window.gettext = window.gettext || function (msgid) { |
||||
|
}; |
||||
|
window.ngettext = window.ngettext || function (singular, plural, count) { |
||||
|
}; |
||||
|
window.gettext_noop = window.gettext_noop || function (msgid) { |
||||
|
}; |
||||
|
window.pgettext = window.pgettext || function (context, msgid) { |
||||
|
}; |
||||
|
window.npgettext = window.npgettext || function (context, singular, plural, count) { |
||||
|
}; |
||||
|
window.interpolate = window.interpolate || function (fmt, obj, named) { |
||||
|
}; |
||||
|
window.get_format = window.get_format || function (format_type) { |
||||
|
}; |
@ -0,0 +1,47 @@ |
|||||
|
const path = require('path'); |
||||
|
const Encore = require('@symfony/webpack-encore'); |
||||
|
|
||||
|
if (!Encore.isRuntimeEnvironmentConfigured()) { |
||||
|
Encore.configureRuntimeEnvironment(process.env.NODE_ENV || 'dev'); |
||||
|
} |
||||
|
|
||||
|
if (Encore.isProduction()) { |
||||
|
process.env.NODE_ENV = 'production'; |
||||
|
} |
||||
|
|
||||
|
const BUILD_DIR = path.normalize(__dirname + '/../static/build/') |
||||
|
|
||||
|
Encore |
||||
|
.setOutputPath(BUILD_DIR) |
||||
|
.setPublicPath('/static/build') |
||||
|
.splitEntryChunks() |
||||
|
.enableSingleRuntimeChunk() |
||||
|
.cleanupOutputBeforeBuild() |
||||
|
.enablePostCssLoader() |
||||
|
.enableSourceMaps(!Encore.isProduction()) |
||||
|
.enableVersioning(Encore.isProduction()) |
||||
|
.configureBabel((config) => { |
||||
|
}) |
||||
|
.configureBabelPresetEnv((config) => { |
||||
|
config.useBuiltIns = 'usage'; |
||||
|
config.corejs = 3; |
||||
|
}) |
||||
|
.enableSassLoader() |
||||
|
.configureDevServerOptions((config) => { |
||||
|
config.static = false; |
||||
|
}) |
||||
|
|
||||
|
// Common CSS and JavaScript for the whole website
|
||||
|
.addEntry('common', __dirname + '/src/common/js/main.js') |
||||
|
; |
||||
|
|
||||
|
if (Encore.isDevServer() || Encore.isDev()) { |
||||
|
Encore.disableCssExtraction(); |
||||
|
} |
||||
|
|
||||
|
Encore.addAliases({ |
||||
|
'@common': path.resolve(__dirname, 'src/common/'), |
||||
|
'@modules': path.resolve(__dirname, 'src/modules/') |
||||
|
}) |
||||
|
|
||||
|
module.exports = Encore.getWebpackConfig(); |
File diff suppressed because it is too large
@ -0,0 +1,22 @@ |
|||||
|
# SOME DESCRIPTIVE TITLE. |
||||
|
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER |
||||
|
# This file is distributed under the same license as the PACKAGE package. |
||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. |
||||
|
# |
||||
|
#, fuzzy |
||||
|
msgid "" |
||||
|
msgstr "" |
||||
|
"Project-Id-Version: PACKAGE VERSION\n" |
||||
|
"Report-Msgid-Bugs-To: \n" |
||||
|
"POT-Creation-Date: 2021-05-05 15:11+0000\n" |
||||
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" |
||||
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" |
||||
|
"Language-Team: LANGUAGE <LL@li.org>\n" |
||||
|
"Language: es\n" |
||||
|
"MIME-Version: 1.0\n" |
||||
|
"Content-Type: text/plain; charset=UTF-8\n" |
||||
|
"Content-Transfer-Encoding: 8bit\n" |
||||
|
|
||||
|
#: www/templates/index.html:5 |
||||
|
msgid "Hello World" |
||||
|
msgstr "Hola Mundo" |
@ -0,0 +1,22 @@ |
|||||
|
# SOME DESCRIPTIVE TITLE. |
||||
|
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER |
||||
|
# This file is distributed under the same license as the PACKAGE package. |
||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. |
||||
|
# |
||||
|
#, fuzzy |
||||
|
msgid "" |
||||
|
msgstr "" |
||||
|
"Project-Id-Version: PACKAGE VERSION\n" |
||||
|
"Report-Msgid-Bugs-To: \n" |
||||
|
"POT-Creation-Date: 2021-05-05 15:06+0000\n" |
||||
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" |
||||
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" |
||||
|
"Language-Team: LANGUAGE <LL@li.org>\n" |
||||
|
"Language: es\n" |
||||
|
"MIME-Version: 1.0\n" |
||||
|
"Content-Type: text/plain; charset=UTF-8\n" |
||||
|
"Content-Transfer-Encoding: 8bit\n" |
||||
|
|
||||
|
#: frontend/src/common/js/main.js:3 |
||||
|
msgid "Hello World" |
||||
|
msgstr "Hola Mundo" |
@ -0,0 +1,20 @@ |
|||||
|
#, fuzzy |
||||
|
msgid "" |
||||
|
msgstr "" |
||||
|
"Project-Id-Version: almalinuxorg/1.0\n" |
||||
|
"Report-Msgid-Bugs-To: \n" |
||||
|
"POT-Creation-Date: 2021-05-05 13:46+0000\n" |
||||
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" |
||||
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" |
||||
|
"Language-Team: LANGUAGE <LL@li.org>\n" |
||||
|
"Language: ru\n" |
||||
|
"MIME-Version: 1.0\n" |
||||
|
"Content-Type: text/plain; charset=UTF-8\n" |
||||
|
"Content-Transfer-Encoding: 8bit\n" |
||||
|
"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" |
||||
|
"%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n" |
||||
|
"%100>=11 && n%100<=14)? 2 : 3);\n" |
||||
|
|
||||
|
#: www/templates/index.html:3 |
||||
|
msgid "Hello World" |
||||
|
msgstr "Привет мир" |
@ -0,0 +1,20 @@ |
|||||
|
#, fuzzy |
||||
|
msgid "" |
||||
|
msgstr "" |
||||
|
"Project-Id-Version: almalinuxorg/1.0\n" |
||||
|
"Report-Msgid-Bugs-To: \n" |
||||
|
"POT-Creation-Date: 2021-05-05 14:23+0000\n" |
||||
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" |
||||
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" |
||||
|
"Language-Team: LANGUAGE <LL@li.org>\n" |
||||
|
"Language: ru\n" |
||||
|
"MIME-Version: 1.0\n" |
||||
|
"Content-Type: text/plain; charset=UTF-8\n" |
||||
|
"Content-Transfer-Encoding: 8bit\n" |
||||
|
"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" |
||||
|
"%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n" |
||||
|
"%100>=11 && n%100<=14)? 2 : 3);\n" |
||||
|
|
||||
|
#: frontend/src/common/js/main.js:1 |
||||
|
msgid "Hello World" |
||||
|
msgstr "Привет мир" |
@ -0,0 +1,22 @@ |
|||||
|
#!/usr/bin/env python |
||||
|
"""Django's command-line utility for administrative tasks.""" |
||||
|
import os |
||||
|
import sys |
||||
|
|
||||
|
|
||||
|
def main(): |
||||
|
"""Run administrative tasks.""" |
||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'almalinux.settings') |
||||
|
try: |
||||
|
from django.core.management import execute_from_command_line |
||||
|
except ImportError as exc: |
||||
|
raise ImportError( |
||||
|
"Couldn't import Django. Are you sure it's installed and " |
||||
|
"available on your PYTHONPATH environment variable? Did you " |
||||
|
"forget to activate a virtual environment?" |
||||
|
) from exc |
||||
|
execute_from_command_line(sys.argv) |
||||
|
|
||||
|
|
||||
|
if __name__ == '__main__': |
||||
|
main() |
@ -0,0 +1,2 @@ |
|||||
|
* |
||||
|
!.gitignore |
@ -0,0 +1,15 @@ |
|||||
|
from django.contrib import admin |
||||
|
|
||||
|
# from django.conf.urls import url |
||||
|
|
||||
|
# Register your models here. |
||||
|
|
||||
|
# admin.site.register(...) |
||||
|
|
||||
|
# Admin customizations |
||||
|
frontpage_admin_urls = [ |
||||
|
# url(r'^tweet/process/$', admin.site.admin_view(...CUSTOM_ADMIN_VIEW)) |
||||
|
] |
||||
|
|
||||
|
_original_urls_reader = admin.site.get_urls |
||||
|
admin.site.get_urls = lambda: (_original_urls_reader() + frontpage_admin_urls) |
@ -0,0 +1,5 @@ |
|||||
|
from django.apps import AppConfig |
||||
|
|
||||
|
|
||||
|
class WWWConfig(AppConfig): |
||||
|
name = 'www' |
@ -0,0 +1 @@ |
|||||
|
# Create your models here. |
@ -0,0 +1,6 @@ |
|||||
|
{% extends 'layouts/base.html' %} |
||||
|
{% load i18n %} |
||||
|
|
||||
|
{% block body %} |
||||
|
<span>{% translate "Hello World" %}</span> |
||||
|
{% endblock %} |
@ -0,0 +1,20 @@ |
|||||
|
{% load encore %} |
||||
|
{% load static %} |
||||
|
<!doctype html> |
||||
|
<html lang="en"> |
||||
|
<head> |
||||
|
<meta charset="UTF-8"> |
||||
|
<meta content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" |
||||
|
name="viewport"> |
||||
|
<meta content="ie=edge" http-equiv="X-UA-Compatible"> |
||||
|
<title>{% block title %}AlmaLinux{% endblock %}</title> |
||||
|
{% encore_entrypoint_css 'common' %} |
||||
|
{% block head_end %}{% endblock %} |
||||
|
</head> |
||||
|
<body> |
||||
|
{% block body %}{% endblock %} |
||||
|
<script src="{% url 'javascript-catalog' %}" defer></script> |
||||
|
{% encore_entrypoint_js 'common' %} |
||||
|
{% block body_end %}{% endblock %} |
||||
|
</body> |
||||
|
</html> |
@ -0,0 +1 @@ |
|||||
|
# Create your tests here. |
@ -0,0 +1,7 @@ |
|||||
|
from django.urls import path |
||||
|
|
||||
|
from . import views |
||||
|
|
||||
|
urlpatterns = [ |
||||
|
path('', views.index, name='index'), |
||||
|
] |
@ -0,0 +1,6 @@ |
|||||
|
from django.http import HttpResponse, HttpRequest |
||||
|
from django.shortcuts import render |
||||
|
|
||||
|
|
||||
|
def index(request: HttpRequest) -> HttpResponse: |
||||
|
return render(request, 'index.html') |
Loading…
Reference in new issue