Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add publication date to collections #3369

Merged
merged 12 commits into from
Dec 20, 2018
4 changes: 2 additions & 2 deletions saleor/core/templatetags/status.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,10 @@ def render_page_availability(page):

@register.inclusion_tag('dashboard/includes/_collection_availability.html')
def render_collection_availability(collection):
if collection.is_published:
if collection.is_visible:
label_cls = LABEL_SUCCESS
else:
label_cls = LABEL_DANGER
return {'is_published': collection.is_published,
return {'is_visible': collection.is_visible,
'collection': collection,
'label_cls': label_cls}
4 changes: 4 additions & 0 deletions saleor/dashboard/collection/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ class Meta:
'is_published': pgettext_lazy(
'Collection published toggle',
'Published'),
'published_date': pgettext_lazy(
'The publication date field, can be a posterior date for '
'a planned publication.',
'Published date'),
'description': pgettext_lazy(
'Description field of a collection',
'Description')}
Expand Down
2 changes: 2 additions & 0 deletions saleor/graphql/product/mutations/products.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ class CollectionInput(graphene.InputObjectType):
description = graphene.String(description='Description of the collection.')
background_image = Upload(description='Background image file.')
seo = SeoInput(description='Search engine optimization fields.')
published_date = graphene.String(
k-brk marked this conversation as resolved.
Show resolved Hide resolved
description='Publication date. ISO 8601 standard.')


class CollectionCreateInput(CollectionInput):
Expand Down
4 changes: 4 additions & 0 deletions saleor/graphql/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,7 @@ type Collection implements Node {
backgroundImage: Image
isPublished: Boolean!
description: String!
publishedDate: Date
}

