Compare commits
5 Commits
master
...
with_posts
| Author | SHA1 | Date |
|---|---|---|
|
|
5b85fabcbd | |
|
|
d774f2f93c | |
|
|
982216767b | |
|
|
319d4374e8 | |
|
|
99da3f2800 |
|
|
@ -160,3 +160,12 @@ cython_debug/
|
||||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
|
# static
|
||||||
|
staticfiles/
|
||||||
|
|
||||||
|
# database
|
||||||
|
db.sqlite3
|
||||||
|
|
||||||
|
# todo
|
||||||
|
todo*
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -11,6 +11,7 @@ https://docs.djangoproject.com/en/3.0/ref/settings/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
|
@ -25,7 +26,7 @@ SECRET_KEY = '!2g)+m+_h9fq9%il5+t5#qnj^9502or6$=2!$==v=i2*c#7q*m'
|
||||||
# SECURITY WARNING: don't run with debug turned on in production!
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
|
|
||||||
ALLOWED_HOSTS = []
|
ALLOWED_HOSTS = ['localhost', '127.0.0.1', 'beyond-heroes.com', 'www.beyond-heroes.com']
|
||||||
|
|
||||||
|
|
||||||
# Application definition
|
# Application definition
|
||||||
|
|
@ -33,26 +34,43 @@ ALLOWED_HOSTS = []
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
'crispy_forms',
|
'crispy_forms',
|
||||||
'crispy_bootstrap4',
|
'crispy_bootstrap4',
|
||||||
|
|
||||||
'blog.apps.BlogConfig',
|
'blog.apps.BlogConfig',
|
||||||
'users.apps.UsersConfig',
|
'users.apps.UsersConfig',
|
||||||
|
'api.apps.APIConfig',
|
||||||
|
|
||||||
'django.contrib.admin',
|
'django.contrib.admin',
|
||||||
'django.contrib.auth',
|
'django.contrib.auth',
|
||||||
'django.contrib.contenttypes',
|
'django.contrib.contenttypes',
|
||||||
'django.contrib.sessions',
|
'django.contrib.sessions',
|
||||||
'django.contrib.messages',
|
'django.contrib.messages',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
|
'django.contrib.sites', # for django-allauth
|
||||||
|
|
||||||
|
'corsheaders',
|
||||||
|
'dj_rest_auth',
|
||||||
|
'rest_framework',
|
||||||
|
'rest_framework.authtoken',
|
||||||
|
# 'allauth',
|
||||||
|
# 'allauth.account',
|
||||||
|
# 'allauth.socialaccount',
|
||||||
|
# 'dj_rest_auth.registration', # for api side user registration
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
'django.middleware.security.SecurityMiddleware',
|
'django.middleware.security.SecurityMiddleware',
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
|
'corsheaders.middleware.CorsMiddleware', # cors-headers
|
||||||
'django.middleware.common.CommonMiddleware',
|
'django.middleware.common.CommonMiddleware',
|
||||||
'django.middleware.csrf.CsrfViewMiddleware',
|
'django.middleware.csrf.CsrfViewMiddleware',
|
||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
|
# 'allauth.account.middleware.AccountMiddleware',
|
||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
CORS_ORIGIN_WHITELIST = ( 'http://localhost:3000', 'http://www.beyond-heroes.com')
|
||||||
|
|
||||||
ROOT_URLCONF = 'BH.urls'
|
ROOT_URLCONF = 'BH.urls'
|
||||||
|
|
||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
|
|
@ -103,6 +121,17 @@ AUTH_PASSWORD_VALIDATORS = [
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
REST_FRAMEWORK = {
|
||||||
|
'DEFAULT_PERMISSION_CLASSES': [
|
||||||
|
# 'rest_framework.permissions.AllowAny',
|
||||||
|
'rest_framework.permissions.IsAuthenticated',
|
||||||
|
],
|
||||||
|
'DEFAULT_AUTHENTICATION_CLASSES': (
|
||||||
|
'rest_framework.authentication.SessionAuthentication', # causes CSRF Token conflicts in API
|
||||||
|
'rest_framework.authentication.TokenAuthentication',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
# Internationalization
|
# Internationalization
|
||||||
# https://docs.djangoproject.com/en/3.0/topics/i18n/
|
# https://docs.djangoproject.com/en/3.0/topics/i18n/
|
||||||
|
|
@ -121,6 +150,8 @@ USE_TZ = True
|
||||||
# Static files (CSS, JavaScript, Images)
|
# Static files (CSS, JavaScript, Images)
|
||||||
# https://docs.djangoproject.com/en/3.0/howto/static-files/
|
# https://docs.djangoproject.com/en/3.0/howto/static-files/
|
||||||
|
|
||||||
|
# remember to `python manage.py collectstatic`
|
||||||
|
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
|
||||||
STATIC_URL = '/static/'
|
STATIC_URL = '/static/'
|
||||||
MEDIA_ROOT = os.path.join(BASE_DIR + '/media')
|
MEDIA_ROOT = os.path.join(BASE_DIR + '/media')
|
||||||
MEDIA_URL = '/media/'
|
MEDIA_URL = '/media/'
|
||||||
|
|
@ -128,3 +159,8 @@ MEDIA_URL = '/media/'
|
||||||
CRISPY_TEMPLATE_PACK = 'bootstrap4'
|
CRISPY_TEMPLATE_PACK = 'bootstrap4'
|
||||||
LOGIN_REDIRECT_URL = 'Home'
|
LOGIN_REDIRECT_URL = 'Home'
|
||||||
LOGIN_URL = 'Login'
|
LOGIN_URL = 'Login'
|
||||||
|
|
||||||
|
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' # prints to console
|
||||||
|
SITE_ID = 1 # for django-allauth
|
||||||
|
|
||||||
|
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
"""TechBlog URL Configuration
|
"""BH URL Configuration
|
||||||
|
|
||||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||||
https://docs.djangoproject.com/en/3.0/topics/http/urls/
|
https://docs.djangoproject.com/en/3.0/topics/http/urls/
|
||||||
|
|
@ -20,8 +20,12 @@ from django.urls import path, include
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('', include('blog.urls')),
|
path('', include('blog.urls')),
|
||||||
path('users/', include('users.urls')),
|
|
||||||
path('admin/', admin.site.urls),
|
path('admin/', admin.site.urls),
|
||||||
|
path('api/v1/', include('api.urls')),
|
||||||
|
path('users/', include('users.urls')),
|
||||||
|
path('api-auth/', include('rest_framework.urls')),
|
||||||
|
path('api/v1/dj-rest-auth/', include('dj_rest_auth.urls')),
|
||||||
|
# path('api/v1/dj-rest-auth/register/', include('dj_rest_auth.registration.urls')),
|
||||||
]
|
]
|
||||||
|
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
from .views import *
|
||||||
|
from users.models import *
|
||||||
|
|
||||||
|
def is_action_processable(request):
|
||||||
|
user = Users.objects.get(request.body["user"])
|
||||||
|
if user:
|
||||||
|
# TODO implement
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def process_action(request):
|
||||||
|
if not is_action_processable(request):
|
||||||
|
return
|
||||||
|
print(request.body)
|
||||||
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
from django.contrib import admin
|
||||||
|
from .models import *
|
||||||
|
|
||||||
|
|
||||||
|
# Register your models here.
|
||||||
|
admin.site.register(Province)
|
||||||
|
admin.site.register(AssaultTroop)
|
||||||
|
admin.site.register(Player)
|
||||||
|
admin.site.register(Server)
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class APIConfig(AppConfig):
|
||||||
|
name = 'api'
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
# Defines Data ffs
|
||||||
|
|
||||||
|
|
||||||
|
VIEW_ACTION = 0
|
||||||
|
PURCHASE_ACTION = 1
|
||||||
|
MODIFY_ACTION = 2
|
||||||
|
EQUIP_ACTION = 3
|
||||||
|
|
||||||
|
# Need to define costs for purchase actions and requirements for modify and equip actions
|
||||||
|
|
||||||
|
# Saving space and reducing response times of the server is a priority so keeping in mind that this
|
||||||
|
# data will probably live on the RAM (as file-io is costly) so it needs to be small and easy to parse
|
||||||
|
|
||||||
|
# # sights,trg,brl
|
||||||
|
# # cost,ammo^inter^ >^skin,number
|
||||||
|
# # |----|^^|-||-|>^>^|--||------|
|
||||||
|
# WEAPON_DATA = """000000000000000000000000000000""" # Use Ints goddamit
|
||||||
|
# oh yeah, ints
|
||||||
|
WEAPON_DATA = [] # wtf
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
from rest_framework.filters import BaseFilterBackend
|
||||||
|
from users.models import *
|
||||||
|
|
||||||
|
class UserDataFilterBackend(BaseFilterBackend):
|
||||||
|
def filter_queryset(self, request, queryset, view):
|
||||||
|
queryset = queryset.filter(user__id=request.user.id)
|
||||||
|
|
||||||
|
return queryset
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
# Generated by Django 5.2.9 on 2026-02-19 10:09
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Player',
|
||||||
|
fields=[
|
||||||
|
('id', models.IntegerField(primary_key=True, serialize=False)),
|
||||||
|
('name', models.CharField(max_length=50)),
|
||||||
|
('faction', models.IntegerField()),
|
||||||
|
('server', models.CharField(max_length=20)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'ordering': ['id'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Province',
|
||||||
|
fields=[
|
||||||
|
('id', models.IntegerField(primary_key=True, serialize=False)),
|
||||||
|
('name', models.CharField(max_length=100)),
|
||||||
|
('faction', models.IntegerField()),
|
||||||
|
('map', models.CharField(max_length=255)),
|
||||||
|
('mov_speed', models.IntegerField()),
|
||||||
|
('ats', models.JSONField(null=True)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'ordering': ['id'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Server',
|
||||||
|
fields=[
|
||||||
|
('id', models.IntegerField(primary_key=True, serialize=False)),
|
||||||
|
('players', models.IntegerField()),
|
||||||
|
('capacity', models.IntegerField()),
|
||||||
|
('region', models.CharField(max_length=3)),
|
||||||
|
('address', models.CharField(max_length=20)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'ordering': ['id'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='AssaultTroop',
|
||||||
|
fields=[
|
||||||
|
('id', models.IntegerField(primary_key=True, serialize=False)),
|
||||||
|
('name', models.CharField(max_length=100)),
|
||||||
|
('faction', models.IntegerField()),
|
||||||
|
('type', models.IntegerField()),
|
||||||
|
('province', models.IntegerField()),
|
||||||
|
('orders', models.JSONField(null=True)),
|
||||||
|
('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ats', to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'ordering': ['id'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
from django.db import models
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from django.db.models.fields import CharField, IntegerField
|
||||||
|
|
||||||
|
|
||||||
|
# Create your models here.
|
||||||
|
class Province(models.Model):
|
||||||
|
id = models.IntegerField(primary_key = True)
|
||||||
|
name = models.CharField(max_length = 100, blank = False)
|
||||||
|
faction = models.IntegerField() # 0 - Neutral, 1 - Allies, 2 - Axis
|
||||||
|
map = models.CharField(max_length=255)
|
||||||
|
mov_speed = models.IntegerField()
|
||||||
|
ats = models.JSONField(null=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ['id']
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.name} - {self.faction}"
|
||||||
|
|
||||||
|
|
||||||
|
class AssaultTroop(models.Model):
|
||||||
|
id = models.IntegerField(primary_key = True)
|
||||||
|
name = models.CharField(max_length = 100, blank = False)
|
||||||
|
faction = models.IntegerField() # 0 - Neutral, 1 - Allies, 2 - Axis
|
||||||
|
type = models.IntegerField()
|
||||||
|
province = models.IntegerField() # Province ID (-1 for not deployed)
|
||||||
|
orders = models.JSONField(null=True)
|
||||||
|
owner = models.ForeignKey(
|
||||||
|
'auth.User',
|
||||||
|
related_name='ats',
|
||||||
|
on_delete=models.CASCADE
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ['id']
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.name} - {self.province},{self.faction}"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Player(models.Model):
|
||||||
|
id = models.IntegerField(primary_key = True)
|
||||||
|
name = models.CharField(max_length = 50, blank = False)
|
||||||
|
faction = models.IntegerField()
|
||||||
|
server = CharField(max_length = 20, blank = False)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ['id']
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Server(models.Model):
|
||||||
|
id = IntegerField(primary_key = True)
|
||||||
|
players = IntegerField() # total current players
|
||||||
|
capacity = IntegerField() # max player capacity
|
||||||
|
region = CharField(max_length = 3, blank = False) # 3 letter abb. for region
|
||||||
|
address = CharField(max_length = 20, blank = False)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ['id']
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.address} - {self.region}"
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
from rest_framework import permissions
|
||||||
|
|
||||||
|
class IsSuperUserOrReadOnly(permissions.BasePermission):
|
||||||
|
def has_permission(self, request, view):
|
||||||
|
if request.method in permissions.SAFE_METHODS:
|
||||||
|
return True
|
||||||
|
return request.user.is_superuser
|
||||||
|
|
||||||
|
|
||||||
|
class IsStaff(permissions.BasePermission):
|
||||||
|
def has_permission(self, request, view):
|
||||||
|
return request.user.is_staff
|
||||||
|
|
||||||
|
|
||||||
|
class IsSuperUserOrAuthReadOnly(permissions.BasePermission):
|
||||||
|
def has_permission(self, request, view):
|
||||||
|
if request.method in permissions.SAFE_METHODS:
|
||||||
|
return request.user != None
|
||||||
|
return request.user.is_superuser
|
||||||
|
|
||||||
|
|
||||||
|
class IsSuperUser(permissions.BasePermission):
|
||||||
|
def has_permission(self, request, view):
|
||||||
|
return request.user.is_superuser
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
from rest_framework import serializers
|
||||||
|
from .models import *
|
||||||
|
|
||||||
|
class ProvinceSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Province
|
||||||
|
fields = ['id', 'name', 'faction', 'map', 'mov_speed', 'ats']
|
||||||
|
|
||||||
|
|
||||||
|
class AssaultTroopSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = AssaultTroop
|
||||||
|
fields = ['id', 'name', 'faction', 'type', 'province', 'orders', 'owner']
|
||||||
|
|
||||||
|
|
||||||
|
class PlayerSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Player
|
||||||
|
fields = ['id', 'name', 'faction', 'server']
|
||||||
|
|
||||||
|
|
||||||
|
class ServerSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Server
|
||||||
|
fields = ['id', 'players', 'capacity', 'region', 'address']
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
from django import template
|
||||||
|
from django.template.defaultfilters import stringfilter
|
||||||
|
|
||||||
|
import markdown as md
|
||||||
|
|
||||||
|
|
||||||
|
register = template.Library()
|
||||||
|
|
||||||
|
@register.filter()
|
||||||
|
@stringfilter
|
||||||
|
def markdown(value):
|
||||||
|
return md.markdown(value, extensions=['markdown.extensions.fenced_code'])
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
from django.test import TestCase
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from rest_framework.test import APITestCase, APIClient
|
||||||
|
from rest_framework import status
|
||||||
|
|
||||||
|
# Create your tests here.
|
||||||
|
class UnAuthAccessTests(APITestCase):
|
||||||
|
def test_province_get(self):
|
||||||
|
response = self.client.get("/api/v1/provinces/")
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
def test_assault_troops_get(self):
|
||||||
|
response = self.client.get("/api/v1/assault_troops/")
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
def test_servers_get(self):
|
||||||
|
response = self.client.get("/api/v1/servers/")
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
def test_players_get(self):
|
||||||
|
response = self.client.get("/api/v1/players/")
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
||||||
|
|
||||||
|
def test_user_action_get(self):
|
||||||
|
response = self.client.get("/api/v1/user_action/")
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
||||||
|
|
||||||
|
def test_user_action_post(self):
|
||||||
|
response = self.client.post("/api/v1/user_action/", {"user": 1, "action": "142"}, format="json")
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
||||||
|
|
||||||
|
|
||||||
|
class AuthAccessTests(APITestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.user = User.objects.create_user(username="test", password="pass123")
|
||||||
|
self.user2 = User.objects.create_user(username="test2", password="pass123", is_staff=True)
|
||||||
|
|
||||||
|
def test_province_get(self):
|
||||||
|
self.client.login(username="test", password="pass123")
|
||||||
|
response = self.client.get("/api/v1/provinces/")
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
def test_assault_troops_get(self):
|
||||||
|
self.client.login(username="test", password="pass123")
|
||||||
|
response = self.client.get("/api/v1/assault_troops/")
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
def test_servers_get(self):
|
||||||
|
self.client.login(username="test", password="pass123")
|
||||||
|
response = self.client.get("/api/v1/servers/")
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
def test_players_get(self):
|
||||||
|
self.client.login(username="test", password="pass123")
|
||||||
|
response = self.client.get("/api/v1/players/")
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
||||||
|
|
||||||
|
def test_players_staff_get(self):
|
||||||
|
self.client.login(username="test2", password="pass123")
|
||||||
|
response = self.client.get("/api/v1/players/")
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
def test_user_action_post(self):
|
||||||
|
self.client.login(username="test", password="pass123")
|
||||||
|
response = self.client.post("/api/v1/user_action/", {"user": 1, "action": "142"}, format="json")
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||||
|
|
||||||
|
def test_user_data_get(self):
|
||||||
|
self.client.login(username="test", password="pass123")
|
||||||
|
response = self.client.get("/api/v1/user_data/")
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
from django.urls import path
|
||||||
|
from .views import *
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('provinces/', ProvincesView.as_view()),
|
||||||
|
path('provinces/<int:pk>/', ProvinceView.as_view()),
|
||||||
|
path('assault_troops/', AssaultTroopsView.as_view()),
|
||||||
|
path('assault_troops/<int:nm>/', AssaultTroopView.as_view()),
|
||||||
|
path('players/', PlayersView.as_view()),
|
||||||
|
path('players/<int:nm>/', PlayerView.as_view()),
|
||||||
|
path('servers/', ServersView.as_view()),
|
||||||
|
path('servers/<int:nm>/', ServerView.as_view()),
|
||||||
|
path('user_data/', UserDataView.as_view()),
|
||||||
|
path('user_data/<int:pk>/', UserDatumView.as_view()),
|
||||||
|
path('user_action/', UserActionView.as_view()),
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,87 @@
|
||||||
|
from rest_framework import generics, permissions, mixins
|
||||||
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
from .models import *
|
||||||
|
from .filters import *
|
||||||
|
from .actions import *
|
||||||
|
from .serializers import *
|
||||||
|
from .permissions import *
|
||||||
|
from users.models import *
|
||||||
|
from users.serializers import *
|
||||||
|
|
||||||
|
@method_decorator(csrf_exempt, name='dispatch')
|
||||||
|
class ProvincesView(generics.ListCreateAPIView):
|
||||||
|
permission_classes = (IsSuperUserOrReadOnly,)
|
||||||
|
queryset = Province.objects.all()
|
||||||
|
serializer_class = ProvinceSerializer
|
||||||
|
|
||||||
|
@method_decorator(csrf_exempt, name='dispatch')
|
||||||
|
class ProvinceView(generics.RetrieveUpdateDestroyAPIView):
|
||||||
|
permission_classes = (IsSuperUserOrReadOnly,)
|
||||||
|
queryset = Province.objects.all()
|
||||||
|
serializer_class = ProvinceSerializer
|
||||||
|
|
||||||
|
|
||||||
|
@method_decorator(csrf_exempt, name='dispatch')
|
||||||
|
class AssaultTroopsView(generics.ListCreateAPIView):
|
||||||
|
permission_classes = (IsSuperUserOrReadOnly,)
|
||||||
|
queryset = AssaultTroop.objects.all()
|
||||||
|
serializer_class = AssaultTroopSerializer
|
||||||
|
|
||||||
|
@method_decorator(csrf_exempt, name='dispatch')
|
||||||
|
class AssaultTroopView(generics.RetrieveUpdateDestroyAPIView):
|
||||||
|
permission_classes = (IsSuperUserOrReadOnly,)
|
||||||
|
queryset = AssaultTroop.objects.all()
|
||||||
|
serializer_class = AssaultTroopSerializer
|
||||||
|
|
||||||
|
|
||||||
|
@method_decorator(csrf_exempt, name='dispatch')
|
||||||
|
class PlayersView(generics.ListCreateAPIView):
|
||||||
|
permission_classes = (IsStaff,) # Only Staff can see player info, i.e. authorized servers
|
||||||
|
queryset = Player.objects.all()
|
||||||
|
serializer_class = PlayerSerializer
|
||||||
|
|
||||||
|
@method_decorator(csrf_exempt, name='dispatch')
|
||||||
|
class PlayerView(generics.RetrieveUpdateDestroyAPIView):
|
||||||
|
permission_classes = (IsStaff,)
|
||||||
|
queryset = Player.objects.all()
|
||||||
|
serializer_class = PlayerSerializer
|
||||||
|
|
||||||
|
|
||||||
|
@method_decorator(csrf_exempt, name='dispatch')
|
||||||
|
class ServersView(generics.ListCreateAPIView):
|
||||||
|
permission_classes = (IsSuperUserOrReadOnly,)
|
||||||
|
queryset = Server.objects.all()
|
||||||
|
serializer_class = ServerSerializer
|
||||||
|
|
||||||
|
@method_decorator(csrf_exempt, name='dispatch')
|
||||||
|
class ServerView(generics.RetrieveUpdateDestroyAPIView):
|
||||||
|
permission_classes = (IsSuperUserOrReadOnly,)
|
||||||
|
queryset = Server.objects.all()
|
||||||
|
serializer_class = ServerSerializer
|
||||||
|
|
||||||
|
|
||||||
|
@method_decorator(csrf_exempt, name='dispatch')
|
||||||
|
class UserDatumView(generics.RetrieveUpdateDestroyAPIView):
|
||||||
|
permission_classes = (IsSuperUser,)
|
||||||
|
queryset = UserData.objects.all()
|
||||||
|
serializer_class = UserDataSerializer
|
||||||
|
# filter_backends = [UserDataFilterBackend]
|
||||||
|
|
||||||
|
@method_decorator(csrf_exempt, name='dispatch')
|
||||||
|
class UserDataView(generics.ListCreateAPIView):
|
||||||
|
permission_classes = (IsSuperUserOrAuthReadOnly,)
|
||||||
|
queryset = UserData.objects.all()
|
||||||
|
serializer_class = UserDataSerializer
|
||||||
|
filter_backends = [UserDataFilterBackend]
|
||||||
|
|
||||||
|
|
||||||
|
@method_decorator(csrf_exempt, name='dispatch')
|
||||||
|
class UserActionView(mixins.CreateModelMixin, generics.GenericAPIView):
|
||||||
|
permission_classes = (permissions.IsAuthenticated,)
|
||||||
|
queryset = UserAction.objects.all()
|
||||||
|
serializer_class = UserActionSerializer
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
process_action(request)
|
||||||
|
return self.create(request, *args, **kwargs)
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,9 @@
|
||||||
|
from django import forms
|
||||||
|
from .models import Post
|
||||||
|
|
||||||
|
|
||||||
|
class PostForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Post
|
||||||
|
fields = ['content']
|
||||||
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
# Generated by Django 5.2.9 on 2026-02-19 10:09
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('blog', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='blog',
|
||||||
|
name='id',
|
||||||
|
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='post',
|
||||||
|
name='id',
|
||||||
|
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
|
||||||
|
),
|
||||||
|
]
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -1,6 +1,6 @@
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils import timezone
|
# from django.utils import timezone
|
||||||
|
|
||||||
|
|
||||||
# Create your models here.
|
# Create your models here.
|
||||||
|
|
@ -8,7 +8,8 @@ class Blog(models.Model):
|
||||||
content = models.TextField()
|
content = models.TextField()
|
||||||
title = models.CharField(max_length=150)
|
title = models.CharField(max_length=150)
|
||||||
author = models.ForeignKey(User, on_delete=models.CASCADE)
|
author = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
date_posted = models.DateTimeField(default=timezone.now)
|
# date_posted = models.DateTimeField(default=timezone.now)
|
||||||
|
date_posted = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return '/'
|
return '/'
|
||||||
|
|
@ -18,4 +19,4 @@ class Blog(models.Model):
|
||||||
class Post(models.Model):
|
class Post(models.Model):
|
||||||
content = models.TextField()
|
content = models.TextField()
|
||||||
author = models.ForeignKey(User, on_delete=models.CASCADE)
|
author = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
date_posted = models.DateTimeField(default=timezone.now)
|
date_posted = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
|
||||||
Binary file not shown.
12
blog/urls.py
12
blog/urls.py
|
|
@ -4,10 +4,12 @@ from . import views
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('', views.home, name='Home'),
|
path('', views.home, name='Home'),
|
||||||
path('news/', views.news, name='News'),
|
path('news/', views.news, name='News'),
|
||||||
path('blog/<int:pk>', views.BlogDetailView.as_view(), name='Blog'),
|
# path('blog/<int:pk>', views.BlogDetailView.as_view(), name='Blog'),
|
||||||
path('blog/create/', views.BlogCreateView.as_view(), name='Blog Create'),
|
# path('blog/create/', views.BlogCreateView.as_view(), name='Blog Create'),
|
||||||
path('post/<int:pk>', views.PostDetailView.as_view(), name='Post'),
|
# path('post/<int:pk>', views.PostDetailView.as_view(), name='Post'),
|
||||||
path('post/create/', views.PostCreateView.as_view(), name='Post Create'),
|
# path('post/create/', views.PostCreateView.as_view(), name='Post Create'),
|
||||||
path('dev/', views.dev, name='Dev'),
|
# path('dev/', views.dev, name='Dev'),
|
||||||
|
path('chat/', views.chat, name='Chat'),
|
||||||
|
path("chat/p/", views.posts_partial, name="ChatPartial"),
|
||||||
path('dev/support/', views.support, name='Support'),
|
path('dev/support/', views.support, name='Support'),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,10 @@ from django.shortcuts import render
|
||||||
from django.contrib.auth.mixins import *
|
from django.contrib.auth.mixins import *
|
||||||
from django.views.generic import *
|
from django.views.generic import *
|
||||||
from .models import *
|
from .models import *
|
||||||
|
from .forms import *
|
||||||
|
|
||||||
|
MAX_POSTS = 150
|
||||||
|
MARGIN = 20
|
||||||
|
|
||||||
# Create your views here.
|
# Create your views here.
|
||||||
def news(request):
|
def news(request):
|
||||||
|
|
@ -49,7 +52,7 @@ class BlogCreateView(LoginRequiredMixin, CreateView):
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
form.instance.author = self.request.user
|
form.instance.author = self.request.user
|
||||||
return super().form_valid(form)
|
return super().form_valid(form) and self.request.user.is_staff
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -68,6 +71,39 @@ class PostCreateView(LoginRequiredMixin, CreateView):
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
|
def chat(request):
|
||||||
|
if request.method == "POST":
|
||||||
|
form = PostForm(request.POST)
|
||||||
|
|
||||||
|
if form.is_valid():
|
||||||
|
post = form.save(commit=False)
|
||||||
|
post.author = request.user
|
||||||
|
post.save()
|
||||||
|
print(Post.objects.count())
|
||||||
|
if Post.objects.count() > MAX_POSTS + MARGIN:
|
||||||
|
qs = Post.objects.order_by("-date_posted")
|
||||||
|
old_ids = qs.values_list("id", flat=True)[MAX_POSTS:]
|
||||||
|
|
||||||
|
if old_ids:
|
||||||
|
Post.objects.filter(id__in=old_ids).delete()
|
||||||
|
|
||||||
|
return render(request, "blog/partials/post.html", {"post": post})
|
||||||
|
|
||||||
|
posts = Post.objects.select_related("author").order_by("-date_posted")
|
||||||
|
form = PostForm()
|
||||||
|
|
||||||
|
return render(request, "blog/postList.html", {
|
||||||
|
"posts": posts,
|
||||||
|
"form": form
|
||||||
|
})
|
||||||
|
|
||||||
|
def posts_partial(request):
|
||||||
|
posts = Post.objects.select_related("author").order_by("-date_posted")[:40]
|
||||||
|
|
||||||
|
return render(request, "blog/partials/postList.html", {
|
||||||
|
"posts": posts
|
||||||
|
})
|
||||||
|
|
||||||
def dev(request):
|
def dev(request):
|
||||||
return render(request, 'dev.html', {'title': 'Development'})
|
return render(request, 'dev.html', {'title': 'Development'})
|
||||||
|
|
||||||
|
|
|
||||||
BIN
db.sqlite3
BIN
db.sqlite3
Binary file not shown.
|
|
@ -1,13 +1,19 @@
|
||||||
Django==5.0.2
|
asgiref==3.11.0
|
||||||
django-bootstrap4==22.3
|
beautifulsoup4==4.14.3
|
||||||
django-braces==1.15.0
|
crispy-bootstrap4==2025.6
|
||||||
django-cors-headers==4.3.1
|
Django==5.2.9
|
||||||
django-crispy-forms==2.1
|
dj-rest-auth==7.1.1
|
||||||
Markdown==3.4.1
|
django-bootstrap4==25.3
|
||||||
|
django-braces==1.17.0
|
||||||
|
django-cors-headers==4.9.0
|
||||||
|
django-crispy-forms==2.5
|
||||||
|
django-rest-framework==0.1.0
|
||||||
|
djangorestframework==3.16.1
|
||||||
|
Markdown==3.10
|
||||||
MarkupSafe==2.1.1
|
MarkupSafe==2.1.1
|
||||||
crispy==0.7.3
|
mysqlclient==2.2.7
|
||||||
crispy-bootstrap4==2023.1
|
python-dotenv==1.2.1
|
||||||
bootstrap4==0.1.0
|
soupsieve==2.8
|
||||||
cachetools==5.2.0
|
sqlparse==0.5.4
|
||||||
certifi==2022.9.24
|
typing_extensions==4.15.0
|
||||||
cffi==1.15.1
|
tzdata==2025.3
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
@ echo off
|
@ echo off
|
||||||
echo The devlopment server will start in a few seconds...
|
echo The devlopment server will start in a few seconds...
|
||||||
python manage.py makemigrations
|
|
||||||
python manage.py migrate
|
|
||||||
python manage.py runserver 3000
|
python manage.py runserver 3000
|
||||||
|
|
|
||||||
|
|
@ -3,80 +3,535 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Beyond Heroes</title>
|
<title>Beyond Heroes - Official Website</title>
|
||||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
|
<!-- <link rel="stylesheet" href="demo3.css"> -->
|
||||||
<!-- Custom CSS -->
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
<style>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
/* body {
|
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;700&display=swap" rel="stylesheet">
|
||||||
background-color: #f8f9fa;
|
<link href="https://fonts.googleapis.com/css2?family=Mozilla+Headline" rel="stylesheet">
|
||||||
color: #eeeeee;
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||||
|
|
||||||
|
<style type="text/css">
|
||||||
|
:root {
|
||||||
|
--primary-color: #009020; /* Accents */
|
||||||
|
--primary-dark-color: #006020; /* Secondary Accents */
|
||||||
|
--secondary-color: #333; /* Dark gray for text */
|
||||||
|
--background-color: #1a1a1a; /* Dark background */
|
||||||
|
--card-bg-color: #2a2a2a; /* Background for cards */
|
||||||
|
/* --font-family: 'Montserrat', sans-serif;*/
|
||||||
}
|
}
|
||||||
.card {
|
|
||||||
border: 1px;
|
@font-face {
|
||||||
border-radius: 15px;
|
font-family: 'Alte DIN 1451 Mittelschrift';
|
||||||
border-color: #eeeeee;
|
src: url('mittelschrift.ttf') format('truetype'); /* Chrome 4+, Firefox 3.5, Opera 10+, Safari 3—5 */
|
||||||
box-shadow: 0 0 10px rgba(250, 250, 250, 0.1);
|
|
||||||
}
|
}
|
||||||
.list-group-item {
|
body {
|
||||||
background-color: #1e1f10;
|
font-family: 'Alte DIN 1451 Mittelschrift', sans-serif;
|
||||||
border-color: #eeeeee;
|
|
||||||
}
|
}
|
||||||
.card-header {
|
|
||||||
background-color: #111111;
|
/* Allgemeine Stile (General Styles) */
|
||||||
border-bottom: 1px solid #eeeeee;
|
body {
|
||||||
|
margin: 0;
|
||||||
|
/* font-family: var(--font-family);*/
|
||||||
|
color: white;
|
||||||
|
background-color: var(--background-color);
|
||||||
|
line-height: 1.6;
|
||||||
}
|
}
|
||||||
.card-body {
|
|
||||||
background-color: #1e1f10;
|
h1, h2 {
|
||||||
|
font-family: 'Mozilla Headline';
|
||||||
|
letter-spacing: -1px;
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
.profile-image {
|
|
||||||
max-width: 125px;
|
h3 {
|
||||||
border-radius: 50%;
|
margin: 0;
|
||||||
padding: 20px;
|
}
|
||||||
}*/
|
|
||||||
.link {
|
a {
|
||||||
|
color: var(--primary-color);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
section {
|
||||||
|
padding: 60px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
border: 0;
|
||||||
|
border-top: 1px solid #444;
|
||||||
|
margin: 15px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hero-Bereich (Hero Section) */
|
||||||
|
.hero-section {
|
||||||
|
position: relative;
|
||||||
|
height: 100vh; /* Volle Bildschirmhöhe (Full viewport height) */
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-content {
|
||||||
|
background-color: rgba(0, 0, 0, 0.8);
|
||||||
|
padding: 40px;
|
||||||
|
margin: 0px 20%;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-section h1 {
|
||||||
|
font-size: 4.7em;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-block-start: 0;
|
||||||
|
margin-block-end: 0;
|
||||||
|
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-section p {
|
||||||
|
margin-block-start: 0;
|
||||||
|
font-size: 1.2em;
|
||||||
|
font-weight: lighter;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.discord-button {
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
color: var(--background-color);
|
||||||
|
padding: 15px 30px;
|
||||||
|
letter-spacing: +0.8px;
|
||||||
|
border-radius: 50px;
|
||||||
|
font-weight: bold;
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.discord-button:hover {
|
||||||
|
background-color: var(--primary-dark-color);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Team-Bereich (Team Section) */
|
||||||
|
.team-container {
|
||||||
|
display: flex;
|
||||||
|
gap: 40px;
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: auto;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dev-cards-column {
|
||||||
|
flex: 6;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dev-card {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 20px;
|
||||||
|
background-color: var(--card-bg-color);
|
||||||
|
padding: 15px;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dev-card:hover, .dev-card.active {
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
color: var(--background-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dev-card:hover .dev-info p, .dev-card.active .dev-info p {
|
||||||
|
color: var(--background-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dev-card img {
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dev-info h3 {
|
||||||
|
margin-block-end: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dev-info p {
|
||||||
|
margin-block-start: 0;
|
||||||
|
margin-block-end: 0;
|
||||||
|
font-style: italic;
|
||||||
|
color: #bbb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dev-bio-column {
|
||||||
|
flex: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
#dev-bio-box {
|
||||||
|
background-color: var(--card-bg-color);
|
||||||
|
padding: 30px;
|
||||||
|
border-radius: 8px;
|
||||||
|
min-height: 975px; /* Stellt sicher, dass die Box groß genug ist (Ensures the box is large enough) */
|
||||||
|
}
|
||||||
|
|
||||||
|
#dev-bio-box p {
|
||||||
|
margin-block-start: 5px;
|
||||||
|
margin-block-end: 0;
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#dev-bio-box h3 {
|
||||||
|
color: var(--primary-color);
|
||||||
|
margin-block-end: 0;
|
||||||
|
font-size: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* NEUE NAV-BAR (NEW NAV BAR) */
|
||||||
|
#navbar {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px 00px;
|
||||||
|
z-index: 1000;
|
||||||
|
transition: background-color 0.3s ease, padding 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
#navbar .nav-brand{
|
||||||
|
margin-left: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#navbar .nav-links-div{
|
||||||
|
margin-right: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#navbar.scrolled {
|
||||||
|
position: fixed;
|
||||||
|
padding: 20px 40px;
|
||||||
|
top: 0;
|
||||||
|
background-color: var(--background-color); /* Feste Hintergrundfarbe (Solid background color) */
|
||||||
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
|
||||||
|
padding: 10px 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#navbar.scrolled .nav-brand{
|
||||||
|
margin-left: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#navbar.scrolled .nav-links-div{
|
||||||
|
margin-right: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-brand {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.5em;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-links {
|
||||||
|
list-style: none;
|
||||||
|
letter-spacing: +0.6px;
|
||||||
|
display: flex;
|
||||||
|
gap: 30px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-links a {
|
||||||
|
color: white;
|
||||||
|
font-weight: bold;
|
||||||
|
transition: color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-links button {
|
||||||
|
color: white;
|
||||||
|
font-weight: bold;
|
||||||
|
transition: color 0.3s ease;
|
||||||
|
background: transparent;
|
||||||
|
box-shadow: 0 0 0 #000;
|
||||||
|
border-width: 0;
|
||||||
|
font-size: 1em;
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-links a:hover, button:hover{
|
||||||
|
color: var(--primary-color);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive Design (Für kleinere Bildschirme) (For smaller screens) */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.hero-section h1 {
|
||||||
|
font-size: 2.5em;
|
||||||
|
}
|
||||||
|
.hero-content p {
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
.news-container, .team-container {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.dev-bio-column {
|
||||||
|
min-height: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* NEUE ALLGEMEINE HILFSKLASSE (NEW GENERAL HELPER CLASS) */
|
||||||
|
.text-center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* NEUER SUPPORT-BEREICH (NEW SUPPORT SECTION) */
|
||||||
|
#support-section {
|
||||||
|
/* margin-top: 90px;*/
|
||||||
|
padding: 80px 20px;
|
||||||
|
/* background-color: var(--card-bg-color);*/
|
||||||
|
}
|
||||||
|
|
||||||
|
#support-section h2 {
|
||||||
|
color: var(--primary-color);
|
||||||
|
font-size: 2.5em;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
color: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
#support-section h3 {
|
||||||
|
margin: 20px;
|
||||||
|
color: #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
#support-section p {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: auto;
|
||||||
|
font-size: 1.1em;
|
||||||
|
color: #bbb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* NEUER SUPPORT-BEREICH (NEW SUPPORT SECTION) */
|
||||||
|
#about-section {
|
||||||
|
padding: 80px 20px;
|
||||||
|
background-color: var(--card-bg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#about-section h2 {
|
||||||
|
color: var(--primary-color);
|
||||||
|
font-size: 2.5em;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#about-section h3 {
|
||||||
|
margin: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#about-section p {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: auto;
|
||||||
|
font-size: 1.1em;
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fußzeile (Footer) */
|
||||||
|
footer {
|
||||||
|
background-color: #111;
|
||||||
|
text-align: center;
|
||||||
|
padding: 30px;
|
||||||
|
margin-top: 50px;
|
||||||
|
font-size: 0.9em;
|
||||||
|
color: #bbb;
|
||||||
|
}
|
||||||
|
footer p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.footer-socials {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-socials a {
|
||||||
|
color: #fff;
|
||||||
|
font-size: 2em; /* Größe der Icons (Size of the icons) */
|
||||||
|
margin: 0 50px;
|
||||||
|
transition: color 0.3s ease;
|
||||||
|
}
|
||||||
|
.footer-socials a:hover {
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Neuigkeiten-Bereich (News Section) */
|
||||||
|
#news-section h2, #team-section h2 {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
font-size: 2.5em;
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* NEUE STILE FÜR DEN NEUIGKEITEN-BEREICH (NEW STYLES FOR NEWS SECTION) */
|
||||||
|
.news-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 30px;
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: auto;
|
||||||
|
flex-wrap: wrap; /* Erlaubt das Umbrechen auf kleineren Bildschirmen (Allows wrapping on smaller screens) */
|
||||||
|
}
|
||||||
|
|
||||||
|
.news-post {
|
||||||
|
background-color: var(--card-bg-color);
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden; /* Stellt sicher, dass das Bild innerhalb der Form bleibt (Ensures the image stays within the shape) */
|
||||||
|
width: 350px; /* Feste Breite für jede Karte (Fixed width for each card) */
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.4);
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.news-post:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.news-post img {
|
||||||
|
width: 100%;
|
||||||
|
height: 200px;
|
||||||
|
object-fit: cover;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-content {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.news-post h3 {
|
||||||
|
color: var(--primary-color);
|
||||||
|
margin-block-end: 0;
|
||||||
|
font-size: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.news-post p {
|
||||||
|
font-size: 1.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.news-post p.news-date {
|
||||||
|
margin-block-start: 0;
|
||||||
|
margin-block-end: 0;
|
||||||
|
font-style: italic;
|
||||||
|
font-size: 0.9em;
|
||||||
|
color: #bbb;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.news-post .read-more {
|
||||||
|
display: inline-block;
|
||||||
|
color: var(--primary-color);
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
.news-post .read-more:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ... Der Rest des CSS-Codes bleibt gleich ... */
|
||||||
|
|
||||||
|
/* Responsive Design (Für kleinere Bildschirme) */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
/* ... Die bereits existierenden Media-Query-Styles bleiben bestehen ... */
|
||||||
|
.news-container {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.news-post {
|
||||||
|
width: 90%; /* Nimmt fast die ganze Breite ein (Takes up almost the full width) */
|
||||||
|
max-width: 400px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************************************************************************************/
|
||||||
|
|
||||||
|
/* NEUER SUPPORT-BEREICH (NEW SUPPORT SECTION) */
|
||||||
|
#supporters-carousel {
|
||||||
|
padding-top: 50px;
|
||||||
|
text-align: center;
|
||||||
|
/* background-color: var(--card-bg-color);*/
|
||||||
|
}
|
||||||
|
#supporters-carousel h3 {
|
||||||
|
color: white;
|
||||||
|
font-size: 2em;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
.carousel-container {
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
position: relative;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
.carousel-content {
|
||||||
|
display: inline-block;
|
||||||
|
animation: scroll-left 20s linear infinite; /* Animation für das Karussell (Animation for the carousel) */
|
||||||
|
}
|
||||||
|
.supporter-item {
|
||||||
|
display: inline-block;
|
||||||
|
background-color: var(--card-bg-color);
|
||||||
|
border: 1px solid var(--primary-color);
|
||||||
|
color: white;
|
||||||
|
padding: 15px 25px;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin: 0 10px;
|
||||||
|
text-align: center;
|
||||||
|
min-width: 150px;
|
||||||
|
}
|
||||||
|
.supporter-item h4 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
.supporter-item p {
|
||||||
|
margin: 5px 0 0;
|
||||||
|
font-size: 1.1em;
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
/* Animation Keyframes für das Scrollen (Animation keyframes for scrolling) */
|
||||||
|
@keyframes scroll-left {
|
||||||
|
0% { transform: translateX(0); }
|
||||||
|
100% { transform: translateX(-50%); }
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<nav class="navbar navbar-expand-lg navbar-dark" style="background-color: #000;">
|
|
||||||
<a class="navbar-brand" href="/">
|
<nav id="navbar">
|
||||||
<img src="/media/logo.jpg" alt="BH Logo" height="40"> Beyond Heroes
|
<div class="nav-brand">Beyond Heroes</div>
|
||||||
</a>
|
<div class="nav-links-div">
|
||||||
<!-- <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
<ul class="nav-links">
|
||||||
<span class="navbar-toggler-icon"></span>
|
{% if user.is_authenticated %}
|
||||||
</button> -->
|
<li><a href="/#news-section">News</a></li>
|
||||||
<div class="ml-auto"></div>
|
<li><a href="{% url 'Chat' %}">Chat</a></li>
|
||||||
<div class="justify-content-center" id="navbarNav">
|
{% else %}
|
||||||
<ul class="navbar-nav">
|
<li><a href="/#hero-section">Home</a></li>
|
||||||
<li class="nav-item" style="padding-right: 10px; padding-left: 10px;">
|
<li><a href="/#about-section">About</a></li>
|
||||||
<a class="nav-link" href="/news">News</a>
|
{% endif %}
|
||||||
</li>
|
<li><a href="/dev/support">Support Us</a></li>
|
||||||
<li class="nav-item" style="padding-right: 10px; padding-left: 10px;">
|
|
||||||
<a class="nav-link" href="/dev/">Development</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item" style="padding-right: 10px; padding-left: 10px;">
|
|
||||||
<a class="nav-link" href="/dev/support/">Support Us</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="ml-auto"></div>
|
|
||||||
<div class="justify-content-center" id="navbarNav">
|
|
||||||
<ul class="navbar-nav">
|
|
||||||
{% if user.is_authenticated %}
|
{% if user.is_authenticated %}
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<form action="{% url 'Logout' %}" method="post">
|
<form action="{% url 'Logout' %}" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<button class="nav-link btn btn-link" type="submit">Logout</button>
|
<button class="nav-link" type="submit">Logout</button>
|
||||||
</form>
|
</form>
|
||||||
</li>
|
</li>
|
||||||
{% else %}
|
{% else %}
|
||||||
<li class="nav-item">
|
<!-- <li class="nav-item">
|
||||||
<a class="nav-link" href="{% url 'Login' %}">Login</a>
|
<a class="nav-link" href="{% url 'Login' %}">Login</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="{% url 'Register' %}">Register</a>
|
<a class="nav-link" href="{% url 'Register' %}">Register</a>
|
||||||
</li>
|
</li> -->
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -86,14 +541,34 @@
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
<div style="padding: 30px;"></div>
|
|
||||||
<footer>
|
<footer>
|
||||||
<div class="container">
|
<div class="footer-socials">
|
||||||
<!-- <p style="text-align: center;">© Beyond Heroes. All rights reserved.</p> -->
|
<a href="https://www.youtube.com/@BeyondHeroesWWII/" target="blank" aria-label="YouTube"><i class="fab fa-youtube"></i></a>
|
||||||
|
<a href="https://discord.gg/gnnfKKuumg" target="blank" aria-label="Discord"><i class="fab fa-discord"></i></a>
|
||||||
|
<!-- <a href="#" aria-label="Twitter"><i class="fab fa-twitter"></i></a> -->
|
||||||
|
<a href="https://www.reddit.com/r/beyondheroes/" target="blank" aria-label="Reddit"><i class="fab fa-reddit"></i></a>
|
||||||
</div>
|
</div>
|
||||||
|
<p style="padding-top:10px">Beyond Heroes™ | 2026 All Rights Reserved.</p>
|
||||||
</footer>
|
</footer>
|
||||||
<!-- Bootstrap JavaScript and dependencies -->
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.6/dist/umd/popper.min.js"></script>
|
<script type="text/javascript">
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.min.js"></script>
|
const navbar = document.getElementById('navbar');
|
||||||
|
const heroSection = document.querySelector('.hero-section');
|
||||||
|
|
||||||
|
if (heroSection == null) {
|
||||||
|
navbar.classList.add('scrolled');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
window.addEventListener('scroll', () => {
|
||||||
|
const heroHeight = heroSection.offsetHeight;
|
||||||
|
if (window.scrollY > heroHeight - 70) {
|
||||||
|
navbar.classList.add('scrolled');
|
||||||
|
} else {
|
||||||
|
navbar.classList.remove('scrolled');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
@ -1,85 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
{% if title %}
|
|
||||||
<title>WeBlog - {{ title }}</title>
|
|
||||||
{% else %}
|
|
||||||
<title>WeBlog - We connect your brains!</title>
|
|
||||||
{% endif %}
|
|
||||||
<link rel="icon" type="image/x-icon" href="WeBlog.ico">
|
|
||||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
|
|
||||||
integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
|
|
||||||
<script src="https://code.jquery.com/jquery-3.1.1.min.js" integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8="></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<nav class="navbar navbar-expand-lg navbar-dark bg-primary rounded-bottom" style="background: #a4e5ff; margin-bottom:10px">
|
|
||||||
<div class="container">
|
|
||||||
<a class="navbar-brand" href="/">TechBlog</a>
|
|
||||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
|
||||||
<span class="navbar-toggler-icon"></span>
|
|
||||||
</button>
|
|
||||||
<div class="collapse navbar-collapse" id="navbarNav">
|
|
||||||
<ul class="navbar-nav">
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" href="/">Home<span class="sr-only">(current)</span></a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" href="{% url 'About' %}">About</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" href="{% url 'People' %}">People</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" href="{% url 'Explore' %}">Explore</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div style="margin-left: 60%;"></div>
|
|
||||||
<ul class="navbar-nav nav-rev" style="margin-right: 0px;">
|
|
||||||
{% if user.is_authenticated %}
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" id='a' href="{% url 'Blog Create' %}">Post</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" id='a' href="{% url 'Profile' %}">Profile</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" id='a' href="{% url 'Logout' %}">Logout</a>
|
|
||||||
</li>
|
|
||||||
{% else %}
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" href="{% url 'Login' %}">Login</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" id=a href="{% url 'Register' %}">Register</a>
|
|
||||||
</li>
|
|
||||||
{% endif %}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
<div id="container"
|
|
||||||
style="font-family: Rockwell; margin-bottom: 0; padding-bottom: 10px; padding-top: 20px; padding-right: 100px; padding-left: 100px;">
|
|
||||||
{% if messages %}
|
|
||||||
{% for message in messages %}
|
|
||||||
<div class="alert alert-{{ message.tags }}">
|
|
||||||
{{ message }}
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
{% block content %}
|
|
||||||
{% endblock %}
|
|
||||||
</div>
|
|
||||||
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js"
|
|
||||||
integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n"
|
|
||||||
crossorigin="anonymous"></script>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"
|
|
||||||
integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo"
|
|
||||||
crossorigin="anonymous"></script>
|
|
||||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"
|
|
||||||
integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6"
|
|
||||||
crossorigin="anonymous"></script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
BIN
templates/bg.jpg
BIN
templates/bg.jpg
Binary file not shown.
|
Before Width: | Height: | Size: 101 KiB |
|
|
@ -2,31 +2,48 @@
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
{% load markdown_extras %}
|
{% load markdown_extras %}
|
||||||
|
<!-- Main Content -->
|
||||||
|
<div class="container mt-5">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-8">
|
||||||
|
|
||||||
<div style="">
|
<!-- Feed -->
|
||||||
<h1 class="h1">We Blog</h1>
|
<div class="card">
|
||||||
<p class="lead">We connect your brains!</p>
|
<div class="card-header">
|
||||||
<hr class="my-4" />
|
Latest Posts
|
||||||
</div>
|
</div>
|
||||||
<ul class="list-unstyled">
|
<div class="card-body">
|
||||||
{% for post in blogs %}
|
{% for blog in blogs %}
|
||||||
<div class="jumbotron" id="jumbotron" style="background-color: #ffffff60;">
|
<!-- Posts will be dynamically added here -->
|
||||||
<li class="media">
|
<div class="card mb-3">
|
||||||
<article class="media content-section">
|
<div class="card-body">
|
||||||
<div class="media-body">
|
<h4 class="card-title"><a href="{% url 'Blog' blog.id %}" class="text-dark">{{ blog.title }}</a></h4>
|
||||||
<div class="article-metadata">
|
<p class="card-text">{{ blog.content | markdown | safe }}</p>
|
||||||
<a class="mr-2" href="{% url 'NamedProfile' post.author.id %}">{{ post.author }}</a>
|
|
||||||
<small class="text-muted">{{ post.data_posted|date:"M j, Y" }}</small>
|
|
||||||
<br>
|
|
||||||
</div>
|
</div>
|
||||||
<a class="article-title" style="font-size: 40px;" href="{% url 'Blog' post.id %}">{{ post.title | markdown | safe }}</a>
|
|
||||||
<hr class="my-4" />
|
|
||||||
<p class="article-content">{{ post.content | markdown | safe }}</p>
|
|
||||||
</div>
|
</div>
|
||||||
</article>
|
<!-- End of Posts -->
|
||||||
</li>
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Sidebar -->
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<div class="card mb-4">
|
||||||
|
<div class="card-header">
|
||||||
|
Updates
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<ul class="list-group list-group-flush">
|
||||||
|
{% for topic in topics %}
|
||||||
|
<li class="list-group-item">{{ topic }}</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
{% load markdown_extras %}
|
{% load markdown_extras %}
|
||||||
|
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
|
||||||
|
<br><br>
|
||||||
<!-- Main Content -->
|
<!-- Main Content -->
|
||||||
<div class="container mt-5">
|
<div class="container mt-5">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
{% load markdown_extras %}
|
||||||
|
<div class="message">
|
||||||
|
<div class="message-author">{{ post.author.username }} <span style="font-size: 10px">{{ post.date_posted | date:"Y/m/d H:i" }}</span></div>
|
||||||
|
<p class="message-content">{{ post.content | markdown | safe }}</p>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
{% for post in posts reversed %}
|
||||||
|
{% include "blog/partials/post.html" %}
|
||||||
|
{% endfor %}
|
||||||
|
|
@ -0,0 +1,121 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
|
||||||
|
<br><br><br>
|
||||||
|
|
||||||
|
<h2 style="opacity: 80%; padding-left:40px">General Chat</h2>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.chat-container{
|
||||||
|
/* width:420px;*/
|
||||||
|
height:470px;
|
||||||
|
border-radius:12px;
|
||||||
|
display:flex;
|
||||||
|
flex-direction:column;
|
||||||
|
overflow:hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.messages{
|
||||||
|
flex:1;
|
||||||
|
overflow-y:auto;
|
||||||
|
padding: 10px 40px;
|
||||||
|
display:flex;
|
||||||
|
flex-direction:column;
|
||||||
|
gap:10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message{
|
||||||
|
background:#334155;
|
||||||
|
padding:2px 5px;
|
||||||
|
border-radius:2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-author{
|
||||||
|
font-size:15px;
|
||||||
|
padding: 0px 5px;
|
||||||
|
opacity:0.7;
|
||||||
|
/* margin-bottom:4px;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 18px;
|
||||||
|
padding: 0px 5px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-area{
|
||||||
|
display:flex;
|
||||||
|
height: 60px;
|
||||||
|
padding:20px 40px;
|
||||||
|
border-top:1px solid rgba(255,255,255,0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-area input{
|
||||||
|
flex: 1;
|
||||||
|
padding:10px;
|
||||||
|
font-size: 30px;
|
||||||
|
border-radius:8px;
|
||||||
|
border:none;
|
||||||
|
outline:none;
|
||||||
|
background:#0f172a;
|
||||||
|
color:white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-area button{
|
||||||
|
/* flex: 1;*/
|
||||||
|
margin-left:8px;
|
||||||
|
padding:10px 14px;
|
||||||
|
border:none;
|
||||||
|
border-radius:8px;
|
||||||
|
background:#3b82f6;
|
||||||
|
color:white;
|
||||||
|
cursor:pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="chat-container">
|
||||||
|
<div
|
||||||
|
id="post-list"
|
||||||
|
class="messages"
|
||||||
|
hx-get="{% url 'ChatPartial' %}"
|
||||||
|
hx-trigger="every 3s"
|
||||||
|
hx-swap="innerHTML">
|
||||||
|
{% include "blog/partials/postList.html" %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form
|
||||||
|
class="input-area"
|
||||||
|
id="post-form"
|
||||||
|
hx-post="{% url 'Chat' %}"
|
||||||
|
hx-target="#post-list"
|
||||||
|
hx-swap="beforeend">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ form.content }}
|
||||||
|
<button type="submit">Send</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
var container = document.getElementById("post-list");
|
||||||
|
let autoScroll = true
|
||||||
|
function isAtBottom(){
|
||||||
|
return container.scrollHeight - container.scrollTop - container.clientHeight < 50;
|
||||||
|
}
|
||||||
|
|
||||||
|
container.addEventListener("scroll", () => {
|
||||||
|
autoScroll = isAtBottom();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.body.addEventListener("htmx:afterSwap", (e) => {
|
||||||
|
if(e.target.id !== "post-list") return
|
||||||
|
if(autoScroll){ container.scrollTop = container.scrollHeight; }
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
|
|
@ -1,24 +1,62 @@
|
||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<style type="text/css">
|
|
||||||
body {
|
{% if not user.is_authenticated %}
|
||||||
background-image: url('/media/bg.jpg'); background-size: cover; background-repeat: no-repeat;
|
<header class="hero-section" style="background-image: url('bg.png');">
|
||||||
}
|
<div class="hero-content">
|
||||||
</style>
|
<h1>BEYOND HEROES</h1>
|
||||||
<div style="position: absolute; bottom: 1; right: 0;margin: 25px; width: 30%; background-color: rgba(0, 0, 0, 0.6); color: #eeeeee; border-radius: 2px;">
|
<p style="letter-spacing: +0.7px;">The project that rose from the ashes of a game that brought us all together. We aim to recreate the experience that we enjoyed throughout the years, while also adding new features that make this game truly <i>Beyond Heroes</i> and Generals.</p>
|
||||||
<div style="padding: 20px;">
|
<br>
|
||||||
<h2 style="text-align: justify;">Beyond Heroes</h2>
|
<a href="{% url 'Register' %}" target="blank" class="discord-button">Join The Community</a>
|
||||||
|
<p style="padding-top: 20px; font-size: 1em">already a part? <a href="{% url 'Login' %}">Login</a></p>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
<section style="margin-top: 60px;" id="news-section">
|
||||||
|
<h2>DEVELOPMENT NEWS</h2>
|
||||||
|
<div class="news-container">
|
||||||
|
<article class="news-post">
|
||||||
|
<img src="https://picsum.photos/seed/news-1/400/250" alt="Gameplay Video">
|
||||||
|
<div class="post-content">
|
||||||
|
<h3>New 3D Models!</h3>
|
||||||
|
<p class="news-date">20 April 2025</p>
|
||||||
|
<p>Look at the great looking 3D Models made by our skilled modellers... <a href="#news-section">Read More</a></p>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
<article class="news-post">
|
||||||
|
<img src="https://picsum.photos/seed/news-4/400/250" alt="Alpha-Test">
|
||||||
|
<div class="post-content">
|
||||||
|
<h3>Happy New Year!</h3>
|
||||||
|
<p class="news-date">01 January 2025</p>
|
||||||
|
<p>This was a great year and we have made good progress... <a href="#news-section">Read More</a></p>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
<article class="news-post">
|
||||||
|
<img src="https://picsum.photos/seed/news-3/400/250" alt="Community Umfrage">
|
||||||
|
<div class="post-content">
|
||||||
|
<h3>New 3D Models!</h3>
|
||||||
|
<p class="news-date">26 November 2024</p>
|
||||||
|
<p>Look at the great looking 3D Models made by our skilled modellers... <a href="#news-section">Read More</a></p>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<section id="about-section" class="text-center">
|
||||||
|
<h2>ABOUT US</h2>
|
||||||
|
<h3 style="text-align: justify-all; letter-spacing: +0.5px;">Our Developers</h3>
|
||||||
<p style="text-align: justify;">
|
<p style="text-align: justify;">
|
||||||
The project that rose from the ashes of a game that was once. We aim to recreate the experience that we enjoyed throughout the years, while also adding new features that make it truly <i>Beyond</i> Heroes and Generals.<br>
|
Made up of enthusiastic helpers from all over the globe, with a strong core in Germany, Poland, France, India, Argentina, Checkia, Slovakia, Canada, and more. Sharing ideas across so many cultures gives us fresh perspectives and helps us keep eyes on different parts that might be more relevant locally and their experience with other mmos. That way, we can reach farther and keep a foot in major countries around the globe. If you'd like to join, just fill out the <a href="https://forms.gle/wf4LTUHNW7hjPXcz6" target="blank">form</a> and don't be scared to hit us up on Discord! Our work environment is super hostile and we do require you to have a sense of humor and the latest version of sarcasm which you can get here <a href="https://en.wikipedia.org/wiki/Sarcasm" target="blank">Sarcasm-StopMakingNewBranches-win-x64.7z</a>
|
||||||
Join Us on this journey to relive the old days and experience something new by clicking on the button below!
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
<h3 style="text-align: justify-all; letter-spacing: +0.5px;">Our Vision</h3>
|
||||||
</div>
|
<p style="text-align: justify;">
|
||||||
<div style="position: absolute; bottom: 0; right: 0; margin-right: 25px; margin-bottom: 30px;">
|
Beyond Heroes, aims to create an exciting World War gaming experience inspired by 'Heroes & Generals' but without the issues it had. Our goal is to provide the same immersive and challenging game while renewing some changes and upgrading the technical systems that preceded. Through strategic gameplay that made HnG so interesting, War, and balanced mechanics, our game aims to deliver a fun and rewarding experience that appeals to old and new players. We value our community and strive to provide a respectful and enjoyable environment that fosters teamwork, camaraderie, and sportsmanship. Also this text sounds corporate af, and thus this anti-climatic sentence is here to break with the same old corporate speech that makes us pay 5$ to open the game. The team is mostly comprised of people who share the same core values those being: Being against the new trend of p2w mechanics, combating <a href="https://colinmagazine.com/blogs/news/live-service-fatigue-are-we-tired-of-battle-passes-microtransactions" target="blank">microtransaction fatigue</a>, being in favour of <a href="https://www.stopkillinggames.com/" target="blank">stop killing games</a>, being in favour of <a href="https://www.youtube.com/watch?v=2_Dtmpe9qaQ" target="blank">clippy</a> and against anti-consumer practices <a href="https://www.forbes.com/sites/paultassi/2024/05/01/nba-2k-players-furious-about-kobe-bryant-collector-level-reward-removal/" target="blank">(1)</a> <a href="https://www.gamesindustry.biz/epic-ea-roblox-and-more-face-eu-complaint-over-tricking-players-into-spending" target="blank">(2)</a>, being absolutely against <a href="https://en.wikipedia.org/wiki/Enshittification" target="blank">enshittification</a>, and in favor of remakes.
|
||||||
<button class="btn" style="padding: 5px 75px; font-family:'Trebuchet MS', sans-serif; font-size: 30px; font-weight: bold; background-color: #139358; color: white; border-radius: 2px;" onmouseover="this.style.backgroundColor='#0f7a4c'" onmouseout="this.style.backgroundColor='#139358'">
|
</p>
|
||||||
To Battle!
|
</section>
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 35 KiB |
|
|
@ -1,34 +1,45 @@
|
||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<style type="text/css">
|
<br>
|
||||||
body {
|
<section id="support-section" class="text-center">
|
||||||
background-image: url('/media/bg.jpg'); background-size: cover; background-repeat: no-repeat;
|
<h2>SUPPORT US</h2>
|
||||||
}
|
<h3 style="text-align: justify-all; letter-spacing: +0.5px;">Hi! So, you want to support the project, I hear?</h3>
|
||||||
</style>
|
|
||||||
<div style="background-color: rgba(0, 0, 0, 0.6); color: #eeeeee; border-radius: 2px; margin-top: 10px;" class="container">
|
|
||||||
<div style="padding: 20px;">
|
|
||||||
<h2 style="text-align: justify;">Beyond Heroes</h2>
|
|
||||||
<p style="text-align: justify;">
|
<p style="text-align: justify;">
|
||||||
The project that rose from the ashes of a game that was once. We aim to recreate the experience that we enjoyed throughout the years, while also adding new features that make it truly <i>Beyond</i> Heroes and Generals.<br>
|
Well, the main way you can do that is by joining the team or telling people to join the team! You can fill out these forms if you're interested in any <a class="text-light" href="#">dev position</a> or if you're interested in <a class="text-light" href="#">translation</a>.<br>
|
||||||
</p>
|
</p>
|
||||||
<hr style="background-color: #999999;">
|
<h3 style="text-align: justify-all; letter-spacing: +0.5px;">If you want to help economically... let's talk first...</h3>
|
||||||
<div class="container">
|
|
||||||
<h3 style="text-align: justify;">Why Help Us</h3>
|
|
||||||
<p style="text-align: justify;">
|
<p style="text-align: justify;">
|
||||||
Similique earum unde unde exercitationem totam voluptatibus. Cupiditate reiciendis aut ducimus sunt. Sunt laboriosam rerum qui maxime quod aut aspernatur. Ipsa reprehenderit quas nihil vel dignissimos at. Repellendus minima dolorem ipsa sit est ipsum accusamus.<br>
|
Here's the thing, I really appreciate that you want to help us out, and we LOVE your energy. We really don't want to accept economic help, and there are a few reasons as to why. Mostly, we can't (or don't want to) promise anything, we don't want to get your hopes up, and we don't want to lead you to believe in something that might not happen. Giving us money has a worse ROI than playing it all on red in roulette, and as such, to me personally, it feels like asking for money from you just because you like us. If that's the case, okay, alright, no problem; we still have to solve the distribution issue, but whatever, we'll get to it eventually. Otherwise, understand that the reason the links are so hidden is exactly because we don't want you to feel like you wasted money - at least not before you knowingly and willingly understand that this is just like giving it to us because you like us and want to, or you have money to spare and don't care. If you want to specifically help out a team member, assign it on the message or something so I know who not to give it to :P
|
||||||
|
<br>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
<p style="text-align: justify; margin-top: 10px;">
|
||||||
<div class="container">
|
If you understand the above-mentioned stuff, then here's the links to our <a class="text-light" href="https://ko-fi.com/beyondheroes" target="blank">Ko-fi</a>, and <a class="text-light" href="https://www.buymeacoffee.com/beyondheroes" target="blank">Buy Me a Coffee</a>.
|
||||||
<h3 style="text-align: justify;">How to Help Us</h3>
|
|
||||||
<p style="text-align: justify;">
|
|
||||||
Laborum nobis incidunt voluptate magnam voluptatibus. Itaque sequi eveniet vitae voluptas voluptatem ullam alias omnis. Officiis quia dolores architecto.Voluptate quia aspernatur excepturi voluptas molestias. Maxime magnam maiores sunt dolorum. Id possimus ullam rerum dignissimos.<br>
|
|
||||||
</p>
|
</p>
|
||||||
<button class="btn" style="padding: 5px 30px; font-family:'Trebuchet MS', sans-serif; font-size: 20px; font-weight: bold; background-color: #139358; color: white; border-radius: 2px;" onmouseover="this.style.backgroundColor='#0f7a4c'" onmouseout="this.style.backgroundColor='#139358'">
|
|
||||||
Donate
|
<div id="supporters-carousel">
|
||||||
</button>
|
<h3>Special Thanks to...</h3>
|
||||||
|
<div class="carousel-container">
|
||||||
|
<div class="carousel-content">
|
||||||
|
<div class="supporter-item"><h4>J. Doe</h4><p>€500</p></div>
|
||||||
|
<div class="supporter-item"><h4>A. Smith</h4><p>€250</p></div>
|
||||||
|
<div class="supporter-item"><h4>M. Garcia</h4><p>€150</p></div>
|
||||||
|
<div class="supporter-item"><h4>J. Doe</h4><p>€500</p></div>
|
||||||
|
<div class="supporter-item"><h4>A. Smith</h4><p>€250</p></div>
|
||||||
|
<div class="supporter-item"><h4>M. Garcia</h4><p>€150</p></div>
|
||||||
|
<div class="supporter-item"><h4>L. Miller</h4><p>€100</p></div>
|
||||||
|
<div class="supporter-item"><h4>P. Jones</h4><p>€80</p></div>
|
||||||
|
<div class="supporter-item"><h4>T. Kim</h4><p>€60</p></div>
|
||||||
|
<div class="supporter-item"><h4>J. Doe</h4><p>€500</p></div>
|
||||||
|
<div class="supporter-item"><h4>A. Smith</h4><p>€250</p></div>
|
||||||
|
<div class="supporter-item"><h4>M. Garcia</h4><p>€150</p></div>
|
||||||
|
<div class="supporter-item"><h4>L. Miller</h4><p>€100</p></div>
|
||||||
|
<div class="supporter-item"><h4>P. Jones</h4><p>€80</p></div>
|
||||||
|
<div class="supporter-item"><h4>T. Kim</h4><p>€60</p></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% load crispy_forms_tags %}
|
{% load crispy_forms_tags %}
|
||||||
|
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
|
||||||
|
<br><br>
|
||||||
<div class="container mt-5">
|
<div class="container mt-5">
|
||||||
<form method="POST">
|
<form method="POST">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% load crispy_forms_tags %}
|
{% load crispy_forms_tags %}
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
|
||||||
|
<br><br>
|
||||||
{% if messages %}
|
{% if messages %}
|
||||||
{% for message in messages %}
|
{% for message in messages %}
|
||||||
<div class="alert alert-{{message.tags}}">{{ message }}</div>
|
<div class="alert alert-{{message.tags}}">{{ message }}</div>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% load crispy_forms_tags %}
|
{% load crispy_forms_tags %}
|
||||||
|
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
<div class="container mt-5">
|
<div class="container mt-5">
|
||||||
<form method="POST">
|
<form method="POST">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1,5 +1,5 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from .models import Profile
|
from .models import *
|
||||||
|
|
||||||
|
|
||||||
# Register your models here.
|
admin.site.register(UserData)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.contrib.auth.forms import UserCreationForm
|
from django.contrib.auth.forms import UserCreationForm
|
||||||
from .models import Profile
|
from .models import *
|
||||||
|
|
||||||
|
|
||||||
class UserRegisterForm(UserCreationForm):
|
class UserRegisterForm(UserCreationForm):
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 5.2.9 on 2026-02-19 10:09
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('users', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='profile',
|
||||||
|
name='id',
|
||||||
|
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
# Generated by Django 5.2.9 on 2026-02-28 03:45
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('users', '0002_alter_profile_id'),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='UserData',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(max_length=40)),
|
||||||
|
('xp', models.IntegerField()),
|
||||||
|
('money', models.IntegerField()),
|
||||||
|
('equipment', models.CharField(default='0;', max_length=1024)),
|
||||||
|
('inventory', models.CharField(default='0;', max_length=1024)),
|
||||||
|
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='Profile',
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
# Generated by Django 5.2.9 on 2026-03-05 12:51
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('users', '0003_userdata_delete_profile'),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='UserAction',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('action', models.CharField(max_length=200)),
|
||||||
|
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -3,14 +3,28 @@ from django.contrib.auth.models import User
|
||||||
|
|
||||||
|
|
||||||
# Create your models here.
|
# Create your models here.
|
||||||
class Profile(models.Model):
|
class UserData(models.Model):
|
||||||
user = models.OneToOneField(User, on_delete=models.CASCADE)
|
user = models.OneToOneField(User, on_delete=models.CASCADE)
|
||||||
name = models.CharField(max_length=100)
|
name = models.CharField(max_length=40)
|
||||||
about = models.TextField(default='Hi, I am new to TechBlog')
|
xp = models.IntegerField()
|
||||||
gender = models.TextField(default='None')
|
money = models.IntegerField()
|
||||||
dob = models.DateField(default='1999-01-01')
|
equipment = models.CharField(max_length=1024, default='0;')
|
||||||
# image = models.ImageField(default='default.png', name='profile_pic', upload_to='profile_pics')
|
inventory = models.CharField(max_length=1024, default='0;')
|
||||||
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.user.username}'s Profile"
|
return f"{self.user.username}'s data"
|
||||||
|
|
||||||
|
|
||||||
|
# So this is my beautiful brainchild to keep user data in about 1KB per user, I'm not too sure but still
|
||||||
|
# it's worth the try, so the main payload is the inventory along the soldiers and their weapons and
|
||||||
|
# vehicles along with the mods for everything which I have separated by `;`, `.` and `,` for soldier,
|
||||||
|
# equipment class and each equipment. I'll try a bit of bit-hacking to pack info of as much as 20 bytes
|
||||||
|
# in 2 for the mods. ammo|--|sights|---|internals|---|, trigger|--|barrel|--|skins|----|, number|--------|
|
||||||
|
|
||||||
|
|
||||||
|
class UserAction(models.Model):
|
||||||
|
user = models.OneToOneField(User, on_delete=models.CASCADE)
|
||||||
|
action = models.CharField(max_length=200)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.user.username}'s action"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
from rest_framework import serializers
|
||||||
|
from .models import *
|
||||||
|
|
||||||
|
class UserDataSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = UserData
|
||||||
|
fields = ['user', 'name', 'xp', 'money', 'equipment', 'inventory']
|
||||||
|
|
||||||
|
|
||||||
|
class UserActionSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = UserAction
|
||||||
|
fields = ['user', 'action']
|
||||||
|
|
@ -3,8 +3,8 @@ from django.contrib.auth import views as login_view
|
||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('profile/', views.profile, name='Profile'),
|
# path('profile/', views.profile, name='Profile'),
|
||||||
path('profile/<int:pk>', views.profile, name='NamedProfile'),
|
# path('profile/<int:pk>', views.profile, name='NamedProfile'),
|
||||||
path('login/', login_view.LoginView.as_view(template_name='users/login.html'), name='Login'),
|
path('login/', login_view.LoginView.as_view(template_name='users/login.html'), name='Login'),
|
||||||
path('logout/', login_view.LogoutView.as_view(template_name='users/logout.html'), name='Logout'),
|
path('logout/', login_view.LogoutView.as_view(template_name='users/logout.html'), name='Logout'),
|
||||||
path('register/', views.register, name='Register')
|
path('register/', views.register, name='Register')
|
||||||
|
|
|
||||||
|
|
@ -11,52 +11,36 @@ from django.views.generic import *
|
||||||
# def users(request):
|
# def users(request):
|
||||||
# return render(request, 'users/users.html', {'title': 'Users'})
|
# return render(request, 'users/users.html', {'title': 'Users'})
|
||||||
|
|
||||||
def getFromArr(arr, indices, *args, **kwargs):
|
# def getFromArr(arr, indices, *args, **kwargs):
|
||||||
x = []
|
# x = []
|
||||||
for i in indices:
|
# for i in indices:
|
||||||
x.append(arr[i])
|
# x.append(arr[i])
|
||||||
return x
|
# return x
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
# @login_required
|
||||||
def profile(request, *args, **kwargs):
|
# def profile(request, *args, **kwargs):
|
||||||
try:
|
# try:
|
||||||
user = User._default_manager.all()[kwargs['pk'] - 1]
|
# user = User._default_manager.all()[kwargs['pk'] - 1]
|
||||||
except:
|
# except:
|
||||||
user = request.user
|
# user = request.user
|
||||||
print(user.id)
|
# print(user.id)
|
||||||
|
|
||||||
allow_empty = True
|
# allow_empty = True
|
||||||
queryset = None
|
# queryset = Post._default_manager.all()
|
||||||
model = Post
|
# context_object_name = 'posts'
|
||||||
paginate_by = None
|
# ordering = ['-date_posted']
|
||||||
paginate_orphans = 0
|
|
||||||
context_object_name = 'posts'
|
|
||||||
ordering = ['-date_posted']
|
|
||||||
|
|
||||||
if queryset is not None:
|
# if ordering:
|
||||||
queryset = queryset
|
# if isinstance(ordering, str):
|
||||||
if isinstance(queryset, QuerySet):
|
# ordering = (ordering,)
|
||||||
queryset = queryset.all()
|
# queryset = queryset.order_by(*ordering)
|
||||||
elif model is not None:
|
|
||||||
queryset = model._default_manager.all()
|
|
||||||
else:
|
|
||||||
raise ImproperlyConfigured(
|
|
||||||
"%(cls)s is missing a QuerySet. Define "
|
|
||||||
"%(cls)s.model, %(cls)s.queryset, or override "
|
|
||||||
"%(cls)s.get_queryset()." % {"cls": self.__class__.__name__}
|
|
||||||
)
|
|
||||||
|
|
||||||
if ordering:
|
# return render(request, 'users/profile.html', {'title': 'Profile', 'profileUser': user, context_object_name: queryset})
|
||||||
if isinstance(ordering, str):
|
|
||||||
ordering = (ordering,)
|
|
||||||
queryset = queryset.order_by(*ordering)
|
|
||||||
|
|
||||||
return render(request, 'users/profile.html', {'title': 'Profile', 'profileUser': user, context_object_name: queryset})
|
|
||||||
|
|
||||||
|
|
||||||
def people(request):
|
# def people(request):
|
||||||
return render(request, 'users/people.html', {'title': 'People', 'users': User._default_manager.all()})
|
# return render(request, 'users/people.html', {'title': 'People', 'users': User._default_manager.all()})
|
||||||
|
|
||||||
|
|
||||||
users = {
|
users = {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue