Basic email template for notifications #96

Merged
Anna Sirota merged 14 commits from notification-templates into main 2024-05-02 14:04:24 +02:00
60 changed files with 588 additions and 502 deletions
Showing only changes of commit db7ce35c08 - Show all commits

1
.gitmodules vendored
View File

@ -1,3 +1,4 @@
[submodule "assets_shared"] [submodule "assets_shared"]
path = assets_shared path = assets_shared
url = https://projects.blender.org/infrastructure/web-assets.git url = https://projects.blender.org/infrastructure/web-assets.git
branch = v2

View File

@ -24,7 +24,7 @@
<div class="ext-detail-tagline"> <div class="ext-detail-tagline">
<a href="{{ extension.get_absolute_url }}">{{ extension.name }}</a> <a href="{{ extension.get_absolute_url }}">{{ extension.name }}</a>
</div> </div>
<div class="ext-detail-authors ml-3"> <div class="ext-detail-authors ms-3">
<a href="{% url 'extensions:by-type' type_slug=extension.type_slug %}"> <a href="{% url 'extensions:by-type' type_slug=extension.type_slug %}">
{{ extension.get_type_display }} {{ extension.get_type_display }}
</a> </a>
@ -44,7 +44,7 @@
{% block hero_tabs %} {% block hero_tabs %}
<nav class="hero-tabs"> <nav class="hero-tabs">
<span class="ml-auto"></span> <span class="ms-auto"></span>
<div class="btn-row mb-1"> <div class="btn-row mb-1">
{% if request.user.is_staff %} {% if request.user.is_staff %}
@ -88,7 +88,7 @@
<div class="dl-row"> <div class="dl-row">
<div class="dl-col"> <div class="dl-col">
<dt>Status</dt> <dt>Status</dt>
<dd>{% include "common/components/status.html" with class="d-block" %}</dd> <dd>{% include "common/components/status.html" %}</dd>
</div> </div>
</div> </div>
<div class="dl-row"> <div class="dl-row">

@ -1 +1 @@
Subproject commit 9d428a82a5b51c1eec99639f3baebeac3b1f93f9 Subproject commit af61a962e1a30898279b4efdbb07a2dcb230a257

View File

