sandwitches 2.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. sandwitches/__init__.py +6 -0
  2. sandwitches/admin.py +69 -0
  3. sandwitches/api.py +207 -0
  4. sandwitches/asgi.py +16 -0
  5. sandwitches/feeds.py +23 -0
  6. sandwitches/forms.py +196 -0
  7. sandwitches/locale/nl/LC_MESSAGES/django.mo +0 -0
  8. sandwitches/locale/nl/LC_MESSAGES/django.po +1010 -0
  9. sandwitches/migrations/0001_initial.py +328 -0
  10. sandwitches/migrations/0002_historicalrecipe_servings_recipe_servings.py +27 -0
  11. sandwitches/migrations/0003_setting.py +35 -0
  12. sandwitches/migrations/0004_alter_setting_ai_api_key_and_more.py +37 -0
  13. sandwitches/migrations/0005_rating_comment.py +17 -0
  14. sandwitches/migrations/0006_historicalrecipe_is_highlighted_and_more.py +22 -0
  15. sandwitches/migrations/__init__.py +0 -0
  16. sandwitches/models.py +218 -0
  17. sandwitches/settings.py +220 -0
  18. sandwitches/storage.py +114 -0
  19. sandwitches/tasks.py +115 -0
  20. sandwitches/templates/admin/admin_base.html +118 -0
  21. sandwitches/templates/admin/confirm_delete.html +23 -0
  22. sandwitches/templates/admin/dashboard.html +262 -0
  23. sandwitches/templates/admin/rating_list.html +38 -0
  24. sandwitches/templates/admin/recipe_form.html +184 -0
  25. sandwitches/templates/admin/recipe_list.html +64 -0
  26. sandwitches/templates/admin/tag_form.html +30 -0
  27. sandwitches/templates/admin/tag_list.html +37 -0
  28. sandwitches/templates/admin/task_detail.html +91 -0
  29. sandwitches/templates/admin/task_list.html +41 -0
  30. sandwitches/templates/admin/user_form.html +37 -0
  31. sandwitches/templates/admin/user_list.html +60 -0
  32. sandwitches/templates/base.html +94 -0
  33. sandwitches/templates/base_beer.html +57 -0
  34. sandwitches/templates/components/carousel_scripts.html +59 -0
  35. sandwitches/templates/components/favorites_search_form.html +85 -0
  36. sandwitches/templates/components/footer.html +14 -0
  37. sandwitches/templates/components/ingredients_scripts.html +50 -0
  38. sandwitches/templates/components/ingredients_section.html +11 -0
  39. sandwitches/templates/components/instructions_section.html +9 -0
  40. sandwitches/templates/components/language_dialog.html +26 -0
  41. sandwitches/templates/components/navbar.html +27 -0
  42. sandwitches/templates/components/rating_section.html +66 -0
  43. sandwitches/templates/components/recipe_header.html +32 -0
  44. sandwitches/templates/components/search_form.html +106 -0
  45. sandwitches/templates/components/search_scripts.html +98 -0
  46. sandwitches/templates/components/side_menu.html +35 -0
  47. sandwitches/templates/components/user_menu.html +10 -0
  48. sandwitches/templates/detail.html +178 -0
  49. sandwitches/templates/favorites.html +42 -0
  50. sandwitches/templates/index.html +76 -0
  51. sandwitches/templates/login.html +57 -0
  52. sandwitches/templates/partials/recipe_list.html +87 -0
  53. sandwitches/templates/recipe_form.html +119 -0
  54. sandwitches/templates/setup.html +105 -0
  55. sandwitches/templates/signup.html +133 -0
  56. sandwitches/templatetags/__init__.py +0 -0
  57. sandwitches/templatetags/custom_filters.py +15 -0
  58. sandwitches/templatetags/markdown_extras.py +17 -0
  59. sandwitches/urls.py +109 -0
  60. sandwitches/utils.py +222 -0
  61. sandwitches/views.py +647 -0
  62. sandwitches/wsgi.py +16 -0
  63. sandwitches-2.2.0.dist-info/METADATA +104 -0
  64. sandwitches-2.2.0.dist-info/RECORD +65 -0
  65. sandwitches-2.2.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,76 @@
