Merge branch 'release/v12.02'
This commit is contained in:
@@ -3730,3 +3730,16 @@ class VideoAnalysis(models.Model):
|
|||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ShareKey(models.Model):
|
||||||
|
location = models.TextField() # absolute path
|
||||||
|
token = models.CharField(max_length=40, primary_key=True)
|
||||||
|
creation_date = models.DateTimeField(auto_now_add=True)
|
||||||
|
expiration_seconds = models.BigIntegerField()
|
||||||
|
|
||||||
|
|
||||||
|
@property
|
||||||
|
def expired(self):
|
||||||
|
return self.creation_date + datetime.timedelta(self.expiration_seconds) < timezone.now()
|
||||||
|
|||||||
@@ -713,8 +713,10 @@ def get_dates_timeperiod(request,startdatestring='',enddatestring='',
|
|||||||
if not timeperiod:
|
if not timeperiod:
|
||||||
timeperiod = defaulttimeperiod
|
timeperiod = defaulttimeperiod
|
||||||
|
|
||||||
startdatestring = request.GET.get('startdate')
|
if startdatestring == '':
|
||||||
enddatestring = request.GET.get('enddate')
|
startdatestring = request.GET.get('startdate')
|
||||||
|
if enddatestring == '':
|
||||||
|
enddatestring = request.GET.get('enddate')
|
||||||
|
|
||||||
if startdatestring and enddatestring:
|
if startdatestring and enddatestring:
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -195,6 +195,9 @@ def can_add_session(user):
|
|||||||
|
|
||||||
@rules.predicate
|
@rules.predicate
|
||||||
def can_plan(user):
|
def can_plan(user):
|
||||||
|
if user.is_anonymous:
|
||||||
|
return False
|
||||||
|
|
||||||
return user.rower.rowerplan in ['plan','coach','freecoach']
|
return user.rower.rowerplan in ['plan','coach','freecoach']
|
||||||
|
|
||||||
# checks if rower is coach of user (or is user himself)
|
# checks if rower is coach of user (or is user himself)
|
||||||
|
|||||||
@@ -37,8 +37,15 @@
|
|||||||
</table>
|
</table>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<form enctype="multipart/form-data" action="/rowers/access/share/" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input name="url" value="{{ request.path }}{{ timeperiod }}/" type="hidden">
|
||||||
|
<label for="id_ndays">Number of days link is valid:</label>
|
||||||
|
<input name="ndays" id="id_ndays" type="number" step="1" value="7">
|
||||||
|
<input type="submit" value="create shareable link">
|
||||||
|
</form>
|
||||||
|
</p>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|||||||
42
rowers/templates/share.html
Normal file
42
rowers/templates/share.html
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
{% extends "newbase.html" %}
|
||||||
|
{% load staticfiles %}
|
||||||
|
{% load rowerfilters %}
|
||||||
|
{% load leaflet_tags %}
|
||||||
|
|
||||||
|
{% block meta %}
|
||||||
|
{% leaflet_js %}
|
||||||
|
{% leaflet_css %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block title %}Share Page{% endblock %}
|
||||||
|
|
||||||
|
{% block scripts %}
|
||||||
|
{% include "monitorjobs.html" %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block og_title %}{{ race.name }}{% endblock %}
|
||||||
|
{% block description %}Virtual Rowing Race {{ race.name }}{% endblock %}
|
||||||
|
|
||||||
|
{% if racelogo %}
|
||||||
|
{% block og_image %}
|
||||||
|
<meta property="og:image" content="http://rowsandall.com/{{ racelogo.filename|spacetohtml }}" />
|
||||||
|
<meta property="og:image:secure_url" content="https://rowsandall.com/{{ racelogo.filename |spacetohtml }}" />
|
||||||
|
<meta property="og:image:width" content="{{ racelogo.width }}" />
|
||||||
|
<meta property="og:image:height" content="{{ racelogo.height }}" />
|
||||||
|
{% endblock %}
|
||||||
|
{% block image_src %}
|
||||||
|
<link rel="image_src" href="/{{ racelogo.filename |spacetohtml }}" />
|
||||||
|
{% endblock %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
|
||||||
|
|
||||||
|
<h1>Sharing link created.</h1>
|
||||||
|
<p>The link is <a href="{{ base_url }}{% url 'sharedPage' key.pk %}">{{ base_url }}{% url 'sharedPage' key.pk %}</a>. It will be valid until {{ key.expiration_date|date:"l, N dS" }} at {{ key.expiration_date|time:"g:i a" }}.</p>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block sidebar %}
|
||||||
|
{% include 'menu_racing.html' %}
|
||||||
|
{% endblock %}
|
||||||
@@ -56,6 +56,13 @@ def sigdig(value, digits = 3):
|
|||||||
fmtstr = "%.0f"
|
fmtstr = "%.0f"
|
||||||
return fmtstr % (round(value, places))
|
return fmtstr % (round(value, places))
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def pickle(dc):
|
||||||
|
s = dict()
|
||||||
|
for key, value in dc.items():
|
||||||
|
s[key] = value
|
||||||
|
|
||||||
|
return s
|
||||||
|
|
||||||
@register.filter(is_safe=True, needs_autoescape=True)
|
@register.filter(is_safe=True, needs_autoescape=True)
|
||||||
@stringfilter
|
@stringfilter
|
||||||
|
|||||||
@@ -721,6 +721,8 @@ urlpatterns = [
|
|||||||
name='plannedsession_comment_view'),
|
name='plannedsession_comment_view'),
|
||||||
re_path(r'^sessions/print/user/(?P<userid>\d+)/$',views.plannedsessions_print_view,
|
re_path(r'^sessions/print/user/(?P<userid>\d+)/$',views.plannedsessions_print_view,
|
||||||
name='plannedsessions_print_view'),
|
name='plannedsessions_print_view'),
|
||||||
|
re_path(r'^sessions/print/user/(?P<userid>\d+)/(?P<startdatestring>\d+-\d+-\d+)/(?P<enddatestring>\d+-\d+-\d+)/$',views.plannedsessions_print_view,
|
||||||
|
name='plannedsessions_print_view'),
|
||||||
re_path(r'^sessions/sendcalendar/$',views.plannedsessions_icsemail_view,
|
re_path(r'^sessions/sendcalendar/$',views.plannedsessions_icsemail_view,
|
||||||
name='plannedsessions_coach_icsemail_view'),
|
name='plannedsessions_coach_icsemail_view'),
|
||||||
re_path(r'^sessions/sendcalendar/user/(?P<userid>\d+)/$',views.plannedsessions_icsemail_view,
|
re_path(r'^sessions/sendcalendar/user/(?P<userid>\d+)/$',views.plannedsessions_icsemail_view,
|
||||||
@@ -747,6 +749,8 @@ urlpatterns = [
|
|||||||
# URLS to be created
|
# URLS to be created
|
||||||
re_path(r'^help/$',TemplateView.as_view(template_name='help.html'), name='help'),
|
re_path(r'^help/$',TemplateView.as_view(template_name='help.html'), name='help'),
|
||||||
re_path(r'^workout/api/upload/',views.workout_upload_api,name='workout_upload_api'),
|
re_path(r'^workout/api/upload/',views.workout_upload_api,name='workout_upload_api'),
|
||||||
|
re_path(r'^access/share/$',views.createShareURL, name="sharedURL"),
|
||||||
|
re_path(r'^access/(?P<key>\w+)/$', views.sharedPage, name="sharedPage"),
|
||||||
]
|
]
|
||||||
|
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
|
|||||||
@@ -319,7 +319,10 @@ def trendflexdata(workouts, options,userid=0):
|
|||||||
|
|
||||||
today = datetime.date.today()
|
today = datetime.date.today()
|
||||||
|
|
||||||
datadf['days ago'] = list(map(lambda x : x.days, datadf.date - today))
|
try:
|
||||||
|
datadf['days ago'] = list(map(lambda x : x.days, datadf.date - today))
|
||||||
|
except TypeError:
|
||||||
|
datadf['days ago'] = 0
|
||||||
|
|
||||||
|
|
||||||
if groupby != 'date':
|
if groupby != 'date':
|
||||||
|
|||||||
@@ -1183,14 +1183,16 @@ def plannedsessions_view(request,
|
|||||||
'unmatchedworkouts':unmatchedworkouts,
|
'unmatchedworkouts':unmatchedworkouts,
|
||||||
})
|
})
|
||||||
|
|
||||||
@login_required()
|
@allow_shares
|
||||||
def plannedsessions_print_view(request,userid=0):
|
#@login_required()
|
||||||
|
def plannedsessions_print_view(request,userid=0,startdatestring='',enddatestring=''):
|
||||||
|
|
||||||
r = getrequestplanrower(request,userid=userid)
|
r = getrequestplanrower(request,userid=userid)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
startdate,enddate = get_dates_timeperiod(request)
|
startdate,enddate = get_dates_timeperiod(request,startdatestring=startdatestring,
|
||||||
|
enddatestring=enddatestring)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
trainingplan = TrainingPlan.objects.filter(
|
trainingplan = TrainingPlan.objects.filter(
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ from django.core.exceptions import PermissionDenied
|
|||||||
from django.template import RequestContext
|
from django.template import RequestContext
|
||||||
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.urls import resolve
|
||||||
from django.utils.datastructures import MultiValueDictKeyError
|
from django.utils.datastructures import MultiValueDictKeyError
|
||||||
from django.utils import timezone,translation
|
from django.utils import timezone,translation
|
||||||
from django.core.mail import send_mail, BadHeaderError
|
from django.core.mail import send_mail, BadHeaderError
|
||||||
@@ -112,7 +113,7 @@ from rowers.models import (
|
|||||||
RaceLogo,RowerBillingAddressForm,PaidPlan,
|
RaceLogo,RowerBillingAddressForm,PaidPlan,
|
||||||
AlertEditForm, ConditionEditForm,
|
AlertEditForm, ConditionEditForm,
|
||||||
PlannedSessionComment,CoachRequest,CoachOffer,
|
PlannedSessionComment,CoachRequest,CoachOffer,
|
||||||
VideoAnalysis
|
VideoAnalysis,ShareKey,
|
||||||
)
|
)
|
||||||
from rowers.models import (
|
from rowers.models import (
|
||||||
RowerPowerForm,RowerForm,GraphImage,AdvancedWorkoutForm,
|
RowerPowerForm,RowerForm,GraphImage,AdvancedWorkoutForm,
|
||||||
@@ -251,11 +252,62 @@ from rq.exceptions import NoSuchJobError
|
|||||||
from rq.registry import StartedJobRegistry
|
from rq.registry import StartedJobRegistry
|
||||||
from rq import Queue,cancel_job
|
from rq import Queue,cancel_job
|
||||||
|
|
||||||
|
from django.utils.crypto import get_random_string
|
||||||
|
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django_mailbox.models import Message,Mailbox,MessageAttachment
|
from django_mailbox.models import Message,Mailbox,MessageAttachment
|
||||||
|
|
||||||
from rules.contrib.views import permission_required, objectgetter
|
from rules.contrib.views import permission_required, objectgetter
|
||||||
|
|
||||||
|
# creating shareable views
|
||||||
|
def allow_shares(view_func):
|
||||||
|
def sharify(request, *args, **kwargs):
|
||||||
|
shared = kwargs.get('__shared', None)
|
||||||
|
if shared is not None:
|
||||||
|
del kwargs["__shared"]
|
||||||
|
request.session['shared'] = True
|
||||||
|
return view_func(request, *args, **kwargs)
|
||||||
|
else: return login_required(view_func)(request, *args, **kwargs)
|
||||||
|
return sharify
|
||||||
|
|
||||||
|
class SharifyError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def sharedPage(request, key):
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
shareKey = ShareKey.objects.get(pk=key)
|
||||||
|
except:
|
||||||
|
raise SharifyError
|
||||||
|
if shareKey.expired:
|
||||||
|
raise SharifyError
|
||||||
|
func, args, kwargs = resolve(shareKey.location)
|
||||||
|
kwargs["__shared"] = True
|
||||||
|
return func(request, *args, **kwargs)
|
||||||
|
except SharifyError:
|
||||||
|
raise Http404 # or add a more detailed error page. This either means that the key doesn’t exist or is expired.
|
||||||
|
|
||||||
|
def createShareURL(request):
|
||||||
|
if request.method == 'POST':
|
||||||
|
url = request.POST['url']
|
||||||
|
ndays = int(request.POST['ndays'])
|
||||||
|
key = ShareKey.objects.create(pk=get_random_string(40),
|
||||||
|
expiration_seconds=60*60*24*ndays,
|
||||||
|
location = url)
|
||||||
|
key.save()
|
||||||
|
return render(request, 'share.html', {"key":key})
|
||||||
|
else:
|
||||||
|
raise Http404
|
||||||
|
|
||||||
|
def createShareModel(request, model_id):
|
||||||
|
task = MyModel.objects.get(pk=model_id)
|
||||||
|
key = ShareKey.objects.create(pk=get_random_string(40),
|
||||||
|
expiration_seconds=60*60*24, # 1 day
|
||||||
|
location = task.get_absolute_url(),
|
||||||
|
)
|
||||||
|
key.save()
|
||||||
|
return render(request, 'share.html', {"key":key})
|
||||||
|
|
||||||
# Utility to get stroke data in a JSON response
|
# Utility to get stroke data in a JSON response
|
||||||
class JSONResponse(HttpResponse):
|
class JSONResponse(HttpResponse):
|
||||||
def __init__(self, data, **kwargs):
|
def __init__(self, data, **kwargs):
|
||||||
@@ -439,6 +491,9 @@ def getrequestplanrower(request,rowerid=0,userid=0,notpermanent=False):
|
|||||||
except Rower.DoesNotExist:
|
except Rower.DoesNotExist:
|
||||||
raise Http404("Rower doesn't exist")
|
raise Http404("Rower doesn't exist")
|
||||||
|
|
||||||
|
if 'shared' in request.session and request.session['shared']:
|
||||||
|
return r
|
||||||
|
|
||||||
if r.user != request.user and not can_plan_user(request.user,r ):
|
if r.user != request.user and not can_plan_user(request.user,r ):
|
||||||
request.session['rowerid'] = r.id
|
request.session['rowerid'] = r.id
|
||||||
raise PermissionDenied("You have no access to this user")
|
raise PermissionDenied("You have no access to this user")
|
||||||
|
|||||||
Reference in New Issue
Block a user