Merge branch 'release/v20.2.2'
This commit is contained in:
@@ -8,7 +8,7 @@ from .models import (
|
|||||||
WorkoutComment, C2WorldClassAgePerformance, PlannedSession,
|
WorkoutComment, C2WorldClassAgePerformance, PlannedSession,
|
||||||
GeoCourse, GeoPolygon, GeoPoint, VirtualRace, VirtualRaceResult,
|
GeoCourse, GeoPolygon, GeoPoint, VirtualRace, VirtualRaceResult,
|
||||||
PaidPlan, IndoorVirtualRaceResult, ShareKey,
|
PaidPlan, IndoorVirtualRaceResult, ShareKey,
|
||||||
CourseStandard, StandardCollection, InstantPlan,
|
CourseStandard, StandardCollection, InstantPlan, UserMessage
|
||||||
)
|
)
|
||||||
|
|
||||||
# Register your models here so you can use them in the Admin module
|
# Register your models here so you can use them in the Admin module
|
||||||
@@ -170,6 +170,9 @@ class CourseStandardAdmin(admin.ModelAdmin):
|
|||||||
class InstantPlanAdmin(admin.ModelAdmin):
|
class InstantPlanAdmin(admin.ModelAdmin):
|
||||||
list_display = ('name','duration','price')
|
list_display = ('name','duration','price')
|
||||||
|
|
||||||
|
class UserMessageAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ('receiver','datetime','subject')
|
||||||
|
|
||||||
admin.site.unregister(User)
|
admin.site.unregister(User)
|
||||||
admin.site.register(User, UserAdmin)
|
admin.site.register(User, UserAdmin)
|
||||||
admin.site.register(Workout, WorkoutAdmin)
|
admin.site.register(Workout, WorkoutAdmin)
|
||||||
@@ -192,3 +195,4 @@ admin.site.register(ShareKey, ShareKeyAdmin)
|
|||||||
admin.site.register(CourseStandard, CourseStandardAdmin)
|
admin.site.register(CourseStandard, CourseStandardAdmin)
|
||||||
admin.site.register(StandardCollection, StandardCollectionAdmin)
|
admin.site.register(StandardCollection, StandardCollectionAdmin)
|
||||||
admin.site.register(InstantPlan, InstantPlanAdmin)
|
admin.site.register(InstantPlan, InstantPlanAdmin)
|
||||||
|
admin.site.register(UserMessage, UserMessageAdmin)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
from django.contrib.staticfiles import finders
|
from django.contrib.staticfiles import finders
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
import gc
|
import gc
|
||||||
@@ -112,17 +112,22 @@ def send_template_email(from_email, to_email, subject,
|
|||||||
else:
|
else:
|
||||||
emailbounced = False
|
emailbounced = False
|
||||||
|
|
||||||
try:
|
for recipient in to_email:
|
||||||
usr = User.objects.get(email=to_email)
|
try:
|
||||||
umsg = UserMessage(
|
soup = BeautifulSoup(html_content)
|
||||||
receiver = usr.rower,
|
|
||||||
datetime = timezone.now(),
|
s2 = soup.body
|
||||||
text = text_content,
|
|
||||||
subject=subject,
|
usr = User.objects.get(email=recipient)
|
||||||
)
|
umsg = UserMessage(
|
||||||
umsg.save()
|
receiver = usr.rower,
|
||||||
except User.DoesNotExist:
|
datetime = timezone.now(),
|
||||||
pass
|
text = '{text}'.format(text=s2),
|
||||||
|
subject=subject,
|
||||||
|
)
|
||||||
|
umsg.save()
|
||||||
|
except User.DoesNotExist:
|
||||||
|
pass
|
||||||
|
|
||||||
if not emailbounced:
|
if not emailbounced:
|
||||||
res = msg.send()
|
res = msg.send()
|
||||||
|
|||||||
@@ -1247,6 +1247,14 @@ class UserMessage(models.Model):
|
|||||||
isread = models.BooleanField(default=False)
|
isread = models.BooleanField(default=False)
|
||||||
text = models.CharField(max_length=1000)
|
text = models.CharField(max_length=1000)
|
||||||
subject = models.CharField(max_length=100,default='Message')
|
subject = models.CharField(max_length=100,default='Message')
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return '{r1} {r2} {d} {subject}'.format(
|
||||||
|
r1 = self.receiver.user.first_name,
|
||||||
|
r2 = self.receiver.user.last_name,
|
||||||
|
d = self.datetime,
|
||||||
|
subject = self.subject
|
||||||
|
)
|
||||||
|
|
||||||
# requestor is user
|
# requestor is user
|
||||||
|
|
||||||
|
|||||||
24
rowers/templates/contactformemail.html
Normal file
24
rowers/templates/contactformemail.html
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
{% extends "emailbase.html" %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<p>New Contact Form Message</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
From: {{ firstname }} {{ lastname }}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Email: {{ email }}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Subject: {{ subject }}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
{{ message }}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Best Regards, the Rowsandall Team
|
||||||
|
</p>
|
||||||
|
{% endblock %}
|
||||||
@@ -2,6 +2,14 @@
|
|||||||
{% load rowerfilters %}
|
{% load rowerfilters %}
|
||||||
<h1><a href="/rowers/me/edit/">Profile</a></h1>
|
<h1><a href="/rowers/me/edit/">Profile</a></h1>
|
||||||
<ul class="cd-accordion-menu animated">
|
<ul class="cd-accordion-menu animated">
|
||||||
|
<li id="manage-messgs">
|
||||||
|
<a href="/rowers/me/messages/">
|
||||||
|
<i class="fas fa-envelope fa-fw"></i> Messages
|
||||||
|
{% if rower|usermessages %}
|
||||||
|
( {{ rower|usermessages }} )
|
||||||
|
{% endif %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
<li id="manage-prefs-simple">
|
<li id="manage-prefs-simple">
|
||||||
<a href="/rowers/me/prefs/">
|
<a href="/rowers/me/prefs/">
|
||||||
<i class="fas fa-sliders-v-square fa-fw"></i> Threshold
|
<i class="fas fa-sliders-v-square fa-fw"></i> Threshold
|
||||||
|
|||||||
59
rowers/templates/user_messages.html
Normal file
59
rowers/templates/user_messages.html
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
{% extends "newbase.html" %}
|
||||||
|
|
||||||
|
{% block title %}Messages{% endblock %}
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
<h1>{{ rower.user.first_name }}'s New Messages</h1>
|
||||||
|
|
||||||
|
<ul class="main-content">
|
||||||
|
{% for usermessage in usermessages %}
|
||||||
|
{% if not usermessage.isread %}
|
||||||
|
<li class="rounder">
|
||||||
|
<h2>{{ usermessage.subject }}</h2>
|
||||||
|
<p><em>{{ usermessage.datetime }}</em></p>
|
||||||
|
<p>
|
||||||
|
{{ usermessage.text|safe }}
|
||||||
|
</p>
|
||||||
|
{% if request.user.rower == usermessage.receiver %}
|
||||||
|
<p>
|
||||||
|
<a href="/rowers/me/messages/{{ usermessage.id }}/delete/">
|
||||||
|
<i class="fas fa-trash-alt fa-fw"></i>
|
||||||
|
</a>
|
||||||
|
<a href="/rowers/me/messages/{{ usermessage.id }}/markread/">
|
||||||
|
<i class="fas fa-check fa-fw"></i>
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h1>{{ rower.user.first_name }}'s Previous Messages</h1>
|
||||||
|
|
||||||
|
<ul class="main-content">
|
||||||
|
{% for usermessage in usermessages %}
|
||||||
|
{% if usermessage.isread %}
|
||||||
|
<li class="rounder">
|
||||||
|
<h2>{{ usermessage.subject }}</h2>
|
||||||
|
<p><em>{{ usermessage.datetime }}</em></p>
|
||||||
|
<p>
|
||||||
|
{{ usermessage.text|safe }}
|
||||||
|
</p>
|
||||||
|
{% if request.user.rower == usermessage.receiver %}
|
||||||
|
<p>
|
||||||
|
<a href="/rowers/me/messages/{{ usermessage.id }}/delete/">
|
||||||
|
<i class="fas fa-trash-alt fa-fw"></i>
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block sidebar %}
|
||||||
|
{% include 'menu_profile.html' %}
|
||||||
|
{% endblock %}
|
||||||
@@ -15,7 +15,7 @@ from rowers.models import (
|
|||||||
course_length, WorkoutComment,
|
course_length, WorkoutComment,
|
||||||
TrainingMacroCycle, TrainingMesoCycle, TrainingMicroCycle,
|
TrainingMacroCycle, TrainingMesoCycle, TrainingMicroCycle,
|
||||||
Rower, Workout, SiteAnnouncement, TeamInvite, TeamRequest, CoachOffer, CoachRequest,
|
Rower, Workout, SiteAnnouncement, TeamInvite, TeamRequest, CoachOffer, CoachRequest,
|
||||||
VirtualRaceFollower, VirtualRace, favanalysischoices,
|
VirtualRaceFollower, VirtualRace, favanalysischoices, UserMessage,
|
||||||
Team, TrainingPlan, TrainingTarget)
|
Team, TrainingPlan, TrainingTarget)
|
||||||
|
|
||||||
from rowers.plannedsessions import (
|
from rowers.plannedsessions import (
|
||||||
@@ -56,6 +56,15 @@ def workoutdate(id): # pragma: no cover
|
|||||||
except Workout.DoesNotExist:
|
except Workout.DoesNotExist:
|
||||||
return 'unknown'
|
return 'unknown'
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def usermessages(rower):
|
||||||
|
try:
|
||||||
|
msgs = UserMessage.objects.filter(receiver=rower, isread=False)
|
||||||
|
return msgs.count()
|
||||||
|
except UserMessage.DoesNotExist:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
@register.filter
|
@register.filter
|
||||||
def isfollower(user, id):
|
def isfollower(user, id):
|
||||||
|
|||||||
BIN
rowers/tests/testdata/testdata.tcx.gz
vendored
BIN
rowers/tests/testdata/testdata.tcx.gz
vendored
Binary file not shown.
@@ -650,6 +650,10 @@ urlpatterns = [
|
|||||||
re_path(r'^alerts/(?P<id>\d+)/report/$',
|
re_path(r'^alerts/(?P<id>\d+)/report/$',
|
||||||
views.alert_report_view, name='alert_report_view'),
|
views.alert_report_view, name='alert_report_view'),
|
||||||
re_path(r'^me/deactivate/$', views.deactivate_user, name='deactivate_user'),
|
re_path(r'^me/deactivate/$', views.deactivate_user, name='deactivate_user'),
|
||||||
|
re_path(r'^me/messages/$', views.user_messages, name='user_messages'),
|
||||||
|
re_path(r'^me/messages/(?P<id>\d+)/delete/$', views.user_message_delete, name='user_message_delete'),
|
||||||
|
re_path(r'^me/messages/(?P<id>\d+)/markread/$', views.user_message_markread, name='user_message_markread'),
|
||||||
|
re_path(r'^me/messages/user/(?P<userid>\d+)/$', views.user_messages, name='user_messages'),
|
||||||
re_path(r'^me/delete/$', views.remove_user, name='remove_user'),
|
re_path(r'^me/delete/$', views.remove_user, name='remove_user'),
|
||||||
re_path(r'^survey/$', views.survey, name='survey'),
|
re_path(r'^survey/$', views.survey, name='survey'),
|
||||||
re_path(r'^me/gdpr-optin-confirm/?/$',
|
re_path(r'^me/gdpr-optin-confirm/?/$',
|
||||||
|
|||||||
@@ -158,7 +158,8 @@ from rowers.models import (
|
|||||||
VideoAnalysis, ShareKey,
|
VideoAnalysis, ShareKey,
|
||||||
StandardCollection, CourseStandard,
|
StandardCollection, CourseStandard,
|
||||||
VirtualRaceFollower, TombStone, InstantPlan,
|
VirtualRaceFollower, TombStone, InstantPlan,
|
||||||
PlannedSessionStep,InStrokeAnalysis, ForceCurveAnalysis, SyncRecord
|
PlannedSessionStep,InStrokeAnalysis, ForceCurveAnalysis, SyncRecord,
|
||||||
|
UserMessage,
|
||||||
)
|
)
|
||||||
from rowers.models import ( RowerPowerForm, RowerHRZonesForm, SimpleRowerPowerForm,
|
from rowers.models import ( RowerPowerForm, RowerHRZonesForm, SimpleRowerPowerForm,
|
||||||
RowerForm, RowerCPForm, GraphImage, AdvancedWorkoutForm,
|
RowerForm, RowerCPForm, GraphImage, AdvancedWorkoutForm,
|
||||||
@@ -1289,11 +1290,24 @@ def sendmail(request):
|
|||||||
subject = 'Rowsandall Contact Form:'+form.cleaned_data['subject']
|
subject = 'Rowsandall Contact Form:'+form.cleaned_data['subject']
|
||||||
message = form.cleaned_data['message']
|
message = form.cleaned_data['message']
|
||||||
fullemail = firstname + " " + lastname + " " + "<" + email + ">"
|
fullemail = firstname + " " + lastname + " " + "<" + email + ">"
|
||||||
try:
|
d = {
|
||||||
send_mail(subject, message, fullemail, ['info@rowsandall.com'])
|
'firstname': firstname,
|
||||||
except:
|
'lastname': lastname,
|
||||||
|
'email': email,
|
||||||
|
'subject': subject,
|
||||||
|
'message': message,
|
||||||
|
}
|
||||||
|
res = send_template_email('Rowsandall <info@rowsandall.com>',
|
||||||
|
['Rowsandall <info@rowsandall.com>'],
|
||||||
|
subject,
|
||||||
|
'contactformemail.html',
|
||||||
|
d
|
||||||
|
)
|
||||||
|
if res:
|
||||||
|
messages.info(request,"Your contact form was submitted")
|
||||||
|
else:
|
||||||
messages.error(
|
messages.error(
|
||||||
request, "Something went wrong trying to send the email")
|
request, "Something went wrong trying to send the form")
|
||||||
return HttpResponseRedirect('/rowers/email/thankyou/')
|
return HttpResponseRedirect('/rowers/email/thankyou/')
|
||||||
else:
|
else:
|
||||||
if not success:
|
if not success:
|
||||||
|
|||||||
@@ -248,6 +248,50 @@ def start_plantrial_view(request):
|
|||||||
|
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
|
@login_required()
|
||||||
|
@permission_required('rower.is_coach', fn=get_user_by_userid, raise_exception=True)
|
||||||
|
def user_messages(request,userid=0):
|
||||||
|
r = getrequestrowercoachee(request, userid=userid, notpermanent=True)
|
||||||
|
usermessages = UserMessage.objects.filter(receiver=r).order_by('-datetime')
|
||||||
|
return render(request,'user_messages.html',
|
||||||
|
{'usermessages':usermessages,
|
||||||
|
'rower':r})
|
||||||
|
|
||||||
|
@login_required()
|
||||||
|
def user_message_delete(request,id=0):
|
||||||
|
try:
|
||||||
|
msg = UserMessage.objects.get(id=id)
|
||||||
|
except UserMessage.DoesNotExist:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if msg.receiver == request.user.rower:
|
||||||
|
msg.delete()
|
||||||
|
messages.info(request,'Deleted message {id}'.format(id=id))
|
||||||
|
else:
|
||||||
|
messages.error('You are not allowed to delete this message')
|
||||||
|
|
||||||
|
url = reverse('user_messages')
|
||||||
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required()
|
||||||
|
def user_message_markread(request,id=0):
|
||||||
|
try:
|
||||||
|
msg = UserMessage.objects.get(id=id)
|
||||||
|
except UserMessage.DoesNotExist:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if msg.receiver == request.user.rower:
|
||||||
|
msg.isread = True
|
||||||
|
msg.save()
|
||||||
|
messages.info(request,'Marked message {id} read'.format(id=id))
|
||||||
|
else:
|
||||||
|
messages.error('You are not allowed to change this message')
|
||||||
|
|
||||||
|
url = reverse('user_messages')
|
||||||
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Page where user can manage his favorite charts
|
# Page where user can manage his favorite charts
|
||||||
@login_required()
|
@login_required()
|
||||||
|
|||||||
@@ -166,6 +166,14 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
{% if user.rower|usermessages %}
|
||||||
|
<li>
|
||||||
|
<a class="" href="{% url 'user_messages' %}" title="Messages" id="id-messages">
|
||||||
|
<i class="fas fa-envelope fa-fw"></i>
|
||||||
|
({{ user.rower|usermessages }})
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
<li>
|
<li>
|
||||||
<a class="" href="{% url 'logout' %}?next=/login/" title="Sign Out" id="id-logout">
|
<a class="" href="{% url 'logout' %}?next=/login/" title="Sign Out" id="id-logout">
|
||||||
<i class="fas fa-sign-out-alt "></i>
|
<i class="fas fa-sign-out-alt "></i>
|
||||||
|
|||||||
Reference in New Issue
Block a user