by roundabout, Tuesday, 28 January 2025, 17:32:23 (1738085543), pushed by roundabout, Tuesday, 28 January 2025, 17:32:25 (1738085545)
Author identity: vlad <vlad.muntoiu@gmail.com>
ed61be47a2f94ef128d33f718dd8df8f4a911a69
app.py
@@ -626,6 +626,12 @@ def user_profile(username):
case "follows":
return flask.render_template("user-profile-follows.html", user=user,
relationship=old_relationship)
case "organisation":
return flask.render_template("user-profile-organisation.html", user=user,
relationship=old_relationship)
case "trust":
return flask.render_template("user-profile-trust.html", user=user,
relationship=old_relationship)
case _:
return flask.render_template("user-profile-overview.html", user=user,
relationship=old_relationship)
@@ -656,6 +662,25 @@ def user_profile(username):
db.session.commit()
return flask.redirect("?", code=303)
case "trust-confirm":
trust_level = int(flask.request.form["trust-level"])
existing_trust = UserTrust.query.filter_by(
host_username=flask.session.get("username"),
trusted_username=username).first()
password = flask.request.form.get("password")
if not bcrypt.check_password_hash(db.session.get(User, flask.session.get("username")).password_hashed,
password):
flask.flash(Markup(_("Invalid password")), category="error")
return flask.redirect("?action=trust", code=303)
if existing_trust:
existing_trust.cancel()
if 0 <= trust_level <= 2:
trust = UserTrust(db.session.get(User, flask.session.get("username")), db.session.get(User, username),
trust_level)
db.session.add(trust)
db.session.commit()
return flask.redirect("?", code=303)
@app.route("/<username>/<repository>/")
@@ -1586,6 +1611,8 @@ def repository_users(username, repository):
updated_user = User.query.filter_by(
username=flask.request.form.get("update-username")).first()
relationship = RepoAccess.query.filter_by(repo=repo_data, user=updated_user).first()
if relationship.automatic:
flask.abort(400)
if flask.request.form.get("update-level") == -1:
relationship.delete()
else:
models.py
@@ -25,6 +25,7 @@ __all__ = [
"UserFollow",
"UserNotification",
"User",
"UserTrust",
"Notification",
"PostVote",
"Post",
@@ -59,16 +60,18 @@ with (app.app_context()):
user_username = db.Column(db.String(32), db.ForeignKey("user.username"), nullable=False)
repo_route = db.Column(db.String(98), db.ForeignKey("repo.route"), nullable=False)
access_level = db.Column(db.SmallInteger(), nullable=False) # 0 read-only, 1 read-write, 2 admin
automatic = db.Column(db.Boolean, default=False, nullable=False, server_default="false")
user = db.relationship("User", back_populates="repo_access")
repo = db.relationship("Repo", back_populates="repo_access")
__table_args__ = (db.UniqueConstraint("user_username", "repo_route", name="_user_repo_uc"),)
def __init__(self, user, repo, level):
def __init__(self, user, repo, level, automatic=False):
self.user_username = user.username
self.repo_route = repo.route
self.access_level = level
self.automatic = automatic
class RepoFavourite(db.Model):
@@ -136,6 +139,8 @@ with (app.app_context()):
comments = db.relationship("Comment", back_populates="owner")
prs = db.relationship("PullRequest", back_populates="owner")
notifications = db.relationship("UserNotification", back_populates="user")
trusts = db.relationship("UserTrust", back_populates="host", foreign_keys="[UserTrust.host_username]")
trusted_by = db.relationship("UserTrust", back_populates="trusted", foreign_keys="[UserTrust.trusted_username]")
def __init__(self, username, password, email=None, display_name=None):
self.username = username
@@ -176,6 +181,38 @@ with (app.app_context()):
celery_tasks.send_notification.apply_async(args=[user_notification.id])
class UserTrust(db.Model):
id = db.Column(db.Integer, primary_key=True)
host_username = db.Column(db.String(32), db.ForeignKey("user.username"), nullable=False)
trusted_username = db.Column(db.String(32), db.ForeignKey("user.username"), nullable=False)
trust_level = db.Column(db.SmallInteger, nullable=False)
host = db.relationship("User", back_populates="trusts", foreign_keys=[host_username])
trusted = db.relationship("User", back_populates="trusted_by", foreign_keys=[trusted_username])
__table_args__ = (db.UniqueConstraint("host_username", "trusted_username", name="_host_trusted_uc"),)
def __init__(self, host, trusted, level):
self.host_username = host.username
self.trusted_username = trusted.username
self.trust_level = level
# Add user to all of the host's repositories
for repo in host.repositories:
existing_relationship = RepoAccess.query.filter_by(user=trusted, repo=repo).first()
if existing_relationship:
continue
relationship = RepoAccess(trusted, repo, level, automatic=True)
db.session.add(relationship)
def cancel(self):
"""Remove the trusted user from all of the host's repositories."""
relationships = RepoAccess.query.filter(RepoAccess.repo.owner == self.host, RepoAccess.user == self.trusted, RepoAccess.automatic == True)
relationships.delete()
db.session.delete(self)
class Repo(db.Model):
route = db.Column(db.String(98), unique=True, nullable=False, primary_key=True)
owner_name = db.Column(db.String(32), db.ForeignKey("user.username"), nullable=False)
templates/repository/repo-users.html
@@ -15,7 +15,7 @@
<img src="/info/{{ relationship.user.username }}/avatar" style="width: 48px; height: 48px;">
</a>
</figure>
{% if user_relationship.access_level == 2 %}
{% if user_relationship.access_level == 2 and not relationship.automatic %}
<section class="card-main flexible-space">
<h3>{{ relationship.user.username }}</h3>
{% if relationship.user.username == username %}
@@ -57,6 +57,11 @@
{% elif relationship.access_level == 2 %}
{% trans %}Administrator{% endtrans %}
{% endif %}
{% if relationship.automatic %}
<p>
{% trans %}This access was granted as part of user-level trust.{% endtrans %}
</p>
{% endif %}
</section>
{% endif %}
</article>
templates/user-profile-organisation.html
@@ -0,0 +1,28 @@
{% extends "user-profile.html" %}
{% block title %}
{% trans username=user.username %}{{ username }}'s organisation settings{% endtrans %}
{% endblock %}
{% set active_page = "following" %}
{% block content %}
<x-hbox class="wrap homogenous">
{% if user.trusts | length %}
{% for trusted in user.trusts %}
<x-frame style="flex-basis: 128px; --width: 128px;">
<article class="card" style="flex: 0 1 auto;">
<section class="card-main">
<a href="/{{ trusted.trusted.username }}">
<img class="avatar" src="/info/{{ trusted.trusted.username }}/avatar" style="width: 100%;">
<div class="thumbnail-marquee">
<span class="inner-thumbnail-marquee" style="animation-play-state: paused;">{{ trusted.trusted.username }}</span>
</div>
</a>
</section>
</article>
</x-frame>
{% endfor %}
{% else %}
<p>{% trans %}This user hasn't trusted anyone to manage their account yet.{% endtrans %}</p>
{% endif %}
</x-hbox>
{% endblock %}
templates/user-profile-trust.html
@@ -0,0 +1,23 @@
{% extends "user-profile.html" %}
{% block title %}
{% trans username=user.username %}Trust {{ username }}{% endtrans %}
{% endblock %}
{% set active_page = "trust" %}
{% block content %}
<x-hbox class="wrap homogenous">
{% trans %}Trusting this user will grant them the equivalent role in all your repositories. If the role is read-write or admin, they will also be able to create new repositories on your behalf.{% endtrans %}
<form method="post" action="/{{ user.username }}/?action=trust-confirm" class="vbox">
<select id="trust-level" name="trust-level" required>
<option value="0">{% trans %}Read-only{% endtrans %}</option>
<option value="1">{% trans %}Read-write{% endtrans %}</option>
<option value="2">{% trans %}Administrator{% endtrans %}</option>
</select>
<label>
{% trans %}Enter your password to confirm{% endtrans %}
<input type="password" name="password">
</label>
<button type="submit" class="button">{% trans %}Trust{% endtrans %}</button>
</form>
</x-hbox>
{% endblock %}
templates/user-profile.html
@@ -30,6 +30,11 @@
{% trans %}Following{% endtrans %}
</a>
</li>
<li class="{% if active_page == 'organisation' %}selected{% endif %}">
<a href="?action=organisation">
{% trans %}Organisation{% endtrans %}
</a>
</li>
</ul>
<div class="flexible-space"></div>
@@ -55,7 +60,7 @@
<x-hbox style="align-items: center;" id="profile-header">
<img src="/info/{{ user.username }}/avatar" class="avatar" id="profile-avatar">
<x-vbox class="nopad">
<x-vbox>
{% if user.display_name and user.display_name != user.username %}
<hgroup id="username">
<h1>{{ user.display_name }}</h1>
@@ -64,6 +69,9 @@
{% else %}
<h1 class="headline">{{ user.username }}</h1>
{% endif %}
{% if logged_in_user and logged_in_user != user.username %}
<a href="/{{ user.username }}/?action=trust">{% trans %}Trust this user{% endtrans %}</a>
{% endif %}
</x-vbox>
<div class="flexible-space"></div>
<ul class="noindent" style="list-style: none; text-align: end;">