Skip to content

Commit

Permalink
Merge pull request #221 from uchicago-sg/fix-edit-and-sold
Browse files Browse the repository at this point in the history
Allow edit and mark as sold for unauthenticated users.
  • Loading branch information
georgeteo committed Jan 25, 2016
2 parents d4718a0 + 268d1c5 commit c2ff247
Show file tree
Hide file tree
Showing 11 changed files with 204 additions and 75 deletions.
24 changes: 13 additions & 11 deletions caravel/controllers/custom_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@

from caravel import model, utils


class OnlyValid(object):

"""Validates that the credential is correct."""

def __call__(self, form, field):
Expand All @@ -21,7 +23,9 @@ def __call__(self, form, field):
if not field.data or not field.data.valid:
raise ValidationError("Invalid credential.")


class MatchesPrincipal(threading.local):

"""Ensures that only the existing Principal is used."""

def __init__(self):
Expand All @@ -41,7 +45,9 @@ def __call__(self, form, field):
else:
raise ValidationError("Please use the same account for edits.")


class PrincipalField(wtforms.StringField):

"""A PrincipalField is one that represents an email address."""

def __init__(self, label, **kwargs):
Expand All @@ -58,15 +64,10 @@ def __init__(self, label, **kwargs):
def process_formdata(self, values):
"""Overrides the value sent in the form if asked."""

device = utils.Device.from_request(request)
user = users.get_current_user()
if user:
self.data = utils.Principal(user.email(), device, "GOOGLE_APPS")
elif ((not self.requires_validation) and values and
re.match(r'^[^@]+@[^@]+$', values[0])):
self.data = utils.Principal(values[0], device, "EMAIL")
else:
self.data = None
email = None
if not self.requires_validation and values:
email = values[0]
self.data = utils.Principal.from_request(request, email)

def _value(self):
"""Returns the user for this user."""
Expand Down Expand Up @@ -110,6 +111,7 @@ def __call__(self, **kwargs):


class PhotoField(wtforms.StringField):

"""A PhotoField represents a photo object stored in Cloud Storage."""

