feat: change entire downloads
This commit is contained in:
@@ -2,12 +2,25 @@ from django.db import models
|
||||
from django.urls import reverse
|
||||
from django.utils.text import slugify
|
||||
|
||||
from apps.core.utils import CONTENT_FORMAT_CHOICES, FORMAT_PLAIN, render_content
|
||||
|
||||
|
||||
INSTALLABLE_PLATFORM_SPECS = (
|
||||
("windows_download_url", "Windows", "images/icon-windows.svg"),
|
||||
("macos_download_url", "macOS", "images/icon-macos.svg"),
|
||||
("linux_download_url", "Linux", "images/icon-linux.svg"),
|
||||
("source_code_url", "Source code", None),
|
||||
)
|
||||
|
||||
|
||||
class MainProduct(models.Model):
|
||||
name = models.CharField(max_length=200)
|
||||
slug = models.SlugField(unique=True, blank=True)
|
||||
short_description = models.CharField(max_length=300)
|
||||
description = models.TextField()
|
||||
description_format = models.CharField(
|
||||
max_length=20, choices=CONTENT_FORMAT_CHOICES, default=FORMAT_PLAIN
|
||||
)
|
||||
image = models.ImageField(upload_to="products/main/", blank=True, null=True)
|
||||
order = models.PositiveIntegerField(default=0)
|
||||
is_active = models.BooleanField(default=True)
|
||||
@@ -19,6 +32,10 @@ class MainProduct(models.Model):
|
||||
verbose_name = "Main Product"
|
||||
verbose_name_plural = "Main Products"
|
||||
|
||||
@property
|
||||
def rendered_description(self):
|
||||
return render_content(self.description, self.description_format)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
@@ -32,6 +49,13 @@ class MainProduct(models.Model):
|
||||
|
||||
|
||||
class SubProduct(models.Model):
|
||||
DISTRIBUTION_INSTALLABLE = "installable"
|
||||
DISTRIBUTION_PACKAGE = "package"
|
||||
DISTRIBUTION_CHOICES = [
|
||||
(DISTRIBUTION_INSTALLABLE, "Installable application"),
|
||||
(DISTRIBUTION_PACKAGE, "Package (external / non-installable)"),
|
||||
]
|
||||
|
||||
main_product = models.ForeignKey(
|
||||
MainProduct,
|
||||
on_delete=models.CASCADE,
|
||||
@@ -41,9 +65,18 @@ class SubProduct(models.Model):
|
||||
slug = models.SlugField(blank=True)
|
||||
short_description = models.CharField(max_length=300)
|
||||
description = models.TextField()
|
||||
description_format = models.CharField(
|
||||
max_length=20, choices=CONTENT_FORMAT_CHOICES, default=FORMAT_PLAIN
|
||||
)
|
||||
image = models.ImageField(upload_to="products/sub/", blank=True, null=True)
|
||||
order = models.PositiveIntegerField(default=0)
|
||||
is_active = models.BooleanField(default=True)
|
||||
distribution = models.CharField(
|
||||
max_length=20,
|
||||
choices=DISTRIBUTION_CHOICES,
|
||||
default=DISTRIBUTION_INSTALLABLE,
|
||||
help_text="Only affects how releases appear on the public site.",
|
||||
)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
@@ -53,6 +86,10 @@ class SubProduct(models.Model):
|
||||
verbose_name = "Sub Product"
|
||||
verbose_name_plural = "Sub Products"
|
||||
|
||||
@property
|
||||
def rendered_description(self):
|
||||
return render_content(self.description, self.description_format)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.main_product.name} › {self.name}"
|
||||
|
||||
@@ -70,6 +107,15 @@ class SubProduct(models.Model):
|
||||
},
|
||||
)
|
||||
|
||||
def get_versions_archive_url(self):
|
||||
return reverse(
|
||||
"products:sub_product_versions",
|
||||
kwargs={
|
||||
"main_slug": self.main_product.slug,
|
||||
"sub_slug": self.slug,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
class Article(models.Model):
|
||||
sub_product = models.ForeignKey(
|
||||
@@ -79,6 +125,9 @@ class Article(models.Model):
|
||||
)
|
||||
title = models.CharField(max_length=300)
|
||||
description = models.TextField()
|
||||
description_format = models.CharField(
|
||||
max_length=20, choices=CONTENT_FORMAT_CHOICES, default=FORMAT_PLAIN
|
||||
)
|
||||
order = models.PositiveIntegerField(default=0)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
@@ -88,10 +137,64 @@ class Article(models.Model):
|
||||
verbose_name = "Article"
|
||||
verbose_name_plural = "Articles"
|
||||
|
||||
@property
|
||||
def rendered_description(self):
|
||||
return render_content(self.description, self.description_format)
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
|
||||
class SubProductVersion(models.Model):
|
||||
sub_product = models.ForeignKey(
|
||||
SubProduct,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="versions",
|
||||
)
|
||||
version = models.CharField(max_length=80)
|
||||
is_featured_stable = models.BooleanField(
|
||||
default=False,
|
||||
help_text="Highlighted as the main release on the product page.",
|
||||
)
|
||||
windows_download_url = models.URLField(blank=True)
|
||||
macos_download_url = models.URLField(blank=True)
|
||||
linux_download_url = models.URLField(blank=True)
|
||||
source_code_url = models.URLField(blank=True)
|
||||
package_resource_url = models.URLField(
|
||||
blank=True,
|
||||
help_text="Shown on the site when set. For Resources it is the main Open link; for Installable it appears under the platform grid.",
|
||||
)
|
||||
release_notes = models.TextField(blank=True)
|
||||
is_active = models.BooleanField(default=True)
|
||||
order = models.PositiveIntegerField(default=0)
|
||||
|
||||
class Meta:
|
||||
ordering = ["order", "version", "pk"]
|
||||
unique_together = [["sub_product", "version"]]
|
||||
verbose_name = "Release version"
|
||||
verbose_name_plural = "Release versions"
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.sub_product.name} v{self.version}"
|
||||
|
||||
def install_urls_for_specs(self, specs):
|
||||
out = []
|
||||
for field_name, label, icon in specs:
|
||||
url = getattr(self, field_name, "") or ""
|
||||
if url.strip():
|
||||
out.append({"field": field_name, "label": label, "icon": icon, "url": url})
|
||||
return out
|
||||
|
||||
def has_any_install_asset(self):
|
||||
return any(
|
||||
(getattr(self, f[0]) or "").strip()
|
||||
for f in INSTALLABLE_PLATFORM_SPECS
|
||||
)
|
||||
|
||||
def has_package_link(self):
|
||||
return bool((self.package_resource_url or "").strip())
|
||||
|
||||
|
||||
class ArticleSection(models.Model):
|
||||
article = models.ForeignKey(
|
||||
Article,
|
||||
@@ -100,6 +203,9 @@ class ArticleSection(models.Model):
|
||||
)
|
||||
title = models.CharField(max_length=200)
|
||||
value = models.TextField()
|
||||
value_format = models.CharField(
|
||||
max_length=20, choices=CONTENT_FORMAT_CHOICES, default=FORMAT_PLAIN
|
||||
)
|
||||
order = models.PositiveIntegerField(default=0)
|
||||
|
||||
class Meta:
|
||||
@@ -107,5 +213,9 @@ class ArticleSection(models.Model):
|
||||
verbose_name = "Article Section"
|
||||
verbose_name_plural = "Article Sections"
|
||||
|
||||
@property
|
||||
def rendered_value(self):
|
||||
return render_content(self.value, self.value_format)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.article.title} › {self.title}"
|
||||
|
||||
Reference in New Issue
Block a user