Add content collections with create_default script
This commit is contained in:
parent
f9c3e82b66
commit
5425c1b22c
|
|
@ -1,18 +1,10 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
from wagtail import blocks
|
from wagtail import blocks
|
||||||
from wagtail.admin.panels import FieldPanel
|
from wagtail.admin.panels import FieldPanel
|
||||||
from wagtail.documents.blocks import DocumentChooserBlock
|
from wagtail.documents.blocks import DocumentChooserBlock
|
||||||
from wagtail.snippets.models import register_snippet
|
from wagtail.snippets.models import register_snippet
|
||||||
|
|
||||||
|
|
||||||
class VisualisationType(models.TextChoices):
|
|
||||||
LEARNING_MEDIA = 'LearningMedia', _('Lernmedien')
|
|
||||||
LINK = 'Link', _('Links')
|
|
||||||
ANKER = 'Anker', _('Verankerung')
|
|
||||||
CROSSREFERENCE = 'CrossReference', _('Querverweise')
|
|
||||||
|
|
||||||
|
|
||||||
@register_snippet
|
@register_snippet
|
||||||
class MediaLibraryContent(models.Model):
|
class MediaLibraryContent(models.Model):
|
||||||
title = models.TextField()
|
title = models.TextField()
|
||||||
|
|
@ -53,16 +45,11 @@ class CrossReferenceBlock(blocks.StructBlock):
|
||||||
category = blocks.PageChooserBlock(page_type='media_library.MediaCategoryPage')
|
category = blocks.PageChooserBlock(page_type='media_library.MediaCategoryPage')
|
||||||
|
|
||||||
|
|
||||||
class ContentCollection(blocks.StructBlock):
|
class MediaContentCollection(blocks.StructBlock):
|
||||||
"""
|
"""
|
||||||
Lernmedien, Links, Querverweise, Verankerung
|
Lernmedien, Links, Querverweise, Verankerung
|
||||||
"""
|
"""
|
||||||
title = blocks.TextBlock()
|
title = blocks.TextBlock()
|
||||||
collection_type = blocks.MultipleChoiceBlock(
|
|
||||||
choices=VisualisationType.choices,
|
|
||||||
max_length=20,
|
|
||||||
default=VisualisationType.LEARNING_MEDIA
|
|
||||||
)
|
|
||||||
contents = blocks.StreamBlock([
|
contents = blocks.StreamBlock([
|
||||||
('Links', LinkBlock()),
|
('Links', LinkBlock()),
|
||||||
('Documents', DocumentChooserBlock()),
|
('Documents', DocumentChooserBlock()),
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ def create_default_documents():
|
||||||
document = LibraryDocumentFactory(
|
document = LibraryDocumentFactory(
|
||||||
title='V1 C25 ZGB CH',
|
title='V1 C25 ZGB CH',
|
||||||
display_text='Schweizerisches Zivilgesetzbuch',
|
display_text='Schweizerisches Zivilgesetzbuch',
|
||||||
description='Ein wundervolles Dokument, Bachblüten für Leseratten und metaphysisches Wolbefinden für Handyvekäufer.',
|
description='Ein wundervolles Dokument, Bachblüten für Leseratten und metaphysisches Wohlbefinden für Handyvekäufer.',
|
||||||
link_display_text='Dokument laden',
|
link_display_text='Dokument laden',
|
||||||
file=factory.django.FileField(from_path=os.path.join(path, filename), filename=filename),
|
file=factory.django.FileField(from_path=os.path.join(path, filename), filename=filename),
|
||||||
collection=collection
|
collection=collection
|
||||||
|
|
@ -40,7 +40,7 @@ def create_default_documents():
|
||||||
document = LibraryDocumentFactory(
|
document = LibraryDocumentFactory(
|
||||||
title='V1 C25 ',
|
title='V1 C25 ',
|
||||||
display_text='Pdf showcase ',
|
display_text='Pdf showcase ',
|
||||||
description='Ein wundervolles Dokument, Bachblüten für Leseratten und metaphysisches Wolbefinden für Handyvekäufer.',
|
description='Ein wundervolles Dokument, Bachblüten für Leseratten und metaphysisches Wohlbefinden für Handyvekäufer.',
|
||||||
link_display_text='Dokument laden',
|
link_display_text='Dokument laden',
|
||||||
file=factory.django.FileField(from_path=os.path.join(path, filename), filename=filename),
|
file=factory.django.FileField(from_path=os.path.join(path, filename), filename=filename),
|
||||||
collection=collection
|
collection=collection
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import json
|
||||||
from vbv_lernwelt.course.consts import COURSE_VERSICHERUNGSVERMITTLERIN
|
from vbv_lernwelt.course.consts import COURSE_VERSICHERUNGSVERMITTLERIN
|
||||||
from vbv_lernwelt.course.models import CoursePage, Course
|
from vbv_lernwelt.course.models import CoursePage, Course
|
||||||
from vbv_lernwelt.media_library.tests.media_library_factories import MediaLibraryPageFactory, MediaCategoryPageFactory, \
|
from vbv_lernwelt.media_library.tests.media_library_factories import MediaLibraryPageFactory, MediaCategoryPageFactory, \
|
||||||
collection_body_dict
|
create_media_content_link, LinkBlockFactory, create_link_collection, create_document_collection
|
||||||
|
|
||||||
|
|
||||||
def create_default_media_library():
|
def create_default_media_library():
|
||||||
|
|
@ -22,11 +22,20 @@ def create_default_media_library():
|
||||||
die der Fahrzeugbesitzer und die Fahrzeugbesitzerin in einem grösseren Schadenfall oft nur schwer selbst aufbringen kann.
|
die der Fahrzeugbesitzer und die Fahrzeugbesitzerin in einem grösseren Schadenfall oft nur schwer selbst aufbringen kann.
|
||||||
'''.strip()
|
'''.strip()
|
||||||
description = 'Supi'
|
description = 'Supi'
|
||||||
|
body_data = json.dumps([
|
||||||
|
create_document_collection(),
|
||||||
|
create_link_collection(
|
||||||
|
links_dict=[
|
||||||
|
create_media_content_link(LinkBlockFactory(title='Nationales Versicherungsbüro', url='https://www.vbv.ch/')),
|
||||||
|
create_media_content_link(LinkBlockFactory(title='Adressen der Strassenverkehrsämter', url='https://www.vbv.ch/')),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
])
|
||||||
media_category = MediaCategoryPageFactory(
|
media_category = MediaCategoryPageFactory(
|
||||||
title=cat.name,
|
title=cat.name,
|
||||||
course_category=cat,
|
course_category=cat,
|
||||||
parent=media_lib_page,
|
parent=media_lib_page,
|
||||||
introduction_text=introduction_text,
|
introduction_text=introduction_text,
|
||||||
description=description,
|
description=description,
|
||||||
body=json.dumps(collection_body_dict())
|
body=body_data,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
# Iterativ GmbH
|
|
||||||
# http://www.iterativ.ch/
|
|
||||||
#
|
|
||||||
# Copyright (c) 2015 Iterativ GmbH. All rights reserved.
|
|
||||||
#
|
|
||||||
# Created on 2022-09-14
|
|
||||||
# @author: lorenz.padberg@iterativ.ch
|
|
||||||
[{"id": "a2a7ef07-64d9-444e-8069-4894a1e857ee",
|
|
||||||
"type": "content_collection",
|
|
||||||
"value": {"title": "Lernmedien",
|
|
||||||
"contents": [{
|
|
||||||
"id": "5b24aa0b-17d7-4b04-8698-f86d2116d6df",
|
|
||||||
"type": "Documents",
|
|
||||||
"value": 2}, {
|
|
||||||
"id": "e2d43794-037f-4a19-8a71-c7e9d96d8dac",
|
|
||||||
"type": "Documents",
|
|
||||||
"value": 1}],
|
|
||||||
"collection_type": [
|
|
||||||
"LearningMedia"]}
|
|
||||||
},
|
|
||||||
{"id": "23d13543-2f37-44b8-b3e2-0ef36f7c5a13",
|
|
||||||
"type": "content_collection",
|
|
||||||
"value": {"title": "Links", "contents": [
|
|
||||||
{"id": "57e35e12-ceb3-4873-9629-a6c5a3c2f6da",
|
|
||||||
"type": "Links",
|
|
||||||
"value": {"url": "http://www.admin.ch",
|
|
||||||
"title": "Wichtige Webseite",
|
|
||||||
"description": "interessantes zu versicherungen",
|
|
||||||
"link_display_text": "Link öffnen"}}],
|
|
||||||
"collection_type": [
|
|
||||||
"LearningMedia"]}}]
|
|
||||||
|
|
@ -6,6 +6,6 @@ from vbv_lernwelt.media_library.create_default_media_library import create_defau
|
||||||
|
|
||||||
@click.command()
|
@click.command()
|
||||||
def command():
|
def command():
|
||||||
create_default_media_library()
|
|
||||||
create_default_collections()
|
create_default_collections()
|
||||||
create_default_documents()
|
create_default_documents()
|
||||||
|
create_default_media_library()
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# Generated by Django 3.2.13 on 2022-09-19 15:57
|
# Generated by Django 3.2.13 on 2022-09-23 12:42
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
@ -18,9 +18,9 @@ class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('course', '0001_initial'),
|
('course', '0001_initial'),
|
||||||
('wagtailcore', '0069_log_entry_jsonfield'),
|
|
||||||
('taggit', '0004_alter_taggeditem_content_type_alter_taggeditem_tag'),
|
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
('taggit', '0004_alter_taggeditem_content_type_alter_taggeditem_tag'),
|
||||||
|
('wagtailcore', '0069_log_entry_jsonfield'),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
|
|
@ -49,7 +49,7 @@ class Migration(migrations.Migration):
|
||||||
('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')),
|
('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')),
|
||||||
('introduction_text', models.TextField(default='')),
|
('introduction_text', models.TextField(default='')),
|
||||||
('description', wagtail.fields.RichTextField(default='')),
|
('description', wagtail.fields.RichTextField(default='')),
|
||||||
('body', wagtail.fields.StreamField([('content_collection', wagtail.blocks.StructBlock([('title', wagtail.blocks.TextBlock()), ('collection_type', wagtail.blocks.MultipleChoiceBlock(choices=[('LearningMedia', 'Lernmedien'), ('Link', 'Links'), ('Anker', 'Verankerung'), ('CrossReference', 'Querverweise')], max_length=20)), ('contents', wagtail.blocks.StreamBlock([('Links', wagtail.blocks.StructBlock([('title', wagtail.blocks.TextBlock(blank=False, null=False)), ('description', wagtail.blocks.TextBlock(default='')), ('link_display_text', wagtail.blocks.CharBlock(default='Link öffnen', max_length=255)), ('url', wagtail.blocks.URLBlock())])), ('Documents', wagtail.documents.blocks.DocumentChooserBlock()), ('Ankers', vbv_lernwelt.media_library.content_blocks.AnchorBlock()), ('CrossReference', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock(default='')), ('link_display_text', wagtail.blocks.CharBlock(default='Link öffnen', max_length=255)), ('category', wagtail.blocks.PageChooserBlock(page_type=['media_library.MediaCategoryPage']))]))]))]))], null=True, use_json_field=True)),
|
('body', wagtail.fields.StreamField([('content_collection', wagtail.blocks.StructBlock([('title', wagtail.blocks.TextBlock()), ('contents', wagtail.blocks.StreamBlock([('Links', wagtail.blocks.StructBlock([('title', wagtail.blocks.TextBlock(blank=False, null=False)), ('description', wagtail.blocks.TextBlock(default='')), ('link_display_text', wagtail.blocks.CharBlock(default='Link öffnen', max_length=255)), ('url', wagtail.blocks.URLBlock())])), ('Documents', wagtail.documents.blocks.DocumentChooserBlock()), ('Ankers', vbv_lernwelt.media_library.content_blocks.AnchorBlock()), ('CrossReference', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock(default='')), ('link_display_text', wagtail.blocks.CharBlock(default='Link öffnen', max_length=255)), ('category', wagtail.blocks.PageChooserBlock(page_type=['media_library.MediaCategoryPage']))]))]))]))], null=True, use_json_field=True)),
|
||||||
('course_category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='course.coursecategory')),
|
('course_category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='course.coursecategory')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ from wagtail.documents.models import AbstractDocument, Document
|
||||||
from wagtail.models import Page
|
from wagtail.models import Page
|
||||||
|
|
||||||
from vbv_lernwelt.core.model_utils import find_available_slug
|
from vbv_lernwelt.core.model_utils import find_available_slug
|
||||||
from vbv_lernwelt.media_library.content_blocks import ContentCollection
|
from vbv_lernwelt.media_library.content_blocks import MediaContentCollection
|
||||||
|
|
||||||
|
|
||||||
class MediaLibraryPage(Page):
|
class MediaLibraryPage(Page):
|
||||||
|
|
@ -22,7 +22,6 @@ class MediaLibraryPage(Page):
|
||||||
super(MediaLibraryPage, self).full_clean(*args, **kwargs)
|
super(MediaLibraryPage, self).full_clean(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
# Todo: use wagtail collections for this... Only applicable for documents, since links etc. dont have collections
|
|
||||||
class MediaCategoryPage(Page):
|
class MediaCategoryPage(Page):
|
||||||
"""
|
"""
|
||||||
Handlungsfeld. zB. Fahrzeug
|
Handlungsfeld. zB. Fahrzeug
|
||||||
|
|
@ -33,7 +32,7 @@ class MediaCategoryPage(Page):
|
||||||
description = fields.RichTextField(default='')
|
description = fields.RichTextField(default='')
|
||||||
|
|
||||||
body = fields.StreamField(
|
body = fields.StreamField(
|
||||||
[('content_collection', ContentCollection())],
|
[('content_collection', MediaContentCollection())],
|
||||||
use_json_field=True,
|
use_json_field=True,
|
||||||
null=True
|
null=True
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
import json
|
import uuid
|
||||||
|
|
||||||
import wagtail_factories
|
import wagtail_factories
|
||||||
|
|
||||||
from vbv_lernwelt.media_library.content_blocks import ContentCollection, AnchorBlock, LinkBlock, CrossReferenceBlock
|
from vbv_lernwelt.media_library.content_blocks import MediaContentCollection, AnchorBlock, LinkBlock, \
|
||||||
|
CrossReferenceBlock
|
||||||
from vbv_lernwelt.media_library.models import LibraryDocument, MediaLibraryPage, MediaCategoryPage
|
from vbv_lernwelt.media_library.models import LibraryDocument, MediaLibraryPage, MediaCategoryPage
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -29,7 +30,7 @@ class AnchorBlockFactory(wagtail_factories.StructBlockFactory):
|
||||||
class LinkBlockFactory(wagtail_factories.StructBlockFactory):
|
class LinkBlockFactory(wagtail_factories.StructBlockFactory):
|
||||||
title = 'Interesting link'
|
title = 'Interesting link'
|
||||||
description = 'This link is really interesting...'
|
description = 'This link is really interesting...'
|
||||||
url = 'www.example.com'
|
url = 'https://www.vbv.ch/'
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = LinkBlock
|
model = LinkBlock
|
||||||
|
|
@ -40,7 +41,7 @@ class CrossReferenceBlockFactory(wagtail_factories.StructBlockFactory):
|
||||||
model = CrossReferenceBlock
|
model = CrossReferenceBlock
|
||||||
|
|
||||||
|
|
||||||
class ContentCollectionFactory(wagtail_factories.StructBlockFactory):
|
class MediaContentCollectionFactory(wagtail_factories.StructBlockFactory):
|
||||||
title = 'Links'
|
title = 'Links'
|
||||||
contents = wagtail_factories.StreamFieldFactory({
|
contents = wagtail_factories.StreamFieldFactory({
|
||||||
'Links': LinkBlockFactory,
|
'Links': LinkBlockFactory,
|
||||||
|
|
@ -48,68 +49,52 @@ class ContentCollectionFactory(wagtail_factories.StructBlockFactory):
|
||||||
})
|
})
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ContentCollection
|
model = MediaContentCollection
|
||||||
|
|
||||||
|
|
||||||
class MediaCategoryPageFactory(wagtail_factories.PageFactory):
|
class MediaCategoryPageFactory(wagtail_factories.PageFactory):
|
||||||
title = 'Fahrzeug'
|
title = 'Fahrzeug'
|
||||||
introduction_text = 'Das Auto ist für viele der grösste Stolz! Es birgt aber ...'
|
introduction_text = 'Das Auto ist für viele der grösste Stolz! Es birgt aber ...'
|
||||||
description = 'Das erwartet dich in diesem Handlungsfeld'
|
description = 'Das erwartet dich in diesem Handlungsfeld'
|
||||||
body = wagtail_factories.StreamFieldFactory({'contents': ContentCollectionFactory})
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = MediaCategoryPage
|
model = MediaCategoryPage
|
||||||
|
|
||||||
|
|
||||||
def generate_default_category():
|
def create_media_content_link(link_block=None):
|
||||||
category = MediaCategoryPageFactory()
|
if link_block is None:
|
||||||
category.body = json.dumps(collection_body_dict())
|
link_block = LinkBlockFactory()
|
||||||
category.save()
|
return {
|
||||||
return category
|
"id": str(uuid.uuid4()),
|
||||||
|
|
||||||
|
|
||||||
def generate_default_content_string(
|
|
||||||
block_type='Links',
|
|
||||||
content_idx=0,
|
|
||||||
content_property='url',
|
|
||||||
stream_field_name='contents'):
|
|
||||||
return f'{stream_field_name}__{content_idx}__{block_type}__{content_property}'
|
|
||||||
|
|
||||||
|
|
||||||
def generate_default_content2(**contents_dict):
|
|
||||||
# TODO: hierarchical test, has to be refactored.
|
|
||||||
stream_field_name = 'contents'
|
|
||||||
content_idx = 0
|
|
||||||
block_type = 'Links'
|
|
||||||
content_property = 'url'
|
|
||||||
value = 'iterativ.ch'
|
|
||||||
contents_dict[
|
|
||||||
f'body__content_collection__0__{stream_field_name}__{content_idx}__{block_type}__{content_property}'] = value
|
|
||||||
return contents_dict
|
|
||||||
|
|
||||||
|
|
||||||
def link_dict(link_block=LinkBlockFactory()):
|
|
||||||
d = {
|
|
||||||
"type": "Links",
|
"type": "Links",
|
||||||
"collection_type": ["LearningMedia"],
|
"value": dict(link_block.items())
|
||||||
"value": block_to_dict(link_block)}
|
|
||||||
return d
|
|
||||||
|
|
||||||
|
|
||||||
def document_dict(document=LibraryDocumentFactory()):
|
|
||||||
d = {
|
|
||||||
"type": "Documents",
|
|
||||||
"id": document.id
|
|
||||||
}
|
}
|
||||||
return d
|
|
||||||
|
|
||||||
|
|
||||||
def block_to_dict(block):
|
def create_link_collection(links_dict=None):
|
||||||
return dict(block.items())
|
return {
|
||||||
|
"id": str(uuid.uuid4()),
|
||||||
|
"type": "content_collection",
|
||||||
|
"value": {
|
||||||
|
"title": "Links",
|
||||||
|
"contents": [link_dict for link_dict in links_dict]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def collection_body_dict():
|
def create_document_collection(document_ids=None):
|
||||||
d = [{"type": "content_collection",
|
if document_ids is None:
|
||||||
"value": {"title": "Links", "contents": [link_dict()], }},
|
document_ids = [d.id for d in LibraryDocument.objects.all()]
|
||||||
{"type": "content_collection",
|
|
||||||
"value": {"title": "Lernmedien", "contents": [document_dict()], }}]
|
return {
|
||||||
|
"id": str(uuid.uuid4()),
|
||||||
|
"type": "content_collection",
|
||||||
|
"value": {
|
||||||
|
"title": "Lernmedien",
|
||||||
|
"contents": [{
|
||||||
|
"id": str(uuid.uuid4()),
|
||||||
|
"type": "Documents",
|
||||||
|
"value": doc_id
|
||||||
|
} for doc_id in document_ids]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,12 @@
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from wagtail.core.models import Collection
|
|
||||||
|
|
||||||
from vbv_lernwelt.core.create_default_users import create_default_users
|
from vbv_lernwelt.core.create_default_users import create_default_users
|
||||||
from vbv_lernwelt.core.tests.helpers import create_locales_for_wagtail
|
from vbv_lernwelt.core.tests.helpers import create_locales_for_wagtail
|
||||||
from vbv_lernwelt.media_library.create_default_documents import create_default_collections, create_default_documents
|
from vbv_lernwelt.media_library.models import MediaCategoryPage
|
||||||
from vbv_lernwelt.media_library.models import LibraryDocument, MediaCategoryPage
|
from vbv_lernwelt.media_library.tests.media_library_factories import MediaContentCollectionFactory, MediaCategoryPageFactory, \
|
||||||
from vbv_lernwelt.media_library.tests.media_library_factories import ContentCollectionFactory, MediaCategoryPageFactory, \
|
LinkBlockFactory, generate_default_content2, collection_body_dict
|
||||||
LinkBlockFactory, generate_default_category, generate_default_content2, collection_body_dict
|
|
||||||
|
|
||||||
|
|
||||||
class TestMediaLibraryFactories(TestCase):
|
class TestMediaLibraryFactories(TestCase):
|
||||||
|
|
@ -17,7 +15,7 @@ class TestMediaLibraryFactories(TestCase):
|
||||||
create_locales_for_wagtail()
|
create_locales_for_wagtail()
|
||||||
|
|
||||||
def test_content_collection_factory(self):
|
def test_content_collection_factory(self):
|
||||||
content_collection = ContentCollectionFactory()
|
content_collection = MediaContentCollectionFactory()
|
||||||
self.assertEqual(content_collection.get('title'), 'Links')
|
self.assertEqual(content_collection.get('title'), 'Links')
|
||||||
self.assertEqual(content_collection.get('collection_type'), 'LearningMedia')
|
self.assertEqual(content_collection.get('collection_type'), 'LearningMedia')
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue