Skip to content

Commit

Permalink
Merge pull request #156 from 2077-Collective/staging
Browse files Browse the repository at this point in the history
PR: Add TinyMCE to acknowledgement field
  • Loading branch information
happychuks authored Aug 30, 2024
2 parents 83b0105 + bb02d68 commit 406935e
Show file tree
Hide file tree
Showing 54 changed files with 205 additions and 249 deletions.
16 changes: 7 additions & 9 deletions server/apps/research/admin/article_admin.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,27 @@
from django.contrib import admin
from django import forms
from django_ckeditor_5.widgets import CKEditor5Widget
from apps.research.models import Article, Author
from tinymce.widgets import TinyMCE

@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
"""Admin interface for the Article model."""
class Media:
# Reference the JavaScript file
js = ('ckeditor/js/custom_ckeditor_admin.js',)
fieldsets = [
('Article Details', {'fields': ['title', 'authors', 'categories', 'thumb', 'content', 'summary', 'status', 'scheduled_publish_time']}),
('Article Details', {'fields': ['title', 'authors', 'acknowledgement', 'categories', 'thumb', 'content', 'summary', 'status', 'scheduled_publish_time']}),
]
list_display = ('title', 'display_authors', 'status', 'views', 'display_categories', 'created_at', 'scheduled_publish_time')
list_display = ('title', 'display_authors', 'status', 'views', 'display_categories', 'min_read', 'created_at', 'scheduled_publish_time')
search_fields = ('title', 'authors__user__username', 'authors__twitter_username', 'content')
list_per_page = 25
list_filter = ('authors', 'status', 'categories', 'created_at')
readonly_fields = ('views', 'slug')
list_editable = ('status',)

def get_form(self, request, obj=None, **kwargs):
"""Return a form with CKEditor5Widget for the content field."""
"""Return a form with TinyMCE Widget for the selected fields."""
form = super().get_form(request, obj, **kwargs)
if 'content' in form.base_fields:
form.base_fields['content'].widget = CKEditor5Widget(attrs={"class": "django_ckeditor_5"}, config_name='extends')
for field_name in ['content', 'acknowledgement']:
if field_name in form.base_fields:
form.base_fields[field_name].widget = TinyMCE(attrs={'cols': 80, 'rows': 30, 'id': f"{field_name}_richtext_field", 'placeholder': f"Enter {field_name} here"})
return form

def display_authors(self, obj):
Expand Down
4 changes: 2 additions & 2 deletions server/apps/research/admin/author_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ class AuthorAdmin(admin.ModelAdmin):
"""Admin interface for the Author model."""

fieldsets = [
('Author Details', {'fields': ['user', 'bio', 'twitter_username']}),
('Author Details', {'fields': ['user', 'full_name', 'bio', 'twitter_username']}),
]
list_display = ('user', 'bio', 'twitter_username')
list_display = ('user', 'full_name', 'bio', 'twitter_username')
list_per_page = 25
search_fields = ('user__username', 'twitter_username')

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Generated by Django 5.0.8 on 2024-08-29 03:39

import tinymce.models
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('research', '0014_alter_article_title'),
]

operations = [
migrations.AlterModelOptions(
name='article',
options={'ordering': ('-scheduled_publish_time',)},
),
migrations.AddField(
model_name='article',
name='acknowledgement',
field=models.TextField(blank=True, null=True),
),
migrations.AddField(
model_name='author',
name='full_name',
field=models.CharField(blank=True, max_length=255, null=True),
),
migrations.AlterField(
model_name='article',
name='content',
field=tinymce.models.HTMLField(blank=True, null=True),
),
]
18 changes: 18 additions & 0 deletions server/apps/research/migrations/0016_article_time_read.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 5.0.8 on 2024-08-29 05:41

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('research', '0015_alter_article_options_article_acknowledgement_and_more'),
]

operations = [
migrations.AddField(
model_name='article',
name='time_read',
field=models.IntegerField(editable=False, null=True),
),
]
17 changes: 17 additions & 0 deletions server/apps/research/migrations/0017_remove_article_time_read.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 5.0.8 on 2024-08-29 05:57

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('research', '0016_article_time_read'),
]

operations = [
migrations.RemoveField(
model_name='article',
name='time_read',
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 5.0.8 on 2024-08-29 07:19

import tinymce.models
from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('research', '0017_remove_article_time_read'),
]

operations = [
migrations.AlterField(
model_name='article',
name='acknowledgement',
field=tinymce.models.HTMLField(blank=True, null=True),
),
]
30 changes: 24 additions & 6 deletions server/apps/research/models/article.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
from django.db import models
from django.utils.text import slugify
from django_ckeditor_5.fields import CKEditor5Field
from apps.common.models import BaseModel
from apps.research.managers import ArticleObjects
from .category import Category
from .author import Author
from django.utils import timezone
from django.conf import settings
from tinymce.models import HTMLField
import math


def get_default_thumb():
Expand All @@ -21,33 +22,43 @@ class Article(BaseModel):
)

