mirror of
https://git.sr.ht/~tsileo/microblog.pub
synced 2024-11-15 03:04:28 +00:00
More work on post visibility
This commit is contained in:
parent
866979309c
commit
8e977a2b14
9 changed files with 59 additions and 38 deletions
|
@ -124,6 +124,9 @@ class MicroblogPubBackend(Backend):
|
||||||
object_id = activity.get_object_id()
|
object_id = activity.get_object_id()
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
|
object_visibility = None
|
||||||
|
if object_id:
|
||||||
|
object_visibility = ap.get_visibility(activity.get_object())
|
||||||
|
|
||||||
actor_id = activity.get_actor().id
|
actor_id = activity.get_actor().id
|
||||||
|
|
||||||
|
@ -141,6 +144,7 @@ class MicroblogPubBackend(Backend):
|
||||||
"visibility": visibility.name,
|
"visibility": visibility.name,
|
||||||
"actor_id": actor_id,
|
"actor_id": actor_id,
|
||||||
"object_id": object_id,
|
"object_id": object_id,
|
||||||
|
"object_visibility": object_visibility.name,
|
||||||
"poll_answer": False,
|
"poll_answer": False,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -670,7 +674,7 @@ def gen_feed():
|
||||||
fg.logo(ME.get("icon", {}).get("url"))
|
fg.logo(ME.get("icon", {}).get("url"))
|
||||||
fg.language("en")
|
fg.language("en")
|
||||||
for item in DB.activities.find(
|
for item in DB.activities.find(
|
||||||
{"box": Box.OUTBOX.value, "type": "Create", "meta.deleted": False}, limit=10
|
{"box": Box.OUTBOX.value, "type": "Create", "meta.deleted": False, "meta.public": True}, limit=10
|
||||||
).sort("_id", -1):
|
).sort("_id", -1):
|
||||||
fe = fg.add_entry()
|
fe = fg.add_entry()
|
||||||
fe.id(item["activity"]["object"].get("url"))
|
fe.id(item["activity"]["object"].get("url"))
|
||||||
|
@ -684,7 +688,7 @@ def json_feed(path: str) -> Dict[str, Any]:
|
||||||
"""JSON Feed (https://jsonfeed.org/) document."""
|
"""JSON Feed (https://jsonfeed.org/) document."""
|
||||||
data = []
|
data = []
|
||||||
for item in DB.activities.find(
|
for item in DB.activities.find(
|
||||||
{"box": Box.OUTBOX.value, "type": "Create", "meta.deleted": False}, limit=10
|
{"box": Box.OUTBOX.value, "type": "Create", "meta.deleted": False, "meta.public": True}, limit=10
|
||||||
).sort("_id", -1):
|
).sort("_id", -1):
|
||||||
data.append(
|
data.append(
|
||||||
{
|
{
|
||||||
|
|
17
app.py
17
app.py
|
@ -247,6 +247,16 @@ def _get_file_url(url, size, kind):
|
||||||
return url
|
return url
|
||||||
|
|
||||||
|
|
||||||
|
@app.template_filter()
|
||||||
|
def visibility(v: str) -> str:
|
||||||
|
return ap.Visibility[v].value.lower()
|
||||||
|
|
||||||
|
|
||||||
|
@app.template_filter()
|
||||||
|
def visibility_is_public(v: str) -> bool:
|
||||||
|
return v in [ap.Visibility.PUBLIC.name, ap.Visibility.UNLISTED.name]
|
||||||
|
|
||||||
|
|
||||||
@app.template_filter()
|
@app.template_filter()
|
||||||
def emojify(text):
|
def emojify(text):
|
||||||
return emoji_unicode.replace(
|
return emoji_unicode.replace(
|
||||||
|
@ -1691,6 +1701,10 @@ def api_delete():
|
||||||
def api_boost():
|
def api_boost():
|
||||||
note = _user_api_get_note()
|
note = _user_api_get_note()
|
||||||
|
|
||||||
|
# Ensures the note visibility allow us to build an Announce (in respect to the post visibility)
|
||||||
|
if ap.get_visibility(note) not in [ap.Visibility.PUBLIC, ap.VISIBILITY.UNLISTED]:
|
||||||
|
abort(400)
|
||||||
|
|
||||||
announce = note.build_announce(MY_PERSON)
|
announce = note.build_announce(MY_PERSON)
|
||||||
announce_id = post_to_outbox(announce)
|
announce_id = post_to_outbox(announce)
|
||||||
|
|
||||||
|
@ -1841,6 +1855,7 @@ def admin_bookmarks():
|
||||||
|
|
||||||
@app.route("/inbox", methods=["GET", "POST"]) # noqa: C901
|
@app.route("/inbox", methods=["GET", "POST"]) # noqa: C901
|
||||||
def inbox():
|
def inbox():
|
||||||
|
# GET /inbox
|
||||||
if request.method == "GET":
|
if request.method == "GET":
|
||||||
if not is_api_request():
|
if not is_api_request():
|
||||||
abort(404)
|
abort(404)
|
||||||
|
@ -1859,6 +1874,7 @@ def inbox():
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# POST/ inbox
|
||||||
try:
|
try:
|
||||||
data = request.get_json(force=True)
|
data = request.get_json(force=True)
|
||||||
except Exception:
|
except Exception:
|
||||||
|
@ -3125,6 +3141,7 @@ def task_fetch_remote_question():
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
remote_question = get_backend().fetch_iri(iri, no_cache=True)
|
remote_question = get_backend().fetch_iri(iri, no_cache=True)
|
||||||
|
# FIXME(tsileo): compute and set `meta.object_visiblity` (also update utils.py to do it)
|
||||||
if (
|
if (
|
||||||
local_question
|
local_question
|
||||||
and (
|
and (
|
||||||
|
|
|
@ -104,4 +104,5 @@ class _1_MetaMigrationt(Migration):
|
||||||
set_meta["meta.server"] = urlparse(data["remote_id"]).netloc
|
set_meta["meta.server"] = urlparse(data["remote_id"]).netloc
|
||||||
|
|
||||||
logger.info(f"meta={set_meta}\n")
|
logger.info(f"meta={set_meta}\n")
|
||||||
|
if set_meta:
|
||||||
DB.activities.update_one({"_id": data["_id"]}, {"$set": set_meta})
|
DB.activities.update_one({"_id": data["_id"]}, {"$set": set_meta})
|
||||||
|
|
|
@ -270,19 +270,20 @@ a:hover {
|
||||||
float: left;
|
float: left;
|
||||||
border-radius:2px;
|
border-radius:2px;
|
||||||
}
|
}
|
||||||
.bar-icon {
|
|
||||||
background: $background-color;
|
|
||||||
padding: 5px;
|
|
||||||
color: $color-light;
|
|
||||||
margin-right: 10px;
|
|
||||||
float: left;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bar-item:hover {
|
.bar-item:hover {
|
||||||
background: $primary-color;
|
background: $primary-color;
|
||||||
color: $background-color;
|
color: $background-color;
|
||||||
}
|
}
|
||||||
|
.bar-item-no-border {
|
||||||
|
color: $color-light;
|
||||||
|
background: inherit;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
.bar-item-no-border:hover {
|
||||||
|
color: $color-light;
|
||||||
|
background: inherit;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
button.bar-item {
|
button.bar-item {
|
||||||
border: 0
|
border: 0
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if item.meta.object %}
|
{% if item.meta.object %}
|
||||||
{{ utils.display_note(item.meta.object, ui=False, meta={'actor': item.meta.object_actor}) }}
|
{{ utils.display_note(item.meta.object, ui=False, meta=item.meta) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% elif item | has_type('Create') %}
|
{% elif item | has_type('Create') %}
|
||||||
{{ utils.display_note(item.activity.object, meta=item.meta, no_color=True) }}
|
{{ utils.display_note(item.activity.object, meta=item.meta, no_color=True) }}
|
||||||
|
|
|
@ -6,13 +6,13 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
<title>{% block title %}{{ config.NAME }}{% endblock %}'s microblog</title>
|
<title>{% block title %}{{ config.NAME }}{% endblock %}'s microblog</title>
|
||||||
<link rel="stylesheet" href="https://unpkg.com/purecss@1.0.0/build/pure-min.css" integrity="sha384-nn4HPE8lTHyVtfCBi5yW9d20FjT8BJwUXyWZT9InLYax14RDjBj46LmSztkmNP9w" crossorigin="anonymous">
|
<link rel="stylesheet" href="https://unpkg.com/purecss@1.0.0/build/pure-min.css" integrity="sha384-nn4HPE8lTHyVtfCBi5yW9d20FjT8BJwUXyWZT9InLYax14RDjBj46LmSztkmNP9w" crossorigin="anonymous">
|
||||||
<link rel="stylesheet" href="/static/css/feathericon.min.css">
|
|
||||||
<link rel="authorization_endpoint" href="{{ config.ID }}/indieauth">
|
<link rel="authorization_endpoint" href="{{ config.ID }}/indieauth">
|
||||||
<link rel="token_endpoint" href="{{ config.ID }}/token">
|
<link rel="token_endpoint" href="{{ config.ID }}/token">
|
||||||
{% if not request.args.get("older_than") and not request.args.get("previous_than") %}<link rel="canonical" href="https://{{ config.DOMAIN }}{{ request.path }}">{% endif %}
|
{% if not request.args.get("older_than") and not request.args.get("previous_than") %}<link rel="canonical" href="https://{{ config.DOMAIN }}{{ request.path }}">{% endif %}
|
||||||
{% block links %}{% endblock %}
|
{% block links %}{% endblock %}
|
||||||
{% if config.THEME_COLOR %}<meta name="theme-color" content="{{ config.THEME_COLOR }}">{% endif %}
|
{% if config.THEME_COLOR %}<meta name="theme-color" content="{{ config.THEME_COLOR }}">{% endif %}
|
||||||
<style>{{ config.CSS | safe }}
|
<style>{{ config.CSS | safe }}
|
||||||
|
.icon { color: #555; }
|
||||||
.emoji {
|
.emoji {
|
||||||
width: 20px;
|
width: 20px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if item.meta.object %}
|
{% if item.meta.object %}
|
||||||
{{ utils.display_note(item.meta.object, meta={'actor': item.meta.object_actor}) }}
|
{{ utils.display_note(item.meta.object, meta=item.meta) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
<p style="margin-left:70px;padding-bottom:5px;display:inline-block;"><span class="bar-item-no-hover"><a style="color:#808080;" href="{{ boost_actor.url | get_url }}">{{ boost_actor.name or boost_actor.preferredUsername }}</a> boosted</span></p>
|
<p style="margin-left:70px;padding-bottom:5px;display:inline-block;"><span class="bar-item-no-hover"><a style="color:#808080;" href="{{ boost_actor.url | get_url }}">{{ boost_actor.name or boost_actor.preferredUsername }}</a> boosted</span></p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if item.meta.object %}
|
{% if item.meta.object %}
|
||||||
{{ utils.display_note(item.meta.object, ui=True) }}
|
{{ utils.display_note(item.meta.object, ui=True, meta=item.meta) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@
|
||||||
{% set boost_actor = item.meta.actor %}
|
{% set boost_actor = item.meta.actor %}
|
||||||
<p style="margin-left:70px;padding-bottom:5px;display:inline-block;"><span class="bar-item-no-hover"><a style="color:#808080;" href="{{ boost_actor.url | get_url }}">{{ boost_actor.name or boost_actor.preferredUsername }}</a> liked</span></p>
|
<p style="margin-left:70px;padding-bottom:5px;display:inline-block;"><span class="bar-item-no-hover"><a style="color:#808080;" href="{{ boost_actor.url | get_url }}">{{ boost_actor.name or boost_actor.preferredUsername }}</a> liked</span></p>
|
||||||
{% if item.meta.object %}
|
{% if item.meta.object %}
|
||||||
{{ utils.display_note(item.meta.object, ui=False, meta={'actor': item.meta.object_actor}) }}
|
{{ utils.display_note(item.meta.object, ui=False, meta=item.meta) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@
|
||||||
|
|
||||||
{% if item | has_type('question_ended') %}
|
{% if item | has_type('question_ended') %}
|
||||||
<p style="margin-left:70px;padding-bottom:5px;display:inline-block;"><span class="bar-item-no-hover">poll ended</span></p>
|
<p style="margin-left:70px;padding-bottom:5px;display:inline-block;"><span class="bar-item-no-hover">poll ended</span></p>
|
||||||
{{ utils.display_note(item.activity) }}
|
{{ utils.display_note(item.activity, meta={"object_visibility": "PUBLIC"}) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -17,7 +17,9 @@
|
||||||
|
|
||||||
{% macro display_note(obj, perma=False, ui=False, likes=[], shares=[], meta={}, no_color=False) -%}
|
{% macro display_note(obj, perma=False, ui=False, likes=[], shares=[], meta={}, no_color=False) -%}
|
||||||
|
|
||||||
{% if meta.actor %}
|
{% if meta.object_actor %}
|
||||||
|
{% set actor = meta.object_actor %}
|
||||||
|
{% elif meta.actor %}
|
||||||
{% set actor = meta.actor %}
|
{% set actor = meta.actor %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% set actor = obj.attributedTo | get_actor %}
|
{% set actor = obj.attributedTo | get_actor %}
|
||||||
|
@ -51,12 +53,11 @@
|
||||||
|
|
||||||
<div class="note-wrapper">
|
<div class="note-wrapper">
|
||||||
<div style="clear:both;height:20px;">
|
<div style="clear:both;height:20px;">
|
||||||
<a href="{{ actor | url_or_id | get_url }}" style="margin:0;text-decoration:none;margin: 0;text-decoration: none;display: block;width: 80%;overflow: hidden;white-space: nowrap;text-overflow: ellipsis;float: left;" class="no-hover"><strong>{{ actor.name or actor.preferredUsername }}</strong>
|
<a href="{{ actor | url_or_id | get_url }}" style="margin:0;text-decoration:none;margin: 0;text-decoration: none;display: block;width: 75%;overflow: hidden;white-space: nowrap;text-overflow: ellipsis;float: left;" class="no-hover"><strong>{{ actor.name or actor.preferredUsername }}</strong>
|
||||||
<span class="l">@{% if not no_color and obj.id | is_from_outbox %}<span class="pcolor">{{ actor.preferredUsername }}</span>{% else %}{{ actor.preferredUsername }}{% endif %}@{% if not no_color and obj.id | is_from_outbox %}<span class="pcolor">{{ actor | url_or_id | get_url | domain }}</span>{% else %}{{ actor | url_or_id | get_url | domain }}{% endif %}</span></a>
|
<span class="l">@{% if not no_color and obj.id | is_from_outbox %}<span class="pcolor">{{ actor.preferredUsername }}</span>{% else %}{{ actor.preferredUsername }}{% endif %}@{% if not no_color and obj.id | is_from_outbox %}<span class="pcolor">{{ actor | url_or_id | get_url | domain }}</span>{% else %}{{ actor | url_or_id | get_url | domain }}{% endif %}</span></a>
|
||||||
|
|
||||||
{% if not perma %}
|
{% if not perma %}
|
||||||
<span style="float:right;width: 20%;text-align: right;overflow: hidden;white-space: nowrap;text-overflow: ellipsis;display: block;">
|
<span style="float:right;width: 25%;text-align: right;overflow: hidden;white-space: nowrap;text-overflow: ellipsis;display: block;">
|
||||||
<i class="fe fe-unlock" title="Public" style="font-size:1em;padding-right:5px;"></i>
|
|
||||||
<a rel="noopener" class="u-url u-uid note-permalink l" href="{{ obj | url_or_id | get_url }}">
|
<a rel="noopener" class="u-url u-uid note-permalink l" href="{{ obj | url_or_id | get_url }}">
|
||||||
<time class="dt-published" title="{{ obj.published }}" datetime="{{ obj.published }}">{{ obj.published | format_timeago }}</time></a>
|
<time class="dt-published" title="{{ obj.published }}" datetime="{{ obj.published }}">{{ obj.published | format_timeago }}</time></a>
|
||||||
</span>
|
</span>
|
||||||
|
@ -164,27 +165,21 @@
|
||||||
<div class="bottom-bar">
|
<div class="bottom-bar">
|
||||||
{% if perma %}
|
{% if perma %}
|
||||||
<span class="perma-item" style="float:left;padding:5px;">{{ obj.published | format_time }}</span>
|
<span class="perma-item" style="float:left;padding:5px;">{{ obj.published | format_time }}</span>
|
||||||
{% if not (obj.id | is_from_outbox) %}
|
|
||||||
<a class="bar-icon" href="{{ obj | url_or_id | get_url }}">
|
|
||||||
<i class="fe fe-link-external"></i>
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
{% else %}
|
|
||||||
<a class="bar-icon" style="font-size:1.5em;" href="{{ obj | url_or_id | get_url }}">
|
|
||||||
<i class="fe fe-link-external"></i>
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if meta.count_reply and obj.id | is_from_outbox %}<a class ="bar-item" href="{{ obj.url | get_url }}"><strong>{{ meta.count_reply }}</strong> replies</a>
|
{% if meta.count_reply and obj.id | is_from_outbox %}<a class ="bar-item" href="{{ obj.url | get_url }}"><strong>{{ meta.count_reply }}</strong> replies</a>
|
||||||
{% elif meta.count_reply and session.logged_in %}
|
{% elif meta.count_reply and session.logged_in %}
|
||||||
<a class ="bar-item" href="/admin/thread?oid={{aid}}"><strong>{{ meta.count_reply }}</strong> replies</a>{% endif %}
|
<a class="bar-item" href="/admin/thread?oid={{aid}}"><strong>{{ meta.count_reply }}</strong> replies</a>{% endif %}
|
||||||
|
|
||||||
|
|
||||||
{% if not perma and meta.count_boost and obj.id | is_from_outbox %}<a class ="bar-item" href="{{ obj.url | get_url }}"><strong>{{ meta.count_boost }}</strong> boosts</a>{% endif %}
|
{% if not perma and meta.count_boost and obj.id | is_from_outbox %}<a class ="bar-item" href="{{ obj.url | get_url }}"><strong>{{ meta.count_boost }}</strong> boosts</a>{% endif %}
|
||||||
{% if not perma and meta.count_like and obj.id | is_from_outbox %}<a class ="bar-item" href="{{ obj.url | get_url }}"><strong>{{ meta.count_like }}</strong> likes</a>{% endif %}
|
{% if not perma and meta.count_like and obj.id | is_from_outbox %}<a class ="bar-item" href="{{ obj.url | get_url }}"><strong>{{ meta.count_like }}</strong> likes</a>{% endif %}
|
||||||
|
|
||||||
{% if session.logged_in %}
|
{% if session.logged_in %}
|
||||||
{% if ui%}
|
{% if ui%}
|
||||||
|
<a class="bar-item" href="/admin/new?reply={{ aid }}">reply</a>
|
||||||
|
|
||||||
|
{% if meta.object_visibility | visibility_is_public %}
|
||||||
{% if meta.boosted %}
|
{% if meta.boosted %}
|
||||||
<form action="/api/undo" class="action-form" method="POST">
|
<form action="/api/undo" class="action-form" method="POST">
|
||||||
<input type="hidden" name="redirect" value="{{ redir }}">
|
<input type="hidden" name="redirect" value="{{ redir }}">
|
||||||
|
@ -197,11 +192,10 @@
|
||||||
<input type="hidden" name="redirect" value="{{ redir }}">
|
<input type="hidden" name="redirect" value="{{ redir }}">
|
||||||
<input type="hidden" name="id" value="{{ obj.id }}">
|
<input type="hidden" name="id" value="{{ obj.id }}">
|
||||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||||
<button type="submit" class="bar-icon" style="font-size:1.5em;">
|
<button type="submit" class="bar-item">boost</button>
|
||||||
<i class="fe fe-share" title="Public"></i>
|
|
||||||
</button>
|
|
||||||
</form>
|
</form>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if meta.liked %}
|
{% if meta.liked %}
|
||||||
<form action="/api/undo" class="action-form" method="POST">
|
<form action="/api/undo" class="action-form" method="POST">
|
||||||
|
@ -244,7 +238,7 @@
|
||||||
<input type="hidden" name="redirect" value="{{ redir }}">
|
<input type="hidden" name="redirect" value="{{ redir }}">
|
||||||
<input type="hidden" name="id" value="{{ obj.id }}">
|
<input type="hidden" name="id" value="{{ obj.id }}">
|
||||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||||
<button type="submit" class="bar-item">delete</button>
|
<button type="submit" class="bar-item" onclick="return confirm('Confirm the delete action?');">delete</button>
|
||||||
</form>
|
</form>
|
||||||
{% if meta.pinned %}
|
{% if meta.pinned %}
|
||||||
<form action="/api/note/unpin" class="action-form" method="POST">
|
<form action="/api/note/unpin" class="action-form" method="POST">
|
||||||
|
@ -267,12 +261,16 @@
|
||||||
<input type="hidden" name="redirect" value="{{ redir }}">
|
<input type="hidden" name="redirect" value="{{ redir }}">
|
||||||
<input type="hidden" name="actor" value="{{ actor.id }}">
|
<input type="hidden" name="actor" value="{{ actor.id }}">
|
||||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||||
<button type="submit" class="bar-item">block</button>
|
<button type="submit" class="bar-item" onclick="return confirm('Confirm the block action?');">block</button>
|
||||||
</form>
|
</form>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a class="bar-item" href="/admin/new?reply={{ aid }}">reply</a>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<a class="bar-item" href="{{ obj | url_or_id | get_url }}">permalink</a>
|
||||||
|
{% if session.logged_in %}
|
||||||
|
<a class="bar-item bar-item-no-border">{{ meta.object_visibility | visibility }}</a>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue