Compare commits

..

No commits in common. "c37470a53dc66f9248bcb4d15d287f9d60b4ece6" and "b15d095dc1da7942734591f8d5001a85488be61a" have entirely different histories.

59 changed files with 426 additions and 81 deletions

8
.gitignore vendored
View File

@ -1,15 +1,11 @@
# Byte-compiled / optimized / DLL files # Byte-compiled / optimized / DLL files
*/__pycache__/ __pycache__/
*.py[cod] *.py[cod]
*$py.class *$py.class
# C extensions # C extensions
*.so *.so
#added
staticfiles/
*/migrations/
blog/migrations/
# Distribution / packaging # Distribution / packaging
.Python .Python
build/ build/

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -17,8 +17,6 @@ from dotenv import load_dotenv
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
TEMPLATES_DIR = os.path.join(BASE_DIR + '/templates') TEMPLATES_DIR = os.path.join(BASE_DIR + '/templates')
DEFAULT_AUTO_FIELD='django.db.models.AutoField'
# Quick-start development settings - unsuitable for production # Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/ # See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/
@ -29,9 +27,9 @@ load_dotenv()
SECRET_KEY = os.getenv('SECRET_KEY') SECRET_KEY = os.getenv('SECRET_KEY')
# 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 = False
ALLOWED_HOSTS = ['localhost', '127.0.0.1', 'beyond-heroes.com', 'www.beyond-heroes.com'] ALLOWED_HOSTS = ['localhost', '127.0.0.1']
# Application definition # Application definition
@ -40,6 +38,7 @@ INSTALLED_APPS = [
'crispy_forms', 'crispy_forms',
'crispy_bootstrap4', 'crispy_bootstrap4',
'blog.apps.BlogConfig', 'blog.apps.BlogConfig',
'users.apps.UsersConfig',
'django.contrib.admin', 'django.contrib.admin',
'django.contrib.auth', 'django.contrib.auth',
'django.contrib.contenttypes', 'django.contrib.contenttypes',
@ -84,12 +83,8 @@ WSGI_APPLICATION = 'BH.wsgi.application'
DATABASES = { DATABASES = {
'default': { 'default': {
'ENGINE': 'django.db.backends.mysql', 'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.getenv('DB_NAME'), 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
'USER': os.getenv('DB_USER'),
'PASSWORD': os.getenv('DB_PASSWORD'),
'HOST': os.getenv('DB_HOST'),
'PORT': os.getenv('DB_PORT'),
} }
} }
@ -130,7 +125,6 @@ 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/
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/'

View File

@ -20,6 +20,7 @@ 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),
] ]

View File

@ -1,12 +0,0 @@
# Use the official MariaDB base image
FROM mariadb:latest
# Set environment variables
ENV MARIADB_ROOT_PASSWORD=DB_ROOTPW
ENV MARIADB_ROOT_HOST=%
ENV MARIADB_DATABASE=DBNAME
ENV MARIADB_USER=DB_USER
ENV MARIADB_PASSWORD=DB_PASSWORD
# Expose the MariaDB port
EXPOSE 3306

View File

@ -1,23 +1,13 @@
## Requirements
The website requires the following:
- Docker
- Docker Compose
- A running MariaDB Instance
## Deployment ## Deployment
To deploy the website: To deploy the website:
- first clone the repository to any local folder - first clone the repository to any local folder
- then open the folder - then open the folder with the `manage.py` file
- create a new file called `.env` and add the following: - then `Shift`+`Right Click` and click on `Open PowerShell window here`
``` - then create the virtual environment by typing `py -m venv .venv`
SECRET_KEY=your_secret_key - then activate the virtual environment by typing `.venv\Scripts\Activate`
DB_NAME=your_db_name - then install the requirements by typing `pip install -r requirements.txt`
DB_USER=your_db_user - then type in `py manage.py runserver 3000`
DB_PASSWORD=your_db_password - open browser at address `localhost:3000` or `127.0.0.1:3000`
DB_HOST=your_db_host
DB_PORT=your_db_port
```
- run `docker-compose up --build` this starts the website at localhost:3030
## Structure ## Structure
The website has a Home page and a News page currently. The News page shows all developer blogs. The website has a Home page and a News page currently. The News page shows all developer blogs.

