diff --git a/mozillians/phonebook/forms.py b/mozillians/phonebook/forms.py index 0bffa1cd7..36a8bb007 100644 --- a/mozillians/phonebook/forms.py +++ b/mozillians/phonebook/forms.py @@ -156,42 +156,15 @@ def clean_username(self): return username -class ProfileForm(happyforms.ModelForm): +class BasicInformationForm(happyforms.ModelForm): photo = forms.ImageField(label=_lazy(u'Profile Photo'), required=False) photo_delete = forms.BooleanField(label=_lazy(u'Remove Profile Photo'), required=False) - date_mozillian = forms.DateField( - required=False, - label=_lazy(u'When did you get involved with Mozilla?'), - widget=MonthYearWidget(years=range(1998, datetime.today().year + 1), - required=False)) - skills = forms.CharField( - label='', - help_text=_lazy(u'Start typing to add a skill (example: Python, ' - u'javascript, Graphic Design, User Research)'), - required=False) - lat = forms.FloatField(widget=forms.HiddenInput) - lng = forms.FloatField(widget=forms.HiddenInput) - savecountry = forms.BooleanField( - label=_lazy(u'Required'), - initial=True, required=False, - widget=forms.CheckboxInput(attrs={'disabled': 'disabled'}) - ) - saveregion = forms.BooleanField(label=_lazy(u'Save'), required=False, show_hidden_initial=True) - savecity = forms.BooleanField(label=_lazy(u'Save'), required=False, show_hidden_initial=True) class Meta: model = UserProfile - fields = ('full_name', 'ircname', 'bio', 'photo', - 'allows_community_sites', 'tshirt', - 'title', 'allows_mozilla_sites', - 'date_mozillian', 'story_link', 'timezone', - 'privacy_photo', 'privacy_full_name', 'privacy_ircname', - 'privacy_timezone', 'privacy_tshirt', - 'privacy_bio', 'privacy_geo_city', 'privacy_geo_region', - 'privacy_geo_country', 'privacy_groups', - 'privacy_skills', 'privacy_languages', - 'privacy_date_mozillian', 'privacy_story_link', 'privacy_title') + fields = ('photo', 'privacy_photo', 'full_name', + 'privacy_full_name', 'bio', 'privacy_bio',) widgets = {'bio': forms.Textarea()} def clean_photo(self): @@ -215,6 +188,18 @@ def clean_photo(self): photo.size = cleaned_photo.tell() return photo + +class SkillsForm(happyforms.ModelForm): + skills = forms.CharField( + label='', + help_text=_lazy(u'Start typing to add a skill (example: Python, ' + u'javascript, Graphic Design, User Research)'), + required=False) + + class Meta: + model = UserProfile + fields = ('privacy_skills',) + def clean_skills(self): if not re.match(r'^[a-zA-Z0-9 +.:,-]*$', self.cleaned_data['skills']): # Commas cannot be included in skill names because we use them to @@ -226,6 +211,32 @@ def clean_skills(self): map(lambda x: x.strip() or False, skills.lower().split(','))) + def save(self, *args, **kwargs): + """Save the data to profile.""" + self.instance.set_membership(Skill, self.cleaned_data['skills']) + super(SkillsForm, self).save(*args, **kwargs) + + +class LanguagesPrivacyForm(happyforms.ModelForm): + class Meta: + model = UserProfile + fields = ('privacy_languages',) + + +class LocationForm(happyforms.ModelForm): + lat = forms.FloatField(widget=forms.HiddenInput) + lng = forms.FloatField(widget=forms.HiddenInput) + savecountry = forms.BooleanField(label=_lazy(u'Required'), + initial=True, required=False, + widget=forms.CheckboxInput(attrs={'disabled': 'disabled'})) + saveregion = forms.BooleanField(label=_lazy(u'Save'), required=False, show_hidden_initial=True) + savecity = forms.BooleanField(label=_lazy(u'Save'), required=False, show_hidden_initial=True) + + class Meta: + model = UserProfile + fields = ('timezone', 'privacy_timezone', 'privacy_geo_city', 'privacy_geo_region', + 'privacy_geo_country',) + def clean(self): # If lng/lat were provided, make sure they point at a country somewhere... if self.cleaned_data.get('lat') is not None and self.cleaned_data.get('lng') is not None: @@ -256,10 +267,43 @@ def clean(self): return self.cleaned_data - def save(self, *args, **kwargs): - """Save the data to profile.""" - self.instance.set_membership(Skill, self.cleaned_data['skills']) - super(ProfileForm, self).save(*args, **kwargs) + +class ContributionForm(happyforms.ModelForm): + date_mozillian = forms.DateField( + required=False, + label=_lazy(u'When did you get involved with Mozilla?'), + widget=MonthYearWidget(years=range(1998, datetime.today().year + 1), + required=False)) + + class Meta: + model = UserProfile + fields = ('title', 'privacy_title', + 'date_mozillian', 'privacy_date_mozillian', + 'story_link', 'privacy_story_link',) + + +class TshirtForm(happyforms.ModelForm): + class Meta: + model = UserProfile + fields = ('tshirt', 'privacy_tshirt',) + + +class GroupsPrivacyForm(happyforms.ModelForm): + class Meta: + model = UserProfile + fields = ('privacy_groups',) + + +class IRCForm(happyforms.ModelForm): + class Meta: + model = UserProfile + fields = ('ircname', 'privacy_ircname',) + + +class DeveloperForm(happyforms.ModelForm): + class Meta: + model = UserProfile + fields = ('allows_community_sites', 'allows_mozilla_sites',) class BaseLanguageFormSet(BaseInlineFormSet): @@ -296,11 +340,17 @@ def email_changed(self): return self.cleaned_data['email'] != self.initial['email'] -class RegisterForm(ProfileForm): +class RegisterForm(BasicInformationForm, LocationForm): optin = forms.BooleanField( widget=forms.CheckboxInput(attrs={'class': 'checkbox'}), required=True) + class Meta: + model = UserProfile + fields = ('photo', 'full_name', 'timezone', 'privacy_photo', 'privacy_full_name', 'optin', + 'privacy_timezone', 'privacy_geo_city', 'privacy_geo_region', + 'privacy_geo_country',) + class VouchForm(happyforms.Form): """Vouching is captured via a user's id and a description of the reason for vouching.""" @@ -336,4 +386,4 @@ class APIKeyRequestForm(happyforms.ModelForm): class Meta: model = APIv2App - fields = ('name', 'description', 'url') + fields = ('name', 'description', 'url',) diff --git a/mozillians/phonebook/views.py b/mozillians/phonebook/views.py index c81d80df1..2bb0d6243 100644 --- a/mozillians/phonebook/views.py +++ b/mozillians/phonebook/views.py @@ -5,12 +5,13 @@ from django.contrib.auth.models import User from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from django.db import transaction -from django.http import Http404 +from django.http import HttpResponseRedirect, Http404 from django.shortcuts import get_object_or_404, render from django.utils import timezone from django.views.decorators.cache import cache_page, never_cache from django.views.decorators.http import require_POST +from funfactory.helpers import urlparams from funfactory.urlresolvers import reverse from tower import ugettext as _ from waffle.decorators import waffle_flag @@ -157,79 +158,111 @@ def view_profile(request, username): @never_cache def edit_profile(request): """Edit user profile view.""" - # Don't user request.user + # Don't use request.user user = User.objects.get(pk=request.user.id) profile = user.userprofile user_groups = profile.groups.all().order_by('name') user_skills = stringify_groups(profile.skills.all().order_by('name')) - - user_form = forms.UserForm(request.POST or None, instance=user) - queryset = ExternalAccount.objects.exclude(type=ExternalAccount.TYPE_EMAIL) - accounts_formset = forms.AccountsFormset(request.POST or None, - instance=profile, - queryset=queryset) - new_profile = False - form = forms.ProfileForm - language_formset = forms.LanguagesFormset(request.POST or None, - instance=profile, - locale=request.locale) - - if not profile.is_complete: - new_profile = True - form = forms.RegisterForm - - profile_form = form(request.POST or None, request.FILES or None, - instance=profile, - initial={'skills': user_skills, - 'saveregion': True if profile.geo_region else False, - 'savecity': True if profile.geo_city else False, - 'lat': profile.lat, - 'lng': profile.lng}) - - all_forms = [user_form, profile_form, accounts_formset, language_formset] - - # Using ``list`` to force calling is_valid on all the forms, even if earlier - # ones are not valid, so we detect and display all the errors. - if all(list(f.is_valid() for f in all_forms)): - old_username = request.user.username - user_form.save() - profile_form.save() - accounts_formset.save() - language_formset.save() - - if new_profile: - redeem_invite(profile, request.session.get('invite-code')) - messages.info(request, _(u'Your account has been created.')) - elif user.username != old_username: - # Notify the user that their old profile URL won't work. - messages.info(request, - _(u'You changed your username; please note your ' - u'profile URL has also changed.')) - - return redirect('phonebook:profile_view', user.username) - emails = ExternalAccount.objects.filter(type=ExternalAccount.TYPE_EMAIL) - email_privacy_form = forms.EmailPrivacyForm(request.POST or None, instance=profile) - alternate_email_formset = forms.AlternateEmailFormset(request.POST or None, - instance=profile, - queryset=emails) + accounts_qs = ExternalAccount.objects.exclude(type=ExternalAccount.TYPE_EMAIL) + + sections = { + 'registration_section': ['user_form', 'registration_form'], + 'basic_section': ['user_form', 'basic_information_form'], + 'groups_section': ['groups_privacy_form'], + 'skills_section': ['skills_form'], + 'email_section': ['email_privacy_form', 'alternate_email_formset'], + 'languages_section': ['language_privacy_form', 'language_formset'], + 'accounts_section': ['accounts_formset'], + 'irc_section': ['irc_form'], + 'location_section': ['location_form'], + 'contribution_section': ['contribution_form'], + 'tshirt_section': ['tshirt_form'], + 'developer_section': ['developer_form'] + } + + curr_sect = next((s for s in sections.keys() if s in request.POST), None) + + def get_request_data(form): + if curr_sect and form in sections[curr_sect]: + return request.POST + return None + + ctx = {} + ctx['user_form'] = forms.UserForm(get_request_data('user_form'), instance=user) + ctx['registration_form'] = forms.RegisterForm(get_request_data('registration_form'), + request.FILES or None, + instance=profile) + basic_information_data = get_request_data('basic_information_form') + ctx['basic_information_form'] = forms.BasicInformationForm(basic_information_data, + request.FILES or None, + instance=profile) + ctx['accounts_formset'] = forms.AccountsFormset(get_request_data('accounts_formset'), + instance=profile, + queryset=accounts_qs) + ctx['language_formset'] = forms.LanguagesFormset(get_request_data('language_formset'), + instance=profile, + locale=request.locale) + language_privacy_data = get_request_data('language_privacy_form') + ctx['language_privacy_form'] = forms.LanguagesPrivacyForm(language_privacy_data, + instance=profile) + ctx['skills_form'] = forms.SkillsForm(get_request_data('skills_form'), instance=profile, + initial={'skills': user_skills}) + location_initial = { + 'saveregion': True if profile.geo_region else False, + 'savecity': True if profile.geo_city else False, + 'lat': profile.lat, + 'lng': profile.lng + } + ctx['location_form'] = forms.LocationForm(get_request_data('location_form'), instance=profile, + initial=location_initial) + ctx['contribution_form'] = forms.ContributionForm(get_request_data('contribution_form'), + instance=profile) + ctx['tshirt_form'] = forms.TshirtForm(get_request_data('tshirt_form'), instance=profile) + ctx['groups_privacy_form'] = forms.GroupsPrivacyForm(get_request_data('groups_privacy_form'), + instance=profile) + ctx['irc_form'] = forms.IRCForm(get_request_data('irc_form'), instance=profile) + ctx['developer_form'] = forms.DeveloperForm(get_request_data('developer_form'), + instance=profile) + ctx['email_privacy_form'] = forms.EmailPrivacyForm(get_request_data('email_privacy_form'), + instance=profile) + alternate_email_formset_data = get_request_data('alternate_email_formset') + ctx['alternate_email_formset'] = forms.AlternateEmailFormset(alternate_email_formset_data, + instance=profile, + queryset=emails) + forms_valid = True + if request.POST: + if not curr_sect: + raise Http404 + curr_forms = map(lambda x: ctx[x], sections[curr_sect]) + forms_valid = all(map(lambda x: x.is_valid(), curr_forms)) + if forms_valid: + old_username = request.user.username + for f in curr_forms: + f.save() + + if curr_sect == 'registration_section': + redeem_invite(profile, request.session.get('invite-code')) + elif user.username != old_username: + msg = (u'You changed your username; ' + u'please note your profile URL has also changed.') + messages.info(request, _(msg)) + + next_section = request.GET.get('next') + next_url = urlparams(reverse('phonebook:profile_edit'), next_section) + return HttpResponseRedirect(next_url) + + ctx.update({ + 'user_groups': user_groups, + 'profile': request.user.userprofile, + 'vouch_threshold': settings.CAN_VOUCH_THRESHOLD, + 'mapbox_id': settings.MAPBOX_PROFILE_ID, + 'apps': user.apiapp_set.filter(is_active=True), + 'appsv2': profile.apps.filter(enabled=True), + 'forms_valid': forms_valid + }) - data = dict(profile_form=profile_form, - user_form=user_form, - accounts_formset=accounts_formset, - email_privacy_form=email_privacy_form, - alternate_email_formset=alternate_email_formset, - user_groups=user_groups, - profile=request.user.userprofile, - language_formset=language_formset, - vouch_threshold=settings.CAN_VOUCH_THRESHOLD, - mapbox_id=settings.MAPBOX_PROFILE_ID, - apps=user.apiapp_set.filter(is_active=True), - appsv2=profile.apps.filter(enabled=True)) - - # If there are form errors, don't send a 200 OK. - status = 400 if any(f.errors for f in all_forms) else 200 - return render(request, 'phonebook/edit_profile.html', data, status=status) + return render(request, 'phonebook/edit_profile.html', ctx) @allow_unvouched diff --git a/mozillians/templates/includes/field.html b/mozillians/templates/includes/field.html index bc0f79f6a..39c22773d 100644 --- a/mozillians/templates/includes/field.html +++ b/mozillians/templates/includes/field.html @@ -2,15 +2,18 @@