def process_formdata(self, values):
Expand Down Expand Up @@ -141,7 +143,7 @@ def widget(self, field, **kwargs):
return Markup("""
<div class="thumbnail">
{} <img src="{}"/>
<div class="caption">
<div class="caption">
<a class="btn btn-danger"
onclick="removeThumbnail(this, {!r})">Remove</a>
</div>
Expand All @@ -152,4 +154,4 @@ def widget(self, field, **kwargs):
)
else:
kwargs["type"] = "file"
return super(PhotoField, self).widget(field, **kwargs)
return super(PhotoField, self).widget(field, **kwargs)
26 changes: 20 additions & 6 deletions caravel/controllers/listings.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import datetime
import math
import itertools
from google.appengine.ext import ndb

TOR_DETECTOR = utils.TorDetector()

Expand Down Expand Up @@ -182,17 +183,30 @@ def publish_listing(listing):
Edits a listing.
"""

if is_from_tor() or not users.get_current_user():
if is_from_tor():
abort(403)

if listing.principal.email != users.get_current_user().email():
requester = utils.Principal.from_request(request,
email=listing.principal.email)

if not requester.can_act_as(listing.principal):
abort(403)

listing = model.UnapprovedListing(id=listing.key.id(), **listing.__dict__)
listing.sold = (request.form.get("sold") == "true")
listing.put()
fields = listing.to_dict()

new_listing = model.UnapprovedListing(id=listing.key.id())
for key, value in listing.to_dict().items():
try:
setattr(new_listing, key, value)
except ndb.ComputedPropertyError:
pass

new_listing.sold = (request.form.get("sold") == "true")
new_listing.principal = requester

new_listing.put()

if not isinstance(listing, model.Listing):
if not isinstance(new_listing, model.Listing):
flash("Your edit is awaiting moderation. "
"We'll email you when it is approved.")
return redirect(url_for("show_listing", listing=listing))
Expand Down
12 changes: 9 additions & 3 deletions caravel/storage/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,21 @@ def lookup(key, default):
"""Look up the given key, or return the default."""
return Parameter.get_or_insert(key_name=key, value=default).value


def get_tor_addresses():
return []

send_grid_client = sendgrid.SendGridClient(lookup("sendgrid_client", ""))
app.secret_key = lookup("session_secret", base64.b64encode(os.urandom(32)))
app.secret_key = lookup(
"session_secret",
base64.b64encode(
os.urandom(32))) or os.urandom(32)
slack_url = lookup("slack_url", "")
app.config["RECAPTCHA_PUBLIC_KEY"] = lookup("recaptcha_public_key",
app.config["RECAPTCHA_PUBLIC_KEY"] = lookup(
"recaptcha_public_key",
"6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI")
app.config["RECAPTCHA_PRIVATE_KEY"] = lookup("recaptcha_private_key",
app.config["RECAPTCHA_PRIVATE_KEY"] = lookup(
"recaptcha_private_key",
"6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe")
app.config["RECAPTCHA_DATA_ATTRS"] = {"size": "compact"}

Expand Down
41 changes: 37 additions & 4 deletions caravel/templates/listings/fullpage.html
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,11 @@ <h2>{{ listing.title }}</h2>
</div>
{% if not is_from_tor() %}
<div class="col-md-3">
{% if current_user and listing.principal.email == current_user.email() or is_admin %}

{% if current_user and listing.principal.email == current_user.email() %}
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">
Manage Listing</h3>
<h3 class="panel-title">Manage Listing</h3>
</div>
<form class="panel-body" method="post"
action="{{ url_for('publish_listing', listing=listing) }}">
Expand All @@ -71,7 +71,40 @@ <h3 class="panel-title">
{% endif %}
</form>
</div>

{% elif listing.principal.auth_method == "GOOGLE_APPS" and not current_user %}
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">
Manage Listing</h3>
</div>
<div class="panel-body">
<a class="btn btn-success" href="{{ login_url() }}">
Sign in with CNetID
</a> to edit.
</div>
</div>
{% elif listing.principal.auth_method != "GOOGLE_APPS" %}
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">
Suggest Changes</h3>
</div>
<form class="panel-body" method="post"
action="{{ url_for('publish_listing', listing=listing) }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
<a class="btn btn-default"
href="{{ url_for('edit_listing', listing=listing) }}">
Edit</a>
{% if listing.sold %}
<button type="submit" class="btn btn-success"
name="sold" value="false">Mark as Unsold</button>
{% else %}
<button type="submit" class="btn btn-success"
name="sold" value="true">Mark as Sold</button>
{% endif %}
</form>
</div>

{% endif %}
{% if not listing.sold %}
<div class="panel panel-default">
Expand Down
7 changes: 5 additions & 2 deletions caravel/tests/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,12 +125,15 @@ def setUp(self):
id="listing_a")
self.listing_a.put()

seller_b = utils.Principal("seller-b@uchicago.edu", device,
utils.Principal.EMAIL)
seller_b.validated_by = "fixture for unit test"

self.listing_b = model.Listing(
title=u"Listing \u2606B",
body=u"Body of \u2606B",
posted_at=datetime.datetime.now() - datetime.timedelta(hours=24),
principal=utils.Principal("seller-b@uchicago.edu", device,
utils.Principal.GOOGLE_APPS),
principal=seller_b,
price=71.10,
run_trigger=True,
version=11,
Expand Down
49 changes: 47 additions & 2 deletions caravel/tests/test_listings.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def test_post_inquiry(self):
self.assertLongString(self.emails[0].text)
self.emails.pop(0)

# Approve the first listing.
# Approve the first inquiry.
with self.google_apps_user("admin@uchicago.edu"):
print self.post("/moderation", data={
"csrf_token": self.ajax_csrf_token("/moderation"),
Expand Down Expand Up @@ -238,7 +238,52 @@ def test_edit_listing(self):
self.assertLongString(self.get("/listing_a").data)

def test_publish_listing(self):
with self.google_apps_user("seller-a@uchicago.edu"):
self.post("/listing_b/publish", data=dict(
csrf_token=self.csrf_token("/listing_b"),
sold="true"
))

# Listing is not yet sold.
self.assertLongString(self.get("/listing_b").data)

# Approve the edit.
with self.google_apps_user("admin@uchicago.edu"):
self.post("/moderation", data={
"csrf_token": self.ajax_csrf_token("/moderation"),
"approve": model.UnapprovedListing.query().get().key.urlsafe(),
}).data

# The listing is sold now!
self.assertLongString(self.get("/listing_b").data)

# Listing is no longer visible in searches.
self.assertLongString(self.get("/").data)
self.assertLongString(self.get("/?q=listing").data)

# Try unmarking a listing as sold.
self.post("/listing_b/publish", data=dict(
csrf_token=self.csrf_token("/listing_b/edit"),
sold="false"
))

# Listing is still sold.
self.assertLongString(self.get("/listing_b").data)

# Approve the edit.
with self.google_apps_user("admin@uchicago.edu"):
self.post("/moderation", data={
"csrf_token": self.ajax_csrf_token("/moderation"),
"approve": model.UnapprovedListing.query().get().key.urlsafe(),
}).data

# Listing is no longer sold.
self.assertLongString(self.get("/listing_b").data)

# Listing is visible again in searches.
self.assertLongString(self.get("/").data)

def test_publish_listing_with_cnetid(self):
with self.google_apps_user("seller-b@uchicago.edu"):
# Try marking a listing as sold.
self.post("/listing_a/publish", data=dict(
csrf_token=self.csrf_token("/listing_a"),
Expand Down
3 changes: 3 additions & 0 deletions caravel/tests/test_listings_new_listing_expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ apartments
Posted now .
Price: $3.44
Body of ☆D
Suggest Changes
Edit
Mark as Sold
Contact Seller
From
Sign in with CNetID or
Expand Down
3 changes: 3 additions & 0 deletions caravel/tests/test_listings_post_inquiry_expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ apartments
Posted 2d ago .
Price: $71.10
Body of ☆B
Suggest Changes
Edit
Mark as Sold
Contact Seller
From
Sign in with CNetID or
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ Listing ☆B
apartments
Posted 2d ago .
Price: $71.10
Validated by GOOGLE_APPS. Originally posted by
fixture for unit test. Originally posted by
1.2.3.4 with
mozilla.
Body of ☆B
Manage Listing
Suggest Changes
Edit
Mark as Sold
Contact Seller
Expand Down
Loading

0 comments on commit c2ff247

Please sign in to comment.