View File

@ -1,17 +0,0 @@
First of change the Env variables in the .env and the docker-compose.yml file to your own values.
Then create the docker container and run it with the following command:
```bash
docker-compose up --build
```
This starts the Database \
Then run the following command to create the database tables:
```bash
python manage.py migrate
python manage.py migrate --run-syncdb
```
At last, run the following command to start the server:
```bash
python manage.py runserver 3030
```

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,37 @@
# Generated by Django 5.0.2 on 2024-07-06 16:24
import django.db.models.deletion
import django.utils.timezone
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='Blog',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('content', models.TextField()),
('title', models.CharField(max_length=150)),
('date_posted', models.DateTimeField(default=django.utils.timezone.now)),
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='Post',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('content', models.TextField()),
('date_posted', models.DateTimeField(default=django.utils.timezone.now)),
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
]

View File

Binary file not shown.

View File

@ -7,7 +7,7 @@ from django.utils import timezone
class Blog(models.Model): class Blog(models.Model):
content = models.TextField() content = models.TextField()
title = models.CharField(max_length=150) title = models.CharField(max_length=150)
author = models.TextField() #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)
def get_absolute_url(self): def get_absolute_url(self):

View File

@ -5,6 +5,7 @@ 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='BlogCreate'),
path('dev/', views.dev, name='Dev'), path('dev/', views.dev, name='Dev'),
path('dev/support/', views.support, name='Support'), path('dev/support/', views.support, name='Support'),
] ]

View File

@ -3,6 +3,8 @@ 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 django.contrib.admin.views.decorators import staff_member_required
from django.utils.decorators import method_decorator
# Create your views here. # Create your views here.
@ -43,6 +45,17 @@ class BlogDetailView(DetailView):
template_name = 'blog/blogDetail.html' template_name = 'blog/blogDetail.html'
@method_decorator(staff_member_required, name='dispatch')
class BlogCreateView(LoginRequiredMixin, CreateView):
model = Blog
template_name = 'blog/blogCreate.html'
fields = ['title', 'content']
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
def dev(request): def dev(request):
return render(request, 'dev.html', {'title': 'Development'}) return render(request, 'dev.html', {'title': 'Development'})

BIN
db.sqlite3 Normal file

Binary file not shown.

View File

@ -1,15 +0,0 @@
version: '3.8'
services:
mariadb:
build: .
container_name: mariadb
restart: unless-stopped
environment:
MARIADB_ROOT_PASSWORD: 'DB_ROOTPW'
MARIADB_ROOT_HOST: '%'
MARIADB_DATABASE: 'DBNAME'
MARIADB_USER: 'DB_USER'
MARIADB_PASSWORD: 'DB_PASSWORD'
ports:
- "3306:3306"

BIN
media/default.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

5
server.bat Normal file
View File

@ -0,0 +1,5 @@
@ echo off
echo The devlopment server will start in a few seconds...
python manage.py makemigrations
python manage.py migrate
python manage.py runserver 3000

View File

@ -8,6 +8,7 @@
</head> </head>
<body> <body>
<nav class="navbar navbar-expand-lg navbar-dark" style="background-color: #000;"> <nav class="navbar navbar-expand-lg navbar-dark" style="background-color: #000;">
<div class="container">
<a class="navbar-brand" href="/"> <a class="navbar-brand" href="/">
<img src="/media/logo.jpg" alt="BH Logo" height="40"> Beyond Heroes <img src="/media/logo.jpg" alt="BH Logo" height="40"> Beyond Heroes
</a> </a>
@ -25,6 +26,35 @@
</li> </li>
</ul> </ul>
</div> </div>
<div class="ml-auto"></div>
<div class="justify-content-center" id="navbarNav">
<ul class="navbar-nav">
{% if user.is_authenticated %}
<li class="nav-item">
{% if user.is_staff %}
<form action="{% url 'BlogCreate' %}" method="get">
{% csrf_token %}
<button class="nav-link btn btn-link" type="submit">Post</button>
</form>
{% endif %}
</li>
<li class="nav-item">
<form action="{% url 'Logout' %}" method="post">
{% csrf_token %}
<button class="nav-link btn btn-link" type="submit">Logout</button>
</form>
</li>
{% else %}
<li class="nav-item">
<a class="nav-link" href="{% url 'Login' %}">Login</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'Register' %}">Register</a>
</li>
{% endif %}
</ul>
</div>
</div>
</nav> </nav>
{% block content %} {% block content %}