1
+ {% extends "base_beer.html" %}
2
+ {% load i18n static %}
3
+
4
+ {% block title %}Sandwitches{% endblock %}
5
+
6
+ {% block extra_head %}
7
+ {{ block.super }}
8
+ <style>
9
+ @media only screen and (min-width: 992px) {
10
+ main.container {
11
+ padding-left: 10%;
12
+ padding-right: 10%;
13
+ }
14
+ }
15
+ </style>
16
+ {% endblock %}
17
+
18
+ {% block content %}
19
+ <div class="large-space"></div>
20
+
21
+ <article class="round secondary s12 m12 l12">
22
+ <div class="row align-center">
23
+ <div class="max">
24
+ <h4 class="upper">{% trans 'Sandwitches' %}</h4>
25
+ <p class="large-text">{% trans 'Sandwiches so good, they haunt you!' %}</p>
26
+ </div>
27
+ </div>
28
+ </article>
29
+
30
+ <div class="space"></div>
31
+
32
+ {% include "components/search_form.html" %}
33
+
34
+ {% if highlighted_recipes %}
35
+ <div class="large-space"></div>
36
+ <h4 class="upper center-align">{% trans 'Highlights' %}</h4>
37
+ <div class="small-space"></div>
38
+ <div class="relative">
39
+ <div class="row scroll snap no-scrollbar" id="highlighted-carousel" style="gap: 20px; flex-wrap: nowrap;">
40
+ {% for recipe in highlighted_recipes %}
41
+ <a href="{{ recipe.get_absolute_url }}">
42
+ <article class="round no-padding elevate" style="min-width: 300px; width: 300px; flex-shrink: 0; scroll-snap-align: start;">
43
+ {% if recipe.image %}
44
+ <img src="{{ recipe.image_medium.url }}" class="responsive top-round" alt="{{ recipe.title }}" style="height: 200px; object-fit: cover;">
45
+ {% else %}
46
+ <div class="primary medium-height top-round middle-align center-align" style="height:200px;">
47
+ <i class="extra">lunch_dining</i>
48
+ </div>
49
+ {% endif %}
50
+ <div class="padding">
51
+ <h5>{{ recipe.title }}</h5>
52
+ </div>
53
+ </article>
54
+ </a>
55
+ {% endfor %}
56
+ </div>
57
+
58
+ <button class="circle surface absolute left middle-align elevate" onclick="scrollCarousel(-1)" style="top: 50%; transform: translateY(-50%); left: -20px; z-index: 10;">
59
+ <i>chevron_left</i>
60
+ </button>
61
+ <button class="circle surface absolute right middle-align elevate" onclick="scrollCarousel(1)" style="top: 50%; transform: translateY(-50%); right: -20px; z-index: 10;">
62
+ <i>chevron_right</i>
63
+ </button>
64
+ </div>
65
+ {% endif %}
66
+
67
+ <div class="large-space"></div>
68
+
69
+ {% include "partials/recipe_list.html" %}
70
+
71
+ {% endblock %}
72
+
73
+ {% block page_scripts %}
74
+ {% include "components/search_scripts.html" %}
75
+ {% include "components/carousel_scripts.html" %}
76
+ {% endblock %}
@@ -0,0 +1,57 @@
1
+ {% extends "base_beer.html" %}
2
+ {% load static i18n %}
3
+ {% block title %}{% trans "Login" %}{% endblock %}
4
+
5
+ {% block content %}
6
+ <div class="large-space"></div>
7
+
8
+ <div class="grid">
9
+ <div class="s12 m10 l8 xl6 middle-align center-align" style="margin: 0 auto;">
10
+ <article class="round elevate">
11
+ <div class="padding">
12
+ <h4 class="center-align primary-text">{% trans "Login to your account" %}</h4>
13
+ <p class="center-align">{% trans "Welcome back!" %}</p>
14
+ </div>
15
+
16
+ <form method="post" novalidate>
17
+ {% csrf_token %}
18
+
19
+ {% if form.non_field_errors %}
20
+ <div class="padding error surface round mb">
21
+ {% for error in form.non_field_errors %}
22
+ <div class="row align-center">
23
+ <i class="error-text">warning</i>
24
+ <span class="error-text">{{ error }}</span>
25
+ </div>
26
+ {% endfor %}
27
+ </div>
28
+ {% endif %}
29
+
30
+ <div class="field label border round {% if form.username.errors %}error{% endif %}">
31
+ <input type="text" name="{{ form.username.name }}" id="{{ form.username.id_for_label }}" value="{{ form.username.value|default:'' }}">
32
+ <label>{% trans "Username" %}</label>
33
+ {% if form.username.errors %}
34
+ <span class="helper error-text">{{ form.username.errors.0 }}</span>
35
+ {% endif %}
36
+ </div>
37
+
38
+ <div class="field label border round {% if form.password.errors %}error{% endif %}">
39
+ <input type="password" name="{{ form.password.name }}" id="{{ form.password.id_for_label }}">
40
+ <label>{% trans "Password" %}</label>
41
+ {% if form.password.errors %}
42
+ <span class="helper error-text">{{ form.password.errors.0 }}</span>
43
+ {% endif %}
44
+ </div>
45
+
46
+ <div class="large-space"></div>
47
+
48
+ <nav class="right-align">
49
+ <a class="button transparent border round" href="{% url 'index' %}">{% trans "Cancel" %}</a>
50
+ <button type="submit" class="button primary round">{% trans "Login" %}</button>
51
+ </nav>
52
+
53
+ </form>
54
+ </article>
55
+ </div>
56
+ </div>
57
+ {% endblock %}
@@ -0,0 +1,87 @@
1
+ {% load i18n %}
2
+ <div class="grid justify-center" id="recipe-grid">
3
+ {% for recipe in recipes %}
4
+ <div class="s12 m6 l4 xl3 recipe-card">
5
+ <article class="round no-padding elevate">
6
+ <div style="position: relative;">
7
+ {% if recipe.image %}
8
+ <img src="{{ recipe.image_medium.url }}" class="responsive top-round" style="height:220px; width:100%; object-fit:cover; cursor:pointer;" loading="lazy" alt="{{ recipe.title }}" onclick="location.href='{% url 'recipe_detail' recipe.slug %}';">
9
+ {% else %}
10
+ <div class="primary medium-height top-round middle-align center-align" style="height:220px; cursor:pointer;" onclick="location.href='{% url 'recipe_detail' recipe.slug %}';">
11
+ <i class="extra">lunch_dining</i>
12
+ </div>
13
+ {% endif %}
14
+
15
+ {% if user.is_authenticated %}
16
+ <div class="absolute top left padding">
17
+ <a href="{% url 'toggle_favorite' recipe.pk %}" class="button circle surface white-text">
18
+ <i class="{% if recipe in user.favorites.all %}primary-text{% else %}black-text{% endif %}">
19
+ {% if recipe in user.favorites.all %}favorite{% else %}favorite_border{% endif %}
20
+ </i>
21
+ </a>
22
+ </div>
23
+ {% endif %}
24
+ </div>
25
+
26
+ <div class="padding">
27
+ <h5 class="truncate pointer" onclick="location.href='{% url 'recipe_detail' recipe.slug %}';">{{ recipe.title }}</h5>
28
+
29
+ <div class="row align-center">
30
+ {% if recipe.uploaded_by %}
31
+ <a href="{% url 'index' %}?uploader={{ recipe.uploaded_by.username|urlencode }}" class="row align-center" style="color: inherit; text-decoration: none;">
32
+ {% if recipe.uploaded_by.avatar %}
33
+ <img src="{{ recipe.uploaded_by.avatar.url }}" class="circle tiny" alt="{{ recipe.uploaded_by.username }}">
34
+ {% else %}
35
+ <i class="tiny circle surface">person</i>
36
+ {% endif %}
37
+ <span class="tiny-text left-margin">{{ recipe.uploaded_by.username }}</span>
38
+ </a>
39
+ {% else %}
40
+ <i class="tiny circle surface">person_off</i>
41
+ <span class="tiny-text left-margin">{% trans "Unknown" %}</span>
42
+ {% endif %}
43
+ </div>
44
+
45
+ <div class="space"></div>
46
+
47
+ <div class="row wrap">
48
+ {% if recipe.favorited_by.all %}
49
+ <span class="tiny-text">
50
+ {% if recipe.favorited_by.count == 1 %}
51
+ {% trans "Liked by" %} {{ recipe.favorited_by.first.username }}
52
+ {% else %}
53
+ {% trans "Liked by" %} {{ recipe.favorited_by.first.username }} {% trans "and" %} {{ recipe.favorited_by.count|add:"-1" }} {% trans "others" %}
54
+ {% endif %}
55
+ </span>
56
+ {% endif %}
57
+ </div>
58
+
59
+ <div class="space"></div>
60
+
61
+ <div class="row wrap">
62
+ {% for tag in recipe.tags.all %}
63
+ <a href="{% url 'index' %}?tag={{ tag.name|urlencode }}" class="chip small round surface margin-bottom">{{ tag.name }}</a>
64
+ {% endfor %}
65
+ </div>
66
+ </div>
67
+
68
+ <div class="padding pt-0">
69
+ <a href="{% url 'recipe_detail' recipe.slug %}" class="button fill round responsive">
70
+ <span>{% trans 'View Recipe' %}</span>
71
+ <i class="suffix">arrow_forward</i>
72
+ </a>
73
+ </div>
74
+ </article>
75
+ </div>
76
+ {% empty %}
77
+ <div class="s12 center-align">
78
+ <div class="large-space"></div>
79
+ <i class="extra gray-text">no_food</i>
80
+ <h5 class="gray-text">{% trans "No sandwitches found." %}</h5>
81
+ <p>{% trans "Be the first to create one!" %}</p>
82
+ {% if user.is_authenticated %}
83
+ <a href="{% url 'admin:sandwitches_recipe_add' %}" class="button primary round">{% trans "Add Recipe" %}</a>
84
+ {% endif %}
85
+ </div>
86
+ {% endfor %}
87
+ </div>
@@ -0,0 +1,119 @@
1
+ {% extends "base_beer.html" %}
2
+ {% load i18n %}
3
+
4
+ {% block title %}{% trans "Edit Recipe" %}{% endblock %}
5
+
6
+ {% block content %}
7
+ <div class="large-space"></div>
8
+ <article class="round s12 m10 l8 offset-m1 offset-l2 elevate">
9
+ <div class="padding">
10
+ <h4 class="center-align">
11
+ {% if recipe.pk %}
12
+ {% trans "Edit Recipe:" %} {{ recipe.title }}
13
+ {% else %}
14
+ {% trans "New Recipe" %}
15
+ {% endif %}
16
+ </h4>
17
+ <div class="space"></div>
18
+
19
+ <form method="post" enctype="multipart/form-data">
20
+ {% csrf_token %}
21
+
22
+ <div class="grid">
23
+ <div class="s12">
24
+ <div class="field label border round">
25
+ {{ form.title }}
26
+ <label>{% trans "Title" %}</label>
27
+ </div>
28
+ {% if form.title.errors %}
29
+ <p class="error-text tiny-text no-margin">{{ form.title.errors.0 }}</p>
30
+ {% endif %}
31
+ </div>
32
+
33
+ <div class="s12">
34
+ <div class="field textarea label border round" style="height: auto;">
35
+ {{ form.description }}
36
+ <label>{% trans "Description" %}</label>
37
+ </div>
38
+ {% if form.description.errors %}
39
+ <p class="error-text tiny-text no-margin">{{ form.description.errors.0 }}</p>
40
+ {% endif %}
41
+ </div>
42
+
43
+ <div class="s12 m6">
44
+ <div class="field textarea label border round">
45
+ {{ form.ingredients }}
46
+ <label>{% trans "Ingredients" %}</label>
47
+ </div>
48
+ </div>
49
+
50
+ <div class="s12 m6">
51
+ <div class="field textarea label border round">
52
+ {{ form.instructions }}
53
+ <label>{% trans "Instructions" %}</label>
54
+ </div>
55
+ </div>
56
+
57
+ <!-- Image Upload with Preview -->
58
+ <div class="s12 m6">
59
+ <div class="field label border round">
60
+ {{ form.image }}
61
+ <label>{% trans "Image" %}</label>
62
+ </div>
63
+ </div>
64
+ <div class="s12 m6 center-align relative">
65
+ <div class="padding border round dashed surface-variant" style="min-height: 150px; display: flex; align-items: center; justify-content: center;">
66
+ <img id="image-preview" src="{% if recipe.image %}{{ recipe.image.url }}{% endif %}" class="responsive round" style="max-height: 200px; {% if not recipe.image %}display:none;{% endif %}">
67
+ <span id="image-placeholder" class="gray-text" {% if recipe.image %}style="display:none;"{% endif %}>{% trans "Image Preview" %}</span>
68
+ </div>
69
+ </div>
70
+
71
+ <div class="s12">
72
+ <div class="field label border round">
73
+ {{ form.tags }}
74
+ <label>{% trans "Tags (comma separated)" %}</label>
75
+ </div>
76
+ </div>
77
+ </div>
78
+
79
+ <div class="large-space"></div>
80
+
81
+ <div class="row right-align">
82
+ <!-- Fallback to index if referer is missing, or history.back -->
83
+ <a href="javascript:history.back()" class="button transparent border round">{% trans "Cancel" %}</a>
84
+ <button type="submit" class="button primary round">
85
+ <i class="front">save</i>
86
+ <span>{% trans "Save Recipe" %}</span>
87
+ </button>
88
+ </div>
89
+ </form>
90
+ </div>
91
+ </article>
92
+ <div class="large-space"></div>
93
+
94
+ {% endblock %}
95
+
96
+ {% block page_scripts %}
97
+ <script>
98
+ // Image Preview Logic
99
+ var imageInput = document.querySelector('input[type="file"]');
100
+ if (imageInput) {
101
+ imageInput.onchange = function (evt) {
102
+ var tgt = evt.target || window.event.srcElement,
103
+ files = tgt.files;
104
+
105
+ if (FileReader && files && files.length) {
106
+ var fr = new FileReader();
107
+ fr.onload = function () {
108
+ var preview = document.getElementById('image-preview');
109
+ var placeholder = document.getElementById('image-placeholder');
110
+ preview.src = fr.result;
111
+ preview.style.display = 'block';
112
+ if(placeholder) placeholder.style.display = 'none';
113
+ }
114
+ fr.readAsDataURL(files[0]);
115
+ }
116
+ }
117
+ }
118
+ </script>
119
+ {% endblock %}
@@ -0,0 +1,105 @@
1
+ {% extends "base_beer.html" %}
2
+ {% load static i18n %}
3
+ {% block title %}{% trans "Initial setup — Create admin" %}{% endblock %}
4
+
5
+ {% block content %}
6
+ <div class="large-space"></div>
7
+
8
+ <div class="large-space"></div>
9
+
10
+ <div class="grid">
11
+ <div class="s12 m10 l8 xl6 middle-align center-align" style="margin: 0 auto;">
12
+ <article class="round elevate">
13
+ <div class="padding">
14
+ <h4 class="center-align primary-text">{% trans "Create initial administrator" %}</h4>
15
+ <p class="center-align">
16
+ {% trans "This page is only available when there are no admin users in the database." %}
17
+ </p>
18
+ <p class="center-align">
19
+ {% trans "After creating the account you will be logged in and redirected to the admin." %}
20
+ </p>
21
+ </div>
22
+
23
+ <form method="post" novalidate>
24
+ {% csrf_token %}
25
+
26
+ {% if form.non_field_errors %}
27
+ <div class="padding error surface round mb">
28
+ {% for err in form.non_field_errors %}
29
+ <div class="row align-center">
30
+ <i class="error-text">warning</i>
31
+ <span class="error-text">{{ err }}</span>
32
+ </div>
33
+ {% endfor %}
34
+ </div>
35
+ {% endif %}
36
+
37
+ <div class="grid">
38
+ <div class="s12 m6">
39
+ <div class="field label border round {% if form.first_name.errors %}error{% endif %}">
40
+ <input type="text" name="{{ form.first_name.name }}" id="{{ form.first_name.id_for_label }}" value="{{ form.first_name.value|default:'' }}">
41
+ <label>{% trans "First name" %}</label>
42
+ {% if form.first_name.errors %}
43
+ <span class="helper error-text">{{ form.first_name.errors.0 }}</span>
44
+ {% endif %}
45
+ </div>
46
+ </div>
47
+ <div class="s12 m6">
48
+ <div class="field label border round {% if form.last_name.errors %}error{% endif %}">
49
+ <input type="text" name="{{ form.last_name.name }}" id="{{ form.last_name.id_for_label }}" value="{{ form.last_name.value|default:'' }}">
50
+ <label>{% trans "Last name" %}</label>
51
+ {% if form.last_name.errors %}
52
+ <span class="helper error-text">{{ form.last_name.errors.0 }}</span>
53
+ {% endif %}
54
+ </div>
55
+ </div>
56
+ </div>
57
+
58
+ <div class="field label border round {% if form.username.errors %}error{% endif %}">
59
+ <input type="text" name="{{ form.username.name }}" id="{{ form.username.id_for_label }}" value="{{ form.username.value|default:'' }}">
60
+ <label>{% trans "Username" %}</label>
61
+ {% if form.username.errors %}
62
+ <span class="helper error-text">{{ form.username.errors.0 }}</span>
63
+ {% endif %}
64
+ </div>
65
+
66
+ <div class="field label border round {% if form.email.errors %}error{% endif %}">
67
+ <input type="email" name="{{ form.email.name }}" id="{{ form.email.id_for_label }}" value="{{ form.email.value|default:'' }}">
68
+ <label>{% trans "Email (optional)" %}</label>
69
+ {% if form.email.errors %}
70
+ <span class="helper error-text">{{ form.email.errors.0 }}</span>
71
+ {% endif %}
72
+ </div>
73
+
74
+ <div class="space"></div>
75
+ <div class="divider"></div>
76
+ <div class="space"></div>
77
+
78
+ <div class="field label border round {% if form.password1.errors %}error{% endif %}">
79
+ <input type="password" name="{{ form.password1.name }}" id="{{ form.password1.id_for_label }}">
80
+ <label>{% trans "Password" %}</label>
81
+ {% if form.password1.errors %}
82
+ <span class="helper error-text">{{ form.password1.errors.0 }}</span>
83
+ {% endif %}
84
+ </div>
85
+
86
+ <div class="field label border round {% if form.password2.errors %}error{% endif %}">
87
+ <input type="password" name="{{ form.password2.name }}" id="{{ form.password2.id_for_label }}">
88
+ <label>{% trans "Confirm password" %}</label>
89
+ {% if form.password2.errors %}
90
+ <span class="helper error-text">{{ form.password2.errors.0 }}</span>
91
+ {% endif %}
92
+ </div>
93
+
94
+ <div class="large-space"></div>
95
+
96
+ <nav class="right-align">
97
+ <a class="button transparent border round" href="{% url 'index' %}">{% trans "Cancel" %}</a>
98
+ <button type="submit" class="button primary round">{% trans "Create admin" %}</button>
99
+ </nav>
100
+
101
+ </form>
102
+ </article>
103
+ </div>
104
+ </div>
105
+ {% endblock %}
@@ -0,0 +1,133 @@
1
+ {% extends "base_beer.html" %}
2
+ {% load static i18n %}
3
+ {% block title %}{% trans "Sign Up" %}{% endblock %}
4
+
5
+ {% block content %}
6
+ <div class="large-space"></div>
7
+
8
+ <div class="grid">
9
+ <div class="s12 m10 l8 xl6 middle-align center-align" style="margin: 0 auto;">
10
+ <article class="round elevate">
11
+ <div class="padding">
12
+ <h4 class="center-align primary-text">{% trans "Create your account" %}</h4>
13
+ <p class="center-align">{% trans "Join the Sandwitches community today!" %}</p>
14
+ </div>
15
+
16
+ <form method="post" enctype="multipart/form-data" novalidate>
17
+ {% csrf_token %}
18
+
19
+ {% if form.non_field_errors %}
20
+ <div class="padding error surface round mb">
21
+ {% for err in form.non_field_errors %}
22
+ <div class="row align-center">
23
+ <i class="error-text">warning</i>
24
+ <span class="error-text">{{ err }}</span>
25
+ </div>
26
+ {% endfor %}
27
+ </div>
28
+ {% endif %}
29
+
30
+ <div class="grid">
31
+ <div class="s12 m6">
32
+ <div class="field label border round {% if form.first_name.errors %}error{% endif %}">
33
+ <input type="text" name="{{ form.first_name.name }}" id="{{ form.first_name.id_for_label }}" value="{{ form.first_name.value|default:'' }}">
34
+ <label>{% trans "First name" %}</label>
35
+ {% if form.first_name.errors %}
36
+ <span class="helper error-text">{{ form.first_name.errors.0 }}</span>
37
+ {% endif %}
38
+ </div>
39
+ </div>
40
+ <div class="s12 m6">
41
+ <div class="field label border round {% if form.last_name.errors %}error{% endif %}">
42
+ <input type="text" name="{{ form.last_name.name }}" id="{{ form.last_name.id_for_label }}" value="{{ form.last_name.value|default:'' }}">
43
+ <label>{% trans "Last name" %}</label>
44
+ {% if form.last_name.errors %}
45
+ <span class="helper error-text">{{ form.last_name.errors.0 }}</span>
46
+ {% endif %}
47
+ </div>
48
+ </div>
49
+ </div>
50
+
51
+ <div class="field label border round {% if form.username.errors %}error{% endif %}">
52
+ <input type="text" name="{{ form.username.name }}" id="{{ form.username.id_for_label }}" value="{{ form.username.value|default:'' }}">
53
+ <label>{% trans "Username" %}</label>
54
+ {% if form.username.errors %}
55
+ <span class="helper error-text">{{ form.username.errors.0 }}</span>
56
+ {% endif %}
57
+ </div>
58
+
59
+ <div class="field label border round {% if form.email.errors %}error{% endif %}">
60
+ <input type="email" name="{{ form.email.name }}" id="{{ form.email.id_for_label }}" value="{{ form.email.value|default:'' }}">
61
+ <label>{% trans "Email (optional)" %}</label>
62
+ {% if form.email.errors %}
63
+ <span class="helper error-text">{{ form.email.errors.0 }}</span>
64
+ {% endif %}
65
+ </div>
66
+
67
+ <div class="field label border round {% if form.language.errors %}error{% endif %}">
68
+ <select name="{{ form.language.name }}" id="{{ form.language.id_for_label }}">
69
+ {% for value, label in form.language.field.choices %}
70
+ <option value="{{ value }}" {% if form.language.value == value %}selected{% endif %}>{{ label }}</option>
71
+ {% endfor %}
72
+ </select>
73
+ <label>{% trans "Preferred Language" %}</label>
74
+ {% if form.language.errors %}
75
+ <span class="helper error-text">{{ form.language.errors.0 }}</span>
76
+ {% endif %}
77
+ </div>
78
+
79
+ <div class="field label border round textarea {% if form.bio.errors %}error{% endif %}">
80
+ <textarea name="{{ form.bio.name }}" id="{{ form.bio.id_for_label }}" rows="3">{{ form.bio.value|default:'' }}</textarea>
81
+ <label>{% trans "Bio" %}</label>
82
+ {% if form.bio.errors %}
83
+ <span class="helper error-text">{{ form.bio.errors.0 }}</span>
84
+ {% endif %}
85
+ </div>
86
+
87
+ <div class="field middle-align {% if form.avatar.errors %}error{% endif %}">
88
+ <nav>
89
+ <div class="max">
90
+ <label class="button border transparent round">
91
+ <input type="file" name="{{ form.avatar.name }}" id="{{ form.avatar.id_for_label }}" accept="image/*">
92
+ <i>upload</i>
93
+ <span>{% trans "Upload Profile Picture" %}</span>
94
+ </label>
95
+ </div>
96
+ </nav>
97
+ {% if form.avatar.errors %}
98
+ <span class="helper error-text">{{ form.avatar.errors.0 }}</span>
99
+ {% endif %}
100
+ </div>
101
+
102
+ <div class="space"></div>
103
+ <div class="divider"></div>
104
+ <div class="space"></div>
105
+
106
+ <div class="field label border round {% if form.password1.errors %}error{% endif %}">
107
+ <input type="password" name="{{ form.password1.name }}" id="{{ form.password1.id_for_label }}">
108
+ <label>{% trans "Password" %}</label>
109
+ {% if form.password1.errors %}
110
+ <span class="helper error-text">{{ form.password1.errors.0 }}</span>
111
+ {% endif %}
112
+ </div>
113
+
114
+ <div class="field label border round {% if form.password2.errors %}error{% endif %}">
115
+ <input type="password" name="{{ form.password2.name }}" id="{{ form.password2.id_for_label }}">
116
+ <label>{% trans "Confirm password" %}</label>
117
+ {% if form.password2.errors %}
118
+ <span class="helper error-text">{{ form.password2.errors.0 }}</span>
119
+ {% endif %}
120
+ </div>
121
+
122
+ <div class="large-space"></div>
123
+
124
+ <nav class="right-align">
125
+ <a class="button transparent border round" href="{% url 'index' %}">{% trans "Cancel" %}</a>
126
+ <button type="submit" class="button primary round">{% trans "Sign Up" %}</button>
127
+ </nav>
128
+
129
+ </form>
130
+ </article>
131
+ </div>
132
+ </div>
133
+ {% endblock %}
File without changes
@@ -0,0 +1,15 @@
1
+ from django import template
2
+
3
+ register = template.Library()
4
+
5
+
6
+ @register.filter
7
+ def split(value, arg):
8
+ return value.split(arg)
9
+
10
+
11
+ @register.filter
12
+ def strip(value):
13
+ if isinstance(value, str):
14
+ return value.strip()
15
+ return value
@@ -0,0 +1,17 @@
1
+ import markdown
2
+
3
+ from django import template
4
+ from django.template.defaultfilters import stringfilter
5
+
6
+ register = template.Library()
7
+
8
+
9
+ @register.filter
10
+ @stringfilter
11
+ def convert_markdown(value):
12
+ md = markdown.markdown(
13
+ value,
14
+ extensions=["markdown.extensions.fenced_code", "markdown.extensions.tables"],
15
+ )
16
+
17
+ return md