From 4ffb5c8d9db148bb08071a978cb5cc0830cb2223 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Lente?= Date: Tue, 14 May 2024 16:37:37 +0200 Subject: [PATCH 01/32] UI: Add template list dropdown select sort by front-end base Add template list dropdown select sort by front-end base for sorting by select options. Part of #126 --- common/static/common/styles/_forms.sass | 9 +++ extensions/templates/extensions/list.html | 69 +++++++++++++++-------- 2 files changed, 54 insertions(+), 24 deletions(-) diff --git a/common/static/common/styles/_forms.sass b/common/static/common/styles/_forms.sass index f2a07daa..f42b92b2 100644 --- a/common/static/common/styles/_forms.sass +++ b/common/static/common/styles/_forms.sass @@ -10,6 +10,10 @@ +margin(2, left) .form-control-sm + font-size: var(--fs-sm) + height: calc(var(--spacer) * 2) + +padding(0, y) + &[type="file"] font-size: var(--fs-xs) height: calc(var(--spacer) * 2) @@ -22,3 +26,8 @@ .was-validated .form-control:invalid, .form-control.is-invalid background-image: none + +select + &.form-control + &:hover + cursor: pointer diff --git a/extensions/templates/extensions/list.html b/extensions/templates/extensions/list.html index a4be17db..80e63738 100644 --- a/extensions/templates/extensions/list.html +++ b/extensions/templates/extensions/list.html @@ -6,33 +6,56 @@ {% block content %}
- {% if author %} -

{% blocktranslate %}Extensions by{% endblocktranslate %} {{ author }}

- {% endif %} - {% if team %} -

{% blocktranslate %}Extensions by{% endblocktranslate %} {{ team.name }}

- {% endif %} - {% if tag %} -

- {% blocktranslate %}Extensions with the tag{% endblocktranslate %} - {% include "extensions/components/badge_tag.html" %} -

- {% endif %} {% if type %} -

{{ type }}

- {% endif %} - {% if request.GET.q %} -

{{ page_obj.paginator.count }} result{{ page_obj.paginator.count | pluralize }} for {{ request.GET.q }}

+
+

{{ type }}

