forked from rc/aircox
		
	start static pages
This commit is contained in:
		@ -1,6 +1,7 @@
 | 
				
			|||||||
from .article import ArticleAdmin
 | 
					from .article import ArticleAdmin
 | 
				
			||||||
from .episode import DiffusionAdmin, EpisodeAdmin
 | 
					from .episode import DiffusionAdmin, EpisodeAdmin
 | 
				
			||||||
from .log import LogAdmin
 | 
					from .log import LogAdmin
 | 
				
			||||||
 | 
					from .page import PageAdmin, StaticPageAdmin
 | 
				
			||||||
from .program import ProgramAdmin, ScheduleAdmin, StreamAdmin
 | 
					from .program import ProgramAdmin, ScheduleAdmin, StreamAdmin
 | 
				
			||||||
from .sound import SoundAdmin, TrackAdmin
 | 
					from .sound import SoundAdmin, TrackAdmin
 | 
				
			||||||
from .station import StationAdmin
 | 
					from .station import StationAdmin
 | 
				
			||||||
 | 
				
			|||||||
@ -11,8 +11,7 @@ __all__ = ['ArticleAdmin']
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
@admin.register(Article)
 | 
					@admin.register(Article)
 | 
				
			||||||
class ArticleAdmin(PageAdmin):
 | 
					class ArticleAdmin(PageAdmin):
 | 
				
			||||||
    list_filter = PageAdmin.list_filter
 | 
					    search_fields = PageAdmin.search_fields + ('parent__title',)
 | 
				
			||||||
    search_fields = PageAdmin.search_fields + ['parent__title']
 | 
					 | 
				
			||||||
    # TODO: readonly field
 | 
					    # TODO: readonly field
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -56,7 +56,7 @@ class EpisodeAdmin(PageAdmin):
 | 
				
			|||||||
    form = EpisodeAdminForm
 | 
					    form = EpisodeAdminForm
 | 
				
			||||||
    list_display = PageAdmin.list_display
 | 
					    list_display = PageAdmin.list_display
 | 
				
			||||||
    list_filter = PageAdmin.list_filter
 | 
					    list_filter = PageAdmin.list_filter
 | 
				
			||||||
    search_fields = PageAdmin.search_fields + ['parent__title']
 | 
					    search_fields = PageAdmin.search_fields + ('parent__title',)
 | 
				
			||||||
    # readonly_fields = ('parent',)
 | 
					    # readonly_fields = ('parent',)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    inlines = [TracksInline, SoundInline, DiffusionInline]
 | 
					    inlines = [TracksInline, SoundInline, DiffusionInline]
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
import urllib
 | 
					from copy import deepcopy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.contrib import admin
 | 
					from django.contrib import admin
 | 
				
			||||||
from django.http import QueryDict
 | 
					from django.http import QueryDict
 | 
				
			||||||
@ -7,7 +7,7 @@ from django.utils.translation import gettext_lazy as _
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from adminsortable2.admin import SortableInlineAdminMixin
 | 
					from adminsortable2.admin import SortableInlineAdminMixin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from ..models import Category, NavItem, Page
 | 
					from ..models import Category, NavItem, Page, StaticPage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
__all__ = ['CategoryAdmin', 'PageAdmin', 'NavItemInline']
 | 
					__all__ = ['CategoryAdmin', 'PageAdmin', 'NavItemInline']
 | 
				
			||||||
@ -22,25 +22,24 @@ class CategoryAdmin(admin.ModelAdmin):
 | 
				
			|||||||
    prepopulated_fields = {"slug": ("title",)}
 | 
					    prepopulated_fields = {"slug": ("title",)}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# limit category choice
 | 
					class BasePageAdmin(admin.ModelAdmin):
 | 
				
			||||||
