Compare commits
74 Commits
feature/lo
...
feature/30
Author | SHA1 | Date | |
---|---|---|---|
c5f6355f19 | |||
dac63e99ba | |||
9f369456d5 | |||
011a58f6b3 | |||
67ce6cb50b | |||
86cc7f23fe | |||
3eaa186b66 | |||
cc59254ba4 | |||
67a6517716 | |||
0f5474c2d3 | |||
17e71b71d4 | |||
b1aa4473e9 | |||
871acd1dce | |||
14ca45d111 | |||
c71fe4cf2d | |||
7359bf5fab | |||
4f0182fc3e | |||
30f259fb4d | |||
53f89caef5 | |||
3855fb28d7 | |||
3a20a60f05 | |||
648264c9fc | |||
a7c8848fd6 | |||
07ea2f164c | |||
b0396f5223 | |||
88bffbef8e | |||
2022a924c4 | |||
d5827b2006 | |||
a9013d9673 | |||
0c38ca9a15 | |||
734d09df90 | |||
e601b9bf6b | |||
6f6bd6376d | |||
dc2a703c39 | |||
063777ece4 | |||
4675e5814f | |||
35e0f912fe | |||
07fe1bc3ca | |||
4a43a4bf37 | |||
61cf148417 | |||
d547ee9db3 | |||
c7b699f615 | |||
86f95a5dd0 | |||
916c4b80f7 | |||
b31dc9fc5f | |||
8f048369bf | |||
f974469996 | |||
dbbd7b0802 | |||
d04e986419 | |||
cbbda88850 | |||
981c440ce3 | |||
c7368f5c44 | |||
abca946883 | |||
20dd880a11 | |||
946c3091c0 | |||
4bacd1a28d | |||
086789aa88 | |||
3c5b447d4c | |||
cfbb876d2c | |||
4e0bfef483 | |||
ccdb49c8ea | |||
90dd281f7c | |||
d9df6e67a8 | |||
cd20e730ba | |||
c613d15e89 | |||
2380398fa9 | |||
4c5b9c7648 | |||
91a3f3171d | |||
4e366d94c9 | |||
773390b09f | |||
ac0121d96e | |||
a0c4f2cad3 | |||
66bd9f648b | |||
485eb7b886 |
10
Readme.md
10
Readme.md
@@ -22,7 +22,7 @@ Right now it depends on the following non-core Python 3 libraries. These can be
|
|||||||
* [django-widget-tweaks](https://github.com/jazzband/django-widget-tweaks) Tweak the form field rendering in templates, not in python-level form definitions.
|
* [django-widget-tweaks](https://github.com/jazzband/django-widget-tweaks) Tweak the form field rendering in templates, not in python-level form definitions.
|
||||||
* [django-taggit](https://github.com/jazzband/django-taggit) A simpler approach to tagging with Django.
|
* [django-taggit](https://github.com/jazzband/django-taggit) A simpler approach to tagging with Django.
|
||||||
|
|
||||||
### Bundled Dependencies
|
### Bundled Dependencies
|
||||||
We also leverage some other great OpenSource projects' code. We bundle those in the distribution to obsolete the need to pull those files from monitored CDNs.
|
We also leverage some other great OpenSource projects' code. We bundle those in the distribution to obsolete the need to pull those files from monitored CDNs.
|
||||||
|
|
||||||
* [OpenLayers](https://openlayers.org/) [6.4.3] OpenLayers makes it easy to put a dynamic map in any web page. It can display map tiles, vector data and markers loaded from any source.
|
* [OpenLayers](https://openlayers.org/) [6.4.3] OpenLayers makes it easy to put a dynamic map in any web page. It can display map tiles, vector data and markers loaded from any source.
|
||||||
@@ -88,7 +88,9 @@ Run `django_lostplaces/managy.py collectstatic` and you should be ready to go.
|
|||||||
|
|
||||||
If you haven't already setup a django instance, see [django's documentation](https://docs.djangoproject.com/en/3.1/topics/install/).
|
If you haven't already setup a django instance, see [django's documentation](https://docs.djangoproject.com/en/3.1/topics/install/).
|
||||||
|
|
||||||
After that, download the desired release (probably the latest one) [from the releases page](https://git.mowoe.com/reverend/lostplaces-backend/releases) and install it using `pip install --user name-of-the-file.tar.gz`
|
Afterwards, either
|
||||||
|
- download the desired release [from the releases page](https://git.mowoe.com/reverend/lostplaces-backend/releases) and install it using `pip install --user name-of-the-file.tar.gz`
|
||||||
|
- or install the latest release using `pip install django-lostplaces`
|
||||||
|
|
||||||
*Note: You can run pip install without the --user flag, which will require root privileges and introduces potential security issues.*
|
*Note: You can run pip install without the --user flag, which will require root privileges and introduces potential security issues.*
|
||||||
|
|
||||||
@@ -144,3 +146,7 @@ Before making the django instance public, you should tweak the config `settings.
|
|||||||
|
|
||||||
|
|
||||||
Run `django_lostplaces/manage.py collectstatic` you should be ready to go.
|
Run `django_lostplaces/manage.py collectstatic` you should be ready to go.
|
||||||
|
|
||||||
|
|
||||||
|
### Contact
|
||||||
|
If you run into any issues, have any questions or If you are interested in this project in general, feel free to get in touch with us via [reverend@reverend2048.de](mailto:reverend@reverend2048.de), we do speak English and German.
|
@@ -1,3 +1,4 @@
|
|||||||
|
exclude
|
||||||
include LICENSE
|
include LICENSE
|
||||||
include Readme.rst
|
include Readme.rst
|
||||||
include Pipfile
|
include Pipfile
|
@@ -9,8 +9,6 @@ from django.contrib.auth.admin import UserAdmin
|
|||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from lostplaces.models import *
|
from lostplaces.models import *
|
||||||
|
|
||||||
from lostplaces.forms import ExplorerCreationForm, ExplorerChangeForm
|
|
||||||
|
|
||||||
# Register your models here.
|
# Register your models here.
|
||||||
|
|
||||||
class VoucherAdmin(admin.ModelAdmin):
|
class VoucherAdmin(admin.ModelAdmin):
|
||||||
|
11
django_lostplaces/lostplaces/common.py
Normal file
11
django_lostplaces/lostplaces/common.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
def get_all_subclasses(cls):
|
||||||
|
'''
|
||||||
|
Gets all subclasses recursively, does not contain
|
||||||
|
abstract classes
|
||||||
|
'''
|
||||||
|
subclass_list = []
|
||||||
|
for subclass in cls.__subclasses__():
|
||||||
|
if not subclass._meta.abstract:
|
||||||
|
subclass_list.append(subclass)
|
||||||
|
subclass_list += get_all_subclasses(subclass)
|
||||||
|
return subclass_list
|
@@ -8,12 +8,12 @@ from django.db import models
|
|||||||
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
|
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from lostplaces.models import Place, PlaceImage, Voucher
|
from lostplaces.models import Place, PlaceImage, Voucher, Explorer
|
||||||
|
|
||||||
class ExplorerCreationForm(UserCreationForm):
|
class SignupVoucherForm(UserCreationForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
fields = ('username', 'email')
|
fields = ('username', 'email', 'first_name', 'last_name')
|
||||||
voucher = forms.CharField(
|
voucher = forms.CharField(
|
||||||
max_length=30,
|
max_length=30,
|
||||||
help_text=_('The Voucher you got from an administrator')
|
help_text=_('The Voucher you got from an administrator')
|
||||||
@@ -35,10 +35,33 @@ class ExplorerCreationForm(UserCreationForm):
|
|||||||
fetched_voucher.delete()
|
fetched_voucher.delete()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
class ExplorerChangeForm(UserChangeForm):
|
class ExplorerUserChangeForm(UserChangeForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
fields = ('username', 'email')
|
fields = [ 'username', 'first_name', 'last_name', 'email' ]
|
||||||
|
''' Hide password hint.'''
|
||||||
|
password = None
|
||||||
|
|
||||||
|
''' Display username, but display it non-editable and not required. '''
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.fields['username'].required = False
|
||||||
|
self.fields['username'].help_text = None
|
||||||
|
self.fields['username'].widget.attrs['disabled'] = 'disabled'
|
||||||
|
|
||||||
|
def clean_username(self):
|
||||||
|
# As shown in the above answer.
|
||||||
|
instance = getattr(self, 'instance', None)
|
||||||
|
if instance:
|
||||||
|
return instance.username
|
||||||
|
else:
|
||||||
|
return self.cleaned_data.get('username', None)
|
||||||
|
|
||||||
|
class ExplorerChangeForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Explorer
|
||||||
|
fields = '__all__'
|
||||||
|
exclude = ['user', 'favorite_places']
|
||||||
|
|
||||||
class PlaceForm(forms.ModelForm):
|
class PlaceForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2020-10-11 21:53+0200\n"
|
"POT-Creation-Date: 2020-12-25 16:04+0100\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: Commander1024 <commander@commander1024.de>\n"
|
"Last-Translator: Commander1024 <commander@commander1024.de>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@@ -30,7 +30,7 @@ msgstr "Ungültiger Voucher"
|
|||||||
msgid "Expired voucher"
|
msgid "Expired voucher"
|
||||||
msgstr "Abgelaufener Voucher"
|
msgstr "Abgelaufener Voucher"
|
||||||
|
|
||||||
#: models/abstract_models.py:29
|
#: models/abstract_models.py:29 templates/explorer/profile.html:31
|
||||||
msgid "Name"
|
msgid "Name"
|
||||||
msgstr "Name"
|
msgstr "Name"
|
||||||
|
|
||||||
@@ -74,6 +74,22 @@ msgstr "Adresse (URL)"
|
|||||||
msgid "link text"
|
msgid "link text"
|
||||||
msgstr "Linktext"
|
msgstr "Linktext"
|
||||||
|
|
||||||
|
#: models/models.py:47
|
||||||
|
msgid "Biography"
|
||||||
|
msgstr "Beschreibung"
|
||||||
|
|
||||||
|
#: models/models.py:48
|
||||||
|
msgid "Describe yourself, your preferences, etc. in a few sentences."
|
||||||
|
msgstr "Beschreibe Dich selbst, Deine Vorlieben, usw. in ein paar Sätzen."
|
||||||
|
|
||||||
|
#: models/models.py:56
|
||||||
|
msgid "Profile image"
|
||||||
|
msgstr "Profilbild"
|
||||||
|
|
||||||
|
#: models/models.py:57
|
||||||
|
msgid "Optional profile image for display in Explorer profile"
|
||||||
|
msgstr "Optionales Profilbild zur Anzeige im Explorerprofil"
|
||||||
|
|
||||||
#: models/place.py:21
|
#: models/place.py:21
|
||||||
msgid "Location"
|
msgid "Location"
|
||||||
msgstr "Ort"
|
msgstr "Ort"
|
||||||
@@ -110,38 +126,92 @@ msgstr "Du wirst in 5 Sekunden weitergeleitet"
|
|||||||
msgid "Go Back"
|
msgid "Go Back"
|
||||||
msgstr "Zurück"
|
msgstr "Zurück"
|
||||||
|
|
||||||
|
#: templates/explorer/profile.html:41
|
||||||
|
msgid "E-mail"
|
||||||
|
msgstr "E-Mail"
|
||||||
|
|
||||||
|
#: templates/explorer/profile.html:52
|
||||||
|
msgid "Joined"
|
||||||
|
msgstr "Beigetreten"
|
||||||
|
|
||||||
|
#: templates/explorer/profile.html:60
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "All Places"
|
||||||
|
msgid "Places"
|
||||||
|
msgstr "Alle Places"
|
||||||
|
|
||||||
|
#: templates/explorer/profile.html:68
|
||||||
|
msgid "Place assets"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/explorer/profile.html:76
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "Edit Explorer profile"
|
||||||
|
msgid "Edit Profile"
|
||||||
|
msgstr "Explorerprofil bearbeiten"
|
||||||
|
|
||||||
|
#: templates/explorer/profile.html:87
|
||||||
|
msgid "Favorite places"
|
||||||
|
msgstr "Favoriten"
|
||||||
|
|
||||||
|
#: templates/explorer/profile.html:103
|
||||||
|
msgid "Places submitted by"
|
||||||
|
msgstr "Places hinzugefügt von"
|
||||||
|
|
||||||
|
#: templates/explorer/profile.html:118
|
||||||
|
msgid "Images submitted by"
|
||||||
|
msgstr "Bilder hinzugefügt von"
|
||||||
|
|
||||||
|
#: templates/explorer/profile.html:140
|
||||||
|
msgid "Photo albums submitted by"
|
||||||
|
msgstr "Fotoalben hinzugefügt von"
|
||||||
|
|
||||||
|
#: templates/explorer/profile_update.html:6
|
||||||
|
#: templates/explorer/profile_update.html:12
|
||||||
|
msgid "Edit Explorer profile"
|
||||||
|
msgstr "Explorerprofil bearbeiten"
|
||||||
|
|
||||||
|
#: templates/explorer/profile_update.html:48
|
||||||
|
#: templates/place/place_update.html:42
|
||||||
|
msgid "Update"
|
||||||
|
msgstr "Aktualisieren"
|
||||||
|
|
||||||
#: templates/global.html:32
|
#: templates/global.html:32
|
||||||
msgid "Logout"
|
msgid "Logout"
|
||||||
msgstr "Ausloggen"
|
msgstr "Ausloggen"
|
||||||
|
|
||||||
#: templates/global.html:34
|
#: templates/global.html:33
|
||||||
|
msgid "Profile"
|
||||||
|
msgstr "Profil"
|
||||||
|
|
||||||
|
#: templates/global.html:35
|
||||||
msgid "Admin"
|
msgid "Admin"
|
||||||
msgstr "Admin"
|
msgstr "Admin"
|
||||||
|
|
||||||
#: templates/global.html:39 templates/registration/login.html:4
|
#: templates/global.html:40 templates/registration/login.html:4
|
||||||
#: templates/registration/login.html:10 templates/registration/login.html:23
|
#: templates/registration/login.html:10 templates/registration/login.html:23
|
||||||
msgid "Login"
|
msgid "Login"
|
||||||
msgstr "Anmelden"
|
msgstr "Anmelden"
|
||||||
|
|
||||||
#: templates/global.html:40 templates/registration/login.html:29
|
#: templates/global.html:41 templates/registration/login.html:29
|
||||||
#: templates/signup.html:6 templates/signup.html:12 templates/signup.html:41
|
#: templates/signup.html:6 templates/signup.html:12 templates/signup.html:49
|
||||||
msgid "Sign up"
|
msgid "Sign up"
|
||||||
msgstr "Registrieren"
|
msgstr "Registrieren"
|
||||||
|
|
||||||
#: templates/global.html:50 templates/home.html:10
|
#: templates/global.html:51 templates/home.html:10
|
||||||
msgid "Home"
|
msgid "Home"
|
||||||
msgstr "Startseite"
|
msgstr "Startseite"
|
||||||
|
|
||||||
#: templates/global.html:51
|
#: templates/global.html:52
|
||||||
msgid "UrBex Codex"
|
msgid "UrBex Codex"
|
||||||
msgstr "UrBex Codex"
|
msgstr "UrBex Codex"
|
||||||
|
|
||||||
#: templates/global.html:56 templates/place/place_create.html:5
|
#: templates/global.html:57 templates/place/place_create.html:5
|
||||||
#: templates/place/place_create.html:10
|
#: templates/place/place_create.html:10
|
||||||
msgid "Create place"
|
msgid "Create place"
|
||||||
msgstr "Place erstellen"
|
msgstr "Place erstellen"
|
||||||
|
|
||||||
#: templates/global.html:57
|
#: templates/global.html:58
|
||||||
msgid "All places"
|
msgid "All places"
|
||||||
msgstr "Alle Places"
|
msgstr "Alle Places"
|
||||||
|
|
||||||
@@ -194,6 +264,14 @@ msgstr "Abschicken"
|
|||||||
msgid "Cancel"
|
msgid "Cancel"
|
||||||
msgstr "Abbrechen"
|
msgstr "Abbrechen"
|
||||||
|
|
||||||
|
#: templates/partials/icons/place_favorite.html:6
|
||||||
|
msgid "Remove from favorites"
|
||||||
|
msgstr "Aus den Favoriten entfernen"
|
||||||
|
|
||||||
|
#: templates/partials/icons/place_favorite.html:10
|
||||||
|
msgid "Save as favorite"
|
||||||
|
msgstr "In den Favoriten speichern"
|
||||||
|
|
||||||
#: templates/partials/nav/footer.html:64
|
#: templates/partials/nav/footer.html:64
|
||||||
msgid "Made by"
|
msgid "Made by"
|
||||||
msgstr "Erstellt von"
|
msgstr "Erstellt von"
|
||||||
@@ -318,10 +396,6 @@ msgstr "Alle Places"
|
|||||||
msgid "Our lost places"
|
msgid "Our lost places"
|
||||||
msgstr "Unsere Lost Places"
|
msgstr "Unsere Lost Places"
|
||||||
|
|
||||||
#: templates/place/place_update.html:42
|
|
||||||
msgid "Update"
|
|
||||||
msgstr "Aktualisieren"
|
|
||||||
|
|
||||||
#: templates/place_image/place_image_create.html:7
|
#: templates/place_image/place_image_create.html:7
|
||||||
msgid "Submit images to a place"
|
msgid "Submit images to a place"
|
||||||
msgstr "Bilder zu einem Place hinzufügen"
|
msgstr "Bilder zu einem Place hinzufügen"
|
||||||
@@ -334,6 +408,16 @@ msgstr "Noch kein Konto?"
|
|||||||
msgid "Please login to proceed"
|
msgid "Please login to proceed"
|
||||||
msgstr "Bitte log Dich ein um fortzufahren"
|
msgstr "Bitte log Dich ein um fortzufahren"
|
||||||
|
|
||||||
|
#: views/explorer_views.py:78
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "Successfully updated place"
|
||||||
|
msgid "Successfully updated Explorer profile"
|
||||||
|
msgstr "Place erfolgreich aktualisiert"
|
||||||
|
|
||||||
|
#: views/explorer_views.py:84 views/place_views.py:105
|
||||||
|
msgid "Please fill in all required fields."
|
||||||
|
msgstr "Bitte füll alle benötigten Felder aus."
|
||||||
|
|
||||||
#: views/place_image_views.py:26
|
#: views/place_image_views.py:26
|
||||||
msgid "Image(s) submitted successfully"
|
msgid "Image(s) submitted successfully"
|
||||||
msgstr "Bild(er) erfolgreich hinzugefügt"
|
msgstr "Bild(er) erfolgreich hinzugefügt"
|
||||||
@@ -358,10 +442,6 @@ msgstr "Du darfst diesen Place nicht bearbeiten"
|
|||||||
msgid "Successfully created place"
|
msgid "Successfully created place"
|
||||||
msgstr "Place erfolgreich erstellt"
|
msgstr "Place erfolgreich erstellt"
|
||||||
|
|
||||||
#: views/place_views.py:105
|
|
||||||
msgid "Please fill in all required fields."
|
|
||||||
msgstr "Bitte füll alle benötigten Felder aus."
|
|
||||||
|
|
||||||
#: views/place_views.py:112
|
#: views/place_views.py:112
|
||||||
msgid "Successfully deleted place"
|
msgid "Successfully deleted place"
|
||||||
msgstr "Place erfolgreich gelöscht"
|
msgstr "Place erfolgreich gelöscht"
|
||||||
@@ -385,3 +465,8 @@ msgstr "Fotoalbum-Link gelöscht"
|
|||||||
#: views/views.py:60
|
#: views/views.py:60
|
||||||
msgid "You are not allowed to edit this photo album link"
|
msgid "You are not allowed to edit this photo album link"
|
||||||
msgstr "Du darfst diesen Fotoalbum-Link nicht bearbeiten"
|
msgstr "Du darfst diesen Fotoalbum-Link nicht bearbeiten"
|
||||||
|
|
||||||
|
#, fuzzy
|
||||||
|
#~| msgid "Filename(s)"
|
||||||
|
#~ msgid "Filename"
|
||||||
|
#~ msgstr "Dateiname(n)"
|
||||||
|
@@ -19,4 +19,4 @@ class Migration(migrations.Migration):
|
|||||||
migrations.DeleteModel(
|
migrations.DeleteModel(
|
||||||
name='Expireable'
|
name='Expireable'
|
||||||
)
|
)
|
||||||
]
|
]
|
@@ -0,0 +1,22 @@
|
|||||||
|
# Generated by Django 3.1.1 on 2020-10-04 19:37
|
||||||
|
# Edited by reverend
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.utils.timezone
|
||||||
|
from django.utils.timezone import utc
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('lostplaces', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='Voucher'
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='Expireable'
|
||||||
|
)
|
||||||
|
]
|
@@ -22,4 +22,4 @@ class Migration(migrations.Migration):
|
|||||||
'abstract': False,
|
'abstract': False,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
]
|
]
|
@@ -6,19 +6,34 @@
|
|||||||
database.
|
database.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
import os
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.db.models.signals import post_save
|
from django.db.models.signals import post_save, pre_save
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from lostplaces.models.abstract_models import Expireable
|
from lostplaces.models.abstract_models import Expireable
|
||||||
|
from lostplaces.models.place import Place
|
||||||
|
|
||||||
|
from easy_thumbnails.fields import ThumbnailerImageField
|
||||||
|
from easy_thumbnails.files import get_thumbnailer
|
||||||
|
|
||||||
|
def generate_profile_image_filename(instance, filename):
|
||||||
|
"""
|
||||||
|
Callback for generating filename for uploaded explorer profile images.
|
||||||
|
Returns filename as: explorer_pk-username.jpg
|
||||||
|
"""
|
||||||
|
|
||||||
|
return 'explorers/' + str(instance.user.pk) + '-' + str(instance.user.username) + '.' + filename.split('.')[-1]
|
||||||
|
|
||||||
class Explorer(models.Model):
|
class Explorer(models.Model):
|
||||||
"""
|
"""
|
||||||
Profile that is linked to the a User.
|
Profile that is linked to the Django user.
|
||||||
Every user has a profile.
|
Every user has a profile.
|
||||||
|
Provides additional attributes for user profile.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
user = models.OneToOneField(
|
user = models.OneToOneField(
|
||||||
@@ -26,10 +41,31 @@ class Explorer(models.Model):
|
|||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
related_name='explorer'
|
related_name='explorer'
|
||||||
)
|
)
|
||||||
|
bio = models.TextField(
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
verbose_name=_('Biography / Description'),
|
||||||
|
help_text=_('Describe yourself, your preferences, etc. in a few sentences.')
|
||||||
|
)
|
||||||
|
profile_image = ThumbnailerImageField(
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
upload_to=generate_profile_image_filename,
|
||||||
|
resize_source=dict(size=(400, 400),
|
||||||
|
sharpen=True),
|
||||||
|
verbose_name=_('Profile image'),
|
||||||
|
help_text=_('Optional profile image for display in Explorer profile')
|
||||||
|
)
|
||||||
|
favorite_places = models.ManyToManyField(
|
||||||
|
Place,
|
||||||
|
related_name='favorite_places',
|
||||||
|
verbose_name='Explorers favorite places',
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.user.username
|
return self.user.username
|
||||||
|
|
||||||
@receiver(post_save, sender=User)
|
@receiver(post_save, sender=User)
|
||||||
def create_user_profile(sender, instance, created, **kwargs):
|
def create_user_profile(sender, instance, created, **kwargs):
|
||||||
if created:
|
if created:
|
||||||
@@ -39,13 +75,34 @@ def create_user_profile(sender, instance, created, **kwargs):
|
|||||||
def save_user_profile(sender, instance, **kwargs):
|
def save_user_profile(sender, instance, **kwargs):
|
||||||
instance.explorer.save()
|
instance.explorer.save()
|
||||||
|
|
||||||
|
@receiver(pre_save, sender=Explorer)
|
||||||
|
def auto_delete_file_on_change(sender, instance, **kwargs):
|
||||||
|
"""
|
||||||
|
Deletes old file from filesystem
|
||||||
|
when corresponding `Explorer` object is updated
|
||||||
|
with new file.
|
||||||
|
"""
|
||||||
|
if not instance.pk:
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
old_file = Explorer.objects.get(pk=instance.pk).profile_image
|
||||||
|
except Explorer.DoesNotExist:
|
||||||
|
return False
|
||||||
|
print("Deleting:", old_file)
|
||||||
|
new_file = instance.profile_image
|
||||||
|
if not old_file == new_file:
|
||||||
|
old_file.delete(save=False)
|
||||||
|
|
||||||
class Voucher(Expireable):
|
class Voucher(Expireable):
|
||||||
"""
|
"""
|
||||||
Vouchers are authorization to created_when = models.DateTimeField(auto_now_add=True)
|
Vouchers are authorization tokens to allow the registration of new users.
|
||||||
expires_when = models.DateTimeField()kens to allow the registration of new users.
|
|
||||||
A voucher has a code, a creation and a deletion date, which are all
|
A voucher has a code, a creation and a deletion date, which are all
|
||||||
positional. Creation date is being set automatically during voucher
|
positional. Creation date is being set automatically during voucher
|
||||||
creation.
|
creation.
|
||||||
|
|
||||||
|
created_when = models.DateTimeField(auto_now_add=True)
|
||||||
|
expires_when = models.DateTimeField()
|
||||||
"""
|
"""
|
||||||
|
|
||||||
code = models.CharField(unique=True, max_length=30)
|
code = models.CharField(unique=True, max_length=30)
|
||||||
@@ -56,4 +113,3 @@ class Voucher(Expireable):
|
|||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "Voucher " + str(self.code)
|
return "Voucher " + str(self.code)
|
||||||
|
|
||||||
|
@@ -49,10 +49,10 @@ class Place(Submittable, Taggable, Mapable):
|
|||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
def generate_image_upload_path(instance, filename):
|
def generate_place_image_filename(instance, filename):
|
||||||
"""
|
"""
|
||||||
Callback for generating path for uploaded images.
|
Callback for generating filename for uploaded place images.
|
||||||
Returns filename as: place_pk-placename{-rnd_string}.jpg
|
Returns filename as: place_pk-placename{-number}.jpg
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return 'places/' + str(instance.place.pk) + '-' + str(instance.place.name) + '.' + filename.split('.')[-1]
|
return 'places/' + str(instance.place.pk) + '-' + str(instance.place.name) + '.' + filename.split('.')[-1]
|
||||||
@@ -72,7 +72,7 @@ class PlaceAsset(Submittable):
|
|||||||
null=True
|
null=True
|
||||||
)
|
)
|
||||||
|
|
||||||
class PlaceImage (Submittable):
|
class PlaceImage(PlaceAsset):
|
||||||
"""
|
"""
|
||||||
PlaceImage defines an image file object that points to a file in uploads/.
|
PlaceImage defines an image file object that points to a file in uploads/.
|
||||||
Intermediate image sizes are generated as defined in THUMBNAIL_ALIASES.
|
Intermediate image sizes are generated as defined in THUMBNAIL_ALIASES.
|
||||||
@@ -84,7 +84,7 @@ class PlaceImage (Submittable):
|
|||||||
verbose_name=_('Description'),
|
verbose_name=_('Description'),
|
||||||
)
|
)
|
||||||
filename = ThumbnailerImageField(
|
filename = ThumbnailerImageField(
|
||||||
upload_to=generate_image_upload_path,
|
upload_to=generate_place_image_filename,
|
||||||
resize_source=dict(size=(2560, 2560),
|
resize_source=dict(size=(2560, 2560),
|
||||||
sharpen=True),
|
sharpen=True),
|
||||||
verbose_name=_('Filename(s)'),
|
verbose_name=_('Filename(s)'),
|
||||||
@@ -104,7 +104,6 @@ class PlaceImage (Submittable):
|
|||||||
|
|
||||||
return 'Image ' + str(self.pk)
|
return 'Image ' + str(self.pk)
|
||||||
|
|
||||||
|
|
||||||
# These two auto-delete files from filesystem when they are unneeded:
|
# These two auto-delete files from filesystem when they are unneeded:
|
||||||
|
|
||||||
@receiver(post_delete, sender=PlaceImage)
|
@receiver(post_delete, sender=PlaceImage)
|
||||||
@@ -118,7 +117,6 @@ def auto_delete_file_on_delete(sender, instance, **kwargs):
|
|||||||
thumbmanager = get_thumbnailer(instance.filename)
|
thumbmanager = get_thumbnailer(instance.filename)
|
||||||
thumbmanager.delete(save=False)
|
thumbmanager.delete(save=False)
|
||||||
|
|
||||||
|
|
||||||
@receiver(pre_save, sender=PlaceImage)
|
@receiver(pre_save, sender=PlaceImage)
|
||||||
def auto_delete_file_on_change(sender, instance, **kwargs):
|
def auto_delete_file_on_change(sender, instance, **kwargs):
|
||||||
"""
|
"""
|
||||||
@@ -137,5 +135,4 @@ def auto_delete_file_on_change(sender, instance, **kwargs):
|
|||||||
# No need to delete thumbnails, as they will be overwritten on regeneration.
|
# No need to delete thumbnails, as they will be overwritten on regeneration.
|
||||||
new_file = instance.filename
|
new_file = instance.filename
|
||||||
if not old_file == new_file:
|
if not old_file == new_file:
|
||||||
if os.path.isfile(old_file.path):
|
old_file.delete(save=False)
|
||||||
os.remove(old_file.path)
|
|
||||||
|
163
django_lostplaces/lostplaces/static/icons/favourite_filled.svg
Normal file
163
django_lostplaces/lostplaces/static/icons/favourite_filled.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 108 KiB |
BIN
django_lostplaces/lostplaces/static/images/missing_image.png
Normal file
BIN
django_lostplaces/lostplaces/static/images/missing_image.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
@@ -862,10 +862,9 @@ body {
|
|||||||
margin: 0 3px; }
|
margin: 0 3px; }
|
||||||
|
|
||||||
.LP-PlaceTeaser {
|
.LP-PlaceTeaser {
|
||||||
width: 280px; }
|
width: 100%; }
|
||||||
.LP-PlaceTeaser .LP-PlaceTeaser__Image {
|
.LP-PlaceTeaser .LP-PlaceTeaser__Image {
|
||||||
height: 165px;
|
width: 100%;
|
||||||
width: 280px;
|
|
||||||
overflow: hidden; }
|
overflow: hidden; }
|
||||||
.LP-PlaceTeaser .LP-PlaceTeaser__Image .LP-Image {
|
.LP-PlaceTeaser .LP-PlaceTeaser__Image .LP-Image {
|
||||||
max-width: unset;
|
max-width: unset;
|
||||||
@@ -873,6 +872,8 @@ body {
|
|||||||
object-fit: cover; }
|
object-fit: cover; }
|
||||||
.LP-PlaceTeaser .LP-PlaceTeaser__Meta {
|
.LP-PlaceTeaser .LP-PlaceTeaser__Meta {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
@@ -883,19 +884,30 @@ body {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
font-size: 0.9rem; }
|
font-size: 1.3rem; }
|
||||||
.LP-PlaceTeaser .LP-PlaceTeaser__Meta .LP-Headline {
|
.LP-PlaceTeaser .LP-PlaceTeaser__Meta .LP-Headline {
|
||||||
font-family: "Montserrat", Helvetica, sans-serif;
|
font-family: "Montserrat", Helvetica, sans-serif;
|
||||||
color: #565656;
|
color: #565656;
|
||||||
font-size: 1rem;
|
font-size: 2rem;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
margin: 0px; }
|
margin: 0px; }
|
||||||
|
.LP-PlaceTeaser .LP-PlaceTeaser__Meta .LP-Icon {
|
||||||
|
height: 2em;
|
||||||
|
width: 2em; }
|
||||||
.LP-PlaceTeaser .LP-PlaceTeaser__Description {
|
.LP-PlaceTeaser .LP-PlaceTeaser__Description {
|
||||||
font-family: "Montserrat", Helvetica, sans-serif;
|
font-family: "Montserrat", Helvetica, sans-serif;
|
||||||
color: #565656;
|
color: #565656;
|
||||||
display: none; }
|
order: 3;
|
||||||
|
flex-basis: 100%; }
|
||||||
|
.LP-PlaceTeaser .LP-PlaceTeaser__Description .LP-Paragraph {
|
||||||
|
font-size: 22px; }
|
||||||
|
|
||||||
@media (min-width: 650px) {
|
@media (min-width: 650px) {
|
||||||
|
.LP-PlaceTeaser {
|
||||||
|
width: 280px; }
|
||||||
|
.LP-PlaceTeaser__Image {
|
||||||
|
width: 280px;
|
||||||
|
height: 165px; }
|
||||||
.LP-PlaceTeaser--extended {
|
.LP-PlaceTeaser--extended {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
@@ -904,12 +916,15 @@ body {
|
|||||||
height: 165px; }
|
height: 165px; }
|
||||||
.LP-PlaceTeaser--extended .LP-PlaceTeaser__Meta {
|
.LP-PlaceTeaser--extended .LP-PlaceTeaser__Meta {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 5px;
|
|
||||||
padding-left: 25px;
|
padding-left: 25px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-wrap: nowrap;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: flex-start; }
|
align-items: flex-start;
|
||||||
|
gap: unset; }
|
||||||
|
.LP-PlaceTeaser--extended .LP-PlaceTeaser__Meta .LP-Headline, .LP-PlaceTeaser--extended .LP-PlaceTeaser__Meta .LP-Paragraph {
|
||||||
|
font-size: unset; }
|
||||||
.LP-PlaceTeaser--extended .LP-PlaceTeaser__Meta .LP-PlaceTeaser__Info .LP-Headline {
|
.LP-PlaceTeaser--extended .LP-PlaceTeaser__Meta .LP-PlaceTeaser__Info .LP-Headline {
|
||||||
font-size: 28px; }
|
font-size: 28px; }
|
||||||
.LP-PlaceTeaser--extended .LP-PlaceTeaser__Meta .LP-PlaceTeaser__Icons {
|
.LP-PlaceTeaser--extended .LP-PlaceTeaser__Meta .LP-PlaceTeaser__Icons {
|
||||||
@@ -917,10 +932,16 @@ body {
|
|||||||
.LP-PlaceTeaser--extended .LP-PlaceTeaser__Meta .LP-PlaceTeaser__Icons ul {
|
.LP-PlaceTeaser--extended .LP-PlaceTeaser__Meta .LP-PlaceTeaser__Icons ul {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0; }
|
margin: 0; }
|
||||||
|
.LP-PlaceTeaser--extended .LP-PlaceTeaser__Meta .LP-Icon {
|
||||||
|
height: 20px;
|
||||||
|
width: 20px; }
|
||||||
.LP-PlaceTeaser--extended .LP-PlaceTeaser__Description {
|
.LP-PlaceTeaser--extended .LP-PlaceTeaser__Description {
|
||||||
display: block;
|
display: block;
|
||||||
max-height: 55px;
|
max-height: 55px;
|
||||||
overflow: hidden; }
|
overflow: hidden;
|
||||||
|
order: unset; }
|
||||||
|
.LP-PlaceTeaser--extended .LP-PlaceTeaser__Description .LP-Paragraph {
|
||||||
|
font-size: unset; }
|
||||||
.LP-PlaceTeaser--extended .LP-PlaceTeaser__Image {
|
.LP-PlaceTeaser--extended .LP-PlaceTeaser__Image {
|
||||||
height: 165px;
|
height: 165px;
|
||||||
width: 280px;
|
width: 280px;
|
||||||
@@ -1006,6 +1027,31 @@ body {
|
|||||||
.LP-MessageList .LP-MessageList__Item {
|
.LP-MessageList .LP-MessageList__Item {
|
||||||
margin: 5px 0; }
|
margin: 5px 0; }
|
||||||
|
|
||||||
|
.LP-UserInfo {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: #D7CEC7;
|
||||||
|
max-width: 400px;
|
||||||
|
width: 100%;
|
||||||
|
min-width: 300px; }
|
||||||
|
.LP-UserInfo__UserName {
|
||||||
|
background-color: #C09F80; }
|
||||||
|
.LP-UserInfo__UserName .LP-Headline {
|
||||||
|
color: black;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
text-align: center;
|
||||||
|
padding: 5px;
|
||||||
|
font-size: 25px; }
|
||||||
|
.LP-UserInfo__Meta {
|
||||||
|
padding: 5px; }
|
||||||
|
.LP-UserInfo__Meta * {
|
||||||
|
font-family: "Montserrat", Helvetica, sans-serif;
|
||||||
|
font-size: 18px; }
|
||||||
|
.LP-UserInfo__Meta .LP-UserInfo__Key {
|
||||||
|
padding-right: 25px;
|
||||||
|
font-weight: bold; }
|
||||||
|
|
||||||
.LP-Header {
|
.LP-Header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -1069,7 +1115,9 @@ body {
|
|||||||
box-shadow: 0 0 2px #565656; }
|
box-shadow: 0 0 2px #565656; }
|
||||||
|
|
||||||
.LP-PlaceList .LP-PlaceList__List {
|
.LP-PlaceList .LP-PlaceList__List {
|
||||||
list-style-type: none; }
|
list-style-type: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0; }
|
||||||
.LP-PlaceList .LP-PlaceList__List .LP-Link .LP-Place:hover {
|
.LP-PlaceList .LP-PlaceList__List .LP-Link .LP-Place:hover {
|
||||||
color: #565656;
|
color: #565656;
|
||||||
background-color: #f9f9f9;
|
background-color: #f9f9f9;
|
||||||
@@ -1080,6 +1128,10 @@ body {
|
|||||||
.LP-PlaceList .LP-PlaceList__List .LP-PlaceList__Item {
|
.LP-PlaceList .LP-PlaceList__List .LP-PlaceList__Item {
|
||||||
max-width: 900px;
|
max-width: 900px;
|
||||||
margin: 18px 0; }
|
margin: 18px 0; }
|
||||||
|
@media (max-width: 750px) {
|
||||||
|
.LP-PlaceList .LP-PlaceList__List .LP-PlaceList__Item {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 35px; } }
|
||||||
|
|
||||||
.LP-PlaceList .LP-Pagination {
|
.LP-PlaceList .LP-Pagination {
|
||||||
margin-top: 50px; }
|
margin-top: 50px; }
|
||||||
@@ -1661,6 +1713,18 @@ body {
|
|||||||
padding: .5em;
|
padding: .5em;
|
||||||
border-radius: 2px; }
|
border-radius: 2px; }
|
||||||
|
|
||||||
|
.LP-UserProfile {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row; }
|
||||||
|
.LP-UserProfile__Bio {
|
||||||
|
flex-grow: 5;
|
||||||
|
flex-shrink: 10;
|
||||||
|
padding-right: 35px; }
|
||||||
|
.LP-UserProfile__Info {
|
||||||
|
flex-grow: 10;
|
||||||
|
flex-shrink: 1;
|
||||||
|
flex-basis: max-content; }
|
||||||
|
|
||||||
.LP-MainContainer {
|
.LP-MainContainer {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
max-width: 1280px; }
|
max-width: 1280px; }
|
||||||
@@ -1673,8 +1737,9 @@ body {
|
|||||||
width: 100%; } }
|
width: 100%; } }
|
||||||
|
|
||||||
.LP-PlaceDetail .LP-PlaceDetail__Image {
|
.LP-PlaceDetail .LP-PlaceDetail__Image {
|
||||||
width: 700px;
|
max-width: 700px;
|
||||||
max-height: 500px;
|
max-height: 500px;
|
||||||
|
width: 50%;
|
||||||
box-shadow: 0 0 10px #565656;
|
box-shadow: 0 0 10px #565656;
|
||||||
box-shadow: 0 0 10px #565656;
|
box-shadow: 0 0 10px #565656;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
@@ -1682,15 +1747,16 @@ body {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
float: right;
|
float: right;
|
||||||
margin-left: 35px;
|
margin-left: 25px;
|
||||||
margin-bottom: 35px;
|
margin-bottom: 25px;
|
||||||
margin-right: 35px;
|
margin-right: 25px;
|
||||||
overflow: hidden; }
|
overflow: hidden; }
|
||||||
|
|
||||||
@media (max-width: 1000px) {
|
@media (max-width: 750px) {
|
||||||
.LP-PlaceDetail .LP-PlaceDetail__Header .LP-PlaceDetail__Image {
|
.LP-PlaceDetail .LP-PlaceDetail__Header .LP-PlaceDetail__Image {
|
||||||
float: none;
|
float: none;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
max-width: unset;
|
||||||
height: auto;
|
height: auto;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
161
django_lostplaces/lostplaces/templates/explorer/profile.html
Normal file
161
django_lostplaces/lostplaces/templates/explorer/profile.html
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
{% extends 'global.html'%}
|
||||||
|
{% load static %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% load svg_icon %}
|
||||||
|
|
||||||
|
{% block maincontent %}
|
||||||
|
|
||||||
|
<div class="LP-UserProfile">
|
||||||
|
<div class="LP-UserProfile__Bio">
|
||||||
|
<h2 class="LP-Headline">Bio</h2>
|
||||||
|
<p class="LP-Paragraph">{{explorer.bio}}</p>
|
||||||
|
</div>
|
||||||
|
<div class="LP-UserProfile__Info">
|
||||||
|
<div class="LP-UserInfo">
|
||||||
|
<div class="LP-UserInfo__UserName">
|
||||||
|
<h1 class="LP-Headline">{{explorer.user.username}}</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="LP-UserInfo__Meta">
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
{% if explorer.profile_image %}
|
||||||
|
<figure class="LP-UserInfo__Image">
|
||||||
|
<img src="{{ explorer.profile_image.url }}" class="LP-Image" />
|
||||||
|
</figure>
|
||||||
|
{% endif %}
|
||||||
|
<tr>
|
||||||
|
{% if explorer.user.first_name %}
|
||||||
|
<td class="LP-UserInfo__Key">
|
||||||
|
<span class="LP-Paragraph">{% trans 'Name' %}</span>
|
||||||
|
</td>
|
||||||
|
<td class="LP-UserInfo__Value">
|
||||||
|
<span class="LP-Paragraph">{{explorer.user.first_name}} {{explorer.user.last_name}}</span>
|
||||||
|
</td>
|
||||||
|
{% endif %}
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
{% if explorer.user.email %}
|
||||||
|
<td class="LP-UserInfo__Key">
|
||||||
|
<span class="LP-Paragraph">{% trans 'E-mail' %}</span>
|
||||||
|
</td>
|
||||||
|
<td class="LP-UserInfo__Value">
|
||||||
|
<span class="LP-Paragraph">
|
||||||
|
<a href="{{explorer.user.email}}" class="LP-Link">{{explorer.user.email}}</a>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
{% endif %}
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="LP-UserInfo__Key">
|
||||||
|
<span class="LP-Paragraph">{% trans 'Joined' %}</span>
|
||||||
|
</td>
|
||||||
|
<td class="LP-UserInfo__Value">
|
||||||
|
<span class="LP-Paragraph">{{explorer.user.date_joined|date:'d.m.Y'}}</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="LP-UserInfo__Key">
|
||||||
|
<span class="LP-Paragraph">{% trans 'Places'%}</span>
|
||||||
|
</td>
|
||||||
|
<td class="LP-UserInfo__Value">
|
||||||
|
<span class="LP-Paragraph">{{place_count}}</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="LP-UserInfo__Key">
|
||||||
|
<span class="LP-Paragraph">{% trans 'Place assets'%}</span>
|
||||||
|
</td>
|
||||||
|
<td class="LP-UserInfo__Value">
|
||||||
|
<span class="LP-Paragraph">{{asset_count}}</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a href="/explorer/update/"><button class="LP-Button LP-Button">{% trans 'Edit Profile' %}</button></a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section class="LP-Section">
|
||||||
|
<div class="LP-PlaceList">
|
||||||
|
<h1 class="LP-Headline">{% trans 'Favorite places' %}</h1>
|
||||||
|
<ul class="LP-PlaceList__List">
|
||||||
|
{% for place in explorer.favorite_places.all %}
|
||||||
|
<li class="LP-PlaceList__Item">
|
||||||
|
{% include 'partials/place_teaser.html' with place=place extended=True %}
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{% include 'partials/nav/pagination.html' %}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="LP-Section">
|
||||||
|
<div class="LP-PlaceList">
|
||||||
|
<h1 class="LP-Headline">{% trans 'Places submitted by' %} {{explorer.user.username}}</h1>
|
||||||
|
<ul class="LP-PlaceList__List">
|
||||||
|
{% for place in place_list %}
|
||||||
|
<li class="LP-PlaceList__Item">
|
||||||
|
{% include 'partials/place_teaser.html' with place=place extended=True %}
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{% include 'partials/nav/pagination.html' %}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="LP-Section">
|
||||||
|
<h1 class="LP-Headline">{% trans 'Images submitted by' %} {{explorer.user.username}}</h1>
|
||||||
|
<div class="LP-ImageGrid">
|
||||||
|
<ul class="LP-ImageGrid__Container">
|
||||||
|
{% for place_image in assets.placeimages.all %}
|
||||||
|
<li class="LP-ImageGrid__Item">
|
||||||
|
<a href="{{ place_image.filename.large.url }}" class="LP-Link">
|
||||||
|
<img class="LP-Image" src="{{ place_image.filename.thumbnail.url }}">
|
||||||
|
</a>
|
||||||
|
{% if user.explorer == place_image.submitted_by%}
|
||||||
|
<span class="LP-ImageGrid__DeleteItem" title="Bild löschen">
|
||||||
|
<a href="{% url 'place_image_delete' pk=place_image.id %}" class="LP-Link">
|
||||||
|
<img class="LP-Icon" src="{% static 'icons/cancel.svg' %}" />
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class=" LP-Section">
|
||||||
|
<h1 class="LP-Headline">{% trans 'Photo albums submitted by' %} {{explorer.user.username}}</h1>
|
||||||
|
<div class="LP-LinkList">
|
||||||
|
<ul class="LP-LinkList__Container">
|
||||||
|
{% for photo_album in assets.photoalbums.all %}
|
||||||
|
<li class="LP-LinkList__Item">
|
||||||
|
<a target="_blank" href="{{photo_album.url}}" class="LP-Link">
|
||||||
|
<span class="LP-Text">{{photo_album.label}}</span>
|
||||||
|
</a>
|
||||||
|
{% if user.explorer == photo_album.submitted_by%}
|
||||||
|
<a href="{% url 'photo_album_delete' pk=photo_album.pk%}" class="LP-Link LP-LinkList__ItemHover" title="Delete Photo Album">
|
||||||
|
<div class="RV-Iconized__Container RV-Iconized__Container--small">
|
||||||
|
{% icon 'trash' className="RV-Iconized__Icon" %}
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{% endblock maincontent %}
|
@@ -0,0 +1,55 @@
|
|||||||
|
{% extends 'global.html'%}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load thumbnail %}
|
||||||
|
{% load widget_tweaks %}
|
||||||
|
|
||||||
|
# {% block title %}{% trans 'Edit Explorer profile' %}{% endblock %}
|
||||||
|
|
||||||
|
{% block maincontent %}
|
||||||
|
|
||||||
|
<form class="LP-Form" method="POST" enctype="multipart/form-data">
|
||||||
|
<fieldset class="LP-Form__Fieldset">
|
||||||
|
<legend class="LP-Form__Legend">{% trans 'Edit Explorer profile' %}</legend>
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="LP-Form__Composition LP-Form__Composition--breakable">
|
||||||
|
<div class="LP-Form__Field">
|
||||||
|
{% include 'partials/form/inputField.html' with field=explorer_user_change_form.username %}
|
||||||
|
</div>
|
||||||
|
<div class="LP-Form__Field">
|
||||||
|
{% include 'partials/form/inputField.html' with field=explorer_user_change_form.email %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="LP-Form__Composition LP-Form__Composition--breakable">
|
||||||
|
<div class="LP-Form__Field">
|
||||||
|
{% include 'partials/form/inputField.html' with field=explorer_user_change_form.first_name %}
|
||||||
|
</div>
|
||||||
|
<div class="LP-Form__Field">
|
||||||
|
{% include 'partials/form/inputField.html' with field=explorer_user_change_form.last_name %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="LP-Form__Composition">
|
||||||
|
<div class="LP-Form__Field">
|
||||||
|
{% include 'partials/form/inputField.html' with field=explorer_change_form.bio %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="LP-Form__Composition">
|
||||||
|
{% if explorer_image %}
|
||||||
|
<div class="LP-Form__Field">
|
||||||
|
<img src="{% thumbnail explorer_image 200x200 %}"/>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="LP-Form__Field">
|
||||||
|
{% include 'partials/form/inputField.html' with field=explorer_change_form.profile_image %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% trans 'Update' as action %}
|
||||||
|
<div class="LP-Form__Composition LP-Form__Composition--buttons">
|
||||||
|
{% include 'partials/form/submit.html' with referrer=request.META.HTTP_REFERER action=action %}
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% endblock maincontent %}
|
@@ -29,9 +29,10 @@
|
|||||||
<span class="LP-Paragraph">
|
<span class="LP-Paragraph">
|
||||||
{% if user.is_authenticated %}
|
{% if user.is_authenticated %}
|
||||||
Hi {{ user.username }}!
|
Hi {{ user.username }}!
|
||||||
<a class="LP-Link" href="{% url 'logout' %}"><span class="LP-Link__Text">{% trans 'Logout' %}</span></a>
|
<a class="LP-Link" href="{% url 'logout' %}"><span class="LP-Link__Text">{% trans 'Logout' %}</span></a> |
|
||||||
|
<a class="LP-Link" href="{% url 'explorer_profile' explorer_id=user.pk%}"><span class="LP-Link__Text">{% trans 'Profile' %}</span></a>
|
||||||
{% if user.is_superuser %}
|
{% if user.is_superuser %}
|
||||||
<a class="LP-Link" href="{% url 'admin:index' %}" target="_blank"><span class="LP-Link__Text">{% trans 'Admin' %}</span></a>
|
| <a class="LP-Link" href="{% url 'admin:index' %}" target="_blank"><span class="LP-Link__Text">{% trans 'Admin' %}</span></a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% else %}
|
{% else %}
|
||||||
|
@@ -21,30 +21,7 @@
|
|||||||
<ul class="LP-PlaceGrid__Grid">
|
<ul class="LP-PlaceGrid__Grid">
|
||||||
{% for place in place_list %}
|
{% for place in place_list %}
|
||||||
<li class="LP-PlaceGrid__Item">
|
<li class="LP-PlaceGrid__Item">
|
||||||
<a href="{% url 'place_detail' pk=place.pk %}" class="LP-Link">
|
{% include 'partials/place_teaser.html' with place=place%}
|
||||||
<article class="LP-PlaceTeaser">
|
|
||||||
<div class="LP-PlaceTeaser__Image">
|
|
||||||
<img class="LP-Image" src="{{ place.placeimages.first.filename.thumbnail.url}}" />
|
|
||||||
</div>
|
|
||||||
<div class="LP-PlaceTeaser__Meta">
|
|
||||||
<div class="LP-PlaceTeaser__Info">
|
|
||||||
<span class="LP-PlaceTeaser__Title">
|
|
||||||
<h1 class="LP-Headline LP-Headline--teaser">{{place.name|truncatechars:19}}</h1>
|
|
||||||
</span>
|
|
||||||
<span class="LP-PlaceTeaser__Detail">
|
|
||||||
<p class="LP-Paragraph">{{place.location|truncatechars:25}}</p>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="LP-PlaceTeaser__Icons">
|
|
||||||
<ul class="LP-Icon__List">
|
|
||||||
<li class="LP-Icon__Item"><img class="LP-Icon" src="{% static '/icons/favourite.svg' %}" /></li>
|
|
||||||
<li class="LP-Icon__Item"><img class="LP-Icon" src="{% static '/icons/location.svg' %}" /></li>
|
|
||||||
<li class="LP-Icon__Item"><img class="LP-Icon" src="{% static '/icons/flag.svg' %}" /></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
</a>
|
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
@@ -0,0 +1,14 @@
|
|||||||
|
{%load static %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% if request.user %}
|
||||||
|
{% if place in request.user.explorer.favorite_places.all %}
|
||||||
|
<a href="{% url 'place_unfavorite' place_id=place.id %}" class="LP-Link" title="{% trans 'Remove from favorites' %}">
|
||||||
|
<img class="LP-Icon" src="{% static '/icons/favourite_filled.svg' %}" />
|
||||||
|
</a>
|
||||||
|
{%else%}
|
||||||
|
<a href="{% url 'place_favorite' place_id=place.id %}" class="LP-Link" title="{% trans 'Save as favorite' %}">
|
||||||
|
<img class="LP-Icon" src="{% static '/icons/favourite.svg' %}" />
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
@@ -0,0 +1,39 @@
|
|||||||
|
{%load static %}
|
||||||
|
|
||||||
|
<article class="LP-PlaceTeaser {% if extended %} LP-PlaceTeaser--extended{% endif %}">
|
||||||
|
<a href="{% url 'place_detail' pk=place.pk %}" class="LP-Link">
|
||||||
|
<div class="LP-PlaceTeaser__Image">
|
||||||
|
{% if place.placeimages.all|length > 0 %}
|
||||||
|
<img class="LP-Image" src="{{ place.placeimages.first.filename.thumbnail.url}}" />
|
||||||
|
{% else %}
|
||||||
|
<img class="LP-Image" src="{% static 'images/missing_image.png' %}" />
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<div class="LP-PlaceTeaser__Meta">
|
||||||
|
<div class="LP-PlaceTeaser__Info">
|
||||||
|
<span class="LP-PlaceTeaser__Title">
|
||||||
|
<h1 class="LP-Headline LP-Headline--teaser">{{place.name|truncatechars:19}}</h1>
|
||||||
|
</span>
|
||||||
|
<span class="LP-PlaceTeaser__Detail">
|
||||||
|
<p class="LP-Paragraph">{{place.location|truncatechars:25}}</p>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="LP-PlaceTeaser__Description">
|
||||||
|
<p class="LP-Paragraph">
|
||||||
|
{% if place.description|length > 210 %}
|
||||||
|
{{place.description|truncatechars:210|truncatewords:-1}}
|
||||||
|
{% else %}
|
||||||
|
{{place.description}}
|
||||||
|
{% endif %}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="LP-PlaceTeaser__Icons">
|
||||||
|
<ul class="LP-Icon__List">
|
||||||
|
<li class="LP-Icon__Item">{% include 'partials/icons/place_favorite.html' with place=place%}</li>
|
||||||
|
<li class="LP-Icon__Item"><img class="LP-Icon" src="{% static '/icons/location.svg' %}" /></li>
|
||||||
|
<li class="LP-Icon__Item"><img class="LP-Icon" src="{% static '/icons/flag.svg' %}" /></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
@@ -23,7 +23,7 @@
|
|||||||
<article class="LP-PlaceDetail">
|
<article class="LP-PlaceDetail">
|
||||||
|
|
||||||
<header class="LP-PlaceDetail__Header">
|
<header class="LP-PlaceDetail__Header">
|
||||||
<h1 class="LP-Headline">{{ place.name }}</h1>
|
<h1 class="LP-Headline">{{ place.name }} {% include 'partials/icons/place_favorite.html' %}</h1>
|
||||||
{% if place.placeimages.first.filename.hero.url %}
|
{% if place.placeimages.first.filename.hero.url %}
|
||||||
<figure class="LP-PlaceDetail__Image">
|
<figure class="LP-PlaceDetail__Image">
|
||||||
<img src="{{ place.placeimages.first.filename.hero.url }}" class="LP-Image" />
|
<img src="{{ place.placeimages.first.filename.hero.url }}" class="LP-Image" />
|
||||||
|
@@ -17,39 +17,7 @@
|
|||||||
<ul class="LP-PlaceList__List">
|
<ul class="LP-PlaceList__List">
|
||||||
{% for place in place_list %}
|
{% for place in place_list %}
|
||||||
<li class="LP-PlaceList__Item">
|
<li class="LP-PlaceList__Item">
|
||||||
<a href="{% url 'place_detail' pk=place.pk %}" class="LP-Link">
|
{% include 'partials/place_teaser.html' with place=place extended=True %}
|
||||||
<article class="LP-PlaceTeaser LP-PlaceTeaser--extended">
|
|
||||||
<div class="LP-PlaceTeaser__Image">
|
|
||||||
<img class="LP-Image" src="{{ place.placeimages.first.filename.thumbnail.url }}" />
|
|
||||||
</div>
|
|
||||||
<div class="LP-PlaceTeaser__Meta">
|
|
||||||
<div class="LP-PlaceTeaser__Info">
|
|
||||||
<span class="LP-PlaceTeaser__Title">
|
|
||||||
<h2 class="LP-Headline LP-Headline--teaser">{{place.name}}</h2>
|
|
||||||
</span>
|
|
||||||
<span class="LP-PlaceTeaser__Detail">
|
|
||||||
<p class="LP-Paragraph">{{place.location}}</p>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="LP-PlaceTeaser__Description">
|
|
||||||
<p class="LP-Paragraph">
|
|
||||||
{% if place.description|length > 210 %}
|
|
||||||
{{place.description|truncatechars:210|truncatewords:-1}}
|
|
||||||
{% else %}
|
|
||||||
{{place.description}}
|
|
||||||
{% endif %}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="LP-PlaceTeaser__Icons">
|
|
||||||
<ul class="LP-Icon__List">
|
|
||||||
<li class="LP-Icon__Item"><img class="LP-Icon" src="{% static '/icons/favourite.svg' %}" /></li>
|
|
||||||
<li class="LP-Icon__Item"><img class="LP-Icon" src="{% static '/icons/location.svg' %}" /></li>
|
|
||||||
<li class="LP-Icon__Item"><img class="LP-Icon" src="{% static '/icons/flag.svg' %}" /></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
</a>
|
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
@@ -19,6 +19,14 @@
|
|||||||
{% include 'partials/form/inputField.html' with field=form.email %}
|
{% include 'partials/form/inputField.html' with field=form.email %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="LP-Form__Composition LP-Form__Composition--breakable">
|
||||||
|
<div class="LP-Form__Field">
|
||||||
|
{% include 'partials/form/inputField.html' with field=form.first_name %}
|
||||||
|
</div>
|
||||||
|
<div class="LP-Form__Field">
|
||||||
|
{% include 'partials/form/inputField.html' with field=form.last_name %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="LP-Form__Composition">
|
<div class="LP-Form__Composition">
|
||||||
<div class="LP-Form__Field">
|
<div class="LP-Form__Field">
|
||||||
|
@@ -4,15 +4,15 @@ from django import forms
|
|||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
from lostplaces.tests.forms import FormTestCase
|
from lostplaces.tests.forms import FormTestCase
|
||||||
from lostplaces.forms import ExplorerCreationForm
|
from lostplaces.forms import SignupVoucherForm
|
||||||
from lostplaces.models.models import Voucher
|
from lostplaces.models.models import Voucher
|
||||||
|
|
||||||
class ExplorerCreationFormTestCase(FormTestCase):
|
class SignupVoucherFormTestCase(FormTestCase):
|
||||||
"""
|
"""
|
||||||
This test case only tests for the voucher since all other aspects don't realy matter
|
This test case only tests for the voucher since all other aspects don't realy matter
|
||||||
to this project and are already tested by django
|
to this project and are already tested by django
|
||||||
"""
|
"""
|
||||||
form = ExplorerCreationForm
|
form = SignupVoucherForm
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
@@ -37,7 +37,7 @@ class ExplorerCreationFormTestCase(FormTestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def test_validation_valid(self):
|
def test_validation_valid(self):
|
||||||
form = ExplorerCreationForm(self.post_data)
|
form = SignupVoucherForm(self.post_data)
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
form.is_valid(),
|
form.is_valid(),
|
||||||
msg='Expecting the %s to validate' % (
|
msg='Expecting the %s to validate' % (
|
||||||
@@ -49,7 +49,7 @@ class ExplorerCreationFormTestCase(FormTestCase):
|
|||||||
self.post_data = {
|
self.post_data = {
|
||||||
'voucher': 'Imanotacode123'
|
'voucher': 'Imanotacode123'
|
||||||
}
|
}
|
||||||
form = ExplorerCreationForm(self.post_data)
|
form = SignupVoucherForm(self.post_data)
|
||||||
self.assertFalse(
|
self.assertFalse(
|
||||||
form.is_valid(),
|
form.is_valid(),
|
||||||
msg='Expecting the %s to not validate' % (
|
msg='Expecting the %s to not validate' % (
|
||||||
|
@@ -11,11 +11,15 @@ from lostplaces.views import (
|
|||||||
PlaceDeleteView,
|
PlaceDeleteView,
|
||||||
PlaceTagDeleteView,
|
PlaceTagDeleteView,
|
||||||
PlaceTagSubmitView,
|
PlaceTagSubmitView,
|
||||||
|
PlaceFavoriteView,
|
||||||
|
PlaceUnfavoriteView,
|
||||||
PhotoAlbumCreateView,
|
PhotoAlbumCreateView,
|
||||||
PhotoAlbumDeleteView,
|
PhotoAlbumDeleteView,
|
||||||
PlaceImageCreateView,
|
PlaceImageCreateView,
|
||||||
PlaceImageDeleteView,
|
PlaceImageDeleteView,
|
||||||
FlatView
|
FlatView,
|
||||||
|
ExplorerProfileView,
|
||||||
|
ExplorerProfileUpdateView
|
||||||
)
|
)
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
@@ -33,5 +37,11 @@ urlpatterns = [
|
|||||||
|
|
||||||
# POST-only URLs for tag submission
|
# POST-only URLs for tag submission
|
||||||
path('place/tag/<int:tagged_id>', PlaceTagSubmitView.as_view(), name='place_tag_submit'),
|
path('place/tag/<int:tagged_id>', PlaceTagSubmitView.as_view(), name='place_tag_submit'),
|
||||||
path('place/tag/delete/<int:tagged_id>/<int:tag_id>', PlaceTagDeleteView.as_view(), name='place_tag_delete')
|
path('place/tag/delete/<int:tagged_id>/<int:tag_id>', PlaceTagDeleteView.as_view(), name='place_tag_delete'),
|
||||||
|
|
||||||
|
path('explorer/<int:explorer_id>/', ExplorerProfileView.as_view(), name='explorer_profile'),
|
||||||
|
path('explorer/update/', ExplorerProfileUpdateView.as_view(), name='explorer_profile_update'),
|
||||||
|
|
||||||
|
path('explorer/favorite/<int:place_id>/', PlaceFavoriteView.as_view(), name='place_favorite'),
|
||||||
|
path('explorer/unfavorite/<int:place_id>/', PlaceUnfavoriteView.as_view(), name='place_unfavorite')
|
||||||
]
|
]
|
||||||
|
@@ -4,4 +4,5 @@
|
|||||||
from lostplaces.views.base_views import *
|
from lostplaces.views.base_views import *
|
||||||
from lostplaces.views.views import *
|
from lostplaces.views.views import *
|
||||||
from lostplaces.views.place_views import *
|
from lostplaces.views.place_views import *
|
||||||
from lostplaces.views.place_image_views import *
|
from lostplaces.views.place_image_views import *
|
||||||
|
from lostplaces.views.explorer_views import *
|
87
django_lostplaces/lostplaces/views/explorer_views.py
Normal file
87
django_lostplaces/lostplaces/views/explorer_views.py
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from django.views import View
|
||||||
|
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from django.shortcuts import render, redirect, get_object_or_404
|
||||||
|
from django.urls import reverse_lazy
|
||||||
|
from django.contrib import messages
|
||||||
|
|
||||||
|
from lostplaces.common import get_all_subclasses
|
||||||
|
from lostplaces.views.base_views import IsAuthenticatedMixin
|
||||||
|
from lostplaces.models.models import Explorer
|
||||||
|
from lostplaces.models.place import Place, PlaceAsset
|
||||||
|
from lostplaces.forms import ExplorerChangeForm, ExplorerUserChangeForm
|
||||||
|
|
||||||
|
class ExplorerProfileView(IsAuthenticatedMixin, View):
|
||||||
|
def get(self, request, explorer_id):
|
||||||
|
explorer = get_object_or_404(Explorer, pk=explorer_id)
|
||||||
|
place_list = Place.objects.filter(submitted_by=explorer)
|
||||||
|
place_count = place_list.count()
|
||||||
|
|
||||||
|
context={
|
||||||
|
'explorer': explorer,
|
||||||
|
'place_count': place_count,
|
||||||
|
'place_list': place_list,
|
||||||
|
'assets': {}
|
||||||
|
}
|
||||||
|
|
||||||
|
asset_count = 0
|
||||||
|
for subclass in get_all_subclasses(PlaceAsset): # kinda ugly, but PlaceAsset cannot be abstract according to django ORM
|
||||||
|
objects = subclass.objects.filter(submitted_by=explorer)
|
||||||
|
context['assets'][subclass.__name__.lower()+'s'] = objects
|
||||||
|
asset_count += objects.count()
|
||||||
|
|
||||||
|
context['asset_count'] = asset_count
|
||||||
|
|
||||||
|
return render(
|
||||||
|
request=request,
|
||||||
|
template_name='explorer/profile.html',
|
||||||
|
context=context
|
||||||
|
)
|
||||||
|
|
||||||
|
class ExplorerProfileUpdateView(IsAuthenticatedMixin, View):
|
||||||
|
success_message = ''
|
||||||
|
permission_denied_message = ''
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
context = {
|
||||||
|
'explorer_user_change_form': ExplorerUserChangeForm(instance=request.user),
|
||||||
|
'explorer_change_form': ExplorerChangeForm(instance=request.user.explorer)
|
||||||
|
}
|
||||||
|
if request.user.explorer.profile_image:
|
||||||
|
context['explorer_image'] = request.user.explorer.profile_image
|
||||||
|
return render(request, 'explorer/profile_update.html', context)
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
print(request.POST)
|
||||||
|
explorer_user_change_form = ExplorerUserChangeForm(
|
||||||
|
request.POST,
|
||||||
|
instance=request.user
|
||||||
|
)
|
||||||
|
explorer_change_form = ExplorerChangeForm(
|
||||||
|
request.POST,
|
||||||
|
request.FILES,
|
||||||
|
instance=request.user.explorer
|
||||||
|
)
|
||||||
|
|
||||||
|
if explorer_change_form.is_valid() and explorer_user_change_form.is_valid():
|
||||||
|
explorer_user_change_form.save()
|
||||||
|
explorer_change_form.save()
|
||||||
|
|
||||||
|
print(explorer_change_form.cleaned_data)
|
||||||
|
|
||||||
|
messages.success(
|
||||||
|
self.request,
|
||||||
|
_('Successfully updated Explorer profile')
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# Usually the browser should have checked the form before sending.
|
||||||
|
messages.error(
|
||||||
|
self.request,
|
||||||
|
_('Please fill in all required fields.')
|
||||||
|
)
|
||||||
|
return redirect(reverse_lazy('explorer_profile_update'))
|
||||||
|
|
@@ -119,3 +119,36 @@ class PlaceDeleteView(IsAuthenticatedMixin, IsPlaceSubmitterMixin, DeleteView):
|
|||||||
|
|
||||||
def get_place(self):
|
def get_place(self):
|
||||||
return self.get_object()
|
return self.get_object()
|
||||||
|
|
||||||
|
class PlaceFavoriteView(IsAuthenticatedMixin, View):
|
||||||
|
|
||||||
|
def get(self, request, place_id):
|
||||||
|
place = get_object_or_404(Place, id=place_id)
|
||||||
|
if request.user is not None:
|
||||||
|
request.user.explorer.favorite_places.add(place)
|
||||||
|
request.user.explorer.save()
|
||||||
|
|
||||||
|
referer = request.META.get('HTTP_REFERER')
|
||||||
|
if referer is not None:
|
||||||
|
return redirect(referer)
|
||||||
|
else:
|
||||||
|
return redirect(
|
||||||
|
reverse_lazy('place_detail', kwargs={'pk': place.pk})
|
||||||
|
)
|
||||||
|
|
||||||
|
class PlaceUnfavoriteView(IsAuthenticatedMixin, View):
|
||||||
|
|
||||||
|
def get(self, request, place_id):
|
||||||
|
place = get_object_or_404(Place, id=place_id)
|
||||||
|
if request.user is not None:
|
||||||
|
request.user.explorer.favorite_places.remove(place)
|
||||||
|
request.user.explorer.save()
|
||||||
|
|
||||||
|
referer = request.META.get('HTTP_REFERER')
|
||||||
|
if referer is not None:
|
||||||
|
return redirect(referer)
|
||||||
|
else:
|
||||||
|
return redirect(
|
||||||
|
reverse_lazy('place_detail', kwargs={'pk': place.pk})
|
||||||
|
)
|
||||||
|
|
@@ -11,7 +11,7 @@ from django.shortcuts import render, redirect, get_object_or_404
|
|||||||
from django.http import HttpResponseForbidden
|
from django.http import HttpResponseForbidden
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from lostplaces.forms import ExplorerCreationForm, TagSubmitForm
|
from lostplaces.forms import SignupVoucherForm, TagSubmitForm
|
||||||
from lostplaces.models import Place, PhotoAlbum
|
from lostplaces.models import Place, PhotoAlbum
|
||||||
from lostplaces.views.base_views import IsAuthenticatedMixin
|
from lostplaces.views.base_views import IsAuthenticatedMixin
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ from lostplaces.views.base_views import (
|
|||||||
from taggit.models import Tag
|
from taggit.models import Tag
|
||||||
|
|
||||||
class SignUpView(SuccessMessageMixin, CreateView):
|
class SignUpView(SuccessMessageMixin, CreateView):
|
||||||
form_class = ExplorerCreationForm
|
form_class = SignupVoucherForm
|
||||||
success_url = reverse_lazy('login')
|
success_url = reverse_lazy('login')
|
||||||
template_name = 'signup.html'
|
template_name = 'signup.html'
|
||||||
success_message = _('User created')
|
success_message = _('User created')
|
||||||
|
@@ -1,8 +0,0 @@
|
|||||||
# Config options for coverage
|
|
||||||
# Docs: https://coverage.readthedocs.io/en/latest/config.html
|
|
||||||
|
|
||||||
[coverage:run]
|
|
||||||
source = .
|
|
||||||
|
|
||||||
[coverage:report]
|
|
||||||
show_missing = True
|
|
9
setup.py → django_lostplaces/setup.py
Normal file → Executable file
9
setup.py → django_lostplaces/setup.py
Normal file → Executable file
@@ -1,9 +1,9 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from setuptools import setup, find_packages
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
with open('Readme.md') as f:
|
with open('../Readme.md') as f:
|
||||||
readme = f.read()
|
readme = f.read()
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
@@ -13,7 +13,7 @@ setup(
|
|||||||
author='Reverend',
|
author='Reverend',
|
||||||
author_email='reverend@reverend2048.de',
|
author_email='reverend@reverend2048.de',
|
||||||
url='https://git.mowoe.com/reverend/lostplaces-backend',
|
url='https://git.mowoe.com/reverend/lostplaces-backend',
|
||||||
packages=find_packages(exclude=['lostplaces']),
|
packages=find_packages(exclude=['django_lostplaces']),
|
||||||
long_description=readme,
|
long_description=readme,
|
||||||
long_description_content_type='text/markdown',
|
long_description_content_type='text/markdown',
|
||||||
classifiers=[
|
classifiers=[
|
||||||
@@ -31,5 +31,6 @@ setup(
|
|||||||
'django-widget-tweaks',
|
'django-widget-tweaks',
|
||||||
'django-taggit'
|
'django-taggit'
|
||||||
],
|
],
|
||||||
include_package_data=True
|
include_package_data=True,
|
||||||
|
license='MIT'
|
||||||
)
|
)
|
Reference in New Issue
Block a user