View File

@ -0,0 +1,19 @@
{% extends 'base.html' %}
{% block content %}
{% load crispy_forms_tags %}
<div class="container mt-5">
<form method="POST">
{% csrf_token %}
<div class="card mb-4">
<div class="card-header">
Create a New Blog Article
</div>
<div class="card-body">
{{ form | crispy }}
<br>
<button class="btn btn-light" type="submit"> Post </button>
</div>
</div>
</form>
</div>
{% endblock %}

View File

@ -10,7 +10,7 @@
<p class="card-text">{{ object.content | markdown | safe }}</p> <p class="card-text">{{ object.content | markdown | safe }}</p>
</div> </div>
<div class="card-footer"> <div class="card-footer">
By <cite title="{{ object.author }}"></cite> on <cite title="{{ object.date_posted }}">{{ object.date_posted|date:"M j, o" }}</cite> By <cite title="{{ object.author }}"><a href="#" class="link text-dark">{{ object.author }}</a></cite> on <cite title="{{ object.date_posted }}">{{ object.date_posted|date:"M j, o" }}</cite>
</div> </div>
</div> </div>

View File

@ -0,0 +1,24 @@
{% extends 'base.html' %}
{% block content %}
{% load crispy_forms_tags %}
<div class="container mt-5">
<form method="POST">
{% csrf_token %}
<div class="card mb-4">
<div class="card-header">
Login
</div>
<div class="card-body">
{{ form | crispy }}
<br>
<button class="btn btn-light" type="submit"> Login </button>
</div>
<div class="card-footer">
<small class="text-muted">
Don't Have An Account? <a class="link" href="{% url 'Register' %}">Register</a>
</small>
</div>
</div>
</form>
</div>
{% endblock %}

View File

@ -0,0 +1,25 @@
{% extends 'base.html' %}
{% block content %}
{% load crispy_forms_tags %}
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{message.tags}}">{{ message }}</div>
{% endfor %}
{% endif %}
<div class="container mt-5">
<div class="card mb-4">
<div class="card-header h5">
You have been Logged Out
</div>
<div class="card-body">
Hope you enjoyed Today!
</div>
<div class="card-footer">
<small class="text-muted">
Go to <a href="{% url 'Home' %}" class="link">Home</a> or <a href="{% url 'Login' %}" class="link" type="submit"> Log in </a> Again?
</small>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,58 @@
{% extends 'base.html' %}
{% block content %}
{% load markdown_extras %}
<!-- Main Content -->
<div class="container mt-5">
<div class="row">
<!-- Mainbar -->
<div class="col-lg-8">
<div class="card-body">
<div class="card">
<div class="card-header">
Latest Posts
</div>
<div class="card-body">
{% for post in posts %}
{% if post.author == profileUser %}
<!-- Posts will be dynamically added here -->
<div class="card mb-3 userPost">
<div class="card-body">
<h5 class="card-title">{{ post.author.username }} <a href="#" class="link text-secondary lead" style="text-decoration: none;">@{{ post.author.id }}</a></h5>
<p class="card-text">{{ post.content | markdown | safe }}</p>
</div>
</div>
<!-- End of Posts -->
{% endif %}
{% endfor %}
</div>
</div>
</div>
</div>
<!-- Sidebar -->
<div class="col-lg-4">
<div class="card mb-4">
<div class="row" style="padding-bottom: 20px;">
<div class="col-md-4 text-center">
<img src="https://cdn.pixabay.com/photo/2017/06/13/12/54/profile-2398783_1280.png" alt="Profile Image" class="profile-image">
</div>
<div class="col-md-8">
<h2 class="mt-3" style="margin-bottom: 0px;">{{ profileUser.username }}</h2>
<p class="text-secondary lead" style="margin-bottom: 0px;">@{{ profileUser.id }}</p>
<p>Date Joined: Jan 1, 2022</p>
{% if profileUser == user %}
<a href="#" class="link">Edit Profile</a>
{% endif %}
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,24 @@
{% extends 'base.html' %}
{% block content %}
{% load crispy_forms_tags %}
<div class="container mt-5">
<form method="POST">
{% csrf_token %}
<div class="card mb-4">
<div class="card-header">
Create an Account
</div>
<div class="card-body">
{{ form | crispy }}
<br>
<button class="btn btn-light sm" type="submit"> Register </button>
</div>
<div class="card-footer">
<small class="text-muted">
Already Have An Account? <a class="link" href="{% url 'Login' %}">Sign In</a>
</small>
</div>
</div>
</form>
</div>
{% endblock %}

