From e83fed2f6484e329c88bc211c49b3d8ce9cefe1d Mon Sep 17 00:00:00 2001 From: Aquiles Carattino Date: Sat, 18 Sep 2021 11:51:41 +0200 Subject: [PATCH] Added volunteer model, page, and view. Missing adding categories --- config/settings/base.py | 1 + .../cms/migrations/0027_volunteerindexpage.py | 29 ++++++++ klimaat_helpdesk/cms/models.py | 27 +++++++- .../volunteers/includes/volunteer_block.html | 35 ++++++++++ .../templates/volunteers/volunteers_list.html | 66 +++++++++++++++++++ klimaat_helpdesk/volunteers/__init__.py | 0 klimaat_helpdesk/volunteers/admin.py | 23 +++++++ klimaat_helpdesk/volunteers/apps.py | 7 ++ klimaat_helpdesk/volunteers/models.py | 57 ++++++++++++++++ 9 files changed, 244 insertions(+), 1 deletion(-) create mode 100644 klimaat_helpdesk/cms/migrations/0027_volunteerindexpage.py create mode 100644 klimaat_helpdesk/templates/volunteers/includes/volunteer_block.html create mode 100644 klimaat_helpdesk/templates/volunteers/volunteers_list.html create mode 100644 klimaat_helpdesk/volunteers/__init__.py create mode 100644 klimaat_helpdesk/volunteers/admin.py create mode 100644 klimaat_helpdesk/volunteers/apps.py create mode 100644 klimaat_helpdesk/volunteers/models.py diff --git a/config/settings/base.py b/config/settings/base.py index 044dd30..38fb93a 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -102,6 +102,7 @@ "klimaat_helpdesk.users.apps.UsersConfig", "klimaat_helpdesk.core.apps.CoreConfig", "klimaat_helpdesk.experts.apps.ExpertsConfig", + "klimaat_helpdesk.volunteers.apps.VolunteersConfig", "klimaat_helpdesk.cms.apps.CmsConfig", "klimaat_helpdesk.search.apps.SearchConfig", ] diff --git a/klimaat_helpdesk/cms/migrations/0027_volunteerindexpage.py b/klimaat_helpdesk/cms/migrations/0027_volunteerindexpage.py new file mode 100644 index 0000000..ec3a697 --- /dev/null +++ b/klimaat_helpdesk/cms/migrations/0027_volunteerindexpage.py @@ -0,0 +1,29 @@ +# Generated by Django 3.1.13 on 2021-09-18 09:50 + +from django.db import migrations, models +import django.db.models.deletion +import wagtail.core.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('wagtailcore', '0060_fix_workflow_unique_constraint'), + ('cms', '0026_auto_20201108_1222'), + ] + + operations = [ + migrations.CreateModel( + name='VolunteerIndexPage', + fields=[ + ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')), + ('subtitle', models.CharField(max_length=128)), + ('intro', wagtail.core.fields.RichTextField(blank=True)), + ('outro', wagtail.core.fields.RichTextField(blank=True)), + ], + options={ + 'abstract': False, + }, + bases=('wagtailcore.page',), + ), + ] diff --git a/klimaat_helpdesk/cms/models.py b/klimaat_helpdesk/cms/models.py index aadd928..1978bbd 100644 --- a/klimaat_helpdesk/cms/models.py +++ b/klimaat_helpdesk/cms/models.py @@ -21,6 +21,7 @@ from klimaat_helpdesk.cms.blocks import AnswerRichTextBlock, QuoteBlock, AnswerImageBlock, AnswerOriginBlock, \ RelatedItemsBlock from klimaat_helpdesk.experts.models import Expert +from klimaat_helpdesk.volunteers.models import Volunteer class ExpertAnswerRelationship(Orderable, models.Model): @@ -204,7 +205,7 @@ def get_references(self): TODO: References for articles can be separated from the origin and make them a proper ListBlock that can be handled by editors as they see fit. Having the references within a StreamField of 'origins' seems counter intuitive. - + """ ref_list = [] try: @@ -391,6 +392,30 @@ def get_context(self, request, *args, **kwargs): return context +class VolunteerIndexPage(Page): + """ List of volunteers on the website """ + template = 'volunteers/volunteers_list.html' + subtitle = models.CharField(max_length=128, blank=False) + intro = RichTextField(blank=True) + outro = RichTextField(blank=True) + + content_panels = Page.content_panels + [ + FieldPanel('subtitle'), + FieldPanel('intro'), + FieldPanel('outro'), + ] + + def get_context(self, request, *args, **kwargs): + context = super(VolunteerIndexPage, self).get_context(request, *args, **kwargs) + volunteers = Volunteer.objects.all() + + context.update({ + 'volunteers': volunteers, + 'answers_page': AnswerIndexPage.objects.first().url, + }) + return context + + class GeneralPage(Page): """ A page that won't show sidebar. Ideal for privacy policy, etc. """ template = 'cms/general_page.html' diff --git a/klimaat_helpdesk/templates/volunteers/includes/volunteer_block.html b/klimaat_helpdesk/templates/volunteers/includes/volunteer_block.html new file mode 100644 index 0000000..bdff24a --- /dev/null +++ b/klimaat_helpdesk/templates/volunteers/includes/volunteer_block.html @@ -0,0 +1,35 @@ +{% load static wagtailimages_tags i18n %} + +
+
+ {% image volunteer.picture fill-200x200 as image %} + {{ image.alt }} +
+ +
+
+

{{ volunteer.name }}