+ + {# TODO: @back-end add sort based on select options #} +
+ + +
+
+ {% else %} + {% if author %} +

{% blocktranslate %}Extensions by{% endblocktranslate %} {{ author }}

+ {% endif %} + {% if team %} +

{% blocktranslate %}Extensions by{% endblocktranslate %} {{ team.name }}

+ {% endif %} + {% if tag %} +

+ {% blocktranslate %}Extensions with the tag{% endblocktranslate %} + {% include "extensions/components/badge_tag.html" %} +

+ {% endif %} + {% if request.GET.q %} +

{{ page_obj.paginator.count }} result{{ page_obj.paginator.count | pluralize }} for {{ request.GET.q }}

+ {% endif %} {% endif %} {% if tags %} -
+
{# TODO: @back-end add conditional link to either Add-ons or Themes pages #} - - All + +
+ All +
+
+ 1 +
{% for list_tag in tags %} @@ -40,11 +63,9 @@ {{ list_tag.name }}
{# TODO: @back-end add tags count dynamic #} - {% comment %} -
- 1 -
- {% endcomment %} +
+ 1 +
{% endfor %}
-- 2.30.2 From e604250d1e827d8294c2f10f6594799cf371e6f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Lente?= Date: Tue, 14 May 2024 17:10:34 +0200 Subject: [PATCH 02/32] UI: Make template list badge tags count width resized with content Part of #126 --- common/static/common/styles/_utilities.sass | 8 ++++++++ extensions/templates/extensions/list.html | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/common/static/common/styles/_utilities.sass b/common/static/common/styles/_utilities.sass index b0da3e10..ae5f0dfb 100644 --- a/common/static/common/styles/_utilities.sass +++ b/common/static/common/styles/_utilities.sass @@ -31,9 +31,17 @@ overflow: hidden padding: 0 !important +@for $i from 1 through 5 + .mw-#{$i} + min-width: var(--spacer-#{$i}) + .opacity-50 opacity: 0.5 +@for $i from 1 through 5 + .rounded-#{$i} + border-radius: var(--spacer-#{$i}) + .show opacity: 1 diff --git a/extensions/templates/extensions/list.html b/extensions/templates/extensions/list.html index 80e63738..2789bdd1 100644 --- a/extensions/templates/extensions/list.html +++ b/extensions/templates/extensions/list.html @@ -63,7 +63,7 @@ {{ list_tag.name }}
{# TODO: @back-end add tags count dynamic #} -
+
1
-- 2.30.2 From d0eb57cb7f29b6f681cf9865503a26edd73a21c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Lente?= Date: Fri, 17 May 2024 15:19:56 +0200 Subject: [PATCH 03/32] UI: Change template list box filter sort layout and style Change template list box filter sort layout and style by making it horizontal and add horizontal scrolling. --- common/static/common/styles/_box.sass | 8 ++++++++ extensions/templates/extensions/list.html | 6 ++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/common/static/common/styles/_box.sass b/common/static/common/styles/_box.sass index 68d1a477..95d8a0cd 100644 --- a/common/static/common/styles/_box.sass +++ b/common/static/common/styles/_box.sass @@ -1,5 +1,6 @@ .box-outline @extend .box + background-color: transparent border: var(--border-width) solid var(--border-color) box-shadow: none @@ -7,3 +8,10 @@ .box &.is-flatpage +style-rich-text + +.box-filter-sort + .box-filter-sort-end + background: linear-gradient(270deg, rgba(48, 53, 69, 1) 0%, rgba(0, 0, 0, 0) 100%) // --box-bg-color + + .box-filter-sort-start + background: linear-gradient(90deg, rgba(48, 53, 69, 1) 0%, rgba(0, 0, 0, 0) 100%) // --box-bg-color diff --git a/extensions/templates/extensions/list.html b/extensions/templates/extensions/list.html index 2789bdd1..a00cb7c7 100644 --- a/extensions/templates/extensions/list.html +++ b/extensions/templates/extensions/list.html @@ -46,8 +46,9 @@ {% if tags %}
-
-
+
+
+
{# TODO: @back-end add conditional link to either Add-ons or Themes pages #}
@@ -69,6 +70,7 @@ {% endfor %}
+
-- 2.30.2 From 4ccddebb5f8680f902e85f608f9442ea6bccc2c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Lente?= Date: Fri, 17 May 2024 15:37:50 +0200 Subject: [PATCH 04/32] UI: Make template list boxes filter and sort layout horizontal one line --- extensions/templates/extensions/list.html | 78 ++++++++++++----------- 1 file changed, 40 insertions(+), 38 deletions(-) diff --git a/extensions/templates/extensions/list.html b/extensions/templates/extensions/list.html index a00cb7c7..0bd44958 100644 --- a/extensions/templates/extensions/list.html +++ b/extensions/templates/extensions/list.html @@ -7,24 +7,7 @@
{% if type %} -
-

{{ type }}

- - {# TODO: @back-end add sort based on select options #} -
- - -
-
+

{{ type }}

{% else %} {% if author %}

{% blocktranslate %}Extensions by{% endblocktranslate %} {{ author }}

@@ -46,31 +29,50 @@ {% if tags %}
-
-
-
- {# TODO: @back-end add conditional link to either Add-ons or Themes pages #} - -
- All -
-
- 1 -
-
- {% for list_tag in tags %} - +
+
+ {# TODO: add js hide box filter sort end and start before and after scroll #} +
+
+
+ {# TODO: @back-end add sort based on select options #} +
+ + +
-
@@ -79,7 +81,7 @@
{% if object_list %} -
+
{% for extension in object_list %} {% include "extensions/components/card.html" with show_type=False %} {% endfor %} -- 2.30.2 From d21595e2139215df70f57f4e5ca6fb8f9fad1131 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Lente?= Date: Fri, 17 May 2024 16:30:27 +0200 Subject: [PATCH 05/32] UI: Add style scrollbar for Chromium browsers Normalize scrollbar display in Chromium browsers. --- common/static/common/styles/_extension.sass | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/common/static/common/styles/_extension.sass b/common/static/common/styles/_extension.sass index 0ed02e20..5245cfcb 100644 --- a/common/static/common/styles/_extension.sass +++ b/common/static/common/styles/_extension.sass @@ -40,6 +40,20 @@ --color-danger-bg: hsl(0deg, 25%, 28%) --color-danger-bg-hover: hsl(0deg, 25%, 32%) +/* Scrollbar. */ +/* Style scrollbar in Chromium browsers. */ +::-webkit-scrollbar + height: 0.2rem + width: 0.2rem + + &-thumb + background-color: var(--color-text-secondary) + border-radius: 0.1rem + + &-track + background-color: transparent + border-radius: 0.1rem + /* Site-wide annoncements */ .site-announcement-alpha @extend .alert -- 2.30.2 From 64b5ddf9443506e301678fa446cda20795ece60a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Lente?= Date: Fri, 17 May 2024 17:58:37 +0200 Subject: [PATCH 06/32] UI: Add js app function scrollFilters Create function scrollFilters to manage display of box filter sort's start and end gradients. --- common/static/common/scripts/app.js | 20 ++++++++++++++++++++ extensions/templates/extensions/list.html | 7 +++---- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/common/static/common/scripts/app.js b/common/static/common/scripts/app.js index fd09c59d..95d45637 100644 --- a/common/static/common/scripts/app.js +++ b/common/static/common/scripts/app.js @@ -111,12 +111,32 @@ init(); } + + // Create function scrollFilters to manage display of box filter sort's start and end gradients + function scrollFilters() { + const boxFilterSortBtns = document.querySelector('.js-box-filter-sort-btns'); + const boxFilterSortEnd = document.querySelector('.js-box-filter-sort-end'); + const boxFilterSortStart = document.querySelector('.js-box-filter-sort-start'); + + boxFilterSortBtns.addEventListener('scroll', function() { + if (boxFilterSortBtns.scrollLeft == 0) { + boxFilterSortStart.classList.add('d-none'); + } else if (boxFilterSortBtns.scrollLeft + boxFilterSortBtns.clientWidth == boxFilterSortBtns.scrollWidth) { + boxFilterSortEnd.classList.add('d-none'); + } else { + boxFilterSortEnd.classList.remove('d-none'); + boxFilterSortStart.classList.remove('d-none'); + } + }); + } + // Create function init function init() { agreeWithTerms(); btnBack(); commentForm(); copyInstallUrl(); + scrollFilters(); } document.addEventListener('DOMContentLoaded', function() { diff --git a/extensions/templates/extensions/list.html b/extensions/templates/extensions/list.html index 0bd44958..e602397e 100644 --- a/extensions/templates/extensions/list.html +++ b/extensions/templates/extensions/list.html @@ -31,9 +31,8 @@
- {# TODO: add js hide box filter sort end and start before and after scroll #} -
-
+
+
{# TODO: @back-end add conditional link to either Add-ons or Themes pages #}
@@ -55,7 +54,7 @@ {% endfor %}
-
+
{# TODO: @back-end add sort based on select options #} -- 2.30.2 From 74453f4d4fadc4a784a3f5517a1f1547a6cf0313 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Lente?= Date: Wed, 22 May 2024 16:42:34 +0200 Subject: [PATCH 07/32] Fix: Template list post-merge template tags issues --- extensions/templates/extensions/list.html | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/extensions/templates/extensions/list.html b/extensions/templates/extensions/list.html index 9a9f72ff..e3d0fec0 100644 --- a/extensions/templates/extensions/list.html +++ b/extensions/templates/extensions/list.html @@ -34,9 +34,6 @@
- {% else %} - -
- All -
-
- 1 -
-
- {% endif %} - - {% for list_tag in tags %} {% for list_tag in tags %} + {# TODO @back-end: Find a proper way to get the plural tag type to build the URL. #}
{{ list_tag.name }} -- 2.30.2 From 0ed83d96b52da169498c2d52c4802fbd563ce9e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Lente?= Date: Wed, 22 May 2024 17:20:56 +0200 Subject: [PATCH 08/32] UI: Change template list tags display bar to js dropdown base --- common/static/common/styles/_navigation.sass | 3 + extensions/templates/extensions/list.html | 60 +++++++++++++------- 2 files changed, 42 insertions(+), 21 deletions(-) diff --git a/common/static/common/styles/_navigation.sass b/common/static/common/styles/_navigation.sass index d571c501..c3f77b05 100644 --- a/common/static/common/styles/_navigation.sass +++ b/common/static/common/styles/_navigation.sass @@ -1,3 +1,6 @@ +.dropdown-toggle + height: calc(var(--spacer) * 2) + .navbar-search input min-width: calc(var(--spacer) * 4) diff --git a/extensions/templates/extensions/list.html b/extensions/templates/extensions/list.html index e3d0fec0..89948787 100644 --- a/extensions/templates/extensions/list.html +++ b/extensions/templates/extensions/list.html @@ -30,35 +30,53 @@
-
-
-
- -
+
+
+
Filters
+ -
- 1 -
-
- {% for list_tag in tags %} - {# TODO @back-end: Find a proper way to get the plural tag type to build the URL. #} - -
- {{ list_tag.name }} -
{# TODO: @back-end add tags count dynamic #} -
+
1
-
- {% endfor %} + + + +
-
{# TODO: @back-end add sort based on select options #} -
+
+ {# TODO: change to web-assets dropdown #} - - - - - - - - - -
+ +
{% endif %} -- 2.30.2 From 3640061d7d3eac14daa19ecb403ad95bec48b39f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Lente?= Date: Wed, 22 May 2024 18:16:35 +0200 Subject: [PATCH 12/32] UI: Change template list sort select layout Make template list sort select layout horizontally aligned with headings. --- extensions/templates/extensions/list.html | 134 +++++++++++----------- 1 file changed, 66 insertions(+), 68 deletions(-) diff --git a/extensions/templates/extensions/list.html b/extensions/templates/extensions/list.html index 30b05923..0a02af36 100644 --- a/extensions/templates/extensions/list.html +++ b/extensions/templates/extensions/list.html @@ -7,31 +7,13 @@
{% if type %} -

{{ type }}

- {% else %} - {% if author %} -

{% blocktranslate %}Extensions by{% endblocktranslate %} {{ author }}

- {% endif %} - {% if team %} -

{% blocktranslate %}Extensions by{% endblocktranslate %} {{ team.name }}

- {% endif %} - {% if tag %} -

- {% blocktranslate %}Extensions with the tag{% endblocktranslate %} - {% include "extensions/components/badge_tag.html" %} -

- {% endif %} - {% if request.GET.q %} -

{{ page_obj.paginator.count }} result{{ page_obj.paginator.count | pluralize }} for {{ request.GET.q }}

- {% endif %} - {% endif %} +
+
+

{{ type }}

- {% if tags %} -
-
-
-
- - -
+ {% endif %}
+ {% else %} + {% if author %} +

{% blocktranslate %}Extensions by{% endblocktranslate %} {{ author }}

+ {% endif %} + {% if team %} +

{% blocktranslate %}Extensions by{% endblocktranslate %} {{ team.name }}

+ {% endif %} + {% if tag %} +

+ {% blocktranslate %}Extensions with the tag{% endblocktranslate %} + {% include "extensions/components/badge_tag.html" %} +

+ {% endif %} + {% if request.GET.q %} +

{{ page_obj.paginator.count }} result{{ page_obj.paginator.count | pluralize }} for {{ request.GET.q }}

+ {% endif %} {% endif %}
-- 2.30.2 From 840b38014f3b61c1b6c9a57c0fc9aebe0c13c4c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Lente?= Date: Thu, 23 May 2024 12:10:37 +0200 Subject: [PATCH 13/32] Cleanup: Remove js app unused function scrollFilters Remove js app unused function scrollFilters, that is no longer needed. --- common/static/common/scripts/app.js | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/common/static/common/scripts/app.js b/common/static/common/scripts/app.js index 8a105e10..53e80078 100644 --- a/common/static/common/scripts/app.js +++ b/common/static/common/scripts/app.js @@ -131,24 +131,6 @@ init(); } - // Create function scrollFilters to manage display of box filter sort's start and end gradients - function scrollFilters() { - const boxFilterSortBtns = document.querySelector('.js-box-filter-sort-btns'); - const boxFilterSortEnd = document.querySelector('.js-box-filter-sort-end'); - const boxFilterSortStart = document.querySelector('.js-box-filter-sort-start'); - - boxFilterSortBtns.addEventListener('scroll', function() { - if (boxFilterSortBtns.scrollLeft == 0) { - boxFilterSortStart.classList.add('d-none'); - } else if (boxFilterSortBtns.scrollLeft + boxFilterSortBtns.clientWidth == boxFilterSortBtns.scrollWidth) { - boxFilterSortEnd.classList.add('d-none'); - } else { - boxFilterSortEnd.classList.remove('d-none'); - boxFilterSortStart.classList.remove('d-none'); - } - }); - } - // Create function init function init() { agreeWithTerms(); @@ -156,7 +138,6 @@ btnBack(); commentForm(); copyInstallUrl(); - scrollFilters(); } document.addEventListener('DOMContentLoaded', function() { -- 2.30.2 From 3f8a875f3b28bfc164fb71f554710f6f4ef648a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Lente?= Date: Thu, 23 May 2024 12:20:27 +0200 Subject: [PATCH 14/32] Fix: Template list headings and filter dropdowns layout Fix template list headings and filter dropdowns layout if tag is selected. --- extensions/templates/extensions/list.html | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/extensions/templates/extensions/list.html b/extensions/templates/extensions/list.html index 0a02af36..aad43418 100644 --- a/extensions/templates/extensions/list.html +++ b/extensions/templates/extensions/list.html @@ -6,10 +6,17 @@ {% block content %}
- {% if type %} + {% if tag or type %}
-
-

{{ type }}

+
+ {% if type %} +

{{ type }}

+ {% else %} +

+ {% blocktranslate %}Extensions with the tag{% endblocktranslate %} + {% include "extensions/components/badge_tag.html" %} +

+ {% endif %} {% if tags %}
@@ -42,7 +49,6 @@ 1
- {% else %} {% endif %} @@ -112,12 +118,6 @@ {% if team %}

{% blocktranslate %}Extensions by{% endblocktranslate %} {{ team.name }}

{% endif %} - {% if tag %} -

- {% blocktranslate %}Extensions with the tag{% endblocktranslate %} - {% include "extensions/components/badge_tag.html" %} -

- {% endif %} {% if request.GET.q %}

{{ page_obj.paginator.count }} result{{ page_obj.paginator.count | pluralize }} for {{ request.GET.q }}

{% endif %} -- 2.30.2 From 0c01330587b907ab1dba1ea944f71e81212ce250 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Lente?= Date: Thu, 23 May 2024 12:28:45 +0200 Subject: [PATCH 15/32] UI: Add style component navigation overflow scrolling --- common/static/common/styles/_navigation.sass | 4 ++++ extensions/templates/extensions/list.html | 8 ++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/common/static/common/styles/_navigation.sass b/common/static/common/styles/_navigation.sass index c3f77b05..8a00a0a8 100644 --- a/common/static/common/styles/_navigation.sass +++ b/common/static/common/styles/_navigation.sass @@ -1,6 +1,10 @@ .dropdown-toggle height: calc(var(--spacer) * 2) +.dropdown-menu-filter-sort + max-height: calc(var(--spacer) * 28) + overflow: auto + .navbar-search input min-width: calc(var(--spacer) * 4) diff --git a/extensions/templates/extensions/list.html b/extensions/templates/extensions/list.html index aad43418..fea20880 100644 --- a/extensions/templates/extensions/list.html +++ b/extensions/templates/extensions/list.html @@ -21,7 +21,7 @@ {% if tags %}
{% block footer %} + + {% include "_footer.html" %} {% endblock footer %} diff --git a/common/templates/common/components/field.html b/common/templates/common/components/field.html index 5c8e347f..826cc098 100644 --- a/common/templates/common/components/field.html +++ b/common/templates/common/components/field.html @@ -14,7 +14,7 @@ {% if not field.is_hidden %} {% endif %} @@ -24,7 +24,7 @@ {% if not field.is_hidden %} {% endif %} diff --git a/common/templates/common/components/meta.html b/common/templates/common/components/meta.html index f1681ac4..cd0e8b35 100644 --- a/common/templates/common/components/meta.html +++ b/common/templates/common/components/meta.html @@ -3,9 +3,9 @@ {% load common %} {% with default_title="Blender Extensions" %} - {% with default_author="Blender Institute" %} + {% with default_author="Blender Foundation" %} - {% with default_description="Blender Extensions is a web based service developed by Blender Institute that allows people to share open source add-ons for Blender." %} + {% with default_description="Blender Extensions is a web based service developed by Blender Foundation that allows people to share open source add-ons for Blender." %} {% if not image_url %} {% absolute_url default_image_path as image_url %} diff --git a/common/templates/common/components/pagination.html b/common/templates/common/components/pagination.html index b99966e1..8bb900ca 100644 --- a/common/templates/common/components/pagination.html +++ b/common/templates/common/components/pagination.html @@ -1,26 +1,59 @@ {% load common %} {% get_proper_elided_page_range page_obj as page_range %} diff --git a/common/templates/common/components/status.html b/common/templates/common/components/status.html index d8e10276..085c5db1 100644 --- a/common/templates/common/components/status.html +++ b/common/templates/common/components/status.html @@ -1,52 +1,6 @@ -{% with status=object.get_status_display %} - {% if 'incomplete' in status.lower %} -
- - {{ status }} -
- - {% elif 'disabled' in status.lower %} -
- - {{ status }} -
- - {% elif 'deleted' in status.lower %} -
- - {{ status }} -
- - {% elif 'awaiting' in status.lower %} -
- - {{ status }} -
- - {% elif 'approved' in status.lower %} -
- - {{ status }} -
- - {% elif 'confirmed' in status.lower %} -
- {{ status }} -
- - {% elif 'untriaged' in status.lower %} -
- {{ status }} -
- - {% elif 'resolved' in status.lower %} -
- {{ status }} -
- - {% else %} -
- {{ status }} -
- {% endif %} +{% with label=label|default:object.get_status_display slug=slug|default:object.get_status_display|slugify %} +
+ {% if icon %}{% endif %} + {{ label }} +
{% endwith %} diff --git a/common/templates/common/paginator.html b/common/templates/common/paginator.html index 5b7615f1..44e8e5f0 100644 --- a/common/templates/common/paginator.html +++ b/common/templates/common/paginator.html @@ -7,7 +7,7 @@
  • - +
  • {% endif %} @@ -19,7 +19,7 @@ {% if pager.has_next %}
  • - +
  • diff --git a/common/templatetags/common.py b/common/templatetags/common.py index 1e1f7367..614ff0cc 100644 --- a/common/templatetags/common.py +++ b/common/templatetags/common.py @@ -29,6 +29,17 @@ def absolute_url(context, path: str) -> str: return utils.absolutify(path, request=request) +# Allows for example to go to another page of search +# results while keeping the search query. +# Credit: https://stackoverflow.com/questions/46026268/ +@register.simple_tag(takes_context=True) +def query_transform(context, **kwargs): + query = context['request'].GET.copy() + for k, v in kwargs.items(): + query[k] = v + return query.urlencode() + + class PaginationRenderer: def __init__(self, pager): self.pager = pager diff --git a/common/tests/factories/extensions.py b/common/tests/factories/extensions.py index 1da75db1..b49bbbcd 100644 --- a/common/tests/factories/extensions.py +++ b/common/tests/factories/extensions.py @@ -91,8 +91,8 @@ class VersionFactory(DjangoModelFactory): if not extracted: return - tags = Platform.objects.filter(slug__in=extracted) - self.platforms.add(*tags) + platforms = Platform.objects.filter(slug__in=extracted) + self.platforms.add(*platforms) @factory.post_generation def tags(self, create, extracted, **kwargs): diff --git a/common/tests/utils.py b/common/tests/utils.py index 194778b2..e95e4bad 100644 --- a/common/tests/utils.py +++ b/common/tests/utils.py @@ -1,9 +1,13 @@ import itertools +from typing import Tuple import django.urls as urls from django.utils.functional import cached_property from django.utils.regex_helper import normalize +from apitokens.models import UserToken + + try: # Django 2.0 url_resolver_types = (urls.URLResolver,) DJANGO_2 = True @@ -109,3 +113,11 @@ class CheckFilePropertiesMixin: self.assertEqual(file.original_name, kwargs.get('original_name')) if 'size_bytes' in kwargs: self.assertEqual(file.size_bytes, kwargs.get('size_bytes')) + + +def create_user_token(*args, **kwargs) -> Tuple['UserToken', str]: + token_key = UserToken.generate_token_key() + kwargs['token_hash'] = UserToken.generate_hash(token_key) + kwargs['token_prefix'] = UserToken.generate_token_prefix(token_key) + token = UserToken.objects.create(*args, **kwargs) + return token, token_key diff --git a/constants/base.py b/constants/base.py index 7cc04781..4b05fb51 100644 --- a/constants/base.py +++ b/constants/base.py @@ -10,7 +10,7 @@ EXTENSION_TYPE_CHOICES = Choices( ('BPY', 1, _('Add-on')), ('THEME', 2, _('Theme')), ) -STATUS_INCOMPLETE = 1 +STATUS_DRAFT = 1 STATUS_AWAITING_REVIEW = 2 STATUS_APPROVED = 3 STATUS_DISABLED = 4 @@ -18,7 +18,7 @@ STATUS_DISABLED_BY_AUTHOR = 5 # Extension statuses EXTENSION_STATUS_CHOICES = Choices( - ('INCOMPLETE', STATUS_INCOMPLETE, _('Incomplete')), + ('DRAFT', STATUS_DRAFT, _('Draft')), ('AWAITING_REVIEW', STATUS_AWAITING_REVIEW, _('Awaiting Review')), ('APPROVED', STATUS_APPROVED, _('Approved')), ('DISABLED', STATUS_DISABLED, _('Disabled by staff')), diff --git a/extensions/forms.py b/extensions/forms.py index 42060ee9..17f88287 100644 --- a/extensions/forms.py +++ b/extensions/forms.py @@ -163,6 +163,18 @@ class ExtensionUpdateForm(forms.ModelForm): self.add_preview_formset.error_messages['too_few_forms'] = self.msg_need_previews + user_teams = self.request.user.teams.all() + if self.request.user in self.instance.authors.all() and len(user_teams) > 0: + team_slug = None + if self.instance.team: + team_slug = self.instance.team.slug + choices = [(None, 'None'), *[(team.slug, team.name) for team in user_teams]] + self.fields['team'] = forms.ChoiceField( + choices=choices, + required=False, + initial=team_slug, + ) + def is_valid(self, *args, **kwargs) -> bool: """Validate all nested forms and form(set)s first.""" if 'submit_draft' in self.data: @@ -198,6 +210,27 @@ class ExtensionUpdateForm(forms.ModelForm): return all(is_valid_flags) + def clean_team(self): + # don't modify instance if the field value wasn't sent + # empty value reset the team + if 'team' in self.data: + # TODO permissions check + # shouldn't happen normally: the form doesn't render the select + if self.request.user not in self.instance.authors.all(): + self.add_error('team', _('Not allowed to set the team')) + return + + team_slug = self.cleaned_data['team'] + if team_slug: + team = self.request.user.teams.filter(slug=team_slug).first() + if not team: + self.add_error('team', _('User does not belong to the team')) + return + else: + self.instance.team = team + else: + self.instance.team = None + def clean(self): """Perform additional validation and status changes.""" super().clean() @@ -206,7 +239,7 @@ class ExtensionUpdateForm(forms.ModelForm): if self.instance.status != self.instance.STATUSES.AWAITING_REVIEW: self.add_error(None, self.msg_cannot_convert_to_draft) else: - self.instance.status = self.instance.STATUSES.INCOMPLETE + self.instance.status = self.instance.STATUSES.DRAFT self.instance.converted_to_draft = True # Send the extension and version to the review, if possible diff --git a/extensions/migrations/0001_initial.py b/extensions/migrations/0001_initial.py index 9f6f0293..96fdcef8 100644 --- a/extensions/migrations/0001_initial.py +++ b/extensions/migrations/0001_initial.py @@ -29,7 +29,7 @@ class Migration(migrations.Migration): ('name', models.CharField(max_length=255, unique=True)), ('description', models.TextField(help_text='\n

    Markdown\nis supported.

    \n')), ('tagline', models.CharField(help_text='A very short description', max_length=128)), - ('status', models.PositiveSmallIntegerField(choices=[(1, 'Incomplete'), (2, 'Awaiting Review'), (3, 'Approved'), (4, 'Disabled by staff'), (5, 'Disabled by author')], default=1)), + ('status', models.PositiveSmallIntegerField(choices=[(1, 'Draft'), (2, 'Awaiting Review'), (3, 'Approved'), (4, 'Disabled by staff'), (5, 'Disabled by author')], default=1)), ('doc_url', models.URLField(blank=True, help_text='URL of the documentation', null=True)), ('tracker_url', models.URLField(blank=True, help_text='URL of the issue tracker', null=True)), ('homepage_url', models.URLField(blank=True, help_text='URL of the homepage', null=True)), diff --git a/extensions/models.py b/extensions/models.py index 26896d48..15eeba63 100644 --- a/extensions/models.py +++ b/extensions/models.py @@ -16,6 +16,7 @@ from constants.base import ( EXTENSION_STATUS_CHOICES, EXTENSION_TYPE_CHOICES, EXTENSION_TYPE_SLUGS, + EXTENSION_TYPE_SLUGS_SINGULAR, FILE_STATUS_CHOICES, ) import common.help_texts @@ -128,12 +129,19 @@ class ExtensionManager(models.Manager): def unlisted(self): return self.exclude(status=self.model.STATUSES.APPROVED) - def authored_by(self, user_id: int): - return self.filter(maintainer__user_id=user_id) + def _authored_by_filter(self, user): + filter = Q(maintainer__user_id=user.pk) + user_teams = user.teams.all() + if user_teams: + filter = filter | Q(team__in=[t.pk for t in user_teams]) + return filter - def listed_or_authored_by(self, user_id: int): + def authored_by(self, user): + return self.filter(self._authored_by_filter(user)).distinct() + + def listed_or_authored_by(self, user): return self.filter( - Q(status=self.model.STATUSES.APPROVED) | Q(maintainer__user_id=user_id) + Q(status=self.model.STATUSES.APPROVED) | self._authored_by_filter(user) ).distinct() @@ -183,7 +191,7 @@ class Extension(CreatedModifiedMixin, RatingMixin, TrackChangesMixin, models.Mod ) previews = FilterableManyToManyField('files.File', through='Preview', related_name='extensions') - status = models.PositiveSmallIntegerField(choices=STATUSES, default=STATUSES.INCOMPLETE) + status = models.PositiveSmallIntegerField(choices=STATUSES, default=STATUSES.DRAFT) support = models.URLField( help_text='URL for reporting issues or contact details for support.', null=True, blank=True ) @@ -212,6 +220,10 @@ class Extension(CreatedModifiedMixin, RatingMixin, TrackChangesMixin, models.Mod def type_slug(self) -> str: return EXTENSION_TYPE_SLUGS[self.type] + @property + def type_slug_singular(self) -> str: + return EXTENSION_TYPE_SLUGS_SINGULAR[self.type] + @property def status_slug(self) -> str: return utils.slugify(EXTENSION_STATUS_CHOICES[self.status - 1][1]) @@ -336,23 +348,6 @@ class Extension(CreatedModifiedMixin, RatingMixin, TrackChangesMixin, models.Mod versions = sorted(versions, key=lambda v: v.date_created, reverse=True) return versions[0] - @property - def current_version(self): - """Return the latest public listed version of an extension. - - If the add-on is not public, it can return a listed version awaiting - review (since non-public add-ons should not have public versions). - - If the add-on has not been created yet or is deleted, it returns None. - """ - if not self.id: - return None - try: - return self.version - except ObjectDoesNotExist: - pass - return None - def can_request_review(self): """Return whether an add-on can request a review or not.""" if self.is_disabled or self.status in ( @@ -379,16 +374,20 @@ class Extension(CreatedModifiedMixin, RatingMixin, TrackChangesMixin, models.Mod def should_redirect_to_submit_flow(self): return ( - self.status == self.STATUSES.INCOMPLETE + self.status == self.STATUSES.DRAFT and not self.has_complete_metadata() and self.latest_version is not None ) def has_maintainer(self, user) -> bool: - """Return True if given user is listed as a maintainer.""" + """Return True if given user is listed as a maintainer or is a member of the team.""" if user is None or user.is_anonymous: return False - return user in self.authors.all() + if user in self.authors.all(): + return True + if self.team and user in self.team.users.all(): + return True + return False def can_rate(self, user) -> bool: """Return True if given user can rate this extension. @@ -631,12 +630,14 @@ class Version(CreatedModifiedMixin, RatingMixin, TrackChangesMixin, models.Model @property def download_url(self) -> str: + filename = f'{self.extension.type_slug_singular}-{self.extension.slug}-v{self.version}.zip' return reverse( 'extensions:version-download', kwargs={ 'type_slug': self.extension.type_slug, 'slug': self.extension.slug, 'version': self.version, + 'filename': filename, }, ) diff --git a/extensions/signals.py b/extensions/signals.py index 54034d79..928d22a4 100644 --- a/extensions/signals.py +++ b/extensions/signals.py @@ -104,7 +104,7 @@ def _set_is_listed( return if extension.status == extensions.models.Extension.STATUSES.APPROVED and not new_is_listed: - extension.status = extensions.models.Extension.STATUSES.INCOMPLETE + extension.status = extensions.models.Extension.STATUSES.DRAFT logger.info('Extension pk=%s becomes listed', extension.pk) extension.is_listed = new_is_listed diff --git a/extensions/static/extensions/scripts/manage.js b/extensions/static/extensions/scripts/manage.js index e793bff3..dc717d9a 100644 --- a/extensions/static/extensions/scripts/manage.js +++ b/extensions/static/extensions/scripts/manage.js @@ -38,7 +38,7 @@ function appendImageUploadForm() {
    - +
    diff --git a/extensions/templates/extensions/base.html b/extensions/templates/extensions/base.html index 3b0bd322..1e1f407c 100644 --- a/extensions/templates/extensions/base.html +++ b/extensions/templates/extensions/base.html @@ -18,7 +18,10 @@
    {% endblock hero_breadcrumbs %} -

    {% include "extensions/components/icon.html" with classes="icon-lg" %} {{ extension.name }}

    +

    + {% include "extensions/components/icon.html" with classes="icon-lg me-2" %} + {{ extension.name }} +

    {% if latest.tagline %} @@ -39,45 +42,44 @@ {% endif %} {% if not extension.is_approved %} - - {{ extension.get_status_display }} - + {% include "common/components/status.html" with object=extension classes="badge-outline" %} {% endif %}
  • {% block hero_tabs %} -