0
users/__init__.py Normal file
View File

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.

5
users/admin.py Normal file
View File

@ -0,0 +1,5 @@
from django.contrib import admin
from .models import Profile
# Register your models here.

8
users/apps.py Normal file
View File

@ -0,0 +1,8 @@
from django.apps import AppConfig
class UsersConfig(AppConfig):
name = 'users'
def ready(self):
import users.signals

21
users/forms.py Normal file
View File

@ -0,0 +1,21 @@
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
from .models import Profile
class UserRegisterForm(UserCreationForm):
email = forms.EmailField()
class Meta:
model = User
fields = ['username', 'email', 'password1', 'password2']
class UserUpdateForm(forms.ModelForm):
email = forms.EmailField()
class Meta:
model = User
fields = ['username', 'email']

View File

@ -0,0 +1,28 @@
# Generated by Django 5.0.2 on 2024-07-06 16:24
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='Profile',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100)),
('about', models.TextField(default='Hi, I am new to TechBlog')),
('gender', models.TextField(default='None')),
('dob', models.DateField(default='1999-01-01')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
]

View File

Binary file not shown.

16
users/models.py Normal file
View File

@ -0,0 +1,16 @@
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
name = models.CharField(max_length=100)
about = models.TextField(default='Hi, I am new to TechBlog')
gender = models.TextField(default='None')
dob = models.DateField(default='1999-01-01')
# image = models.ImageField(default='default.png', name='profile_pic', upload_to='profile_pics')
def __str__(self):
return f"{self.user.username}'s Profile"

3
users/signals.py Normal file
View File

@ -0,0 +1,3 @@
from django.db.models.signals import post_save
from django.contrib.auth.models import User
from django.dispatch import receiver

3
users/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

11
users/urls.py Normal file
View File

@ -0,0 +1,11 @@
from django.urls import path
from django.contrib.auth import views as login_view
from . import views
urlpatterns = [
path('profile/', views.profile, name='Profile'),
path('profile/<int:pk>', views.profile, name='NamedProfile'),
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('register/', views.register, name='Register')
]

77
users/views.py Normal file
View File

@ -0,0 +1,77 @@
from django.shortcuts import render, redirect
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from .forms import UserRegisterForm
from blog.models import *
from .models import *
from django.views.generic import *
# Create your views here.
# def users(request):
# return render(request, 'users/users.html', {'title': 'Users'})
def getFromArr(arr, indices, *args, **kwargs):
x = []
for i in indices:
x.append(arr[i])
return x
@login_required
def profile(request, *args, **kwargs):
try:
user = User._default_manager.all()[kwargs['pk'] - 1]
except:
user = request.user
print(user.id)
allow_empty = True
queryset = None
model = Post
paginate_by = None
paginate_orphans = 0
context_object_name = 'posts'
ordering = ['-date_posted']
if queryset is not None:
queryset = queryset
if isinstance(queryset, QuerySet):
queryset = queryset.all()
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:
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})
users = {
'user': User.objects.all()
}
def login(request):
return render(request, 'users/login.html', {'title': 'Login'})
def register(request):
if request.method == 'POST':
form = UserRegisterForm(request.POST)
if form.is_valid():
form.save()
username = form.cleaned_data.get('username')
messages.success(request, f'{username}! Your account has been created.')
return redirect('Login')
else:
form = UserRegisterForm()
return render(request, 'users/register.html', {'title': 'Register', 'form': form})