diff --git a/.gitignore b/.gitignore index 9c92758..9f0ffaa 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,6 @@ /.idea /._bootstrapTemplates /env +.env +/blechreiz/static_collection +/blechreiz/media/user_images \ No newline at end of file diff --git a/blechreiz/context_processors.py b/blechreiz/context_processors.py new file mode 100644 index 0000000..909894c --- /dev/null +++ b/blechreiz/context_processors.py @@ -0,0 +1,7 @@ +import os + + +def google_maps(request): + return { + "GOOGLE_MAPS_API_KEY": os.environ.get("GOOGLE_MAPS_API_KEY", ""), + } diff --git a/blechreiz/settings.py b/blechreiz/settings.py index 624ebbe..4e38710 100644 --- a/blechreiz/settings.py +++ b/blechreiz/settings.py @@ -6,6 +6,13 @@ from pathlib import Path BASE_DIR = Path(__file__).resolve().parent PROJECT_PATH = os.path.abspath(os.path.dirname(__file__)) +# Load environment variables from .env file if python-dotenv is installed +try: + from dotenv import load_dotenv + load_dotenv(os.path.join(PROJECT_PATH, ".env")) +except ImportError: + pass + # Django settings for blechreiz project. @@ -32,7 +39,7 @@ DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" EMAIL_HOST = "smtp.blechreiz.com" EMAIL_HOST_USER = "m02b721a" -EMAIL_HOST_PASSWORD = "9Hp4WG5bZ2WVPX5z" +EMAIL_HOST_PASSWORD = os.environ.get("EMAIL_HOST_PASSWORD", "") EMAIL_USE_TLS = False @@ -83,7 +90,7 @@ STATICFILES_FINDERS = [ ] # Make this unique, and don't share it with anybody. -SECRET_KEY = "x$8&9s6t%eeg=wyhar87934wh_s$dbpm(73g4ho&n)9_wogj6p" +SECRET_KEY = os.environ["DJANGO_SECRET_KEY"] MIDDLEWARE = [ "django.middleware.security.SecurityMiddleware", @@ -119,7 +126,6 @@ TEMPLATES = [ "django.template.context_processors.media", "django.template.context_processors.static", "sekizai.context_processors.sekizai", - "blechreiz.context_processors.google_maps", ], }, }, @@ -165,18 +171,12 @@ GCAL_COUPLING = { "eventPrefix": "Blechreiz: ", "developerKey": "blechreiz-homepage", "clientId": "34462582242-4kpdvvbi27ajt4u22uitqurpve9o8ipj.apps.googleusercontent.com", - "client_secret": "y4t9XBrJdCODPTO5UvtONWWn", + "client_secret": os.environ.get("GCAL_CLIENT_SECRET", ""), "credentials_file": PROJECT_PATH + "/calendarCredentials.dat", "push_url": "https://blechreiz.bauer.technology/eventplanner_gcal/gcalApiCallback", } -# Google Maps API Key -# Get your API key from https://console.cloud.google.com/apis/credentials -# Enable the Maps JavaScript API and Geocoding API -GOOGLE_MAPS_API_KEY = "AIzaSyCf9Lm5ckjmVd08scTOd7fB1dC_UCoumKg" - - # Crispy Forms configuration CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5" CRISPY_TEMPLATE_PACK = "bootstrap5" diff --git a/eventplanner/views.py b/eventplanner/views.py index a69d81c..294f6a6 100644 --- a/eventplanner/views.py +++ b/eventplanner/views.py @@ -2,6 +2,7 @@ import datetime from crispy_forms.helper import FormHelper from crispy_forms.layout import Submit +from django.core.exceptions import PermissionDenied from django.forms import TextInput from django.forms.models import ModelForm from django.http import HttpResponse @@ -114,6 +115,8 @@ def events_grid(request): def deleteEvent(request, pk): + if not EventParticipation.isAdmin(request.user): + raise PermissionDenied Event.objects.get(pk=pk).delete() return redirect(events_grid) diff --git a/location_field/static/location_field/form.css b/location_field/static/location_field/form.css new file mode 100644 index 0000000..dce0f54 --- /dev/null +++ b/location_field/static/location_field/form.css @@ -0,0 +1,39 @@ + +/* Hide map dialog initially - jQuery UI dialog will show it */ +.map_dialog { + display: none; +} + +input.locationwidget +{ + cursor: pointer !important; + background-color: rgb(255, 255, 255) !important; +} + +.map_dialog .form-horizontal +{ + margin-top: 10px; +} + +.map_dialog input +{ + width: 390px; +} + + + +.map_dialog input.displayed_location_input +{ + width: 350px; +} + +.map_dialog .map_canvas img +{ + max-width: none; +} + +.map_dialog label +{ + margin-bottom: 0px; + display: inline; +} \ No newline at end of file diff --git a/location_field/static/location_field/form.js b/location_field/static/location_field/form.js new file mode 100644 index 0000000..5969e7d --- /dev/null +++ b/location_field/static/location_field/form.js @@ -0,0 +1,190 @@ +($ || django.jQuery)(function($){ + + + + // map: the div that is made to the map + // coordinate_field: input element where position & zoom is stored ( and read to init the map ) + function MapObject( mapDiv, coordinate_field, init_position, init_zoom ) + { + // Default Argument for init_position + init_position = typeof init_position !== 'undefined' ? init_position : new google.maps.LatLng( 49.340174,10.890595 ); + init_zoom = typeof init_zoom !== 'undefined' ? init_zoom : 16; + + + // --------------------------- Members -------------------------------------- + + this.map = new google.maps.Map( mapDiv, { + mapTypeId: google.maps.MapTypeId.HYBRID, + zoomControl: true, + streetViewControl: false, + zoomControlOptions: { style: google.maps.ZoomControlStyle.SMALL } + } ); + + this.geocoder = new google.maps.Geocoder(); + this.geocoder.region = "de"; + this.geocoder.location = init_position; + + this.marker = new google.maps.Marker({ + map: this.map, + position: init_position, + draggable: true + }); + + + // --------------------------- Methods -------------------------------------- + + this.placeMarkerUsingAddressString = function ( addressStr ) + { + var theObject = this; + this.geocode( addressStr , function(l) { + theObject.placeMarker( l ); + theObject.saveToInputField(); + }); + } + + this.placeMarker = function( location ) + { + this.marker.setPosition( location ); + this.map.setCenter( location ); + this.map.panTo( location ); + } + + + this.geocode = function(address, cb) + { + var result; + if (this.geocoder) { + this.geocoder.geocode({"address": address}, function(results, status) { + if (status == google.maps.GeocoderStatus.OK) { + cb(results[0].geometry.location); + } + }); + } + } + + this.geocode_reverse = function( cb) + { + if (this.geocoder) { + this.geocoder.geocode({"latLng": this.marker.getPosition() }, function(results, status) { + if (status == google.maps.GeocoderStatus.OK) { + cb( results ); + } + }); + } + } + + this.saveToInputField = function() + { + var p = this.marker.getPosition(); + coordinate_field.value = p.lat().toFixed(6) + "," + p.lng().toFixed(6) + "," + this.map.getZoom() ; + } + + this.loadFromInputField = function() + { + if ( coordinate_field.value ) // Already values in the coordinate field + { + var l = coordinate_field.value.split(/,/); + if (l.length > 1) + { + this.placeMarker( new google.maps.LatLng(l[0], l[1]) ); + this.map.setZoom( parseInt( l[2] ) ); + } + } + else + { + this.placeMarker( init_position ); + this.map.setZoom( 16 ); + } + } + + + // --------------------------- Constructor ------------------------------------- + + this.init = function() + { + var theObject = this; + // Event handling + google.maps.event.addListener( this.marker, 'dragend', function(mouseEvent) { + theObject.saveToInputField(); + }); + + google.maps.event.addListener( this.map, 'click', function(mouseEvent){ + theObject.placeMarker( mouseEvent.latLng ); + theObject.saveToInputField(); + }); + + google.maps.event.addListener( this.map, 'zoom_changed', function(mouseEvent){ + theObject.saveToInputField(); + }); + + theObject.loadFromInputField(); + } + + this.init(); + } + + + $(".map_dialog").dialog( { + modal: true, + width: 650, + height: 730, + resizeable: false, + autoOpen: false + } ); + + + $('input[data-location-widget]').click( function() { + + var formCoordField = $(this); + var $basedField = $( formCoordField.attr('data-based-field') ); + var $dialogElement = $( formCoordField.attr('data-dialog-id') ); + + var $dialogCoordField = $dialogElement.find(".coordinate_input"); + var $dialogLocationField = $dialogElement.find(".displayed_location_input"); + $dialogLocationField.val( $basedField.val() ); + $dialogCoordField.val( formCoordField.val() ); + + // Init Map if not yet initialized: + var map; + if ( ! $dialogElement[0].map ) + { + map = $( formCoordField.attr('data-map') )[0]; + var zoom = parseInt( formCoordField.attr('data-zoom') ); + + // Add Buttons + $dialogElement.dialog( "option", "buttons", { + Abbrechen: function() { + $( this ).dialog( "close" ); + }, + Ok: function() { + $(formCoordField).val( $dialogCoordField.val() ); + $basedField.val( $dialogLocationField.val() ); + $( this ).dialog( "close" ); + } } + ); + + // Init Map + $dialogElement[0].map = new MapObject( map, $dialogCoordField[0] ); + } + $dialogElement[0].map.placeMarkerUsingAddressString( $dialogLocationField.val() ); + + $dialogLocationField.on("keypress", function(e) { + if ( e.keyCode == 13 ) { // enter + $dialogElement[0].map.placeMarkerUsingAddressString( $dialogLocationField.val() ); + return false; + } + }); + + $dialogElement.find(".coord_to_display_button").click( function() { + $dialogElement[0].map.geocode_reverse( function(result) { + $dialogLocationField.val( result[0].formatted_address ); + }); + }); + + + $dialogElement.dialog('open'); + }); + + + +}); diff --git a/location_field/widgets.py b/location_field/widgets.py index a2e3991..5d233f8 100644 --- a/location_field/widgets.py +++ b/location_field/widgets.py @@ -1,6 +1,5 @@ import os -from django.conf import settings from django.forms import widgets from django.utils.safestring import mark_safe @@ -57,7 +56,7 @@ class LocationWidget(widgets.TextInput): @property def media(self): - api_key = getattr(settings, "GOOGLE_MAPS_API_KEY", "") + api_key = os.environ.get("GOOGLE_MAPS_API_KEY", "") maps_url = f"https://maps.googleapis.com/maps/api/js?key={api_key}&language=de" return widgets.Media( diff --git a/musicians/views.py b/musicians/views.py index 9558ef9..761b156 100644 --- a/musicians/views.py +++ b/musicians/views.py @@ -5,6 +5,7 @@ from django import forms from django.contrib.auth import authenticate, login, logout from django.contrib.auth.models import User from django.contrib.auth.views import PasswordChangeView +from django.core.exceptions import PermissionDenied from django.http import HttpResponse, HttpResponseRedirect from django.shortcuts import get_object_or_404, redirect, render from django.urls import reverse_lazy @@ -76,6 +77,9 @@ def own_profile(request): def user_edit(request, username): musician = get_object_or_404(Musician, user__username=username) + if request.user != musician.user and not request.user.is_staff: + raise PermissionDenied + if request.method == "POST": form = UserEditForm(request.POST, instance=musician) if form.is_valid(): @@ -93,7 +97,20 @@ class MusicianUpdate(UpdateView): model = Musician template_name = "musicians/musician_edit.html" success_url = "/books/" - fields = "__all__" + fields = [ + "image", + "small_image", + "instrument", + "birthday", + "street", + "city", + "zip_code", + "phone_home", + "phone_mobile", + "phone_work", + "position", + "public_description", + ] def addressbook(request): @@ -137,11 +154,11 @@ def login_view(request): result["err"] = "" if user is not None: if user.is_active: - if not request.POST.get("remember", None): - # Expire in one year + if request.POST.get("remember", None): + # "Remember me" checked: keep session for one year request.session.set_expiry(timedelta(weeks=52)) else: - # Expire on browser close + # No "remember me": expire on browser close request.session.set_expiry(0) login(request, user) diff --git a/website/views.py b/website/views.py index b7d7678..c5bc05d 100644 --- a/website/views.py +++ b/website/views.py @@ -1,3 +1,5 @@ +import os + from django.contrib.auth.decorators import login_required from django.shortcuts import render @@ -10,7 +12,9 @@ from eventplanner.snippets import ( @login_required def home_view(request): - context = {} + context = { + "GOOGLE_MAPS_API_KEY": os.environ.get("GOOGLE_MAPS_API_KEY", ""), + } # Event participation for slider text if EventParticipation.isMember(request.user):