Various fixes
This commit is contained in:
1395
bootstrapTheme/static/js/bootstrap-datepicker.js
vendored
Normal file
1395
bootstrapTheme/static/js/bootstrap-datepicker.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
5
bootstrapTheme/static/js/bootstrap-timepicker.js
vendored
Normal file
5
bootstrapTheme/static/js/bootstrap-timepicker.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -8,19 +8,16 @@ object: {{route.event}} {% endcomment %} {% load sekizai_tags static %}
|
|||||||
media="screen"
|
media="screen"
|
||||||
/>{% endaddtoblock %} {% addtoblock "js" strip %}
|
/>{% endaddtoblock %} {% addtoblock "js" strip %}
|
||||||
<script
|
<script
|
||||||
|
async
|
||||||
type="text/javascript"
|
type="text/javascript"
|
||||||
src="https://maps.googleapis.com/maps/api/js?key={{ GOOGLE_MAPS_API_KEY }}&language=de"
|
src="https://maps.googleapis.com/maps/api/js?key={{ GOOGLE_MAPS_API_KEY }}&language=de&loading=async&callback=initRouteMap"
|
||||||
></script>
|
></script>
|
||||||
{% endaddtoblock %} {% addtoblock "js" %}
|
{% endaddtoblock %} {% addtoblock "js" %}
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
function OpenWindowControl(controlDiv, map) {
|
function OpenWindowControl(controlDiv, map) {
|
||||||
// Set CSS styles for the DIV containing the control
|
|
||||||
// Setting padding to 5 px will offset the control
|
|
||||||
// from the edge of the map
|
|
||||||
controlDiv.style.paddingTop = '6px';
|
controlDiv.style.paddingTop = '6px';
|
||||||
|
|
||||||
// Set CSS for the control border
|
|
||||||
var controlUI = document.createElement('div');
|
var controlUI = document.createElement('div');
|
||||||
controlUI.style.backgroundColor = 'white';
|
controlUI.style.backgroundColor = 'white';
|
||||||
controlUI.style.borderStyle = 'solid';
|
controlUI.style.borderStyle = 'solid';
|
||||||
@@ -30,7 +27,6 @@ object: {{route.event}} {% endcomment %} {% load sekizai_tags static %}
|
|||||||
controlUI.title = 'Fenster mit Konzert Info anzeigen';
|
controlUI.title = 'Fenster mit Konzert Info anzeigen';
|
||||||
controlDiv.appendChild(controlUI);
|
controlDiv.appendChild(controlUI);
|
||||||
|
|
||||||
// Set CSS for the control interior
|
|
||||||
var controlText = document.createElement('div');
|
var controlText = document.createElement('div');
|
||||||
controlText.style.fontFamily = 'Arial,sans-serif';
|
controlText.style.fontFamily = 'Arial,sans-serif';
|
||||||
controlText.style.fontSize = '12px';
|
controlText.style.fontSize = '12px';
|
||||||
@@ -39,30 +35,29 @@ object: {{route.event}} {% endcomment %} {% load sekizai_tags static %}
|
|||||||
controlText.innerHTML = 'Konzert Info anzeigen';
|
controlText.innerHTML = 'Konzert Info anzeigen';
|
||||||
controlUI.appendChild(controlText);
|
controlUI.appendChild(controlText);
|
||||||
|
|
||||||
google.maps.event.addDomListener(controlUI, 'click', function() {
|
controlUI.addEventListener('click', function() {
|
||||||
$("#map_box").show();
|
$("#map_box").show();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ToggleTargetControl(controlDiv, map, directionsDisplay, getRouteBounds) {
|
||||||
function ShowTargetControl(controlDiv, map) {
|
|
||||||
// Set CSS styles for the DIV containing the control
|
|
||||||
// Setting padding to 5 px will offset the control
|
|
||||||
// from the edge of the map
|
|
||||||
controlDiv.style.paddingTop = '6px';
|
controlDiv.style.paddingTop = '6px';
|
||||||
controlDiv.style.paddingRight = '6px';
|
controlDiv.style.paddingRight = '6px';
|
||||||
|
|
||||||
// Set CSS for the control border
|
var showingLocation = false;
|
||||||
|
{% if not route.event.map_location %}
|
||||||
|
var geocodedLocation = null;
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
var controlUI = document.createElement('div');
|
var controlUI = document.createElement('div');
|
||||||
controlUI.style.backgroundColor = 'white';
|
controlUI.style.backgroundColor = 'white';
|
||||||
controlUI.style.borderStyle = 'solid';
|
controlUI.style.borderStyle = 'solid';
|
||||||
controlUI.style.borderWidth = '1px';
|
controlUI.style.borderWidth = '1px';
|
||||||
controlUI.style.cursor = 'pointer';
|
controlUI.style.cursor = 'pointer';
|
||||||
controlUI.style.textAlign = 'center';
|
controlUI.style.textAlign = 'center';
|
||||||
controlUI.title = 'Zum Zielpunkt springen';
|
controlUI.title = 'Zwischen Route und Konzertort wechseln';
|
||||||
controlDiv.appendChild(controlUI);
|
controlDiv.appendChild(controlUI);
|
||||||
|
|
||||||
// Set CSS for the control interior
|
|
||||||
var controlText = document.createElement('div');
|
var controlText = document.createElement('div');
|
||||||
controlText.style.fontFamily = 'Arial,sans-serif';
|
controlText.style.fontFamily = 'Arial,sans-serif';
|
||||||
controlText.style.fontSize = '12px';
|
controlText.style.fontSize = '12px';
|
||||||
@@ -71,83 +66,105 @@ object: {{route.event}} {% endcomment %} {% load sekizai_tags static %}
|
|||||||
controlText.innerHTML = 'Konzertort anzeigen';
|
controlText.innerHTML = 'Konzertort anzeigen';
|
||||||
controlUI.appendChild(controlText);
|
controlUI.appendChild(controlText);
|
||||||
|
|
||||||
google.maps.event.addDomListener(controlUI, 'click', function()
|
function goToLocation() {
|
||||||
{
|
|
||||||
{% if not route.event.map_location %}
|
{% if not route.event.map_location %}
|
||||||
|
if (geocodedLocation) {
|
||||||
|
map.setMapTypeId(google.maps.MapTypeId.HYBRID);
|
||||||
|
map.setZoom(15);
|
||||||
|
map.setCenter(geocodedLocation);
|
||||||
|
} else {
|
||||||
var geocoder = new google.maps.Geocoder();
|
var geocoder = new google.maps.Geocoder();
|
||||||
geocoder.geocode({"address": "{{ route.event.location }}", "region": "de"}, function(results, status) {
|
geocoder.geocode({"address": "{{ route.event.location }}", "region": "de"}, function(results, status) {
|
||||||
if (status === google.maps.GeocoderStatus.OK) {
|
if (status === google.maps.GeocoderStatus.OK) {
|
||||||
|
geocodedLocation = results[0].geometry.location;
|
||||||
map.setMapTypeId(google.maps.MapTypeId.HYBRID);
|
map.setMapTypeId(google.maps.MapTypeId.HYBRID);
|
||||||
map.setZoom(15);
|
map.setZoom(15);
|
||||||
map.setCenter(results[0].geometry.location);
|
map.setCenter(geocodedLocation);
|
||||||
} else {
|
} else {
|
||||||
console.error("Geocoding failed: " + status);
|
console.error("Geocoding failed: " + status);
|
||||||
|
// revert toggle state on failure
|
||||||
|
showingLocation = false;
|
||||||
|
controlText.innerHTML = 'Konzertort anzeigen';
|
||||||
|
directionsDisplay.setMap(map);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
{% else %}
|
{% else %}
|
||||||
var loc = new google.maps.LatLng({{ route.event.map_location }});
|
var loc = new google.maps.LatLng({{ route.event.map_location }});
|
||||||
map.setMapTypeId(google.maps.MapTypeId.HYBRID);
|
map.setMapTypeId(google.maps.MapTypeId.HYBRID);
|
||||||
map.setZoom(20);
|
map.setZoom(20);
|
||||||
map.setCenter(loc);
|
map.setCenter(loc);
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
}
|
||||||
|
|
||||||
|
controlUI.addEventListener('click', function() {
|
||||||
|
if (!showingLocation) {
|
||||||
|
showingLocation = true;
|
||||||
|
controlText.innerHTML = 'Route anzeigen';
|
||||||
|
directionsDisplay.setMap(null);
|
||||||
|
goToLocation();
|
||||||
|
} else {
|
||||||
|
showingLocation = false;
|
||||||
|
controlText.innerHTML = 'Konzertort anzeigen';
|
||||||
|
directionsDisplay.setMap(map);
|
||||||
|
map.setMapTypeId(google.maps.MapTypeId.ROADMAP);
|
||||||
|
var bounds = getRouteBounds();
|
||||||
|
if (bounds) {
|
||||||
|
map.fitBounds(bounds);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).ready(function() {
|
function initRouteMap() {
|
||||||
|
var m = document.getElementById("map");
|
||||||
var m = $("#map")[0];
|
|
||||||
|
|
||||||
var myOptions = {
|
var myOptions = {
|
||||||
zoom: 10,
|
zoom: 10,
|
||||||
mapTypeId: google.maps.MapTypeId.ROAD,
|
mapTypeId: google.maps.MapTypeId.ROADMAP,
|
||||||
zoomControl: false,
|
zoomControl: false,
|
||||||
panControl: false,
|
|
||||||
streetViewControl: false,
|
streetViewControl: false,
|
||||||
scrollwheel: false
|
scrollwheel: false
|
||||||
}
|
};
|
||||||
var directionsService = new google.maps.DirectionsService();
|
|
||||||
var directionsDisplay = new google.maps.DirectionsRenderer();
|
|
||||||
var map = new google.maps.Map(m, myOptions);
|
var map = new google.maps.Map(m, myOptions);
|
||||||
|
|
||||||
|
var directionsService = new google.maps.DirectionsService();
|
||||||
|
var directionsDisplay = new google.maps.DirectionsRenderer();
|
||||||
directionsDisplay.setMap(map);
|
directionsDisplay.setMap(map);
|
||||||
|
|
||||||
|
var routeBounds = null;
|
||||||
|
|
||||||
var request = {
|
var request = {
|
||||||
origin: "{{route.origin}}",
|
origin: "{{route.origin}}",
|
||||||
destination: "{{route.destination}}",
|
destination: "{{route.destination}}",
|
||||||
travelMode: google.maps.TravelMode.DRIVING
|
travelMode: google.maps.TravelMode.DRIVING
|
||||||
}
|
};
|
||||||
|
|
||||||
directionsService.route(request, function(response, status) {
|
directionsService.route(request, function(response, status) {
|
||||||
if (status === google.maps.DirectionsStatus.OK) {
|
if (status === google.maps.DirectionsStatus.OK) {
|
||||||
directionsDisplay.setDirections(response);
|
directionsDisplay.setDirections(response);
|
||||||
|
|
||||||
var leg = response.routes[0].legs[0];
|
var leg = response.routes[0].legs[0];
|
||||||
|
|
||||||
$("#route_duration").html(leg.duration.text);
|
$("#route_duration").html(leg.duration.text);
|
||||||
$("#route_distance").html(leg.distance.text);
|
$("#route_distance").html(leg.distance.text);
|
||||||
|
routeBounds = response.routes[0].bounds;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
var showInfoControlDiv = document.createElement('div');
|
var showInfoControlDiv = document.createElement('div');
|
||||||
var showInfoControl = new OpenWindowControl(showInfoControlDiv, map);
|
new OpenWindowControl(showInfoControlDiv, map);
|
||||||
showInfoControlDiv.index = 1;
|
showInfoControlDiv.index = 1;
|
||||||
map.controls[google.maps.ControlPosition.TOP_RIGHT].push(showInfoControlDiv);
|
map.controls[google.maps.ControlPosition.TOP_RIGHT].push(showInfoControlDiv);
|
||||||
|
|
||||||
var showTargetControlDiv = document.createElement('div');
|
var showTargetControlDiv = document.createElement('div');
|
||||||
var showTargetControl = new ShowTargetControl(showTargetControlDiv, map);
|
new ToggleTargetControl(showTargetControlDiv, map, directionsDisplay, function() { return routeBounds; });
|
||||||
|
|
||||||
showTargetControlDiv.index = 2;
|
showTargetControlDiv.index = 2;
|
||||||
map.controls[google.maps.ControlPosition.TOP_RIGHT].push(showTargetControlDiv);
|
map.controls[google.maps.ControlPosition.TOP_RIGHT].push(showTargetControlDiv);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
$("#map_box a").click(function() {
|
$("#map_box a").click(function() {
|
||||||
$("#map_box").hide();
|
$("#map_box").hide();
|
||||||
map.setOptions({ scrollwheel: true });
|
map.setOptions({ scrollwheel: true });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
|
||||||
</script>
|
</script>
|
||||||
{% endaddtoblock %}
|
{% endaddtoblock %}
|
||||||
|
|
||||||
|
|||||||
7
eventplanner_gcal/admin.py
Normal file
7
eventplanner_gcal/admin.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
from .models import GCalMapping, GCalPushChannel, UserGCalCoupling
|
||||||
|
|
||||||
|
admin.site.register(UserGCalCoupling)
|
||||||
|
admin.site.register(GCalMapping)
|
||||||
|
admin.site.register(GCalPushChannel)
|
||||||
@@ -79,11 +79,20 @@ def create_gcal_service_object():
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _invalidate_service_on_error(exc):
|
||||||
|
"""Reset the cached service object so the next call retries credential loading."""
|
||||||
|
global _service_object
|
||||||
|
logger.warning(f"Invalidating cached GCal service due to error: {exc}")
|
||||||
|
_service_object = None
|
||||||
|
|
||||||
|
|
||||||
def get_service_object():
|
def get_service_object():
|
||||||
"""Get or create the Google Calendar service object."""
|
"""Get or create the Google Calendar service object."""
|
||||||
global _service_object
|
global _service_object
|
||||||
if _service_object is None:
|
if _service_object is None:
|
||||||
_service_object = create_gcal_service_object()
|
_service_object = create_gcal_service_object()
|
||||||
|
if _service_object is None:
|
||||||
|
logger.error("Failed to create Google Calendar service object")
|
||||||
return _service_object
|
return _service_object
|
||||||
|
|
||||||
|
|
||||||
@@ -93,6 +102,12 @@ def reset_service_object():
|
|||||||
_service_object = None
|
_service_object = None
|
||||||
|
|
||||||
|
|
||||||
|
def get_service_object_fresh():
|
||||||
|
"""Force-create a new service object, bypassing and replacing the cache."""
|
||||||
|
reset_service_object()
|
||||||
|
return get_service_object()
|
||||||
|
|
||||||
|
|
||||||
# --------------------- Building GCal event representation ------------------------------------
|
# --------------------- Building GCal event representation ------------------------------------
|
||||||
|
|
||||||
|
|
||||||
@@ -303,6 +318,7 @@ def delete_all_gcal_events(service=None):
|
|||||||
batch.execute()
|
batch.execute()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error deleting GCal events: {e}")
|
logger.error(f"Error deleting GCal events: {e}")
|
||||||
|
_invalidate_service_on_error(e)
|
||||||
|
|
||||||
GCalMapping.objects.all().delete()
|
GCalMapping.objects.all().delete()
|
||||||
|
|
||||||
@@ -389,6 +405,7 @@ def sync_from_local_to_google(service=None):
|
|||||||
batch.execute()
|
batch.execute()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error executing batch request: {e}")
|
logger.error(f"Error executing batch request: {e}")
|
||||||
|
_invalidate_service_on_error(e)
|
||||||
|
|
||||||
return len(events_to_create_django_id), len(events_to_delete_google_id)
|
return len(events_to_create_django_id), len(events_to_delete_google_id)
|
||||||
|
|
||||||
@@ -485,8 +502,23 @@ def check_gcal_subscription(
|
|||||||
|
|
||||||
callback_url = settings.GCAL_COUPLING["push_url"]
|
callback_url = settings.GCAL_COUPLING["push_url"]
|
||||||
|
|
||||||
try:
|
channels = GCalPushChannel.objects.filter(address=callback_url)
|
||||||
db_channel = GCalPushChannel.objects.get(address=callback_url)
|
|
||||||
|
if channels.count() > 1:
|
||||||
|
logger.warning(
|
||||||
|
f"Multiple GCal channels found for {callback_url}. Stopping all and creating fresh one."
|
||||||
|
)
|
||||||
|
for ch in channels:
|
||||||
|
ch.stop(service)
|
||||||
|
channels = GCalPushChannel.objects.none()
|
||||||
|
|
||||||
|
db_channel = channels.first()
|
||||||
|
|
||||||
|
if db_channel is None:
|
||||||
|
logger.info(f"No GCalCallback Channel exists yet for: {callback_url}")
|
||||||
|
sync_from_local_to_google(service)
|
||||||
|
GCalPushChannel.create_new(callback_url, service, time_to_live)
|
||||||
|
return
|
||||||
|
|
||||||
cur_time = int(time.time() * 1000)
|
cur_time = int(time.time() * 1000)
|
||||||
|
|
||||||
@@ -503,15 +535,6 @@ def check_gcal_subscription(
|
|||||||
logger.info(
|
logger.info(
|
||||||
"Google calendar subscription had expired - getting new subscription"
|
"Google calendar subscription had expired - getting new subscription"
|
||||||
)
|
)
|
||||||
# to get back in sync again we have to decide which data to take
|
|
||||||
# so we use the local data as reference
|
|
||||||
sync_from_local_to_google(service)
|
|
||||||
GCalPushChannel.create_new(callback_url, service, time_to_live)
|
|
||||||
|
|
||||||
except GCalPushChannel.DoesNotExist:
|
|
||||||
logger.info(f"No GCalCallback Channel exists yet for: {callback_url}")
|
|
||||||
# to get back in sync again we have to decide which data to take
|
|
||||||
# so we use the local data as reference
|
|
||||||
sync_from_local_to_google(service)
|
sync_from_local_to_google(service)
|
||||||
GCalPushChannel.create_new(callback_url, service, time_to_live)
|
GCalPushChannel.create_new(callback_url, service, time_to_live)
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
from django.core.management.base import NoArgsCommand
|
from django.core.management.base import BaseCommand
|
||||||
|
|
||||||
from eventplanner_gcal.google_sync import checkGCalSubscription
|
from eventplanner_gcal.google_sync import check_gcal_subscription
|
||||||
|
|
||||||
class Command(NoArgsCommand):
|
|
||||||
|
class Command(BaseCommand):
|
||||||
help = 'Checks if the GCal notification channel is still active'
|
help = 'Checks if the GCal notification channel is still active'
|
||||||
def handle_noargs(self, **options):
|
|
||||||
print ( "Checking Subscription")
|
def handle(self, *args, **options):
|
||||||
checkGCalSubscription()
|
self.stdout.write('Checking Subscription')
|
||||||
|
check_gcal_subscription()
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
from django.core.management.base import NoArgsCommand
|
from django.core.management.base import BaseCommand
|
||||||
from eventplanner_gcal.google_sync import deleteAllGCalEvents
|
|
||||||
|
|
||||||
class Command(NoArgsCommand):
|
from eventplanner_gcal.google_sync import delete_all_gcal_events
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
help = 'Delete all events in the google calendar created by this app'
|
help = 'Delete all events in the google calendar created by this app'
|
||||||
def handle_noargs(self, **options):
|
|
||||||
print ("Deleting all GCal Events.")
|
def handle(self, *args, **options):
|
||||||
nrOfDeletedEvents = deleteAllGCalEvents()
|
self.stdout.write('Deleting all GCal Events.')
|
||||||
print ("Deleted %d events. To Restore them from local database run gcal_sync" % (nrOfDeletedEvents, ) )
|
nr_deleted = delete_all_gcal_events()
|
||||||
|
self.stdout.write(f'Deleted {nr_deleted} events. To restore them run gcal_sync')
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
from django.core.management.base import NoArgsCommand
|
from django.core.management.base import BaseCommand
|
||||||
|
|
||||||
from eventplanner_gcal.google_sync import stopAllGCalSubscriptions
|
from eventplanner_gcal.google_sync import stop_all_gcal_subscriptions
|
||||||
|
|
||||||
class Command(NoArgsCommand):
|
|
||||||
|
class Command(BaseCommand):
|
||||||
help = 'Stops all GCal subscriptions'
|
help = 'Stops all GCal subscriptions'
|
||||||
def handle_noargs(self, **options):
|
|
||||||
stopAllGCalSubscriptions()
|
def handle(self, *args, **options):
|
||||||
|
stop_all_gcal_subscriptions()
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
from django.core.management.base import NoArgsCommand
|
from django.core.management.base import BaseCommand
|
||||||
|
|
||||||
from eventplanner_gcal.google_sync import syncFromLocalToGoogle
|
from eventplanner_gcal.google_sync import sync_from_local_to_google
|
||||||
|
|
||||||
class Command(NoArgsCommand):
|
|
||||||
|
class Command(BaseCommand):
|
||||||
help = 'Synchronize Google Calendar with locally stored Events'
|
help = 'Synchronize Google Calendar with locally stored Events'
|
||||||
def handle_noargs(self, **options):
|
|
||||||
print ( "Running Sync")
|
def handle(self, *args, **options):
|
||||||
created, deleted = syncFromLocalToGoogle()
|
self.stdout.write('Running Sync')
|
||||||
print ( "Created %d and deleted %d events" % (created,deleted) )
|
created, deleted = sync_from_local_to_google()
|
||||||
|
self.stdout.write(f'Created {created} and deleted {deleted} events')
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ Views for Google Calendar integration management.
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.shortcuts import redirect, render
|
from django.shortcuts import redirect, render
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
@@ -18,12 +19,14 @@ from .models import UserGCalCoupling
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
def run_sync(request):
|
def run_sync(request):
|
||||||
"""Manually trigger a sync from local to Google Calendar."""
|
"""Manually trigger a sync from local to Google Calendar."""
|
||||||
sync_from_local_to_google()
|
sync_from_local_to_google()
|
||||||
return redirect("/")
|
return redirect("/")
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
def manage(request):
|
def manage(request):
|
||||||
"""
|
"""
|
||||||
View for managing Google Calendar integration settings.
|
View for managing Google Calendar integration settings.
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/jquery-ui.min.js"></script>
|
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/jquery-ui.min.js"></script>
|
||||||
{% endaddtoblock %} {% addtoblock "css" strip %}<link
|
{% endaddtoblock %} {% addtoblock "css" strip %}<link
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
href="{{STATIC_URL}}/css/jquery-ui-1.8.21.custom.css"
|
href="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/themes/smoothness/jquery-ui.css"
|
||||||
type="text/css"
|
type="text/css"
|
||||||
media="screen"
|
media="screen"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ def change_password(request):
|
|||||||
|
|
||||||
def logout_view(request):
|
def logout_view(request):
|
||||||
logout(request)
|
logout(request)
|
||||||
return redirect(login_view)
|
return redirect('musicians:login')
|
||||||
|
|
||||||
|
|
||||||
def userlistForAutocompletion(request):
|
def userlistForAutocompletion(request):
|
||||||
|
|||||||
@@ -22,3 +22,5 @@ reportlab>=4.2
|
|||||||
|
|
||||||
# Image handling
|
# Image handling
|
||||||
Pillow>=10.4
|
Pillow>=10.4
|
||||||
|
|
||||||
|
dotenv
|
||||||
Reference in New Issue
Block a user