Skip to content

Commit 4651b3b

Browse files
authored
Merge pull request #933 from p2pu/2021-website-refresh
2021 website refresh
2 parents 86e3123 + e410bdc commit 4651b3b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+2543
-1497
lines changed

Diff for: .github/workflows/ci.yml

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ jobs:
1111
runs-on: ubuntu-latest
1212
steps:
1313
- uses: actions/checkout@v2
14+
with:
15+
submodules: true
1416
- name: Build docker image
1517
run: docker build -t p2pu/learning-circles .
1618
- name: start postgres container

Diff for: .gitmodules

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "p2pu-theme"]
2+
path = p2pu-theme
3+
url = https://github.com/p2pu/p2pu-theme.git

Diff for: Dockerfile

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
FROM node:lts-slim AS frontend
22
WORKDIR /opt/app/
33
COPY package.json /opt/app/
4+
COPY p2pu-theme/ /opt/app/p2pu-theme/
45
RUN npm install --quiet --production
56
COPY . /opt/app/
67
RUN npm run build

Diff for: contact/__init__.py

Whitespace-only changes.

Diff for: contact/tasks.py

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from django.conf import settings
2+
from django.core.mail import EmailMultiAlternatives
3+
4+
from celery import shared_task
5+
6+
from studygroups.email_helper import render_html_with_css
7+
from studygroups.utils import html_body_to_text
8+
from studygroups.utils import render_to_string_ctx
9+
10+
11+
@shared_task
12+
def send_contact_form_inquiry(email, name, content, source, organization=None):
13+
context = {
14+
"email": email,
15+
"name": name,
16+
"content": content,
17+
"source": source,
18+
"organization": organization,
19+
}
20+
21+
subject_template = 'contact/contact_email-subject.txt'
22+
html_email_template = 'contact/contact_email.html'
23+
subject = render_to_string_ctx(subject_template, context).strip(' \n')
24+
html_body = render_html_with_css(html_email_template, context)
25+
text_body = html_body_to_text(html_body)
26+
27+
to = [ settings.TEAM_EMAIL ]
28+
email = EmailMultiAlternatives(
29+
subject,
30+
text_body,
31+
settings.DEFAULT_FROM_EMAIL,
32+
to,
33+
)
34+
email.attach_alternative(html_body, 'text/html')
35+
email.send()

Diff for: contact/urls.py

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from django.conf.urls import url
2+
3+
from contact import views
4+
5+
urlpatterns = [
6+
url(r'^contact/$', views.ContactAPIView.as_view(), name='api_contact_form')
7+
]

Diff for: contact/views.py

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from rest_framework import serializers
2+
from rest_framework.views import APIView
3+
from rest_framework.response import Response
4+
from django import http
5+
6+
from .tasks import send_contact_form_inquiry
7+
8+
9+
# Serializers define the API representation.
10+
class ContactSerializer(serializers.Serializer):
11+
email = serializers.EmailField()
12+
name = serializers.CharField(max_length=255)
13+
content = serializers.CharField()
14+
source = serializers.CharField(max_length=255)
15+
organization = serializers.CharField(max_length=255, required=False)
16+
17+
def create(self, validated_data):
18+
return validated_data
19+
20+
21+
class ContactAPIView(APIView):
22+
authentication_classes = []
23+
permission_classes = []
24+
25+
def post(self, request, *args, **kwargs):
26+
serializer = ContactSerializer(data=request.data, context={'request': request})
27+
serializer.is_valid(raise_exception=True)
28+
# call async task to send email
29+
send_contact_form_inquiry.delay(**serializer.data)
30+
31+
if request.GET.get('next') and not request.is_ajax():
32+
# TODO should this be validated?
33+
return http.HttpResponseRedirect(request.GET.get('next'))
34+
35+
data = {"status": "sent"}
36+
return Response(data)

Diff for: custom_registration/forms.py

+6-3
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,15 @@
66
from django.forms import ValidationError
77
from django.contrib.auth import password_validation
88
from django.contrib.auth.forms import UserCreationForm
9+
from django.utils.safestring import mark_safe
10+
911
from studygroups.models import Profile
1012

1113

1214
class SignupForm(UserCreationForm):
13-
communication_opt_in = forms.BooleanField(required=False, initial=False, label=_('P2PU can contact me.'), help_text=_('Joining the community comes with an expectation that you would like to learn about upcoming events, new features, and updates from around the world. If you do not want to receive any of these messages, uncheck this box.'))
14-
interested_in_learning = forms.CharField(required=False, label=_('What are you interested in learning?'))
15+
communication_opt_in = forms.BooleanField(required=False, initial=False, label=_('P2PU can contact me'), help_text=_('Join our mailing list to learn about upcoming events, new courses, and news from the community. (Approximately six emails/year)'))
16+
17+
consent_opt_in = forms.BooleanField(required=True, initial=False, label=mark_safe(_('I consent to P2PU storing my data and accept the <a href="https://www.p2pu.org/en/terms/">terms of service</a>')), help_text=_('P2PU values your privacy and will never sell your data.'))
1518

1619
def __init__(self, *args, **kwargs):
1720
super(SignupForm, self).__init__(*args, **kwargs)
@@ -28,7 +31,7 @@ def clean(self):
2831

2932
class Meta:
3033
model = User
31-
fields = ['email', 'first_name', 'last_name', 'password1', 'password2', 'interested_in_learning', 'communication_opt_in']
34+
fields = ['first_name', 'last_name', 'email', 'password1', 'password2', 'communication_opt_in', 'consent_opt_in']
3235

3336

3437
class CustomPasswordResetForm(PasswordResetForm):

Diff for: custom_registration/models.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
from django.contrib.auth.models import User
22
from django.contrib.auth.tokens import PasswordResetTokenGenerator
3-
from django.utils import six
43
from django.utils import timezone
54
from django.utils.http import urlsafe_base64_encode
65
from django.utils.encoding import force_bytes
76
from django.conf import settings
7+
from django.core.mail import EmailMultiAlternatives
8+
9+
from studygroups.models import Profile
10+
from studygroups.utils import html_body_to_text
811
from studygroups.utils import render_to_string_ctx
912
from studygroups.email_helper import render_html_with_css
10-
from django.core.mail import EmailMultiAlternatives
1113

1214
import random
1315
import string
1416

15-
from studygroups.models import Profile
16-
from studygroups.utils import html_body_to_text
1717

1818
def create_user(email, first_name, last_name, password, communication_opt_in=False, interested_in_learning=''):
1919
""" Create a new user using the email as the username """

Diff for: custom_registration/tests.py

+6-3
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def test_account_create(self):
4040
"first_name": "firstname",
4141
"last_name": "lastname",
4242
"communication_opt_in": "on",
43-
"interested_in_learning": "python",
43+
"consent_opt_in": "on",
4444
"password1": "password",
4545
"password2": "password",
4646
}
@@ -49,7 +49,6 @@ def test_account_create(self):
4949
users = User.objects.filter(email__iexact=data['email'])
5050
self.assertEqual(users.count(), 1)
5151
profile = Profile.objects.get(user=users.first())
52-
self.assertEqual(profile.interested_in_learning, "python")
5352
self.assertEqual(profile.communication_opt_in, True)
5453
self.assertEqual(len(mail.outbox), 1) ##
5554
self.assertIn('Please confirm your email address', mail.outbox[0].subject)
@@ -64,6 +63,7 @@ def test_facilitator_signup_with_mixed_case(self):
6463
"password1": "password",
6564
"password2": "password",
6665
"communication_opt_in": "on",
66+
"consent_opt_in": "on",
6767
}
6868
resp = c.post('/en/accounts/register/', data)
6969
self.assertRedirects(resp, '/en/')
@@ -140,6 +140,7 @@ def test_api_account_create(self):
140140
"last_name": "Test",
141141
"password": "12345",
142142
"communication_opt_in": False,
143+
"consent_opt_in": True,
143144
"g-recaptcha-response": "blah",
144145
}
145146
resp = c.post(url, data=json.dumps(data), content_type='application/json')
@@ -197,7 +198,8 @@ def test_email_address_confirm(self):
197198
"last_name": "Test",
198199
"password": "12345",
199200
"g-recaptcha-response": "blah",
200-
"communication_opt_in": False
201+
"communication_opt_in": False,
202+
"consent_opt_in": True,
201203
}
202204
resp = c.post(url, data=json.dumps(data), content_type='application/json')
203205
self.assertEqual(resp.status_code, 200)
@@ -225,6 +227,7 @@ def test_send_new_user_email(self):
225227
"first_name": "firstname",
226228
"last_name": "lastname",
227229
"communication_opt_in": "on",
230+
"consent_opt_in": "on",
228231
"password1": "password",
229232
"password2": "password",
230233
}

Diff for: custom_registration/views.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from django.utils.http import urlsafe_base64_decode
66
from django.utils.translation import ugettext as _
77
from django.utils.decorators import method_decorator
8+
from django.views.decorators.csrf import csrf_exempt
89
from django.views.decorators.debug import sensitive_post_parameters
910
from django.views.decorators.cache import never_cache
1011
from django.views.generic.edit import FormView
@@ -71,7 +72,7 @@ def form_valid(self, form):
7172
return json_response(self.request, {"status": "error", "errors": '1011010010010010111'})
7273

7374
user = form.save(commit=False)
74-
user = create_user(user.email, user.first_name, user.last_name, form.cleaned_data['password1'], form.cleaned_data['communication_opt_in'], form.cleaned_data['interested_in_learning'], )
75+
user = create_user(user.email, user.first_name, user.last_name, form.cleaned_data['password1'], form.cleaned_data['communication_opt_in'])
7576
login(self.request, user)
7677
return http.HttpResponseRedirect(self.get_success_url())
7778

@@ -120,6 +121,7 @@ def _validate(value):
120121
return json_response(request, { "status": "created", "user": user.username });
121122

122123

124+
@method_decorator(csrf_exempt, name='dispatch')
123125
class AjaxLoginView(View):
124126
def post(self, request):
125127
post_schema = {
@@ -168,6 +170,9 @@ def get(self, request):
168170
{"text": "My team", "url": reverse('studygroups_organize')},
169171
]
170172

173+
if request.user.is_staff or TeamMembership.objects.active().filter(user=request.user):
174+
user_data['team'] = True
175+
171176
if request.user.is_staff:
172177
user_data["links"][:0] = [
173178
{"text": "Staff dash", "url": reverse('studygroups_staff_dash')},

Diff for: docker-compose.test.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ services:
1010
- 1080:80
1111
learning-circles:
1212
build: .
13-
image: learning-circles
13+
image: p2pu/learning-circles:local
1414
links:
1515
- selenium
1616
ports:

Diff for: e2e/tests/selenium/locators.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ class LearningCircleCreationPageLocators(object):
3434
FACILITATOR_CONCERNS_FIELD = (By.ID, "facilitator_concerns")
3535
SUCCESS_ALERT = (By.CSS_SELECTOR, ".alert.alert-success")
3636
DANGER_ALERT = (By.CSS_SELECTOR, ".alert.alert-danger")
37-
ALERT_CLOSE_BUTTON = (By.CSS_SELECTOR, ".alert.alert-dismissible button.close")
37+
ALERT_CLOSE_BUTTON = (By.CSS_SELECTOR, ".alert.alert-dismissible .btn-close")
38+
3839
COURSE_CARDS = (By.CSS_SELECTOR, "#react-tabs-1 .search-results .result-item")
3940
PUBLISH_BUTTON = (By.CSS_SELECTOR, ".action-bar button.publish")
4041
SAVE_BUTTON = (By.CSS_SELECTOR, ".action-bar button.save")

Diff for: frontend/components/dashboard/ActiveLearningCirclesTable.jsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ export default class LearningCirclesTable extends Component {
8282
date = moment(lc.start_date).format('MMM D, YYYY')
8383
}
8484

85-
const classes = lc.draft ? 'bg-secondary' : '';
85+
const classes = lc.draft ? 'bg-cream-dark' : '';
8686

8787
return(
8888
<tr key={ lc.id } className={`${classes}`}>
@@ -91,7 +91,7 @@ export default class LearningCirclesTable extends Component {
9191
{ this.props.teamId && <td>{ lc.facilitator }</td>}
9292
<td>{ lc.signup_count }</td>
9393
<td>{ date }</td>
94-
{ (this.props.user || this.props.userIsOrganizer) && <td><a href={ lc.studygroup_path } className="p2pu-btn btn-sm dark">manage</a></td> }
94+
{ (this.props.user || this.props.userIsOrganizer) && <td><a href={ lc.studygroup_path } className="p2pu-btn btn btn-sm dark">manage</a></td> }
9595
</tr>
9696
)
9797
})
@@ -104,7 +104,7 @@ export default class LearningCirclesTable extends Component {
104104
{
105105
this.state.learningCircles.map(lc => {
106106
const date = lc.next_meeting_date ? moment(lc.next_meeting_date).format('MMM D, YYYY') : "n/a";
107-
const classes = lc.draft ? 'bg-secondary' : '';
107+
const classes = lc.draft ? 'bg-cream-dark' : '';
108108

109109
return(
110110
<div className={`meeting-card p-2 ${classes}`} key={lc.id}>
@@ -124,7 +124,7 @@ export default class LearningCirclesTable extends Component {
124124
</div>
125125
</div>
126126

127-
{ (this.props.user || this.props.userIsOrganizer) && <a href={ lc.studygroup_path } className="p2pu-btn btn-sm dark m-0 my-2">manage</a> }
127+
{ (this.props.user || this.props.userIsOrganizer) && <a href={ lc.studygroup_path } className="p2pu-btn btn btn-sm dark m-0 my-2">manage</a> }
128128
</div>
129129
)
130130
})

Diff for: frontend/components/dashboard/Announcements.jsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export default class Announcements extends Component {
5151
<Card className={`bg-${announcement.color} feedback-survey d-block w-100`} key={`announcement-${index}`}>
5252
<div className="bold text-center text-white">{announcement.text}</div>
5353
<div className="text-center mt-3">
54-
<a href={announcement.link} className="p2pu-btn light btn-sm" target="_blank">{announcement.link_text}</a>
54+
<a href={announcement.link} className="p2pu-btn btn btn-sm secondary transparent" target="_blank">{announcement.link_text}</a>
5555
</div>
5656
</Card>
5757
)

Diff for: frontend/components/dashboard/CompletedLearningCirclesTable.jsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export default class LearningCirclesTable extends Component {
7272
{
7373
this.state.learningCircles.map(lc => {
7474
const date = lc.last_meeting_date ? moment(lc.last_meeting_date).format('MMM D, YYYY') : "n/a";
75-
const classes = lc.draft ? 'bg-secondary' : '';
75+
const classes = lc.draft ? 'bg-cream-dark' : '';
7676

7777
return(
7878
<tr key={ lc.id } className={`${classes}`}>
@@ -81,7 +81,7 @@ export default class LearningCirclesTable extends Component {
8181
{ this.props.teamId && <td>{ lc.facilitator }</td>}
8282
<td>{ lc.signup_count }</td>
8383
<td>{ date }</td>
84-
{ (this.props.user || this.props.userIsOrganizer) && <td><a href={ lc.studygroup_path } className="p2pu-btn btn-sm dark">manage</a></td> }
84+
{ (this.props.user || this.props.userIsOrganizer) && <td><a href={ lc.studygroup_path } className="p2pu-btn btn btn-sm dark">manage</a></td> }
8585
</tr>
8686
)
8787
})
@@ -94,7 +94,7 @@ export default class LearningCirclesTable extends Component {
9494
{
9595
this.state.learningCircles.map(lc => {
9696
const date = lc.last_meeting_date ? moment(lc.last_meeting_date).format('MMM D, YYYY') : "n/a";
97-
const classes = lc.draft ? 'bg-secondary' : '';
97+
const classes = lc.draft ? 'bg-cream-dark' : '';
9898

9999
return(
100100
<div className={`meeting-card p-2 ${classes}`} key={lc.id}>
@@ -114,7 +114,7 @@ export default class LearningCirclesTable extends Component {
114114
</div>
115115
</div>
116116

117-
{ (this.props.user || this.props.userIsOrganizer) && <a href={ lc.studygroup_path } className="p2pu-btn btn-sm dark m-0 my-2">manage</a> }
117+
{ (this.props.user || this.props.userIsOrganizer) && <a href={ lc.studygroup_path } className="p2pu-btn btn btn-sm dark m-0 my-2">manage</a> }
118118
</div>
119119
)
120120
})

Diff for: frontend/components/dashboard/CoursesTable.jsx

+5-5
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export default class CoursesTable extends Component {
5858
return(
5959
<div className="py-2">
6060
<div>You haven't added any courses.</div>
61-
<a href={"/en/course/create"} className="p2pu-btn dark btn-sm ml-0">Add a course</a>
61+
<a href={"/en/course/create"} className="p2pu-btn dark btn btn-sm ml-0">Add a course</a>
6262
</div>
6363
)
6464
};
@@ -79,14 +79,14 @@ export default class CoursesTable extends Component {
7979
{
8080
this.state.courses.map(course => {
8181
const date = moment(course.created_at).format('MMM D, YYYY')
82-
const classes = course.unlisted ? 'bg-secondary' : '';
82+
const classes = course.unlisted ? 'bg-cream-dark' : '';
8383

8484
return(
8585
<tr key={course.id} className={`${classes}`}>
8686
<td><a href={course.course_page_path}>{`${course.unlisted ? "[UNLISTED] " : ""}${course.title}`}</a></td>
8787
<td>{course.provider}</td>
8888
<td>{date}</td>
89-
<td><a href={course.course_edit_path} className="p2pu-btn btn-sm dark">edit</a></td>
89+
<td><a href={course.course_edit_path} className="p2pu-btn btn btn-sm dark">edit</a></td>
9090
</tr>
9191
)
9292
})
@@ -99,7 +99,7 @@ export default class CoursesTable extends Component {
9999
{
100100
this.state.courses.map((course, index) => {
101101
const date = moment(course.created_at).format('MMM D, YYYY')
102-
const classes = course.unlisted ? 'bg-secondary' : '';
102+
const classes = course.unlisted ? 'bg-cream-dark' : '';
103103
const delay = index * 100;
104104

105105
return(
@@ -118,7 +118,7 @@ export default class CoursesTable extends Component {
118118
</div>
119119
</div>
120120

121-
<a href={ course.course_edit_path } className="p2pu-btn btn-sm dark m-0 my-2">edit</a>
121+
<a href={ course.course_edit_path } className="p2pu-btn btn btn-sm dark m-0 my-2">edit</a>
122122
</div>
123123
)
124124
})

0 commit comments

Comments
 (0)