diff --git a/ads/models.py b/ads/models.py index f7f8cf904..e90285cee 100644 --- a/ads/models.py +++ b/ads/models.py @@ -90,7 +90,7 @@ class AdTagSettings(ClusterableModel, BaseSetting): ), MultiFieldPanel( [ - InlinePanel("home_sidebar_placements", label="Home sidebar placement tags"), + InlinePanel("home_sidebar_placements", label="Sidebar placement tags"), ], heading="Sidebar Placement Tags" ), diff --git a/ads/templatetags/ubyssey_ad_filters.py b/ads/templatetags/ubyssey_ad_filters.py index 2d06390f5..7e0c08c6a 100644 --- a/ads/templatetags/ubyssey_ad_filters.py +++ b/ads/templatetags/ubyssey_ad_filters.py @@ -23,7 +23,8 @@ def inject_ads(value, is_mobile): is_mobile = True # Break down content into paragraphs - paragraphs = value.split("

") + soup = BeautifulSoup(value, 'html.parser') + paragraphs = soup.select(".article-content > p") if PARAGRAPHS_PER_AD < len(paragraphs): # If the article is somehow too short for even one ad, it doesn't get any x = range(0, len(paragraphs), PARAGRAPHS_PER_AD) @@ -44,11 +45,9 @@ def inject_ads(value, is_mobile): 'size' : size, } ad_string = render_to_string('ads/advertisement_inline.html', context=ad_context) - paragraphs[n] = ad_string + paragraphs[n] + paragraphs[n].insert_after(BeautifulSoup(ad_string, 'html.parser')) - # Assemble our text back with injected HTML - value = "

".join(paragraphs) - return value + return soup.prettify() @register.filter(name='specify_homepage_sidebar_ads') @stringfilter diff --git a/ads/templatetags/ubyssey_ad_tags.py b/ads/templatetags/ubyssey_ad_tags.py index 8ef5840f7..71c6a15a4 100644 --- a/ads/templatetags/ubyssey_ad_tags.py +++ b/ads/templatetags/ubyssey_ad_tags.py @@ -30,12 +30,12 @@ def gpt_define_tag(slug, is_mobile=False) -> dict: 'skyscraper' : '[[300, 250], [300, 600]]', 'banner': '[468, 60]', 'leaderboard': '[[728, 90], [970, 90]]', - 'mobile-leaderboard': '[300, 50]' + 'mobile-leaderboard': '[320, 50]' } if is_mobile and "Intra_Article" in ad_slot.dfp: return { 'div_id' : ad_slot.div_id, - 'dfs' : ad_slot.dfs, + 'dfp' : ad_slot.dfp, 'size' : SIZES['mobile-leaderboard'], } else: diff --git a/archive/migrations/0002_sectionpageorderables.py b/archive/migrations/0002_sectionpageorderables.py new file mode 100644 index 000000000..3c7813d07 --- /dev/null +++ b/archive/migrations/0002_sectionpageorderables.py @@ -0,0 +1,29 @@ +# Generated by Django 3.2.11 on 2023-07-11 11:26 + +from django.db import migrations, models +import django.db.models.deletion +import modelcluster.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('wagtailcore', '0066_collection_management_permissions'), + ('archive', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='SectionPageOrderables', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('sort_order', models.IntegerField(blank=True, editable=False, null=True)), + ('page', modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='sections_filters', to='archive.archivepage')), + ('section_filter', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailcore.page', verbose_name='Page Link')), + ], + options={ + 'ordering': ['sort_order'], + 'abstract': False, + }, + ), + ] diff --git a/archive/migrations/0003_auto_20230711_2106.py b/archive/migrations/0003_auto_20230711_2106.py new file mode 100644 index 000000000..50f981218 --- /dev/null +++ b/archive/migrations/0003_auto_20230711_2106.py @@ -0,0 +1,35 @@ +# Generated by Django 3.2.11 on 2023-07-12 04:06 + +from django.db import migrations, models +import django.db.models.deletion +import modelcluster.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('wagtailcore', '0066_collection_management_permissions'), + ('section', '0003_sectionpage_use_parent_colour'), + ('archive', '0002_sectionpageorderables'), + ] + + operations = [ + migrations.AlterField( + model_name='sectionpageorderables', + name='section_filter', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailcore.page', verbose_name='Section Page'), + ), + migrations.CreateModel( + name='MagazineOrderables', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('sort_order', models.IntegerField(blank=True, editable=False, null=True)), + ('magazine_filter', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='section.categorysnippet', verbose_name='Magazine')), + ('page', modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='magazines_filters', to='archive.archivepage')), + ], + options={ + 'ordering': ['sort_order'], + 'abstract': False, + }, + ), + ] diff --git a/archive/migrations/0004_spooforderables.py b/archive/migrations/0004_spooforderables.py new file mode 100644 index 000000000..75d0baa57 --- /dev/null +++ b/archive/migrations/0004_spooforderables.py @@ -0,0 +1,29 @@ +# Generated by Django 3.2.11 on 2023-07-12 04:48 + +from django.db import migrations, models +import django.db.models.deletion +import modelcluster.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('section', '0003_sectionpage_use_parent_colour'), + ('archive', '0003_auto_20230711_2106'), + ] + + operations = [ + migrations.CreateModel( + name='SpoofOrderables', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('sort_order', models.IntegerField(blank=True, editable=False, null=True)), + ('page', modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='spoofs_filters', to='archive.archivepage')), + ('spoof_filter', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='section.categorysnippet', verbose_name='Spoof')), + ], + options={ + 'ordering': ['sort_order'], + 'abstract': False, + }, + ), + ] diff --git a/archive/models.py b/archive/models.py index d40308451..b409e718a 100644 --- a/archive/models.py +++ b/archive/models.py @@ -1,19 +1,122 @@ from django.db import models from django_user_agents.utils import get_user_agent from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator +from django.shortcuts import render +from specialfeaturelanding.models import SpecialLandingPage +from section.models import SectionablePage, CategorySnippet from article.models import ArticlePage +from modelcluster.fields import ParentalKey + from section.models import SectionPage -from wagtail.core.models import Page +from wagtail.core.models import Page, Orderable +from wagtail.snippets.edit_handlers import SnippetChooserPanel +from wagtail.admin.edit_handlers import MultiFieldPanel, InlinePanel, HelpPanel, PageChooserPanel + +from wagtail.contrib.routable_page.models import RoutablePageMixin, route +from videos.models import VideosPage, VideoSnippet + +class SectionPageOrderables(Orderable): + page = ParentalKey("archive.ArchivePage", on_delete=models.CASCADE, related_name="sections_filters") + + section_filter = models.ForeignKey( + 'wagtailcore.Page', + null=True, + blank=True, + on_delete=models.SET_NULL, + related_name='+', + verbose_name="Section Page" + ) + + panels = [ + MultiFieldPanel( + [ + PageChooserPanel('section_filter', 'section.SectionPage'), + ], + heading="Section Pages", + ), + ] + + +class MagazineOrderables(Orderable): + page = ParentalKey("archive.ArchivePage", on_delete=models.CASCADE, related_name="magazines_filters") + + magazine_filter = models.ForeignKey( + 'section.CategorySnippet', + null=True, + blank=True, + on_delete=models.SET_NULL, + related_name='+', + verbose_name="Magazine" + ) + + panels = [ + MultiFieldPanel( + [ + SnippetChooserPanel('magazine_filter'), + ], + heading="Magazine", + ), + ] + +class SpoofOrderables(Orderable): + page = ParentalKey("archive.ArchivePage", on_delete=models.CASCADE, related_name="spoofs_filters") + + spoof_filter = models.ForeignKey( + 'section.CategorySnippet', + null=True, + blank=True, + on_delete=models.SET_NULL, + related_name='+', + verbose_name="Spoof" + ) + + panels = [ + MultiFieldPanel( + [ + SnippetChooserPanel('spoof_filter'), + ], + heading="Spoof", + ), + ] -class ArchivePage(Page): + +class ArchivePage(RoutablePageMixin, Page): template = "archive/archive_page.html" parent_page_types = [ 'home.HomePage', ] max_count_per_parent = 1 - + + + content_panels = Page.content_panels + [ + MultiFieldPanel( + [ + HelpPanel("List all the pages you want to have in the section filter"), + InlinePanel('sections_filters', min_num=1, label="Section"), + ], + heading="Section Filters", + classname="collapsible", + ), + MultiFieldPanel( + [ + HelpPanel("List all the category snippets you want to have in the magazine filter"), + InlinePanel('magazines_filters', min_num=1, label="Magazines"), + ], + heading="Magazine Filters", + classname="collapsible", + ), + MultiFieldPanel( + [ + HelpPanel("List all the category snippets you want to have in the spoof filter"), + InlinePanel('spoofs_filters', min_num=1, label="Spoofs"), + ], + heading="Spoof Filters", + classname="collapsible", + ), + ] + def __get_years(self): """ Returns: @@ -39,37 +142,34 @@ def __parse_int_or_none(self, maybe_int): except (TypeError, ValueError): return None - def get_context(self, request, *args, **kwargs): - context = super().get_context(request, *args, **kwargs) - # Getting information from the HTTP request + def get_context(self, request, video_section): + # Get queries and context + context = super().get_context(request) search_query = request.GET.get("q") - page = request.GET.get("page") order = request.GET.get("order") - section_slug = request.GET.get('section') self.year = self.__parse_int_or_none(request.GET.get('year')) - if order == 'oldest': - article_order = "explicit_published_at" - else: - article_order = "-explicit_published_at" - context["order"] = order - - # Get and trim the queryset of articles - if section_slug: - articles = ArticlePage.objects.from_section(section_slug=section_slug).live().public().order_by(article_order) - else: - articles = ArticlePage.objects.live().public().order_by(article_order) - if self.year: - articles = articles.filter(explicit_published_at__year=str(self.year)) + + # Set context + context['video_section'] = video_section + context['sections'] = self.sections_filters.all() + context['order'] = order + context['year'] = self.year + context['years'] = self.__get_years() + context['q'] = search_query + context['meta'] = { 'title': 'Archive' } - # If there's a search query, then we run the search on the articles LAST. - # Once we hit thes earch then we can't run .filter(...) on the results as if it were a queryset - if search_query: - context["search_query"] = search_query - articles = articles.search(search_query) + return context + + def get_paginated_articles(self, context, objects, video_section, request): + page = request.GET.get("page") - # Paginate all posts by 15 per page - paginator = Paginator(articles, per_page=15) + if video_section == False: + # Paginate all posts by 15 per page + paginator = Paginator(objects, per_page=15) + else: + # Paginate all posts by 15 per page + paginator = Paginator(objects, per_page=5) try: # If the page exists and the ?page=x is an int paginated_articles = paginator.page(page) @@ -83,14 +183,183 @@ def get_context(self, request, *args, **kwargs): paginated_articles = paginator.page(paginator.num_pages) context["page_obj"] = paginated_articles #this object is often called page_obj in Django docs. Careful, because but Page means something else in Wagtail + - # set context - context['sections'] = SectionPage.objects.live() - context['section_slug'] = section_slug - context['order'] = order - context['year'] = self.year - context['years'] = self.__get_years() - context['q'] = search_query - context['meta'] = { 'title': 'Archive' } + return context + + def get_order_objects(self, order, objects, videos_section): + if videos_section == False: + if order == 'oldest': + article_order = "explicit_published_at" + else: + article_order = "-explicit_published_at" + + return objects.order_by(article_order) + else: + if order == 'oldest': + videos_order = "created_at" + else: + videos_order = "-created_at" + + return objects.order_by(videos_order) + + + def get_year_objects(self, objects, videos_section): + if videos_section == False: + return objects.filter(explicit_published_at__year=str(self.year)) + else: + return objects.filter(created_at__gte=str(self.year) + "-01-01", created_at__lte = str(self.year + 1) + "-12-31") + + def get_search_objects(self, search_query, objects, video_section): + + # If there's a search query, then we run the search on the articles LAST. + # Once we hit thes earch then we can't run .filter(...) on the results as if it were a queryset + if video_section == False: + return objects.search(search_query) + else: + return objects.filter(title=search_query) + + def get_category(self): + return CategorySnippet.objects.all() + categories = property(fget=get_category) + + @route(r'^$', name='general_view') + def get_archive_general_articles(self, request): + video_section = False + context = self.get_context(request, video_section) + context["section_slug"] = "All" + search_query = context["q"] + + articles = ArticlePage.objects.live().public() + + if context["order"]: + articles = self.get_order_objects(context["order"], articles, video_section) - return context \ No newline at end of file + if self.year: + articles = self.get_year_objects(articles, video_section) + + # The larger issue is that the search query in general search will always prioritize articles over videos. If users what to find videos then they have to select the videos section then search + if search_query: + videos = VideoSnippet.objects.all() + articles = self.get_search_objects(search_query, articles, video_section) + videos = self.get_search_objects(search_query, videos, True) + + if len(articles) < 1 and len(videos) > 0: + video_section = True + context = self.get_paginated_articles(context, videos, video_section, request) + context["video_section"] = True + else: + context = self.get_paginated_articles(context, articles, video_section, request) + else: + context = self.get_paginated_articles(context, articles, video_section, request) + + + return render(request, "archive/archive_page.html", context) + + @route(r'^section/(?P[-\w]+)/$', name='section_view') + @route(r'^magazines/$', name='magazines_general_view') + def get_section_articles(self, request, sections_slug="magazine"): + video_section = False + context = self.get_context(request, video_section) + context['section_slug'] = sections_slug + + search_query = context["q"] + + articles = ArticlePage.objects.from_section(section_slug=sections_slug).live().public() + + if context["order"]: + articles = self.get_order_objects(context["order"], articles, video_section) + + if self.year: + articles = self.get_year_objects(articles, video_section) + + if search_query: + articles = self.get_search_objects(search_query, articles, video_section) + + context = self.get_paginated_articles(context, articles, video_section, request) + + return render(request, "archive/archive_page.html", context) + + @route(r'^videos/$', name="videos_view") + def get_videos(self, request, *args, **kwargs): + video_section = True + context = self.get_context(request, video_section) + + search_query = context["q"] + + videos = VideoSnippet.objects.all() + + if context["order"]: + videos = self.get_order_objects(context["order"], videos, video_section) + + if self.year: + videos = self.get_year_objects(videos, video_section) + + if search_query: + videos = self.get_search_objects(search_query, videos, video_section) + + + context = self.get_paginated_articles(context, videos, video_section, request) + + return render(request, "archive/archive_page.html", context) + + + @route(r'^magazines/(?P[-\w]+)/$', name="magazines_view") + def get_magazine_articles(self, request, magazine_slug): + video_section = False + context = self.get_context(request, video_section) + context['magazine_slug'] = magazine_slug + + search_query = context["q"] + + + if len(SpecialLandingPage.objects.filter(category__slug=magazine_slug)) > 0: + articles = ArticlePage.objects.from_magazine_special_section(section_slug=magazine_slug) + else: + articles = ArticlePage.objects.live().public().filter(category__slug=magazine_slug) + + if context["order"]: + articles = self.get_order_objects(context["order"], articles, video_section) + + if self.year: + articles = self.get_year_objects(articles, video_section) + + if search_query: + articles = self.get_search_objects(search_query, articles, video_section) + + context = self.get_paginated_articles(context, articles, video_section, request) + + return render(request, "archive/archive_page.html", context) + + @route(r'^spoofs/(?P[-\w]+)/$', name="spoofs_view") + @route(r'^spoofs/$', name="spoofs_general_view") + def get_spoof_articles(self, request, spoof_slug="All Spoofs"): + video_section = False + context = self.get_context(request, video_section) + context['spoof_slug'] = spoof_slug + + search_query = context["q"] + + if spoof_slug == "All Spoofs": + articles = ArticlePage.objects.none() + + for iter in self.spoofs_filters.all(): + sections = SectionPage.objects.filter(categories__slug=iter.spoof_filter.slug).live().public() + articles = articles | ArticlePage.objects.from_section(section_slug=sections[0].slug).live().public() + else: + sections = SectionPage.objects.filter(categories__slug=spoof_slug).live().public() + articles = ArticlePage.objects.from_section(section_slug=sections[0].slug).live().public() + + if context["order"]: + articles = self.get_order_objects(context["order"], articles, video_section) + + if self.year: + articles = self.get_year_objects(articles, video_section) + + if search_query: + articles = self.get_search_objects(search_query, articles, video_section) + + + context = self.get_paginated_articles(context, articles, video_section, request) + + return render(request, "archive/archive_page.html", context) \ No newline at end of file diff --git a/archive/templates/archive/archive_page.html b/archive/templates/archive/archive_page.html index c62e67d54..46592e6d8 100644 --- a/archive/templates/archive/archive_page.html +++ b/archive/templates/archive/archive_page.html @@ -3,6 +3,7 @@ {% block header %} {% include 'navigation/headers/topbar.html' %} {% include 'navigation/headers/mobile.html' %} +{% load archive_tags %} {% endblock %} {% block content %} @@ -10,11 +11,17 @@
-