@ -5,7 +5,7 @@ function galleriaCloneFirstItem() {
let firstGalleriaItem = galleriaCarrousel.firstElementChild.cloneNode(true); let firstGalleriaItem = galleriaCarrousel.firstElementChild.cloneNode(true);
firstGalleriaItem.classList.remove('js-galleria-item-preview', 'is-active'); firstGalleriaItem.classList.remove('js-galleria-item-preview', 'is-active');
firstGalleriaItem.classList.add('js-expand-on-click'); firstGalleriaItem.classList.add('js-expand-on-click');
firstGalleriaItem.id = 'galleria-item-large'; firstGalleriaItem.id = 'galleria-item-lg';
document.getElementById("galleria-container").prepend(firstGalleriaItem); document.getElementById("galleria-container").prepend(firstGalleriaItem);
} }
@ -13,7 +13,7 @@ function galleriaCloneFirstItem() {
function galleriaSetLargePreview(item) { function galleriaSetLargePreview(item) {
let previewsContainer = document.getElementById('galleria-items'); let previewsContainer = document.getElementById('galleria-items');
let previewLarge = document.getElementById('galleria-item-large'); let previewLarge = document.getElementById('galleria-item-lg');
let thumbnails = document.getElementsByClassName("js-galleria-item-preview"); let thumbnails = document.getElementsByClassName("js-galleria-item-preview");
[].forEach.call(thumbnails, function(el) { [].forEach.call(thumbnails, function(el) {
el.classList.remove("is-active"); el.classList.remove("is-active");

View File

@ -4,7 +4,7 @@
z-index: 0 z-index: 0
> li > li
--border-color: var(--box-background-color) --border-color: var(--box-bg-color)
position: relative position: relative
&:first-child &:first-child
@ -16,7 +16,7 @@
&:last-child &:last-child
/* Remove bottom half of the vertical line for last item. */ /* Remove bottom half of the vertical line for last item. */
.comment-card:before .comment-card:before
height: calc(50% - 1rem) height: calc(50% - var(--spacer))
.activity-status-change:before .activity-status-change:before
height: calc(50% + var(--border-width)) height: calc(50% + var(--border-width))
@ -50,21 +50,25 @@
left: var(--border-width) left: var(--border-width)
position: absolute position: absolute
top: 50% top: 50%
width: 2rem width: calc(var(--spacer) * 2)
z-index: -1 z-index: -1
.activity-icon
top: 1.2rem
.profile-avatar .profile-avatar
border: var(--border-width) solid var(--border-color) border: var(--border-width) solid var(--border-color)
background-color: var(--border-color) background-color: var(--border-color)
.comment-card .comment-card
&:after &:after
top: 2rem top: 3.2rem
.activity-icon .activity-icon
top: 1.25rem top: 2.2rem
.activity-status-change .activity-status-change
color: var(--text-color-tertiary) color: var(--color-text-tertiary)
.profile-avatar .profile-avatar
+margin(3, right) +margin(3, right)
@ -73,7 +77,7 @@
left: 0 left: 0
strong strong
color: var(--text-color-secondary) color: var(--color-text-secondary)
.badge .badge
border: none border: none
@ -83,22 +87,22 @@
&.activity-status-approved &.activity-status-approved
.activity-icon .activity-icon
border-color: var(--color-success) border-color: var(--color-success)
box-shadow: 0 0 1rem var(--color-success-bg) box-shadow: 0 0 var(--spacer) var(--color-success-bg)
color: var(--color-success-text) color: var(--color-success-text)
.activity-icon .activity-icon
align-items: center align-items: center
background-color: var(--background-color) background-color: var(--color-bg)
border-radius: 100% border-radius: 100%
border: var(--border-width) solid var(--border-color) border: var(--border-width) solid var(--border-color)
color: var(--text-color-tertiary) color: var(--color-text-tertiary)
display: flex display: flex
font-size: var(--font-size-small) font-size: var(--fs-sm)
height: 1.5rem height: var(--spacer-4)
justify-content: center justify-content: center
left: -.66rem left: -.66rem
position: absolute position: absolute
width: 1.5rem width: var(--spacer-4)
/* Comment form */ /* Comment form */
.comment-form .comment-form
@ -112,9 +116,6 @@
select select
width: auto width: auto
button[type="submit"]
min-width: 50%
textarea textarea
height: calc(var(--spacer) * 8) height: calc(var(--spacer) * 8)
max-height: 0 max-height: 0

View File

@ -1,17 +1,24 @@
.badge-card
+border-radius(lg)
border-bottom-left-radius: 0
border-bottom-right-radius: 0
display: flex
+padding(2, y)
a.badge-tag a.badge-tag
--badge-color: var(--text-color-secondary) --badge-color: var(--color-text-secondary)
--badge-bg: var(--text-color-tertiary) --badge-bg: var(--color-text-tertiary)
background-color: transparent background-color: transparent
text-decoration: none !important text-decoration: none !important
&:hover &:hover
background-color: transparent background-color: transparent
border-color: var(--text-color) border-color: var(--color-text)
color: var(--text-color) color: var(--color-text)
.badge-tag .badge-tag
font-size: var(--font-size-extra-small) font-size: var(--fs-xs)
.badge-status .badge-status
&-approved &-approved
@ -24,10 +31,3 @@ a.badge-tag
&-disabled-by-staff, &-disabled-by-staff,
&-disabled-by-author &-disabled-by-author
@extend .badge-secondary @extend .badge-secondary
.card-badge
+border-radius(lg)
border-bottom-left-radius: 0
border-bottom-right-radius: 0
display: flex
+padding(2, y)

View File

@ -0,0 +1,29 @@
.card
@extend .box
+media-sm
--cards-items-per-row: 2
+media-md
--cards-items-per-row: 3
+media-lg
--cards-items-per-row: 4
.cards-item-content
overflow: hidden
.crads-item-excerpt
line-height: calc(24 / 18)
.cards-item-extra
text-transform: none
.cards-item-extra-rating-stars
margin-bottom: .2rem
.stars
font-size: 1.4rem
.cards-item-title
+padding(0, y)

View File

@ -11,33 +11,36 @@
z-index: 0 z-index: 0
&:before &:before
background-color: var(--box-background-color) background-color: var(--box-bg-color)
border-radius: .25rem border-radius: .4rem
content: '' content: ''
display: block display: block
height: 1rem height: var(--spacer)
left: -.33rem left: -.33rem
position: absolute position: absolute
rotate: 45deg rotate: 45deg
top: 1rem top: var(--spacer)
width: 1rem width: var(--spacer)
z-index: -1 z-index: -1
p:last-child p:last-child
margin-bottom: 0 margin-bottom: 0
header header
color: var(--text-color-secondary) color: var(--color-text-secondary)
font-size: var(--font-size-small) font-size: var(--fs-sm)
+margin(2, bottom) +margin(2, bottom)
ul ul
align-items: center align-items: center
display: flex display: flex
+list-unstyled +list-unstyled
gap: 1rem gap: var(--spacer)
margin: 0 margin: 0
li
line-height: var(--lh-sm)
aside aside
+margin(2, top) +margin(2, top)

View File

@ -1,14 +1,17 @@
\:root
--lh-sm: 2.4rem
.hero.extension-detail .hero.extension-detail
--hero-max-height: 0 --hero-max-height: 0
--hero-min-height: 240px --hero-min-height: 24.0rem
--font-size-hero-title: clamp(3rem, 4vw + 1rem, 48px) --fs-hero-title: clamp(4.8rem, 4vw + 1.6rem, 4.8rem)
--font-size-large: 18px --fs-lg: 1.8rem
--border-width: 2px --border-width: .2rem
--hero-background-color: hsl(213, 10%, 14%) --hero-bg-color: hsl(213, 10%, 14%)
background-color: var(--hero-background-color) background-color: var(--hero-bg-color)
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='40' height='40' viewBox='0 0 40 40'%3E%3Cg fill-rule='evenodd'%3E%3Cg fill='%233e4248' fill-opacity='0.21'%3E%3Cpath d='M0 38.59l2.83-2.83 1.41 1.41L1.41 40H0v-1.41zM0 1.4l2.83 2.83 1.41-1.41L1.41 0H0v1.41zM38.59 40l-2.83-2.83 1.41-1.41L40 38.59V40h-1.41zM40 1.41l-2.83 2.83-1.41-1.41L38.59 0H40v1.41zM20 18.6l2.83-2.83 1.41 1.41L21.41 20l2.83 2.83-1.41 1.41L20 21.41l-2.83 2.83-1.41-1.41L18.59 20l-2.83-2.83 1.41-1.41L20 18.59z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E") background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='40' height='40' viewBox='0 0 40 40'%3E%3Cg fill-rule='evenodd'%3E%3Cg fill='%233e4248' fill-opacity='0.21'%3E%3Cpath d='M0 38.59l2.83-2.83 1.41 1.41L1.41 40H0v-1.41zM0 1.4l2.83 2.83 1.41-1.41L1.41 0H0v1.41zM38.59 40l-2.83-2.83 1.41-1.41L40 38.59V40h-1.41zM40 1.41l-2.83 2.83-1.41-1.41L38.59 0H40v1.41zM20 18.6l2.83-2.83 1.41 1.41L21.41 20l2.83 2.83-1.41 1.41L20 21.41l-2.83 2.83-1.41-1.41L18.59 20l-2.83-2.83 1.41-1.41L20 18.59z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E")
background-size: 40px background-size: 4.0rem
flex-direction: column flex-direction: column
overflow: initial overflow: initial
text-shadow: none text-shadow: none
@ -24,10 +27,10 @@
.hero-overlay .hero-overlay
background-color: transparent background-color: transparent
background-image: linear-gradient(0deg, var(--background-color), hsla(213, 10%, 14%, 0)) background-image: linear-gradient(0deg, var(--color-bg), hsla(213, 10%, 14%, 0))
&.extension-review &.extension-review
--hero-min-height: 210px --hero-min-height: 21.0rem
.ext-detail-download-danger .ext-detail-download-danger
background-color: var(--color-danger-bg) background-color: var(--color-danger-bg)
@ -42,8 +45,8 @@
@extend .alert @extend .alert
@extend .alert-danger @extend .alert-danger
border-radius: 0 border-radius: 0
font-size: var(--font-size-small) font-size: var(--fs-sm)
+font-weight-bold +fw-bold
+padding(1, y) +padding(1, y)
text-align: center text-align: center
@ -54,17 +57,17 @@
color: var(--color-warning-text); color: var(--color-warning-text);
.hero-breadcrumbs .hero-breadcrumbs
font-size: var(--font-size-small) font-size: var(--fs-sm)
a a
color: var(--text-color-secondary) color: var(--color-text-secondary)
display: inline-block display: inline-block
+padding(2) +padding(2)
.ext-detail-name .ext-detail-name
font-size: var(--font-size-title-massive) font-size: var(--fs-title-massive)
+font-weight-title +fw-title
line-height: var(--font-size-title-massive) line-height: var(--fs-title-massive)
margin-bottom: 0 margin-bottom: 0
.ext-detail-info-authors .ext-detail-info-authors
@ -72,7 +75,7 @@
flex-wrap: wrap flex-wrap: wrap
.ext-detail-authors .ext-detail-authors
color: var(--text-color-secondary) color: var(--color-text-secondary)
.ext-detail-tagline .ext-detail-tagline
+margin(2, bottom) +margin(2, bottom)
@ -81,9 +84,12 @@
+padding(4) +padding(4)
+style-rich-text +style-rich-text
pre
+margin(3, bottom)
.ext-detail-info .ext-detail-info
dd dd
color: var(--text-color) color: var(--color-text)
overflow: hidden overflow: hidden
text-overflow: ellipsis text-overflow: ellipsis
white-space: nowrap white-space: nowrap
@ -93,8 +99,9 @@
margin-bottom: 0 margin-bottom: 0
dt dt
color: var(--text-color-secondary) color: var(--color-text-secondary)
font-size: var(--font-size-small) font-size: var(--fs-sm)
line-height: calc(24 / 14)
dd dd
font-family: var(--font-family-body) font-family: var(--font-family-body)
@ -108,7 +115,7 @@
white-space: nowrap white-space: nowrap
&:hover &:hover
color: var(--text-color-primary) color: var(--color-text-primary)
.dl-col .dl-col
overflow: hidden overflow: hidden
@ -134,10 +141,10 @@
padding: 0 padding: 0
strong strong
font-size: var(--font-size-large) font-size: var(--fs-lg)
i i
font-size: var(--font-size-large) font-size: var(--fs-lg)
+margin(3, right) +margin(3, right)
.ext-detail-download .ext-detail-download
@ -145,7 +152,7 @@
+padding(2, y) +padding(2, y)
.btn-accent .btn-accent
box-shadow: 2px 5px 5px rgba(0,82,255,.102), 1px 10px 15px rgba(0,82,255,.102), 2px 10px 30px rgba(0,82,255,.349) box-shadow: .2rem .8rem .8rem rgba(0,82,255,.102), 1.6rem 1.0rem 1.8rem rgba(0,82,255,.102), 3.2rem 1.0rem 3.2rem rgba(0,82,255,.349)
.btn-install-drag, .btn-install-drag,
.btn-install-drag:active .btn-install-drag:active
@ -160,134 +167,21 @@
summary summary
.date .date
+margin(2, left) +margin(2, left)
color: var(--text-color-secondary) color: var(--color-text-secondary)
+font-weight-normal +fw-normal
.cards-list
+media-sm
--cards-list-items-per-row: 2
+media-md
--cards-list-items-per-row: 3
+media-lg
--cards-list-items-per-row: 4
.ext-card
+box-card
display: flex
flex-direction: column
height: 100%
overflow: hidden
transition: box-shadow ease-in-out 1s
&:hover
box-shadow: 10px 10px 20px 0px rgba(0, 0, 0, .04), -10px 0 20px 0px rgba(0, 0, 0, .04)
&.is-background-blur
background-color: hsl(213, 10%, 21%)
border: thin solid hsl(213, 10%, 20%)
position: relative
.ext-card-body
--text-color-secondary: hsla(213, 40%, 90%, .6)
border-bottom-left-radius: var(--border-radius-lg)
border-bottom-right-radius: var(--border-radius-lg)
+padding(1, top)
mix-blend-mode: screen
position: relative
z-index: 1
&:has(.ext-card-admin)
.ext-card-body
border-radius: 0
.ext-card-thumbnail-img
-webkit-mask-image: -webkit-gradient(linear, left 60%, left bottom, from(rgba(0,0,0,1)), to(rgba(0,0,0,0)))
.ext-card-thumbnail:hover
&+.ext-card-body .ext-card-title
color: var(--text-color-primary)
&.ext-card-row
flex-direction: row
+margin(3, bottom)
.ext-card-thumbnail
--card-thumbnail-width: 240px
border-top-right-radius: 0
border-bottom-left-radius: var(--border-radius-lg)
height: 100%
img
border-top-right-radius: 0
border-bottom-left-radius: var(--border-radius-lg)
.ext-blender-version
display: inline-block
.ext-card-thumbnail-blur
bottom: 0
filter: blur(50px)
left: 0
position: absolute
right: 0
transform: scale(1.25)
top: 0
z-index: 0
opacity: .5
.ext-card-thumbnail
--card-thumbnail-width: 100%
align-items: center
border-top-left-radius: var(--border-radius-lg)
border-top-right-radius: var(--border-radius-lg)
display: block
justify-content: center
max-width: var(--card-thumbnail-width)
overflow: hidden
.ext-card-thumbnail-img
background-position: center
background-size: cover
+make-aspect-ratio('16x9')
transition: transform ease-out var(--transition-speed)
.ext-card-body
display: flex
flex: 1
flex-direction: column
justify-content: space-between
+padding(3)
p
line-height: 1.2
.ext-card-title
font-size: var(--font-size-large)
+margin(3, bottom)
transition: color var(--transition-speed)
a
text-decoration: none
.ext-list-details .ext-list-details
@extend .list-inline @extend .list-inline
align-items: center align-items: center
color: var(--text-color-secondary) color: var(--color-text-secondary)
font-size: var(--font-size-small) font-size: var(--fs-sm)
line-height: var(--lh-sm)
margin: auto 0 0 0 margin: auto 0 0 0
&+.ext-list-details &+.ext-list-details
+margin(2, top) +margin(2, top)
.ext-card-tags
display: flex
flex-wrap: wrap
gap: .25rem
justify-content: flex-start
/* Show only on row list view.*/ /* Show only on row list view.*/
.ext-blender-version .ext-blender-version
display: none display: none
@ -295,19 +189,6 @@
.ext-edit-field-row .ext-edit-field-row
+margin(2, top) +margin(2, top)
.ext-card-admin
align-items: center
background-color: hsla(213, 80%, 1%, .33)
border-bottom-left-radius: var(--border-radius-lg)
border-bottom-right-radius: var(--border-radius-lg)
border-top: var(--border-width) solid var(--border-color)
display: flex
justify-content: space-between
z-index: 1
dd
font-family: var(--font-body)
.previews-upload .previews-upload
+box-card +box-card
+padding(3) +padding(3)
@ -316,27 +197,29 @@
.previews-list .previews-list
display: flex display: flex
flex-direction: column flex-direction: column
gap: .5rem gap: .8rem
.previews-list-item .previews-list-item
--preview-thumbnail-max-size: 180px --preview-thumbnail-max-size: calc(12.4rem * 16 / 9)
align-items: center align-items: start
background-color: background-color:
border-radius: var(--border-radius-lg) border-radius: var(--border-radius-lg)
border: var(--border-width) solid var(--border-color) border: var(--border-width) solid var(--border-color)
display: flex display: flex
+padding(1, y) +padding(2, y)
.previews-list-item-thumbnail .previews-list-item-thumbnail
margin: 0 margin: 0
+margin(2, y)
width: var(--preview-thumbnail-max-size) width: var(--preview-thumbnail-max-size)
.previews-list-item-thumbnail-img .previews-list-item-thumbnail-img
background-color: var(--background-color) background-color: var(--color-bg)
background-position: center background-position: center
background-size: cover background-size: cover
border-radius: var(--border-radius) border-radius: var(--border-radius)
height: 12.4rem
+make-aspect-ratio('16x9') +make-aspect-ratio('16x9')
.details .details
@ -344,7 +227,8 @@
flex: 1 flex: 1
label label
font-size: var(--font-size-small) font-size: var(--fs-sm)
line-height: var(--lh-sm)
ul ul
+list-unstyled +list-unstyled
@ -365,7 +249,7 @@
.form-control .form-control
&[type="file"] &[type="file"]
font-size: var(--font-size-extra-small) font-size: var(--fs-xs)
max-width: 50% max-width: 50%
.ext-version-history .ext-version-history
@ -374,49 +258,52 @@
ul ul
@extend .list-inline @extend .list-inline
color: var(--text-color-secondary) color: var(--color-text-secondary)
+font-weight-normal +fw-normal
gap: 0 1rem gap: 0 1.6rem
margin: 0 margin: 0
+margin(3, right) +margin(3, right)
.blender-version .blender-version
color: var(--text-color-secondary) color: var(--color-text-secondary)
+font-weight-normal +fw-normal
+margin(3, left) +margin(3, left)
details[open] details
padding: 0
&[open]
.show-on-collapse .show-on-collapse
display: none display: none
.ext-detail-info .ext-detail-info
.ext-detail-permissions .ext-detail-permissions
strong strong
font-size: var(--font-size-normal) font-size: var(--fs-normal)
small small
display: block display: block
i i
font-size: var(--font-size-normal) font-size: var(--fs-normal)
+margin(2, right) +margin(2, right)
/* Settings */ /* Settings */
.settings .settings
.form-control .form-control
&[disabled] &[disabled]
--input-bg-color: var(--background-color) --input-color-bg: var(--color-bg)
background-color: var(--input-bg-color) background-color: var(--input-color-bg)
color: var(--text-color-secondary) color: var(--color-text-secondary)
&:hover &:hover
--input-bg-color-hover: var(--input-bg-color) --input-color-bg-hover: var(--input-color-bg)
cursor: not-allowed cursor: not-allowed
& + i.i-lock & + i.i-lock
color: var(--text-color-secondary) color: var(--color-text-secondary)
position: absolute position: absolute
right: var(--spacer) right: var(--spacer)
@ -430,7 +317,7 @@
+margin(2, right) +margin(2, right)
.ext-review-list .ext-review-list
color: var(--text-color-secondary) color: var(--color-text-secondary)
th th
+padding(3, x) +padding(3, x)
@ -440,7 +327,7 @@
transition: background-color var(--transition-speed-fast) transition: background-color var(--transition-speed-fast)
a a
color: var(--text-color) color: var(--color-text)
+padding(1, y) +padding(1, y)
padding-inline: 0 !important padding-inline: 0 !important
@ -456,3 +343,36 @@
.ext-review-list-activity .ext-review-list-activity
display: flex display: flex
+padding(0, x) +padding(0, x)
.rating-form
select
color: var(--color-warning)
&:active,
&:hover,
&:focus
color: var(--color-warning)
// TODO: consider adding component boxed nav generic to web-assets, and make variants on top of that
.nav-pills
@extend .dropdown-menu
box-shadow: none
display: block
position: relative
.nav-pills-item
@extend .dropdown-item
+margin(1, bottom)
&.active
background-color: var(--color-accent-bg)
&:last-child
+margin(0, bottom)
.nav-pills-divider
@extend .dropdown-divider
+margin(0, top)

View File

@ -1,3 +1,18 @@
.form-check-input,
.form-check-label
&:hover
cursor: pointer
.form-check-input
height: var(--spacer-4)
.form-check-label
+margin(2, left)
.form-control
&[type="file"]
height: calc(var(--spacer) * 2.5)
/* Override Tagger's styling. */ /* Override Tagger's styling. */
.was-validated .form-control:invalid, .was-validated .form-control:invalid,
.form-control.is-invalid .form-control.is-invalid

View File

@ -1,7 +1,7 @@
.galleria-container .galleria-container
--extension-thumbnail-width: 140px --extension-thumbnail-width: 14.0rem
background-color: var(--background-color-secondary) background-color: var(--color-bg-secondary)
border-radius: var(--border-radius-lg) border-radius: var(--border-radius-lg)
+padding(2) +padding(2)
@ -12,7 +12,7 @@
.galleria-items .galleria-items
display: flex display: flex
gap: .5rem gap: var(--spacer-2)
overflow-x: auto overflow-x: auto
+padding(2, top) +padding(2, top)
scroll-behavior: smooth scroll-behavior: smooth
@ -25,7 +25,7 @@
display: none display: none
&::-webkit-scrollbar &::-webkit-scrollbar
height: 10px height: 1.0rem
width: 100% width: 100%
&::-webkit-scrollbar-thumb &::-webkit-scrollbar-thumb
@ -33,7 +33,7 @@
border-radius: 999em border-radius: 999em
&::-webkit-scrollbar-track &::-webkit-scrollbar-track
background-color: hsl(var(--background-color-h), var(--background-color-s), 80%) background-color: hsl(var(--color-bg-h), var(--color-bg-s), 80%)
.galleria-item .galleria-item
cursor: pointer cursor: pointer
@ -43,7 +43,7 @@
.galleria-item-type-video .galleria-item-type-video
&::after &::after
font-size: 2rem font-size: calc(var(--spacer) * 2)
.galleria-item .galleria-item
+border-radius +border-radius
@ -69,14 +69,14 @@
transform: translate(-50%, -50%) transform: translate(-50%, -50%)
width: initial width: initial
height: initial height: initial
gap: 1rem gap: var(--spacer)
&.is-active &.is-active
background-color: var(--color-primary) background-color: var(--color-accent)
img img
clip-path: inset(0.2rem 0.2rem 0.2rem 0.2rem) clip-path: inset(.2rem .2rem .2rem .2rem)
img img
clip-path: inset(0 0 0 0) clip-path: inset(0 0 0 0)
@ -92,7 +92,7 @@
color: white color: white
content: '\e83e' content: '\e83e'
font-family: 'fontutti' font-family: 'fontutti'
font-size: 4rem font-size: 5.6rem
left: 50% left: 50%
top: 50% top: 50%
pointer-events: none pointer-events: none
@ -120,7 +120,7 @@
background: transparent background: transparent
border: none border: none
cursor: pointer cursor: pointer
font-size: 4rem font-size: 5.6rem
height: 100vh height: 100vh
max-width: 200px max-width: 200px
opacity: .6 opacity: .6
@ -143,7 +143,7 @@
&.btn-close &.btn-close
fill: white fill: white
font-size: 2rem font-size: 3.2rem
height: 20vh height: 20vh
max-height: 80px max-height: 80px
max-width: 80px max-width: 80px
@ -174,9 +174,9 @@
.indicator .indicator
background-color: rgba(black, .5) background-color: rgba(black, .5)
bottom: 1rem bottom: var(--spacer)
color: white color: white
+font-weight-bold +fw-bold
min-width: 3ch min-width: 3ch
+padding(2, x) +padding(2, x)
position: absolute position: absolute
@ -184,10 +184,10 @@
text-align: right text-align: right
.caption .caption
backdrop-filter: blur(25px) backdrop-filter: blur(2.8rem)
background-color: rgba(black, .5) background-color: rgba(black, .5)
border-radius: var(--border-radius) border-radius: var(--border-radius)
bottom: 1rem bottom: var(--spacer)
color: white color: white
+font-weight(500) +font-weight(500)
line-height: 1.5 line-height: 1.5
@ -195,7 +195,7 @@
+padding(2, x) +padding(2, x)
position: absolute position: absolute
text-align: center text-align: center
text-shadow: 1px 1px 0 black, 2px 2px 3px rgba(black, .5) text-shadow: .1rem .1rem 0 black, .2rem .2rem rgba(black, .5)
body.is-galleria-active body.is-galleria-active
overflow: hidden overflow: hidden

View File

@ -4,7 +4,7 @@
&.is-flatpage &.is-flatpage
--hero-max-height: 0 --hero-max-height: 0
--hero-min-height: 240px --hero-min-height: 24.0rem
background: transparent background: transparent
.hero-content .hero-content
@ -23,7 +23,7 @@
> a:not(.btn) > a:not(.btn)
background-color: transparent background-color: transparent
border-radius: 0 border-radius: 0
color: var(--text-color-secondary) color: var(--color-text-secondary)
display: inline-block display: inline-block
+padding(4, x) +padding(4, x)
+padding(2, y) +padding(2, y)
@ -32,7 +32,7 @@
&::after &::after
background-color: currentColor background-color: currentColor
bottom: -1px bottom: -.1rem
content: '' content: ''
height: var(--border-width) height: var(--border-width)
left: 0 left: 0
@ -48,7 +48,7 @@
opacity: 1 opacity: 1
&.is-active &.is-active
+font-weight-bold +fw-bold
color: white color: white
&::after &::after

View File

@ -1,14 +1,23 @@
.dl-col
margin-bottom: -.1rem // Compensate border-bottom height
.dl-col-2 .dl-col-2
flex-grow: 2 flex-grow: 2
.dl-row
+padding(2, bottom)
&:first-child
+padding(0, top)
.list-filters .list-filters
+box-card +box-card
background-color: var(--background-color-tertiary) background-color: var(--color-bg-tertiary)
+padding(3) +padding(3)
h3 h3
border-bottom: var(--border-width) solid var(--border-color) border-bottom: var(--border-width) solid var(--border-color)
color: var(--text-color-secondary) color: var(--color-text-secondary)
+padding(2, bottom) +padding(2, bottom)
ul ul
@ -17,12 +26,12 @@
li li
&.is-active &.is-active
color: var(--text-color-primary) color: var(--color-text-primary)
+font-weight-bold +fw-bold
a a
display: block display: block
&:hover &:hover
color: var(--text-color-primary) color: var(--color-text-primary)
text-decoration: none text-decoration: none

View File

@ -7,7 +7,8 @@
h4, h4,
h5, h5,
h6 h6
+margin(4, top) +margin(3, bottom)
+padding(3, top)
img img
+border-radius(lg) +border-radius(lg)
@ -19,9 +20,4 @@
& > & >
h1:first-of-type, h1:first-of-type,
h2:first-of-type, +padding(0, top)
h3:first-of-type,
h4:first-of-type,
h5:first-of-type,
h6:first-of-type
+margin(0, top)

View File

@ -0,0 +1,17 @@
.nav-global
--nav-global-border-radius: var(--border-radius)
--nav-global-border-radius-lg: var(--border-radius-lg)
--nav-global-button-height: calc(var(--spacer) * 2.5)
--nav-global-font-size: var(--fs-sm)
--nav-global-link-padding-y: var(--nav-global-spacer-xs);
--nav-global-navbar-height: var(--navbar-primary-height, var(--spacer-6));
--nav-global-spacer: var(--spacer)
--nav-global-spacer-sm: var(--spacer-2)
--nav-global-spacer-xs: var(--spacer-1)
.btn-primary
color: var(--color-accent) !important
input,
.form-control
height: var(--nav-global-button-height)

View File

@ -1,22 +1,23 @@
.background-color .background-color
background-color: var(--background-color) background-color: var(--color-bg)
.background-color-primary .background-color-primary
background-color: var(--background-color-primary) background-color: var(--color-bg-primary)
.background-color-secondary .background-color-secondary
background-color: var(--background-color-secondary) background-color: var(--color-bg-secondary)
.background-color-tertiary .background-color-tertiary
background-color: var(--background-color-tertiary) background-color: var(--color-bg-tertiary)
.border-bottom-tertiary .border-bottom-tertiary
border-bottom: thin solid var(--background-color-tertiary) border-bottom: thin solid var(--color-bg-tertiary)
.cursor-move .cursor-move
&:hover &:hover
cursor: move !important cursor: move !important
// TODO: move utilities 'fade' and 'show' to web-assets
.fade .fade
opacity: 0 opacity: 0
// TODO: make variable 'transition-speed-slow' work // TODO: make variable 'transition-speed-slow' work
@ -36,6 +37,13 @@
.show .show
opacity: 1 opacity: 1
.text-accent
color: var(--color-accent)
a.text-accent
&:hover
color: var(--color-accent)
.text-underline .text-underline
text-decoration: underline !important text-decoration: underline !important

View File

@ -2,41 +2,13 @@
$font-path: '/static/fonts' $font-path: '/static/fonts'
/* Import variables.*/ /* Import variables.*/
$grid-breakpoints: (xs: 0,sm: 768px,md: 1020px,lg: 1220px,xl: 1380px,xxl: 1680px) !default $grid-breakpoints: (xs: 0,sm: 768px,md: 1020px,lg: 1220px,xl: 1380px,xxl: 1680px)
$container-max-widths: (sm: 760px, md: 1020px, lg: 1070px, xl: 1320px, xxl: 1600px) $container-max-widths: (sm: 760px, md: 1020px, lg: 1070px, xl: 1320px, xxl: 1600px)
$container-width: map-get($container-max-widths, 'xl') $container-width: map-get($container-max-widths, 'xl')
@import '../../../../assets_shared/src/styles/_media_queries.sass' /* Web Assets. */
@import '../../../../assets_shared/src/styles/_mixins.sass' @import '../../../../assets_shared/src/styles/main.sass'
@import '../../../../assets_shared/src/styles/_variables.sass'
/* Import Bootstrap. */
@import '../../../../assets_shared/src/styles/bootstrap/bootstrap.scss'
@import '../../../../assets_shared/src/styles/_utilities.sass'
@import '../../../../assets_shared/src/styles/_fonts.sass'
@import '../../../../assets_shared/src/styles/_bootstrap_overrides.sass'
@import '../../../../assets_shared/src/styles/_alert.sass'
@import '../../../../assets_shared/src/styles/_badge.sass'
@import '../../../../assets_shared/src/styles/_base.sass'
@import '../../../../assets_shared/src/styles/_box.sass'
@import '../../../../assets_shared/src/styles/_button.sass'
@import '../../../../assets_shared/src/styles/_cards.sass'
@import '../../../../assets_shared/src/styles/_code.sass'
@import '../../../../assets_shared/src/styles/_details.sass'
@import '../../../../assets_shared/src/styles/_footer.sass'
@import '../../../../assets_shared/src/styles/_forms.sass'
@import '../../../../assets_shared/src/styles/_hero.sass'
@import '../../../../assets_shared/src/styles/_list.sass'
@import '../../../../assets_shared/src/styles/_navigation.sass'
@import '../../../../assets_shared/src/styles/_navigation_global.scss'
@import '../../../../assets_shared/src/styles/_pagination.sass'
@import '../../../../assets_shared/src/styles/_sidebar.sass'
@import '../../../../assets_shared/src/styles/_table.sass'
@import '../../../../assets_shared/src/styles/_type.sass'
/* Extension Platform specific styling. */ /* Extension Platform specific styling. */
@import '_mixins.sass' @import '_mixins.sass'
@ -46,6 +18,7 @@ $container-width: map-get($container-max-widths, 'xl')
@import '_alert.sass' @import '_alert.sass'
@import '_badge.sass' @import '_badge.sass'
@import '_box.sass' @import '_box.sass'
@import '_cards.sass'
@import '_code.sass' @import '_code.sass'
@import '_comments.sass' @import '_comments.sass'
@import '_extension.sass' @import '_extension.sass'
@ -54,6 +27,7 @@ $container-width: map-get($container-max-widths, 'xl')
@import '_galleria.sass' @import '_galleria.sass'
@import '_hero.sass' @import '_hero.sass'
@import '_list.sass' @import '_list.sass'
@import '_navigation_global.sass'
@import '_table.sass' @import '_table.sass'
@import 'ratings/static/ratings/styles/_review.sass' @import 'ratings/static/ratings/styles/_review.sass'
@import 'ratings/static/ratings/styles/_stars.sass' @import 'ratings/static/ratings/styles/_stars.sass'
@ -66,7 +40,7 @@ $container-width: map-get($container-max-widths, 'xl')
+media-xs +media-xs
width: 60px width: 60px
/* Temporarily here until it can be moved to web-assets v2. */ /* TODO: temporarily here until it can be moved to web-assets v2. */
.nav-global-links-right .nav-global-links-right
gap: 0 var(--spacer-2) gap: 0 var(--spacer-2)
.navbar-search .navbar-search
@ -77,11 +51,11 @@ $container-width: map-get($container-max-widths, 'xl')
.profile-avatar .profile-avatar
border-radius: 50% border-radius: 50%
height: var(--spacer-4)
pointer-events: none pointer-events: none
width: 26px
.search-highlight .search-highlight
background-color: var(--btn-bg-color) background-color: var(--btn-color-bg)
border-radius: var(--border-radius) border-radius: var(--border-radius)
color: var(--btn-color) color: var(--btn-color)
font-style: normal font-style: normal
@ -97,13 +71,13 @@ $container-width: map-get($container-max-widths, 'xl')
border-radius: var(--border-radius) border-radius: var(--border-radius)
color: hsl(0, 100%, 90%) !important color: hsl(0, 100%, 90%) !important
display: flex display: flex
+font-weight-bold +fw-bold
font-size: var(--font-size-extra-small) font-size: var(--fs-xs)
justify-content: center justify-content: center
padding-block: .1rem padding-block: .1rem
padding-inline: .4rem padding-inline: var(--spacer-1)
position: absolute position: absolute
top: -.4rem top: calc(var(--spacer-2) * -1)
transition: background-color var(--transition-speed), box-shadow 500ms, color var(--transition-speed) transition: background-color var(--transition-speed), box-shadow 500ms, color var(--transition-speed)
right: -.5rem right: -.5rem

View File

@ -40,12 +40,12 @@
.tagger > ul > li:not(.tagger-new) a:visited, .tagger > ul > li:not(.tagger-new) a:visited,
.tagger-new ul a, .tagger-new ul a,
.tagger-new ul a:visited { .tagger-new ul a:visited {
color: var(--text-color); color: var(--color-text);
} }
.tagger > ul > li:not(.tagger-new) > a, .tagger > ul > li:not(.tagger-new) > a,
.tagger li:not(.tagger-new) > span, .tagger li:not(.tagger-new) > span,
.tagger .tagger-new ul { .tagger .tagger-new ul {
background: var(--btn-bg-color); background: var(--btn-color-bg);
border-radius: var(--border-radius); border-radius: var(--border-radius);
padding: 4px 4px 4px 8px; padding: 4px 4px 4px 8px;

View File

@ -125,8 +125,8 @@
</li> </li>
{% block nav-upload %} {% block nav-upload %}
<li> <li class="me-2">
<a href="{% url 'extensions:submit' %}" class="btn btn-primary text-primary"> <a href="{% url 'extensions:submit' %}" class="btn btn-primary">
<i class="i-upload"></i> <i class="i-upload"></i>
<span>Upload Extension</span> <span>Upload Extension</span>
</a> </a>

View File

@ -1,3 +1,4 @@
{# TODO: check if template is used and needed #}
<li class="nav-item {% include "common/components/_nav_item_active" %}"> <li class="nav-item {% include "common/components/_nav_item_active" %}">
{% include "common/components/nav_link.html" %} {% include "common/components/nav_link.html" %}
</li> </li>

View File

@ -1,5 +1,5 @@
{% if name %} {% if name %}
<a class="nav-link {% include "common/components/_nav_item_active" %} {% if classes %}{{ classes }}{% endif %}" href="{% url name %}">{{ title }}</a> <a class="nav-pills-item {% include "common/components/_nav_item_active" %} {% if classes %}{{ classes }}{% endif %}" href="{% url name %}">{{ title }}</a>
{% elif path %} {% elif path %}
<a class="nav-link {% include "common/components/_nav_item_active" %} {% if classes %}{{ classes }}{% endif %}" href="{{ path }}">{{ title }}</a> <a class="nav-pills-item {% include "common/components/_nav_item_active" %} {% if classes %}{{ classes }}{% endif %}" href="{{ path }}">{{ title }}</a>
{% endif %} {% endif %}

View File

@ -14,7 +14,7 @@
{% endblock hero %} {% endblock hero %}
{% block content %} {% block content %}
<div class="box p-5 my-3 is-flatpage"> <div class="box my-3 is-flatpage">
{{ flatpage.content|markdown }} {{ flatpage.content|markdown }}
</div> </div>
{% endblock content %} {% endblock content %}

View File

@ -10,6 +10,7 @@ class Verb:
REPORTED_RATING = 'reported rating' REPORTED_RATING = 'reported rating'
REQUESTED_CHANGES = 'requested changes' REQUESTED_CHANGES = 'requested changes'
REQUESTED_REVIEW = 'requested review' REQUESTED_REVIEW = 'requested review'
UPLOADED_NEW_VERSION = 'uploaded new version'
class Flag: class Flag:

View File

@ -8,6 +8,7 @@ from django.db.models.signals import m2m_changed, pre_save, post_save, pre_delet
from django.dispatch import receiver from django.dispatch import receiver
from constants.activity import Flag from constants.activity import Flag
from reviewers.models import ApprovalActivity
import extensions.models import extensions.models
import files.models import files.models
@ -165,3 +166,26 @@ def _auto_approve_subsequent_uploads(
args = {'f_id': file.pk, 'pk': instance.pk, 'sender': sender, 's': file.source.name} args = {'f_id': file.pk, 'pk': instance.pk, 'sender': sender, 's': file.source.name}
logger.info('Auto-approving file pk=%(f_id)s of %(sender)s pk=%(pk)s source=%(s)s', args) logger.info('Auto-approving file pk=%(f_id)s of %(sender)s pk=%(pk)s source=%(s)s', args)
file.save(update_fields={'status', 'date_modified'}) file.save(update_fields={'status', 'date_modified'})
@receiver(post_save, sender=extensions.models.Version)
def _create_approval_activity_for_new_version_if_listed(
sender: object,
instance: extensions.models.Version,
created: bool,
raw: bool,
**kwargs: object,
):
if raw:
return
if not created:
return
extension = instance.extension
if not extension.is_listed or not instance.file:
return
ApprovalActivity(
type=ApprovalActivity.ActivityType.UPLOADED_NEW_VERSION,
user=instance.file.user,
extension=instance.extension,
message=f'uploaded new version: {instance.version}',
).save()

View File

@ -1,8 +1,8 @@
// TODO: improve and refactor variable namings // TODO: improve and refactor variable namings
const formsetContainer = document.getElementById('add-image-container'); const formsetContainer = document.getElementById('add-img-container');
const form = document.getElementById('update-extension-form'); const form = document.getElementById('update-extension-form');
const btnAddImage = document.getElementById('btn-add-image'); const btnAddImage = document.getElementById('btn-add-img');
const formsetPrefix = 'form'; const formsetPrefix = 'form';
const inputTotalForms = document.getElementById(`id_${formsetPrefix}-TOTAL_FORMS`); const inputTotalForms = document.getElementById(`id_${formsetPrefix}-TOTAL_FORMS`);
const tagInput = document.getElementById('id_tags'); const tagInput = document.getElementById('id_tags');
@ -28,7 +28,7 @@ function appendImageUploadForm() {
const formRow = document.createElement('div'); const formRow = document.createElement('div');
const newFormHTML = ` const newFormHTML = `
<div class="previews-list-item"> <div class="previews-list-item">
<div class="align-items-center d-flex previews-list-item-thumbnail pl-3"> <div class="align-items-center d-flex previews-list-item-thumbnail ps-3">
<div class="js-input-img-thumbnail previews-list-item-thumbnail-img" title="Preview"> <div class="js-input-img-thumbnail previews-list-item-thumbnail-img" title="Preview">
<div class="align-items-center d-flex js-input-img-thumbnail-icon justify-content-center"> <div class="align-items-center d-flex js-input-img-thumbnail-icon justify-content-center">
<i class="i-image"></i> <i class="i-image"></i>
@ -44,10 +44,10 @@ function appendImageUploadForm() {
<input accept="image/jpg,image/jpeg,image/png,image/webp,video/mp4" class="form-control js-input-img" id="id_${formsetPrefix}-${i}-source" type="file" name="${formsetPrefix}-${i}-source"> <input accept="image/jpg,image/jpeg,image/png,image/webp,video/mp4" class="form-control js-input-img" id="id_${formsetPrefix}-${i}-source" type="file" name="${formsetPrefix}-${i}-source">
<ul class="pt-0"> <ul class="pt-0">
<li> <li>
<button class="btn btn-link btn-sm js-btn-reset-img-upload-form pl-2 pr-0"><i class="i-refresh"></i> Reset</button> <button class="btn btn-link btn-sm js-btn-reset-img-upload-form ps-2 pe-0"><i class="i-refresh"></i> Reset</button>
</li> </li>
<li> <li>
<button class="btn btn-link btn-sm js-btn-remove-img-upload-form pl-2 pr-0"><i class="i-trash"></i> Delete</button> <button class="btn btn-link btn-sm js-btn-remove-img-upload-form ps-2 pe-0"><i class="i-trash"></i> Delete</button>
</li> </li>
</ul> </ul>
</div> </div>

View File

@ -71,7 +71,7 @@
{% trans "Version History" %} {% trans "Version History" %}
</a> </a>
<span class="ml-auto"></span> <span class="ms-auto"></span>
<div class="btn-row"> <div class="btn-row">
{% if is_maintainer %} {% if is_maintainer %}

View File

@ -1,5 +1,5 @@
<a href="{% url "extensions:by-support" support_slug=slug %}" title="Support: {{ name }}" <a href="{% url "extensions:by-support" support_slug=slug %}" title="Support: {{ name }}"
class="badge text-decoration-none align-middle {% if not small %}px-3 py-2 my-1{% endif %} badge-dark{#% if slug == 'official' %}primary{% elif slug == 'community' %}success{% else %}warning{% endif % #}" class="badge text-decoration-none align-middle {% if not small %}px-3 py-2 my-1{% endif %} badge-dark{#% if slug == 'official' %}primary{% elif slug == 'community' %}success{% else %}warning{% endif % #}"
style="{% if not small %}font-size: var(--font-size-base);{% endif %}"> style="{% if not small %}font-size: var(--fs-base);{% endif %}">
<b>{{ name }}</b> <b>{{ name }}</b>
</a> </a>

View File

@ -1,25 +1,23 @@
{% load common filters %} {% load common filters %}
{% with latest=extension.latest_version thumbnail_360p_url=extension.get_previews.0.thumbnail_360p_url %} {% with latest=extension.latest_version thumbnail_360p_url=extension.previews.listed.first.thumbnail_360p_url %}
<div class="cards-item">
<div class="ext-card {% if blur %}is-background-blur{% endif %}"> <div class="cards-item-content">
{% if blur %} <a href="{{ extension.get_absolute_url }}">
<div class="ext-card-thumbnail-blur" style="background-image: url({{ thumbnail_360p_url }});"></div> <div class="cards-item-thumbnail">
{% endif %} <img alt="{{ extension.name }}" src="{{ thumbnail_360p_url }}" title="{{ extension.name }}">
</div>
<a class="ext-card-thumbnail" href="{{ extension.get_absolute_url }}">
<div class="ext-card-thumbnail-img" style="background-image: url({{ thumbnail_360p_url }});" title="{{ extension.name }}"></div>
</a> </a>
<h3 class="cards-item-title">
<div class="ext-card-body">
<h3 class="ext-card-title">
<a href="{{ extension.get_absolute_url }}">{{ extension.name }}</a> <a href="{{ extension.get_absolute_url }}">{{ extension.name }}</a>
</h3> </h3>
<div class="cards-item-excerpt">
<p> <p>
{{ latest.tagline }} {{ latest.tagline }}
</p> </p>
</div>
<ul class="ext-list-details"> <div class="cards-item-extra">
<li class="ext-card-author"> <ul>
<li>
{% if extension.team %} {% if extension.team %}
{% with team=extension.team %} {% with team=extension.team %}
<a href="{{ team.get_absolute_url }}" title="{{ team.name }}">{{ team.name }}</a> <a href="{{ team.get_absolute_url }}" title="{{ team.name }}">{{ team.name }}</a>
@ -30,10 +28,10 @@
</li> </li>
</ul> </ul>
<ul class="ext-list-details mt-1"> <ul class="cards-item-extra-rating-stars">
{% if extension.average_score %} {% if extension.average_score %}
<li> <li>
<a href="{{ extension.get_ratings_url }}"> <a class="align-items-center d-flex" href="{{ extension.get_ratings_url }}">
{% include "ratings/components/average.html" with score=extension.average_score %} {% include "ratings/components/average.html" with score=extension.average_score %}
({{ extension.text_ratings_count|int_compact }}) ({{ extension.text_ratings_count|int_compact }})
</a> </a>
@ -47,22 +45,25 @@
{% endif %} {% endif %}
{% if show_type %} {% if show_type %}
<li class="ml-auto"> <li class="ms-auto">
{{ extension.get_type_display }} {{ extension.get_type_display }}
</li> </li>
{% endif %} {% endif %}
</ul> </ul>
{% if latest.tags.count %} {% if latest.tags.count %}
<ul class="ext-list-details ext-card-tags"> <ul>
<li class="ext-card-tags"> {% for tag in latest.tags.all %}
{% include "extensions/components/tags.html" with small=True version=latest %} <li>
{% include "extensions/components/badge_tag.html" with small=True version=latest %}
</li> </li>
{% endfor %}
</ul> </ul>
{% endif %} {% endif %}
</div>
</div>
{# Author/admin tools can be added here in an extending template #} {# Author/admin tools can be added here in an extending template #}
{% block admin %}{% endblock admin %} {% block admin %}{% endblock admin %}
</div> </div>
</div>
{% endwith %} {% endwith %}

View File

@ -2,9 +2,9 @@
{# Info Summary #} {# Info Summary #}
<section class="ext-detail-info"> <section class="ext-detail-info">
<div class="card"> <div class="card p-0">
{% if is_initial %} {% if is_initial %}
<div class="badge badge-info card-badge"> <div class="badge badge-card badge-info">
Information retrieved from manifest Information retrieved from manifest
</div> </div>
{% else %} {% else %}

View File

@ -19,11 +19,13 @@
{# Description #} {# Description #}
{% block extension_description %} {% block extension_description %}
{% if extension.description %}
<section id="about" class="mt-3"> <section id="about" class="mt-3">
<div class="box ext-detail-description"> <div class="box ext-detail-description">
{{ extension.description|markdown }} {{ extension.description|markdown }}
</div> </div>
</section> </section>
{% endif %}
{% endblock extension_description %} {% endblock extension_description %}
{# What's New #} {# What's New #}
@ -82,7 +84,7 @@
</ul> </ul>
</div> </div>
{% else %} {% else %}
<p class="mb-0"><i class="i-check mr-0"></i> This extension does not require special permissions.</p> <p class="mb-0"><i class="i-check me-0"></i> This extension does not require special permissions.</p>
{% endif %} {% endif %}
</section> </section>
{% endif %} {% endif %}
@ -125,7 +127,7 @@
<dt>{% trans 'Rating' %}</dt> <dt>{% trans 'Rating' %}</dt>
<dd> <dd>
{% if extension.average_score %} {% if extension.average_score %}
<a href="{{ extension.get_ratings_url }}" class="text-decoration-none"> <a href="{{ extension.get_ratings_url }}" class="align-items-center d-flex stars-helper-detail text-decoration-none">
{% include "ratings/components/average.html" with score=extension.average_score %} {% include "ratings/components/average.html" with score=extension.average_score %}
({{ extension.ratings.listed.count }}) ({{ extension.ratings.listed.count }})
</a> </a>
@ -274,7 +276,7 @@
{% trans 'Reviews' %} {% trans 'Reviews' %}
</a> </a>
</h2> </h2>
<a href="{{ extension.get_ratings_url }}" class="ml-auto">See all</a> <a href="{{ extension.get_ratings_url }}" class="ms-auto">See all</a>
</div> </div>
</div> </div>
</div> </div>

View File

@ -82,7 +82,7 @@
<section class="card p-3 mt-3"> <section class="card p-3 mt-3">
<div class="btn-col"> <div class="btn-col">
<button type="submit" name="save_draft" class="btn btn-primary btn-warning"> <button type="submit" name="save_draft" class="btn btn-warning">
<i class="i-check"></i> <i class="i-check"></i>
<span>{% trans 'Save Draft' %}</span> <span>{% trans 'Save Draft' %}</span>
</button> </button>

View File

@ -25,7 +25,7 @@
</div> </div>
</div> </div>
<div class="hero-background" style="background-image: url(https://www.blender.org/wp-content/uploads/2022/08/piotr-krynski-scanislands-blender-33lts-crop.jpg); background-position-y: 50%"></div> <div class="hero-bg" style="background-image: url(https://www.blender.org/wp-content/uploads/2022/08/piotr-krynski-scanislands-blender-33lts-crop.jpg); background-position-y: 50%"></div>
<div class="hero-overlay"></div> <div class="hero-overlay"></div>
<div class="hero-credits"></div> <div class="hero-credits"></div>
</div> </div>
@ -38,10 +38,10 @@
<h2> <h2>
<a href="{% url 'extensions:by-type' type_slug='add-ons' %}">Add-ons</a> <a href="{% url 'extensions:by-type' type_slug='add-ons' %}">Add-ons</a>
</h2> </h2>
<a href="{% url 'extensions:by-type' type_slug='add-ons' %}" class="ml-auto">See all</a> <a href="{% url 'extensions:by-type' type_slug='add-ons' %}" class="ms-auto">See all</a>
</div> </div>
<p>Extend Blender capabilities with these add-ons by the community.</p> <p>Extend Blender capabilities with these add-ons by the community.</p>
<div class="cards-list mt-3"> <div class="cards cards-lg-4 cards-md-3 cards-sm-2 mt-3">
{% for extension in addons %} {% for extension in addons %}
{% include "extensions/components/card.html" %} {% include "extensions/components/card.html" %}
{% endfor %} {% endfor %}
@ -53,12 +53,12 @@
<h2> <h2>
<a href="{% url 'extensions:by-type' type_slug='themes' %}">Themes</a> <a href="{% url 'extensions:by-type' type_slug='themes' %}">Themes</a>
</h2> </h2>
<a href="{% url 'extensions:by-type' type_slug='themes' %}" class="ml-auto fw-normal"> <a href="{% url 'extensions:by-type' type_slug='themes' %}" class="ms-auto fw-normal">
See all See all
</a> </a>
</div> </div>
<p>Blender themes to your liking. Dark, light, flat, colorful, and everything in between.</p> <p>Blender themes to your liking. Dark, light, flat, colorful, and everything in between.</p>
<div class="cards-list mt-3"> <div class="cards cards-lg-4 cards-md-3 cards-sm-2 mt-3">
{% for extension in themes %} {% for extension in themes %}
{% include "extensions/components/card.html" %} {% include "extensions/components/card.html" %}
{% endfor %} {% endfor %}
@ -66,7 +66,7 @@
<div class="my-5 text-center"> <div class="my-5 text-center">
Got an add-on or theme to share with the community? Got an add-on or theme to share with the community?
<a href="{% url 'extensions:submit' %}" class="text-primary ml-2"> <a href="{% url 'extensions:submit' %}" class="text-accent ms-2">
Upload Upload
<i class="i-chevron-right"></i> <i class="i-chevron-right"></i>
</a> </a>

View File

@ -33,7 +33,7 @@
{% endif %} {% endif %}
{% if tag %} {% if tag %}
<h2 class="d-flex align-items-center"> <h2 class="d-flex align-items-center">
<span class="mr-3">{% blocktranslate %}Extensions with the tag{% endblocktranslate %}</span> <span class="me-3">{% blocktranslate %}Extensions with the tag{% endblocktranslate %}</span>
{% include "extensions/components/badge_tag.html" %} {% include "extensions/components/badge_tag.html" %}
</h2> </h2>
{% endif %} {% endif %}
@ -47,7 +47,7 @@
<div class="row"> <div class="row">
<div class="col"> <div class="col">
{% if object_list %} {% if object_list %}
<div class="cards-list card-layout-horizontal cards-3"> <div class="cards cards-3">
{% for extension in object_list %} {% for extension in object_list %}
{% include "extensions/components/card.html" with show_type=False %} {% include "extensions/components/card.html" with show_type=False %}
{% endfor %} {% endfor %}

View File

@ -1,15 +1,14 @@
{% load common %} {% load common %}
{# Upload new preview images #} {# Upload new preview images #}
{% load i18n %} {% load i18n %}
<div class="previews-upload"> <div id="add-img-container" class="previews-list">
<div id="add-image-container" class="previews-list">
{{ add_preview_formset.management_form }} {{ add_preview_formset.management_form }}
{{ add_preview_formset.non_form_errors }} {{ add_preview_formset.non_form_errors }}
{% for newform in add_preview_formset %} {% for newform in add_preview_formset %}
{% with inlineform=newform|add_form_classes %} {% with inlineform=newform|add_form_classes %}
<div class="ext-edit-field-row js-ext-edit-field-row"> <div class="ext-edit-field-row js-ext-edit-field-row">
<div class="previews-list-item"> <div class="previews-list-item">
<div class="align-items-center d-flex previews-list-item-thumbnail pl-3"> <div class="d-flex previews-list-item-thumbnail ps-3">
<div class="js-input-img-thumbnail previews-list-item-thumbnail-img" title="Preview"> <div class="js-input-img-thumbnail previews-list-item-thumbnail-img" title="Preview">
<div class="align-items-center d-flex js-input-img-thumbnail-icon justify-content-center"> <div class="align-items-center d-flex js-input-img-thumbnail-icon justify-content-center">
<i class="i-image"></i> <i class="i-image"></i>
@ -24,7 +23,7 @@
{% include "common/components/field.html" with field=inlineform.source label='File' %} {% include "common/components/field.html" with field=inlineform.source label='File' %}
<ul class="pt-0"> <ul class="pt-0">
<li> <li>
<button class="btn btn-link btn-sm js-btn-reset-img-upload-form pl-2 pr-0"><i class="i-refresh"></i> Reset</button> <button class="btn btn-link btn-sm js-btn-reset-img-upload-form ps-2 pe-0"><i class="i-refresh"></i> Reset</button>
</li> </li>
</ul> </ul>
</div> </div>
@ -35,10 +34,9 @@
{% endwith %} {% endwith %}
{% endfor %} {% endfor %}
</div> </div>
</div>
<div class="row"> <div class="row">
<div class="col text-right mt-3"> <div class="col text-right mt-3">
<a id="btn-add-image" class="btn"> <a id="btn-add-img" class="btn">
<i class="i-plus"></i> <i class="i-plus"></i>
<span>{% trans 'Add Preview' %}</span> <span>{% trans 'Add Preview' %}</span>
</a> </a>

View File

@ -2,16 +2,17 @@
{% load i18n %} {% load i18n %}
{% block admin %} {% block admin %}
<div class="ext-card-admin p-3"> <div class="bg-secondary cards-item-extra pt-3">
<div> <ul class="w-100">
<li class="d-flex justify-content-between me-0 w-100">
<a href="{{ extension.get_manage_url }}" class="btn btn-sm"> <a href="{{ extension.get_manage_url }}" class="btn btn-sm">
<i class="i-edit"></i> <i class="i-edit"></i>
<span>{% trans 'Edit' %}</span> <span>{% trans 'Edit' %}</span>
</a> </a>
<div class="align-items-center d-flex">
{% include "common/components/status.html" with object=extension class="badge-tag" %}
</div> </div>
</li>
<div> </ul>
{% include "common/components/status.html" with object=extension %}
</div>
</div> </div>
{% endblock admin %} {% endblock admin %}

View File

@ -25,7 +25,7 @@
<div class="row mt-4"> <div class="row mt-4">
<div class="col"> <div class="col">
{% if object_list %} {% if object_list %}
<div class="cards-list"> <div class="cards">
{% for extension in object_list %} {% for extension in object_list %}
{% include "extensions/manage/components/card.html" with show_type=True %} {% include "extensions/manage/components/card.html" with show_type=True %}
{% endfor %} {% endfor %}

View File

@ -68,7 +68,7 @@
<small>Source File</small> <small>Source File</small>
</a> </a>
</li> </li>
<li class="ml-auto"> <li class="ms-auto">
{% include "common/components/field.html" with field=inlineform.DELETE %} {% include "common/components/field.html" with field=inlineform.DELETE %}
</li> </li>
<li> <li>
@ -153,7 +153,7 @@
const previewDragContainer = document.querySelector('.js-previews-drag-container'); const previewDragContainer = document.querySelector('.js-previews-drag-container');
const previewDragClass = 'js-preview-drag'; const previewDragClass = 'js-preview-drag';
{% comment %} dragula([document.querySelector('.js-preview-drag'), document.querySelector('.cards-list')]); {% endcomment %} {% comment %} dragula([document.querySelector('.js-preview-drag'), document.querySelector('.cards')]); {% endcomment %}
dragula([previewDragContainer, previewDragContainer], { dragula([previewDragContainer, previewDragContainer], {
moves: function (el, container, handle) { moves: function (el, container, handle) {
return handle.className.includes(previewDragClass); return handle.className.includes(previewDragClass);

View File

@ -29,7 +29,7 @@
<span class="show-on-collapse blender-version"> <span class="show-on-collapse blender-version">
{% include "extensions/components/blender_version.html" with version=version %} {% include "extensions/components/blender_version.html" with version=version %}
</span> </span>
<ul class="ml-auto"> <ul class="ms-auto">
<li class="show-on-collapse">{{ version.file.size_bytes|filesizeformat }}</li> <li class="show-on-collapse">{{ version.file.size_bytes|filesizeformat }}</li>
<li class="show-on-collapse"><i class="i-download"></i> {{ version.download_count }}</li> <li class="show-on-collapse"><i class="i-download"></i> {{ version.download_count }}</li>
<li> <li>

View File

@ -10,6 +10,7 @@ from common.tests.factories.users import UserFactory
from common.tests.utils import _get_all_form_errors from common.tests.utils import _get_all_form_errors
from extensions.models import Extension, Version from extensions.models import Extension, Version
from files.models import File from files.models import File
from reviewers.models import ApprovalActivity
import utils import utils
@ -425,6 +426,14 @@ class NewVersionTest(TestCase):
f'/add-ons/{self.extension.slug}/manage/versions/new/{file.pk}/', f'/add-ons/{self.extension.slug}/manage/versions/new/{file.pk}/',
) )
self.assertEqual(self.extension.versions.count(), 1) self.assertEqual(self.extension.versions.count(), 1)
self.extension.approve()
self.assertEqual(
ApprovalActivity.objects.filter(
extension=self.extension,
type=ApprovalActivity.ActivityType.UPLOADED_NEW_VERSION,
).count(),
0,
)
# Check step 2: finalise new version and send to review # Check step 2: finalise new version and send to review
url = response['Location'] url = response['Location']
@ -444,3 +453,10 @@ class NewVersionTest(TestCase):
self.assertEqual(new_version.schema_version, '1.0.0') self.assertEqual(new_version.schema_version, '1.0.0')
self.assertEqual(new_version.release_notes, 'new version') self.assertEqual(new_version.release_notes, 'new version')
self.assertEqual(new_version.file.get_status_display(), 'Approved') self.assertEqual(new_version.file.get_status_display(), 'Approved')
self.assertEqual(
ApprovalActivity.objects.filter(
extension=self.extension,
type=ApprovalActivity.ActivityType.UPLOADED_NEW_VERSION,
).count(),
1,
)

View File

@ -18,6 +18,7 @@ VERB2FLAGS = {
Verb.REPORTED_RATING: [Flag.MODERATOR], Verb.REPORTED_RATING: [Flag.MODERATOR],
Verb.REQUESTED_CHANGES: [Flag.AUTHOR, Flag.REVIEWER], Verb.REQUESTED_CHANGES: [Flag.AUTHOR, Flag.REVIEWER],
Verb.REQUESTED_REVIEW: [Flag.MODERATOR, Flag.REVIEWER], Verb.REQUESTED_REVIEW: [Flag.MODERATOR, Flag.REVIEWER],
Verb.UPLOADED_NEW_VERSION: [],
} }
@ -41,7 +42,7 @@ def _create_notifications(
notifications = [] notifications = []
flags = VERB2FLAGS.get(instance.verb, None) flags = VERB2FLAGS.get(instance.verb, None)
if not flags: if flags is None:
logger.warning(f'no follower flags for verb={instance.verb}, nobody will be notified') logger.warning(f'no follower flags for verb={instance.verb}, nobody will be notified')
return return

View File

@ -20,7 +20,7 @@ and will most likely fail due to differences in configuration paths and so on.
To avoid adding more dependencies to the project itself, `ansible` uses its own `virtualenv`. To avoid adding more dependencies to the project itself, `ansible` uses its own `virtualenv`.
To set it up use the following commands: To set it up use the following commands:
virtualenv .venv -p python python3.10 -m venv .venv
source .venv/bin/activate source .venv/bin/activate
pip install -r requirements.txt pip install -r requirements.txt

View File

@ -32,7 +32,7 @@
h1 { h1 {
background: linear-gradient(to right, #0cc, violet); background: linear-gradient(to right, #0cc, violet);
background-clip: text; background-clip: text;
-webkit-background-clip: text; -webkit-bg-clip: text;
-webkit-text-fill-color: transparent; -webkit-text-fill-color: transparent;
font-weight: 300; font-weight: 300;
font-size: 1.6em; font-size: 1.6em;

View File

@ -51,6 +51,7 @@
-webkit-mask-size: var(--star-size) var(--star-size) -webkit-mask-size: var(--star-size) var(--star-size)
mask-size: var(--star-size) var(--star-size) mask-size: var(--star-size) var(--star-size)
text-align: left text-align: left
transform: translateY(-.2rem)
width: var(--stars-number) width: var(--stars-number)
span span
@ -62,6 +63,11 @@
--star-size: 1.6em --star-size: 1.6em
width: 8em width: 8em
// TODO: refactor stars-helper
.stars-helper
max-height: 1.4rem
transform: translateY(-.1rem)
.ratings-summary .ratings-summary
display: flex display: flex
flex-direction: column flex-direction: column
@ -87,9 +93,9 @@
text-decoration: none text-decoration: none
span span
color: var(--text-color-secondary) color: var(--color-text-secondary)
display: block display: block
font-size: var(--font-size-base) font-size: var(--fs-base)
.stars .stars
+margin(auto, x) +margin(auto, x)

View File

@ -2,6 +2,6 @@
{% blocktranslate asvar title with score=score %}Rated {{ score }} out of 5{% endblocktranslate %} {% blocktranslate asvar title with score=score %}Rated {{ score }} out of 5{% endblocktranslate %}
<span class="stars{% if size %} {{ size }}{% endif %}" title="{{ title }}"> <span class="stars me-1 {% if size %} {{ size }}{% endif %}" title="{{ title }}">
<span style="width: {% widthratio score 5 100 %}%"></span> <span style="width: {% widthratio score 5 100 %}%"></span>
</span> </span>

View File

@ -14,9 +14,9 @@
{{ rating.user }} {{ rating.user }}
</a> </a>
</li> </li>
<li class="mr-auto"> <li class="align-items-center d-flex me-auto">
{% with score_percentage=rating.score %} {% with score_percentage=rating.score %}
<a href="{{ extension.get_ratings_url }}?score={{ rating.score }}"> <a class="stars-helper" href="{{ extension.get_ratings_url }}?score={{ rating.score }}">
{% include "ratings/components/average.html" with score=rating.score %} {% include "ratings/components/average.html" with score=rating.score %}
</a> </a>
{% endwith %} {% endwith %}

View File

@ -4,7 +4,7 @@
{% block page_title %}Rate {{ extension.name }}{% endblock page_title %} {% block page_title %}Rate {{ extension.name }}{% endblock page_title %}
{% block content %} {% block content %}
<div class="container"> <div class="container rating-form">
<form method="post" enctype="multipart/form-data"> <form method="post" enctype="multipart/form-data">
{% csrf_token %} {% csrf_token %}
{% with form=form|add_form_classes %} {% with form=form|add_form_classes %}

View File

@ -13,7 +13,7 @@
{% endif %} {% endif %}
</h2> </h2>
{% if score %} {% if score %}
<div class="ml-auto"> <div class="ms-auto">
<a href="{{ extension.get_ratings_url }}" class="text-muted">See all</a> <a href="{{ extension.get_ratings_url }}" class="text-muted">See all</a>
</div> </div>
{% endif %} {% endif %}

View File

@ -0,0 +1,18 @@
# Generated by Django 4.2.11 on 2024-04-29 17:33
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('reviewers', '0008_alter_approvalactivity_message'),
]
operations = [
migrations.AlterField(
model_name='approvalactivity',
name='type',
field=models.CharField(choices=[('COM', 'Comment'), ('APR', 'Approved'), ('AWC', 'Awaiting Changes'), ('AWR', 'Awaiting Review'), ('UNV', 'Uploaded New Version')], default='COM', max_length=3),
),
]

View File

@ -80,6 +80,7 @@ class ApprovalActivity(CreatedModifiedMixin, RecordDeletionMixin, models.Model):
APPROVED = "APR", _("Approved") APPROVED = "APR", _("Approved")
AWAITING_CHANGES = "AWC", _("Awaiting Changes") AWAITING_CHANGES = "AWC", _("Awaiting Changes")
AWAITING_REVIEW = "AWR", _("Awaiting Review") AWAITING_REVIEW = "AWR", _("Awaiting Review")
UPLOADED_NEW_VERSION = "UNV", _("Uploaded New Version")
user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True) user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True)
extension = models.ForeignKey( extension = models.ForeignKey(

View File

@ -30,6 +30,7 @@ def _create_action_from_review_and_follow(
ApprovalActivity.ActivityType.AWAITING_CHANGES: Verb.REQUESTED_CHANGES, ApprovalActivity.ActivityType.AWAITING_CHANGES: Verb.REQUESTED_CHANGES,
ApprovalActivity.ActivityType.AWAITING_REVIEW: Verb.REQUESTED_REVIEW, ApprovalActivity.ActivityType.AWAITING_REVIEW: Verb.REQUESTED_REVIEW,
ApprovalActivity.ActivityType.COMMENT: Verb.COMMENTED, ApprovalActivity.ActivityType.COMMENT: Verb.COMMENTED,
ApprovalActivity.ActivityType.UPLOADED_NEW_VERSION: Verb.UPLOADED_NEW_VERSION,
} }
action.send( action.send(
instance.user, instance.user,

View File

@ -6,27 +6,25 @@
{{ extension.name }} {{ extension.name }}
</a> </a>
</td> </td>
<td> <td>{% include "extensions/components/authors.html" %}</td>
{% if extension.authors.count %}
{% include "extensions/components/authors.html" %}
{% endif %}
</td>
<td title="{{ extension.date_created }}">{{ extension.date_created|naturaltime_compact }}</td> <td title="{{ extension.date_created }}">{{ extension.date_created|naturaltime_compact }}</td>
<td class="d-flex"> <td class="d-flex">
<a href="{{ extension.get_review_url }}#activity"> <a href="{{ extension.get_review_url }}#activity">
<span>{{ extension.review_activity.all|length }}</span> <span>{{ stats.count }}</span>
</a> </a>
<a href="{{ extension.get_review_url }}#activity-{{ stats.last_activity.id }}" class="ms-3">
{% if extension.review_activity.all %} <span>{{ stats.last_activity.date_created|naturaltime_compact }}</span>
<a href="{{ extension.get_review_url }}#activity-{{ extension.review_activity.all.last.id }}" class="ml-3">
<span>{{ extension.review_activity.all.last.date_created|naturaltime_compact }}</span>
</a> </a>
{% endif %}
{% include "files/components/scan_details_flag.html" with suspicious_files=extension.suspicious_files %} {% include "files/components/scan_details_flag.html" with suspicious_files=extension.suspicious_files %}
</td> </td>
<td> <td>
<a href="{{ extension.get_review_url }}" class="text-decoration-none"> <a href="{{ extension.get_review_url }}" class="text-decoration-none">
{% include "common/components/status.html" with object=extension class="d-block" %} {% with last_type=stats.last_type_display|default:"Awaiting Review" %}
<div class="d-block badge badge-status-{{ last_type|slugify }}">
<i class="i-eye"></i>
<span>{{ last_type }}</span>
</div>
{% endwith %}
</a> </a>
</td> </td>
</tr> </tr>

View File

@ -24,7 +24,7 @@
{% trans "Version History" %} {% trans "Version History" %}
</a> </a>
<span class="ml-auto"></span> <span class="ms-auto"></span>
<div class="btn-row"> <div class="btn-row">
{% if is_maintainer %} {% if is_maintainer %}
@ -101,8 +101,7 @@
{% for activity in extension.review_activity.all %} {% for activity in extension.review_activity.all %}
<li id="activity-{{ activity.id }}"> <li id="activity-{{ activity.id }}">
{# All activities except comments. #} {% if activity.type in status_change_types %}
{% if activity.type != 'COM' %}
<div class="activity-item activity-status-change activity-status-{{ activity.get_type_display|slugify }}"> <div class="activity-item activity-status-change activity-status-{{ activity.get_type_display|slugify }}">
<i class="activity-icon i-activity-{{ activity.get_type_display|slugify }}"></i> <i class="activity-icon i-activity-{{ activity.get_type_display|slugify }}"></i>
@ -139,7 +138,7 @@
{{ activity.user }} {{ activity.user }}
</a> </a>
</li> </li>
<li class="ml-auto"> <li class="ms-auto">
<a href="#activity-{{ activity.id }}" title="{{ activity.date_created }}"> <a href="#activity-{{ activity.id }}" title="{{ activity.date_created }}">
{{ activity.date_created|naturaltime_compact }} {{ activity.date_created|naturaltime_compact }}
</a> </a>
@ -167,8 +166,11 @@
{% include "common/components/field.html" with field=form.message %} {% include "common/components/field.html" with field=form.message %}
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
<div class="btn-row ml-3 w-100 justify-content-end"> <div class="btn-row ms-3 w-100 justify-content-end">
{% if is_maintainer or request.user.is_moderator %}
{% include "common/components/field.html" with field=form.type %} {% include "common/components/field.html" with field=form.type %}
{% endif %}
<button type="submit" id="activity-submit" class="btn btn-primary"> <button type="submit" id="activity-submit" class="btn btn-primary">
<span>{% trans "Comment" %}</span> <span>{% trans "Comment" %}</span>
</button> </button>

View File

@ -34,12 +34,10 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for extension in object_list %} {% for stats in object_list %}
{% if user.is_moderator %} {% with extension=stats.extension %}
{% include 'reviewers/components/review_list_item.html' %} {% include 'reviewers/components/review_list_item.html' with extension=extension stats=stats %}
{% elif extension.status_slug == 'awaiting-review' %} {% endwith %}
{% include 'reviewers/components/review_list_item.html' %}
{% endif %}
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>

View File

@ -3,13 +3,21 @@ from django.shortcuts import reverse
from common.tests.factories.extensions import create_version from common.tests.factories.extensions import create_version
from files.models import File from files.models import File
from reviewers.models import ApprovalActivity
class CommentsViewTest(TestCase): class CommentsViewTest(TestCase):
fixtures = ['licenses'] fixtures = ['licenses']
def setUp(self): def setUp(self):
self.default_version = create_version(file__status=File.STATUSES.AWAITING_REVIEW) version = create_version(file__status=File.STATUSES.AWAITING_REVIEW)
self.default_version = version
ApprovalActivity(
type=ApprovalActivity.ActivityType.COMMENT,
user=version.file.user,
extension=version.extension,
message='test comment',
).save()
# List of extensions under review does not require authentication # List of extensions under review does not require authentication
def test_list_visibility(self): def test_list_visibility(self):

View File

@ -13,15 +13,51 @@ from reviewers.models import ApprovalActivity
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
STATUS_CHANGE_TYPES = [
ApprovalActivity.ActivityType.APPROVED,
ApprovalActivity.ActivityType.AWAITING_CHANGES,
ApprovalActivity.ActivityType.AWAITING_REVIEW,
]
class ApprovalQueueView(ListView): class ApprovalQueueView(ListView):
model = Extension model = Extension
paginate_by = 100 paginate_by = 100
def get_queryset(self): def get_queryset(self):
return Extension.objects.exclude(status=Extension.STATUSES.APPROVED).order_by( qs = (
'-date_created' ApprovalActivity.objects.prefetch_related(
'extension',
'extension__authors',
'extension__versions',
'extension__versions__file',
'extension__versions__file__validation',
) )
.order_by('-date_created')
.all()
)
by_extension = {}
result = []
for item in qs:
extension = item.extension
stats = by_extension.get(extension, None)
if not stats:
# this check guarantees that we add a record only once per extension,
# and iterating over qs we get result also ordered by item.date_created
stats = {
'count': 0,
'extension': extension,
'last_activity': None,
'last_type_display': None,
}
by_extension[extension] = stats
result.append(stats)
stats['count'] += 1
if not stats.get('last_activity', None):
stats['last_activity'] = item
if not stats.get('last_type_display', None) and item.type in STATUS_CHANGE_TYPES:
stats['last_type_display'] = item.get_type_display
return result
template_name = 'reviewers/extensions_review_list.html' template_name = 'reviewers/extensions_review_list.html'
@ -36,32 +72,30 @@ class ExtensionsApprovalDetailView(DetailView):
ctx['pending_previews'] = self.object.preview_set.exclude( ctx['pending_previews'] = self.object.preview_set.exclude(
file__status=File.STATUSES.APPROVED file__status=File.STATUSES.APPROVED
) )
ctx['status_change_types'] = STATUS_CHANGE_TYPES
if self.request.user.is_authenticated: if self.request.user.is_authenticated:
form = ctx['comment_form'] = CommentForm() form = ctx['comment_form'] = CommentForm()
# Remove 'Approved' status from dropdown it not moderator # anyone can comment
filtered_activity_types = ApprovalActivity.ActivityType.choices filtered_activity_types = {ApprovalActivity.ActivityType.COMMENT}
user = self.request.user user = self.request.user
if not (user.is_moderator or user.is_superuser): if self.object.has_maintainer(user):
filtered_activity_types = [ filtered_activity_types.add(ApprovalActivity.ActivityType.AWAITING_REVIEW)
t if user.is_moderator or user.is_superuser:
for t in ApprovalActivity.ActivityType.choices filtered_activity_types.update(
if t[0] [
not in [
ApprovalActivity.ActivityType.APPROVED, ApprovalActivity.ActivityType.APPROVED,
ApprovalActivity.ActivityType.AWAITING_CHANGES, ApprovalActivity.ActivityType.AWAITING_CHANGES,
] ]
] )
if not self.object.has_maintainer(user): choices = list(
# Other accounts can only comment filter(
filtered_activity_types = [ lambda c: c[0] in filtered_activity_types, ApprovalActivity.ActivityType.choices
t )
for t in ApprovalActivity.ActivityType.choices )
if t[0] == ApprovalActivity.ActivityType.COMMENT form.fields['type'].choices = choices
] form.fields['type'].widget.choices = choices
form.fields['type'].choices = filtered_activity_types if len(choices) == 1:
form.fields['type'].widget.choices = filtered_activity_types
if len(filtered_activity_types) == 1:
form.fields['type'].widget = django.forms.HiddenInput() form.fields['type'].widget = django.forms.HiddenInput()
return ctx return ctx

View File

@ -1,3 +1,3 @@
{% for _slug, badge in badges.items %} {% for _slug, badge in badges.items %}
<img class="mr-2" width="{{ width }}" src="{{ badge.image }}" title="{{ badge.description }}" label="{{ badge.label }}" /> <img class="me-2" width="{{ width }}" src="{{ badge.image }}" title="{{ badge.description }}" label="{{ badge.label }}" />
{% endfor %} {% endfor %}

View File

@ -1,7 +1,7 @@
{% load static %} {% load static %}
<img src="{% if user.image %}{{ user.image.url }}{% else %}{% static 'common/images/blank-profile-pic.png' %}{% endif %}" class="profile-avatar {{ classes }}"> <img src="{% if user.image %}{{ user.image.url }}{% else %}{% static 'common/images/blank-profile-pic.png' %}{% endif %}" class="profile-avatar {{ classes }}">
{% if show_name %} {% if show_name %}
<span class="ml-2"> <span class="ms-2">
{% firstof user.full_name user.username %} {% firstof user.full_name user.username %}
</span> </span>
{% endif %} {% endif %}

View File

@ -12,7 +12,7 @@
<div class="row"> <div class="row">
<div class="d-none d-md-block col-md-3"> <div class="d-none d-md-block col-md-3">
<div class="is-sticky pt-4"> <div class="is-sticky pt-4">
<nav class="box nav-drawer-nested"> <nav class="box nav-drawer-nested p-3">
<div class="nav-drawer-body fw-bold"> <div class="nav-drawer-body fw-bold">
{% include 'users/settings/tabs.html' %} {% include 'users/settings/tabs.html' %}
</div> </div>

View File

@ -27,12 +27,12 @@
</div> </div>
<div class="col-md-6 mb-4"> <div class="col-md-6 mb-4">
<label class="mb-2 h3" for="email"> <label class="mb-2 h3" for="email">
<span class="mr-1">Email</span> <span class="me-1">Email</span>
{% if user.confirmed_email_at %} {% if user.confirmed_email_at %}
<span class="align-middle badge badge-sm badge-success"><i class="i-check"></i>Confirmed</span> <span class="align-middle badge badge-success fs-xs"><i class="i-check"></i>Confirmed</span>
{% else %} {% else %}
<span class="align-middle badge badge-sm badge-warning"><i class="i-alert-triangle"></i>Not yet confirmed</span> <span class="align-middle badge badge-warning fs-xs"><i class="i-alert-triangle"></i>Not yet confirmed</span>
{% endif %} {% endif %}
</label> </label>
<div class="align-items-center d-flex position-relative"> <div class="align-items-center d-flex position-relative">

View File

@ -3,9 +3,11 @@
{% include "common/components/nav_link.html" with name="users:my-profile" title="Profile" classes="i-home py-2" %} {% include "common/components/nav_link.html" with name="users:my-profile" title="Profile" classes="i-home py-2" %}
{% if user.teams.count %} {% if user.teams.count %}
{% include "common/components/nav_link.html" with name="teams:list" title="Teams" classes="i-people py-2" %} {% include "common/components/nav_link.html" with name="teams:list" title="Teams" classes="i-users py-2" %}
{% endif %} {% endif %}
<div class="nav-pills-divider"></div>
{% include "common/components/nav_link.html" with name="users:my-profile-delete" title="Delete account" classes="i-trash py-2" %} {% include "common/components/nav_link.html" with name="users:my-profile-delete" title="Delete account" classes="i-trash py-2" %}
</div> </div>
{% endspaceless %} {% endspaceless %}