diff --git a/events/forms.py b/events/forms.py
index 078a49a..01a4b73 100644
--- a/events/forms.py
+++ b/events/forms.py
@@ -183,6 +183,10 @@ class TeamDefinitionForm(forms.ModelForm):
class DeleteTeamForm(forms.Form):
confirm = forms.BooleanField(label="Yes, delete team", required=True)
+class TeamContactForm(forms.Form):
+ to = forms.ChoiceField(label=_(""))
+ body = forms.CharField(label=_(""), widget=forms.widgets.Textarea)
+
class TeamEventForm(forms.ModelForm):
recurrences = recurrence.forms.RecurrenceField(label="Repeat", required=False)
class Meta:
diff --git a/get_together/templates/get_together/base.html b/get_together/templates/get_together/base.html
index 1f52352..37776fd 100644
--- a/get_together/templates/get_together/base.html
+++ b/get_together/templates/get_together/base.html
@@ -48,10 +48,10 @@
-
- Events{% if request.resolver_match.url_name == "events" %} (current){% endif %}
+ Events{% if request.resolver_match.url_name == "events" %} (current){% endif %}
-
- Teams{% if request.resolver_match.url_name == "teams" %} (current){% endif %}
+ Teams{% if request.resolver_match.url_name == "teams" %} (current){% endif %}
{% comment %}
-
@@ -66,8 +66,8 @@
{% if request.user.profile.realname %}{{ request.user.profile.realname }}{% else %}{{ request.user.username }}{% endif %}
{% else %}
diff --git a/get_together/templates/get_together/emails/member_contact.html b/get_together/templates/get_together/emails/member_contact.html
new file mode 100644
index 0000000..049d099
--- /dev/null
+++ b/get_together/templates/get_together/emails/member_contact.html
@@ -0,0 +1,13 @@
+{% extends "get_together/emails/base.html" %}
+
+{% block content %}
+Message from {{team.name|striptags}}
+
+Sender: {{ sender|striptags }}
+
+{{body|striptags}}
+
+
+Go to the team page.
+
+{% endblock %}
diff --git a/get_together/templates/get_together/emails/member_contact.txt b/get_together/templates/get_together/emails/member_contact.txt
new file mode 100644
index 0000000..022e043
--- /dev/null
+++ b/get_together/templates/get_together/emails/member_contact.txt
@@ -0,0 +1,10 @@
+{% block content %}
+== Message from {{team.name}} ==
+
+Sender: {{ sender }}
+
+{{ body|striptags }}
+
+Team page: https://{{site.domain}}{% url 'show-team' team.id %}
+
+{% endblock %}
diff --git a/get_together/templates/get_together/events/show_event.html b/get_together/templates/get_together/events/show_event.html
index 6c5f069..be18c94 100644
--- a/get_together/templates/get_together/events/show_event.html
+++ b/get_together/templates/get_together/events/show_event.html
@@ -102,17 +102,6 @@
{{ event.name }}
Hosted by {{ team.name }}
- {% if can_edit_event %}
-
-
-
-
- {% endif %}
{% if settings.SOCIAL_AUTH_TWITTER_KEY %}
{% endif %}
@@ -125,6 +114,17 @@
{% if not is_attending %}
Attend
{% endif %}
+ {% if can_edit_event %}
+
+
+
+
+ {% endif %}
{{ event.summary|markdown }}
diff --git a/get_together/templates/get_together/teams/manage_members.html b/get_together/templates/get_together/teams/manage_members.html
new file mode 100644
index 0000000..b613b7b
--- /dev/null
+++ b/get_together/templates/get_together/teams/manage_members.html
@@ -0,0 +1,82 @@
+{% extends "get_together/base.html" %}
+{% load markup tz %}
+
+{% block add_to_title %} | {{team.name}}{% endblock %}
+
+{% block styles %}
+
+{% endblock %}
+
+{% block content %}
+
+
+
+
+
+
+
+ Name |
+ Email |
+ Role |
+ Joined |
+
+ {% for member in members %}
+
+ |
+ {{member.user}} |
+
+ {% if member.user.user.account.is_email_confirmed %}
+
+ {% endif %}
+ |
+ {{member.role_name}} |
+ {{member.joined_date}} |
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+{% endblock %}
+
+{% block javascript %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/get_together/templates/get_together/teams/show_team.html b/get_together/templates/get_together/teams/show_team.html
index aa0ca05..0ba4fe3 100644
--- a/get_together/templates/get_together/teams/show_team.html
+++ b/get_together/templates/get_together/teams/show_team.html
@@ -22,19 +22,19 @@
Welcome to {{ team.name }}
+
{% if can_edit_team %}
-
Edit Team
+
Edit Team
+
Manage Members
{% endif %}
{% if is_member %}
{% if not team.owner_profile == request.user.profile %}
Leave Team{% endif %}
{% else %}
Join Team
{% endif %}
-
iCal
-
+
-
-
+
{% if team.description %}
{{ team.description|markdown }} |
@@ -53,7 +53,9 @@
- Upcoming Events
+ Upcoming Events
+
+
{% for event in upcoming_events %}
@@ -65,7 +67,7 @@
{% if can_create_event %}
{% endif %}
diff --git a/get_together/urls.py b/get_together/urls.py
index 24da216..900227a 100644
--- a/get_together/urls.py
+++ b/get_together/urls.py
@@ -69,6 +69,7 @@ urlpatterns = [
path('team/
/+join/', event_views.join_team, name='join-team'),
path('team//+leave/', event_views.leave_team, name='leave-team'),
path('team//+delete/', views.delete_team, name='delete-team'),
+ path('team//+members/', views.manage_members, name='manage-members'),
path('team//events.ics', feeds.TeamEventsCalendar(), name='team-event-ical'),
path('+create-team/', views.start_new_team, name='create-team'),
diff --git a/get_together/views/events.py b/get_together/views/events.py
index 104f308..0a32c17 100644
--- a/get_together/views/events.py
+++ b/get_together/views/events.py
@@ -136,6 +136,7 @@ def create_event(request, team_id):
else:
return redirect('home')
+@login_required
def add_event_photo(request, event_id):
event = get_object_or_404(Event, id=event_id)
if not request.user.profile.can_edit_event(event):
@@ -165,6 +166,7 @@ def add_event_photo(request, event_id):
else:
return redirect('home')
+@login_required
def add_place_to_event(request, event_id):
event = get_object_or_404(Event, id=event_id)
if not request.user.profile.can_edit_event(event):
@@ -200,6 +202,7 @@ def add_place_to_event(request, event_id):
else:
return redirect('home')
+@login_required
def add_place_to_series(request, series_id):
series = get_object_or_404(EventSeries, id=series_id)
if not request.user.profile.can_edit_series(series):
@@ -232,6 +235,7 @@ def add_place_to_series(request, series_id):
else:
return redirect('home')
+@login_required
def edit_event(request, event_id):
event = get_object_or_404(Event, id=event_id)
@@ -276,6 +280,7 @@ def edit_event(request, event_id):
else:
return redirect('home')
+@login_required
def delete_event(request, event_id):
event = get_object_or_404(Event, id=event_id)
if not request.user.profile.can_edit_event(event):
@@ -308,6 +313,7 @@ def delete_event(request, event_id):
else:
return redirect('home')
+@login_required
def edit_series(request, series_id):
series = get_object_or_404(EventSeries, id=series_id)
@@ -339,6 +345,7 @@ def edit_series(request, series_id):
else:
return redirect('home')
+@login_required
def delete_series(request, series_id):
series = get_object_or_404(EventSeries, id=series_id)
if not request.user.profile.can_edit_series(series):
@@ -409,6 +416,7 @@ def create_common_event(request, org_slug):
else:
return redirect('home')
+@login_required
def create_common_event_team_select(request, event_id):
teams = request.user.profile.moderating
if len(teams) == 1:
diff --git a/get_together/views/speakers.py b/get_together/views/speakers.py
index adccacc..a7f6ab8 100644
--- a/get_together/views/speakers.py
+++ b/get_together/views/speakers.py
@@ -40,7 +40,7 @@ def show_speaker(request, speaker_id):
context = {
'speaker': speaker,
'talks': Talk.objects.filter(speaker=speaker),
- 'presentations': Presentation.objects.filter(talk__speaker=speaker, status=Presentation.ACCEPTED),
+ 'presentations': Presentation.objects.filter(talk__speaker=speaker, status=Presentation.ACCEPTED).order_by('-event__start_time'),
}
return render(request, 'get_together/speakers/show_speaker.html', context)
diff --git a/get_together/views/teams.py b/get_together/views/teams.py
index bd8d23a..5d0ad15 100644
--- a/get_together/views/teams.py
+++ b/get_together/views/teams.py
@@ -3,12 +3,17 @@ from django.utils.translation import ugettext_lazy as _
from django.contrib import messages
from django.contrib.auth import logout as logout_user
from django.contrib.auth.decorators import login_required
+from django.contrib.sites.models import Site
from django.shortcuts import render, redirect, get_object_or_404
from django.http import HttpResponse, JsonResponse
+from django.core.mail import send_mail
+from django.template.loader import get_template, render_to_string
+from django.conf import settings
+
from events.models.profiles import Organization, Team, UserProfile, Member
from events.models.events import Event, CommonEvent, Place, Attendee
-from events.forms import TeamForm, NewTeamForm, DeleteTeamForm
+from events.forms import TeamForm, NewTeamForm, DeleteTeamForm, TeamContactForm
from events import location
import datetime
@@ -51,6 +56,7 @@ def show_team(request, team_id, *args, **kwargs):
}
return render(request, 'get_together/teams/show_team.html', context)
+@login_required
def create_team(request, *args, **kwargs):
if request.method == 'GET':
form = NewTeamForm()
@@ -75,6 +81,7 @@ def create_team(request, *args, **kwargs):
else:
return redirect('home')
+@login_required
def edit_team(request, team_id):
team = get_object_or_404(Team, id=team_id)
if not request.user.profile.can_edit_team(team):
@@ -105,6 +112,7 @@ def edit_team(request, team_id):
else:
return redirect('home')
+@login_required
def delete_team(request, team_id):
team = get_object_or_404(Team, id=team_id)
if not request.user.profile.can_edit_team(team):
@@ -133,6 +141,86 @@ def delete_team(request, team_id):
else:
return redirect('home')
+
+@login_required
+def manage_members(request, team_id):
+ team = get_object_or_404(Team, id=team_id)
+ if not request.user.profile.can_edit_team(team):
+ messages.add_message(request, messages.WARNING, message=_('You can not manage this team\'s members.'))
+ return redirect('show-team', team_id)
+
+ members = Member.objects.filter(team=team).order_by('user__realname')
+ member_choices = [(member.id, member.user) for member in members if member.user.user.account.is_email_confirmed]
+ default_choices = [('all', 'All Members (%s)' % len(member_choices)), ('admins', 'Only Administrators')]
+ if request.method == 'POST':
+ contact_form = TeamContactForm(request.POST)
+ contact_form.fields['to'].choices = default_choices + member_choices
+ if contact_form.is_valid():
+ to = contact_form.cleaned_data['to']
+ body = contact_form.cleaned_data['body']
+ if to is not 'admins' and not request.user.profile.can_edit_team(team):
+ messages.add_message(request, messages.WARNING, message=_('You can not contact this team\'s members.'))
+ return redirect('show-team', team_id)
+ if to == 'all':
+ count = 0
+ for member in Member.objects.filter(team=team):
+ if member.user.user.account.is_email_confirmed:
+ contact_member(member, body, request.user.profile)
+ count += 1
+ messages.add_message(request, messages.SUCCESS, message=_('Emailed %s users' % count))
+ elif to == 'admins':
+ count = 0
+ for member in Member.objects.filter(team=team, role=Member.ADMIN):
+ if member.user.user.account.is_email_confirmed:
+ contact_member(member, body, request.user.profile)
+ count += 1
+ messages.add_message(request, messages.SUCCESS, message=_('Emailed %s users' % count))
+ else:
+ try:
+ member = Member.objects.get(id=to)
+ contact_member(member, body, request.user.profile)
+ messages.add_message(request, messages.SUCCESS, message=_('Emailed %s' % member.user))
+ except Member.DoesNotExist:
+ messages.add_message(request, messages.ERROR, message=_('Error sending message: Unknown user (%s)'%to))
+ pass
+ return redirect('manage-members', team_id)
+ else:
+ messages.add_message(request, messages.ERROR, message=_('Error sending message: %s' % contact_form.errors))
+ else:
+ contact_form = TeamContactForm()
+ contact_form.fields['to'].choices = default_choices + member_choices
+
+ context = {
+ 'team': team,
+ 'members': members,
+ 'contact_form': contact_form,
+ 'can_edit_team': request.user.profile.can_edit_team(team),
+ }
+ return render(request, 'get_together/teams/manage_members.html', context)
+
+
+def contact_member(member, body, sender):
+ context = {
+ 'sender': sender,
+ 'team': member.team,
+ 'body': body,
+ 'site': Site.objects.get(id=1),
+ }
+ email_subject = '[GetTogether] Message from %s' % member.team
+ email_body_text = render_to_string('get_together/emails/member_contact.txt', context)
+ email_body_html = render_to_string('get_together/emails/member_contact.html', context)
+ email_recipients = [member.user.user.email]
+ email_from = getattr(settings, 'DEFAULT_FROM_EMAIL', 'noreply@gettogether.community')
+
+ send_mail(
+ from_email=email_from,
+ html_message=email_body_html,
+ message=email_body_text,
+ recipient_list=email_recipients,
+ subject=email_subject,
+ fail_silently=True,
+ )
+
def show_org(request, org_slug):
org = get_object_or_404(Organization, slug=org_slug)
upcoming_events = CommonEvent.objects.filter(organization=org, end_time__gt=datetime.datetime.now()).order_by('start_time')
diff --git a/get_together/views/utils.py b/get_together/views/utils.py
index bd18090..02ca5e5 100644
--- a/get_together/views/utils.py
+++ b/get_together/views/utils.py
@@ -1,17 +1,5 @@
from django.utils.translation import ugettext_lazy as _
-from django.contrib import messages
-from django.contrib.auth import logout as logout_user
-from django.shortcuts import render, redirect
-from django.http import HttpResponse, JsonResponse
-
-from events.models.locale import City
-from events.models.events import Event, Place, Attendee
-from events.models.profiles import Team, UserProfile, Member
-from events.models.search import Searchable
-from events.forms import SearchForm
-
-from accounts.decorators import setup_wanted
from django.conf import settings
import datetime
@@ -20,16 +8,13 @@ import geocoder
import math
import traceback
-from .teams import *
-from .events import *
-from .places import *
-from .user import *
from .new_user import *
KM_PER_DEGREE_LAT = 110.574
KM_PER_DEGREE_LNG = 111.320 # At the equator
DEFAULT_NEAR_DISTANCE = 100 # kilometeres
+
def get_geoip(request):
client_ip = get_client_ip(request)
if client_ip == '127.0.0.1' or client_ip == 'localhost':
@@ -42,6 +27,7 @@ def get_geoip(request):
g = geocoder.ip(client_ip)
return g
+
def get_nearby_teams(request, near_distance=DEFAULT_NEAR_DISTANCE):
g = get_geoip(request)
if g.latlng is None or g.latlng[0] is None or g.latlng[1] is None:
@@ -59,6 +45,7 @@ def get_nearby_teams(request, near_distance=DEFAULT_NEAR_DISTANCE):
print("Error looking for local teams: ", e)
return Team.objects.none()
+
def get_client_ip(request):
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
@@ -66,3 +53,6 @@ def get_client_ip(request):
else:
ip = request.META.get('REMOTE_ADDR')
return ip
+
+
+