Search the archive

+

Search the Archive

+ + {% include 'archive/objects/archive.html' %}
-{% endblock %} \ No newline at end of file +{% endblock %} + + diff --git a/archive/templates/archive/objects/archive.html b/archive/templates/archive/objects/archive.html index 1d3b8c0bd..4136818a0 100644 --- a/archive/templates/archive/objects/archive.html +++ b/archive/templates/archive/objects/archive.html @@ -1,172 +1,30 @@ +{% load wagtailroutablepage_tags %} {% load archive_tags %} +{% load wagtailimages_tags %} -
+ - +
- {% if sections or years %} -
- -
-

Year

- -
- -
-

Section

- -
- -
- {% endif %} - -
-
- - {% if years %} -
- - - {% if year %}{{ year }}{% else %}All years{% endif %} - - - -
-
-
-
-

Filter by year:

-
- -
-
-
    -
  • - All -
  • - {% for y in years %} -
  • - {{ y }} -
  • - {% endfor %} -
-
-
-
- -
- {% endif %} - - {% if sections %} -
- - - {% comment %} NTS ugly placeholder {% endcomment %} - {% if section_slug %}{{ section_slug }}{% else %}All sections{% endif %} - - - -
-
-
-
-

Filter by section:

-
- -
-
-
    -
  • - All -
  • - {% for section in sections %} -
  • - {{ section }} -
  • - {% endfor %} -
-
-
-
- -
- {% endif %} - -
- - - {{ order|title }} - - - -
-
-
-
-

Order by:

-
- -
-
- -
-
-
- -
-
+ {% include 'archive/objects/archive_filter.html' %} +

{% if page_obj %} {{ page_obj.paginator.count }} RESULTS {% else %}NO RESULTS{% endif %} {% if q %} FOR "{{ q }}"{% endif %}

- + {{ order|title }} @@ -184,7 +42,11 @@

{% if page_obj|length > 0 %}
{% for object in page_obj %} - {% include 'archive/objects/article_list.html' with article=object %} + {% if video_section == False or video_section == None %} + {% include 'archive/objects/article_list.html' with article=object %} + {% else %} + {% include 'videos/stream_blocks/video.html' with video=object %} + {% endif %} {% endfor %}
diff --git a/archive/templates/archive/objects/archive_filter.html b/archive/templates/archive/objects/archive_filter.html new file mode 100644 index 000000000..1fee4288b --- /dev/null +++ b/archive/templates/archive/objects/archive_filter.html @@ -0,0 +1,206 @@ +{% load wagtailroutablepage_tags %} +{% load archive_tags %} +{% load humanize %} +{% load static %} +{% load wagtailcore_tags %} +{% load wagtailimages_tags %} +{% load wagtailuserbar %} +{% load ubyssey_ad_tags %} + +{% if sections or years %} +
+ +
+

Year

+
    + {% for y in years %} + {% if y == year %} +
  • + {{ y }} +
  • + {% endif %} + {% endfor %} +
  • + All years +
  • + {% for y in years %} + {% if y != year %} +
  • + {{ y }} +
  • + {% endif %} + {% endfor %} +
+
+ +
+

Section

+ + +

Magazines

+ + +

Spoofs

+ +
+ +
+{% endif %} + +
+
+ + {% if years %} +
+ + + {% if year %}{{ year }}{% else %}All years{% endif %} + + + +
+
+
+
+

Filter by year:

+ + + +
+
    +
  • + All +
  • + {% for y in years %} +
  • + {{ y }} +
  • + {% endfor %} +
+
+
+ +
+ {% endif %} + + {% if sections %} +
+ + + {% if section_slug %}{{ section_slug }}{% else %}All sections{% endif %} + + + +
+
+
+
+

Filter by section:

+ + + +
+ +
+
+
+ {% endif %} +
\ No newline at end of file diff --git a/archive/templates/archive/objects/gallery.html b/archive/templates/archive/objects/gallery.html new file mode 100644 index 000000000..c7734dc47 --- /dev/null +++ b/archive/templates/archive/objects/gallery.html @@ -0,0 +1,190 @@ +{% load archive_tags %} +{% load wagtailimages_tags %} + +
+ + + +
+ + {% if sections or years %} +
+ +
+

Year

+ +
+ +
+

Section

+ +
+ +
+ {% endif %} + +
+
+ + {% if years %} +
+ + + {% if year %}{{ year }}{% else %}All years{% endif %} + + + +
+
+
+
+

Filter by year:

+
+ +
+
+
    +
  • + All +
  • + {% for y in years %} +
  • + {{ y }} +
  • + {% endfor %} +
+
+
+
+ +
+ {% endif %} + + {% if sections %} +
+ + + {% comment %} NTS ugly placeholder {% endcomment %} + {% if section_slug %}{{ section_slug }}{% else %}All sections{% endif %} + + + +
+
+
+
+

Filter by section:

+
+ +
+
+
    +
  • + All +
  • + {% for section in sections %} +
  • + {{ section }} +
  • + {% endfor %} +
+
+
+
+ +
+ {% endif %} +
+ +
+

+ {% if page_obj %} {{ page_obj.paginator.count }} RESULTS {% else %}NO RESULTS{% endif %} + {% if q %} FOR "{{ q }}"{% endif %} +

+ +
+ + {% if page_obj|length > 0 %} + + +
+ {% if page_obj.has_previous %} + + + Previous + + {% endif %} + + + Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }} + + + {% if page_obj.has_next %} + + Next + + + {% endif %} +
+ + {% else %} +
No content found 😔
+ {% endif %} + +
+ +
+ +
\ No newline at end of file diff --git a/archive/templates/archive/objects/gallery_item.html b/archive/templates/archive/objects/gallery_item.html new file mode 100644 index 000000000..10f0403c3 --- /dev/null +++ b/archive/templates/archive/objects/gallery_item.html @@ -0,0 +1,18 @@ +{% load wagtailimages_tags %} + +{% image image height-300 as img%} +{% image image height-600 as big%} + + \ No newline at end of file diff --git a/archive/templatetags/archive_tags.py b/archive/templatetags/archive_tags.py index 299872258..f8b256c88 100644 --- a/archive/templatetags/archive_tags.py +++ b/archive/templatetags/archive_tags.py @@ -1,4 +1,7 @@ from django import template +from infinitefeed.views import getArticles +from django.template.defaultfilters import stringfilter + register = template.Library() @@ -17,8 +20,25 @@ def modify_query_string(context, field, value): dict_[field] = value return dict_.urlencode() +@register.filter +def query_string(request): + fullurl = request.get_full_path() + + fullurllst = fullurl.split("?") + if "?" in fullurl: + parameters = "?" + fullurllst[1] + else: + parameters = "" + + return parameters + +@register.filter +def replace(slug): + return str(slug).replace("-", " ") + @register.simple_tag(takes_context = True) def remove_field_from_query_string(context, field): dict_ = context['request'].GET.copy() dict_.pop(field, None) #Deletes the field if it exists, does nothing if it doesn't. Prevents KeyError - return dict_.urlencode() \ No newline at end of file + return dict_.urlencode() + diff --git a/archive/views.py b/archive/views.py deleted file mode 100644 index 91ea44a21..000000000 --- a/archive/views.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.shortcuts import render - -# Create your views here. diff --git a/article/blocks.py b/article/blocks.py new file mode 100644 index 000000000..2279daa05 --- /dev/null +++ b/article/blocks.py @@ -0,0 +1,118 @@ +from wagtail.core import blocks +from wagtail.core.blocks import field_block +from images import blocks as image_blocks +from videos import blocks as video_blocks +from wagtail.documents.blocks import DocumentChooserBlock + +class PullQuoteBlock(blocks.StructBlock): + content = blocks.CharBlock(required=True) + source = blocks.CharBlock(required=False) + audio = DocumentChooserBlock(required=False, help_text="optional, must be mp3 format") + + class Meta: + template = 'article/stream_blocks/quote.html', + icon = "openquote" + +class HeaderMenuBlock(blocks.StructBlock): + + list = blocks.ListBlock( + blocks.StructBlock( + [ + ('title', blocks.CharBlock()), + ('id',blocks.CharBlock(help_text="Intended to be shared with a header so that this button will send the user to the section of the page with said header")), + ('colour',blocks.CharBlock(default='0071c9')), + ], + label = "Page Link", + ) + ) + + class Meta: + template = 'article/stream_blocks/pageLink.html', + icon = "table" + +class HeaderLinkBlock(blocks.StructBlock): + title = blocks.CharBlock() + id = blocks.CharBlock(help_text="Intended to be shared with a page link button so that clicking the button will scroll the user to this header") + + class Meta: + template = 'article/stream_blocks/header.html', + icon = "title" + +class ChooseSideBlock(blocks.ChoiceBlock): + choices=[('left', 'Left'),('right', 'Right'),] + required=True + +class ChooseViewBlock(blocks.StructBlock): + view = blocks.ChoiceBlock( + choices=[('vs-side-by-side', 'Side By Side'),('vs-over-image', 'Text Over Image'),], + default=('vs-over-image', 'Text Over Image'), + required=True + ) + + class Meta: + template = "article/stream_blocks/ve_switch_view.html", + icon = "view" + +class GapBlock(blocks.StructBlock): + id = blocks.CharBlock(required=False) + height = blocks.IntegerBlock(required=True, default=0, min_value=0) + + class Meta: + template = 'article/stream_blocks/ve_gap.html', + icon = "arrows-up-down" + +class VisualEssayBlock(blocks.StructBlock): + view = ChooseViewBlock() + content = blocks.StreamBlock([ + ('rich_text', blocks.StructBlock( + [ + ('block', blocks.RichTextBlock( + label="Rich Text Block", + help_text = "Write your article contents here. See documentation: https://docs.wagtail.io/en/latest/editor_manual/new_pages/creating_body_content.html#rich-text-fields" + )), + ('side',ChooseSideBlock(default=("right", "Right"))), + ], icon = "doc-full" + )), + ('image', blocks.StructBlock( + [ + ('block', image_blocks.ImageBlock()), + ('side',ChooseSideBlock(default=("left", "Left"))), + ], icon = "image" + )), + ('video', blocks.StructBlock( + [ + ('block', video_blocks.OneOffVideoBlock( + label = "Credited/Captioned One-Off Video", + help_text = "Use this to credit or caption videos that will only be associated with this current article, rather than entered into our video library. You can also embed videos in a Rich Text Block." + )), + ('side',ChooseSideBlock(default=("left", "Left"))), + ], icon = "media" + )), + ('raw_html', blocks.StructBlock( + [ + ('block', blocks.RawHTMLBlock( + label = "Raw HTML Block", + help_text = "WARNING: DO NOT use this unless you really know what you're doing!" + )), + ('side',ChooseSideBlock(default=("left", "Left"))), + ], icon = "code" + )), + ('quote', blocks.StructBlock( + [ + ('block', PullQuoteBlock()), + ('side',ChooseSideBlock(default=("left", "Left"))), + ], icon = "openquote" + )), + ('header_link', blocks.StructBlock( + [ + ('block', HeaderLinkBlock()), + ('side',ChooseSideBlock(default=("right", "Right"))), + ], icon = "title" + )), + ('gap', GapBlock()), + ('switch_view', ChooseViewBlock()), + ]) + + class Meta: + template = 'article/stream_blocks/visual-essay.html', + icon = "form" \ No newline at end of file diff --git a/article/migrations/0019_alter_articlepage_content.py b/article/migrations/0019_alter_articlepage_content.py index b94c8c0b9..2f946dab0 100644 --- a/article/migrations/0019_alter_articlepage_content.py +++ b/article/migrations/0019_alter_articlepage_content.py @@ -21,4 +21,4 @@ class Migration(migrations.Migration): name='content', field=wagtail.core.fields.StreamField([('richtext', wagtail.core.blocks.RichTextBlock(help_text='Write your article contents here. See documentation: https://docs.wagtail.io/en/latest/editor_manual/new_pages/creating_body_content.html#rich-text-fields', label='Rich Text Block')), ('plaintext', wagtail.core.blocks.TextBlock(help_text='Warning: Rich Text Blocks preferred! Plain text primarily exists for importing old Dispatch text.', label='Plain Text Block')), ('dropcap', wagtail.core.blocks.TextBlock(help_text='DO NOT USE - Legacy block. Create a block where special dropcap styling with be applied to the first letter and the first letter only.\n\nThe contents of this block will be enclosed in a

...

element, allowing its targetting for styling.\n\nNo RichText allowed.', label='Dropcap Block', template='article/stream_blocks/dropcap.html')), ('video', wagtail.core.blocks.StructBlock([('video_embed', wagtail.embeds.blocks.EmbedBlock(blank=False, null=False)), ('title', wagtail.core.blocks.CharBlock(max_length=255, required=False)), ('caption', wagtail.core.blocks.CharBlock(max_length=255, required=False)), ('credit', wagtail.core.blocks.CharBlock(max_length=255, required=False))], help_text='Use this to credit or caption videos that will only be associated with this current article, rather than entered into our video library. You can also embed videos in a Rich Text Block.', label='Credited/Captioned One-Off Video')), ('image', wagtail.core.blocks.StructBlock([('image', wagtail.images.blocks.ImageChooserBlock(required=True)), ('style', wagtail.core.blocks.ChoiceBlock(choices=[('default', 'Default'), ('left', 'Left'), ('right', 'Right')])), ('width', wagtail.core.blocks.ChoiceBlock(choices=[('full', 'Full'), ('small', 'Small'), ('medium', 'Medium'), ('large', 'Large')])), ('caption', wagtail.core.blocks.CharBlock(max_length=255, required=False)), ('credit', wagtail.core.blocks.CharBlock(max_length=255, required=False))])), ('raw_html', wagtail.core.blocks.RawHTMLBlock(help_text="WARNING: DO NOT use this unless you really know what you're doing!", label='Raw HTML Block')), ('quote', wagtail.core.blocks.StructBlock([('content', wagtail.core.blocks.CharBlock(required=False)), ('source', wagtail.core.blocks.CharBlock(required=False))], icon='openquote', label='Pull Quote', template='article/stream_blocks/quote.html')), ('gallery', wagtail.snippets.blocks.SnippetChooserBlock(target_model=images.models.GallerySnippet, template='article/stream_blocks/gallery.html'))], blank=True, null=True), ), - ] + ] \ No newline at end of file diff --git a/article/migrations/0020_alter_articlepage_content.py b/article/migrations/0020_alter_articlepage_content.py new file mode 100644 index 000000000..880f080e2 --- /dev/null +++ b/article/migrations/0020_alter_articlepage_content.py @@ -0,0 +1,25 @@ +# Generated by Django 3.2.11 on 2023-08-04 17:18 + +from django.db import migrations +import images.models +import wagtail.core.blocks +import wagtail.core.fields +import wagtail.documents.blocks +import wagtail.embeds.blocks +import wagtail.images.blocks +import wagtail.snippets.blocks + + +class Migration(migrations.Migration): + + dependencies = [ + ('article', '0019_alter_articlepage_content'), + ] + + operations = [ + migrations.AlterField( + model_name='articlepage', + name='content', + field=wagtail.core.fields.StreamField([('richtext', wagtail.core.blocks.RichTextBlock(help_text='Write your article contents here. See documentation: https://docs.wagtail.io/en/latest/editor_manual/new_pages/creating_body_content.html#rich-text-fields', label='Rich Text Block')), ('plaintext', wagtail.core.blocks.TextBlock(help_text='Warning: Rich Text Blocks preferred! Plain text primarily exists for importing old Dispatch text.', label='Plain Text Block')), ('dropcap', wagtail.core.blocks.TextBlock(help_text='DO NOT USE - Legacy block. Create a block where special dropcap styling with be applied to the first letter and the first letter only.\n\nThe contents of this block will be enclosed in a

...