title = models.TextField()
content = CKEditor5Field('Text', null=True, blank=True, config_name='extends')
content = HTMLField(blank=True, null=True)
summary = models.TextField(blank=True)
acknowledgement = HTMLField(blank=True, null=True)
authors = models.ManyToManyField(Author, blank=True, related_name='articles')
slug = models.SlugField(blank=True)
categories = models.ManyToManyField(Category, blank=True, related_name='articles')
thumb = models.ImageField(upload_to='images/', default=get_default_thumb, blank=True)
views = models.PositiveBigIntegerField(default=0)
status = models.CharField(max_length=10, choices=options, default='draft')
status = models.CharField(max_length=10, choices=options, default='draft')
scheduled_publish_time = models.DateTimeField(null=True, blank=True, db_index=True)

objects = models.Manager()
post_objects = ArticleObjects()

class Meta:
ordering = ('-created_at',)
ordering = ('-scheduled_publish_time',)

@property
def min_read(self):
# Compute reading time based on content length
word_count = len(self.content.split())
words_per_minute = 300 # Average reading speed (words per minute)
minutes = max(1, round(word_count / words_per_minute))
return minutes

def __str__(self):
return self.title

def save(self, *args, **kwargs):
"""Override the save method to generate a unique slug."""
if not self.slug:
if not self.slug or self.title_update():
self.slug = self.generate_unique_slug()

"""Override the save method to handle scheduled publishing."""
if self.scheduled_publish_time and self.status == 'draft' and timezone.now() >= self.scheduled_publish_time:
self.status = 'ready'

super().save(*args, **kwargs)

def generate_unique_slug(self):
Expand All @@ -59,3 +70,10 @@ def generate_unique_slug(self):
slug = f"{base_slug}-{num}"
num += 1
return slug

def title_update(self):
"""Check if the title has changed."""
if self.pk:
original = Article.objects.get(pk=self.pk)
return original.title != self.title
return False
1 change: 1 addition & 0 deletions server/apps/research/models/author.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class Author(BaseModel):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.TextField(blank=True, null=True)
twitter_username = models.CharField(max_length=100, blank=True, null=True)
full_name = models.CharField(max_length=255, blank=True, null=True)

class Meta:
verbose_name_plural = 'Authors'
Expand Down
4 changes: 2 additions & 2 deletions server/apps/research/serializers/article_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class Meta:
model = Article
fields = [
'id', 'slug', 'title', 'authors', 'thumb',
'categories', 'summary', 'content',
'categories', 'summary', 'acknowledgement', 'content',
'status', 'views', 'created_at', 'updated_at', 'scheduled_publish_time'
]

Expand All @@ -25,7 +25,7 @@ class ArticleCreateUpdateSerializer(serializers.ModelSerializer):

class Meta:
model = Article
fields = ['title', 'categories', 'thumb', 'content', 'summary', 'status', 'authors', 'scheduled_publish_time']
fields = ['title', 'categories', 'thumb', 'content', 'summary', 'acknowledgement', 'status', 'authors', 'scheduled_publish_time']

# TODO : Debug this method to add the logged-in user as the author when creating a new article
def create(self, validated_data):
Expand Down
2 changes: 1 addition & 1 deletion server/apps/research/serializers/author_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class AuthorSerializer(serializers.ModelSerializer):

class Meta:
model = Author
fields = ['id', 'username', 'bio', 'twitter_username']
fields = ['id', 'username', 'full_name', 'bio', 'twitter_username']

def get_username(self, obj):
return obj.user.username
1 change: 0 additions & 1 deletion server/apps/research/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,4 @@ def publish_scheduled_articles():
"""Publish articles that are scheduled to be published."""
now = timezone.now()
articles_to_publish = Article.objects.filter(status='draft', scheduled_publish_time__lte=now)
articles_to_publish = Article.objects.filter(status='draft', scheduled_publish_time__lte=now)
articles_to_publish.update(status='ready')
2 changes: 1 addition & 1 deletion server/apps/research/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def index(request):
class ArticleViewSet(viewsets.ModelViewSet):
"""API endpoint for articles."""
permission_classes = [ArticleUserWritePermission]
#serializer_class = ArticleSerializer


def get_serializer_class(self):
"""Return appropriate serializer class based on request method."""
Expand Down
15 changes: 8 additions & 7 deletions server/core/config/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,8 @@

THIRD_PARTY_APPS = [
'corsheaders',
'ckeditor_uploader',
'django_ckeditor_5',
'django_celery_beat',
'tinymce',
]

INSTALLED_APPS = DJANGO_APPS + LOCAL_APPS + THIRD_PARTY_APPS
Expand Down Expand Up @@ -97,7 +96,6 @@
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
BASE_DIR / '../../client/build',
BASE_DIR / 'templates',
],
'APP_DIRS': True,
Expand All @@ -107,6 +105,9 @@
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',

# Tinymce Api Context
'core.config.tinymce.tinymce_api_key',
],
},
},
Expand Down Expand Up @@ -162,7 +163,6 @@
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

STATICFILES_DIRS = [
'../client/build/static/',
os.path.join(BASE_DIR, 'static'),
]

Expand All @@ -184,8 +184,9 @@
"staticfiles.W004"
]

# Tinymce API Config
TINYMCE_API_KEY = os.getenv('TINYMCE_API_KEY')

from .jazzmin import *
from .ckeditor import *
from .celery_config import *
from .mail import *

from .mail import *
Loading

0 comments on commit 406935e

Please sign in to comment.