+ {% if volunteer.website %} + + {% endif %} + {% if volunteer.linkedin_profile %} + + {% endif %} + {% if volunteer.twitter_profile %} + + {% endif %} + {% if volunteer.orcid_profile %} + + {% endif %} +
+
+

{{ volunteer.affiliation }}

+

{{ volunteer.bio }}

+
+ + +
+
diff --git a/klimaat_helpdesk/templates/volunteers/volunteers_list.html b/klimaat_helpdesk/templates/volunteers/volunteers_list.html new file mode 100644 index 0000000..034a25b --- /dev/null +++ b/klimaat_helpdesk/templates/volunteers/volunteers_list.html @@ -0,0 +1,66 @@ +{% extends "base.html" %} +{% load i18n static wagtailcore_tags%} + +{% block smokedglass %} +{% endblock %} + +{% block content_area %} +
+ +{% block head %} +
+
+ + + + +
+

{{ page.title }}

+
+ +
+
+ {{ page.intro|richtext }} +
+
+ +
+
+{% endblock %} + +{% block content %} +
+
+
+ {% for volunteer in volunteers %} + {% include 'volunteers/includes/volunteer_block.html' with volunteer=volunteer %} + {% endfor %} +
+
+
+ + + + {% endblock %} +
+{% endblock content_area %} diff --git a/klimaat_helpdesk/volunteers/__init__.py b/klimaat_helpdesk/volunteers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/klimaat_helpdesk/volunteers/admin.py b/klimaat_helpdesk/volunteers/admin.py new file mode 100644 index 0000000..d4feaf8 --- /dev/null +++ b/klimaat_helpdesk/volunteers/admin.py @@ -0,0 +1,23 @@ +from django.utils.translation import gettext_lazy as _ +from django.contrib import admin +from wagtail.contrib.modeladmin.options import ModelAdmin, modeladmin_register + +from klimaat_helpdesk.volunteers.models import Volunteer + + +class VolunteerAdmin(ModelAdmin): + model = Volunteer + menu_label = _('Volunteers') + menu_icon = 'user' + menu_order = 280 + add_to_settings_menu = False + exclude_from_explorer = False + list_display = ('name', 'affiliation', 'email') + search_fields = ('name', 'affiliation') + + +modeladmin_register(VolunteerAdmin) + +admin.site.register([ + Volunteer, +]) diff --git a/klimaat_helpdesk/volunteers/apps.py b/klimaat_helpdesk/volunteers/apps.py new file mode 100644 index 0000000..165fd54 --- /dev/null +++ b/klimaat_helpdesk/volunteers/apps.py @@ -0,0 +1,7 @@ +from django.apps import AppConfig +from django.utils.translation import gettext_lazy as _ + + +class VolunteersConfig(AppConfig): + name = "klimaat_helpdesk.volunteers" + verbose_name = _("Volunteers") diff --git a/klimaat_helpdesk/volunteers/models.py b/klimaat_helpdesk/volunteers/models.py new file mode 100644 index 0000000..20968ab --- /dev/null +++ b/klimaat_helpdesk/volunteers/models.py @@ -0,0 +1,57 @@ +from django.db import models +from django.utils.translation import ugettext_lazy as _ +from taggit.managers import TaggableManager +from wagtail.admin.edit_handlers import FieldPanel +from wagtail.images.edit_handlers import ImageChooserPanel + +from wagtail.snippets.models import register_snippet + +@register_snippet +class Volunteer(models.Model): + """ Volunteers are people who contribute time to Klimaat Helpdesk. The model is similar to those of + experts, with some minor differences that's why it is not a plain inherited object. + """ + + name = models.CharField(_('name'), max_length=255, null=False, blank=False) + email = models.EmailField(_('email'), null=True, blank=True) + bio = models.TextField(verbose_name=_('biography'), null=False, blank=False) + picture = models.ForeignKey('wagtailimages.Image', null=True, related_name='+', on_delete=models.SET_NULL) + areas_expertise = TaggableManager(verbose_name=_('areas of expertise')) + affiliation = models.CharField(_('Affiliation'), blank=False, max_length=128) + website = models.URLField(_('Website'), blank=True) + twitter_profile = models.URLField(_('Twitter Profile'), blank=True, null=True) + linkedin_profile = models.URLField(_('LinkedIn Profile'), blank=True, null=True) + orcid_profile = models.URLField(_('OrcID Link'), blank=True, null=True) + creation_date = models.DateTimeField(auto_now_add=True) + last_updated = models.DateTimeField(auto_now_add=True) + active_since = models.DateTimeField(null=True, auto_now=False, default=None) + + panels = [ + FieldPanel('name'), + ImageChooserPanel('picture', heading="Volunteer's photo, 1:1 aspect ratio (square) works best"), + FieldPanel('email'), + FieldPanel('bio'), + FieldPanel('affiliation'), + FieldPanel('areas_expertise', heading="Areas of expertise. A maximum of 16 characters per word is recommended for optimal mobile display"), + FieldPanel('website'), + FieldPanel('twitter_profile'), + FieldPanel('linkedin_profile'), + FieldPanel('orcid_profile'), + ] + + def __str__(self): + return f"{self.name}" + + @property + def twitter_username(self): + if self.twitter_profile: + if self.twitter_profile.endswith('/'): + self.twitter_profile = self.twitter_profile[:-1] + self.save() + twitter_username = self.twitter_profile.split('/')[-1] + return twitter_username + + class Meta: + ordering = ['name', ] + +