element, allowing its targetting for styling.\n\nNo RichText allowed.', label='Dropcap Block', template='article/stream_blocks/dropcap.html')), ('video', wagtail.core.blocks.StructBlock([('video_embed', wagtail.embeds.blocks.EmbedBlock(blank=False, null=False)), ('title', wagtail.core.blocks.CharBlock(max_length=255, required=False)), ('caption', wagtail.core.blocks.CharBlock(max_length=255, required=False)), ('credit', wagtail.core.blocks.CharBlock(max_length=255, required=False))], help_text='Use this to credit or caption videos that will only be associated with this current article, rather than entered into our video library. You can also embed videos in a Rich Text Block.', label='Credited/Captioned One-Off Video')), ('image', wagtail.core.blocks.StructBlock([('image', wagtail.images.blocks.ImageChooserBlock(required=True)), ('style', wagtail.core.blocks.ChoiceBlock(choices=[('default', 'Default'), ('left', 'Left'), ('right', 'Right')])), ('width', wagtail.core.blocks.ChoiceBlock(choices=[('full', 'Full'), ('small', 'Small'), ('medium', 'Medium'), ('large', 'Large')])), ('caption', wagtail.core.blocks.CharBlock(max_length=255, required=False)), ('credit', wagtail.core.blocks.CharBlock(max_length=255, required=False))])), ('raw_html', wagtail.core.blocks.RawHTMLBlock(help_text="WARNING: DO NOT use this unless you really know what you're doing!", label='Raw HTML Block')), ('quote', wagtail.core.blocks.StructBlock([('content', wagtail.core.blocks.CharBlock(required=True)), ('source', wagtail.core.blocks.CharBlock(required=False)), ('audio', wagtail.documents.blocks.DocumentChooserBlock(help_text='optional, must be mp3 format', required=False))])), ('gallery', wagtail.snippets.blocks.SnippetChooserBlock(target_model=images.models.GallerySnippet, template='article/stream_blocks/gallery.html')), ('header_link', wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.CharBlock()), ('id', wagtail.core.blocks.CharBlock(help_text='Intended to be shared with a page link button so that clicking the button will scroll the user to this header'))])), ('header_menu', wagtail.core.blocks.StructBlock([('list', wagtail.core.blocks.ListBlock(wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.CharBlock()), ('id', wagtail.core.blocks.CharBlock(help_text='Intended to be shared with a header so that this button will send the user to the section of the page with said header')), ('colour', wagtail.core.blocks.CharBlock(default='0071c9'))], label='Page Link')))])), ('visual_essay', wagtail.core.blocks.StructBlock([('view', wagtail.core.blocks.StructBlock([('view', wagtail.core.blocks.ChoiceBlock(choices=[('vs-side-by-side', 'Side By Side'), ('vs-over-image', 'Text Over Image')]))])), ('content', wagtail.core.blocks.StreamBlock([('rich_text', wagtail.core.blocks.StructBlock([('block', wagtail.core.blocks.RichTextBlock(help_text='Write your article contents here. See documentation: https://docs.wagtail.io/en/latest/editor_manual/new_pages/creating_body_content.html#rich-text-fields', label='Rich Text Block')), ('side', wagtail.core.blocks.ChoiceBlock(choices=[('left', 'Left'), ('right', 'Right')]))], icon='doc-full')), ('image', wagtail.core.blocks.StructBlock([('block', wagtail.core.blocks.StructBlock([('image', wagtail.images.blocks.ImageChooserBlock(required=True)), ('style', wagtail.core.blocks.ChoiceBlock(choices=[('default', 'Default'), ('left', 'Left'), ('right', 'Right')])), ('width', wagtail.core.blocks.ChoiceBlock(choices=[('full', 'Full'), ('small', 'Small'), ('medium', 'Medium'), ('large', 'Large')])), ('caption', wagtail.core.blocks.CharBlock(max_length=255, required=False)), ('credit', wagtail.core.blocks.CharBlock(max_length=255, required=False))])), ('side', wagtail.core.blocks.ChoiceBlock(choices=[('left', 'Left'), ('right', 'Right')]))], icon='image')), ('video', wagtail.core.blocks.StructBlock([('block', wagtail.core.blocks.StructBlock([('video_embed', wagtail.embeds.blocks.EmbedBlock(blank=False, null=False)), ('title', wagtail.core.blocks.CharBlock(max_length=255, required=False)), ('caption', wagtail.core.blocks.CharBlock(max_length=255, required=False)), ('credit', wagtail.core.blocks.CharBlock(max_length=255, required=False))], help_text='Use this to credit or caption videos that will only be associated with this current article, rather than entered into our video library. You can also embed videos in a Rich Text Block.', label='Credited/Captioned One-Off Video')), ('side', wagtail.core.blocks.ChoiceBlock(choices=[('left', 'Left'), ('right', 'Right')]))], icon='media')), ('raw_html', wagtail.core.blocks.StructBlock([('block', wagtail.core.blocks.RawHTMLBlock(help_text="WARNING: DO NOT use this unless you really know what you're doing!", label='Raw HTML Block')), ('side', wagtail.core.blocks.ChoiceBlock(choices=[('left', 'Left'), ('right', 'Right')]))], icon='code')), ('quote', wagtail.core.blocks.StructBlock([('block', wagtail.core.blocks.StructBlock([('content', wagtail.core.blocks.CharBlock(required=True)), ('source', wagtail.core.blocks.CharBlock(required=False)), ('audio', wagtail.documents.blocks.DocumentChooserBlock(help_text='optional, must be mp3 format', required=False))])), ('side', wagtail.core.blocks.ChoiceBlock(choices=[('left', 'Left'), ('right', 'Right')]))], icon='openquote')), ('header_link', wagtail.core.blocks.StructBlock([('block', wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.CharBlock()), ('id', wagtail.core.blocks.CharBlock(help_text='Intended to be shared with a page link button so that clicking the button will scroll the user to this header'))])), ('side', wagtail.core.blocks.ChoiceBlock(choices=[('left', 'Left'), ('right', 'Right')]))], icon='title')), ('gap', wagtail.core.blocks.StructBlock([('id', wagtail.core.blocks.CharBlock(required=False)), ('height', wagtail.core.blocks.IntegerBlock(default=0, min_value=0, required=True))])), ('switch_view', wagtail.core.blocks.StructBlock([('view', wagtail.core.blocks.ChoiceBlock(choices=[('vs-side-by-side', 'Side By Side'), ('vs-over-image', 'Text Over Image')]))]))]))]))], blank=True, null=True), + ), + ] diff --git a/article/models.py b/article/models.py index 36487601c..4fcb5e67b 100644 --- a/article/models.py +++ b/article/models.py @@ -12,6 +12,7 @@ from django.db import models from django.db.models import fields from django.db.models.fields import CharField +from django.shortcuts import render from django.db.models.query import QuerySet from django.forms.widgets import Select, Widget from django.utils import timezone @@ -30,6 +31,8 @@ from taggit.models import TaggedItemBase from videos import blocks as video_blocks +from wagtail.contrib.routable_page.models import RoutablePageMixin, route +from article import blocks as article_blocks from wagtail.admin.edit_handlers import ( # Panels @@ -49,6 +52,7 @@ from wagtail.core.fields import StreamField from wagtail.core.models import Page, PageManager, Orderable from wagtail.documents.models import Document +from wagtail.documents.blocks import DocumentChooserBlock from wagtail.documents.edit_handlers import DocumentChooserPanel from wagtail.images.edit_handlers import ImageChooserPanel from wagtail.search import index @@ -61,6 +65,9 @@ from wagtailmodelchooser.edit_handlers import ModelChooserPanel +from wagtail_color_panel.fields import ColorField +from wagtail_color_panel.edit_handlers import NativeColorPanel + UBYSSEY_FOUNDING_DATE = datetime.date(1918,10,17) @@ -145,6 +152,7 @@ class Meta: verbose_name = "Series of Articles" verbose_name_plural = "Series of Articles" + #-----Orderable models----- class ArticleAuthorsOrderable(Orderable): """ @@ -232,6 +240,7 @@ class ConnectedArticleOrderable(Orderable): FieldPanel('article_description') ] + class SeriesOrderable(Orderable): """ Represents a single article in a series of articles. Associated with ArticleSeriesSnippet @@ -422,19 +431,31 @@ class ArticlePageManager(PageManager): def from_section(self, section_slug='', section_root=None) -> QuerySet: from .models import ArticlePage from section.models import SectionPage + if section_slug: try: - new_section_root = SectionPage.objects.get(slug=section_slug) - except Page.DoesNotExist: - new_section_root = None - if new_section_root: - section_root = new_section_root + section_root = SectionPage.objects.get(slug=section_slug) + articles = self.live().public().descendant_of(section_root).exact_type(ArticlePage) + except SectionPage.DoesNotExist: + articles = SectionPage.objects.none() - return self.live().public().descendant_of(section_root).exact_type(ArticlePage) #.order_by('-last_modified_at') + return articles + + def from_magazine_special_section(self, section_slug='', section_root=None) -> QuerySet: + from .models import ArticlePage + from specialfeaturelanding.models import SpecialLandingPage + if section_slug: + try: + section_root = SpecialLandingPage.objects.get(category__slug=section_slug) + articles = self.live().public().descendant_of(section_root).exact_type(ArticlePage) + except SpecialLandingPage.DoesNotExist: + articles = SpecialLandingPage.objects.none() + return articles + #-----Page models----- -class ArticlePage(SectionablePage, UbysseyMenuMixin): +class ArticlePage(RoutablePageMixin, SectionablePage, UbysseyMenuMixin): #-----Django/Wagtail settings etc----- objects = ArticlePageManager() @@ -455,7 +476,7 @@ class ArticlePage(SectionablePage, UbysseyMenuMixin): label="Rich Text Block", help_text = "Write your article contents here. See documentation: https://docs.wagtail.io/en/latest/editor_manual/new_pages/creating_body_content.html#rich-text-fields" )), - ('plaintext',blocks.TextBlock( + ('plaintext', blocks.TextBlock( label="Plain Text Block", help_text = "Warning: Rich Text Blocks preferred! Plain text primarily exists for importing old Dispatch text." )), @@ -474,19 +495,14 @@ class ArticlePage(SectionablePage, UbysseyMenuMixin): label = "Raw HTML Block", help_text = "WARNING: DO NOT use this unless you really know what you're doing!" )), - ('quote', blocks.StructBlock( - [ - ('content',blocks.CharBlock(required=False)), - ('source',blocks.CharBlock(required=False)), - ], - label = "Pull Quote", - template = 'article/stream_blocks/quote.html', - icon = "openquote", - )), + ('quote', article_blocks.PullQuoteBlock()), ('gallery', SnippetChooserBlock( target_model = GallerySnippet, template = 'article/stream_blocks/gallery.html', )), + ('header_link', article_blocks.HeaderLinkBlock()), + ('header_menu', article_blocks.HeaderMenuBlock()), + ('visual_essay', article_blocks.VisualEssayBlock()), ], null=True, blank=True, @@ -571,6 +587,7 @@ class ArticlePage(SectionablePage, UbysseyMenuMixin): on_delete=models.SET_NULL, ) + #-----Hidden stuff: editors don't get to modify these, but they may be programatically changed----- legacy_template = models.CharField( @@ -683,6 +700,8 @@ def get_template(self, request): return "article/article_page_guide_2022.html" elif self.layout == 'magazine-2023': return "article/article_page_magazine_2023.html" + elif self.layout == 'visual-essay': + return "article/article_page_visual_essay.html" return "article/article_page.html" @@ -797,6 +816,7 @@ def get_template(self, request): ('guide-2020', 'Guide (2020 style - currently broken, last checked 2022/09)'), ('guide-2022', 'Guide (2022 style)'), ('magazine-2023', 'Magazine (2023 style)'), + ('visual-essay', 'Visual Essay'), ], ), ), @@ -850,7 +870,7 @@ def get_template(self, request): ], heading="Connected or Related Article Links (Non-Series)", classname="collapsible collapsed", - ), # Connected or Related Article Links (Non-Series) + ), # Connected or Related Article Links (Non-Series) ] # fw_article_panels customization_panels = [ HelpPanel( @@ -930,7 +950,7 @@ def get_context(self, request, *args, **kwargs): context['prev'] = self.get_prev_sibling() context['next'] = self.get_next_sibling() - + if self.current_section == 'guide': # Desired behaviour for guide articles is to always have two adjacent articles. Therefore we create an "infinite loop" if not context['prev']: @@ -943,6 +963,8 @@ def get_context(self, request, *args, **kwargs): if context['next']: context['next'] = context['next'].specific + context["suggested"] = self.get_suggested() + return context @@ -1027,6 +1049,44 @@ def get_authors_with_roles(self) -> str: return authors_with_roles authors_with_roles = property(fget=get_authors_with_roles) + def get_category_articles(self, order='-explicit_published_at') -> QuerySet: + """ + Returns a list of articles within the Article's category + """ + category_articles = ArticlePage.objects.live().public().filter(category=self.category).not_page(self).order_by(order) + + return category_articles + + def get_section_articles(self, order='-explicit_published_at') -> QuerySet: + """ + Returns a list of articles within the Article's section + """ + + section_articles = ArticlePage.objects.live().public().descendant_of(self.get_parent()).not_page(self).order_by(order) + + return section_articles + + def get_suggested(self, number_suggested=6): + """ + Defines the title and articles in the suggested box + """ + + category_articles = self.get_category_articles() + section_articles = self.get_section_articles() + + if self.category == None or len(category_articles) == 0: + suggested = {} + suggested['title'] = "From " + self.get_parent().title + suggested['articles'] = section_articles[:number_suggested] + elif len(section_articles) > 0: + suggested = {} + suggested['title'] = "From " + self.get_parent().title + " - " + self.category.title + suggested['articles'] = category_articles[:number_suggested] + else: + suggested = False + + return suggested + @property def published_at(self): if self.explicit_published_at: diff --git a/article/templates/article/article_like_special_page.html b/article/templates/article/article_like_special_page.html index 6fd8da56b..5ba329a10 100644 --- a/article/templates/article/article_like_special_page.html +++ b/article/templates/article/article_like_special_page.html @@ -16,19 +16,18 @@

{{ page.title }}

- {% include "article/objects/author_bio.html" %}
{% for block in self.content %} - {% include_block block %} + {% include_block block with id=block.id %} {% endfor %}
diff --git a/article/templates/article/article_page.html b/article/templates/article/article_page.html index a4000a1cd..baeee7d83 100644 --- a/article/templates/article/article_page.html +++ b/article/templates/article/article_page.html @@ -14,18 +14,18 @@ {% endblock %} {% block header %} - {% include 'navigation/headers/topbar.html' %} {% include 'navigation/headers/mobile.html' %} + {% block banner_ad %} + + {% for orderable in settings.ads.AdTagSettings.article_header_placements.all %} + {% gpt_placement_tag orderable.ad_slot %} + {% endfor %} + {% endblock %} + {% include 'navigation/headers/topbar.html' %} {% endblock %} {% block content %}
- {% block banner_ad %} - - {% for orderable in settings.ads.AdTagSettings.article_header_placements.all %} - {% gpt_placement_tag orderable.ad_slot %} - {% endfor %} - {% endblock %}
{% for block in self.content %} - {% include_block block %} + {% include_block block with id=block.id %} {% endfor %}

diff --git a/article/templates/article/article_page_guide_2022.html b/article/templates/article/article_page_guide_2022.html index 6c89a20e8..0ac7011a5 100644 --- a/article/templates/article/article_page_guide_2022.html +++ b/article/templates/article/article_page_guide_2022.html @@ -72,12 +72,15 @@ {{ featured_image_object.credit }} {% endif %}
{% endcomment %} +
{% comment %} {% endcomment %} {% for block in self.content %} - {% include_block block %} + {% include_block block with id=block.id %} {% endfor %}
diff --git a/article/templates/article/article_page_magazine_2023.html b/article/templates/article/article_page_magazine_2023.html index 6dbdab565..8d4ac3dcf 100644 --- a/article/templates/article/article_page_magazine_2023.html +++ b/article/templates/article/article_page_magazine_2023.html @@ -104,7 +104,7 @@ {% include 'objects/advertisement.html' with size='box' name='Box_A' id=3 article=article.id %}
{% endcomment %} {% for block in self.content %} - {% include_block block %} + {% include_block block with id=block.id %} {% endfor %}
diff --git a/article/templates/article/article_page_visual_essay.html b/article/templates/article/article_page_visual_essay.html new file mode 100644 index 000000000..7c6375e1a --- /dev/null +++ b/article/templates/article/article_page_visual_essay.html @@ -0,0 +1,120 @@ +{% extends 'article/article_page.html' %} +{% load static %} +{% load dispatch_tags %} +{% load wagtailimages_tags %} + +{% block head_scripts %} + + + +{% endblock %} + +{% block header %} + {% include 'navigation/headers/visual-essay-header.html' %} +{% endblock %} + + +{% block specific_article_class %} + c-article--fw-story +{% endblock %} +{% block banner %} +
+ {% if self.show_timeline%} +
+ {% endif %} + {% if self.header_layout == 'right-image' %} +
+
+ {% include 'article/objects/fw_article_headline_container.html' %} +
+
+ +
+
+ {% elif self.header_layout == 'top-image' %} +
+ +
+ {% include 'article/objects/fw_article_headline_container.html' %} +
+
+ {% elif self.header_layout == 'banner-image' %} + {% with self.featured_media.first as featured_image_object %} + + + {% endwith %} + {% endif %} +
+{% endblock %} +{% block right_column_class %} + fw-story u-container-max +{% endblock %} + +{% if self.header_layout == 'banner-image' %} + {% block banner_ad %}{% endblock %} +{% endif %} + +{% block pre-content %} +{% endblock %} + +{% block right-column %} +
+ +
+{% endblock %} + +{% block suggested_articles %} +{% if suggested %} + {% include 'article/objects/suggested_articles_fw.html' with title=suggested.title articles=suggested.articles %} +{% endif %} +{% endblock %} + +{% block scripts %} + {% if self.show_timeline%} + + {% endif %} + {% endblock %} \ No newline at end of file diff --git a/article/templates/article/objects/author_pinned.html b/article/templates/article/objects/author_pinned.html new file mode 100644 index 000000000..c6c7e3670 --- /dev/null +++ b/article/templates/article/objects/author_pinned.html @@ -0,0 +1,31 @@ +{% load dispatch_filters %} +{% load wagtailcore_tags %} +{% load wagtailimages_tags %} +{% load humanize %} + + \ No newline at end of file diff --git a/article/templates/article/objects/blog_column.html b/article/templates/article/objects/blog_column.html index 59d694d8c..c8ddbf9d5 100644 --- a/article/templates/article/objects/blog_column.html +++ b/article/templates/article/objects/blog_column.html @@ -1,3 +1,5 @@ +{% load articletags %} + {% comment %} This template is for the columns for the blog section on the home page under the digital prints section {% endcomment %} @@ -8,7 +10,7 @@ {% load wagtailcore_tags %} {% load wagtailimages_tags %} {% load video_filters %} -
+
+ + + \ No newline at end of file diff --git a/section/templates/section/objects/section_featured.html b/section/templates/section/objects/section_featured.html index 683e8e625..ebe357801 100644 --- a/section/templates/section/objects/section_featured.html +++ b/section/templates/section/objects/section_featured.html @@ -2,16 +2,22 @@ Arguments: featured_articles a list of article pages Used in both section pages and homepage {% endcomment %} -{% for article in featured_articles %} - {% if forloop.counter == 1 %} - {% include 'article/objects/featured.html' with article=article %} - {% else %} - {% if forloop.counter == 2 %} - diff --git a/section/templates/section/section_page.html b/section/templates/section/section_page.html index 6532edbe6..d0bbc4fe9 100644 --- a/section/templates/section/section_page.html +++ b/section/templates/section/section_page.html @@ -5,6 +5,7 @@ {% load wagtailimages_tags %} {% load wagtailuserbar %} {% load ubyssey_ad_tags %} +{% load ubyssey_ad_filters %} {% block head_scripts %} @@ -14,67 +15,92 @@ {% endblock %} {% block header %} - {% include 'navigation/headers/topbar.html' %} {% include 'navigation/headers/mobile.html' %} + + {% for orderable in settings.ads.AdTagSettings.section_header_placements.all %} + {% gpt_placement_tag orderable.ad_slot %} + {% endfor %} + {% include 'navigation/headers/topbar.html' %} {% endblock %} {% block content %}
- - {% for orderable in settings.ads.AdTagSettings.section_header_placements.all %} - {% gpt_placement_tag orderable.ad_slot %} - {% endfor %} -
-
-

{{ self.title }}

-
- {% comment %} CATEGORY MENU {% endcomment %} - {% if self.category_menu.count %} -
- + +
+ + {% if banner %} + {% image banner width-700 as banner_image %} + + {%else%} +
+

{{title}}

{% endif %} - {% comment %} - featured_articles is added to context by SectionPage - by default it containes the most recent 4 articles - {% endcomment %} -
{% endblock %} diff --git a/specialfeaturelanding/migrations/0020_speciallandingpage_category.py b/specialfeaturelanding/migrations/0020_speciallandingpage_category.py new file mode 100644 index 000000000..88d60b67b --- /dev/null +++ b/specialfeaturelanding/migrations/0020_speciallandingpage_category.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.11 on 2023-07-12 03:07 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('section', '0003_sectionpage_use_parent_colour'), + ('specialfeaturelanding', '0019_alter_speciallandingpage_editorial_stream'), + ] + + operations = [ + migrations.AddField( + model_name='speciallandingpage', + name='category', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='section.categorysnippet'), + ), + ] diff --git a/specialfeaturelanding/models.py b/specialfeaturelanding/models.py index 3bb5c6a6d..a85a6ba35 100644 --- a/specialfeaturelanding/models.py +++ b/specialfeaturelanding/models.py @@ -24,6 +24,7 @@ from wagtail.core.models import Page, Orderable from wagtail.core.fields import StreamField from wagtail.images.blocks import ImageChooserBlock +from wagtail.snippets.edit_handlers import SnippetChooserPanel from wagtailmenus.models import FlatMenu from wagtailmodelchooser.edit_handlers import ModelChooserPanel @@ -39,6 +40,14 @@ class SpecialLandingPage(SectionablePage, UbysseyMenuMixin): # template = "specialfeaturelanding/landing_page_guide_2022_style.html" use_default_template = models.BooleanField(default=True) + + category = models.ForeignKey( + "section.CategorySnippet", + blank=True, + null=True, + on_delete=models.SET_NULL, + ) + layout = models.CharField( null=False, blank=False, @@ -164,6 +173,13 @@ def get_template(self, request): ], heading="Feature Credits", classname="collapsible", + ), + MultiFieldPanel( + [ + SnippetChooserPanel("category"), + ], + heading="Categories", + classname="collapsible", ), MultiFieldPanel( [ diff --git a/sporttourney/admin.py b/sporttourney/admin.py deleted file mode 100644 index 8c38f3f3d..000000000 --- a/sporttourney/admin.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.contrib import admin - -# Register your models here. diff --git a/sporttourney/apps.py b/sporttourney/apps.py deleted file mode 100644 index bf46119ad..000000000 --- a/sporttourney/apps.py +++ /dev/null @@ -1,5 +0,0 @@ -from django.apps import AppConfig - - -class SporttourneyConfig(AppConfig): - name = 'sporttourney' diff --git a/sporttourney/migrations/0001_initial.py b/sporttourney/migrations/0001_initial.py deleted file mode 100644 index bd0362d77..000000000 --- a/sporttourney/migrations/0001_initial.py +++ /dev/null @@ -1,54 +0,0 @@ -# Generated by Django 3.1.12 on 2021-07-09 10:44 - -from django.db import migrations, models -import django.db.models.deletion -import django_extensions.db.fields -import modelcluster.fields -import ubyssey.validators - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ('wagtailimages', '0023_add_choose_permissions'), - ] - - operations = [ - migrations.CreateModel( - name='SportsTournamentSnippet', - fields=[ - ('tournament_name', models.TextField(default='tournament')), - ('slug', django_extensions.db.fields.AutoSlugField(blank=True, editable=False, populate_from='tournament_name', primary_key=True, serialize=False, unique=True)), - ], - options={ - 'verbose_name': 'Sports Tournament', - 'verbose_name_plural': 'Sports Tournaments', - }, - ), - migrations.CreateModel( - name='SportsTeamOrderable', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('sort_order', models.IntegerField(blank=True, editable=False, null=True)), - ('team_name', models.TextField(default='')), - ('team_color', models.CharField(default='#FF0000', max_length=7, validators=[ubyssey.validators.validate_colour_hex])), - ('team_logo', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.image')), - ('tournament', modelcluster.fields.ParentalKey(default='', on_delete=django.db.models.deletion.CASCADE, related_name='tournament_team', to='sporttourney.sportstournamentsnippet')), - ], - options={ - 'ordering': ['sort_order'], - 'abstract': False, - }, - ), - migrations.CreateModel( - name='SportsPlayerProfile', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('full_name', models.CharField(max_length=255)), - ('profile_text', models.CharField(blank=True, max_length=1500)), - ('player_photo', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.image')), - ], - ), - ] diff --git a/sporttourney/migrations/__init__.py b/sporttourney/migrations/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/sporttourney/models.py b/sporttourney/models.py deleted file mode 100644 index 03ed3c394..000000000 --- a/sporttourney/models.py +++ /dev/null @@ -1,130 +0,0 @@ -from django.db import models -from django_extensions.db.fields import AutoSlugField -from modelcluster.fields import ParentalKey -from modelcluster.models import ClusterableModel -from ubyssey.validators import validate_colour_hex -from wagtail.admin.edit_handlers import FieldPanel, InlinePanel, MultiFieldPanel -from wagtail.core.models import Orderable -from wagtail.images.edit_handlers import ImageChooserPanel -from wagtail.snippets.models import register_snippet - -@register_snippet -class SportsTournamentSnippet(ClusterableModel): - tournament_name = models.TextField( - blank=False, - null=False, - default='tournament' - ) - slug = AutoSlugField( - populate_from="tournament_name", - editable=True, - primary_key=True, - unique=True, - blank=False, - null=False, - ) - - panels = [ - MultiFieldPanel( - [ - FieldPanel('tournament_name'), - FieldPanel('slug'), - ], - heading="Tournament Information" - ), - MultiFieldPanel( - [ - InlinePanel("tournament_team", min_num=1, max_num=20, label="Tournament Team"), - ], - heading="Tournament Teams" - ), - ] - - def __str__(self): - return self.tournament_name - - class Meta: - verbose_name = "Sports Tournament" - verbose_name_plural = "Sports Tournaments" - -class SportsTeamOrderable(Orderable): - """ - Based on individual team nodes - """ - team_name = models.TextField( - blank=False, - null=False, - default='', - ) - ## we probably need to install https://pypi.org/project/django-colorfield/ for a more robust version of this - team_color = models.CharField( - blank=False, - null=False, - max_length=7, - default='#FF0000', - validators=[ - validate_colour_hex, - ], - ) - team_logo = models.ForeignKey( - "wagtailimages.Image", - on_delete=models.SET_NULL, - null=True, - blank=True, - related_name="+", - ) - tournament = ParentalKey( - "SportsTournamentSnippet", - default='', - related_name="tournament_team", - ) - - # TODO: representation of location and of stats - panels = [ - MultiFieldPanel( - [ - FieldPanel('team_name'), - FieldPanel('team_color'), - ImageChooserPanel('team_logo'), - ], - heading="Team Information" - ), - # MultiFieldPanel( - # [ - # InlinePanel("player_to_watch", max_num=1, label="Player to watch"), - # ], - # heading="Player to Watch" - # ), - ] - -class SportsPlayerProfile(models.Model): - full_name = models.CharField( - max_length=255, - blank=False, - null=False, - ) - profile_text = models.CharField( - max_length=1500, - blank=True, - null=False, - ) - player_photo = models.ForeignKey( - "wagtailimages.Image", - on_delete=models.SET_NULL, - null=True, - blank=True, - related_name="+", - ) - # players_team = models.ForeignKey( - # "SportsTeamOrderable", - # related_name="player_to_watch", - # on_delete=models.CASCADE, - # ) - panels = [ - FieldPanel('full_name'), - FieldPanel('profile_text'), - ImageChooserPanel('player_photo'), - ] - - def __str__(self): - return self.full_name diff --git a/sporttourney/tests.py b/sporttourney/tests.py deleted file mode 100644 index 7ce503c2d..000000000 --- a/sporttourney/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/sporttourney/views.py b/sporttourney/views.py deleted file mode 100644 index 91ea44a21..000000000 --- a/sporttourney/views.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.shortcuts import render - -# Create your views here. diff --git a/ubyssey/static_src/src/fonts/Mercury Text G2 Bold.otf b/ubyssey/static_src/src/fonts/Mercury Text G2 Bold.otf new file mode 100644 index 000000000..e8458efbf Binary files /dev/null and b/ubyssey/static_src/src/fonts/Mercury Text G2 Bold.otf differ diff --git a/ubyssey/static_src/src/js/advertise.js b/ubyssey/static_src/src/js/advertise.js deleted file mode 100644 index f887e9cc4..000000000 --- a/ubyssey/static_src/src/js/advertise.js +++ /dev/null @@ -1,74 +0,0 @@ -// Scripts for the /advertise/ page -// Bundled as 'a.js' to prevent AdBlocker blocking. - -$(function() { - // Navigation links smooth scrolling - $('a[href*=\\#]').on('click', function(e) { - $('html,body').animate({ scrollTop: $(this.hash).offset().top }, 500); - }); - - $('.o-placements--web .o-placements__placement').click(function() { - $('.o-placements--web .o-placements__placement').removeClass('o-placements__placement--active'); - $(this).addClass('o-placements__placement--active'); - $('.o-placements--web .o-placements__demo__inner img').css('top', $(this).data('offset')); - - }); - - $('.o-placements--print .o-placements__placement--demo').click(function() { - $('.o-placements--print .o-placements__placement').removeClass('o-placements__placement--active'); - $(this).addClass('o-placements__placement--active'); - $('.o-placements--print .o-placements__demo').attr('data-demo', $(this).data('demo')); - }); - - $('.o-placements--guide .o-placements__placement--demo').click(function() { - $('.o-placements--guide .o-placements__placement').removeClass('o-placements__placement--active'); - $(this).addClass('o-placements__placement--active'); - $('.o-placements--guide .o-placements__demo').attr('data-demo', $(this).data('demo')); - }); - - // Open modal - $('.c-production-schedule__view').click(function() { - $('.c-production-schedule__modal').show(); - }) - - // Close modal - $('.c-production-schedule__modal__close').click(function() { - $('.c-production-schedule__modal').hide(); - }); - - $('.c-production-schedule__modal').click(function() { - $('.c-production-schedule__modal').hide(); - }); - - $('.c-production-schedule__modal img').click(function(e) { - e.stopPropagation() - }); - - $('.c-web-slider__point > div').click(function(e) { - var offset = $(this).offset().left - $('.c-web-slider').offset().left; - var content = $(this).data('content'); - var cost = $(this).data('cost'); - slideTo(offset, content, cost); - }); - - function slideTo(offset, content, cost) { - var tooltipWidth = $('.c-web-slider__tooltip').outerWidth(); - var maxOffset = $('.c-web-slider').width() - tooltipWidth; - - offset = Math.max(0, offset - 25); - - $('.c-web-slider__tooltip').css('margin-left', Math.min(offset, maxOffset)); - - var offsetPercent = 8; - - if (offset > maxOffset) { - var offsetDif = offset - maxOffset + 35; - offsetPercent = offsetDif / tooltipWidth * 100; - offsetPercent = Math.min(92, offsetPercent); - } - - $('.c-web-slider__tooltip__arrow').css('left', offsetPercent + '%'); - $('.c-web-slider__tooltip__content').html(content); - $('.c-web-slider__tooltip__cost').text(cost); - } -}); diff --git a/ubyssey/static_src/src/js/advertise_new.js b/ubyssey/static_src/src/js/advertise_new.js index ca462c64d..68e6c752b 100644 --- a/ubyssey/static_src/src/js/advertise_new.js +++ b/ubyssey/static_src/src/js/advertise_new.js @@ -1,5 +1,5 @@ // Scripts for the /advertise/ page -// Bundled as 'a.js' to prevent AdBlocker blocking. +// Bundled as 'a_new.js' to prevent AdBlocker blocking. $(function() { // Navigation links smooth scrolling diff --git a/ubyssey/static_src/src/js/article.jsx b/ubyssey/static_src/src/js/article.jsx index 567d0143b..787ab0748 100644 --- a/ubyssey/static_src/src/js/article.jsx +++ b/ubyssey/static_src/src/js/article.jsx @@ -1,13 +1,9 @@ import React from 'react' import ReactDOM from 'react-dom' -import './modules/Youtube' -import { ArticlesSuggested } from './components/Article' -import { Poll } from './components/Poll' import Search from './components/Search.jsx'; import { AdblockSplash, CookieDisclaimer } from './components/Cookies' import { Galleries } from './components/Gallery' -import Timeline from './components/Timeline.jsx' -import Episode from './components/Podcast/Episode.jsx' + window.articleHeader = false; @@ -16,12 +12,7 @@ const BOX_HEIGHT = 274 const SKYSCRAPER_HEIGHT = 624 $(function () { - $('.c-widget-poll').each(function () { - ReactDOM.render( - , - $(this).get(0) - ) - }) + ReactDOM.render( , document.getElementById('adblock-splash') @@ -30,28 +21,8 @@ $(function () { , document.getElementById('cookie-disclaimer') ) - $('.c-timeline').each(function () { - ReactDOM.render( - , - $(this).get(0) - ) - }) - - $('.c-podcast-episode').each(function () { - ReactDOM.render( - , - $(this).get(0) - ) - }) + + }); if ($('main.article').length) { @@ -181,18 +152,6 @@ if ($('main.article').length) { articleAds() - if (document.getElementById('article-list') !== null) { - const articleList = ReactDOM.render( - , - document.getElementById('article-list') - ); - } - const gatherImages = (gallery) => { var selector, trigger; diff --git a/ubyssey/static_src/src/js/components/Article/ArticlesSuggested.jsx b/ubyssey/static_src/src/js/components/Article/ArticlesSuggested.jsx deleted file mode 100644 index 7597af99f..000000000 --- a/ubyssey/static_src/src/js/components/Article/ArticlesSuggested.jsx +++ /dev/null @@ -1,69 +0,0 @@ - -import React from 'react' -import { ArticlePreview } from './' -import DispatchAPI from '../../api/dispatch' - -class ArticlesSuggested extends React.Component{ - constructor(props) { - super(props) - - var articles = props.articles - articles.unshift(props.currentArticle.id) - - this.articlesTable = {}; - this.articlesTable[this.props.currentArticle.id] = 0; - - this.state = { - articles: [props.currentArticle], - loading: false - } - } - - componentDidMount() { - for (let id of this.props.articles) { - this.getArticle(id) - } - } - - getArticle(id) { - DispatchAPI.articles.rendered(id) - .then((response) => { - const articles = [...this.state.articles, response] - this.setState({articles}) - }) - } - - render() { - const articles = this.state.articles.map((article, index) => { - // only show 3 suggested articles - if (index !== 0 && article.headline !== this.props.currentArticle.headline) { - let featuredImage = article.featured_image - if(featuredImage) { - featuredImage = featuredImage.image.url - } - - return ( - - ) - } - }); - - return ( -
-

Suggested Articles

-
- {articles.filter((article) => {if (article) {return article}}).slice(0, 3)} -
-
- ); - } -} - -export default ArticlesSuggested; diff --git a/ubyssey/static_src/src/js/components/Article/index.js b/ubyssey/static_src/src/js/components/Article/index.js index a1db4f77a..122e66ef0 100644 --- a/ubyssey/static_src/src/js/components/Article/index.js +++ b/ubyssey/static_src/src/js/components/Article/index.js @@ -1,7 +1,6 @@ import Article from './Article.jsx' import ArticlePreview from './ArticlePreview.jsx' import ArticleHeader from './ArticleHeader.jsx' -import ArticlesSuggested from './ArticlesSuggested.jsx' export { Article as Article, diff --git a/ubyssey/static_src/src/js/components/Podcast/Episode.jsx b/ubyssey/static_src/src/js/components/Podcast/Episode.jsx deleted file mode 100644 index 30b056d2f..000000000 --- a/ubyssey/static_src/src/js/components/Podcast/Episode.jsx +++ /dev/null @@ -1,60 +0,0 @@ -import React from 'react' - -class Episode extends React.Component { - constructor(props) { - super(props); - this.state = { - open: window.location ? window.location.hash.includes(props.id) : false, - cacheData: window.location ? window.location.hash.includes(props.id) : false, - maxHeight: 10000 - } - } - - handleClick() { - this.setState(prevstate => ({ - open: !prevstate.open, - cacheData: true, - maxHeight: document.getElementById(this.props.id).clientHeight - })) - } - - render() { - const {description, file, image, publishedAt, id, title} = this.props - const openStyle = this.state.open ? {maxHeight: this.state.maxHeight} : {maxHeight: '150px'} - return ( -
- -
-
-

this.handleClick()}>{title}

-

{publishedAt}

-
-
-

- {description} -

-
-
-
-
- {this.state.cacheData && - - }{ !this.state.cacheData && - - } -
-
-
-
this.handleClick()}> - Show {this.state.open ? 'Less' : 'More'} -
-
- ) - } -} - -export default Episode diff --git a/ubyssey/static_src/src/js/components/Poll/Poll.jsx b/ubyssey/static_src/src/js/components/Poll/Poll.jsx deleted file mode 100644 index 8bdf57191..000000000 --- a/ubyssey/static_src/src/js/components/Poll/Poll.jsx +++ /dev/null @@ -1,238 +0,0 @@ -import React from 'react' -import DispatchAPI from '../../api/dispatch' - -import Cookies from 'js-cookie' -import PollAnswer from './PollAnswer.jsx' - -const COLOR_OPACITY = .8 - -class Poll extends React.Component { - constructor(props) { - super(props); - this.state = { - answers: [], - answerIds: [], - votes: [], - checkedAnswers: [], - hasVoted: false, - pollQuestion: '', - loading: true, - totalVotes: 0, - showResults: false, - pollOpen: true, - } - } - - getCookieName() { - return 'poll_id_' + String(this.props.id) - } - - getCookie(field) { - let cookie = Cookies.get(this.getCookieName()) - if (typeof cookie === 'string' && cookie !== '') { - cookie = JSON.parse(cookie) - if (field) { - return cookie[field] - } - return cookie - } - return cookie - } - - setCookie(voteId, answerId, init) { - if (this.state.pollOpen || init) { - Cookies.set( - this.getCookieName(), - {poll_id: this.props.id, vote_id: voteId, answer_id: answerId}, - { path: '/' } - ) - } - } - - componentDidMount() { - const answerId = Number(this.getCookie('answer_id')) - this.update(answerId) - } - - update(answerId) { - DispatchAPI.polls.get(this.props.id) - .then ((response)=> { - let answers = [] - let votes = [] - let answerIds = [] - let voteId = this.getCookie('vote_id') - - let checkedAnswers = this.state.checkedAnswers || [] - let hasVoted = this.state.hasVoted - - for (let answer of response.answers) { - answers.push(answer['name']) - votes.push(answer['vote_count']) - answerIds.push(answer['id']) - } - - if (answerId) { - checkedAnswers = this.state.checkedAnswers.concat(this.state.answerIds.indexOf (answerId)) - hasVoted = true - } - - let totalVotes = response.total_votes - this.setState({ - answers: answers, - answerIds: answerIds, - votes: votes, - voteId: voteId, - pollQuestion: response.question, - loading: false, - totalVotes: totalVotes, - showResults: response.show_results, - pollOpen: response.is_open, - checkedAnswers: checkedAnswers, - hasVoted: hasVoted - }) - }) - } - - changeAnswers(e, index) { - if (!this.state.hasVoted) { - let deselect = false - let newCheckedAnswers = this.state.checkedAnswers - - if (this.state.checkedAnswers.includes(index)) { - newCheckedAnswers.splice(this.state.checkedAnswers.indexOf (index), 1) - deselect = true - } - - if (!this.props.many) { - newCheckedAnswers = [] - newCheckedAnswers.push(index) - } - - else if (this.props.many) { - newCheckedAnswers.push(index) - } - - this.setState({ - checkedAnswers: newCheckedAnswers, - hasVoted: true - }, () => { - if (!deselect) { - this.vote(); - } - }) - } - } - - vote() { - for (let index of this.state.checkedAnswers) { - const payload = { - poll_id: this.props.id, - vote_id: this.state.voteId, - answer_id: this.state.answerIds[this.state.checkedAnswers[0]] - } - DispatchAPI.polls.vote(this.props.id, payload).then (response => { - this.setCookie(response.id, this.state.answerIds[index]) - this.update() - }) - } - } - - getPollResult(index) { - if (this.state.showResults) { - let width = 0 - - if (this.state.totalVotes !== 0) { - width = String((100*this.state.votes[index]/this.state.totalVotes).toFixed(0)) + '%' - } - - return width - } - } - - renderPollClosed() { - return ( -
-
-
-

This poll is currently closed

-
-
- ) - } - - renderLoadingPoll() { - return ( - - ) - } - - renderShowResults(totalVotes) { - return ( -
-
Total Votes: {totalVotes}
- -
- ) - } - - renderNoResults() { - return ( -
-
-
-

Thank you for your opinion

-

Poll results hidden from public

-
-
- ) - } - - render() { - const {answers, checkedAnswers, hasVoted, pollQuestion, many, - loading, totalVotes, showResults, pollOpen} = this.state - - const pollResult = hasVoted ? 'poll-results' : 'poll-voting' - const buttonStyle = hasVoted ? 'poll-button-voted': 'poll-button-no-vote' - const showResult = showResults ? (hasVoted ? COLOR_OPACITY : 0) : 0 - const notShowResult = showResults ? (hasVoted ? 0 : COLOR_OPACITY) : COLOR_OPACITY - - return ( -
- {!loading && -
-
-

{pollQuestion}

-
- {answers.map((answer, index) =>{ - let isSelected = checkedAnswers.includes(index) ? 'poll-selected' : 'poll-not-selected' - let buttonSelected = checkedAnswers.includes(index) ? 'poll-button-selected' : 'poll-button-not-selected' - let answerPercentage = this.getPollResult(index) - return ( - this.changeAnswers(e, index)} - /> - ) - })} - -
- { !pollOpen && this.renderPollClosed() } - { (pollOpen && hasVoted && !showResults) && this.renderNoResults() } -
- } - { (pollOpen && hasVoted && showResults) && this.renderShowResults(totalVotes) } - - { loading && this.renderLoadingPoll() } -
- ) - } -} - -export default Poll diff --git a/ubyssey/static_src/src/js/components/Poll/PollAnswer.jsx b/ubyssey/static_src/src/js/components/Poll/PollAnswer.jsx deleted file mode 100644 index 99298aac0..000000000 --- a/ubyssey/static_src/src/js/components/Poll/PollAnswer.jsx +++ /dev/null @@ -1,43 +0,0 @@ -import React from 'react'; - -const COLOR_OPACITY = .8 - -const PollAnswer = (props) => { - const { index, answer, hasVoted, showResults, checkedAnswers, answerPercentage } = props - const buttonStyle = hasVoted ? 'poll-button-voted': 'poll-button-no-vote' - const showResult = showResults ? (hasVoted ? COLOR_OPACITY : 0) : 0 - const notShowResult = showResults ? (hasVoted ? 0 : COLOR_OPACITY) : COLOR_OPACITY - let isSelected = checkedAnswers.includes(index) ? 'poll-selected' : 'poll-not-selected' - let buttonSelected = checkedAnswers.includes(index) ? 'poll-button-selected' : 'poll-button-not-selected' - return( - - ) -} - -export default PollAnswer; diff --git a/ubyssey/static_src/src/js/components/Poll/index.js b/ubyssey/static_src/src/js/components/Poll/index.js deleted file mode 100644 index 9a5d6bc98..000000000 --- a/ubyssey/static_src/src/js/components/Poll/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default as Poll } from './Poll.jsx'; \ No newline at end of file diff --git a/ubyssey/static_src/src/js/darkmode.js b/ubyssey/static_src/src/js/darkmode.js index 81babb126..e0112629c 100644 --- a/ubyssey/static_src/src/js/darkmode.js +++ b/ubyssey/static_src/src/js/darkmode.js @@ -4,20 +4,15 @@ // Investigate how to implement the dark mode to the system prefers-color-scheme instead of using darkMode variable $( document ).ready(function() { - storedMode = localStorage.getItem("darkMode"); - - if (storedMode == null) { - console.log("Hello ", storedMode); - localStorage.setItem("darkMode", window.matchMedia('(prefers-color-scheme: dark)').matches - ? 'dark' - : 'light'); - document.getElementsByTagName('meta')["color-scheme"].content = window.matchMedia('(prefers-color-scheme: dark)').matches + storedMode = getCookie("lightMode"); + if (storedMode == "") { + var colorScheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; - - } else { - document.getElementsByTagName('meta')["color-scheme"].content = storedMode; + setDarkMode(colorScheme); + + } else { setDarkMode(storedMode); } @@ -29,50 +24,25 @@ function getDarkMode() { - return document.getElementsByTagName('meta')["color-scheme"].content + return getCookie("lightMode"); } function setDarkMode(mode) { + document.getElementsByTagName('meta')["color-scheme"].content = mode; if (mode == "light") { // nav bar var r = document.querySelector(':root'); document.firstElementChild.setAttribute("color-css-theme", "light"); - r.style.setProperty('--background-nav', 'rgba(255, 255, 255, 0.99)'); - r.style.setProperty('--background', 'rgba(255, 255, 255, 0.99)'); - r.style.setProperty('--nav-mobile-background-barely-transparent', 'rgba(249, 249, 249, 0.99)'); - r.style.setProperty('--nav-h3-color', '#5D5D5D'); - r.style.setProperty('--nav-span-color', '#33331E'); - r.style.setProperty('--nav-headline-h1', '#373737'); - r.style.setProperty('--authors-title-bio', '#1B1B1B'); - r.style.setProperty('--three-bar-logo-color', '#303030'); - r.style.setProperty('--text_color', '#000000'); - - - $('.light-logo').show(); - $('.dark-logo').hide(); - - + r.classList.replace('darkmode', 'lightmode'); } else if (mode == "dark") { // nav ba var r = document.querySelector(':root'); document.firstElementChild.setAttribute("color-css-theme", "dark"); - r.style.setProperty('--background-nav', '#031723'); - r.style.setProperty('--background', '#031621'); - r.style.setProperty('--nav-mobile-background-barely-transparent', '#031723'); - r.style.setProperty('--nav-h3-color', '#EFEFEF'); - r.style.setProperty('--nav-span-color', '#EFEFEF'); - r.style.setProperty('--nav-headline-h1', '#5D5D5D'); - r.style.setProperty('--authors-title-bio', '#D8D8D8'); - r.style.setProperty('--three-bar-logo-color', '#EFEFEF'); - r.style.setProperty('--text_color', '#D9D9D9'); - - $('.light-logo').hide(); - $('.dark-logo').show(); - - + r.classList.replace('lightmode', 'darkmode'); } + document.cookie = "lightMode="+mode+ "; path=/;"; } function DarkModeToggle() { @@ -88,8 +58,6 @@ function DarkModeToggle() { setDarkMode(mode) - localStorage.setItem("darkMode", mode); - } else if (mode == "light") { document.getElementsByTagName('meta')["color-scheme"].content = "dark"; @@ -97,7 +65,22 @@ function DarkModeToggle() { mode = "dark" setDarkMode(mode) - localStorage.setItem("darkMode", mode); } }); -} \ No newline at end of file +} + +function getCookie(cname) { + let name = cname + "="; + let decodedCookie = decodeURIComponent(document.cookie); + let ca = decodedCookie.split(';'); + for(let i = 0; i , - $(this).get(0) - ) - }) - - -}) \ No newline at end of file diff --git a/ubyssey/static_src/src/js/home.js b/ubyssey/static_src/src/js/home.js new file mode 100644 index 000000000..61381404f --- /dev/null +++ b/ubyssey/static_src/src/js/home.js @@ -0,0 +1,62 @@ +function initializeSeemore() { + $('#seemore').click(function (e) { + e.preventDefault(); + openFeed(); + }); +} + +initializeSeemore(); + +function openFeed() { + document.getElementById("feed-section").classList.remove("home_infinitefeed_cutoff"); + document.getElementById("feed-shadow").remove(); + + document.getElementById("loader").removeAttribute("inactive"); +} +function closeFeed() { + var shadow = document.createElement("div"); + shadow.id = "feed-shadow"; + shadow.classList.add("home_infinitefeed_cutoff_shadow"); + + var seemore = document.createElement("a"); + seemore.id = "seemore"; + seemore.href = "#"; + seemore.classList.add("home_infinitefeed_cutoff_seemore"); + seemore.innerHTML = "See more"; + + shadow.appendChild(seemore); + document.getElementById("feed-section").appendChild(shadow); + + initializeSeemore(); + + document.getElementById("feed-section").classList.add("home_infinitefeed_cutoff"); + document.getElementById("loader").setAttribute("inactive", "True"); +} + +updateTimeBox = setInterval( + function() { + var feed = document.getElementById("feed"); + if(document.documentElement.scrollTop > document.getElementById("feed-section").offsetTop) { + var scroll = document.documentElement.scrollTop - document.getElementById("feed-section").offsetTop; + for(let i=0; i scroll && article.offsetTop < scroll + 500) { + document.getElementById("timeBox").innerHTML = "" + article.getAttribute("time") + ""; + break; + } + } + } + } else { + if (document.getElementById("timeBox").innerHTML != "Today") { + document.getElementById("timeBox").innerHTML = "Today"; + } + if(document.documentElement.scrollTop < 100) { + if (loader.hasAttribute("end") == false) { + if (document.getElementById("feed-section").classList.contains("home_infinitefeed_cutoff") == false) { + closeFeed(); + } + } + } + } + }, 100); \ No newline at end of file diff --git a/ubyssey/static_src/src/js/infinitefeed.js b/ubyssey/static_src/src/js/infinitefeed.js new file mode 100644 index 000000000..c2470f1af --- /dev/null +++ b/ubyssey/static_src/src/js/infinitefeed.js @@ -0,0 +1,72 @@ + +var loader = document.getElementById("loader"); +var feed = document.getElementById("feed"); + +var data = {}; + +function getData(attribute) { + if (feed.getAttribute(attribute) != null) { + data[attribute] = feed.getAttribute(attribute); + } +} + +getData("section"); +getData("category"); +getData("search_query"); +getData("label"); + +function getArticles() { + loader.setAttribute("inactive", "True"); + loader.classList.remove("hide"); + data["start"] = loader.getAttribute("start"); + data["number"] = loader.getAttribute("number"); + + $.ajax({ + type:"GET", + url: "/infinitefeed", + data:data, + success: function(data) + { + recievedata(data); + } + }) +} + +function recievedata(data) { + var loader = document.getElementById("loader"); + var feed = document.getElementById("feed"); + if(data == "End of feed") { + loader.setAttribute("inactive", "True"); + loader.setAttribute("end", "True"); + var congratz = document.createElement("p"); + congratz.innerHTML = "You reached the end! 🥳"; + loader.replaceChildren(congratz); + } else { + console.log("epic"); + for (let i=0; i loader.offsetTop - document.body.offsetHeight){ + getArticles(); + loader.setAttribute("start", String(parseInt(loader.getAttribute("start"))+ parseInt(loader.getAttribute("number")))); + } + } +} + +window.onscroll = function() { + loadArticles(); +} \ No newline at end of file diff --git a/ubyssey/static_src/src/js/main.js b/ubyssey/static_src/src/js/main.js index f8808997b..29b82f6e2 100644 --- a/ubyssey/static_src/src/js/main.js +++ b/ubyssey/static_src/src/js/main.js @@ -3,13 +3,22 @@ import upcomingEvents from './widgets/upcoming-events'; (function () { + moveModals(); + initializeModals(); + closeModal(); + + archiveMobileDropDown(); initializeSearchFormActions(); initializeSocialMediaActions(); + initializeAudioQuote() + + initializeFilterDropdown(); + ubysseyHeaderMobilePopUp(); ubysseyHeaderMagazineDropDown(); ubysseyHeaderCultureDropDown(); - archiveMobileDropDown(); + //initializeGallery() if ($('.js-article').length) { mp.pageView('article', $('.js-article'), 1) @@ -147,6 +156,7 @@ function archiveMobileDropDown() { $('.js-dropdown-container').click(function (e) { e.preventDefault(); + closeModal(); var dropdown = $(this).parent(); dropdown.fadeOut(DROPDOWN_FADE_TIME); enableScroll(); @@ -173,40 +183,36 @@ function archiveMobileDropDown() { }); } -function ubysseyHeaderMobilePopUp() { - $('a.menu').click(function (e) { + +function initializeModals() { + let DROPDOWN_FADE_TIME = 100; + var modal = document.getElementById("modal"); + + $('.open-modal > a').click(function (e) { e.preventDefault(); - if ($('nav.mobile').is(':visible')) { - $('nav.mobile').hide(); + var modalLink = $(this).parent().find('.openModal')[0]; + var modalIndex = parseInt(modalLink.getAttribute("modal")); + if (modal.style.display == "block") { + closeModal(); + modal.children[modalIndex].classList.add("hide"); + modal.children[modalIndex].classList.remove("show"); $(this).removeClass('active'); } else { - if ($('#search-form').is(':visible')) { - $('#search-form').hide(); - $('a.search').removeClass('active'); - } - $('nav.mobile').show(); + modal.children[modalIndex].classList.remove("hide"); + modal.children[modalIndex].classList.add("show"); + openModal(); $(this).addClass('active'); } + return false; }); -} -//Pending deletion -function initializeSearch() { - $('.dropdown > a').click(function (e) { + $('a.close-modal').click(function (e) { e.preventDefault(); - var dropdown = $(this).parent().find('.list'); - if (dropdown.is(':visible')) { - dropdown.hide(); - } else { - dropdown.show(); - } - return false; + closeModal(); }); - - - } + function initializeSearchFormActions() { $(document).on('click', 'a.search', function (e) { e.preventDefault(); @@ -279,3 +285,100 @@ function initializeSocialMediaActions() { } + +function initializeAudioQuote() { + $(document).on('click', 'a.playAudioQuote', function (e) { + e.preventDefault(); + + var id = this.getAttribute("audio"); + + var sound = document.getElementById(id); + if (sound.paused) { + sound.play(); + + var icon = document.getElementById("icon-" + id); + var frames = ["fa-volume-off", "fa-volume-low", "fa-volume-high"]; + + var animateIcon = setInterval(function () { + + for (let i=0; i < frames.length; i++) { + if (icon.classList.contains(frames[i])) { + icon.classList.toggle(frames[i]); + if (i == frames.length - 1 ) { + icon.classList.toggle(frames[0]); + } else { + icon.classList.toggle(frames[i+1]); + } + break; + } + } + + if (sound.paused) { + icon.classList.remove("fa-volume-low"); + icon.classList.remove("fa-volume-high"); + icon.classList.add("fa-volume-off"); + clearInterval(animateIcon); + } + + }, 200); + } else { + sound.pause(); + sound.currentTime = 0; + } + + }); +} +function closeModal() { + var modal = document.getElementById("modal"); + modal.style.display = 'none'; + + var content = document.getElementById("content-wrapper"); + content.removeAttribute("tabindex"); + content.removeAttribute("readonly"); + content.removeAttribute("aria-hidden"); + content.removeAttribute("inert"); + + for (let i=0; i < modal.children.length; i++) { + modal.children[i].classList.remove("show"); + modal.children[i].classList.add("hide"); + } + + $('body').removeClass('u-no-scroll'); +} + +function openModal() { + var modal = document.getElementById("modal"); + modal.style.display = 'block'; + + var content = document.getElementById("content-wrapper"); + content.setAttribute("tabindex", "-1"); + content.setAttribute("readonly", ""); + content.setAttribute("aria-hidden", "true"); + content.setAttribute("inert", ""); + + $('body').addClass('u-no-scroll'); +} + +function moveModals() { + var modalBlocks = document.getElementsByClassName("add-to-modal"); + var modal = document.getElementById("modal"); + + for (let i=0; i < modalBlocks.length; i++) { + //modalBlocks[i].remove(); + var div = document.createElement("div"); + div.classList.add("openModal"); + div.setAttribute("modal", i); + + modalBlocks[i].insertAdjacentElement("beforebegin", div); + modal.appendChild(modalBlocks[i]); + } +} + +function initializeFilterDropdown() { + $(document).on('click', 'a.filterDropdown', function (e) { + e.preventDefault(); + this.parentElement.nextElementSibling.classList.toggle('hide_filter'); + this.children[0].classList.toggle("fa-caret-down"); + this.children[0].classList.toggle("fa-caret-up"); + }); +} \ No newline at end of file diff --git a/ubyssey/static_src/src/js/modules/Youtube.js b/ubyssey/static_src/src/js/modules/Youtube.js index 864f5143a..31ca6df85 100644 --- a/ubyssey/static_src/src/js/modules/Youtube.js +++ b/ubyssey/static_src/src/js/modules/Youtube.js @@ -1,4 +1,4 @@ -export function YoutubePlayer(element) { +function YoutubePlayer(element) { const node = $(element); let playerReady = false; let userReady = false; @@ -23,16 +23,18 @@ export function YoutubePlayer(element) { playerReady = true; } } - + console.log("Youtube"); $(element).find('.js-video-launch').click(function(){ $(this).hide(); $(`#video-${node.data('id')}`).show(); + console.log(playerReady); if (playerReady){ player.playVideo(); } else { userReady = true; } }); + } // 2. This code loads the IFrame Player API code asynchronously. @@ -42,6 +44,7 @@ tag.src = 'https://www.youtube.com/iframe_api'; let firstScriptTag = document.getElementsByTagName('script')[0]; firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); +console.log(firstScriptTag); // 3. This function creates an

{{item.featured_media.first.credit}}

-

{% endif %}

{{item.lede}}

\ No newline at end of file diff --git a/ubyssey/templates/ubyssey/archive.html b/ubyssey/templates/ubyssey/archive.html index 4f65c886c..f84b8e6d0 100644 --- a/ubyssey/templates/ubyssey/archive.html +++ b/ubyssey/templates/ubyssey/archive.html @@ -13,41 +13,14 @@
-
-
- - {{ order|title }} - - -
-
-
-
-

Order by:

-
- -
-
- -
-
-
-
-
+

{% if paginated_articles %} {{ paginated_articles.paginator.count }} RESULTS {% else %}NO RESULTS{% endif %} {% if search_query %} FOR "{{ search_query }}"{% endif %}

- + {{ order|title }} diff --git a/ubyssey/templates/ubyssey/base.html b/ubyssey/templates/ubyssey/base.html index ebd7747ff..d7d026451 100644 --- a/ubyssey/templates/ubyssey/base.html +++ b/ubyssey/templates/ubyssey/base.html @@ -2,7 +2,7 @@ {% load wagtailuserbar %} {% load dispatch_tags %} - + @@ -11,7 +11,8 @@ - + + {% include 'ubyssey/meta_tags.html' %} {% block stylesheet %} @@ -57,14 +58,6 @@ node.parentNode.insertBefore(gads, node); })(); // invoke the function - {% comment %} Below script generated by Mailchimp Integrations. It enables Mailchimp popup functionality - {% endcomment %} - {% block head_scripts %} {% endblock %} @@ -129,13 +122,18 @@ }(document, 'script', 'twitter-wjs')); -
+
+ {% block header %} {% endblock %} + +
@@ -215,7 +213,7 @@ mixpanel.init("4941b226d03a988d5f2bc8fea8e0e70d"); - + {% comment %} {% endcomment %} diff --git a/ubyssey/urls.py b/ubyssey/urls.py index 5c5ee7554..77b555b4b 100644 --- a/ubyssey/urls.py +++ b/ubyssey/urls.py @@ -14,6 +14,7 @@ from ubyssey.views.advertise import AdvertiseTheme from ubyssey.views.magazine import magazine, MagazineLandingView, MagazineArticleView +from infinitefeed.views import infinitefeed from ubyssey.zones import * from ubyssey.widgets import * @@ -34,6 +35,12 @@ urlpatterns = [] +settings.WAGTAILIMAGES_FORMAT_CONVERSIONS = { + 'jpeg': 'webp', + 'png': 'webp', + 'webp': 'webp', +} + if settings.DEBUG: import debug_toolbar urlpatterns += [ @@ -71,6 +78,7 @@ # Wagtail re_path(r'^admin/', include(wagtailadmin_urls)), re_path(r'^documents/', include(wagtaildocs_urls)), + re_path(r'^infinitefeed/$', infinitefeed, name='infinitefeed'), re_path(r'^rss/$', FrontpageFeed(), name='frontpage-feed'), re_path(r'^rss/(?P[-\w]+)/$', SectionFeed(), name='section-feed'), re_path(r'^authors/(?P[-\w]+)/rss/$', AuthorFeed(), name='author-feed'), diff --git a/ubyssey/views/feed.py b/ubyssey/views/feed.py index 9b4e331bc..ee31d346a 100644 --- a/ubyssey/views/feed.py +++ b/ubyssey/views/feed.py @@ -138,7 +138,7 @@ def title(self, author): return 'Stories from %s at The Ubyssey' % author.full_name def description(self, author): - return author.description + return author.bio_description def link(self, author): return author.get_full_url() diff --git a/videos/models.py b/videos/models.py index 63eb2efd5..40c9dec7b 100644 --- a/videos/models.py +++ b/videos/models.py @@ -18,6 +18,8 @@ from wagtail.admin.edit_handlers import FieldPanel, InlinePanel, MultiFieldPanel, PageChooserPanel from wagtail.core.models import Orderable, Page from wagtail.snippets.models import register_snippet +from wagtail.search import index + #-----Taggit stuff----- @@ -84,12 +86,14 @@ def get_context(self, request, *args, **kwargs): context["paginated_videos"] = paginated_videos return context + + # @route('^archive/videos$') #-----Snippet models----- @register_snippet -class VideoSnippet(ClusterableModel): +class VideoSnippet(index.Indexed, ClusterableModel): title = models.CharField( max_length=255, @@ -180,7 +184,17 @@ def get_authors_with_urls(self) -> str: heading="Tags" ), ] - + #-----Search fields etc----- + #See https://docs.wagtail.org/en/stable/topics/search/indexing.html + search_fields = [ + index.SearchField('title'), + index.SearchField('slug'), + + index.FilterField('video_authors'), + index.FilterField('tags'), + index.FilterField('slug'), + index.FilterField('created_at'), + ] class Meta: verbose_name = "Video" verbose_name_plural = "Videos" diff --git a/videos/templates/videos/stream_blocks/one_off_video.html b/videos/templates/videos/stream_blocks/one_off_video.html index 7b8f86cfd..d7378e114 100644 --- a/videos/templates/videos/stream_blocks/one_off_video.html +++ b/videos/templates/videos/stream_blocks/one_off_video.html @@ -9,6 +9,9 @@ credit {% endcomment%} {% load video_filters %} +{% load static %} + +
diff --git a/videos/templates/videos/stream_blocks/video.html b/videos/templates/videos/stream_blocks/video.html new file mode 100644 index 000000000..e11e5e9e4 --- /dev/null +++ b/videos/templates/videos/stream_blocks/video.html @@ -0,0 +1,27 @@ +{% load dispatch_filters %} +
+ + +
+ Loading Video... + +
+
Credits: {{ video.get_authors_string|safe }}
+ +
+{% comment %}

+ + {{ video_placement.video.text }} + +

{% endcomment %} +{% comment %}
  • + {% include "videos/video_snippet.html" %} +
  • {% endcomment %} \ No newline at end of file diff --git a/videos/templates/videos/videos_page.html b/videos/templates/videos/videos_page.html index 9f0eb35c3..c9fc99de4 100644 --- a/videos/templates/videos/videos_page.html +++ b/videos/templates/videos/videos_page.html @@ -14,7 +14,7 @@
    {% include 'objects/advertisement.html' with size='leaderboard' name='Leaderboard' id=1 %} {% include 'objects/advertisement.html' with size='mobile-leaderboard' name='Mobile_Leaderboard' id=5 %} -
    +

    Videos

    @@ -34,35 +34,9 @@

    Videos

    {% endif %}
    -
    +
    {% for video in paginated_videos %} - {% load dispatch_filters %} -
    - - -
    - Loading Video... - -
    -
    Credits: {{ video.get_authors_string|safe }}
    - -
    - {% comment %}

    - - {{ video_placement.video.text }} - -

    {% endcomment %} - {% comment %}
  • - {% include "videos/video_snippet.html" %} -
  • {% endcomment %} + {% include 'videos/stream_blocks/video.html' with video=video %} {% endfor %}