From ff6ed73363da196309cdb021aa3daca676dc2a8f Mon Sep 17 00:00:00 2001 From: Daniel Schadt Date: Thu, 30 Jun 2022 20:20:41 +0200 Subject: add friend request machinery --- fietsboek/locale/en/LC_MESSAGES/messages.mo | Bin 4704 -> 5278 bytes fietsboek/locale/en/LC_MESSAGES/messages.po | 48 +++++++++++++++- fietsboek/locale/fietslog.pot | 48 +++++++++++++++- fietsboek/models/__init__.py | 2 +- fietsboek/models/user.py | 32 ++++++++++- fietsboek/routes.py | 8 +++ fietsboek/templates/layout.jinja2 | 3 + fietsboek/templates/profile.jinja2 | 46 +++++++++++++++ fietsboek/views/default.py | 2 +- fietsboek/views/profile.py | 83 ++++++++++++++++++++++++++++ 10 files changed, 265 insertions(+), 7 deletions(-) create mode 100644 fietsboek/templates/profile.jinja2 create mode 100644 fietsboek/views/profile.py diff --git a/fietsboek/locale/en/LC_MESSAGES/messages.mo b/fietsboek/locale/en/LC_MESSAGES/messages.mo index 75ae61f..4559d38 100644 Binary files a/fietsboek/locale/en/LC_MESSAGES/messages.mo and b/fietsboek/locale/en/LC_MESSAGES/messages.mo differ diff --git a/fietsboek/locale/en/LC_MESSAGES/messages.po b/fietsboek/locale/en/LC_MESSAGES/messages.po index 793c18b..c218051 100644 --- a/fietsboek/locale/en/LC_MESSAGES/messages.po +++ b/fietsboek/locale/en/LC_MESSAGES/messages.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2022-06-30 18:24+0200\n" +"POT-Creation-Date: 2022-06-30 20:14+0200\n" "PO-Revision-Date: 2022-06-28 13:11+0200\n" "Last-Translator: \n" "Language: en\n" @@ -307,7 +307,11 @@ msgstr "Logout" msgid "page.navbar.upload" msgstr "Upload" -#: fietsboek/templates/layout.jinja2:54 +#: fietsboek/templates/layout.jinja2:53 +msgid "page.navbar.profile" +msgstr "Profile" + +#: fietsboek/templates/layout.jinja2:57 msgid "page.navbar.admin" msgstr "Admin" @@ -327,6 +331,30 @@ msgstr "Password" msgid "page.login.submit" msgstr "Login" +#: fietsboek/templates/profile.jinja2:5 +msgid "page.my_profile.title" +msgstr "My Profile" + +#: fietsboek/templates/profile.jinja2:6 +msgid "page.my_profile.friends" +msgstr "Friends" + +#: fietsboek/templates/profile.jinja2:13 +msgid "page.my_profile.unfriend" +msgstr "Unfriend" + +#: fietsboek/templates/profile.jinja2:22 +msgid "page.my_profile.accept_friend" +msgstr "Accept" + +#: fietsboek/templates/profile.jinja2:35 +msgid "page.my_profile.friend_request_email" +msgstr "Email of the friend" + +#: fietsboek/templates/profile.jinja2:41 +msgid "page.my_profile.send_friend_request" +msgstr "Send friend request" + #: fietsboek/templates/upload.jinja2:8 msgid "page.upload.form.gpx" msgstr "GPX file" @@ -355,6 +383,22 @@ msgstr "You are now logged in" msgid "flash.logged_out" msgstr "You have been logged out" +#: fietsboek/views/profile.py:34 +msgid "flash.friend_not_found" +msgstr "" + +#: fietsboek/views/profile.py:39 +msgid "flash.friend_already_exists" +msgstr "Friend already exists" + +#: fietsboek/views/profile.py:47 +msgid "flash.friend_added" +msgstr "Friend has been added" + +#: fietsboek/views/profile.py:57 +msgid "flash.friend_request_sent" +msgstr "Friend request sent" + #: fietsboek/views/upload.py:25 msgid "flash.no_file_selected" msgstr "No file selected" diff --git a/fietsboek/locale/fietslog.pot b/fietsboek/locale/fietslog.pot index fcef4e2..8cbb70a 100644 --- a/fietsboek/locale/fietslog.pot +++ b/fietsboek/locale/fietslog.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2022-06-30 18:24+0200\n" +"POT-Creation-Date: 2022-06-30 20:14+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -304,7 +304,11 @@ msgstr "" msgid "page.navbar.upload" msgstr "" -#: fietsboek/templates/layout.jinja2:54 +#: fietsboek/templates/layout.jinja2:53 +msgid "page.navbar.profile" +msgstr "" + +#: fietsboek/templates/layout.jinja2:57 msgid "page.navbar.admin" msgstr "" @@ -324,6 +328,30 @@ msgstr "" msgid "page.login.submit" msgstr "" +#: fietsboek/templates/profile.jinja2:5 +msgid "page.my_profile.title" +msgstr "" + +#: fietsboek/templates/profile.jinja2:6 +msgid "page.my_profile.friends" +msgstr "" + +#: fietsboek/templates/profile.jinja2:13 +msgid "page.my_profile.unfriend" +msgstr "" + +#: fietsboek/templates/profile.jinja2:22 +msgid "page.my_profile.accept_friend" +msgstr "" + +#: fietsboek/templates/profile.jinja2:35 +msgid "page.my_profile.friend_request_email" +msgstr "" + +#: fietsboek/templates/profile.jinja2:41 +msgid "page.my_profile.send_friend_request" +msgstr "" + #: fietsboek/templates/upload.jinja2:8 msgid "page.upload.form.gpx" msgstr "" @@ -352,6 +380,22 @@ msgstr "" msgid "flash.logged_out" msgstr "" +#: fietsboek/views/profile.py:34 +msgid "flash.friend_not_found" +msgstr "" + +#: fietsboek/views/profile.py:39 +msgid "flash.friend_already_exists" +msgstr "" + +#: fietsboek/views/profile.py:47 +msgid "flash.friend_added" +msgstr "" + +#: fietsboek/views/profile.py:57 +msgid "flash.friend_request_sent" +msgstr "" + #: fietsboek/views/upload.py:25 msgid "flash.no_file_selected" msgstr "" diff --git a/fietsboek/models/__init__.py b/fietsboek/models/__init__.py index 3f07926..151593e 100644 --- a/fietsboek/models/__init__.py +++ b/fietsboek/models/__init__.py @@ -5,7 +5,7 @@ import zope.sqlalchemy # Import or define all models here to ensure they are attached to the # ``Base.metadata`` prior to any initialization routines. -from .user import User # flake8: noqa +from .user import User, FriendRequest # flake8: noqa from .badge import Badge # flake8: noqa from .track import Track, TrackCache, Upload # flake8: noqa diff --git a/fietsboek/models/user.py b/fietsboek/models/user.py index c9ec5b6..b8036a0 100644 --- a/fietsboek/models/user.py +++ b/fietsboek/models/user.py @@ -7,10 +7,13 @@ from sqlalchemy import ( Boolean, Table, ForeignKey, + UniqueConstraint, + DateTime, ) from sqlalchemy.orm import relationship from sqlalchemy.orm.session import object_session -from sqlalchemy import select, union, delete +from sqlalchemy.orm.attributes import flag_dirty +from sqlalchemy import select, union, delete, func from .meta import Base @@ -60,6 +63,19 @@ class User(Base): tagged_tracks = relationship('Track', secondary='track_people_assoc', back_populates='tagged_people') uploads = relationship('Upload', back_populates='owner') + @classmethod + def query_by_email(cls, email): + """Returns a query that can be used to query a user by its email. + + This properly ensures that the email is matched case-insensitively. + + :param email: The email address to match. + :type email: str + :return: The prepared query. + :rtype: sqlalchemy.sql.selectable.Select + """ + return select(cls).filter(func.lower(email) == func.lower(cls.email)) + def set_password(self, new_password): """Sets a new password for the user. @@ -117,6 +133,7 @@ class User(Base): session = object_session(self) session.execute(delete(friends_assoc).filter_by(user_1_id=self.id, user_2_id=friend.id)) session.execute(delete(friends_assoc).filter_by(user_1_id=friend.id, user_2_id=self.id)) + flag_dirty(self) def add_friend(self, friend): """Add the given user as a new friend. @@ -132,3 +149,16 @@ class User(Base): Index('idx_users_name', User.name, unique=True) Index('idx_users_email', User.email, unique=True) + + +class FriendRequest(Base): + __tablename__ = 'friend_requests' + id = Column(Integer, primary_key=True) + sender_id = Column(Integer, ForeignKey('users.id')) + recipient_id = Column(Integer, ForeignKey('users.id')) + date = Column(DateTime) + + sender = relationship('User', primaryjoin='User.id == FriendRequest.sender_id', backref='outgoing_requests') + recipient = relationship('User', primaryjoin='User.id == FriendRequest.recipient_id', backref='incoming_requests') + + __table_args__ = (UniqueConstraint('sender_id', 'recipient_id'),) diff --git a/fietsboek/routes.py b/fietsboek/routes.py index f9ee27e..1e3fe9b 100644 --- a/fietsboek/routes.py +++ b/fietsboek/routes.py @@ -3,16 +3,24 @@ def includeme(config): config.add_route('home', '/') config.add_route('login', '/login') config.add_route('logout', '/logout') + config.add_route('upload', '/upload') config.add_route('preview', '/preview/{id}.gpx') config.add_route('finish-upload', '/upload/{id}') config.add_route('cancel-upload', '/cancel/{id}') + config.add_route('details', '/track/{id}') config.add_route('edit', '/edit/{id}') config.add_route('gpx', '/gpx/{id}.gpx') config.add_route('invalidate-share', '/track/{id}/invalidate-link') config.add_route('badge', '/badge/{id}') + config.add_route('admin', '/admin') config.add_route('admin-badge-add', '/admin/add-badge') config.add_route('admin-badge-edit', '/admin/edit-badge') config.add_route('admin-badge-delete', '/admin/delete-badge') + + config.add_route('profile', '/me') + config.add_route('add-friend', '/me/send-friend-request') + config.add_route('delete-friend', '/me/delete-friend') + config.add_route('accept-friend', '/me/accept-friend') diff --git a/fietsboek/templates/layout.jinja2 b/fietsboek/templates/layout.jinja2 index 8551fb5..4c6b1cc 100644 --- a/fietsboek/templates/layout.jinja2 +++ b/fietsboek/templates/layout.jinja2 @@ -49,6 +49,9 @@ + {% if request.identity.is_admin %}