Compare commits
13 Commits
master
...
f65bfb1564
Author | SHA1 | Date | |
---|---|---|---|
f65bfb1564 | |||
2c46cde8e6 | |||
b1395a4b5d | |||
4e03abcac8 | |||
cd360d9ac7 | |||
849a14014c | |||
e9e09104ad | |||
95a847cee0 | |||
8281c1a4c9 | |||
cbae38e893 | |||
efd940f34f | |||
893f441ddd | |||
cd76659ed9 |
@ -30,7 +30,7 @@ class AdminSite(admin.AdminSite):
|
|||||||
'programs': Program.objects.active().values('pk', 'title') \
|
'programs': Program.objects.active().values('pk', 'title') \
|
||||||
.order_by('title'),
|
.order_by('title'),
|
||||||
# today's diffusions
|
# today's diffusions
|
||||||
'diffusions': Diffusion.objects.on_air().date().order_by('start') \
|
'diffusions': Diffusion.objects.date().order_by('start') \
|
||||||
.select_related('episode'),
|
.select_related('episode'),
|
||||||
# TODO: only for dashboard
|
# TODO: only for dashboard
|
||||||
# last comments
|
# last comments
|
||||||
|
32
aircox/filters.py
Normal file
32
aircox/filters.py
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
import django_filters as filters
|
||||||
|
|
||||||
|
from .models import Page, Episode
|
||||||
|
|
||||||
|
|
||||||
|
class PageFilters(filters.FilterSet):
|
||||||
|
q = filters.CharFilter(method='search_filter', label=_('Search'))
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Page
|
||||||
|
fields = {
|
||||||
|
'category__id': ['in'],
|
||||||
|
'pub_date': ['exact', 'gte', 'lte'],
|
||||||
|
}
|
||||||
|
|
||||||
|
def search_filter(self, queryset, name, value):
|
||||||
|
return queryset.search(value)
|
||||||
|
|
||||||
|
|
||||||
|
class EpisodeFilters(PageFilters):
|
||||||
|
podcast = filters.BooleanFilter(method='podcast_filter', label=_('Podcast'))
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Episode
|
||||||
|
fields = PageFilters.Meta.fields.copy()
|
||||||
|
|
||||||
|
def podcast_filter(self, queryset, name, value):
|
||||||
|
if value:
|
||||||
|
return queryset.filter(sound__is_public=True).distinct()
|
||||||
|
return queryset.filter(sound__isnull=True)
|
||||||
|
|
@ -23,6 +23,9 @@ from aircox.models import *
|
|||||||
|
|
||||||
__doc__ = __doc__.format(settings=settings)
|
__doc__ = __doc__.format(settings=settings)
|
||||||
|
|
||||||
|
__all__ = ('PlaylistImport', 'Command')
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger('aircox.commands')
|
logger = logging.getLogger('aircox.commands')
|
||||||
|
|
||||||
|
|
||||||
@ -89,11 +92,10 @@ class PlaylistImport:
|
|||||||
**self.track_kwargs
|
**self.track_kwargs
|
||||||
)
|
)
|
||||||
track.timestamp = timestamp
|
track.timestamp = timestamp
|
||||||
print('track', track, timestamp)
|
|
||||||
track.info = line.get('info')
|
track.info = line.get('info')
|
||||||
tags = line.get('tags')
|
tags = line.get('tags')
|
||||||
if tags:
|
if tags:
|
||||||
track.tags.add(*tags.split(','))
|
track.tags.add(*tags.lower().split(','))
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
'an error occured for track {index}, it may not '
|
'an error occured for track {index}, it may not '
|
||||||
|
@ -62,6 +62,13 @@ class Episode(Page):
|
|||||||
raise ValueError('missing parent program')
|
raise ValueError('missing parent program')
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_default_title(cls, page, date):
|
||||||
|
return settings.AIRCOX_EPISODE_TITLE.format(
|
||||||
|
program=page,
|
||||||
|
date=date.strftime(settings.AIRCOX_EPISODE_TITLE_DATE_FORMAT)
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_init_kwargs_from(cls, page, date, title=None, **kwargs):
|
def get_init_kwargs_from(cls, page, date, title=None, **kwargs):
|
||||||
""" Get default Episode's title """
|
""" Get default Episode's title """
|
||||||
@ -252,7 +259,7 @@ class Diffusion(BaseRerun):
|
|||||||
return self.type == self.TYPE_ON_AIR and \
|
return self.type == self.TYPE_ON_AIR and \
|
||||||
self.start <= now and self.end >= now
|
self.start <= now and self.end >= now
|
||||||
|
|
||||||
# TODO: property?
|
@property
|
||||||
def is_live(self):
|
def is_live(self):
|
||||||
""" True if Diffusion is live (False if there are sounds files). """
|
""" True if Diffusion is live (False if there are sounds files). """
|
||||||
return self.type == self.TYPE_ON_AIR and \
|
return self.type == self.TYPE_ON_AIR and \
|
||||||
|
@ -53,6 +53,11 @@ class PageQuerySet(InheritanceQuerySet):
|
|||||||
return self.filter(parent=parent) if id is None else \
|
return self.filter(parent=parent) if id is None else \
|
||||||
self.filter(parent__id=id)
|
self.filter(parent__id=id)
|
||||||
|
|
||||||
|
def search(self, q, search_content=True):
|
||||||
|
if search_content:
|
||||||
|
return self.filter(models.Q(title__icontains=q) | models.Q(content__icontains=q))
|
||||||
|
return self.filter(title__icontains=q)
|
||||||
|
|
||||||
|
|
||||||
class BasePage(models.Model):
|
class BasePage(models.Model):
|
||||||
""" Base class for publishable content """
|
""" Base class for publishable content """
|
||||||
|
@ -16,7 +16,31 @@
|
|||||||
<tr {% if diffusion.is_now %}class="is-selected"{% endif %}>
|
<tr {% if diffusion.is_now %}class="is-selected"{% endif %}>
|
||||||
<td>{{ diffusion.start|time }} - {{ diffusion.end|time }}</td>
|
<td>{{ diffusion.start|time }} - {{ diffusion.end|time }}</td>
|
||||||
<td><img src="{% thumbnail episode.cover 64x64 crop %}"/></td>
|
<td><img src="{% thumbnail episode.cover 64x64 crop %}"/></td>
|
||||||
<td><a href="{% url "admin:aircox_episode_change" episode.pk %}">{{ episode.title }}</a></td>
|
<td>
|
||||||
|
<a href="{% url "admin:aircox_episode_change" episode.pk %}">{{ episode.title }}</a>
|
||||||
|
|
||||||
|
{% if diffusion.type == diffusion.TYPE_ON_AIR %}
|
||||||
|
<span class="tag is-info">
|
||||||
|
<span class="icon is-small">
|
||||||
|
{% if diffusion.is_live %}
|
||||||
|
<i class="fa fa-microphone"
|
||||||
|
title="{% trans "Live diffusion" %}"></i>
|
||||||
|
{% else %}
|
||||||
|
<i class="fa fa-music"
|
||||||
|
title="{% trans "Differed diffusion" %}"></i>
|
||||||
|
{% endif %}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
{{ diffusion.get_type_display }}
|
||||||
|
</span>
|
||||||
|
{% elif diffusion.type == diffusion.TYPE_CANCEL %}
|
||||||
|
<span class="tag is-danger">
|
||||||
|
{{ diffusion.get_type_display }}</span>
|
||||||
|
{% elif diffusion.type == diffusion.TYPE_UNCONFIRMED %}
|
||||||
|
<span class="tag is-warning">
|
||||||
|
{{ diffusion.get_type_display }}</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
@ -56,6 +56,23 @@ Usefull context:
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="navbar-end">
|
||||||
|
{% block top-nav-tools %}
|
||||||
|
{% endblock %}
|
||||||
|
{% block top-nav-end %}
|
||||||
|
<div class="navbar-item">
|
||||||
|
<form action="{% url 'page-list' %}" method="GET">
|
||||||
|
<div class="control has-icons-left">
|
||||||
|
<span class="icon is-small is-left">
|
||||||
|
<i class="fa fa-search"></i>
|
||||||
|
</span>
|
||||||
|
<input type="text" name="q" class="input"
|
||||||
|
placeholder="{% trans "Search" %}" />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
@ -24,48 +24,7 @@
|
|||||||
|
|
||||||
{% block main %}{{ block.super }}
|
{% block main %}{{ block.super }}
|
||||||
|
|
||||||
{% block before_list %}
|
{% block before_list %}{% endblock %}
|
||||||
{% if filters %}
|
|
||||||
<form method="GET" action="" class="media">
|
|
||||||
<div class="media-content">
|
|
||||||
{% block filters %}
|
|
||||||
{% for label, name, choices in filters %}
|
|
||||||
<div class="field is-horizontal">
|
|
||||||
<div class="field-label">
|
|
||||||
<label class="label">{{ label }}</label>
|
|
||||||
</div>
|
|
||||||
<div class="field-body">
|
|
||||||
<div class="field is-narrow">
|
|
||||||
<div class="control">
|
|
||||||
{% for label, value, checked in choices %}
|
|
||||||
<label class="checkbox">
|
|
||||||
<input type="checkbox" class="checkbox" name="{{ name }}"
|
|
||||||
value="{{ value }}"
|
|
||||||
{% if checked %}checked{% endif %} />
|
|
||||||
{{ label }}
|
|
||||||
</label>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
{% endblock %}
|
|
||||||
</div>
|
|
||||||
<div class="media-right">
|
|
||||||
<div class="field is-grouped is-grouped-right">
|
|
||||||
<div class="control">
|
|
||||||
<button class="button is-primary"/>{% trans "Apply" %}</button>
|
|
||||||
</div>
|
|
||||||
<div class="control">
|
|
||||||
<a href="?" class="button is-secondary">{% trans "Reset" %}</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
<section role="list">
|
<section role="list">
|
||||||
{% block pages_list %}
|
{% block pages_list %}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{% extends "aircox/basepage_detail.html" %}
|
{% extends "aircox/basepage_detail.html" %}
|
||||||
{% load static i18n humanize honeypot %}
|
{% load static i18n humanize honeypot aircox %}
|
||||||
{% comment %}
|
{% comment %}
|
||||||
Base template used to display a Page
|
Base template used to display a Page
|
||||||
|
|
||||||
@ -14,6 +14,19 @@ Context:
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block top-nav-tools %}
|
||||||
|
{% has_perm page "change" as can_edit %}
|
||||||
|
{% if can_edit %}
|
||||||
|
<a class="navbar-item" href="{{ page|admin_url:'change' }}"
|
||||||
|
target="new">
|
||||||
|
<span class="icon is-small">
|
||||||
|
<i class="fa fa-pen"></i>
|
||||||
|
</span>
|
||||||
|
<span>{% trans "Edit" %}</span>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
{{ block.super }}
|
{{ block.super }}
|
||||||
|
|
||||||
|
@ -2,3 +2,62 @@
|
|||||||
{% comment %}Display a list of Pages{% endcomment %}
|
{% comment %}Display a list of Pages{% endcomment %}
|
||||||
{% load i18n aircox %}
|
{% load i18n aircox %}
|
||||||
|
|
||||||
|
{% block before_list %}
|
||||||
|
{{ block.super }}
|
||||||
|
|
||||||
|
{% if view.has_filters and object_list %}
|
||||||
|
<form method="GET" action="" class="media">
|
||||||
|
<div class="media-content">
|
||||||
|
{% block filters %}
|
||||||
|
<div class="field is-horizontal">
|
||||||
|
<div class="field-label">
|
||||||
|
<label class="label">{% trans "Search" %}</label>
|
||||||
|
</div>
|
||||||
|
<div class="field-body">
|
||||||
|
<div class="field">
|
||||||
|
<div class="control has-icons-left">
|
||||||
|
<span class="icon is-small is-left">
|
||||||
|
<i class="fa fa-search"></i>
|
||||||
|
</span>
|
||||||
|
<input class="input" type="text" name="q"
|
||||||
|
value="{{ filterset_data.q }}"
|
||||||
|
placeholder="{% trans "Search content" %}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field is-horizontal">
|
||||||
|
<div class="field-label">
|
||||||
|
<label class="label">{% trans "Categories" %}</label>
|
||||||
|
</div>
|
||||||
|
<div class="field-body">
|
||||||
|
<div class="field is-narrow">
|
||||||
|
<div class="control">
|
||||||
|
{% for label, value in categories %}
|
||||||
|
<label class="checkbox">
|
||||||
|
<input type="checkbox" class="checkbox" name="category__id__in"
|
||||||
|
value="{{ value }}"
|
||||||
|
{% if value in filterset_data.category__id__in %}checked{% endif %} />
|
||||||
|
{{ label }}
|
||||||
|
</label>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
</div>
|
||||||
|
<div class="media-right">
|
||||||
|
<div class="field is-grouped is-grouped-right">
|
||||||
|
<div class="control">
|
||||||
|
<button class="button is-primary"/>{% trans "Apply" %}</button>
|
||||||
|
</div>
|
||||||
|
<div class="control">
|
||||||
|
<a href="?" class="button is-secondary">{% trans "Reset" %}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
65
aircox/templates/aircox/widgets/basepage_item.html
Normal file
65
aircox/templates/aircox/widgets/basepage_item.html
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
{% load i18n easy_thumbnails_tags aircox %}
|
||||||
|
{% comment %}
|
||||||
|
List item for a page
|
||||||
|
|
||||||
|
Context variables:
|
||||||
|
- object: the object to render
|
||||||
|
- render_card: render as card
|
||||||
|
- is-primary: render as primary
|
||||||
|
- has_headline (=False): if True, display headline
|
||||||
|
- has_cover (=True): hide page cover
|
||||||
|
{% endcomment %}
|
||||||
|
|
||||||
|
{% if render_card %}
|
||||||
|
<article class="card {% if is_primary %}is-primary{% endif %}">
|
||||||
|
<header class="card-image">
|
||||||
|
<figure class="image is-4by3">
|
||||||
|
<img src="{% thumbnail object.cover|default:station.default_cover 480x480 %}">
|
||||||
|
</figure>
|
||||||
|
</header>
|
||||||
|
<div class="card-header">
|
||||||
|
<h4 class="title">
|
||||||
|
<a href="{{ object.get_absolute_url }}">
|
||||||
|
{% block card_title %}{{ object.title }}{% endblock %}
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
<article class="media item {% block css %}{% endblock%}">
|
||||||
|
{% if has_cover|default_if_none:True %}
|
||||||
|
<div class="media-left">
|
||||||
|
<img src="{% thumbnail object.cover|default:station.default_cover 128x128 crop=scale %}"
|
||||||
|
class="cover is-small">
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="media-content">
|
||||||
|
<h5 class="title is-5 has-text-weight-normal">
|
||||||
|
{% block title %}
|
||||||
|
{% if object.is_published %}
|
||||||
|
<a href="{{ object.get_absolute_url }}">{{ object.title }}</a>
|
||||||
|
{% else %}
|
||||||
|
{{ object.title }}
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
</h5>
|
||||||
|
<div class="subtitle is-6 has-text-weight-light">
|
||||||
|
{% block subtitle %}
|
||||||
|
{% if object.category %}{{ object.category.title }}{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if has_headline|default_if_none:True %}
|
||||||
|
<div class="headline">
|
||||||
|
{% block headline %}{{ object.headline }}{% endblock %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% block actions %}{% endblock %}
|
||||||
|
</article>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
@ -13,7 +13,11 @@ Context variables:
|
|||||||
{% block title %}
|
{% block title %}
|
||||||
{% if not object.is_published and object.program.is_published %}
|
{% if not object.is_published and object.program.is_published %}
|
||||||
<a href="{{ object.program.get_absolute_url }}">
|
<a href="{{ object.program.get_absolute_url }}">
|
||||||
{{ object.title }}
|
{{ object.program.title }}
|
||||||
|
{% if diffusion %}
|
||||||
|
—
|
||||||
|
{{ diffusion.start|date:"d F" }}
|
||||||
|
{% endif %}
|
||||||
</a>
|
</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ block.super }}
|
{{ block.super }}
|
||||||
|
@ -1,62 +1,6 @@
|
|||||||
{% load i18n easy_thumbnails_tags aircox %}
|
{% extends "aircox/widgets/basepage_item.html" %}
|
||||||
{% comment %}
|
|
||||||
List item for a page
|
|
||||||
|
|
||||||
Context variables:
|
{% block card_title %}
|
||||||
- object: the object to render
|
{% block title %}{{ block.super }}{% endblock %}
|
||||||
- render_card: render as card
|
{% endblock %}
|
||||||
- is-primary: render as primary
|
|
||||||
- has_headline (=False): if True, display headline
|
|
||||||
- has_cover (=True): hide page cover
|
|
||||||
{% endcomment %}
|
|
||||||
|
|
||||||
{% if render_card %}
|
|
||||||
<article class="card {% if is_primary %}is-primary{% endif %}">
|
|
||||||
<header class="card-image">
|
|
||||||
<figure class="image is-4by3">
|
|
||||||
<img src="{% thumbnail object.cover|default:station.default_cover 480x480 %}">
|
|
||||||
</figure>
|
|
||||||
</header>
|
|
||||||
<div class="card-header">
|
|
||||||
<h4 class="title">
|
|
||||||
<a href="{{ object.get_absolute_url }}">{{ object.title }}</a>
|
|
||||||
</h4>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
|
|
||||||
|
|
||||||
{% else %}
|
|
||||||
<article class="media item {% block css %}{% endblock%}">
|
|
||||||
{% if has_cover|default_if_none:True %}
|
|
||||||
<div class="media-left">
|
|
||||||
<img src="{% thumbnail object.cover|default:station.default_cover 128x128 crop=scale %}"
|
|
||||||
class="cover is-small">
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
<div class="media-content">
|
|
||||||
<h5 class="title is-5 has-text-weight-normal">
|
|
||||||
{% block title %}
|
|
||||||
{% if object.is_published %}
|
|
||||||
<a href="{{ object.get_absolute_url }}">{{ object.title }}</a>
|
|
||||||
{% else %}
|
|
||||||
{{ object.title }}
|
|
||||||
{% endif %}
|
|
||||||
{% endblock %}
|
|
||||||
</h5>
|
|
||||||
<div class="subtitle is-6 has-text-weight-light">
|
|
||||||
{% block subtitle %}
|
|
||||||
{% if object.category %}{{ object.category.title }}{% endif %}
|
|
||||||
{% endblock %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% if has_headline|default_if_none:True %}
|
|
||||||
<div class="headline">
|
|
||||||
{% block headline %}{{ object.headline }}{% endblock %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% block actions %}{% endblock %}
|
|
||||||
</article>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
|
@ -2,6 +2,8 @@ import random
|
|||||||
import json
|
import json
|
||||||
|
|
||||||
from django import template
|
from django import template
|
||||||
|
from django.contrib.admin.templatetags.admin_urls import admin_urlname
|
||||||
|
from django.urls import reverse
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
|
||||||
from aircox.models import Page, Diffusion, Log
|
from aircox.models import Page, Diffusion, Log
|
||||||
@ -10,6 +12,12 @@ random.seed()
|
|||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter(name='admin_url')
|
||||||
|
def do_admin_url(obj, arg, pass_id=True):
|
||||||
|
""" Reverse admin url for object """
|
||||||
|
name = admin_urlname(obj._meta, arg)
|
||||||
|
return reverse(name, args=(obj.id,)) if pass_id else reverse(name)
|
||||||
|
|
||||||
@register.filter(name='get_tracks')
|
@register.filter(name='get_tracks')
|
||||||
def do_get_tracks(obj):
|
def do_get_tracks(obj):
|
||||||
""" Get a list of track for the provided log, diffusion, or episode """
|
""" Get a list of track for the provided log, diffusion, or episode """
|
||||||
@ -20,6 +28,14 @@ def do_get_tracks(obj):
|
|||||||
obj = obj.episode
|
obj = obj.episode
|
||||||
return obj.track_set.all()
|
return obj.track_set.all()
|
||||||
|
|
||||||
|
@register.simple_tag(name='has_perm', takes_context=True)
|
||||||
|
def do_has_perm(context, obj, perm, user=None):
|
||||||
|
""" Return True if ``user.has_perm('[APP].[perm]_[MODEL]')`` """
|
||||||
|
if user is None:
|
||||||
|
user = context['request'].user
|
||||||
|
return user.has_perm('{}.{}_{}'.format(
|
||||||
|
obj._meta.app_label, perm, obj._meta.model_name))
|
||||||
|
|
||||||
@register.filter(name='is_diffusion')
|
@register.filter(name='is_diffusion')
|
||||||
def do_is_diffusion(obj):
|
def do_is_diffusion(obj):
|
||||||
""" Return True if object is a Diffusion. """
|
""" Return True if object is a Diffusion. """
|
||||||
|
@ -4,6 +4,7 @@ import datetime
|
|||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.views.generic import ListView
|
from django.views.generic import ListView
|
||||||
|
|
||||||
|
from ..filters import EpisodeFilters
|
||||||
from ..models import Diffusion, Episode, Program, StaticPage, Sound
|
from ..models import Diffusion, Episode, Program, StaticPage, Sound
|
||||||
from .base import BaseView
|
from .base import BaseView
|
||||||
from .program import ProgramPageDetailView
|
from .program import ProgramPageDetailView
|
||||||
@ -25,22 +26,12 @@ class EpisodeDetailView(ProgramPageDetailView):
|
|||||||
|
|
||||||
class EpisodeListView(PageListView):
|
class EpisodeListView(PageListView):
|
||||||
model = Episode
|
model = Episode
|
||||||
|
filterset_class = EpisodeFilters
|
||||||
item_template_name = 'aircox/widgets/episode_item.html'
|
item_template_name = 'aircox/widgets/episode_item.html'
|
||||||
has_headline = True
|
has_headline = True
|
||||||
parent_model = Program
|
parent_model = Program
|
||||||
attach_to_value = StaticPage.ATTACH_TO_EPISODES
|
attach_to_value = StaticPage.ATTACH_TO_EPISODES
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
qs = super().get_queryset()
|
|
||||||
if self.filters and 'podcasts' in self.filters:
|
|
||||||
qs = qs.filter(sound__is_public=True)
|
|
||||||
return qs
|
|
||||||
|
|
||||||
def get_filters(self):
|
|
||||||
return super().get_filters() + (
|
|
||||||
(_('Podcasts'), 'podcasts', tuple()),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class DiffusionListView(GetDateMixin, AttachedToMixin, BaseView, ListView):
|
class DiffusionListView(GetDateMixin, AttachedToMixin, BaseView, ListView):
|
||||||
""" View for timetables """
|
""" View for timetables """
|
||||||
|
@ -80,3 +80,30 @@ class AttachedToMixin:
|
|||||||
.published().first()
|
.published().first()
|
||||||
return super().get_page()
|
return super().get_page()
|
||||||
|
|
||||||
|
|
||||||
|
class FiltersMixin:
|
||||||
|
""" Mixin integrating Django filters' filter set """
|
||||||
|
filterset = None
|
||||||
|
filterset_class = None
|
||||||
|
|
||||||
|
def get_filterset(self, data, query):
|
||||||
|
return self.filterset_class(data, query)
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
query = super().get_queryset()
|
||||||
|
if self.filterset_class:
|
||||||
|
self.filterset = self.get_filterset(self.request.GET.copy(), query)
|
||||||
|
return self.filterset.qs
|
||||||
|
return query
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
filterset = kwargs.setdefault('filterset', self.filterset)
|
||||||
|
if filterset.is_valid():
|
||||||
|
kwargs['filterset_data'] = filterset.form.cleaned_data
|
||||||
|
else:
|
||||||
|
kwargs['filterset_data'] = {}
|
||||||
|
|
||||||
|
params = self.request.GET.copy()
|
||||||
|
kwargs['get_params'] = params.pop('page', True) and params
|
||||||
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
@ -5,11 +5,12 @@ from django.views.generic import DetailView, ListView
|
|||||||
|
|
||||||
from honeypot.decorators import check_honeypot
|
from honeypot.decorators import check_honeypot
|
||||||
|
|
||||||
|
from ..filters import PageFilters
|
||||||
from ..forms import CommentForm
|
from ..forms import CommentForm
|
||||||
from ..models import Category, Comment
|
from ..models import Category, Comment
|
||||||
from ..utils import Redirect
|
from ..utils import Redirect
|
||||||
from .base import BaseView
|
from .base import BaseView
|
||||||
from .mixins import AttachedToMixin, ParentMixin
|
from .mixins import AttachedToMixin, FiltersMixin, ParentMixin
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['BasePageListView', 'BasePageDetailView', 'PageDetailView', 'PageListView']
|
__all__ = ['BasePageListView', 'BasePageDetailView', 'PageDetailView', 'PageListView']
|
||||||
@ -71,50 +72,32 @@ class BasePageDetailView(BaseView, DetailView):
|
|||||||
return self.object
|
return self.object
|
||||||
|
|
||||||
|
|
||||||
class PageListView(BasePageListView):
|
class PageListView(FiltersMixin, BasePageListView):
|
||||||
""" Page list view. """
|
""" Page list view. """
|
||||||
|
filterset_class = PageFilters
|
||||||
template_name = None
|
template_name = None
|
||||||
has_filters = True
|
has_filters = True
|
||||||
categories = None
|
categories = None
|
||||||
filters = None
|
filters = None
|
||||||
|
|
||||||
def get(self, *args, **kwargs):
|
|
||||||
self.categories = set(self.request.GET.getlist('categories'))
|
|
||||||
self.filters = set(self.request.GET.getlist('filters'))
|
|
||||||
return super().get(*args, **kwargs)
|
|
||||||
|
|
||||||
def get_template_names(self):
|
def get_template_names(self):
|
||||||
return super().get_template_names() + ['aircox/page_list.html']
|
return super().get_template_names() + ['aircox/page_list.html']
|
||||||
|
|
||||||
|
def get_filterset(self, data, query):
|
||||||
|
# FIXME: not the most efficient, cause join then split (in django filters)
|
||||||
|
data['category__id__in'] = ','.join(data.getlist('category__id__in'))
|
||||||
|
return super().get_filterset(data, query)
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
qs = super().get_queryset().select_related('category') \
|
qs = super().get_queryset().select_related('category') \
|
||||||
.order_by('-pub_date')
|
.order_by('-pub_date')
|
||||||
|
|
||||||
# category can be filtered based on request.GET['categories']
|
|
||||||
# (by id)
|
|
||||||
if self.categories:
|
|
||||||
qs = qs.filter(category__slug__in=self.categories)
|
|
||||||
return qs
|
return qs
|
||||||
|
|
||||||
def get_filters(self):
|
|
||||||
categories = self.model.objects.published() \
|
|
||||||
.filter(category__isnull=False) \
|
|
||||||
.values_list('category', flat=True)
|
|
||||||
categories = [ (c.title, c.slug, c.slug in self.categories)
|
|
||||||
for c in Category.objects.filter(id__in=categories) ]
|
|
||||||
return (
|
|
||||||
(_('Categories'), 'categories', categories),
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
if not 'filters' in kwargs:
|
kwargs['categories'] = self.model.objects.published() \
|
||||||
filters = self.get_filters()
|
.filter(category__isnull=False) \
|
||||||
for label, fieldName, choices in filters:
|
.values_list('category__title', 'category__id') \
|
||||||
if choices:
|
.distinct()
|
||||||
kwargs['filters'] = filters
|
|
||||||
break;
|
|
||||||
else:
|
|
||||||
kwargs['filters'] = tuple()
|
|
||||||
return super().get_context_data(**kwargs)
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
@ -151,6 +134,5 @@ class PageDetailView(BasePageDetailView):
|
|||||||
comment = form.save(commit=False)
|
comment = form.save(commit=False)
|
||||||
comment.page = self.object
|
comment.page = self.object
|
||||||
comment.save()
|
comment.save()
|
||||||
|
|
||||||
return self.get(request, *args, **kwargs)
|
return self.get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
@ -118,6 +118,8 @@ except:
|
|||||||
)
|
)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
#-- django-taggit
|
||||||
|
TAGGIT_CASE_INSENSITIVE = True
|
||||||
|
|
||||||
#-- django-CKEditor
|
#-- django-CKEditor
|
||||||
CKEDITOR_CONFIGS = {
|
CKEDITOR_CONFIGS = {
|
||||||
|
Reference in New Issue
Block a user