type CollectionAddProducts {
Expand Down Expand Up @@ -438,6 +439,7 @@ input CollectionCreateInput {
description: String
backgroundImage: Upload
seo: SeoInput
publishedDate: String
products: [ID]
}

Expand All @@ -453,6 +455,7 @@ input CollectionInput {
description: String
backgroundImage: Upload
seo: SeoInput
publishedDate: String
}

type CollectionRemoveProducts {
Expand Down Expand Up @@ -668,6 +671,7 @@ input FulfillmentUpdateTrackingInput {
enum GatewaysEnum {
DUMMY
BRAINTREE
RAZORPAY
}

scalar GenericScalar
Expand Down
18 changes: 18 additions & 0 deletions saleor/product/migrations/0080_collection_published_date.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 2.1.3 on 2018-12-03 20:52

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('product', '0079_default_tax_rate_instead_of_empty_field'),
]

operations = [
migrations.AddField(
model_name='collection',
name='published_date',
field=models.DateField(blank=True, null=True),
),
]
13 changes: 11 additions & 2 deletions saleor/product/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,10 @@ class VariantImage(models.Model):
class CollectionQuerySet(models.QuerySet):

def public(self):
return self.filter(is_published=True)
return self.filter(
Q(is_published=True),
Q(published_date__isnull=True)
| Q(published_date__lte=datetime.date.today()))

def visible_to_user(self, user):
has_access_to_all = (
Expand All @@ -454,7 +457,7 @@ class Collection(SeoModel):
upload_to='collection-backgrounds', blank=True, null=True)
is_published = models.BooleanField(default=False)
description = models.TextField(blank=True)

published_date = models.DateField(blank=True, null=True)
objects = CollectionQuerySet.as_manager()
translated = TranslationProxy()

Expand All @@ -469,6 +472,12 @@ def get_absolute_url(self):
'product:collection',
kwargs={'pk': self.id, 'slug': self.slug})

@property
def is_visible(self):
return self.is_published and (
self.published_date is None or
self.published_date <= datetime.date.today())


class CollectionTranslation(SeoModelTranslation):
language_code = models.CharField(max_length=10)
Expand Down
7 changes: 7 additions & 0 deletions templates/collection/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@
You are previewing a collection that is not published.
{% endblocktrans %}
</div>
{% elif object.published_date is not None %}
k-brk marked this conversation as resolved.
Show resolved Hide resolved
<div class="alert alert-warning" role="alert">
{% blocktrans trimmed with date=object.published_date|date context "Unpublished collection details text" %}
<strong>Warning!</strong>
You are previewing a collection that will become visible on <strong>{{ date }}</strong>.
k-brk marked this conversation as resolved.
Show resolved Hide resolved
{% endblocktrans %}
</div>
{% endif %}
{% endblock %}

Expand Down
3 changes: 3 additions & 0 deletions templates/dashboard/collection/detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@
<div class="row">
{{ form.is_published|materializecss }}
</div>
<div class="row">
{{ form.published_date|materializecss }}
</div>
{% include "dashboard/includes/_google_preview.html" with object=collection form=form %}
</div>
<div class="card-action right-align">
Expand Down
20 changes: 14 additions & 6 deletions templates/dashboard/includes/_collection_availability.html
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
{% load i18n %}

<div class="label label-{{ label_cls }}">
{% if is_published %}
{% trans "Published" context "Collection availability status" %}
{% else %}
{% trans "Not Published" context "Collection availability status" %}
{% endif %}
<div class="label label-{{ label_cls }}">
{% if is_visible %}
{% trans "Published" context "Collection availability status" %}
{% elif not collection.is_published %}
k-brk marked this conversation as resolved.
Show resolved Hide resolved
{% trans "Hidden" context "Collection availability status" %}
{% elif collection.published_date is not None %}
<div class="label">
k-brk marked this conversation as resolved.
Show resolved Hide resolved
{% blocktrans with date=collection.published_date context "Collection availability status" %}
Hidden (will become visible on {{ date }})
k-brk marked this conversation as resolved.
Show resolved Hide resolved
{% endblocktrans %}
</div>
{% else %}
{% trans "Hidden" context "Collection availability status" %}
{% endif %}
</div>
24 changes: 17 additions & 7 deletions tests/api/test_collection.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import graphene
import pytest
from datetime import date
from django.utils.text import slugify
from graphql_relay import to_global_id
from unittest.mock import Mock
Expand Down Expand Up @@ -41,7 +42,6 @@ def test_collections_query(
assert collection_data['slug'] == collection.slug
assert collection_data['description'] == collection.description
assert collection_data['products']['totalCount'] == collection.products.count()

# query all collections only as a staff user with proper permissions
staff_api_client.user.user_permissions.add(permission_manage_products)
response = staff_api_client.post_graphql(query)
Expand All @@ -54,16 +54,21 @@ def test_create_collection(
monkeypatch, staff_api_client, product_list, permission_manage_products):
query = """
mutation createCollection(
$name: String!, $slug: String!, $description: String, $products: [ID], $backgroundImage: Upload!, $isPublished: Boolean!) {
$name: String!, $slug: String!, $description: String, $products: [ID], $backgroundImage: Upload!, $isPublished: Boolean!, $publishedDate: String) {
collectionCreate(
input: {name: $name, slug: $slug, description: $description, products: $products, backgroundImage: $backgroundImage, isPublished: $isPublished}) {
input: {name: $name, slug: $slug, description: $description, products: $products, backgroundImage: $backgroundImage, isPublished: $isPublished, publishedDate: $publishedDate}) {
collection {
name
slug
description
products {
totalCount
}
publishedDate
}
errors {
field
message
}
}
}
Expand All @@ -81,10 +86,11 @@ def test_create_collection(
name = 'test-name'
slug = 'test-slug'
description = 'test-description'
published_date = date.today().isoformat()
variables = {
'name': name, 'slug': slug, 'description': description,
'products': product_ids, 'backgroundImage': image_name,
'isPublished': True}
'isPublished': True, 'publishedDate': published_date}
body = get_multipart_request_body(query, variables, image_file, image_name)
response = staff_api_client.post_multipart(
body, permissions=[permission_manage_products])
Expand All @@ -93,6 +99,7 @@ def test_create_collection(
assert data['name'] == name
assert data['slug'] == slug
assert data['description'] == description
assert data['publishedDate'] == published_date
assert data['products']['totalCount'] == len(product_ids)
collection = Collection.objects.get(slug=slug)
assert collection.background_image.file
Expand Down Expand Up @@ -132,13 +139,14 @@ def test_update_collection(
monkeypatch, staff_api_client, collection, permission_manage_products):
query = """
mutation updateCollection(
$name: String!, $slug: String!, $description: String, $id: ID!, $isPublished: Boolean!) {
$name: String!, $slug: String!, $description: String, $id: ID!, $isPublished: Boolean!, $publishedDate: String) {
collectionUpdate(
id: $id, input: {name: $name, slug: $slug, description: $description, isPublished: $isPublished}) {
id: $id, input: {name: $name, slug: $slug, description: $description, isPublished: $isPublished, publishedDate: $publishedDate}) {
collection {
name
slug
description
publishedDate
}
}
}
Expand All @@ -153,15 +161,17 @@ def test_update_collection(
name = 'new-name'
slug = 'new-slug'
description = 'new-description'
published_date = date.today().isoformat()
variables = {
'name': name, 'slug': slug, 'description': description,
'id': to_global_id('Collection', collection.id), 'isPublished': True}
'id': to_global_id('Collection', collection.id), 'isPublished': True, 'publishedDate': published_date}
response = staff_api_client.post_graphql(
query, variables, permissions=[permission_manage_products])
content = get_graphql_content(response)
data = content['data']['collectionUpdate']['collection']
assert data['name'] == name
assert data['slug'] == slug
assert data['publishedDate'] == published_date
assert mock_create_thumbnails.call_count == 0


Expand Down
1 change: 0 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from io import BytesIO
from unittest.mock import MagicMock, Mock

import pytest

from django.contrib.auth.models import Permission
Expand Down
16 changes: 14 additions & 2 deletions tests/test_collection.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from django.urls import reverse

from datetime import date, timedelta
from .utils import get_redirect_location


Expand Down Expand Up @@ -33,8 +33,20 @@ def test_collection_not_exists(client):
assert response.status_code == 404


def test_collection_not_published_404(client, draft_collection):
def test_collection_not_yet_published_returns_404(
admin_client, client, draft_collection):
url_kwargs = {'pk': draft_collection.pk, 'slug': draft_collection.slug}
url = reverse('product:collection', kwargs=url_kwargs)
response = client.get(url)
assert response.status_code == 404

# A non staff user should not have access to collections yet to be published
# A staff user should have access to collections yet to be published
k-brk marked this conversation as resolved.
Show resolved Hide resolved
draft_collection.is_published = True
draft_collection.published_date = date.today() + timedelta(days=1)
draft_collection.save()
k-brk marked this conversation as resolved.
Show resolved Hide resolved
response = client.get(url)
assert response.status_code == 404

response = admin_client.get(url)
assert response.status_code == 200