Skip to content
This repository has been archived by the owner on Feb 8, 2018. It is now read-only.

Revise the New Project form #4130

Merged
merged 14 commits into from
Dec 9, 2016
6 changes: 2 additions & 4 deletions gratipay/models/team/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,9 @@ def insert(cls, owner, **fields):
INSERT INTO teams
(slug, slug_lower, name, homepage,
product_or_service, onboarding_url,
owner)
product_or_service, owner)
VALUES (%(slug)s, %(slug_lower)s, %(name)s, %(homepage)s,
%(product_or_service)s, %(onboarding_url)s,
%(owner)s)
%(product_or_service)s, %(owner)s)
RETURNING teams.*::teams
""", fields)
Expand Down
76 changes: 43 additions & 33 deletions tests/py/test_team_edit.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,20 +241,19 @@ def test_edit(self):
'name': 'Enterprise',
'product_or_service': 'We save galaxies.',
'homepage': 'http://starwars-enterprise.com/',
'onboarding_url': 'http://starwars-enterprise.com/onboarding',
'image': FileUpload(IMAGE, 'logo.png'),
}
data = json.loads(self.client.POST( '/enterprise/edit/edit.json'
, data=edit_data
, auth_as='picard').body)
, auth_as='picard'
).body)

team = T('enterprise')
assert data == team.to_dict()

assert team.name == 'Enterprise'
assert team.product_or_service == 'We save galaxies.'
assert team.homepage == 'http://starwars-enterprise.com/'
assert team.onboarding_url == 'http://starwars-enterprise.com/onboarding'
assert team.load_image('original') == IMAGE

def test_edit_supports_partial_updates(self):
Expand All @@ -265,20 +264,21 @@ def test_edit_supports_partial_updates(self):
'image': FileUpload(IMAGE, 'logo.png'),
}
self.client.POST( '/enterprise/edit/edit.json'
, data=edit_data
, auth_as='picard')
, data=edit_data
, auth_as='picard'
)

team = T('enterprise')
assert team.name == 'The Enterprise'
assert team.product_or_service == 'We save galaxies.'
assert team.homepage == 'http://starwars-enterprise.com/'
assert team.onboarding_url == ''
assert team.load_image('original') == IMAGE

def test_edit_needs_auth(self):
self.make_team(slug='enterprise', is_approved=True)
response = self.client.PxST( '/enterprise/edit/edit.json'
, data={ 'name': 'Enterprise' })
, data={'name': 'Enterprise'}
)
assert response.code == 401
assert T('enterprise').name == 'The Enterprise'

Expand All @@ -288,14 +288,16 @@ def test_only_admin_and_owner_can_edit(self):
self.make_team(slug='enterprise', is_approved=True)

response = self.client.PxST( '/enterprise/edit/edit.json'
, data={ 'name': 'Enterprise' }
, auth_as='alice')
, data={'name': 'Enterprise'}
, auth_as='alice'
)
assert response.code == 403
assert T('enterprise').name == 'The Enterprise'

response = self.client.POST( '/enterprise/edit/edit.json'
, data={ 'name': 'Enterprise' }
, auth_as='admin')
, data={'name': 'Enterprise'}
, auth_as='admin'
)
assert response.code == 200
assert T('enterprise').name == 'Enterprise'

Expand All @@ -306,39 +308,43 @@ def test_cant_edit_closed_teams(self):
self.db.run("UPDATE teams SET is_closed = true WHERE slug = 'enterprise'")

response = self.client.PxST( '/enterprise/edit/edit.json'
, data={ 'name': 'Enterprise' }
, auth_as='picard')
, data={'name': 'Enterprise'}
, auth_as='picard'
)
assert response.code in (403, 410)
assert T('enterprise').name == 'The Enterprise'

def test_cant_edit_rejected_teams(self):
self.make_team(slug='enterprise', is_approved=False)
response = self.client.PxST( '/enterprise/edit/edit.json'
, data={ 'name': 'Enterprise' }
, auth_as='picard')
, data={'name': 'Enterprise'}
, auth_as='picard'
)
assert response.code == 403
assert T('enterprise').name == 'The Enterprise'

def test_can_edit_teams_under_review(self):
self.make_team(slug='enterprise', is_approved=None)
response = self.client.POST( '/enterprise/edit/edit.json'
, data={ 'name': 'Enterprise' }
, auth_as='picard')
, data={'name': 'Enterprise'}
, auth_as='picard'
)
assert response.code == 200
assert T('enterprise').name == 'Enterprise'

def test_can_only_edit_allowed_fields(self):
allowed_fields = set(['name', 'image', 'product_or_service',
'homepage', 'onboarding_url'])
allowed_fields = set(['name', 'image', 'product_or_service', 'homepage'])

team = self.make_team(slug='enterprise', is_approved=None)

fields = vars(team).keys()
fields.remove('onboarding_url') # we are still keeping this in the db for now
for field in fields:
if field not in allowed_fields:
response = self.client.POST( '/enterprise/edit/edit.json'
, data={ field: 'foo' }
, auth_as='picard')
, data={field: 'foo'}
, auth_as='picard'
)
new_team = T('enterprise')
assert response.code == 200
assert getattr(new_team, field) == getattr(team, field)
Expand All @@ -348,30 +354,33 @@ def test_edit_accepts_jpeg_and_png(self):
image_types = ['png', 'jpg', 'jpeg']
for i_type in image_types:
team.save_image(original='', large='', small='', image_type='image/png')
data = { 'image': FileUpload(IMAGE, 'logo.'+i_type) }
data = {'image': FileUpload(IMAGE, 'logo.'+i_type)}
response = self.client.POST( '/enterprise/edit/edit.json'
, data=data
, auth_as='picard')
, data=data
, auth_as='picard'
)
assert response.code == 200
assert team.load_image('original') == IMAGE

def test_edit_with_invalid_image_type_raises_error(self):
team = self.make_team(slug='enterprise', is_approved=True)
invalid_image_types = ['tiff', 'gif', 'bmp', 'svg']
for i_type in invalid_image_types:
data = { 'image': FileUpload(IMAGE, 'logo.'+i_type) }
data = {'image': FileUpload(IMAGE, 'logo.'+i_type)}
response = self.client.PxST( '/enterprise/edit/edit.json'
, data=data
, auth_as='picard')
, data=data
, auth_as='picard'
)
assert response.code == 400
assert "Please upload a PNG or JPG image." in response.body
assert team.load_image('original') == None

def test_edit_with_empty_values_raises_error(self):
self.make_team(slug='enterprise', is_approved=True)
response = self.client.PxST( '/enterprise/edit/edit.json'
, data={ 'name': ' ' }
, auth_as='picard')
, data={'name': ' '}
, auth_as='picard'
)
assert response.code == 400
assert T('enterprise').name == 'The Enterprise'

Expand All @@ -381,8 +390,9 @@ def test_edit_with_bad_url_raises_error(self):
, homepage='http://starwars-enterprise.com/')

r = self.client.PxST( '/enterprise/edit/edit.json'
, data={ 'homepage': 'foo' }
, auth_as='picard')
, data={'homepage': 'foo'}
, auth_as='picard'
)
assert r.code == 400
assert "Please enter an http[s]:// URL for the 'Homepage' field." in r.body
assert T('enterprise').homepage == 'http://starwars-enterprise.com/'
Expand All @@ -394,12 +404,12 @@ def test_edit_with_empty_data_does_nothing(self):
'name': 'Enterprise',
'product_or_service': 'We save galaxies.',
'homepage': 'http://starwars-enterprise.com/',
'onboarding_url': 'http://starwars-enterprise.com/onboarding',
}
self.make_team(**team_data)
r = self.client.POST( '/enterprise/edit/edit.json'
, data={}
, auth_as='picard')
, auth_as='picard'
)
assert r.code == 200

team = T('enterprise')
Expand Down
13 changes: 0 additions & 13 deletions tests/py/test_teams.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,11 +225,9 @@ def test_casing_of_urls_survives(self):
self.make_participant('alice', claimed_time='now', email_address='', last_paypal_result='')
self.post_new(dict( self.valid_data
, homepage='Http://gratipay.com/'
, onboarding_url='http://INSIDE.GRATipay.com/'
))
team = T('gratiteam')
assert team.homepage == 'Http://gratipay.com/'
assert team.onboarding_url == 'http://INSIDE.GRATipay.com/'

def test_casing_of_slug_survives(self):
self.make_participant('alice', claimed_time='now', email_address='', last_paypal_result='')
Expand Down Expand Up @@ -264,14 +262,6 @@ def test_error_message_for_public_review(self):
assert self.db.one("SELECT COUNT(*) FROM teams") == 0
assert "Sorry, you must agree to have your application publicly reviewed." in r.body

def test_error_message_for_payroll(self):
self.make_participant('alice', claimed_time='now', email_address='alice@example.com', last_paypal_result='')
data = dict(self.valid_data)
del data['agree_payroll']
r = self.post_new(data, expected=400)
assert self.db.one("SELECT COUNT(*) FROM teams") == 0
assert "Sorry, you must agree to be responsible for payroll." in r.body

def test_error_message_for_terms(self):
self.make_participant('alice', claimed_time='now', email_address='alice@example.com', last_paypal_result='')
data = dict(self.valid_data)
Expand All @@ -295,9 +285,6 @@ def test_error_message_for_bad_url(self):
assert self.db.one("SELECT COUNT(*) FROM teams") == 0
assert "Please enter an http[s]:// URL for the 'Homepage' field." in r.body

r = self.post_new(dict(self.valid_data, onboarding_url='foo'), expected=400)
assert "an http[s]:// URL for the 'Self-onboarding Documentation URL' field." in r.body

def test_error_message_for_invalid_team_name(self):
self.make_participant('alice', claimed_time='now', email_address='alice@example.com', last_paypal_result='')
data = dict(self.valid_data)
Expand Down
3 changes: 1 addition & 2 deletions www/%team/edit/edit.json.spt
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,7 @@ for field in data.keys():
if not value:
raise Response(400, _("Please fill out the '{}' field.", field_names[field]))

if (field in ('homepage', 'onboarding_url')
and not valid_url(value)):
if field == 'homepage' and not valid_url(value):
raise Response(400,
_( "Please enter an http[s]:// URL for the '{}' field."
, field_names[field]
Expand Down
13 changes: 5 additions & 8 deletions www/%team/edit/index.html.spt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ if team.is_closed:
if team.is_approved is False: # for teams under review, is_approved is None.
raise Response(403, _("You can't edit a rejected team."))

title = _("Edit your team")
title = _("Edit Your Project")
banner = _("Edit")
suppress_sidebar = True

Expand All @@ -44,21 +44,18 @@ suppress_sidebar = True
<form action="edit.json" method="POST" id = "edit-team">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">

<label><h2>{{ _("Team Name") }}</h2></label>
<label><h2>{{ _("Project Name") }}</h2></label>
<input type="text" name="name" value="{{team.name}}" required autofocus>

<label><h2>{{ _("Image") }}</h2></label>
<img src="{{ team.get_image_url('small') }}" align="middle">
<input type="file" name="image" accept="image/png, image/jpeg">

<label><h2>{{ _("Product or Service") }}</h2></label>
<textarea name="product_or_service" required>{{team.product_or_service}}</textarea>

<label><h2>{{ _("Homepage") }}</h2></label>
<input type="text" name="homepage" value="{{team.homepage}}" required>

<label><h2>{{ _("Self-onboarding Documentation URL") }}</h2></label>
<input type="text" name="onboarding_url" value="{{team.onboarding_url}}" required>
<label><h2>{{ _("Image") }}</h2></label>
<img src="{{ team.get_image_url('small') }}" align="middle">
<input type="file" name="image" accept="image/png, image/jpeg">

<br>
<br>
Expand Down
4 changes: 0 additions & 4 deletions www/%team/index.html.spt
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,6 @@ is_team_owner = not user.ANON and team.owner == user.participant.username

<a href="{{ team.homepage }}">{{ _("Homepage") }}</a>

{% if team.onboarding_url %}
| <a href="{{ team.onboarding_url }}">{{ _("Onboarding") }}</a>
{% endif %}

{% if user.ADMIN or is_team_owner %}
|{{ _( "{a} Edit team {_a}"
, a='<a href="./edit">'|safe
Expand Down
42 changes: 14 additions & 28 deletions www/new.spt
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ from gratipay.models.team import Team
request.allow('GET')

if user.ANON:
raise Response(401, _("You must sign in to apply for a new Team."))
raise Response(401, _("You must sign in to apply for your project to join Gratipay."))

if user.participant.email_address is None:
raise Response(400, _("You must have a verified email address to apply for a new Team."))
raise Response(400, _("You must have a verified email address to apply for your project to join Gratipay."))

if not user.participant.has_payout_route:
raise Response(400, _("You must attach a PayPal account to apply for a new Team."))
raise Response(400, _("You must attach a PayPal account to apply for your project to join Gratipay."))

title = _("Apply for a New Team")
title = _("Apply to Join Gratipay")
banner = _("Apply")
suppress_sidebar = True
[---] text/html
Expand All @@ -33,7 +33,7 @@ suppress_sidebar = True
}
</style>
<div class="application-complete" style="display: none;">
<p>{{ _("Thanks! Your public team page is:") }}</p>
<p>{{ _("Thanks! Your public project page is:") }}</p>
<p><a href="" class="team_url"></a></p>
<p>{{ _("And your public review ticket is:") }}</p>
<p><a href="" class="review_url"></a></p>
Expand All @@ -45,28 +45,21 @@ suppress_sidebar = True
<form action="/teams/create.json" method="POST" id="new-team">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">

<label><h2>{{ _("Team Name") }}</h2></label>
<label><h2>{{ _("Project Name") }}</h2></label>
<p><i>{{ _("At least one letter A through Z, plus numbers, dashes (-), underscores (_), periods (.), comma (,) and whitespace.") }}</i></p>
<input type="text" name="name" required autofocus>

<label><h2>{{ _("Image") }}</h2></label>
<p><i>{{ _("PNG or JPG up to 256 kB. Will be cropped square and resized to 160 x 160 and 48 x 48 (with original saved for later).") }}</i></p>
<input type="file" name="image" required>

<label><h2>{{ _("Product or Service") }}</h2></label>
<p><i>{{ _("What product or service does your Team provide?") }}</i></p>
<p><i>{{ _("What product or service does your project provide?") }}</i></p>
<textarea name="product_or_service" required></textarea>

<label><h2>{{ _("Homepage") }}</h2></label>
<p><i>{{ _("What is the customer-facing web page for your product or service?") }}</i></p>
<p><i>{{ _("What is the customer-facing web page for your project?") }}</i></p>
<input type="text" name="homepage" required>

<label><h2>{{ _("Self-onboarding Documentation URL") }}</h2></label>
<p><i>{{ _("Where can people find instructions on how to do your work for you ({0}example{1})? "
, '<a href="https://github.com/gratipay/gratipay.com/blob/master/CONTRIBUTING.md">'|safe
, '</a>'|safe
) }}</i></p>
<input type="text" name="onboarding_url" required>
<label><h2>{{ _("Image") }}</h2></label>
<p><i>{{ _("PNG or JPG up to 256 kB. Will be cropped square and resized to 160 x 160 and 48 x 48 (with original saved for later).") }}</i></p>
<input type="file" name="image" required>

<h2>{{ _("Agreements") }}</h2>
<input type="checkbox" value="true" name="agree_public" id="agree_public">
Expand All @@ -77,24 +70,17 @@ suppress_sidebar = True
) }}
</label>

<br>
<input type="checkbox" value="true" name="agree_payroll" id="agree_payroll">
<label for="agree_payroll">
{{ _( "I agree to be responsible for {0}payroll{1}."
, '<a href="/about/features/payroll">'|safe
, '</a>'|safe
) }}
</label>

<br>
<input type="checkbox" value="true" name="agree_terms" id="agree_terms">
<label for="agree_terms">
{{ _( "I agree to the {0}terms of service{1}."
{{ _( "I agree to the other {0}terms of service{1} as well."
, '<a href="/about/policies/terms-of-service">'|safe
, '</a>'|safe
) }}
</label>

<br>
<br>
<button type="submit">{{ _("Apply") }}</button>
</form>
{% endblock %}