Extensions list: sort_by parameter #159
12
.gitea/issue_template/bug.yaml
Normal file
12
.gitea/issue_template/bug.yaml
Normal file
@ -0,0 +1,12 @@
|
||||
name: Bug Report
|
||||
about: Report issues for the Extensions Platform website.
|
||||
labels:
|
||||
- "Type/Report"
|
||||
- "Status/Needs Triage"
|
||||
- "Priority/Normal"
|
||||
body:
|
||||
- type: textarea
|
||||
id: body
|
||||
attributes:
|
||||
label: "Description"
|
||||
hide_label: true
|
34
.gitea/issue_template/team.yaml
Normal file
34
.gitea/issue_template/team.yaml
Normal file
@ -0,0 +1,34 @@
|
||||
name: Team Management
|
||||
about: Form to request creation of a team, or joining existing ones.
|
||||
labels:
|
||||
- "Type/Team"
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
### Instructions
|
||||
This form is to create, delete, or join a team.
|
||||
|
||||
You can leave teams by yourself from the [Teams page](https://extensions.blender.org/settings/teams/) on your profile.
|
||||
|
||||
- type: textarea
|
||||
id: body
|
||||
attributes:
|
||||
label: "Description"
|
||||
hide_label: true
|
||||
value: |
|
||||
**Team Management Request**
|
||||
Mark the type of request you have:
|
||||
- [ ] Create a team
|
||||
- [ ] Join a team
|
||||
- [ ] Add a member to my team
|
||||
- [ ] Remove a member from my team
|
||||
- [ ] Delete a team
|
||||
|
||||
**Team Information**
|
||||
Name:
|
||||
URL: e.g. https://extensions.blender.org/team/community/
|
||||
|
||||
**User Details**
|
||||
Name of the user to add/remove:
|
||||
Profile URL:
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -68,6 +68,7 @@ wheels/
|
||||
*.egg
|
||||
*.manifest
|
||||
*.spec
|
||||
*.DS_Store
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
|
@ -1,4 +1,5 @@
|
||||
{% extends 'users/settings/base.html' %}
|
||||
{% block page_title %}Tokens{% endblock %}
|
||||
|
||||
{% block settings %}
|
||||
<h1 class="mb-3">Tokens</h1>
|
||||
|
@ -31,9 +31,6 @@ a.badge-tag
|
||||
border-color: var(--color-text)
|
||||
color: var(--color-text)
|
||||
|
||||
.badge-tag
|
||||
font-size: var(--fs-xs)
|
||||
|
||||
.badge-status
|
||||
&-approved,
|
||||
&-resolved
|
||||
|
@ -369,9 +369,11 @@
|
||||
+padding(3, x)
|
||||
+padding(0, y)
|
||||
transition: background-color var(--transition-speed-fast)
|
||||
vertical-align: baseline
|
||||
|
||||
a
|
||||
a:not(.btn)
|
||||
color: var(--color-text)
|
||||
display: inline-block
|
||||
+padding(1, y)
|
||||
padding-inline: 0 !important
|
||||
|
||||
@ -390,6 +392,10 @@
|
||||
.ext-review-list-activity
|
||||
width: 1%
|
||||
|
||||
.ext-review-list-status a
|
||||
vertical-align: text-top
|
||||
width: 100%
|
||||
|
||||
.rating-form
|
||||
select
|
||||
color: var(--color-warning)
|
||||
|
@ -102,7 +102,7 @@
|
||||
|
||||
/* Lightbox component. */
|
||||
.galleria
|
||||
--galleria-btn-width: 100px
|
||||
--galleria-btn-width: 12.8rem
|
||||
--galleria-media-max-width: 100%
|
||||
|
||||
+media-lg
|
||||
@ -148,14 +148,18 @@
|
||||
&.btn-close
|
||||
font-size: 3.2rem
|
||||
height: 20vh
|
||||
max-height: 80px
|
||||
max-width: 80px
|
||||
max-height: 8.0rem
|
||||
max-width: var(--galleria-btn-width)
|
||||
position: absolute
|
||||
right: 0
|
||||
top: 0
|
||||
width: 80px
|
||||
width: var(--galleria-btn-width)
|
||||
z-index: 2
|
||||
|
||||
&.btn-close,
|
||||
&.btn-next,
|
||||
text-align: right
|
||||
|
||||
&.btn-next,
|
||||
&.btn-prev
|
||||
top: 50%
|
||||
@ -166,6 +170,12 @@
|
||||
|
||||
&.btn-prev
|
||||
left: 0
|
||||
text-align: left
|
||||
|
||||
[class^="i-"]::before,
|
||||
[class*=" i-"]::before
|
||||
margin-left: 0
|
||||
margin-right: 0
|
||||
|
||||
.underlay
|
||||
background-color: rgba(0,0,0,0.9)
|
||||
|
@ -186,7 +186,7 @@
|
||||
<li class="dropdown-divider"></li>
|
||||
|
||||
<li>
|
||||
<a href="https://projects.blender.org/infrastructure/extensions-website/issues" class="dropdown-item">
|
||||
<a href="https://projects.blender.org/infrastructure/extensions-website/issues/new?template=.gitea/issue_template/bug.yaml" class="dropdown-item" target="_blank">
|
||||
<i class="i-alert-triangle"></i> {% trans 'Report Problem' %}
|
||||
</a>
|
||||
</li>
|
||||
|
@ -656,10 +656,9 @@ class Version(CreatedModifiedMixin, RatingMixin, TrackChangesMixin, models.Model
|
||||
# TODO: actual signed URLs?
|
||||
return self.file.source.url
|
||||
|
||||
@property
|
||||
def download_url(self) -> str:
|
||||
def download_url(self, append_repository=True) -> str:
|
||||
filename = f'{self.extension.type_slug_singular}-{self.extension.slug}-v{self.version}.zip'
|
||||
return reverse(
|
||||
download_url = reverse(
|
||||
'extensions:version-download',
|
||||
kwargs={
|
||||
'type_slug': self.extension.type_slug,
|
||||
@ -668,6 +667,9 @@ class Version(CreatedModifiedMixin, RatingMixin, TrackChangesMixin, models.Model
|
||||
'filename': filename,
|
||||
},
|
||||
)
|
||||
if append_repository:
|
||||
download_url += '?repository=/api/v1/extensions/'
|
||||
return download_url
|
||||
|
||||
def get_delete_url(self) -> str:
|
||||
return reverse(
|
||||
|
@ -69,9 +69,11 @@
|
||||
{% trans "Reviews" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if extension.latest_version != None %}
|
||||
<a href="{{ extension.get_versions_url }}" class="{% if '/versions/' in request.get_full_path %}is-active{% endif %}">
|
||||
{% trans "Version History" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="btn-row hero-tabs-admin mb-3 mb-md-0">
|
||||
{% if is_maintainer %}
|
||||
|
@ -1,6 +1,6 @@
|
||||
{% load extensions %}
|
||||
|
||||
<a href="{% url "extensions:by-tag" tag_slug=tag.slug %}" title="{{ tag.name }}"
|
||||
class="badge badge-tag{% if not small %} badge-lg{% endif %} {{ class }}">
|
||||
class="badge badge-tag {{ classes }}">
|
||||
{{ tag.name }}
|
||||
</a>
|
||||
|
@ -66,7 +66,7 @@
|
||||
<ul class="flex-wrap">
|
||||
{% for tag in latest.tags.all %}
|
||||
<li class="mb-1">
|
||||
{% include "extensions/components/badge_tag.html" with small=True version=latest %}
|
||||
{% include "extensions/components/badge_tag.html" with version=latest classes="badge-sm" %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
@ -1,9 +1,9 @@
|
||||
<div class="ext-detail-permissions">
|
||||
<dt>Permissions</dt>
|
||||
{% if version.permissions.all %}
|
||||
{% if permissions %}
|
||||
<dd>This version requests the following:
|
||||
<ul>
|
||||
{% for p in version.permissions.all %}
|
||||
{% for p in permissions %}
|
||||
<li>
|
||||
<div>
|
||||
<i class="i-permission-{{ p.slug }}"></i>
|
||||
|
@ -104,14 +104,14 @@
|
||||
|
||||
<div class="dl-row">
|
||||
<div class="dl-col">
|
||||
{% include "extensions/components/detail_card_version_permissions.html" with version=version %}
|
||||
{% include "extensions/components/detail_card_version_permissions.html" with permissions=version.permissions.all %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dl-row">
|
||||
<dd class="ext-detail-info-tags">
|
||||
{% if version.tags.count %}
|
||||
{% include "extensions/components/tags.html" with small=True version=version %}
|
||||
{% include "extensions/components/tags.html" with version=version %}
|
||||
{% else %}
|
||||
No tags.
|
||||
{% endif %}
|
||||
|
@ -66,11 +66,12 @@
|
||||
<hr class="my-4">
|
||||
<section id="permissions" class="ext-detail-permissions">
|
||||
<h2 class="mb-3">{% trans "Permissions" %}</h2>
|
||||
{% if latest.permissions.all %}
|
||||
{% with permissions=latest.permissions.all %}
|
||||
{% if permissions %}
|
||||
<div class="box">
|
||||
<p>This extension requests the following permission{{ latest.permissions.all|pluralize }}:</p>
|
||||
<p>This extension requests the following permission{{ permissions|pluralize }}:</p>
|
||||
<ul>
|
||||
{% for p in latest.permissions.all %}
|
||||
{% for p in permissions %}
|
||||
<li>
|
||||
<div>
|
||||
<i class="i-permission-{{ p.slug }}"></i>
|
||||
@ -86,6 +87,7 @@
|
||||
{% else %}
|
||||
<p class="mb-0"><i class="i-check me-0"></i> This extension does not require special permissions.</p>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
</section>
|
||||
{% endif %}
|
||||
{% endblock extension_permissions %}
|
||||
@ -249,7 +251,9 @@
|
||||
</button>
|
||||
</div>
|
||||
<small class="d-block text-center w-100">
|
||||
or manually <a class="text-muted text-underline" href="{{ request.scheme }}://{{ request.get_host }}{{ latest.download_url }}" download="{{ latest.download_name }}">download</a> and install it.
|
||||
{# TODO @front-end: Replace URL of the manual /dev/ with /latest/. #}
|
||||
...or <a class="text-underline text-primary" href="{{ request.scheme }}://{{ request.get_host }}{{ latest.download_url }}" download="{{ latest.download_name }}">download</a>
|
||||
and <a class="text-underline text-primary" href="https://docs.blender.org/manual/en/dev/editors/preferences/extensions.html#install" target="_blank">Install from Disk</a>
|
||||
</small>
|
||||
</div>
|
||||
{% else %}
|
||||
|
@ -1,5 +1,5 @@
|
||||
{% extends "common/base.html" %}
|
||||
{% load cache i18n %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block page_title %}Extensions{% endblock page_title %}
|
||||
|
||||
@ -32,7 +32,6 @@
|
||||
{% endblock hero %}
|
||||
|
||||
{% block content %}
|
||||
{% cache 60 home %}
|
||||
<section class="mt-3">
|
||||
<div class="d-flex">
|
||||
<h2>
|
||||
@ -78,5 +77,4 @@
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
{% endcache %}
|
||||
{% endblock content %}
|
||||
|
@ -20,6 +20,18 @@
|
||||
const input = document.getElementById('id_{{ image_form.prefix }}-source');
|
||||
const previewEl = document.getElementsByClassName('{{ image_form.prefix }}-preview')[0];
|
||||
const previewElIcon = previewEl.querySelector('.js-i-image');
|
||||
const previewElComputedStyle = window.getComputedStyle(previewEl);
|
||||
const previewElBgImg = previewElComputedStyle.getPropertyValue('background-image');
|
||||
|
||||
function hidePreviewElIcon() {
|
||||
previewElIcon.classList.add('d-none');
|
||||
}
|
||||
|
||||
// Check if previewElBgImg is set
|
||||
if (previewElBgImg.length > 10) {
|
||||
// Hide previewElIcon
|
||||
hidePreviewElIcon();
|
||||
}
|
||||
|
||||
input.addEventListener('change', function() {
|
||||
const curFiles = input.files;
|
||||
@ -27,7 +39,8 @@
|
||||
const dataUrl = URL.createObjectURL(curFiles[0]);
|
||||
|
||||
previewEl.style['background-image'] = `url("${dataUrl}")`;
|
||||
previewElIcon.classList.add('d-none');
|
||||
|
||||
hidePreviewElIcon();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -74,7 +74,7 @@
|
||||
</div>
|
||||
<div class="dl-row">
|
||||
<div class="dl-col">
|
||||
{% include "extensions/components/detail_card_version_permissions.html" with version=version %}
|
||||
{% include "extensions/components/detail_card_version_permissions.html" with permissions=version.permissions.all %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="dl-row">
|
||||
|
@ -66,7 +66,7 @@ class VersionUploadAPITest(APITestCase):
|
||||
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
||||
self.assertEqual(
|
||||
response.data['message'],
|
||||
f'Extension "{other_extension.extension_id}" not maintained by user "{self.user.full_name}"',
|
||||
f'Extension "{other_extension.extension_id}" not maintained by user "{self.user.username}"',
|
||||
)
|
||||
|
||||
def test_version_upload_extension_does_not_exist(self):
|
||||
|
@ -122,12 +122,20 @@ class ApiViewsTest(_BaseTestCase):
|
||||
create_approved_version()
|
||||
url = reverse('extensions:api')
|
||||
|
||||
# returns 1st and 3rd items
|
||||
json = self.client.get(
|
||||
url + '?platform=windows-amd64',
|
||||
HTTP_ACCEPT='application/json',
|
||||
).json()
|
||||
self.assertEqual(len(json['data']), 2)
|
||||
|
||||
# only returns the 3rd item
|
||||
json = self.client.get(
|
||||
url + '?platform=platform-we-dont-know',
|
||||
HTTP_ACCEPT='application/json',
|
||||
).json()
|
||||
self.assertEqual(len(json['data']), 1)
|
||||
|
||||
def test_blender_version_filter_latest_not_max_version(self):
|
||||
version = create_approved_version(blender_version_min='4.0.1')
|
||||
version.date_created
|
||||
|
@ -27,14 +27,14 @@ class ListedExtensionsSerializer(serializers.ModelSerializer):
|
||||
error_messages = {
|
||||
"invalid_blender_version": "Invalid blender_version: use full semantic versioning like "
|
||||
"4.2.0.",
|
||||
"invalid_platform": "Invalid platform: use notation specified in "
|
||||
"https://developer.blender.org/docs/features/extensions/schema/1.0.0/",
|
||||
}
|
||||
|
||||
class Meta:
|
||||
model = Extension
|
||||
fields = ()
|
||||
|
||||
UNKNOWN_PLATFORM = 'unknown-platform-value'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.request = kwargs.pop('request', None)
|
||||
self.blender_version = kwargs.pop('blender_version', None)
|
||||
@ -53,7 +53,7 @@ class ListedExtensionsSerializer(serializers.ModelSerializer):
|
||||
try:
|
||||
Platform.objects.get(slug=self.platform)
|
||||
except Platform.DoesNotExist:
|
||||
self.fail('invalid_platform')
|
||||
self.platform = self.UNKNOWN_PLATFORM
|
||||
|
||||
def to_representation(self, instance):
|
||||
matching_version = None
|
||||
@ -75,7 +75,8 @@ class ListedExtensionsSerializer(serializers.ModelSerializer):
|
||||
continue
|
||||
platform_slugs = set(p.slug for p in v.platforms.all())
|
||||
# empty platforms field matches any platform filter
|
||||
if self.platform and not (not platform_slugs or self.platform in platform_slugs):
|
||||
# UNKNOWN_PLATFORM matches only empty platforms field
|
||||
if self.platform and (platform_slugs and self.platform not in platform_slugs):
|
||||
continue
|
||||
matching_version = v
|
||||
break
|
||||
@ -91,7 +92,9 @@ class ListedExtensionsSerializer(serializers.ModelSerializer):
|
||||
'tagline': matching_version.tagline,
|
||||
'archive_hash': matching_version.file.original_hash,
|
||||
'archive_size': matching_version.file.size_bytes,
|
||||
'archive_url': self.request.build_absolute_uri(matching_version.download_url),
|
||||
'archive_url': self.request.build_absolute_uri(
|
||||
matching_version.download_url(append_repository=False)
|
||||
),
|
||||
'type': EXTENSION_TYPE_SLUGS_SINGULAR.get(instance.type),
|
||||
'blender_version_min': matching_version.blender_version_min,
|
||||
'blender_version_max': matching_version.blender_version_max,
|
||||
|
@ -5,8 +5,6 @@ from django.contrib import admin
|
||||
from django.template.loader import render_to_string
|
||||
from django.urls import reverse
|
||||
from django.utils.safestring import mark_safe
|
||||
import background_task.admin
|
||||
import background_task.models
|
||||
|
||||
from .models import File, FileValidation
|
||||
import files.signals
|
||||
@ -175,48 +173,3 @@ class FileAdmin(admin.ModelAdmin):
|
||||
return obj.validation.is_ok if hasattr(obj, 'validation') else None
|
||||
|
||||
is_ok.boolean = True
|
||||
|
||||
|
||||
try:
|
||||
admin.site.unregister(background_task.models.Task)
|
||||
admin.site.unregister(background_task.models.CompletedTask)
|
||||
except admin.site.NotRegistered:
|
||||
pass
|
||||
|
||||
|
||||
class TaskMixin:
|
||||
"""Modify a few properties of background tasks displayed in admin."""
|
||||
|
||||
def no_errors(self, obj):
|
||||
"""Replace background_task's "has_error".
|
||||
|
||||
Make Django's red/green boolean icons less confusing
|
||||
in the context of "there's an error during task run".
|
||||
"""
|
||||
return not bool(obj.last_error)
|
||||
|
||||
no_errors.boolean = True
|
||||
|
||||
|
||||
@admin.register(background_task.models.Task)
|
||||
@admin.register(background_task.models.CompletedTask)
|
||||
class TaskAdmin(background_task.admin.TaskAdmin, TaskMixin):
|
||||
date_hierarchy = 'run_at'
|
||||
list_display = [
|
||||
'run_at',
|
||||
'task_name',
|
||||
'task_params',
|
||||
'attempts',
|
||||
'no_errors',
|
||||
'locked_by',
|
||||
'locked_by_pid_running',
|
||||
]
|
||||
list_filter = (
|
||||
'task_name',
|
||||
'run_at',
|
||||
'failed_at',
|
||||
'locked_at',
|
||||
'attempts',
|
||||
'creator_content_type',
|
||||
)
|
||||
search_fields = ['task_name', 'task_params', 'last_error', 'verbose_name']
|
||||
|
@ -228,7 +228,7 @@ class TestTasks(TestCase):
|
||||
|
||||
|
||||
expected_abuse_report_text = """Add-on reported: "{extension.name}"
|
||||
{some_user.full_name} reported Add-on "{extension.name}"
|
||||
{some_user.username} reported Add-on "{extension.name}"
|
||||
:
|
||||
|
||||
“test message”
|
||||
@ -243,7 +243,7 @@ https://extensions.local:8111/
|
||||
"""
|
||||
|
||||
expected_new_comment_text = """New comment on Add-on "{extension.name}"
|
||||
{some_user.full_name} commented on Add-on "{extension.name}"
|
||||
{some_user.username} commented on Add-on "{extension.name}"
|
||||
:
|
||||
|
||||
“this is bad”
|
||||
@ -258,7 +258,7 @@ https://extensions.local:8111/
|
||||
"""
|
||||
|
||||
expected_rated_text = """Add-on rated: "{extension.name}"
|
||||
{some_user.full_name} rated extension Add-on "{extension.name}"
|
||||
{some_user.username} rated extension Add-on "{extension.name}"
|
||||
:
|
||||
|
||||
“rating text”
|
||||
@ -273,7 +273,7 @@ https://extensions.local:8111/
|
||||
"""
|
||||
|
||||
expected_review_requested_text = """Add-on review requested: "Edit Breakdown"
|
||||
{some_user.full_name} requested review of Add-on "Edit Breakdown"
|
||||
{some_user.username} requested review of Add-on "Edit Breakdown"
|
||||
:
|
||||
|
||||
“Extension is ready for initial review”
|
||||
|
@ -16,7 +16,12 @@ class NotificationsView(LoginRequiredMixin, ListView):
|
||||
paginate_by = 20
|
||||
|
||||
def get_queryset(self):
|
||||
return Notification.objects.filter(recipient=self.request.user).order_by('-id')
|
||||
return (
|
||||
Notification.objects.filter(recipient=self.request.user)
|
||||
.select_related('action')
|
||||
.prefetch_related('action__action_object', 'action__actor', 'action__target')
|
||||
.order_by('-id')
|
||||
)
|
||||
|
||||
|
||||
class MarkReadAllView(LoginRequiredMixin, FormView):
|
||||
|
@ -15,7 +15,7 @@ Django==4.2.11
|
||||
dj-database-url==1.0.0
|
||||
django-activity-stream==2.0.0
|
||||
django-admin-rangefilter==0.8.5
|
||||
django-background-tasks-updated @ git+https://projects.blender.org/infrastructure/django-background-tasks.git@2e60c4ec2fd1e7155bc3f041e0ea4875495a476b
|
||||
django-background-tasks-updated @ git+https://projects.blender.org/infrastructure/django-background-tasks.git@2cbe547fcf183354c80dfd848537f55fc05bf1d5
|
||||
django-compat==1.0.15
|
||||
django-extended-choices==1.3.3
|
||||
django-loginas==0.3.10
|
||||
|
@ -12,11 +12,13 @@
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="d-flex flex-wrap">
|
||||
{% include "extensions/components/authors.html" %}
|
||||
|
||||
{% if extension.team %}
|
||||
<a class="text-secondary" href="{{ extension.team.get_absolute_url }}">({{ extension.team.name }})</a>
|
||||
<a class="text-secondary" href="{{ extension.team.get_absolute_url }}">({{ extension.team.name }})</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
<td title="{{ extension.date_created }}">{{ extension.date_created|naturaltime_compact }}</td>
|
||||
<td class="ext-review-list-activity" colspan="2">
|
||||
@ -30,7 +32,7 @@
|
||||
<span class="badge">{{ stats.count }}</span>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<td class="ext-review-list-status">
|
||||
<a href="{{ extension.get_review_url }}" class="text-decoration-none">
|
||||
{% with last_type=stats.last_type_display|default:"Awaiting Review" %}
|
||||
{% include "common/components/status.html" with label=last_type slug=last_type|slugify object=extension classes="d-block" icon=True %}
|
||||
|
@ -7,7 +7,7 @@
|
||||
<div class="hero-breadcrumbs">
|
||||
<a href="{% url 'reviewers:approval-queue' %}">
|
||||
<i class="i-chevron-left"></i>
|
||||
<span>{% trans 'All in Queue' %}</span>
|
||||
<span>{% trans 'All in Approval Queue' %}</span>
|
||||
</a>
|
||||
</div>
|
||||
{% endblock hero_breadcrumbs %}
|
||||
@ -21,9 +21,11 @@
|
||||
<a href="#activity">
|
||||
{% trans "Activity" %}
|
||||
</a>
|
||||
{% if extension.latest_version != None %}
|
||||
<a href="{{ extension.get_versions_url }}" class="{% if '/versions/' in request.get_full_path %}is-active{% endif %}">
|
||||
{% trans "Version History" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="btn-row hero-tabs-admin mb-3 mb-md-0">
|
||||
{% if is_maintainer %}
|
||||
|
@ -23,20 +23,17 @@ STATUS_CHANGE_TYPES = [
|
||||
|
||||
class ApprovalQueueView(ListView):
|
||||
model = Extension
|
||||
paginate_by = 100
|
||||
paginate_by = 50
|
||||
|
||||
def get_queryset(self):
|
||||
qs = (
|
||||
ApprovalActivity.objects.prefetch_related(
|
||||
qs = ApprovalActivity.objects.prefetch_related(
|
||||
'extension',
|
||||
'extension__authors',
|
||||
'extension__icon',
|
||||
'extension__versions',
|
||||
'extension__versions__file',
|
||||
'extension__versions__file__validation',
|
||||
)
|
||||
.order_by('-date_created')
|
||||
.all()
|
||||
)
|
||||
).order_by('-id')
|
||||
by_extension = {}
|
||||
by_date_created = []
|
||||
for item in qs:
|
||||
@ -88,7 +85,10 @@ class ExtensionsApprovalDetailView(DetailView):
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
ctx['review_activity'] = (
|
||||
self.object.review_activity.select_related('user').order_by('date_created').all()
|
||||
self.object.review_activity.select_related('user')
|
||||
.prefetch_related('user__groups')
|
||||
.order_by('date_created')
|
||||
.all()
|
||||
)
|
||||
ctx['status_change_types'] = STATUS_CHANGE_TYPES
|
||||
|
||||
|
@ -1,15 +1,21 @@
|
||||
{% extends 'users/settings/base.html' %}
|
||||
{% block page_title %}Teams{% endblock %}
|
||||
|
||||
{% block settings %}
|
||||
<h1 class="mb-3">Teams</h1>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<p>
|
||||
To create a new team or become part of an existing one, please
|
||||
<a href="https://projects.blender.org/infrastructure/extensions-website/issues/new?template=.gitea/issue_template/team.yaml" target="_blank">get in touch</a>.
|
||||
</p>
|
||||
|
||||
{% if team_memberships %}
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="w-100">
|
||||
Team name
|
||||
Name
|
||||
</th>
|
||||
<th>
|
||||
Role
|
||||
@ -61,9 +67,6 @@
|
||||
You are not assigned to any teams yet.
|
||||
</p>
|
||||
{% endif %}
|
||||
<p class="pt-3">
|
||||
We can help you with team management if you <a href="https://projects.blender.org/infrastructure/extensions-website/issues/new?title=Team%20Management%20Request&body=Please%20add%20user%20X%20to%20team%20Y">submit your request</a> to the issue tracker.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock settings %}
|
||||
|
@ -54,7 +54,7 @@ class User(TrackChangesMixin, AbstractUser):
|
||||
is_subscribed_to_notification_emails = models.BooleanField(null=False, default=True)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f'{self.full_name or self.username}'
|
||||
return self.username
|
||||
|
||||
@property
|
||||
def image_url(self) -> Optional[str]:
|
||||
@ -142,4 +142,7 @@ class User(TrackChangesMixin, AbstractUser):
|
||||
@property
|
||||
def is_moderator(self):
|
||||
# Used to review and approve extensions
|
||||
return self.groups.filter(name='moderators').exists()
|
||||
for g in self.groups.all():
|
||||
if g.name == 'moderators':
|
||||
return True
|
||||
return False
|
||||
|
@ -1,7 +1,5 @@
|
||||
{% load static %}
|
||||
<img src="{% if user.image %}{{ user.image.url }}{% else %}{% static 'common/images/blank-profile-pic.png' %}{% endif %}" class="profile-avatar {{ classes }}">
|
||||
{% if show_name %}
|
||||
<span class="ms-2">
|
||||
{% firstof user.full_name user.username %}
|
||||
</span>
|
||||
<span class="ms-2">{{ user.username }}</span>
|
||||
{% endif %}
|
||||
|
@ -1,8 +1,9 @@
|
||||
{% extends 'users/settings/base.html' %}
|
||||
{% block page_title %}Delete Account{% endblock %}
|
||||
|
||||
{% block settings %}
|
||||
{% with can_be_deleted=user.can_be_deleted %}
|
||||
<h1 class="mb-3">Delete account</h1>
|
||||
<h1 class="mb-3">Delete Account</h1>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div>
|
||||
|
@ -1,6 +1,6 @@
|
||||
{% extends 'users/settings/base.html' %}
|
||||
|
||||
{% load static %}
|
||||
{% block page_title %}Profile{% endblock %}
|
||||
|
||||
{% block settings %}
|
||||
<h1 class="mb-3">Profile</h1>
|
||||
@ -15,9 +15,9 @@
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-4">
|
||||
<label class="mb-2 h3" for="userFullName">Full Name</label>
|
||||
<label class="h3 mb-2" for="username">Username</label>
|
||||
<div class="align-items-center d-flex position-relative">
|
||||
<input disabled class="form-control" type="text" value="{{ user.full_name }}" id="userFullName">
|
||||
<input disabled class="form-control" type="text" value="{{ user.username }}" id="username">
|
||||
<i class="i-lock"></i>
|
||||
</div>
|
||||
<p class="helptext mb-0">
|
||||
@ -55,17 +55,6 @@
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-4">
|
||||
<label class="h3 mb-2" for="username">Username</label>
|
||||
<div class="align-items-center d-flex position-relative">
|
||||
<input disabled class="form-control" type="text" value="{{ user.username }}" id="username">
|
||||
<i class="i-lock"></i>
|
||||
</div>
|
||||
<p class="helptext mb-0">
|
||||
Displayed in extension pages, rating comments and so on.
|
||||
</p>
|
||||
<div class="border-bottom-tertiary mt-3"></div>
|
||||
</div>
|
||||
<div class="col-md-6 mb-4">
|
||||
<h3 class="label mb-2">Profile Picture</h3>
|
||||
<img src="{{ user.image_url }}" width="44" height="44" class="rounded" alt="Profile Image">
|
||||
@ -74,9 +63,7 @@
|
||||
</p>
|
||||
<div class="border-bottom-tertiary mt-3"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
{# TODO: check badges display #}
|
||||
{% if user.badges %}
|
||||
<div class="col-md-6 mb-4">
|
||||
@ -94,7 +81,9 @@
|
||||
<div class="border-bottom-tertiary mt-3"></div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h3 class="label mb-2">Preview</h3>
|
||||
<div class="p-2 background-color rounded">
|
||||
|
Loading…
Reference in New Issue
Block a user