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