navigation & breadcrumbs
This commit is contained in:
parent
eaa1e2412a
commit
46a9008cda
|
@ -18,6 +18,7 @@ class EpisodeQuerySet(ProgramChildQuerySet):
|
|||
class Episode(Page):
|
||||
objects = EpisodeQuerySet.as_manager()
|
||||
detail_url_name = "episode-detail"
|
||||
list_url_name = "episode-list"
|
||||
template_prefix = "episode"
|
||||
|
||||
@property
|
||||
|
|
|
@ -139,7 +139,9 @@ class BasePage(Renderable, models.Model):
|
|||
super().save(*args, **kwargs)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse(self.detail_url_name, kwargs={"slug": self.slug}) if self.is_published else "#"
|
||||
if self.is_published:
|
||||
return reverse(self.detail_url_name, kwargs={"slug": self.slug})
|
||||
return ""
|
||||
|
||||
@property
|
||||
def is_draft(self):
|
||||
|
@ -216,6 +218,8 @@ class Page(BasePage):
|
|||
)
|
||||
|
||||
objects = PageQuerySet.as_manager()
|
||||
detail_url_name = ""
|
||||
list_url_name = "page-list"
|
||||
|
||||
@cached_property
|
||||
def parent_subclass(self):
|
||||
|
@ -223,6 +227,15 @@ class Page(BasePage):
|
|||
return Page.objects.get_subclass(id=self.parent_id)
|
||||
return None
|
||||
|
||||
def get_absolute_url(self):
|
||||
if not self.is_published and self.parent_subclass:
|
||||
return self.parent_subclass.get_absolute_url()
|
||||
return super().get_absolute_url()
|
||||
|
||||
@classmethod
|
||||
def get_list_url(cls, kwargs={}):
|
||||
return reverse(cls.list_url_name, kwargs=kwargs)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Publication")
|
||||
verbose_name_plural = _("Publications")
|
||||
|
@ -243,46 +256,32 @@ class StaticPage(BasePage):
|
|||
|
||||
detail_url_name = "static-page-detail"
|
||||
|
||||
ATTACH_TO_HOME = 0x00
|
||||
ATTACH_TO_DIFFUSIONS = 0x01
|
||||
ATTACH_TO_LOGS = 0x02
|
||||
ATTACH_TO_PROGRAMS = 0x03
|
||||
ATTACH_TO_EPISODES = 0x04
|
||||
ATTACH_TO_ARTICLES = 0x05
|
||||
ATTACH_TO_PAGES = 0x06
|
||||
ATTACH_TO_PODCASTS = 0x07
|
||||
class Target(models.TextChoices):
|
||||
HOME = "", _("Home Page")
|
||||
TIMETABLE = "timetable-list", _("Timetable")
|
||||
PROGRAMS = "program-list", _("Programs list")
|
||||
EPISODES = "episode-list", _("Episodes list")
|
||||
ARTICLES = "article-list", _("Articles list")
|
||||
PAGES = "page-list", _("Publications list")
|
||||
PODCASTS = "podcast-list", _("Podcasts list")
|
||||
|
||||
ATTACH_TO_CHOICES = (
|
||||
(ATTACH_TO_HOME, _("Home page")),
|
||||
(ATTACH_TO_DIFFUSIONS, _("Diffusions page")),
|
||||
(ATTACH_TO_LOGS, _("Logs page")),
|
||||
(ATTACH_TO_PROGRAMS, _("Programs list")),
|
||||
(ATTACH_TO_EPISODES, _("Episodes list")),
|
||||
(ATTACH_TO_ARTICLES, _("Articles list")),
|
||||
(ATTACH_TO_PAGES, _("Publications list")),
|
||||
(ATTACH_TO_PODCASTS, _("Podcasts list")),
|
||||
)
|
||||
VIEWS = {
|
||||
ATTACH_TO_HOME: "home",
|
||||
ATTACH_TO_DIFFUSIONS: "diffusion-list",
|
||||
ATTACH_TO_LOGS: "log-list",
|
||||
ATTACH_TO_PROGRAMS: "program-list",
|
||||
ATTACH_TO_EPISODES: "episode-list",
|
||||
ATTACH_TO_ARTICLES: "article-list",
|
||||
ATTACH_TO_PAGES: "page-list",
|
||||
}
|
||||
|
||||
attach_to = models.SmallIntegerField(
|
||||
attach_to = models.CharField(
|
||||
_("attach to"),
|
||||
choices=ATTACH_TO_CHOICES,
|
||||
choices=Target.choices,
|
||||
max_length=32,
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text=_("display this page content to related element"),
|
||||
)
|
||||
|
||||
def get_related_view(self):
|
||||
from ..views import attached
|
||||
|
||||
return self.attach_to and attached.get(self.attach_to) or None
|
||||
|
||||
def get_absolute_url(self):
|
||||
if self.attach_to:
|
||||
return reverse(self.VIEWS[self.attach_to])
|
||||
return reverse(self.attach_to)
|
||||
return super().get_absolute_url()
|
||||
|
||||
|
||||
|
@ -320,7 +319,7 @@ class NavItem(models.Model):
|
|||
station = models.ForeignKey(Station, models.CASCADE, verbose_name=_("station"))
|
||||
menu = models.SlugField(_("menu"), max_length=24)
|
||||
order = models.PositiveSmallIntegerField(_("order"))
|
||||
text = models.CharField(_("title"), max_length=64)
|
||||
text = models.CharField(_("title"), max_length=64, blank=True, null=True)
|
||||
url = models.CharField(_("url"), max_length=256, blank=True, null=True)
|
||||
page = models.ForeignKey(
|
||||
StaticPage,
|
||||
|
@ -339,14 +338,21 @@ class NavItem(models.Model):
|
|||
def get_url(self):
|
||||
return self.url if self.url else self.page.get_absolute_url() if self.page else None
|
||||
|
||||
def get_label(self):
|
||||
if self.text:
|
||||
return self.text
|
||||
elif self.page:
|
||||
return self.page.title
|
||||
|
||||
def render(self, request, css_class="", active_class=""):
|
||||
url = self.get_url()
|
||||
text = self.get_text()
|
||||
if active_class and request.path.startswith(url):
|
||||
css_class += " " + active_class
|
||||
|
||||
if not url:
|
||||
return self.text
|
||||
return text
|
||||
elif not css_class:
|
||||
return format_html('<a href="{}">{}</a>', url, self.text)
|
||||
return format_html('<a href="{}">{}</a>', url, text)
|
||||
else:
|
||||
return format_html('<a href="{}" class="{}">{}</a>', url, css_class, self.text)
|
||||
return format_html('<a href="{}" class="{}">{}</a>', url, css_class, text)
|
||||
|
|
|
@ -61,6 +61,7 @@ class Program(Page):
|
|||
|
||||
objects = ProgramQuerySet.as_manager()
|
||||
detail_url_name = "program-detail"
|
||||
list_url_name = "program-list"
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
|
|
|
@ -33,9 +33,9 @@
|
|||
--a-player-panel-bg: var(--highlight-color);
|
||||
--a-player-bar-bg: var(--highlight-color);
|
||||
--a-player-bar-title-alone-sz: 1.6rem;
|
||||
--button-fg: var(--text-color);
|
||||
--button-fg: var(--highlight-color-2);
|
||||
--button-bg: var(--highlight-color);
|
||||
--button-hg-fg: var(--highlight-color-2);
|
||||
--button-hg-fg: var(--text-color);
|
||||
--button-hg-bg: var(--highlight-color);
|
||||
--button-active-fg: var(--highlight-color);
|
||||
--button-active-bg: var(--highlight-color-2);
|
||||
|
@ -234,6 +234,14 @@
|
|||
height: var(--preview-cover-size);
|
||||
width: var(--preview-cover-size);
|
||||
}
|
||||
.preview-card.small {
|
||||
height: var(--preview-cover-small-size);
|
||||
width: var(--preview-cover-small-size);
|
||||
}
|
||||
.preview-card.tiny {
|
||||
height: var(--preview-cover-tiny-size);
|
||||
width: var(--preview-cover-tiny-size);
|
||||
}
|
||||
.preview-card:not(.header) {
|
||||
box-shadow: 0em 0em 1em rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
@ -259,57 +267,6 @@ preview-header:not(.no-cover) .card-headings .heading, preview-header:not(.no-co
|
|||
margin-bottom: 0.6rem;
|
||||
}
|
||||
|
||||
.header.preview-header {
|
||||
align-items: start;
|
||||
gap: 0.6rem;
|
||||
min-height: unset;
|
||||
padding-top: 0.6rem !important;
|
||||
}
|
||||
.header .headings {
|
||||
width: unset;
|
||||
flex-grow: 1;
|
||||
padding-top: 0 !important;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.header.has-cover {
|
||||
min-height: calc(var(--header-height) / 2);
|
||||
}
|
||||
.header .title {
|
||||
font-size: 40px;
|
||||
}
|
||||
.header .subtitle {
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.header-cover:not(:only-child) {
|
||||
float: right;
|
||||
height: var(--header-height);
|
||||
max-width: calc(var(--header-height) * 2);
|
||||
margin: 0 0 1.2rem 1.2rem;
|
||||
}
|
||||
|
||||
.header-cover:only-child {
|
||||
with: 100%;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
.container.header {
|
||||
width: 100%;
|
||||
}
|
||||
.container.header .headings {
|
||||
width: 100%;
|
||||
clear: both;
|
||||
}
|
||||
.container.header .header-cover {
|
||||
float: none;
|
||||
width: 100%;
|
||||
max-width: unset;
|
||||
height: unset;
|
||||
margin-left: 0rem;
|
||||
margin-right: 0rem;
|
||||
}
|
||||
}
|
||||
.card-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
|
@ -430,6 +387,12 @@ preview-header:not(.no-cover) .card-headings .heading, preview-header:not(.no-co
|
|||
.a-player a {
|
||||
color: var(--a-player-url-fg);
|
||||
}
|
||||
.a-player .button {
|
||||
color: var(--text-black);
|
||||
}
|
||||
.a-player .button:hover {
|
||||
color: var(--button-fg);
|
||||
}
|
||||
|
||||
.a-player-panels {
|
||||
background: var(--a-player-panel-bg);
|
||||
|
@ -2719,12 +2682,12 @@ a.navbar-item:focus, a.navbar-item:focus-within, a.navbar-item:hover, a.navbar-i
|
|||
|
||||
#player .button, #player a.button, #player button.button, #player .nav-urls a, .ax .button, .ax a.button, .ax button.button, .ax .nav-urls a {
|
||||
display: inline-block;
|
||||
padding: 0.6em;
|
||||
border-radius: 4px;
|
||||
padding: 0.4em;
|
||||
border: 1px var(--highlight-color-2-alpha) solid;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
font-size: 1.4rem;
|
||||
color: var(--button-fg);
|
||||
background-color: var(--button-bg);
|
||||
}
|
||||
#player .button .icon, #player a.button .icon, #player button.button .icon, #player .nav-urls a .icon, .ax .button .icon, .ax a.button .icon, .ax button.button .icon, .ax .nav-urls a .icon {
|
||||
|
|
|
@ -33,9 +33,9 @@
|
|||
--a-player-panel-bg: var(--highlight-color);
|
||||
--a-player-bar-bg: var(--highlight-color);
|
||||
--a-player-bar-title-alone-sz: 1.6rem;
|
||||
--button-fg: var(--text-color);
|
||||
--button-fg: var(--highlight-color-2);
|
||||
--button-bg: var(--highlight-color);
|
||||
--button-hg-fg: var(--highlight-color-2);
|
||||
--button-hg-fg: var(--text-color);
|
||||
--button-hg-bg: var(--highlight-color);
|
||||
--button-active-fg: var(--highlight-color);
|
||||
--button-active-bg: var(--highlight-color-2);
|
||||
|
@ -234,6 +234,14 @@
|
|||
height: var(--preview-cover-size);
|
||||
width: var(--preview-cover-size);
|
||||
}
|
||||
.preview-card.small {
|
||||
height: var(--preview-cover-small-size);
|
||||
width: var(--preview-cover-small-size);
|
||||
}
|
||||
.preview-card.tiny {
|
||||
height: var(--preview-cover-tiny-size);
|
||||
width: var(--preview-cover-tiny-size);
|
||||
}
|
||||
.preview-card:not(.header) {
|
||||
box-shadow: 0em 0em 1em rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
@ -259,57 +267,6 @@ preview-header:not(.no-cover) .card-headings .heading, preview-header:not(.no-co
|
|||
margin-bottom: 0.6rem;
|
||||
}
|
||||
|
||||
.header.preview-header {
|
||||
align-items: start;
|
||||
gap: 0.6rem;
|
||||
min-height: unset;
|
||||
padding-top: 0.6rem !important;
|
||||
}
|
||||
.header .headings {
|
||||
width: unset;
|
||||
flex-grow: 1;
|
||||
padding-top: 0 !important;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.header.has-cover {
|
||||
min-height: calc(var(--header-height) / 2);
|
||||
}
|
||||
.header .title {
|
||||
font-size: 40px;
|
||||
}
|
||||
.header .subtitle {
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.header-cover:not(:only-child) {
|
||||
float: right;
|
||||
height: var(--header-height);
|
||||
max-width: calc(var(--header-height) * 2);
|
||||
margin: 0 0 1.2rem 1.2rem;
|
||||
}
|
||||
|
||||
.header-cover:only-child {
|
||||
with: 100%;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
.container.header {
|
||||
width: 100%;
|
||||
}
|
||||
.container.header .headings {
|
||||
width: 100%;
|
||||
clear: both;
|
||||
}
|
||||
.container.header .header-cover {
|
||||
float: none;
|
||||
width: 100%;
|
||||
max-width: unset;
|
||||
height: unset;
|
||||
margin-left: 0rem;
|
||||
margin-right: 0rem;
|
||||
}
|
||||
}
|
||||
.card-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
|
@ -430,6 +387,12 @@ preview-header:not(.no-cover) .card-headings .heading, preview-header:not(.no-co
|
|||
.a-player a {
|
||||
color: var(--a-player-url-fg);
|
||||
}
|
||||
.a-player .button {
|
||||
color: var(--text-black);
|
||||
}
|
||||
.a-player .button:hover {
|
||||
color: var(--button-fg);
|
||||
}
|
||||
|
||||
.a-player-panels {
|
||||
background: var(--a-player-panel-bg);
|
||||
|
@ -7381,6 +7344,9 @@ a.tag:hover {
|
|||
text-decoration: none;
|
||||
padding: 0.4rem;
|
||||
}
|
||||
.page a:hover {
|
||||
color: var(--text-color);
|
||||
}
|
||||
.page section.container {
|
||||
padding-top: 2rem;
|
||||
}
|
||||
|
@ -7419,12 +7385,12 @@ a.tag:hover {
|
|||
|
||||
.button, a.button, button.button, .nav-urls a {
|
||||
display: inline-block;
|
||||
padding: 0.6em;
|
||||
border-radius: 4px;
|
||||
padding: 0.4em;
|
||||
border: 1px var(--highlight-color-2-alpha) solid;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
font-size: 1.4rem;
|
||||
color: var(--button-fg);
|
||||
background-color: var(--button-bg);
|
||||
}
|
||||
.button .icon, a.button .icon, button.button .icon, .nav-urls a .icon {
|
||||
|
@ -7487,7 +7453,6 @@ a.tag:hover {
|
|||
display: none;
|
||||
}
|
||||
.actions button, .actions .action {
|
||||
background-color: var(--highlight-color);
|
||||
justify-content: center;
|
||||
min-width: 2rem;
|
||||
}
|
||||
|
@ -7500,9 +7465,6 @@ a.tag:hover {
|
|||
.actions button label, .actions .action label {
|
||||
margin-left: 0.4rem;
|
||||
}
|
||||
.actions button:hover, .actions button .selected, .actions .action:hover, .actions .action .selected {
|
||||
color: var(--highlight-color-2) !important;
|
||||
}
|
||||
|
||||
.label, .textarea, .input, .select {
|
||||
font-size: 1.4rem;
|
||||
|
@ -7520,6 +7482,10 @@ a.tag:hover {
|
|||
margin-top: 0.6rem;
|
||||
}
|
||||
|
||||
.navs {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.nav {
|
||||
display: flex;
|
||||
background-color: var(--highlight-color);
|
||||
|
@ -7547,7 +7513,7 @@ a.tag:hover {
|
|||
vertical-align: top;
|
||||
display: inline-block;
|
||||
}
|
||||
.nav .nav-item.active {
|
||||
.nav .nav-item.active, .nav .nav-item:hover {
|
||||
background-color: var(--highlight-color-2);
|
||||
color: var(--highlight-color);
|
||||
}
|
||||
|
@ -7581,12 +7547,32 @@ a.tag:hover {
|
|||
white-space: nowrap;
|
||||
}
|
||||
.nav.secondary {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
z-index: 100;
|
||||
box-shadow: 0em 0.5em 0.5em rgba(0, 0, 0, 0.05);
|
||||
justify-content: right;
|
||||
display: none;
|
||||
}
|
||||
.nav-item:hover + .nav.secondary, .nav.secondary:hover {
|
||||
display: flex;
|
||||
top: var(--nav-primary-height);
|
||||
left: 0rem;
|
||||
}
|
||||
.nav.secondary .nav-item {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.breadcrumbs {
|
||||
text-align: right;
|
||||
height: 2.2rem;
|
||||
margin-bottom: 0.4rem;
|
||||
}
|
||||
.breadcrumbs a + a:before {
|
||||
content: "/";
|
||||
margin: 0 0.4rem;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1024px) {
|
||||
.page {
|
||||
margin-top: var(--nav-primary-height);
|
||||
|
@ -7685,7 +7671,7 @@ nav li a, nav li .button {
|
|||
flex-direction: column;
|
||||
}
|
||||
.header.has-cover {
|
||||
min-height: calc(var(--header-height) / 2);
|
||||
min-height: calc(var(--header-height) / 3);
|
||||
}
|
||||
.header .title {
|
||||
font-size: 40px;
|
||||
|
|
|
@ -375,7 +375,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac
|
|||
\**********************/
|
||||
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
|
||||
|
||||
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _fortawesome_fontawesome_free_css_all_min_css__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @fortawesome/fontawesome-free/css/all.min.css */ \"./node_modules/@fortawesome/fontawesome-free/css/all.min.css\");\n/* harmony import */ var _app__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./app */ \"./src/app.js\");\n/* harmony import */ var _vueLoader__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./vueLoader */ \"./src/vueLoader.js\");\n/* harmony import */ var _sound__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./sound */ \"./src/sound.js\");\n/* harmony import */ var _model__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./model */ \"./src/model.js\");\n/* harmony import */ var _assets_common_scss__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./assets/common.scss */ \"./src/assets/common.scss\");\n/**\n * This module includes code available for both the public website and\n * administration interface)\n */\n//-- vendor\n\n\n//-- aircox\n\n\n\n\n\nwindow.aircox = {\n // main application\n loader: null,\n get app() {\n return this.loader.app;\n },\n // player application\n playerLoader: null,\n get playerApp() {\n return this.playerLoader && this.playerLoader.app;\n },\n get player() {\n return this.playerLoader.vm && this.playerLoader.vm.$refs.player;\n },\n Set: _model__WEBPACK_IMPORTED_MODULE_4__.Set,\n Sound: _sound__WEBPACK_IMPORTED_MODULE_3__[\"default\"],\n /**\n * Initialize main application and player.\n */\n init(props = null, {\n hotReload = false,\n el = null,\n config = null,\n playerConfig = null,\n initApp = true,\n initPlayer = true,\n loader = null,\n playerLoader = null\n } = {}) {\n if (initPlayer) {\n playerConfig = playerConfig || _app__WEBPACK_IMPORTED_MODULE_1__.PlayerApp;\n playerLoader = playerLoader || new _vueLoader__WEBPACK_IMPORTED_MODULE_2__[\"default\"](playerConfig);\n playerLoader.enable(false);\n this.playerLoader = playerLoader;\n }\n if (initApp) {\n config = config || window.App || _app__WEBPACK_IMPORTED_MODULE_1__[\"default\"];\n config.el = el || config.el;\n loader = loader || new _vueLoader__WEBPACK_IMPORTED_MODULE_2__[\"default\"]({\n el,\n props,\n ...config\n });\n loader.enable(hotReload);\n this.loader = loader;\n }\n },\n /**\n * Filter navbar dropdown menu items\n */\n filter_menu(event) {\n var filter = new RegExp(event.target.value, 'gi');\n var container = event.target.closest('.navbar-dropdown');\n if (event.target.value) for (let item of container.querySelectorAll('a.navbar-item')) item.style.display = item.innerHTML.search(filter) == -1 ? 'none' : null;else for (let item of container.querySelectorAll('a.navbar-item')) item.style.display = null;\n },\n pickDate(url, date) {\n url = `${url}?date=${date.id}`;\n this.builder.fetch(url);\n }\n};\n\n//# sourceURL=webpack://aircox-assets/./src/index.js?");
|
||||
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _fortawesome_fontawesome_free_css_all_min_css__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @fortawesome/fontawesome-free/css/all.min.css */ \"./node_modules/@fortawesome/fontawesome-free/css/all.min.css\");\n/* harmony import */ var _app__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./app */ \"./src/app.js\");\n/* harmony import */ var _vueLoader__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./vueLoader */ \"./src/vueLoader.js\");\n/* harmony import */ var _sound__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./sound */ \"./src/sound.js\");\n/* harmony import */ var _model__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./model */ \"./src/model.js\");\n/* harmony import */ var _assets_common_scss__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./assets/common.scss */ \"./src/assets/common.scss\");\n/**\n * This module includes code available for both the public website and\n * administration interface)\n */\n//-- vendor\n\n\n//-- aircox\n\n\n\n\n\nwindow.aircox = {\n // main application\n loader: null,\n get app() {\n return this.loader.app;\n },\n // player application\n playerLoader: null,\n get playerApp() {\n return this.playerLoader && this.playerLoader.app;\n },\n get player() {\n return this.playerLoader.vm && this.playerLoader.vm.$refs.player;\n },\n Set: _model__WEBPACK_IMPORTED_MODULE_4__.Set,\n Sound: _sound__WEBPACK_IMPORTED_MODULE_3__[\"default\"],\n /**\n * Initialize main application and player.\n */\n init(props = null, {\n hotReload = false,\n el = null,\n config = null,\n playerConfig = null,\n initApp = true,\n initPlayer = true,\n loader = null,\n playerLoader = null\n } = {}) {\n if (initPlayer) {\n playerConfig = playerConfig || _app__WEBPACK_IMPORTED_MODULE_1__.PlayerApp;\n playerLoader = playerLoader || new _vueLoader__WEBPACK_IMPORTED_MODULE_2__[\"default\"](playerConfig);\n playerLoader.enable(false);\n this.playerLoader = playerLoader;\n }\n if (initApp) {\n config = config || window.App || _app__WEBPACK_IMPORTED_MODULE_1__[\"default\"];\n config.el = el || config.el;\n loader = loader || new _vueLoader__WEBPACK_IMPORTED_MODULE_2__[\"default\"]({\n el,\n props,\n ...config\n });\n loader.enable(hotReload);\n this.loader = loader;\n }\n },\n /**\n * Filter navbar dropdown menu items\n */\n filter_menu(event) {\n var filter = new RegExp(event.target.value, 'gi');\n var container = event.target.closest('.navbar-dropdown');\n if (event.target.value) for (let item of container.querySelectorAll('a.navbar-item')) item.style.display = item.innerHTML.search(filter) == -1 ? 'none' : null;else for (let item of container.querySelectorAll('a.navbar-item')) item.style.display = null;\n },\n pickDate(url, date) {\n url = `${url}?date=${date.id}`;\n this.loader.pageLoad.load(url);\n }\n};\n\n//# sourceURL=webpack://aircox-assets/./src/index.js?");
|
||||
|
||||
/***/ }),
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
{% if diffusions %}
|
||||
<div class="card-grid">
|
||||
{% for obj in diffusions %}
|
||||
{% page_widget "card" obj.episode diffusion=obj timetable=True admin=True tag_class="small" %}
|
||||
{% page_widget "card" obj.episode diffusion=obj timetable=True admin=True tag_class="" %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
|
|
|
@ -61,6 +61,22 @@ Usefull context:
|
|||
aria-label="{% translate "Main menu" %}">
|
||||
</a-switch>
|
||||
<div class="nav-menu">
|
||||
{% for nav_item, secondary in nav_menu %}
|
||||
<a class="nav-item" href="{{ nav_item.get_url }}">
|
||||
{{ nav_item.get_label }}
|
||||
</a>
|
||||
{% if secondary %}
|
||||
<div class="nav secondary">
|
||||
{% for label, url in secondary %}
|
||||
<a href="{{ url }}" class="nav-item">
|
||||
{{ label }}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% comment %}
|
||||
{% block nav-primary-menu %}
|
||||
{% nav_items "top" css_class="nav-item" active_class="active" as items %}
|
||||
{% for item, render in items %}
|
||||
|
@ -72,6 +88,7 @@ Usefull context:
|
|||
</a>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% endcomment %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
</nav>
|
||||
|
@ -79,6 +96,14 @@ Usefull context:
|
|||
{% block secondary-nav %}{% endblock %}
|
||||
</div>
|
||||
|
||||
{% spaceless %}
|
||||
{% block breadcrumbs-container %}
|
||||
<div class="breadcrumbs container">
|
||||
{% block breadcrumbs %}{% endblock %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endspaceless %}
|
||||
|
||||
{% block main-container %}
|
||||
<main class="page">
|
||||
{% block main %}
|
||||
|
@ -99,20 +124,6 @@ Usefull context:
|
|||
{% block subtitle %}
|
||||
{% if subtitle %}
|
||||
{{ subtitle }}
|
||||
{% elif parent and parent.is_published %}
|
||||
<a href="{{ parent.get_absolute_url|escape }}" class="heading subtitle">
|
||||
<span class="icon">
|
||||
<i class="fa fa-angles-right"></i>
|
||||
</span>
|
||||
{{ parent.title }}
|
||||
</a>
|
||||
{% elif page and page.category %}
|
||||
<a href="{% url "page-list" %}?category__id={{ page.category.id }}">
|
||||
<span class="icon">
|
||||
<i class="fa fa-angles-right"></i>
|
||||
</span>
|
||||
{{ page.category.title }}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
</span>
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
{% block head_title %}{{ station.name }}{% endblock %}
|
||||
|
||||
{% block breadcrumbs-container %}{% endblock %}
|
||||
|
||||
{% block content-container %}
|
||||
{{ block.super }}
|
||||
|
||||
|
@ -40,7 +42,7 @@
|
|||
{% endfor %}
|
||||
|
||||
<nav class="nav-urls">
|
||||
<a href="{% url "log-list" %}"
|
||||
<a href="{% url "timetable-list" %}"
|
||||
aria-label="{% translate "Show all program's for today" %}">
|
||||
{% translate "Today" %}
|
||||
</a>
|
||||
|
|
|
@ -23,6 +23,22 @@ Context:
|
|||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
|
||||
{% block breadcrumbs %}
|
||||
{% if parent %}
|
||||
{% include "./widgets/breadcrumbs.html" with page=parent %}
|
||||
{% if page %}
|
||||
<a href="{% url page.list_url_name parent_slug=parent.slug %}">
|
||||
{{ page|verbose_name:True }}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% elif page %}
|
||||
{% include "./widgets/breadcrumbs.html" with page=page no_title=True %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block main %}
|
||||
{{ block.super }}
|
||||
|
||||
|
@ -31,21 +47,9 @@ Context:
|
|||
<section class="container">
|
||||
{% with models=object|verbose_name:True %}
|
||||
<h3 class="title is-3">{% blocktranslate %}Related {{models}}{% endblocktranslate %}</h3>
|
||||
|
||||
{% include "./widgets/carousel.html" with objects=related_objects url_name=object.list_url_name url_category=object.category %}
|
||||
{% endwith %}
|
||||
|
||||
<a-carousel section-class="card-grid">
|
||||
{% for object in related_objects %}
|
||||
{% page_widget "card" object %}
|
||||
{% endfor %}
|
||||
</a-carousel>
|
||||
|
||||
{% if related_url %}
|
||||
<nav class="nav-urls">
|
||||
<a href="{{ related_url }}">
|
||||
{% translate "See more" %}
|
||||
</a>
|
||||
</nav>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -3,12 +3,14 @@
|
|||
{% load i18n aircox %}
|
||||
|
||||
{% block secondary-nav %}
|
||||
{% if view.categories %}
|
||||
{% if categories %}
|
||||
<nav class="nav secondary">
|
||||
<div class="nav-menu nav-categories">
|
||||
{% for id, title in view.categories.items %}
|
||||
<a class="nav-item{% if category_id == id or parent and parent.category_id == id %} active{% endif %}"
|
||||
href="?category__id={{ id }}">{{ title }}</a>
|
||||
{% for cat in categories %}
|
||||
<a class="nav-item{% if cat == category %} active{% endif %}"
|
||||
href="{% url request.resolver_match.url_name category_slug=cat.slug %}">
|
||||
{{ cat.title }}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<a-switch class="button burger"
|
||||
|
@ -19,6 +21,18 @@
|
|||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block title %}
|
||||
{% if parent %}{{ parent.title }}
|
||||
{% else %}{{ block.super }}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% if parent %}{{ parent.content|safe }}
|
||||
{% else %}{{ block.super }}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block header %}
|
||||
{% if page and not object %}
|
||||
{% with page as object %}
|
||||
|
@ -28,3 +42,19 @@
|
|||
{{ block.super }}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
{% if parent %}
|
||||
{% include "./widgets/breadcrumbs.html" with page=parent %}
|
||||
<a href="{{ request.path }}">{{ model|verbose_name:True }}</a>
|
||||
{% elif page %}
|
||||
<a href="{{ request.path }}">{{ page.title }}</a>
|
||||
{% else %}
|
||||
<a href="{% url request.resolver_match.url_name %}">{{ model|verbose_name:True }}</a>
|
||||
{% if category %}
|
||||
<a href="{% url request.resolver_match.url_name category_slug=category.slug %}">
|
||||
{{ category.title }}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
31
aircox/templates/aircox/timetable_list.html
Normal file
31
aircox/templates/aircox/timetable_list.html
Normal file
|
@ -0,0 +1,31 @@
|
|||
{% extends "aircox/page_list.html" %}
|
||||
{% comment %}List of diffusions as a timetable{% endcomment %}
|
||||
{% load i18n aircox humanize %}
|
||||
|
||||
{% block subtitle %}{{ date|date:"l d F Y" }}{% endblock %}
|
||||
|
||||
{% block secondary-nav %}
|
||||
<nav class="nav secondary">
|
||||
{% include "./widgets/dates_menu.html" with url_name=view.redirect_date_url %}
|
||||
</nav>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block breadcrumbs %}
|
||||
{{ block.super }}
|
||||
<a href="{% url "timetable-list" date=date %}">{{ date|date:"l d F Y" }}</a>
|
||||
{% endblock %}
|
||||
|
||||
{% block pages_list %}
|
||||
{% with hide_schedule=True %}
|
||||
<section role="list" class="list">
|
||||
{% for object in object_list %}
|
||||
{% if object.episode %}
|
||||
{% page_widget "item" object.episode diffusion=object timetable=True %}
|
||||
{% else %}
|
||||
{% page_widget "item" object timetable=True %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</section>
|
||||
{% endwith %}
|
||||
{% endblock %}
|
15
aircox/templates/aircox/widgets/breadcrumbs.html
Normal file
15
aircox/templates/aircox/widgets/breadcrumbs.html
Normal file
|
@ -0,0 +1,15 @@
|
|||
{% load aircox %}
|
||||
|
||||
<a href="{% url page.list_url_name %}">
|
||||
{{ page|verbose_name:True }}
|
||||
</a>
|
||||
{% if page.category and not no_cat %}
|
||||
<a href="{% url page.list_url_name category_slug=page.category.slug %}">
|
||||
{{ page.category.title }}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if not no_title %}
|
||||
<a href="{{ page.get_absolute_url }}">
|
||||
{{ page.title|truncatechars:24 }}
|
||||
</a>
|
||||
{% endif %}
|
|
@ -16,7 +16,9 @@ Context:
|
|||
{% if url_name %}
|
||||
<nav class="nav-urls">
|
||||
{% if url_parent %}
|
||||
<a href="{% url url_name parent_slug=program.slug %}">
|
||||
<a href="{% url url_name parent_slug=url_parent.slug %}">
|
||||
{% elif url_category %}
|
||||
<a href="{% url url_name category_slug=url_category.slug %}">
|
||||
{% else %}
|
||||
<a href="{% url url_name %}">
|
||||
{% endif %}
|
||||
|
|
|
@ -40,7 +40,8 @@ The audio player
|
|||
<h4 v-else-if="live && current && current.data.type == 'diffusion'"
|
||||
class="title"
|
||||
aria-description="{% translate "Diffusion currently on air" %}">
|
||||
<a :href="current.data.url">[[ current.data.title ]]</a>
|
||||
<a :href="current.data.url" v-if="current.data.url">[[ current.data.title ]]</a>
|
||||
<template v-else>[[ current.data.title ]]</template>
|
||||
</h4>
|
||||
<h4 v-else class="title is-4" aria-description="{% translate "Currently playing" %}">
|
||||
{{ request.station.name }}
|
||||
|
|
|
@ -38,38 +38,47 @@ api = [
|
|||
urls = [
|
||||
path("", views.HomeView.as_view(), name="home"),
|
||||
path("api/", include((api, "aircox"), namespace="api")),
|
||||
# path('', views.PageDetailView.as_view(model=models.Article),
|
||||
# name='home'),
|
||||
# ---- ---- objects views
|
||||
# ---- articles
|
||||
path(
|
||||
_("articles/"),
|
||||
views.ArticleListView.as_view(model=models.Article),
|
||||
name="article-list",
|
||||
),
|
||||
path(
|
||||
_("articles/c/<slug:category_slug>/"), views.ArticleListView.as_view(model=models.Article), name="article-list"
|
||||
),
|
||||
path(
|
||||
_("articles/<slug:slug>/"),
|
||||
views.ArticleDetailView.as_view(),
|
||||
name="article-detail",
|
||||
),
|
||||
# ---- episodes
|
||||
path(_("episodes/"), views.EpisodeListView.as_view(), name="episode-list"),
|
||||
path(_("episodes/c/<slug:category_slug>"), views.EpisodeListView.as_view(), name="episode-list"),
|
||||
path(
|
||||
_("episodes/<slug:slug>/"),
|
||||
views.EpisodeDetailView.as_view(),
|
||||
name="episode-detail",
|
||||
),
|
||||
path(_("podcasts/"), views.PodcastListView.as_view(), name="podcast-list"),
|
||||
path(_("podcasts/<slug:parent_slug>"), views.PodcastListView.as_view(), name="podcast-list"),
|
||||
path(_("week/"), views.DiffusionListView.as_view(), name="diffusion-list"),
|
||||
path(_("podcasts/c/<slug:category_slug>/"), views.PodcastListView.as_view(), name="podcast-list"),
|
||||
# ---- timetable
|
||||
path(_("timetable/"), views.TimeTableView.as_view(), name="timetable-list"),
|
||||
path(
|
||||
_("week/<date:date>/"),
|
||||
views.DiffusionListView.as_view(),
|
||||
name="diffusion-list",
|
||||
_("timetable/<date:date>/"),
|
||||
views.TimeTableView.as_view(),
|
||||
name="timetable-list",
|
||||
),
|
||||
path(_("logs/"), views.LogListView.as_view(), name="log-list"),
|
||||
path(_("logs/<date:date>/"), views.LogListView.as_view(), name="log-list"),
|
||||
# path('<page_path:path>', views.route_page, name='page'),
|
||||
# ---- pages
|
||||
path(
|
||||
_("publications/"),
|
||||
views.PageListView.as_view(model=models.Page, attach_to_value=models.StaticPage.ATTACH_TO_PAGES),
|
||||
views.PageListView.as_view(model=models.Page, attach_to_value=models.StaticPage.Target.PAGES),
|
||||
name="page-list",
|
||||
),
|
||||
path(
|
||||
_("publications/c/<slug:category_slug>"),
|
||||
views.PageListView.as_view(model=models.Page, attach_to_value=models.StaticPage.Target.PAGES),
|
||||
name="page-list",
|
||||
),
|
||||
path(
|
||||
|
@ -88,27 +97,18 @@ urls = [
|
|||
),
|
||||
name="static-page-detail",
|
||||
),
|
||||
# ---- programs
|
||||
path(_("programs/"), views.ProgramListView.as_view(), name="program-list"),
|
||||
path(_("programs/c/<slug:category_slug>"), views.ProgramListView.as_view(), name="program-list"),
|
||||
path(
|
||||
_("programs/<slug:slug>/"),
|
||||
views.ProgramDetailView.as_view(),
|
||||
name="program-detail",
|
||||
),
|
||||
path(
|
||||
_("programs/<slug:parent_slug>/episodes/"),
|
||||
views.EpisodeListView.as_view(),
|
||||
name="episode-list",
|
||||
),
|
||||
path(
|
||||
_("programs/<slug:parent_slug>/articles/"),
|
||||
views.ArticleListView.as_view(),
|
||||
name="article-list",
|
||||
),
|
||||
path(
|
||||
_("programs/<slug:parent_slug>/publications/"),
|
||||
views.ProgramPageListView.as_view(),
|
||||
name="program-page-list",
|
||||
),
|
||||
path(_("program/<slug:parent_slug>/articles"), views.ArticleListView.as_view(), name="article-list"),
|
||||
path(_("program/<slug:parent_slug>/podcasts"), views.PodcastListView.as_view(), name="podcast-list"),
|
||||
path(_("program/<slug:parent_slug>/episodes"), views.EpisodeListView.as_view(), name="episode-list"),
|
||||
path(_("program/<slug:parent_slug>/diffusions"), views.DiffusionListView.as_view(), name="diffusion-list"),
|
||||
path(
|
||||
"errors/no-station",
|
||||
views.errors.NoStationErrorView.as_view(),
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from . import admin, errors
|
||||
from .article import ArticleDetailView, ArticleListView
|
||||
from .base import BaseAPIView, BaseView
|
||||
from .diffusion import DiffusionListView
|
||||
from .diffusion import DiffusionListView, TimeTableView
|
||||
from .episode import EpisodeDetailView, EpisodeListView, PodcastListView
|
||||
from .home import HomeView
|
||||
from .log import LogListAPIView, LogListView
|
||||
|
@ -26,6 +26,7 @@ __all__ = (
|
|||
"BaseAPIView",
|
||||
"BaseView",
|
||||
"DiffusionListView",
|
||||
"TimeTableView",
|
||||
"EpisodeDetailView",
|
||||
"EpisodeListView",
|
||||
"PodcastListView",
|
||||
|
@ -40,4 +41,15 @@ __all__ = (
|
|||
"ProgramListView",
|
||||
"ProgramPageDetailView",
|
||||
"ProgramPageListView",
|
||||
"attached",
|
||||
)
|
||||
|
||||
|
||||
attached = {}
|
||||
|
||||
for key in __all__:
|
||||
view = globals().get(key)
|
||||
if key == "attached":
|
||||
continue
|
||||
if attach := getattr(view, "attach_to_value", None):
|
||||
attached[attach] = view
|
||||
|
|
|
@ -16,4 +16,4 @@ class ArticleListView(PageListView):
|
|||
model = Article
|
||||
has_headline = True
|
||||
parent_model = Program
|
||||
attach_to_value = StaticPage.ATTACH_TO_ARTICLES
|
||||
attach_to_value = StaticPage.Target.ARTICLES
|
||||
|
|
|
@ -18,6 +18,26 @@ class BaseView(TemplateResponseMixin, ContextMixin):
|
|||
# def get_queryset(self):
|
||||
# return super().get_queryset().station(self.station)
|
||||
|
||||
def get_nav_menu(self):
|
||||
menu = []
|
||||
for item in self.station.navitem_set.all():
|
||||
try:
|
||||
if item.page:
|
||||
view = item.page.get_related_view()
|
||||
secondary = view and view.get_secondary_nav()
|
||||
else:
|
||||
secondary = None
|
||||
menu.append((item, secondary))
|
||||
except:
|
||||
import traceback
|
||||
|
||||
traceback.print_exc()
|
||||
raise
|
||||
return menu
|
||||
|
||||
def get_secondary_nav(self):
|
||||
return None
|
||||
|
||||
def get_related_queryset(self):
|
||||
"""Return a queryset of related pages or None."""
|
||||
return None
|
||||
|
@ -46,6 +66,8 @@ class BaseView(TemplateResponseMixin, ContextMixin):
|
|||
kwargs["title"] = page.display_title
|
||||
kwargs["cover"] = page.cover and page.cover.url
|
||||
|
||||
if "nav_menu" not in kwargs:
|
||||
kwargs["nav_menu"] = self.get_nav_menu()
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
def dispatch(self, *args, **kwargs):
|
||||
|
|
|
@ -1,29 +1,54 @@
|
|||
import datetime
|
||||
|
||||
from django.urls import reverse
|
||||
from django.views.generic import ListView
|
||||
|
||||
from aircox.models import Diffusion, StaticPage
|
||||
from aircox.models import Diffusion, Log, StaticPage
|
||||
from .base import BaseView
|
||||
from .mixins import AttachedToMixin, GetDateMixin
|
||||
|
||||
__all__ = ("DiffusionListView",)
|
||||
__all__ = ("DiffusionListView", "TimeTableView")
|
||||
|
||||
|
||||
class DiffusionListView(GetDateMixin, AttachedToMixin, BaseView, ListView):
|
||||
class BaseDiffusionListView(AttachedToMixin, BaseView, ListView):
|
||||
model = Diffusion
|
||||
queryset = Diffusion.objects.on_air().order_by("-start")
|
||||
|
||||
|
||||
class DiffusionListView(BaseDiffusionListView):
|
||||
"""View for timetables."""
|
||||
|
||||
model = Diffusion
|
||||
redirect_date_url = "diffusion-list"
|
||||
attach_to_value = StaticPage.ATTACH_TO_DIFFUSIONS
|
||||
|
||||
|
||||
class TimeTableView(GetDateMixin, BaseDiffusionListView):
|
||||
model = Diffusion
|
||||
redirect_date_url = "timetable-list"
|
||||
attach_to_value = StaticPage.Target.TIMETABLE
|
||||
template_name = "aircox/timetable_list.html"
|
||||
|
||||
def get_date(self):
|
||||
date = super().get_date()
|
||||
return date if date is not None else datetime.date.today()
|
||||
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().date(self.date).order_by("start")
|
||||
def get_logs(self, date):
|
||||
return Log.objects.on_air().date(self.date).filter(track__isnull=False)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().date(self.date)
|
||||
|
||||
@classmethod
|
||||
def get_secondary_nav(cls):
|
||||
date = datetime.date.today()
|
||||
start = date - datetime.timedelta(days=date.weekday())
|
||||
dates = [start + datetime.timedelta(days=i) for i in range(0, 7)]
|
||||
return tuple((date.strftime("%A %d"), reverse("timetable-list", kwargs={"date": date})) for date in dates)
|
||||
|
||||
def get_context_data(self, object_list=None, **kwargs):
|
||||
start = self.date - datetime.timedelta(days=self.date.weekday())
|
||||
dates = [start + datetime.timedelta(days=i) for i in range(0, 7)]
|
||||
return super().get_context_data(date=self.date, dates=dates, **kwargs)
|
||||
|
||||
if object_list is None:
|
||||
logs = self.get_logs(self.date)
|
||||
object_list = Log.merge_diffusions(logs, self.object_list)
|
||||
return super().get_context_data(date=self.date, dates=dates, object_list=object_list, **kwargs)
|
||||
|
|
|
@ -33,9 +33,9 @@ class EpisodeListView(PageListView):
|
|||
model = Episode
|
||||
filterset_class = EpisodeFilters
|
||||
parent_model = Program
|
||||
attach_to_value = StaticPage.ATTACH_TO_EPISODES
|
||||
attach_to_value = StaticPage.Target.EPISODES
|
||||
|
||||
|
||||
class PodcastListView(EpisodeListView):
|
||||
attach_to_value = StaticPage.ATTACH_TO_PODCASTS
|
||||
attach_to_value = StaticPage.Target.PODCASTS
|
||||
queryset = Episode.objects.published().with_podcasts().order_by("-pub_date")
|
||||
|
|
|
@ -10,7 +10,7 @@ from .mixins import AttachedToMixin
|
|||
|
||||
class HomeView(AttachedToMixin, BaseView, ListView):
|
||||
template_name = "aircox/home.html"
|
||||
attach_to_value = StaticPage.ATTACH_TO_HOME
|
||||
attach_to_value = StaticPage.Target.HOME
|
||||
model = Diffusion
|
||||
queryset = Diffusion.objects.on_air().select_related("episode").order_by("-start")
|
||||
|
||||
|
|
|
@ -6,12 +6,12 @@ from django.views.decorators.cache import cache_page
|
|||
from django.views.generic import ListView
|
||||
from rest_framework.generics import ListAPIView
|
||||
|
||||
from ..models import Diffusion, Log, StaticPage
|
||||
from ..models import Diffusion, Log
|
||||
from ..serializers import LogInfo, LogInfoSerializer
|
||||
from .base import BaseAPIView, BaseView
|
||||
from .mixins import AttachedToMixin, GetDateMixin
|
||||
|
||||
__all__ = ["LogListMixin", "LogListView"]
|
||||
__all__ = ("LogListMixin", "LogListView", "LogListAPIView")
|
||||
|
||||
|
||||
class LogListMixin(GetDateMixin):
|
||||
|
@ -62,7 +62,6 @@ class LogListView(AttachedToMixin, BaseView, LogListMixin, ListView):
|
|||
`request.GET`, defaults to today)."""
|
||||
|
||||
redirect_date_url = "log-list"
|
||||
attach_to_value = StaticPage.ATTACH_TO_LOGS
|
||||
|
||||
def get_date(self):
|
||||
date = super().get_date()
|
||||
|
|
|
@ -44,16 +44,16 @@ class ParentMixin:
|
|||
parent = None
|
||||
"""Parent page object."""
|
||||
|
||||
def get_parent(self, request, *args, **kwargs):
|
||||
def get_parent(self, request, **kwargs):
|
||||
if self.parent_model is None or self.parent_url_kwarg not in kwargs:
|
||||
return
|
||||
|
||||
lookup = {self.parent_field: kwargs[self.parent_url_kwarg]}
|
||||
return get_object_or_404(self.parent_model.objects.select_related("cover"), **lookup)
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.parent = self.get_parent(request, *args, **kwargs)
|
||||
return super().get(request, *args, **kwargs)
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
self.parent = self.get_parent(request, **kwargs)
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_queryset(self):
|
||||
if self.parent is not None:
|
||||
|
@ -61,9 +61,10 @@ class ParentMixin:
|
|||
return super().get_queryset()
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
self.parent = kwargs.setdefault("parent", self.parent)
|
||||
if self.parent is not None:
|
||||
kwargs.setdefault("cover", self.parent.cover.url)
|
||||
parent = kwargs.setdefault("parent", self.parent)
|
||||
|
||||
if parent is not None:
|
||||
kwargs.setdefault("cover", parent.cover.url)
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
from django.http import Http404, HttpResponse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.views.generic import DetailView, ListView
|
||||
from django.urls import reverse
|
||||
from honeypot.decorators import check_honeypot
|
||||
|
||||
from ..filters import PageFilters
|
||||
from ..forms import CommentForm
|
||||
from ..models import Comment
|
||||
from ..models import Comment, Category
|
||||
from ..utils import Redirect
|
||||
from .base import BaseView
|
||||
from .mixins import AttachedToMixin, FiltersMixin, ParentMixin
|
||||
|
@ -18,7 +19,25 @@ __all__ = [
|
|||
]
|
||||
|
||||
|
||||
class BasePageListView(AttachedToMixin, ParentMixin, BaseView, ListView):
|
||||
class BasePageMixin:
|
||||
category = None
|
||||
|
||||
def get_category(self, page, **kwargs):
|
||||
if page:
|
||||
if getattr(page, "category_id", None):
|
||||
return page.category
|
||||
if page.parent_id:
|
||||
return self.get_category(page.parent_subclass)
|
||||
if slug := self.kwargs.get("category_slug"):
|
||||
return Category.objects.get(slug=slug)
|
||||
return None
|
||||
|
||||
def get_context_data(self, *args, **kwargs):
|
||||
kwargs.setdefault("category", self.category)
|
||||
return super().get_context_data(*args, **kwargs)
|
||||
|
||||
|
||||
class BasePageListView(AttachedToMixin, BasePageMixin, ParentMixin, BaseView, ListView):
|
||||
"""Base view class for BasePage list."""
|
||||
|
||||
template_name = "aircox/basepage_list.html"
|
||||
|
@ -26,11 +45,18 @@ class BasePageListView(AttachedToMixin, ParentMixin, BaseView, ListView):
|
|||
paginate_by = 30
|
||||
has_headline = True
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def get(self, *args, **kwargs):
|
||||
self.category = self.get_category(self.parent)
|
||||
return super().get(*args, **kwargs)
|
||||
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().select_subclasses().published().select_related("cover")
|
||||
query = super().get_queryset().select_subclasses().published().select_related("cover")
|
||||
if self.category:
|
||||
query = query.filter(category=self.category)
|
||||
return query
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs.setdefault("has_headline", self.has_headline)
|
||||
|
@ -48,12 +74,15 @@ class BasePageListView(AttachedToMixin, ParentMixin, BaseView, ListView):
|
|||
return context
|
||||
|
||||
|
||||
class BasePageDetailView(BaseView, DetailView):
|
||||
class BasePageDetailView(BasePageMixin, BaseView, DetailView):
|
||||
"""Base view class for BasePage."""
|
||||
|
||||
template_name = "aircox/basepage_detail.html"
|
||||
context_object_name = "page"
|
||||
|
||||
def get(self, *args, **kwargs):
|
||||
return super().get(*args, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
if self.object.cover:
|
||||
kwargs.setdefault("cover", self.object.cover.url)
|
||||
|
@ -81,6 +110,8 @@ class BasePageDetailView(BaseView, DetailView):
|
|||
if redirect_url:
|
||||
raise Redirect(redirect_url)
|
||||
raise Http404("%s not found" % self.model._meta.verbose_name)
|
||||
|
||||
self.category = self.get_category(obj)
|
||||
return obj
|
||||
|
||||
def get_page(self):
|
||||
|
@ -105,26 +136,22 @@ class PageListView(FiltersMixin, BasePageListView):
|
|||
|
||||
def get_queryset(self):
|
||||
qs = super().get_queryset().select_related("category").order_by("-pub_date")
|
||||
cat_ids = self.model.objects.published().values_list("category_id", flat=True)
|
||||
self.categories = Category.objects.filter(id__in=cat_ids)
|
||||
return qs
|
||||
|
||||
@classmethod
|
||||
def get_secondary_nav(cls):
|
||||
cat_ids = cls.model.objects.published().values_list("category_id", flat=True)
|
||||
categories = Category.objects.filter(id__in=cat_ids)
|
||||
return tuple(
|
||||
(category.title, reverse(cls.model.list_url_name, kwargs={"category_slug": category.slug}))
|
||||
for category in categories
|
||||
)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
self.categories = {
|
||||
id: title
|
||||
for title, id in self.model.objects.published()
|
||||
.filter(category__isnull=False)
|
||||
.values_list("category__title", "category__id")
|
||||
.distinct()
|
||||
}
|
||||
cat_id = self.request.GET.get("category__id")
|
||||
if cat_id:
|
||||
cat_id = int(cat_id)
|
||||
kwargs["category_id"] = cat_id
|
||||
|
||||
context = super().get_context_data(**kwargs)
|
||||
if context.get("parent") and not cat_id:
|
||||
kwargs["category_id"] = context["parent"].category_id
|
||||
|
||||
return context
|
||||
kwargs["categories"] = self.categories
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
|
||||
class PageDetailView(BasePageDetailView):
|
||||
|
@ -152,7 +179,6 @@ class PageDetailView(BasePageDetailView):
|
|||
if related:
|
||||
related = related[: self.related_count]
|
||||
kwargs["related_objects"] = related
|
||||
kwargs["related_url"] = self.get_related_url()
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
@classmethod
|
||||
|
|
|
@ -52,7 +52,7 @@ class ProgramDetailView(BaseProgramMixin, PageDetailView):
|
|||
|
||||
class ProgramListView(PageListView):
|
||||
model = Program
|
||||
attach_to_value = StaticPage.ATTACH_TO_PROGRAMS
|
||||
attach_to_value = StaticPage.Target.PROGRAMS
|
||||
|
||||
|
||||
# FIXME: not used
|
||||
|
|
|
@ -39,9 +39,9 @@
|
|||
--a-player-bar-bg: var(--highlight-color);
|
||||
--a-player-bar-title-alone-sz: #{v.$text-size-bigger};
|
||||
|
||||
--button-fg: var(--text-color);
|
||||
--button-fg: var(--highlight-color-2);
|
||||
--button-bg: var(--highlight-color);
|
||||
--button-hg-fg: var(--highlight-color-2);
|
||||
--button-hg-fg: var(--text-color);
|
||||
--button-hg-bg: var(--highlight-color);
|
||||
--button-active-fg: var(--highlight-color);
|
||||
--button-active-bg: var(--highlight-color-2);
|
||||
|
@ -102,13 +102,13 @@
|
|||
@mixin button {
|
||||
.button, a.button, button.button, .nav-urls a {
|
||||
display: inline-block;
|
||||
padding: v.$mp-3e;
|
||||
border-radius: 4px;
|
||||
padding: v.$mp-2e;
|
||||
border: 1px var(--highlight-color-2-alpha) solid;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
font-size: v.$text-size-medium;
|
||||
|
||||
color: var(--button-fg);
|
||||
background-color: var(--button-bg);
|
||||
|
||||
.icon {
|
||||
|
@ -169,7 +169,6 @@
|
|||
}
|
||||
|
||||
|
||||
|
||||
// ---- preview
|
||||
.preview {
|
||||
position: relative;
|
||||
|
@ -355,6 +354,15 @@
|
|||
height: var(--preview-cover-size);
|
||||
width: var(--preview-cover-size);
|
||||
|
||||
&.small {
|
||||
height: var(--preview-cover-small-size);
|
||||
width: var(--preview-cover-small-size);
|
||||
}
|
||||
&.tiny {
|
||||
height: var(--preview-cover-tiny-size);
|
||||
width: var(--preview-cover-tiny-size);
|
||||
}
|
||||
|
||||
&:not(.header) {
|
||||
box-shadow: 0em 0em 1em rgba(0,0,0,0.2);
|
||||
}
|
||||
|
@ -389,72 +397,6 @@
|
|||
|
||||
}
|
||||
|
||||
// ---- page header
|
||||
.header {
|
||||
&.preview-header {
|
||||
//display: flex;
|
||||
align-items: start;
|
||||
gap: v.$mp-3;
|
||||
min-height: unset;
|
||||
padding-top: v.$mp-3 !important;
|
||||
|
||||
}
|
||||
|
||||
.headings {
|
||||
width: unset;
|
||||
flex-grow: 1;
|
||||
padding-top: 0 !important;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
&.has-cover {
|
||||
min-height: calc( var(--header-height) / 2 );
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: v.$h1-size;
|
||||
}
|
||||
.subtitle {
|
||||
font-size: v.$h2-size;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.header-cover:not(:only-child) {
|
||||
float: right;
|
||||
height: var(--header-height);
|
||||
max-width: calc(var(--header-height) * 2);
|
||||
margin: 0 0 v.$mp-4 v.$mp-4;
|
||||
}
|
||||
.header-cover:only-child {
|
||||
with: 100%;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@media screen and (max-width: v.$screen-small) {
|
||||
.container.header {
|
||||
width: 100%;
|
||||
|
||||
.headings {
|
||||
width: 100%;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.header-cover {
|
||||
float: none;
|
||||
width: 100%;
|
||||
max-width: unset;
|
||||
height: unset;
|
||||
margin-left: 0rem;
|
||||
margin-right: 0rem;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// ---- card grid
|
||||
.card-grid {
|
||||
|
@ -606,6 +548,10 @@
|
|||
box-shadow: 0em -0.5em 0.5em rgba(0, 0, 0, 0.05);
|
||||
|
||||
a { color: var(--a-player-url-fg); }
|
||||
.button {
|
||||
color: var(--text-black);
|
||||
&:hover { color: var(--button-fg); }
|
||||
}
|
||||
}
|
||||
|
||||
.a-player-panels {
|
||||
|
|
|
@ -13,8 +13,13 @@
|
|||
color: var(--highlight-color-2);
|
||||
text-decoration: none;
|
||||
padding: v.$mp-2;
|
||||
|
||||
&:hover {
|
||||
color: var(--text-color);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
section.container {
|
||||
padding-top: v.$mp-6;
|
||||
|
||||
|
@ -74,22 +79,12 @@
|
|||
}
|
||||
|
||||
button, .action {
|
||||
background-color: var(--highlight-color);
|
||||
justify-content: center;
|
||||
min-width: 2rem;
|
||||
|
||||
.not-selected { opacity: 0.6; }
|
||||
|
||||
|
||||
.icon { margin: 0em !important; }
|
||||
|
||||
label {
|
||||
margin-left: v.$mp-2;
|
||||
}
|
||||
|
||||
&:hover, .selected {
|
||||
color: var(--highlight-color-2) !important;
|
||||
}
|
||||
label { margin-left: v.$mp-2; }
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -115,6 +110,10 @@
|
|||
}
|
||||
|
||||
// ---- main navigation
|
||||
.navs {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.nav {
|
||||
display: flex;
|
||||
background-color: var(--highlight-color);
|
||||
|
@ -145,7 +144,7 @@
|
|||
display: inline-block;
|
||||
}
|
||||
|
||||
&.active {
|
||||
&.active, &:hover {
|
||||
background-color: var(--highlight-color-2);
|
||||
color: var(--highlight-color);
|
||||
}
|
||||
|
@ -189,7 +188,19 @@
|
|||
}
|
||||
|
||||
&.secondary {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
z-index: 100;
|
||||
box-shadow: 0em 0.5em 0.5em rgba(0, 0, 0, 0.05);
|
||||
|
||||
justify-content: right;
|
||||
display: none;
|
||||
|
||||
.nav-item:hover + &, &:hover {
|
||||
display: flex;
|
||||
top: var(--nav-primary-height);
|
||||
left: 0rem;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
font-size: v.$text-size;
|
||||
|
@ -197,6 +208,18 @@
|
|||
}
|
||||
}
|
||||
|
||||
// ---- breadcrumbs
|
||||
.breadcrumbs {
|
||||
text-align: right;
|
||||
height: calc( v.$mp-3 * 2 + v.$text-size);
|
||||
margin-bottom: v.$mp-2;
|
||||
|
||||
a + a:before {
|
||||
content: "/";
|
||||
margin: 0 v.$mp-2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@media screen and (max-width: v.$screen-normal) {
|
||||
.page {
|
||||
|
@ -324,7 +347,7 @@ nav li {
|
|||
}
|
||||
|
||||
&.has-cover {
|
||||
min-height: calc( var(--header-height) / 2 );
|
||||
min-height: calc( var(--header-height) / 3 );
|
||||
}
|
||||
|
||||
.title {
|
||||
|
|
|
@ -69,6 +69,6 @@ window.aircox = {
|
|||
|
||||
pickDate(url, date) {
|
||||
url = `${url}?date=${date.id}`
|
||||
this.builder.fetch(url)
|
||||
this.loader.pageLoad.load(url)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user