From 38faca33e5b6a562dc43db9b8bcfe841e77e59ed Mon Sep 17 00:00:00 2001 From: Edward Ly Date: Tue, 13 Aug 2024 11:01:44 -0700 Subject: [PATCH 1/6] feat(info): add XML specification and importer for donation attribute Signed-off-by: Edward Ly --- nextcloudappstore/api/v1/release/importer.py | 19 +++++++++++ nextcloudappstore/api/v1/release/info.xsd | 19 +++++++++++ nextcloudappstore/api/v1/release/info.xslt | 33 +++++++++++++++++++ .../api/v1/release/pre-info.xslt | 1 + 4 files changed, 72 insertions(+) diff --git a/nextcloudappstore/api/v1/release/importer.py b/nextcloudappstore/api/v1/release/importer.py index c76a3b8bea0..d4d127a5cd4 100644 --- a/nextcloudappstore/api/v1/release/importer.py +++ b/nextcloudappstore/api/v1/release/importer.py @@ -15,6 +15,7 @@ Category, Database, DatabaseDependency, + Donation, License, PhpExtension, PhpExtensionDependency, @@ -144,6 +145,21 @@ def create_screenshot(img: dict[str, str]) -> Screenshot: obj.screenshots.set(list(shots)) +class DonationsImporter(ScalarImporter): + def import_data(self, key: str, value: Any, obj: Any) -> None: + def create_donation(img: dict[str, str]) -> Donation: + return Donation.objects.create( + url=img["url"], + app=obj, + ordering=img["ordering"], + title=img["title"], + type=img["type"], + ) + + shots = map(lambda val: create_donation(val["donation"]), value) + obj.donations.set(list(shots)) + + class CategoryImporter(ScalarImporter): def import_data(self, key: str, value: Any, obj: Any) -> None: def map_categories(cat: dict) -> Category: @@ -251,6 +267,7 @@ def __init__( self, release_importer: AppReleaseImporter, screenshots_importer: ScreenshotsImporter, + donations_importer: DonationsImporter, attribute_importer: StringAttributeImporter, l10n_importer: L10NImporter, category_importer: CategoryImporter, @@ -261,6 +278,7 @@ def __init__( { "release": release_importer, "screenshots": screenshots_importer, + "donations": donations_importer, "user_docs": attribute_importer, "admin_docs": attribute_importer, "website": attribute_importer, @@ -294,6 +312,7 @@ def _before_import(self, key: str, value: Any, obj: Any) -> tuple[Any, Any]: if self._should_update_everything(value): # clear all relations obj.screenshots.all().delete() + obj.donations.all().delete() obj.authors.all().delete() obj.categories.clear() for translation in obj.translations.all(): diff --git a/nextcloudappstore/api/v1/release/info.xsd b/nextcloudappstore/api/v1/release/info.xsd index 7b0a79ccd97..dfa61327788 100644 --- a/nextcloudappstore/api/v1/release/info.xsd +++ b/nextcloudappstore/api/v1/release/info.xsd @@ -36,6 +36,8 @@ maxOccurs="1"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + <xsl:value-of select="donation/@title"/> + + + + Donate to support this app + + + + + + + + + + other + + + + + + + + + diff --git a/nextcloudappstore/api/v1/release/pre-info.xslt b/nextcloudappstore/api/v1/release/pre-info.xslt index 090fc4589d9..a5b5f3e9b9a 100644 --- a/nextcloudappstore/api/v1/release/pre-info.xslt +++ b/nextcloudappstore/api/v1/release/pre-info.xslt @@ -32,6 +32,7 @@ + From 8cb93dbd731c92cdc0edabe33f82e1a50f4cbdd4 Mon Sep 17 00:00:00 2001 From: Edward Ly Date: Tue, 13 Aug 2024 11:00:44 -0700 Subject: [PATCH 2/6] feat(Donation): add donation table and model Signed-off-by: Edward Ly --- nextcloudappstore/core/admin.py | 8 +++++ .../core/migrations/0033_donation.py | 30 +++++++++++++++++++ nextcloudappstore/core/models.py | 16 ++++++++++ 3 files changed, 54 insertions(+) create mode 100644 nextcloudappstore/core/migrations/0033_donation.py diff --git a/nextcloudappstore/core/admin.py b/nextcloudappstore/core/admin.py index 165f0cbc59b..3522f64e7bf 100644 --- a/nextcloudappstore/core/admin.py +++ b/nextcloudappstore/core/admin.py @@ -12,6 +12,7 @@ Category, Database, DatabaseDependency, + Donation, License, NextcloudRelease, PhpExtension, @@ -136,6 +137,13 @@ class ScreenshotAdmin(admin.ModelAdmin): list_filter = ("app__id",) +@admin.register(Donation) +class DonationAdmin(admin.ModelAdmin): + ordering = ("app", "ordering") + list_display = ("url", "type", "title", "app", "ordering") + list_filter = ("app__id",) + + @admin.register(NextcloudRelease) class NextcloudReleaseAdmin(admin.ModelAdmin): list_display = ("version", "is_current", "has_release", "is_supported") diff --git a/nextcloudappstore/core/migrations/0033_donation.py b/nextcloudappstore/core/migrations/0033_donation.py new file mode 100644 index 00000000000..f69f92fb7b5 --- /dev/null +++ b/nextcloudappstore/core/migrations/0033_donation.py @@ -0,0 +1,30 @@ +# Generated by Django 4.2.14 on 2024-08-13 04:14 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0032_app_is_enterprise_supported'), + ] + + operations = [ + migrations.CreateModel( + name='Donation', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('url', models.URLField(max_length=256, verbose_name='Donation URL')), + ('type', models.CharField(default='other', max_length=256, verbose_name='Donation Type')), + ('title', models.CharField(default='Donate to support this app', max_length=256, verbose_name='Donation Title')), + ('ordering', models.IntegerField(verbose_name='Ordering')), + ('app', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='donations', to='core.app', verbose_name='App')), + ], + options={ + 'verbose_name': 'Donation', + 'verbose_name_plural': 'Donations', + 'ordering': ['ordering'], + }, + ), + ] diff --git a/nextcloudappstore/core/models.py b/nextcloudappstore/core/models.py index f376c125818..45ebe2c4834 100644 --- a/nextcloudappstore/core/models.py +++ b/nextcloudappstore/core/models.py @@ -557,6 +557,22 @@ def __str__(self) -> str: return self.url +class Donation(Model): + url = URLField(max_length=256, verbose_name=_("Donation URL")) + type = CharField(max_length=256, verbose_name=_("Donation Type"), default="other") + title = CharField(max_length=256, verbose_name=_("Donation Title"), default="Donate to support this app") + app = ForeignKey("App", on_delete=CASCADE, verbose_name=_("App"), related_name="donations") + ordering = IntegerField(verbose_name=_("Ordering")) + + class Meta: + verbose_name = _("Donation") + verbose_name_plural = _("Donations") + ordering = ["ordering"] + + def __str__(self) -> str: + return self.url + + class ShellCommand(Model): name = CharField( max_length=256, From 82f0fa12273415108fcba3e277f2cd1c71aeaf1b Mon Sep 17 00:00:00 2001 From: Edward Ly Date: Tue, 13 Aug 2024 12:11:49 -0700 Subject: [PATCH 3/6] feat(icons): add donation icons Signed-off-by: Edward Ly --- .../core/static/assets/css/icons.css | 36 +++++++++++++++++++ .../img/icons/detail/donate-other-white.svg | 1 + .../assets/img/icons/detail/donate-other.svg | 1 + .../img/icons/detail/donate-paypal-white.svg | 1 + .../assets/img/icons/detail/donate-paypal.svg | 1 + .../img/icons/detail/donate-stripe-white.svg | 1 + .../assets/img/icons/detail/donate-stripe.svg | 1 + 7 files changed, 42 insertions(+) create mode 100644 nextcloudappstore/core/static/assets/img/icons/detail/donate-other-white.svg create mode 100644 nextcloudappstore/core/static/assets/img/icons/detail/donate-other.svg create mode 100644 nextcloudappstore/core/static/assets/img/icons/detail/donate-paypal-white.svg create mode 100644 nextcloudappstore/core/static/assets/img/icons/detail/donate-paypal.svg create mode 100644 nextcloudappstore/core/static/assets/img/icons/detail/donate-stripe-white.svg create mode 100644 nextcloudappstore/core/static/assets/img/icons/detail/donate-stripe.svg diff --git a/nextcloudappstore/core/static/assets/css/icons.css b/nextcloudappstore/core/static/assets/css/icons.css index 46112e007e6..3521c591b8c 100644 --- a/nextcloudappstore/core/static/assets/css/icons.css +++ b/nextcloudappstore/core/static/assets/css/icons.css @@ -44,6 +44,12 @@ --original-icon-comment-question-white: url('../img/icons/detail/comment-question-white.svg'); --original-icon-feature-search-dark: url('../img/icons/detail/feature-search.svg'); --original-icon-feature-search-white: url('../img/icons/detail/feature-search-white.svg'); + --original-icon-donate-paypal-dark: url('../img/icons/detail/donate-paypal.svg'); + --original-icon-donate-paypal-white: url('../img/icons/detail/donate-paypal-white.svg'); + --original-icon-donate-stripe-dark: url('../img/icons/detail/donate-stripe.svg'); + --original-icon-donate-stripe-white: url('../img/icons/detail/donate-stripe-white.svg'); + --original-icon-donate-other-dark: url('../img/icons/detail/donate-other.svg'); + --original-icon-donate-other-white: url('../img/icons/detail/donate-other-white.svg'); --original-icon-send-dark: url('../img/icons/detail/send.svg'); --original-icon-send-white: url('../img/icons/detail/send-white.svg'); --original-icon-relevance-dark: url('../img/icons/list/relevance.svg'); @@ -107,6 +113,12 @@ body { --icon-comment-question-white: var(--original-icon-comment-question-white); --icon-feature-search-dark: var(--original-icon-feature-search-dark); --icon-feature-search-white: var(--original-icon-feature-search-white); + --icon-donate-paypal-dark: var(--original-icon-donate-paypal-dark); + --icon-donate-paypal-white: var(--original-icon-donate-paypal-white); + --icon-donate-stripe-dark: var(--original-icon-donate-stripe-dark); + --icon-donate-stripe-white: var(--original-icon-donate-stripe-white); + --icon-donate-other-dark: var(--original-icon-donate-other-dark); + --icon-donate-other-white: var(--original-icon-donate-other-white); --icon-send-dark: var(--original-icon-send-dark); --icon-send-white: var(--original-icon-send-white); --icon-relevance-dark: var(--original-icon-relevance-dark); @@ -307,6 +319,30 @@ body .icon-feature-search-white, body .icon-feature-search.icon-white { background-image: var(--icon-feature-search-white); } +body .icon-donate-paypal, +body .icon-donate-paypal-dark { + background-image: var(--icon-donate-paypal-dark); +} +body .icon-donate-paypal-white, +body .icon-donate-paypal.icon-white { + background-image: var(--icon-donate-paypal-white); +} +body .icon-donate-stripe, +body .icon-donate-stripe-dark { + background-image: var(--icon-donate-stripe-dark); +} +body .icon-donate-stripe-white, +body .icon-donate-stripe.icon-white { + background-image: var(--icon-donate-stripe-white); +} +body .icon-donate-other, +body .icon-donate-other-dark { + background-image: var(--icon-donate-other-dark); +} +body .icon-donate-other-white, +body .icon-donate-other.icon-white { + background-image: var(--icon-donate-other-white); +} body .icon-send, body .icon-send-dark { background-image: var(--icon-send-dark); diff --git a/nextcloudappstore/core/static/assets/img/icons/detail/donate-other-white.svg b/nextcloudappstore/core/static/assets/img/icons/detail/donate-other-white.svg new file mode 100644 index 00000000000..73feff95f88 --- /dev/null +++ b/nextcloudappstore/core/static/assets/img/icons/detail/donate-other-white.svg @@ -0,0 +1 @@ + diff --git a/nextcloudappstore/core/static/assets/img/icons/detail/donate-other.svg b/nextcloudappstore/core/static/assets/img/icons/detail/donate-other.svg new file mode 100644 index 00000000000..dd562a32653 --- /dev/null +++ b/nextcloudappstore/core/static/assets/img/icons/detail/donate-other.svg @@ -0,0 +1 @@ + diff --git a/nextcloudappstore/core/static/assets/img/icons/detail/donate-paypal-white.svg b/nextcloudappstore/core/static/assets/img/icons/detail/donate-paypal-white.svg new file mode 100644 index 00000000000..f9ca79c4326 --- /dev/null +++ b/nextcloudappstore/core/static/assets/img/icons/detail/donate-paypal-white.svg @@ -0,0 +1 @@ + diff --git a/nextcloudappstore/core/static/assets/img/icons/detail/donate-paypal.svg b/nextcloudappstore/core/static/assets/img/icons/detail/donate-paypal.svg new file mode 100644 index 00000000000..481803f15db --- /dev/null +++ b/nextcloudappstore/core/static/assets/img/icons/detail/donate-paypal.svg @@ -0,0 +1 @@ + diff --git a/nextcloudappstore/core/static/assets/img/icons/detail/donate-stripe-white.svg b/nextcloudappstore/core/static/assets/img/icons/detail/donate-stripe-white.svg new file mode 100644 index 00000000000..929dd28570c --- /dev/null +++ b/nextcloudappstore/core/static/assets/img/icons/detail/donate-stripe-white.svg @@ -0,0 +1 @@ + diff --git a/nextcloudappstore/core/static/assets/img/icons/detail/donate-stripe.svg b/nextcloudappstore/core/static/assets/img/icons/detail/donate-stripe.svg new file mode 100644 index 00000000000..acd75988c35 --- /dev/null +++ b/nextcloudappstore/core/static/assets/img/icons/detail/donate-stripe.svg @@ -0,0 +1 @@ + From cf233b46a0fc59066be241228ac1150ebd8a42c3 Mon Sep 17 00:00:00 2001 From: Edward Ly Date: Tue, 13 Aug 2024 11:04:49 -0700 Subject: [PATCH 4/6] feat(detail): add donation buttons to apps Signed-off-by: Edward Ly --- nextcloudappstore/core/static/assets/css/style.css | 2 ++ nextcloudappstore/core/templates/app/detail.html | 13 +++++++++++++ nextcloudappstore/core/views.py | 5 ++++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/nextcloudappstore/core/static/assets/css/style.css b/nextcloudappstore/core/static/assets/css/style.css index d1e4f8c3e10..18d17e1e1aa 100644 --- a/nextcloudappstore/core/static/assets/css/style.css +++ b/nextcloudappstore/core/static/assets/css/style.css @@ -926,11 +926,13 @@ address { } .interact-section h5, +.donate-section h5, .support-section h5 { margin-bottom: 20px; } .interact-section a, .interact-section button, +.donate-section a, .donate-section button, .support-section a, .support-section button { margin-bottom: 10px; } diff --git a/nextcloudappstore/core/templates/app/detail.html b/nextcloudappstore/core/templates/app/detail.html index 521deece62d..a0c5cb3539c 100644 --- a/nextcloudappstore/core/templates/app/detail.html +++ b/nextcloudappstore/core/templates/app/detail.html @@ -135,6 +135,19 @@
{% trans "Interact" %}
{% trans 'Ask questions or discuss' %} + {% if object.donations.all %} + + {% endif %} {% if object.is_enterprise_supported %}
{% trans 'Need Enterprise Support?' %}
diff --git a/nextcloudappstore/core/views.py b/nextcloudappstore/core/views.py index 7c002710b8e..fcc4f15cf57 100644 --- a/nextcloudappstore/core/views.py +++ b/nextcloudappstore/core/views.py @@ -24,7 +24,7 @@ AppRegisterForm, AppReleaseUploadForm, ) -from nextcloudappstore.core.models import App, AppRating, Category, Podcast +from nextcloudappstore.core.models import App, AppRating, Category, Donation, Podcast from nextcloudappstore.core.serializers import AppRatingSerializer from nextcloudappstore.core.versioning import pad_min_version @@ -139,10 +139,13 @@ def get_context_data(self, **kwargs): context["user_has_rated_app"] = True except AppRating.DoesNotExist: pass + + context["donations"] = Donation.objects.filter(app=context["app"]) context["categories"] = Category.objects.prefetch_related("translations").all() context["latest_releases_by_platform_v"] = self.object.latest_releases_by_platform_v() context["is_integration"] = self.object.is_integration context["is_outdated"] = self.object.is_outdated() + return context From c926ae7904ce5f3fe2c1752c481f58f9ec5538ce Mon Sep 17 00:00:00 2001 From: Edward Ly Date: Wed, 14 Aug 2024 15:39:53 -0700 Subject: [PATCH 5/6] fix(tests): add donations key to parser test Signed-off-by: Edward Ly --- nextcloudappstore/api/v1/tests/test_parser.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nextcloudappstore/api/v1/tests/test_parser.py b/nextcloudappstore/api/v1/tests/test_parser.py index d8c83d1a6c7..3197bf60cfa 100644 --- a/nextcloudappstore/api/v1/tests/test_parser.py +++ b/nextcloudappstore/api/v1/tests/test_parser.py @@ -43,6 +43,7 @@ def test_parse_minimal(self): "issue_tracker": "https://github.com/nextcloud/news/issues", "screenshots": [], "categories": [{"category": {"id": "multimedia"}}], + "donations": [], "release": { "databases": [], "licenses": [{"license": {"id": "agpl"}}], @@ -504,6 +505,7 @@ def test_map_data(self): {"screenshot": {"url": "https://example.com/1.png", "small_thumbnail": None, "ordering": 1}}, {"screenshot": {"url": "https://example.com/2.jpg", "small_thumbnail": None, "ordering": 2}}, ], + "donations": [], } } self.assertDictEqual(expected, result) From dd05a8e970798ed42cf70a0e9377c152c42704f5 Mon Sep 17 00:00:00 2001 From: Edward Ly Date: Wed, 14 Aug 2024 22:36:37 -0700 Subject: [PATCH 6/6] feat(docs): add donation attribute usage Signed-off-by: Edward Ly --- docs/developer.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/developer.rst b/docs/developer.rst index a8e874c284a..6da4f22e3b9 100644 --- a/docs/developer.rst +++ b/docs/developer.rst @@ -211,6 +211,8 @@ A full blown example would look like this (needs to be utf-8 encoded): https://github.com/nextcloud/news https://example.com/1.png https://example.com/2.jpg + https://paypal.com/example-link + https://github.com/sponsors/example pgsql @@ -399,6 +401,12 @@ screenshot * must contain an HTTPS URL to an image * can contain a **small-thumbnail** attribute which must contain an https url to an image. This image will be used as small preview (e.g. on the app list overview). Keep it small so it renders fast * will be rendered on the app list and detail page in the given order +donation + * optional + * can occur multiple times containing different donation URLs + * can contain a **title** attribute which must be a string, defaults to **Donate to support this app** + * can contain a **type** attribute, **paypal**, **stripe**, and **other** are allowed values, defaults to **other** + * will be rendered on the app detail page in the given order dependencies/php * optional * can contain a **min-version** attribute (maximum 3 digits separated by dots)