diff --git a/FOSSDB/apps/account/admin.py b/FOSSDB/apps/account/admin.py index e69de29..867f339 100644 --- a/FOSSDB/apps/account/admin.py +++ b/FOSSDB/apps/account/admin.py @@ -0,0 +1,19 @@ +from django.contrib import admin +from django.contrib.auth.admin import UserAdmin + +from .models import User + + +class CustomUserAdmin(UserAdmin): + model = User + fieldsets = UserAdmin.fieldsets + ( + ( + None, + { + "fields": ("profile_picture",), + }, + ), + ) + + +admin.site.register(User, CustomUserAdmin) diff --git a/FOSSDB/apps/account/forms.py b/FOSSDB/apps/account/forms.py index 80d92b6..149eafc 100644 --- a/FOSSDB/apps/account/forms.py +++ b/FOSSDB/apps/account/forms.py @@ -1,7 +1,8 @@ from django import forms from django.contrib.auth.forms import UserCreationForm -from django.contrib.auth.models import User + +from .models import User class SignUpForm(UserCreationForm): diff --git a/FOSSDB/apps/account/migrations/0001_initial.py b/FOSSDB/apps/account/migrations/0001_initial.py new file mode 100644 index 0000000..5fd38f4 --- /dev/null +++ b/FOSSDB/apps/account/migrations/0001_initial.py @@ -0,0 +1,45 @@ +# Generated by Django 4.2.2 on 2023-06-27 12:54 + +import django.contrib.auth.models +import django.contrib.auth.validators +from django.db import migrations, models +import django.utils.timezone +import uuid + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('auth', '0012_alter_user_first_name_max_length'), + ] + + operations = [ + migrations.CreateModel( + name='CustomUser', + fields=[ + ('password', models.CharField(max_length=128, verbose_name='password')), + ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), + ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), + ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')), + ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')), + ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), + ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')), + ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), + ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), + ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, verbose_name='ID')), + ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')), + ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')), + ], + options={ + 'verbose_name': 'user', + 'verbose_name_plural': 'users', + 'abstract': False, + }, + managers=[ + ('objects', django.contrib.auth.models.UserManager()), + ], + ), + ] diff --git a/FOSSDB/apps/account/migrations/0002_customuser_profile_pic.py b/FOSSDB/apps/account/migrations/0002_customuser_profile_pic.py new file mode 100644 index 0000000..63126a7 --- /dev/null +++ b/FOSSDB/apps/account/migrations/0002_customuser_profile_pic.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.2 on 2023-06-27 13:02 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='customuser', + name='profile_pic', + field=models.ImageField(default='default.jpg', upload_to='profile_pics/'), + ), + ] diff --git a/FOSSDB/apps/account/migrations/0003_alter_customuser_profile_pic.py b/FOSSDB/apps/account/migrations/0003_alter_customuser_profile_pic.py new file mode 100644 index 0000000..10db396 --- /dev/null +++ b/FOSSDB/apps/account/migrations/0003_alter_customuser_profile_pic.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.2 on 2023-06-27 13:28 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0002_customuser_profile_pic'), + ] + + operations = [ + migrations.AlterField( + model_name='customuser', + name='profile_pic', + field=models.BinaryField(blank=True), + ), + ] diff --git a/FOSSDB/apps/account/migrations/0004_alter_customuser_profile_pic.py b/FOSSDB/apps/account/migrations/0004_alter_customuser_profile_pic.py new file mode 100644 index 0000000..1b2c597 --- /dev/null +++ b/FOSSDB/apps/account/migrations/0004_alter_customuser_profile_pic.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.2 on 2023-06-27 13:32 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0003_alter_customuser_profile_pic'), + ] + + operations = [ + migrations.AlterField( + model_name='customuser', + name='profile_pic', + field=models.BinaryField(blank=True, editable=True), + ), + ] diff --git a/FOSSDB/apps/account/migrations/0005_rename_profile_pic_customuser_profile_picture.py b/FOSSDB/apps/account/migrations/0005_rename_profile_pic_customuser_profile_picture.py new file mode 100644 index 0000000..1dee064 --- /dev/null +++ b/FOSSDB/apps/account/migrations/0005_rename_profile_pic_customuser_profile_picture.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.2 on 2023-06-27 13:33 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0004_alter_customuser_profile_pic'), + ] + + operations = [ + migrations.RenameField( + model_name='customuser', + old_name='profile_pic', + new_name='profile_picture', + ), + ] diff --git a/FOSSDB/apps/account/migrations/0006_alter_customuser_profile_picture.py b/FOSSDB/apps/account/migrations/0006_alter_customuser_profile_picture.py new file mode 100644 index 0000000..1b959e4 --- /dev/null +++ b/FOSSDB/apps/account/migrations/0006_alter_customuser_profile_picture.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.2 on 2023-06-27 13:34 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0005_rename_profile_pic_customuser_profile_picture'), + ] + + operations = [ + migrations.AlterField( + model_name='customuser', + name='profile_picture', + field=models.ImageField(default='profile_pic/default.jpg', upload_to='profile_pics/'), + ), + ] diff --git a/FOSSDB/apps/account/migrations/0007_alter_customuser_profile_picture.py b/FOSSDB/apps/account/migrations/0007_alter_customuser_profile_picture.py new file mode 100644 index 0000000..48f53d8 --- /dev/null +++ b/FOSSDB/apps/account/migrations/0007_alter_customuser_profile_picture.py @@ -0,0 +1,20 @@ +# Generated by Django 4.2.2 on 2023-06-27 13:43 + +import account.models +from django.db import migrations, models +import pathlib + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0006_alter_customuser_profile_picture'), + ] + + operations = [ + migrations.AlterField( + model_name='customuser', + name='profile_picture', + field=models.ImageField(default=pathlib.PurePosixPath('profile_pic/default.jpg'), upload_to=account.models.get_profile_pic_path), + ), + ] diff --git a/FOSSDB/apps/account/migrations/0008_rename_customuser_user.py b/FOSSDB/apps/account/migrations/0008_rename_customuser_user.py new file mode 100644 index 0000000..25c325b --- /dev/null +++ b/FOSSDB/apps/account/migrations/0008_rename_customuser_user.py @@ -0,0 +1,20 @@ +# Generated by Django 4.2.2 on 2023-06-27 13:51 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('fossdb', '0001_initial'), + ('auth', '0012_alter_user_first_name_max_length'), + ('admin', '0003_logentry_add_action_flag_choices'), + ('account', '0007_alter_customuser_profile_picture'), + ] + + operations = [ + migrations.RenameModel( + old_name='CustomUser', + new_name='User', + ), + ] diff --git a/FOSSDB/apps/account/migrations/0009_rename_user_customuser.py b/FOSSDB/apps/account/migrations/0009_rename_user_customuser.py new file mode 100644 index 0000000..e853ebd --- /dev/null +++ b/FOSSDB/apps/account/migrations/0009_rename_user_customuser.py @@ -0,0 +1,20 @@ +# Generated by Django 4.2.2 on 2023-06-27 13:53 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('fossdb', '0001_initial'), + ('auth', '0012_alter_user_first_name_max_length'), + ('admin', '0003_logentry_add_action_flag_choices'), + ('account', '0008_rename_customuser_user'), + ] + + operations = [ + migrations.RenameModel( + old_name='User', + new_name='CustomUser', + ), + ] diff --git a/FOSSDB/apps/account/migrations/0010_rename_customuser_user.py b/FOSSDB/apps/account/migrations/0010_rename_customuser_user.py new file mode 100644 index 0000000..31d690b --- /dev/null +++ b/FOSSDB/apps/account/migrations/0010_rename_customuser_user.py @@ -0,0 +1,20 @@ +# Generated by Django 4.2.2 on 2023-06-27 13:55 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('admin', '0003_logentry_add_action_flag_choices'), + ('auth', '0012_alter_user_first_name_max_length'), + ('fossdb', '0001_initial'), + ('account', '0009_rename_user_customuser'), + ] + + operations = [ + migrations.RenameModel( + old_name='CustomUser', + new_name='User', + ), + ] diff --git a/FOSSDB/apps/account/models.py b/FOSSDB/apps/account/models.py index e69de29..1cf7561 100644 --- a/FOSSDB/apps/account/models.py +++ b/FOSSDB/apps/account/models.py @@ -0,0 +1,42 @@ +import uuid +from pathlib import Path + +from django.contrib.auth.models import AbstractUser +from django.core.exceptions import ObjectDoesNotExist +from django.db import models + + +def get_profile_pic_path(instance, filename) -> Path: + ext = filename.split(".")[-1] + filename = f"{instance.id}.{ext}" + return Path("profile_pics", filename) + + +class User(AbstractUser): + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, verbose_name="ID") + profile_picture = models.ImageField(upload_to=get_profile_pic_path, default=Path("profile_pic", "default.jpg")) + + @property + def full_name(self): + if not self.first_name and not self.last_name: + return "" + elif self.first_name and not self.last_name: + return self.first_name + elif not self.first_name and self.last_name: + return self.last_name + else: + return f"{self.first_name} {self.last_name}" + + def save(self, *args, **kwags): + old_instance = None + if self.pk: + try: + old_instance = User.objects.get(pk=self.pk) + except ObjectDoesNotExist: + pass + super(User, self).save(*args, **kwags) + + # Check if old instance exists and profile picture is different + if old_instance is not None: + if old_instance.profile_picture and self.profile_picture and old_instance.profile_picture.url != self.profile_picture.url: + old_instance.profile_picture.delete(save=False) diff --git a/FOSSDB/apps/account/urls.py b/FOSSDB/apps/account/urls.py index f060b68..c812fd2 100644 --- a/FOSSDB/apps/account/urls.py +++ b/FOSSDB/apps/account/urls.py @@ -5,4 +5,5 @@ from . import views urlpatterns = [ path("signup/", views.signup_view, name="signup"), path("login/", views.login_view, name="login"), + path("/", views.profile, name="profile"), ] diff --git a/FOSSDB/apps/account/views.py b/FOSSDB/apps/account/views.py index 179216d..873c458 100644 --- a/FOSSDB/apps/account/views.py +++ b/FOSSDB/apps/account/views.py @@ -1,8 +1,19 @@ from django.contrib.auth import authenticate, login from django.contrib.auth.forms import AuthenticationForm -from django.shortcuts import redirect, render +from django.shortcuts import get_object_or_404, redirect, render from .forms import SignUpForm +from .models import User + + +def profile(request, username): + user = get_object_or_404(User, username=username) + + context = { + "title": user.username + ("" if not user.full_name else f" ({user.full_name})"), + "user": user, + } + return render(request, "account/profile.html", context) def signup_view(request): diff --git a/FOSSDB/apps/fossdb/migrations/0001_initial.py b/FOSSDB/apps/fossdb/migrations/0001_initial.py index e0946dc..fc86c22 100644 --- a/FOSSDB/apps/fossdb/migrations/0001_initial.py +++ b/FOSSDB/apps/fossdb/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.2 on 2023-06-27 10:33 +# Generated by Django 4.2.2 on 2023-06-27 12:54 from django.conf import settings from django.db import migrations, models diff --git a/FOSSDB/apps/fossdb/models.py b/FOSSDB/apps/fossdb/models.py index 7233a15..5e0f71e 100644 --- a/FOSSDB/apps/fossdb/models.py +++ b/FOSSDB/apps/fossdb/models.py @@ -1,6 +1,6 @@ import uuid -from django.contrib.auth.models import User +from django.conf import settings from django.db import models @@ -12,7 +12,7 @@ from .tag.models import Tag class Project(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, verbose_name="ID") - owner = models.ForeignKey(User, on_delete=models.CASCADE) + owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) name = models.CharField(max_length=255) description = models.TextField(blank=True, default="") license = models.ManyToManyField(License, blank=True) diff --git a/FOSSDB/apps/fossdb/star/models.py b/FOSSDB/apps/fossdb/star/models.py index 74f0452..cfa2034 100644 --- a/FOSSDB/apps/fossdb/star/models.py +++ b/FOSSDB/apps/fossdb/star/models.py @@ -1,9 +1,7 @@ from django.conf import settings from django.db import models -User = settings.AUTH_USER_MODEL - class Star(models.Model): - user = models.ForeignKey(User, on_delete=models.CASCADE) + user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) project = models.ForeignKey("Project", on_delete=models.CASCADE, related_name="stars") diff --git a/FOSSDB/settings.py b/FOSSDB/settings.py index dbdf365..9e80206 100644 --- a/FOSSDB/settings.py +++ b/FOSSDB/settings.py @@ -75,6 +75,8 @@ TEMPLATES = [ WSGI_APPLICATION = "FOSSDB.wsgi.application" +AUTH_USER_MODEL = "account.User" + # Database # https://docs.djangoproject.com/en/4.0/ref/settings/#databases diff --git a/FOSSDB/urls.py b/FOSSDB/urls.py index 6618184..d5c0637 100644 --- a/FOSSDB/urls.py +++ b/FOSSDB/urls.py @@ -21,8 +21,10 @@ from django.urls import include, path urlpatterns = [ path("", include("fossdb.urls")), path("", include("account.urls")), + # path("", include("django.contrib.auth.urls")), path("admin/", admin.site.urls), - path("", include("django.contrib.auth.urls")), ] urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) +if settings.DEBUG: + urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/media/profile_pics/default.jpg b/media/profile_pics/default.jpg new file mode 100644 index 0000000..82ff366 Binary files /dev/null and b/media/profile_pics/default.jpg differ diff --git a/templates/account/profile.html b/templates/account/profile.html new file mode 100644 index 0000000..b19e5a1 --- /dev/null +++ b/templates/account/profile.html @@ -0,0 +1,10 @@ +{% extends "layout.html" %} +{% load static %} +{% block title %}{{ title }}{% endblock %} +{% block meta %}{% endblock %} +{% block content %} +

{{ user.username }}

+ {{ user.username }}’s profile picture +

{{ user.email }}

+{% endblock %}