Skip to content

Commit

Permalink
Chapter 10: User avatars (10c)
Browse files Browse the repository at this point in the history
  • Loading branch information
miguelgrinberg committed Jun 9, 2019
1 parent 1d203ce commit 168a9e8
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 21 deletions.
9 changes: 8 additions & 1 deletion app/models.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from datetime import datetime
import hashlib
from werkzeug.security import generate_password_hash, check_password_hash
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
from flask import current_app
from flask import current_app, request
from flask_login import UserMixin, AnonymousUserMixin
from . import db, login_manager

Expand Down Expand Up @@ -166,6 +167,12 @@ def ping(self):
self.last_seen = datetime.utcnow()
db.session.add(self)

def gravatar(self, size=100, default='identicon', rating='g'):
url = 'https://secure.gravatar.com/avatar'

This comment has been minimized.

Copy link
@positronn

positronn Jul 24, 2020

When constructing this url, shouldn't the method ask for secure or non-secure request?
When I do the tests test_gravatar fails. I believe it is because the strings
'https://secure.gravatar.com/avatar/' and 'http://www.gravatar.com/avatar/' will never match,
because the method gravatar will always return a string containing at the beginning 'https://secure.gravatar.com/avatar' and never 'http://www.gravatar.com/avatar/'.

I did

    def gravatar(self, size = 100, default = 'identicon', rating = 'g', secure = True):
        if secure:
            url = 'https://www.gravatar.com/avatar'
        else:
            url = 'http://www.gravatar.com/avatar'
        hash = hashlib.md5(self.email.lower().encode('utf-8')).hexdigest()
        return f'{url}/{hash}?s={size}&d={default}&r={rating}'

Is this a good practice? Sorry, I'm relatively new to web dev.

This comment has been minimized.

Copy link
@miguelgrinberg

miguelgrinberg Jul 24, 2020

Author Owner

This is actually a minor bug in this repository. It is not necessary to support http:// gravatars, they can only be https:// even if the page is http://. The test in the 10c section is outdated, it appears I have only made the correction to this test in the 10d commit. I need to move that fix back into 10c.

hash = hashlib.md5(self.email.lower().encode('utf-8')).hexdigest()
return '{url}/{hash}?s={size}&d={default}&r={rating}'.format(
url=url, hash=hash, size=size, default=default, rating=rating)

def __repr__(self):
return '<User %r>' % self.username

Expand Down
8 changes: 8 additions & 0 deletions app/static/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.profile-thumbnail {
position: absolute;
}
.profile-header {
min-height: 260px;
margin-left: 280px;
}

6 changes: 5 additions & 1 deletion app/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
{{ super() }}
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon">
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon">
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='styles.css') }}">
{% endblock %}

{% block navbar %}
Expand All @@ -30,7 +31,10 @@
<ul class="nav navbar-nav navbar-right">
{% if current_user.is_authenticated %}
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Account <b class="caret"></b></a>
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<img src="{{ current_user.gravatar(size=18) }}">
Account <b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li><a href="{{ url_for('auth.change_password') }}">Change Password</a></li>
<li><a href="{{ url_for('auth.change_email_request') }}">Change Email</a></li>
Expand Down
41 changes: 22 additions & 19 deletions app/templates/user.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,30 @@

{% block page_content %}
<div class="page-header">
<h1>{{ user.username }}</h1>
{% if user.name or user.location %}
<p>
{% if user.name %}{{ user.name }}{% endif %}
{% if user.location %}
from <a href="http://maps.google.com/?q={{ user.location }}">{{ user.location }}</a>
{% endif %}
</p>
{% endif %}
{% if current_user.is_administrator() %}
<p><a href="mailto:{{ user.email }}">{{ user.email }}</a></p>
{% endif %}
{% if user.about_me %}<p>{{ user.about_me }}</p>{% endif %}
<p>Member since {{ moment(user.member_since).format('L') }}. Last seen {{ moment(user.last_seen).fromNow() }}.</p>
<p>
{% if user == current_user %}
<a class="btn btn-default" href="{{ url_for('.edit_profile') }}">Edit Profile</a>
<img class="img-rounded profile-thumbnail" src="{{ user.gravatar(size=256) }}">
<div class="profile-header">
<h1>{{ user.username }}</h1>
{% if user.name or user.location %}
<p>
{% if user.name %}{{ user.name }}<br>{% endif %}
{% if user.location %}
from <a href="http://maps.google.com/?q={{ user.location }}">{{ user.location }}</a><br>
{% endif %}
</p>
{% endif %}
{% if current_user.is_administrator() %}
<a class="btn btn-danger" href="{{ url_for('.edit_profile_admin', id=user.id) }}">Edit Profile [Admin]</a>
<p><a href="mailto:{{ user.email }}">{{ user.email }}</a></p>
{% endif %}
</p>
{% if user.about_me %}<p>{{ user.about_me }}</p>{% endif %}
<p>Member since {{ moment(user.member_since).format('L') }}. Last seen {{ moment(user.last_seen).fromNow() }}.</p>
<p>
{% if user == current_user %}
<a class="btn btn-default" href="{{ url_for('.edit_profile') }}">Edit Profile</a>
{% endif %}
{% if current_user.is_administrator() %}
<a class="btn btn-danger" href="{{ url_for('.edit_profile_admin', id=user.id) }}">Edit Profile [Admin]</a>
{% endif %}
</p>
</div>
</div>
{% endblock %}
17 changes: 17 additions & 0 deletions tests/test_user_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,3 +156,20 @@ def test_ping(self):
last_seen_before = u.last_seen
u.ping()
self.assertTrue(u.last_seen > last_seen_before)

def test_gravatar(self):
u = User(email='[email protected]', password='cat')
with self.app.test_request_context('/'):
gravatar = u.gravatar()
gravatar_256 = u.gravatar(size=256)
gravatar_pg = u.gravatar(rating='pg')
gravatar_retro = u.gravatar(default='retro')
with self.app.test_request_context('/', base_url='https://example.com'):
gravatar_ssl = u.gravatar()
self.assertTrue('http://www.gravatar.com/avatar/' +
'd4c74594d841139328695756648b6bd6'in gravatar)
self.assertTrue('s=256' in gravatar_256)
self.assertTrue('r=pg' in gravatar_pg)
self.assertTrue('d=retro' in gravatar_retro)
self.assertTrue('https://secure.gravatar.com/avatar/' +
'd4c74594d841139328695756648b6bd6' in gravatar_ssl)

0 comments on commit 168a9e8

Please sign in to comment.