Skip to content

Commit adecb42

Browse files
authored
Merge branch 'enext' into organizer-creation
2 parents b146753 + a76a64f commit adecb42

File tree

288 files changed

+9485
-14489
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

288 files changed

+9485
-14489
lines changed

.github/workflows/deploy-docs.yml

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
name: Deploy Documentation
2+
3+
on:
4+
push:
5+
branches:
6+
- enext
7+
- main
8+
paths:
9+
- 'doc/**'
10+
- '.github/workflows/deploy-docs.yml'
11+
workflow_dispatch:
12+
13+
permissions:
14+
contents: read
15+
pages: write
16+
id-token: write
17+
18+
concurrency:
19+
group: "pages"
20+
cancel-in-progress: false
21+
22+
jobs:
23+
build:
24+
runs-on: ubuntu-latest
25+
steps:
26+
- name: Checkout
27+
uses: actions/checkout@v4
28+
with:
29+
submodules: recursive
30+
31+
- name: Set up Python
32+
uses: actions/setup-python@v5
33+
with:
34+
python-version: '3.11'
35+
36+
- name: Install system dependencies
37+
run: |
38+
sudo apt-get update
39+
sudo apt-get install -y \
40+
enchant-2 \
41+
libffi-dev \
42+
libssl-dev \
43+
libxml2-dev \
44+
libxslt1-dev \
45+
gettext \
46+
libfreetype-dev \
47+
libjpeg-dev \
48+
libpq-dev \
49+
build-essential
50+
51+
- name: Install dependencies
52+
run: |
53+
python -m pip install --upgrade pip
54+
cd doc
55+
# Install all dependencies (Sphinx, Django, and all packages)
56+
bash install-all-deps.sh
57+
58+
- name: Build unified documentation
59+
run: |
60+
cd doc
61+
# Set environment variables (conf.py handles Django setup automatically)
62+
export DJANGO_SETTINGS_MODULE=eventyay.config.settings
63+
export EVENTYAY_CONFIG_FILE=$(pwd)/eventyay-docs.cfg
64+
export DATABASE_URL="sqlite:///:memory:"
65+
export REDIS_URL="redis://localhost:6379/0"
66+
export MPLCONFIGDIR=/tmp/matplotlib
67+
mkdir -p $MPLCONFIGDIR
68+
# Build documentation
69+
make html
70+
71+
- name: Prepare deployment
72+
run: |
73+
mkdir -p _site
74+
# Copy unified documentation (includes all components: tickets, talk, video)
75+
echo "Copying unified documentation..."
76+
cp -r doc/_build/html/* _site/
77+
# Copy CNAME for custom domain (docs.eventyay.com)
78+
cp doc/CNAME _site/
79+
# Create .nojekyll to prevent GitHub Pages from ignoring files starting with _
80+
touch _site/.nojekyll
81+
# List contents for verification
82+
echo "Documentation site structure:"
83+
ls -la _site/
84+
85+
- name: Setup Pages
86+
uses: actions/configure-pages@v4
87+
88+
- name: Upload artifact
89+
uses: actions/upload-pages-artifact@v3
90+
with:
91+
path: '_site'
92+
93+
deploy:
94+
environment:
95+
name: github-pages
96+
url: ${{ steps.deployment.outputs.page_url }}
97+
runs-on: ubuntu-latest
98+
needs: build
99+
steps:
100+
- name: Deploy to GitHub Pages
101+
id: deployment
102+
uses: actions/deploy-pages@v4
103+

.github/workflows/gh-pages-deploy.yml

Lines changed: 0 additions & 42 deletions
This file was deleted.

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ __pycache__/
1919
.mypy_cache/
2020
.ipython/
2121
_static/
22+
# Exception: Track documentation static files
23+
!doc/_static/
24+
!doc/talk/_static/
25+
!doc/video/_static/
2226
.idea
2327
.secret
2428
atlassian-ide-plugin.xml

README.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,11 +127,11 @@ After running ``uv sync```, activate a virtual environment
127127

128128
Do **not** edit the database numbers (0, 1, etc.).
129129

130-
8. **Create a superuser account** (for accessing the admin panel):
130+
8. **Create a admin user account** (for accessing the admin panel):
131131

132132
.. code-block:: bash
133133
134-
python manage.py createsuperuser
134+
python manage.py create_admin_user
135135
136136
9. **Run the development server**:
137137

File renamed without changes.

app/eventyay/base/management/commands/__init__.py

Whitespace-only changes.
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
from django.core.management.base import BaseCommand, CommandError
2+
from django.core.validators import validate_email
3+
from django.core.exceptions import ValidationError
4+
from django.contrib.auth.password_validation import validate_password
5+
from getpass import getpass
6+
7+
from eventyay.base.models.auth import User
8+
9+
10+
class Command(BaseCommand):
11+
help = 'Creates an admin user without setting is_superuser to True.'
12+
13+
def handle(self, *args, **options):
14+
email = self.get_email()
15+
password = self.get_password()
16+
17+
# Prevent duplicate users
18+
if User.objects.filter(email=email).exists():
19+
raise CommandError(f"A user with email {email} already exists.")
20+
21+
# Create admin user
22+
user = User.objects.create_adminuser(email=email, password=password)
23+
24+
self.stdout.write(self.style.SUCCESS(f"Successfully created admin user: {user.email}"))
25+
26+
def get_email(self):
27+
"""Prompt for email and validate format."""
28+
while True:
29+
email = input("E-mail: ").strip()
30+
if not email:
31+
self.stderr.write(self.style.ERROR("The email field cannot be empty."))
32+
continue
33+
try:
34+
validate_email(email)
35+
return email
36+
except ValidationError:
37+
self.stderr.write(self.style.ERROR("Please enter a valid email address."))
38+
39+
def get_password(self):
40+
"""Prompt for password and apply Django's password validation."""
41+
while True:
42+
password1 = self._get_password("Password: ")
43+
password2 = self._get_password("Confirm password: ")
44+
45+
if password1 != password2:
46+
self.stderr.write(self.style.ERROR("Passwords do not match. Please try again."))
47+
continue
48+
49+
try:
50+
validate_password(password1)
51+
return password1
52+
except ValidationError as e:
53+
for error in e.messages:
54+
self.stderr.write(self.style.ERROR(error))
55+
56+
def _get_password(self, prompt):
57+
"""Securely read a password input."""
58+
return getpass(prompt)

app/eventyay/base/models/access_code.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ class Meta:
7474
}
7575

7676
class urls(EventUrls):
77+
"""URL patterns for SubmitterAccessCode model views."""
7778
base = edit = '{self.event.cfp.urls.access_codes}{self.code}/'
7879
send = '{base}send'
7980
delete = '{base}delete/'

app/eventyay/base/models/auth.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,21 @@ def create_superuser(self, email: str, password: str = None): # NOQA
9797
user.save()
9898
return user
9999

100+
def create_adminuser(self, email: str, password: str = None):
101+
"""
102+
Command: python manage.py create_admin_user
103+
Create an admin user without setting is_superuser to True.
104+
"""
105+
if password is None:
106+
raise ValueError("You must provide a password")
107+
108+
user = self.model(email=email)
109+
user.is_staff = True
110+
user.is_administrator = True
111+
user.set_password(password)
112+
user.save(using=self._db)
113+
return user
114+
100115

101116
def generate_notifications_token():
102117
return get_random_string(length=32)
@@ -723,6 +738,7 @@ def reset_password(self, event, user=None, mail_text=None, orga=False):
723738
reset_password.alters_data = True
724739

725740
class orga_urls(EventUrls):
741+
"""URL patterns for organizer panel views related to this user."""
726742
admin = '/orga/admin/users/{self.code}/'
727743

728744
@transaction.atomic

app/eventyay/base/models/cfp.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,21 @@ def is_field_required(self, field):
6565
return self.fields.get(field, default_fields()[field])['visibility'] == 'required'
6666

6767
for field in default_fields().keys():
68-
setattr(cls, f'request_{field}', property(partial(is_field_requested, field=field)))
69-
setattr(cls, f'require_{field}', property(partial(is_field_required, field=field)))
68+
# Create wrapper functions with clean docstrings to avoid RST formatting issues
69+
def make_request_getter(field_name):
70+
def getter(self):
71+
return is_field_requested(self, field_name)
72+
getter.__doc__ = f"Check if {field_name} field is requested."
73+
return getter
74+
75+
def make_require_getter(field_name):
76+
def getter(self):
77+
return is_field_required(self, field_name)
78+
getter.__doc__ = f"Check if {field_name} field is required."
79+
return getter
80+
81+
setattr(cls, f'request_{field}', property(make_request_getter(field)))
82+
setattr(cls, f'require_{field}', property(make_require_getter(field)))
7083
return cls
7184

7285

@@ -103,6 +116,7 @@ class CfP(PretalxModel):
103116
fields = models.JSONField(default=default_fields)
104117

105118
class urls(EventUrls):
119+
"""URL patterns for public CfP (Call for Proposals) views."""
106120
base = '{self.event.orga_urls.cfp}'
107121
editor = '{base}flow/'
108122
questions = '{base}questions/'

0 commit comments

Comments
 (0)