class PageAdmin(admin.ModelAdmin):
 | 
					    list_display = ('cover_thumb', 'title', 'status', 'parent')
 | 
				
			||||||
    list_display = ('cover_thumb', 'title', 'status', 'category', 'parent')
 | 
					 | 
				
			||||||
    list_display_links = ('cover_thumb', 'title')
 | 
					    list_display_links = ('cover_thumb', 'title')
 | 
				
			||||||
    list_editable = ('status', 'category')
 | 
					    list_editable = ('status',)
 | 
				
			||||||
    list_filter = ('status', 'category')
 | 
					    list_filter = ('status',)
 | 
				
			||||||
    prepopulated_fields = {"slug": ("title",)}
 | 
					    prepopulated_fields = {"slug": ("title",)}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # prepopulate fields using changelist's filters
 | 
					    # prepopulate fields using changelist's filters
 | 
				
			||||||
    prepopulated_filters = ('parent',)
 | 
					    prepopulated_filters = ('parent',)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    search_fields = ['title', 'category__title']
 | 
					    search_fields = ('title',)
 | 
				
			||||||
    fieldsets = [
 | 
					    fieldsets = [
 | 
				
			||||||
        ('', {
 | 
					        ('', {
 | 
				
			||||||
            'fields': ['title', 'slug', 'category', 'cover', 'content'],
 | 
					            'fields': ['title', 'slug', 'cover', 'content'],
 | 
				
			||||||
        }),
 | 
					        }),
 | 
				
			||||||
        (_('Publication Settings'), {
 | 
					        (_('Publication Settings'), {
 | 
				
			||||||
            'fields': ['featured', 'allow_comments', 'status', 'parent'],
 | 
					            'fields': ['status', 'parent'],
 | 
				
			||||||
            'classes': ('collapse',),
 | 
					            # 'classes': ('collapse',),
 | 
				
			||||||
        }),
 | 
					        }),
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -59,10 +58,16 @@ class PageAdmin(admin.ModelAdmin):
 | 
				
			|||||||
    def get_common_context(self, query, extra_context=None):
 | 
					    def get_common_context(self, query, extra_context=None):
 | 
				
			||||||
        extra_context = extra_context or {}
 | 
					        extra_context = extra_context or {}
 | 
				
			||||||
        parent = query.get('parent', None)
 | 
					        parent = query.get('parent', None)
 | 
				
			||||||
        if parent is not None:
 | 
					        extra_context['parent'] = None if parent is None else \
 | 
				
			||||||
            extra_context['parent'] = Page.objects.get_subclass(id=parent)
 | 
					                                  Page.objects.get_subclass(id=parent)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return extra_context
 | 
					        return extra_context
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def render_change_form(self, request, context, *args, **kwargs):
 | 
				
			||||||
 | 
					        if context['original'] and not 'parent' in context:
 | 
				
			||||||
 | 
					            context['parent'] = context['original'].parent
 | 
				
			||||||
 | 
					        return super().render_change_form(request, context, *args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def add_view(self, request, form_url='', extra_context=None):
 | 
					    def add_view(self, request, form_url='', extra_context=None):
 | 
				
			||||||
        filters = QueryDict(request.GET.get('_changelist_filters', ''))
 | 
					        filters = QueryDict(request.GET.get('_changelist_filters', ''))
 | 
				
			||||||
        extra_context = self.get_common_context(filters, extra_context)
 | 
					        extra_context = self.get_common_context(filters, extra_context)
 | 
				
			||||||
@ -73,6 +78,27 @@ class PageAdmin(admin.ModelAdmin):
 | 
				
			|||||||
        return super().changelist_view(request, extra_context)
 | 
					        return super().changelist_view(request, extra_context)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PageAdmin(BasePageAdmin):
 | 
				
			||||||
 | 
					    list_display = BasePageAdmin.list_display + ('category',)
 | 
				
			||||||
 | 
					    list_editable = BasePageAdmin.list_editable + ('category',)
 | 
				
			||||||
 | 
					    list_filter = BasePageAdmin.list_editable + ('category',)
 | 
				
			||||||
 | 
					    search_fields = ('category__title',)
 | 
				
			||||||
 | 
					    fieldsets = deepcopy(BasePageAdmin.fieldsets)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fieldsets[0][1]['fields'].insert(fieldsets[0][1]['fields'].index('slug') + 1, 'category')
 | 
				
			||||||
 | 
					    fieldsets[1][1]['fields'] += ('featured', 'allow_comments')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@admin.register(StaticPage)
 | 
				
			||||||
 | 
					class StaticPageAdmin(BasePageAdmin):
 | 
				
			||||||
 | 
					    list_display = BasePageAdmin.list_display + ('view','menu_title')
 | 
				
			||||||
 | 
					    list_editable = BasePageAdmin.list_editable + ('menu_title',)
 | 
				
			||||||
 | 
					    fieldsets = deepcopy(BasePageAdmin.fieldsets)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fieldsets[0][1]['fields'].insert(fieldsets[0][1]['fields'].index('slug') + 1, 'menu_title')
 | 
				
			||||||
 | 
					    fieldsets[1][1]['fields'] += ('view',)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class NavItemInline(SortableInlineAdminMixin, admin.TabularInline):
 | 
					class NavItemInline(SortableInlineAdminMixin, admin.TabularInline):
 | 
				
			||||||
    model = NavItem
 | 
					    model = NavItem
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
from .article import Article
 | 
					from .article import Article
 | 
				
			||||||
from .page import Category, Page, Comment, NavItem
 | 
					from .page import Category, Page, StaticPage, Comment, NavItem
 | 
				
			||||||
from .program import Program, Stream, Schedule
 | 
					from .program import Program, Stream, Schedule
 | 
				
			||||||
from .episode import Episode, Diffusion
 | 
					from .episode import Episode, Diffusion
 | 
				
			||||||
from .log import Log
 | 
					from .log import Log
 | 
				
			||||||
 | 
				
			|||||||
@ -29,7 +29,7 @@ class LogQuerySet(models.QuerySet):
 | 
				
			|||||||
        start = tz.datetime.combine(date, datetime.time())
 | 
					        start = tz.datetime.combine(date, datetime.time())
 | 
				
			||||||
        end = tz.datetime.combine(date, datetime.time(23, 59, 59, 999))
 | 
					        end = tz.datetime.combine(date, datetime.time(23, 59, 59, 999))
 | 
				
			||||||
        return self.filter(date__range = (start, end))
 | 
					        return self.filter(date__range = (start, end))
 | 
				
			||||||
        # this filter does not work with sql
 | 
					        # this filter does not work with mysql
 | 
				
			||||||
        # return self.filter(date__date=date)
 | 
					        # return self.filter(date__date=date)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def after(self, date):
 | 
					    def after(self, date):
 | 
				
			||||||
 | 
				
			|||||||
@ -54,7 +54,7 @@ class PageQuerySet(InheritanceQuerySet):
 | 
				
			|||||||
               self.filter(parent__id=id)
 | 
					               self.filter(parent__id=id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Page(models.Model):
 | 
					class BasePage(models.Model):
 | 
				
			||||||
    """ Base class for publishable content """
 | 
					    """ Base class for publishable content """
 | 
				
			||||||
    STATUS_DRAFT = 0x00
 | 
					    STATUS_DRAFT = 0x00
 | 
				
			||||||
    STATUS_PUBLISHED = 0x10
 | 
					    STATUS_PUBLISHED = 0x10
 | 
				
			||||||
@ -72,30 +72,22 @@ class Page(models.Model):
 | 
				
			|||||||
    status = models.PositiveSmallIntegerField(
 | 
					    status = models.PositiveSmallIntegerField(
 | 
				
			||||||
        _('status'), default=STATUS_DRAFT, choices=STATUS_CHOICES,
 | 
					        _('status'), default=STATUS_DRAFT, choices=STATUS_CHOICES,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    category = models.ForeignKey(
 | 
					 | 
				
			||||||
        Category, models.SET_NULL,
 | 
					 | 
				
			||||||
        verbose_name=_('category'), blank=True, null=True, db_index=True
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    cover = FilerImageField(
 | 
					    cover = FilerImageField(
 | 
				
			||||||
        on_delete=models.SET_NULL,
 | 
					        on_delete=models.SET_NULL,
 | 
				
			||||||
        verbose_name=_('Cover'), null=True, blank=True,
 | 
					        verbose_name=_('cover'), null=True, blank=True,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    content = RichTextField(
 | 
					    content = RichTextField(
 | 
				
			||||||
        _('content'), blank=True, null=True,
 | 
					        _('content'), blank=True, null=True,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    pub_date = models.DateTimeField(blank=True, null=True)
 | 
					 | 
				
			||||||
    featured = models.BooleanField(
 | 
					 | 
				
			||||||
        _('featured'), default=False,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    allow_comments = models.BooleanField(
 | 
					 | 
				
			||||||
        _('allow comments'), default=True,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    objects = PageQuerySet.as_manager()
 | 
					    objects = PageQuerySet.as_manager()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    detail_url_name = None
 | 
					    detail_url_name = None
 | 
				
			||||||
    item_template_name = 'aircox/widgets/page_item.html'
 | 
					    item_template_name = 'aircox/widgets/page_item.html'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        abstract = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __str__(self):
 | 
					    def __str__(self):
 | 
				
			||||||
        return '{}'.format(self.title or self.pk)
 | 
					        return '{}'.format(self.title or self.pk)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -106,10 +98,6 @@ class Page(models.Model):
 | 
				
			|||||||
            count = Page.objects.filter(slug__startswith=self.slug).count()
 | 
					            count = Page.objects.filter(slug__startswith=self.slug).count()
 | 
				
			||||||
            if count:
 | 
					            if count:
 | 
				
			||||||
                self.slug += '-' + str(count)
 | 
					                self.slug += '-' + str(count)
 | 
				
			||||||
        if self.is_published and self.pub_date is None:
 | 
					 | 
				
			||||||
            self.pub_date = tz.now()
 | 
					 | 
				
			||||||
        elif not self.is_published:
 | 
					 | 
				
			||||||
            self.pub_date = None
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if not self.cover and self.parent:
 | 
					        if not self.cover and self.parent:
 | 
				
			||||||
            self.cover = self.parent.cover
 | 
					            self.cover = self.parent.cover
 | 
				
			||||||
@ -150,9 +138,71 @@ class Page(models.Model):
 | 
				
			|||||||
        return cls(**cls.get_init_kwargs_from(page, **kwargs))
 | 
					        return cls(**cls.get_init_kwargs_from(page, **kwargs))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Page(BasePage):
 | 
				
			||||||
 | 
					    """ Base Page model used for articles and other dated content. """
 | 
				
			||||||
 | 
					    category = models.ForeignKey(
 | 
				
			||||||
 | 
					        Category, models.SET_NULL,
 | 
				
			||||||
 | 
					        verbose_name=_('category'), blank=True, null=True, db_index=True
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    pub_date = models.DateTimeField(blank=True, null=True)
 | 
				
			||||||
 | 
					    featured = models.BooleanField(
 | 
				
			||||||
 | 
					        _('featured'), default=False,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    allow_comments = models.BooleanField(
 | 
				
			||||||
 | 
					        _('allow comments'), default=True,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def save(self, *args, **kwargs):
 | 
				
			||||||
 | 
					        if self.is_published and self.pub_date is None:
 | 
				
			||||||
 | 
					            self.pub_date = tz.now()
 | 
				
			||||||
 | 
					        elif not self.is_published:
 | 
				
			||||||
 | 
					            self.pub_date = None
 | 
				
			||||||
 | 
					        super().save(*args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class StaticPage(Page):
 | 
				
			||||||
 | 
					    """ Static page that eventually can be attached to a specific view. """
 | 
				
			||||||
 | 
					    VIEW_HOME = 0x00
 | 
				
			||||||
 | 
					    VIEW_SCHEDULE = 0x01
 | 
				
			||||||
 | 
					    VIEW_LOG = 0x02
 | 
				
			||||||
 | 
					    VIEW_PROGRAMS = 0x03
 | 
				
			||||||
 | 
					    VIEW_EPISODES = 0x04
 | 
				
			||||||
 | 
					    VIEW_ARTICLES = 0x05
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    VIEW_CHOICES = (
 | 
				
			||||||
 | 
					        (VIEW_HOME, _('Home Page')),
 | 
				
			||||||
 | 
					        (VIEW_SCHEDULE, _('Schedule Page')),
 | 
				
			||||||
 | 
					        (VIEW_LOG, _('Log Page')),
 | 
				
			||||||
 | 
					        (VIEW_PROGRAMS, _('Programs list')),
 | 
				
			||||||
 | 
					        (VIEW_EPISODES, _('Episodes list')),
 | 
				
			||||||
 | 
					        (VIEW_ARTICLES, _('Articles list')),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    view = models.SmallIntegerField(
 | 
				
			||||||
 | 
					        _('attach to'), choices=VIEW_CHOICES, blank=True, null=True,
 | 
				
			||||||
 | 
					        help_text=_('display this page content to related element'),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    menu_title = models.CharField(_('menu title'), max_length=64, blank=True, null=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    is_active = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def render_menu_item(self, request, css_class='', active_class=''):
 | 
				
			||||||
 | 
					        if active_class and request.path.startswith(self.url):
 | 
				
			||||||
 | 
					            css_class += ' ' + active_class
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not self.url:
 | 
				
			||||||
 | 
					            return self.text
 | 
				
			||||||
 | 
					        elif not css_class:
 | 
				
			||||||
 | 
					            return format_html('<a href="{}">{}</a>', self.url, self.text)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            return format_html('<a href="{}" class="{}">{}</a>', self.url,
 | 
				
			||||||
 | 
					                               css_class, self.text)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Comment(models.Model):
 | 
					class Comment(models.Model):
 | 
				
			||||||
    page = models.ForeignKey(
 | 
					    page = models.ForeignKey(
 | 
				
			||||||
        Page, models.CASCADE, verbose_name=_('related page'),
 | 
					        Page, models.CASCADE, verbose_name=_('related page'),
 | 
				
			||||||
 | 
					        # TODO: allow_comment filter
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    nickname = models.CharField(_('nickname'), max_length=32)
 | 
					    nickname = models.CharField(_('nickname'), max_length=32)
 | 
				
			||||||
    email = models.EmailField(_('email'), max_length=32)
 | 
					    email = models.EmailField(_('email'), max_length=32)
 | 
				
			||||||
 | 
				
			|||||||
@ -8,11 +8,9 @@
 | 
				
			|||||||
    › <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
 | 
					    › <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
 | 
				
			||||||
    › {% if has_view_permission %}<a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>{% else %}{{ opts.verbose_name_plural|capfirst }}{% endif %}
 | 
					    › {% if has_view_permission %}<a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>{% else %}{{ opts.verbose_name_plural|capfirst }}{% endif %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    {% with parent=parent|default:original.parent %}
 | 
					 | 
				
			||||||
    {% if parent %}
 | 
					    {% if parent %}
 | 
				
			||||||
    › <a href="{% url opts|admin_urlname:"changelist" %}?parent={{parent.id}}">{{ parent.title }}</a>
 | 
					    › <a href="{% url opts|admin_urlname:"changelist" %}?parent={{parent.id}}">{{ parent.title }}</a>
 | 
				
			||||||
    {% endif %}
 | 
					    {% endif %}
 | 
				
			||||||
    {% endwith %}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    › {% if add %}{% blocktrans with name=opts.verbose_name %}Add {{ name }}{% endblocktrans %}{% else %}{{ original|truncatewords:"18" }}{% endif %}
 | 
					    › {% if add %}{% blocktrans with name=opts.verbose_name %}Add {{ name }}{% endblocktrans %}{% else %}{{ original|truncatewords:"18" }}{% endif %}
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
				
			|||||||
@ -60,6 +60,12 @@ urls = [
 | 
				
			|||||||
    path(_('publications/'),
 | 
					    path(_('publications/'),
 | 
				
			||||||
         views.PageListView.as_view(model=models.Page), name='page-list'),
 | 
					         views.PageListView.as_view(model=models.Page), name='page-list'),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    path(_('pages/'),
 | 
				
			||||||
 | 
					         views.PageListView.as_view(model=models.StaticPage), name='static-page-list'),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    path(_('pages/<slug:slug>/'),
 | 
				
			||||||
 | 
					         views.PageDetailView.as_view(), name='page-detail'),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    path(_('programs/'), views.ProgramListView.as_view(model=models.Program),
 | 
					    path(_('programs/'), views.ProgramListView.as_view(model=models.Program),
 | 
				
			||||||
         name='program-list'),
 | 
					         name='program-list'),
 | 
				
			||||||
    path(_('programs/<slug:slug>/'),
 | 
					    path(_('programs/<slug:slug>/'),
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user