mirror of
https://github.com/mediacms-io/mediacms.git
synced 2025-07-23 00:28:04 +00:00
MediaCMS backend, initial commit
This commit is contained in:
0
users/__init__.py
Normal file
0
users/__init__.py
Normal file
22
users/adapter.py
Normal file
22
users/adapter.py
Normal file
@ -0,0 +1,22 @@
|
||||
from django.urls import reverse
|
||||
from django.conf import settings
|
||||
from allauth.account.adapter import DefaultAccountAdapter
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
|
||||
class MyAccountAdapter(DefaultAccountAdapter):
|
||||
def get_email_confirmation_url_stub(self, request, emailconfirmation):
|
||||
url = reverse("account_confirm_email", args=[emailconfirmation.key])
|
||||
return settings.SSL_FRONTEND_HOST + url
|
||||
|
||||
def clean_email(self, email):
|
||||
if email.split("@")[1] in settings.RESTRICTED_DOMAINS_FOR_USER_REGISTRATION:
|
||||
raise ValidationError("Domain is restricted from registering")
|
||||
return email
|
||||
|
||||
def is_open_for_signup(self, request):
|
||||
return settings.USERS_CAN_SELF_REGISTER
|
||||
|
||||
def send_mail(self, template_prefix, email, context):
|
||||
msg = self.render_mail(template_prefix, email, context)
|
||||
msg.send(fail_silently=True)
|
38
users/admin.py
Normal file
38
users/admin.py
Normal file
@ -0,0 +1,38 @@
|
||||
from django.contrib import admin
|
||||
|
||||
from .models import User
|
||||
|
||||
|
||||
class UserAdmin(admin.ModelAdmin):
|
||||
search_fields = ["email", "username", "name"]
|
||||
exclude = (
|
||||
"user_permissions",
|
||||
"title",
|
||||
"password",
|
||||
"groups",
|
||||
"last_login",
|
||||
"is_featured",
|
||||
"location",
|
||||
"first_name",
|
||||
"last_name",
|
||||
"media_count",
|
||||
"date_joined",
|
||||
"is_staff",
|
||||
"is_active",
|
||||
)
|
||||
list_display = [
|
||||
"username",
|
||||
"name",
|
||||
"email",
|
||||
"logo",
|
||||
"date_added",
|
||||
"is_superuser",
|
||||
"is_editor",
|
||||
"is_manager",
|
||||
"media_count",
|
||||
]
|
||||
list_filter = ["is_superuser", "is_editor", "is_manager"]
|
||||
ordering = ("-date_added",)
|
||||
|
||||
|
||||
admin.site.register(User, UserAdmin)
|
5
users/apps.py
Normal file
5
users/apps.py
Normal file
@ -0,0 +1,5 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class UsersConfig(AppConfig):
|
||||
name = "users"
|
59
users/forms.py
Normal file
59
users/forms.py
Normal file
@ -0,0 +1,59 @@
|
||||
from django import forms
|
||||
from .models import User, Channel
|
||||
|
||||
|
||||
class SignupForm(forms.Form):
|
||||
name = forms.CharField(max_length=100, label="Name")
|
||||
|
||||
def signup(self, request, user):
|
||||
user.name = self.cleaned_data["name"]
|
||||
user.save()
|
||||
|
||||
|
||||
class UserForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = User
|
||||
fields = (
|
||||
"name",
|
||||
"description",
|
||||
"email",
|
||||
"logo",
|
||||
"notification_on_comments",
|
||||
"is_featured",
|
||||
"advancedUser",
|
||||
"is_manager",
|
||||
"is_editor",
|
||||
#"allow_contact",
|
||||
)
|
||||
|
||||
def clean_logo(self):
|
||||
image = self.cleaned_data.get("logo", False)
|
||||
if image:
|
||||
if image.size > 2 * 1024 * 1024:
|
||||
raise forms.ValidationError("Image file too large ( > 2mb )")
|
||||
return image
|
||||
else:
|
||||
raise forms.ValidationError("Please provide a logo")
|
||||
|
||||
def __init__(self, user, *args, **kwargs):
|
||||
super(UserForm, self).__init__(*args, **kwargs)
|
||||
self.fields.pop("is_featured")
|
||||
if not user.is_superuser:
|
||||
self.fields.pop("advancedUser")
|
||||
self.fields.pop("is_manager")
|
||||
self.fields.pop("is_editor")
|
||||
|
||||
|
||||
class ChannelForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Channel
|
||||
fields = ("banner_logo",)
|
||||
|
||||
def clean_banner_logo(self):
|
||||
image = self.cleaned_data.get("banner_logo", False)
|
||||
if image:
|
||||
if image.size > 2 * 1024 * 1024:
|
||||
raise forms.ValidationError("Image file too large ( > 2mb )")
|
||||
return image
|
||||
else:
|
||||
raise forms.ValidationError("Please provide a banner")
|
283
users/migrations/0001_initial.py
Normal file
283
users/migrations/0001_initial.py
Normal file
@ -0,0 +1,283 @@
|
||||
# Generated by Django 3.1.4 on 2020-12-01 07:12
|
||||
|
||||
from django.conf import settings
|
||||
import django.contrib.auth.models
|
||||
import django.contrib.auth.validators
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
import imagekit.models.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
("auth", "0012_alter_user_first_name_max_length"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="User",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("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"
|
||||
),
|
||||
),
|
||||
(
|
||||
"logo",
|
||||
imagekit.models.fields.ProcessedImageField(
|
||||
blank=True,
|
||||
default="userlogos/user.jpg",
|
||||
upload_to="userlogos/%Y/%m/%d",
|
||||
),
|
||||
),
|
||||
("description", models.TextField(blank=True, verbose_name="About me")),
|
||||
(
|
||||
"name",
|
||||
models.CharField(
|
||||
db_index=True, max_length=250, verbose_name="full name"
|
||||
),
|
||||
),
|
||||
(
|
||||
"date_added",
|
||||
models.DateTimeField(
|
||||
db_index=True,
|
||||
default=django.utils.timezone.now,
|
||||
verbose_name="date added",
|
||||
),
|
||||
),
|
||||
(
|
||||
"is_featured",
|
||||
models.BooleanField(
|
||||
db_index=True, default=False, verbose_name="Is featured"
|
||||
),
|
||||
),
|
||||
(
|
||||
"title",
|
||||
models.CharField(blank=True, max_length=250, verbose_name="Title"),
|
||||
),
|
||||
(
|
||||
"advancedUser",
|
||||
models.BooleanField(
|
||||
db_index=True, default=False, verbose_name="advanced user"
|
||||
),
|
||||
),
|
||||
("media_count", models.IntegerField(default=0)),
|
||||
(
|
||||
"notification_on_comments",
|
||||
models.BooleanField(
|
||||
default=True,
|
||||
verbose_name="Whether you will receive email notifications for comments added to your content",
|
||||
),
|
||||
),
|
||||
(
|
||||
"allow_contact",
|
||||
models.BooleanField(
|
||||
default=False,
|
||||
verbose_name="Whether allow contact will be shown on profile page",
|
||||
),
|
||||
),
|
||||
(
|
||||
"location",
|
||||
models.CharField(
|
||||
blank=True, max_length=250, verbose_name="Location"
|
||||
),
|
||||
),
|
||||
(
|
||||
"is_editor",
|
||||
models.BooleanField(
|
||||
db_index=True, default=False, verbose_name="MediaCMS Editor"
|
||||
),
|
||||
),
|
||||
(
|
||||
"is_manager",
|
||||
models.BooleanField(
|
||||
db_index=True, default=False, verbose_name="MediaCMS Manager"
|
||||
),
|
||||
),
|
||||
(
|
||||
"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={
|
||||
"ordering": ["-date_added", "name"],
|
||||
},
|
||||
managers=[
|
||||
("objects", django.contrib.auth.models.UserManager()),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="Notification",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("action", models.CharField(blank=True, max_length=30)),
|
||||
("notify", models.BooleanField(default=False)),
|
||||
(
|
||||
"method",
|
||||
models.CharField(
|
||||
choices=[("email", "Email")], default="email", max_length=20
|
||||
),
|
||||
),
|
||||
(
|
||||
"user",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="notifications",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="Channel",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("title", models.CharField(db_index=True, max_length=90)),
|
||||
("description", models.TextField(blank=True, help_text="description")),
|
||||
("add_date", models.DateTimeField(auto_now_add=True, db_index=True)),
|
||||
("friendly_token", models.CharField(blank=True, max_length=12)),
|
||||
(
|
||||
"banner_logo",
|
||||
imagekit.models.fields.ProcessedImageField(
|
||||
blank=True,
|
||||
default="userlogos/banner.jpg",
|
||||
upload_to="userlogos/%Y/%m/%d",
|
||||
),
|
||||
),
|
||||
(
|
||||
"subscribers",
|
||||
models.ManyToManyField(
|
||||
blank=True,
|
||||
related_name="subscriptions",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
(
|
||||
"user",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="channels",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name="user",
|
||||
index=models.Index(
|
||||
fields=["-date_added", "name"], name="users_user_date_ad_4eb0b8_idx"
|
||||
),
|
||||
),
|
||||
]
|
0
users/migrations/__init__.py
Normal file
0
users/migrations/__init__.py
Normal file
220
users/models.py
Normal file
220
users/models.py
Normal file
@ -0,0 +1,220 @@
|
||||
from django.db import models
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
from django.utils import timezone
|
||||
from django.urls import reverse
|
||||
from django.dispatch import receiver
|
||||
from django.db.models.signals import post_save, post_delete
|
||||
from django.utils.html import strip_tags
|
||||
from django.core.mail import EmailMessage
|
||||
|
||||
from imagekit.processors import ResizeToFill
|
||||
from imagekit.models import ProcessedImageField
|
||||
|
||||
import files.helpers as helpers
|
||||
from files.models import Media, Tag, Category
|
||||
|
||||
|
||||
class User(AbstractUser):
|
||||
logo = ProcessedImageField(
|
||||
upload_to="userlogos/%Y/%m/%d",
|
||||
processors=[ResizeToFill(200, 200)],
|
||||
default="userlogos/user.jpg",
|
||||
format="JPEG",
|
||||
options={"quality": 75},
|
||||
blank=True,
|
||||
)
|
||||
description = models.TextField("About me", blank=True)
|
||||
|
||||
name = models.CharField("full name", max_length=250, db_index=True)
|
||||
date_added = models.DateTimeField("date added", default=timezone.now, db_index=True)
|
||||
is_featured = models.BooleanField("Is featured", default=False, db_index=True)
|
||||
|
||||
title = models.CharField("Title", max_length=250, blank=True)
|
||||
advancedUser = models.BooleanField("advanced user", default=False, db_index=True)
|
||||
media_count = models.IntegerField(default=0) # save number of videos
|
||||
notification_on_comments = models.BooleanField(
|
||||
"Whether you will receive email notifications for comments added to your content",
|
||||
default=True,
|
||||
)
|
||||
location = models.CharField("Location", max_length=250, blank=True)
|
||||
is_editor = models.BooleanField("MediaCMS Editor", default=False, db_index=True)
|
||||
is_manager = models.BooleanField("MediaCMS Manager", default=False, db_index=True)
|
||||
allow_contact = models.BooleanField(
|
||||
"Whether allow contact will be shown on profile page", default=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ["-date_added", "name"]
|
||||
indexes = [models.Index(fields=["-date_added", "name"])]
|
||||
|
||||
def update_user_media(self):
|
||||
self.media_count = Media.objects.filter(listable=True, user=self).count()
|
||||
self.save(update_fields=["media_count"])
|
||||
return True
|
||||
|
||||
def thumbnail_url(self):
|
||||
if self.logo:
|
||||
return helpers.url_from_path(self.logo.path)
|
||||
return None
|
||||
|
||||
def banner_thumbnail_url(self):
|
||||
c = self.channels.filter().order_by("add_date").first()
|
||||
if c:
|
||||
return helpers.url_from_path(c.banner_logo.path)
|
||||
return None
|
||||
|
||||
@property
|
||||
def email_is_verified(self):
|
||||
if self.emailaddress_set.first():
|
||||
if self.emailaddress_set.first().verified:
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_absolute_url(self, api=False):
|
||||
if api:
|
||||
return reverse("api_get_user", kwargs={"username": self.username})
|
||||
else:
|
||||
return reverse("get_user", kwargs={"username": self.username})
|
||||
|
||||
def edit_url(self):
|
||||
return reverse("edit_user", kwargs={"username": self.username})
|
||||
|
||||
def default_channel_edit_url(self):
|
||||
c = self.channels.filter().order_by("add_date").first()
|
||||
if c:
|
||||
return reverse("edit_channel", kwargs={"friendly_token": c.friendly_token})
|
||||
return None
|
||||
|
||||
@property
|
||||
def playlists_info(self):
|
||||
ret = []
|
||||
for playlist in self.playlists.all():
|
||||
c = {}
|
||||
c["title"] = playlist.title
|
||||
c["description"] = playlist.description
|
||||
c["media_count"] = playlist.media_count
|
||||
c["add_date"] = playlist.add_date
|
||||
c["url"] = playlist.get_absolute_url()
|
||||
ret.append(c)
|
||||
return ret
|
||||
|
||||
@property
|
||||
def media_info(self):
|
||||
ret = {}
|
||||
results = []
|
||||
ret["results"] = results
|
||||
ret["user_media"] = "/api/v1/media?author={0}".format(self.username)
|
||||
return ret
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
strip_text_items = ["name", "description", "title"]
|
||||
for item in strip_text_items:
|
||||
setattr(self, item, strip_tags(getattr(self, item, None)))
|
||||
super(User, self).save(*args, **kwargs)
|
||||
|
||||
|
||||
class Channel(models.Model):
|
||||
title = models.CharField(max_length=90, db_index=True)
|
||||
description = models.TextField(blank=True, help_text="description")
|
||||
user = models.ForeignKey(
|
||||
User, on_delete=models.CASCADE, db_index=True, related_name="channels"
|
||||
)
|
||||
add_date = models.DateTimeField(auto_now_add=True, db_index=True)
|
||||
subscribers = models.ManyToManyField(User, related_name="subscriptions", blank=True)
|
||||
friendly_token = models.CharField(blank=True, max_length=12)
|
||||
banner_logo = ProcessedImageField(
|
||||
upload_to="userlogos/%Y/%m/%d",
|
||||
processors=[ResizeToFill(900, 200)],
|
||||
default="userlogos/banner.jpg",
|
||||
format="JPEG",
|
||||
options={"quality": 85},
|
||||
blank=True,
|
||||
)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
strip_text_items = ["description", "title"]
|
||||
for item in strip_text_items:
|
||||
setattr(self, item, strip_tags(getattr(self, item, None)))
|
||||
|
||||
if not self.friendly_token:
|
||||
while True:
|
||||
friendly_token = helpers.produce_friendly_token()
|
||||
if not Channel.objects.filter(friendly_token=friendly_token):
|
||||
self.friendly_token = friendly_token
|
||||
break
|
||||
super(Channel, self).save(*args, **kwargs)
|
||||
|
||||
def __str__(self):
|
||||
return "{0} -{1}".format(self.user.username, self.title)
|
||||
|
||||
def get_absolute_url(self, edit=False):
|
||||
if edit:
|
||||
return reverse(
|
||||
"edit_channel", kwargs={"friendly_token": self.friendly_token}
|
||||
)
|
||||
else:
|
||||
return reverse(
|
||||
"view_channel", kwargs={"friendly_token": self.friendly_token}
|
||||
)
|
||||
|
||||
@property
|
||||
def edit_url(self):
|
||||
return self.get_absolute_url(edit=True)
|
||||
|
||||
|
||||
@receiver(post_save, sender=User)
|
||||
def post_user_create(sender, instance, created, **kwargs):
|
||||
# create a Channel object upon user creation, name it default
|
||||
if created:
|
||||
new = Channel.objects.create(title="default", user=instance)
|
||||
new.save()
|
||||
if settings.ADMINS_NOTIFICATIONS.get("NEW_USER", False):
|
||||
title = "[{}] - New user just registered".format(settings.PORTAL_NAME)
|
||||
msg = """
|
||||
User has just registered with email %s\n
|
||||
Visit user profile page at %s
|
||||
""" % (
|
||||
instance.email,
|
||||
settings.SSL_FRONTEND_HOST + instance.get_absolute_url(),
|
||||
)
|
||||
email = EmailMessage(
|
||||
title, msg, settings.DEFAULT_FROM_EMAIL, settings.ADMIN_EMAIL_LIST
|
||||
)
|
||||
email.send(fail_silently=True)
|
||||
|
||||
|
||||
NOTIFICATION_METHODS = (("email", "Email"),)
|
||||
|
||||
|
||||
class Notification(models.Model):
|
||||
"""User specific notifications
|
||||
To be exposed on user profile
|
||||
Needs work
|
||||
"""
|
||||
|
||||
user = models.ForeignKey(
|
||||
User, on_delete=models.CASCADE, db_index=True, related_name="notifications"
|
||||
)
|
||||
action = models.CharField(max_length=30, blank=True)
|
||||
notify = models.BooleanField(default=False)
|
||||
method = models.CharField(
|
||||
max_length=20, choices=NOTIFICATION_METHODS, default="email"
|
||||
)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
super(Notification, self).save(*args, **kwargs)
|
||||
|
||||
def __str__(self):
|
||||
return self.user.username
|
||||
|
||||
|
||||
@receiver(post_delete, sender=User)
|
||||
def delete_content(sender, instance, **kwargs):
|
||||
"""Delete user related content
|
||||
Upon user deletion
|
||||
"""
|
||||
|
||||
Media.objects.filter(user=instance).delete()
|
||||
Tag.objects.filter(user=instance).delete()
|
||||
Category.objects.filter(user=instance).delete()
|
82
users/serializers.py
Normal file
82
users/serializers.py
Normal file
@ -0,0 +1,82 @@
|
||||
from rest_framework import serializers
|
||||
from .models import User
|
||||
|
||||
|
||||
class UserSerializer(serializers.ModelSerializer):
|
||||
url = serializers.SerializerMethodField()
|
||||
api_url = serializers.SerializerMethodField()
|
||||
thumbnail_url = serializers.SerializerMethodField()
|
||||
|
||||
def get_url(self, obj):
|
||||
return self.context["request"].build_absolute_uri(obj.get_absolute_url())
|
||||
|
||||
def get_api_url(self, obj):
|
||||
return self.context["request"].build_absolute_uri(
|
||||
obj.get_absolute_url(api=True)
|
||||
)
|
||||
|
||||
def get_thumbnail_url(self, obj):
|
||||
return self.context["request"].build_absolute_uri(obj.thumbnail_url())
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
read_only_fields = (
|
||||
"date_added",
|
||||
"is_featured",
|
||||
"uid",
|
||||
"username",
|
||||
"advancedUser",
|
||||
"is_editor",
|
||||
"is_manager",
|
||||
"email_is_verified",
|
||||
)
|
||||
fields = (
|
||||
"description",
|
||||
"date_added",
|
||||
"name",
|
||||
"is_featured",
|
||||
"thumbnail_url",
|
||||
"url",
|
||||
"api_url",
|
||||
"username",
|
||||
"advancedUser",
|
||||
"is_editor",
|
||||
"is_manager",
|
||||
"email_is_verified",
|
||||
)
|
||||
|
||||
|
||||
class UserDetailSerializer(serializers.ModelSerializer):
|
||||
url = serializers.SerializerMethodField()
|
||||
api_url = serializers.SerializerMethodField()
|
||||
thumbnail_url = serializers.SerializerMethodField()
|
||||
|
||||
def get_url(self, obj):
|
||||
return self.context["request"].build_absolute_uri(obj.get_absolute_url())
|
||||
|
||||
def get_api_url(self, obj):
|
||||
return self.context["request"].build_absolute_uri(
|
||||
obj.get_absolute_url(api=True)
|
||||
)
|
||||
|
||||
def get_thumbnail_url(self, obj):
|
||||
return self.context["request"].build_absolute_uri(obj.thumbnail_url())
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
read_only_fields = ("date_added", "is_featured", "uid", "username")
|
||||
fields = (
|
||||
"description",
|
||||
"date_added",
|
||||
"name",
|
||||
"is_featured",
|
||||
"thumbnail_url",
|
||||
"banner_thumbnail_url",
|
||||
"url",
|
||||
"username",
|
||||
"media_info",
|
||||
"api_url",
|
||||
"edit_url",
|
||||
"default_channel_edit_url",
|
||||
)
|
||||
extra_kwargs = {"name": {"required": False}}
|
0
users/tests.py
Normal file
0
users/tests.py
Normal file
43
users/urls.py
Normal file
43
users/urls.py
Normal file
@ -0,0 +1,43 @@
|
||||
from django.conf.urls import url
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r"^user/(?P<username>[\w@._-]*)$", views.view_user, name="get_user"),
|
||||
url(
|
||||
r"^user/(?P<username>[\w@.]*)/media$",
|
||||
views.view_user_media,
|
||||
name="get_user_media",
|
||||
),
|
||||
url(
|
||||
r"^user/(?P<username>[\w@.]*)/playlists$",
|
||||
views.view_user_playlists,
|
||||
name="get_user_playlists",
|
||||
),
|
||||
url(
|
||||
r"^user/(?P<username>[\w@.]*)/about$",
|
||||
views.view_user_about,
|
||||
name="get_user_about",
|
||||
),
|
||||
url(r"^user/(?P<username>[\w@.]*)/edit$", views.edit_user, name="edit_user"),
|
||||
url(
|
||||
r"^channel/(?P<friendly_token>[\w]*)$", views.view_channel, name="view_channel"
|
||||
),
|
||||
url(
|
||||
r"^channel/(?P<friendly_token>[\w]*)/edit$",
|
||||
views.edit_channel,
|
||||
name="edit_channel",
|
||||
),
|
||||
# API VIEWS
|
||||
url(r"^api/v1/users$", views.UserList.as_view(), name="api_users"),
|
||||
url(r"^api/v1/users/$", views.UserList.as_view()),
|
||||
url(
|
||||
r"^api/v1/users/(?P<username>[\w@._-]*)$",
|
||||
views.UserDetail.as_view(),
|
||||
name="api_get_user",
|
||||
),
|
||||
url(
|
||||
r"^api/v1/users/(?P<username>[\w@._-]*)/contact",
|
||||
views.contact_user,
|
||||
name="api_contact_user",
|
||||
),
|
||||
]
|
18
users/validators.py
Normal file
18
users/validators.py
Normal file
@ -0,0 +1,18 @@
|
||||
import re
|
||||
|
||||
from django.core import validators
|
||||
from django.utils.deconstruct import deconstructible
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
@deconstructible
|
||||
class ASCIIUsernameValidator(validators.RegexValidator):
|
||||
regex = r"^[\w]+$"
|
||||
message = _(
|
||||
"Enter a valid username. This value may contain only "
|
||||
"English letters and numbers"
|
||||
)
|
||||
flags = re.ASCII
|
||||
|
||||
|
||||
custom_username_validators = [ASCIIUsernameValidator()]
|
296
users/views.py
Normal file
296
users/views.py
Normal file
@ -0,0 +1,296 @@
|
||||
from django.shortcuts import render
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.core.mail import EmailMessage
|
||||
from django.conf import settings
|
||||
|
||||
from rest_framework import permissions
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.settings import api_settings
|
||||
from rest_framework.exceptions import PermissionDenied
|
||||
from rest_framework import status
|
||||
from rest_framework.parsers import (
|
||||
JSONParser,
|
||||
MultiPartParser,
|
||||
FileUploadParser,
|
||||
FormParser,
|
||||
)
|
||||
from rest_framework.decorators import api_view
|
||||
from cms.permissions import IsUserOrManager
|
||||
from files.methods import is_mediacms_manager, is_mediacms_editor
|
||||
from .models import User, Channel
|
||||
from .forms import UserForm, ChannelForm
|
||||
from .serializers import UserSerializer, UserDetailSerializer
|
||||
|
||||
|
||||
def get_user(username):
|
||||
try:
|
||||
user = User.objects.get(username=username)
|
||||
return user
|
||||
except User.DoesNotExist:
|
||||
return None
|
||||
|
||||
|
||||
def view_user(request, username):
|
||||
context = {}
|
||||
user = get_user(username=username)
|
||||
if not user:
|
||||
return HttpResponseRedirect("/members")
|
||||
context["user"] = user
|
||||
context["CAN_EDIT"] = (
|
||||
True
|
||||
if ((user and user == request.user) or is_mediacms_manager(request.user))
|
||||
else False
|
||||
)
|
||||
context["CAN_DELETE"] = True if is_mediacms_manager(request.user) else False
|
||||
context["SHOW_CONTACT_FORM"] = (
|
||||
True if (user.allow_contact or is_mediacms_editor(request.user)) else False
|
||||
)
|
||||
return render(request, "cms/user.html", context)
|
||||
|
||||
|
||||
def view_user_media(request, username):
|
||||
context = {}
|
||||
user = get_user(username=username)
|
||||
if not user:
|
||||
return HttpResponseRedirect("/members")
|
||||
|
||||
context["user"] = user
|
||||
context["CAN_EDIT"] = (
|
||||
True
|
||||
if ((user and user == request.user) or request.user.is_superuser)
|
||||
else False
|
||||
)
|
||||
context["CAN_DELETE"] = True if request.user.is_superuser else False
|
||||
context["SHOW_CONTACT_FORM"] = (
|
||||
True if (user.allow_contact or is_mediacms_editor(request.user)) else False
|
||||
)
|
||||
return render(request, "cms/user_media.html", context)
|
||||
|
||||
|
||||
def view_user_playlists(request, username):
|
||||
context = {}
|
||||
user = get_user(username=username)
|
||||
if not user:
|
||||
return HttpResponseRedirect("/members")
|
||||
|
||||
context["user"] = user
|
||||
context["CAN_EDIT"] = (
|
||||
True
|
||||
if ((user and user == request.user) or request.user.is_superuser)
|
||||
else False
|
||||
)
|
||||
context["CAN_DELETE"] = True if request.user.is_superuser else False
|
||||
context["SHOW_CONTACT_FORM"] = (
|
||||
True if (user.allow_contact or is_mediacms_editor(request.user)) else False
|
||||
)
|
||||
|
||||
return render(request, "cms/user_playlists.html", context)
|
||||
|
||||
|
||||
def view_user_about(request, username):
|
||||
context = {}
|
||||
user = get_user(username=username)
|
||||
if not user:
|
||||
return HttpResponseRedirect("/members")
|
||||
|
||||
context["user"] = user
|
||||
context["CAN_EDIT"] = (
|
||||
True
|
||||
if ((user and user == request.user) or request.user.is_superuser)
|
||||
else False
|
||||
)
|
||||
context["CAN_DELETE"] = True if request.user.is_superuser else False
|
||||
context["SHOW_CONTACT_FORM"] = (
|
||||
True if (user.allow_contact or is_mediacms_editor(request.user)) else False
|
||||
)
|
||||
|
||||
return render(request, "cms/user_about.html", context)
|
||||
|
||||
|
||||
@login_required
|
||||
def edit_user(request, username):
|
||||
user = get_user(username=username)
|
||||
if not user or (user != request.user and not is_mediacms_manager(request.user)):
|
||||
return HttpResponseRedirect("/")
|
||||
|
||||
if request.method == "POST":
|
||||
form = UserForm(request.user, request.POST, request.FILES, instance=user)
|
||||
if form.is_valid():
|
||||
user = form.save(commit=False)
|
||||
user.save()
|
||||
return HttpResponseRedirect(user.get_absolute_url())
|
||||
else:
|
||||
form = UserForm(request.user, instance=user)
|
||||
return render(request, "cms/user_edit.html", {"form": form})
|
||||
|
||||
|
||||
def view_channel(request, friendly_token):
|
||||
context = {}
|
||||
channel = Channel.objects.filter(friendly_token=friendly_token).first()
|
||||
if not channel:
|
||||
user = None
|
||||
else:
|
||||
user = channel.user
|
||||
context["user"] = user
|
||||
context["CAN_EDIT"] = (
|
||||
True
|
||||
if ((user and user == request.user) or request.user.is_superuser)
|
||||
else False
|
||||
)
|
||||
return render(request, "cms/channel.html", context)
|
||||
|
||||
|
||||
@login_required
|
||||
def edit_channel(request, friendly_token):
|
||||
channel = Channel.objects.filter(friendly_token=friendly_token).first()
|
||||
if not (
|
||||
channel and request.user.is_authenticated and (request.user == channel.user)
|
||||
):
|
||||
return HttpResponseRedirect("/")
|
||||
|
||||
if request.method == "POST":
|
||||
form = ChannelForm(request.POST, request.FILES, instance=channel)
|
||||
if form.is_valid():
|
||||
channel = form.save(commit=False)
|
||||
channel.save()
|
||||
return HttpResponseRedirect(request.user.get_absolute_url())
|
||||
else:
|
||||
form = ChannelForm(instance=channel)
|
||||
return render(request, "cms/channel_edit.html", {"form": form})
|
||||
|
||||
|
||||
@api_view(["POST"])
|
||||
def contact_user(request, username):
|
||||
if not request.user.is_authenticated:
|
||||
return Response(
|
||||
{"detail": "request need be authenticated"},
|
||||
status=status.HTTP_401_UNAUTHORIZED,
|
||||
)
|
||||
user = User.objects.filter(username=username).first()
|
||||
if user and (user.allow_contact or is_mediacms_editor(request.user)):
|
||||
subject = request.data.get("subject")
|
||||
from_email = request.user.email
|
||||
subject = f"[{settings.PORTAL_NAME}] - Message from {from_email}"
|
||||
body = request.data.get("body")
|
||||
body = """
|
||||
You have received a message through the contact form\n
|
||||
Sender name: %s
|
||||
Sender email: %s\n
|
||||
\n %s
|
||||
""" % (
|
||||
request.user.name,
|
||||
from_email,
|
||||
body,
|
||||
)
|
||||
email = EmailMessage(
|
||||
subject,
|
||||
body,
|
||||
settings.DEFAULT_FROM_EMAIL,
|
||||
[user.email],
|
||||
reply_to=[from_email],
|
||||
)
|
||||
email.send(fail_silently=True)
|
||||
|
||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||
|
||||
|
||||
class UserList(APIView):
|
||||
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
|
||||
parser_classes = (JSONParser, MultiPartParser, FormParser, FileUploadParser)
|
||||
|
||||
def get(self, request, format=None):
|
||||
pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
|
||||
paginator = pagination_class()
|
||||
users = User.objects.filter()
|
||||
location = request.GET.get("location", "").strip()
|
||||
if location:
|
||||
users = users.filter(location=location)
|
||||
|
||||
page = paginator.paginate_queryset(users, request)
|
||||
|
||||
serializer = UserSerializer(page, many=True, context={"request": request})
|
||||
return paginator.get_paginated_response(serializer.data)
|
||||
|
||||
|
||||
class UserDetail(APIView):
|
||||
""""""
|
||||
|
||||
permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsUserOrManager)
|
||||
parser_classes = (JSONParser, MultiPartParser, FormParser, FileUploadParser)
|
||||
|
||||
def get_user(self, username):
|
||||
try:
|
||||
user = User.objects.get(username=username)
|
||||
# this need be explicitly called, and will call
|
||||
# has_object_permission() after has_permission has succeeded
|
||||
self.check_object_permissions(self.request, user)
|
||||
return user
|
||||
except PermissionDenied:
|
||||
return Response(
|
||||
{"detail": "not enough permissions"}, status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
except User.DoesNotExist:
|
||||
return Response(
|
||||
{"detail": "user does not exist"}, status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
def get(self, request, username, format=None):
|
||||
# Get user details
|
||||
user = self.get_user(username)
|
||||
if isinstance(user, Response):
|
||||
return user
|
||||
|
||||
serializer = UserDetailSerializer(user, context={"request": request})
|
||||
return Response(serializer.data)
|
||||
|
||||
def post(self, request, uid, format=None):
|
||||
# USER
|
||||
user = self.get_user(uid)
|
||||
if isinstance(user, Response):
|
||||
return user
|
||||
|
||||
serializer = UserDetailSerializer(
|
||||
user, data=request.data, context={"request": request}
|
||||
)
|
||||
if serializer.is_valid():
|
||||
logo = request.data.get("logo")
|
||||
if logo:
|
||||
serializer.save(logo=logo)
|
||||
else:
|
||||
serializer.save()
|
||||
|
||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
def put(self, request, uid, format=None):
|
||||
# ADMIN
|
||||
user = self.get_user(uid)
|
||||
if isinstance(user, Response):
|
||||
return user
|
||||
|
||||
if not request.user.is_superuser:
|
||||
return Response(
|
||||
{"detail": "not allowed"}, status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
action = request.data.get("action")
|
||||
if action == "feature":
|
||||
user.is_featured = True
|
||||
user.save()
|
||||
elif action == "unfeature":
|
||||
user.is_featured = False
|
||||
user.save()
|
||||
|
||||
serializer = UserDetailSerializer(user, context={"request": request})
|
||||
return Response(serializer.data)
|
||||
|
||||
def delete(self, request, username, format=None):
|
||||
# Delete a user
|
||||
user = self.get_user(username)
|
||||
if isinstance(user, Response):
|
||||
return user
|
||||
|
||||
user.delete()
|
||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
Reference in New Issue
Block a user