forked from forks/microblog.pub
HTML page to show tagged objects
This commit is contained in:
parent
4a4c78bf64
commit
1acefc679d
2 changed files with 86 additions and 21 deletions
50
app/main.py
50
app/main.py
|
@ -64,22 +64,13 @@ _RESIZED_CACHE: MutableMapping[tuple[str, int], tuple[bytes, str, Any]] = LFUCac
|
||||||
# TODO(ts):
|
# TODO(ts):
|
||||||
#
|
#
|
||||||
# Next:
|
# Next:
|
||||||
# - inbox/outbox admin
|
|
||||||
# - no counters anymore?
|
|
||||||
# - allow to show tags in the menu
|
|
||||||
# - support update post with history
|
# - support update post with history
|
||||||
# - inbox/outbox in the admin (as in show every objects)
|
|
||||||
# - show likes/announces counter for outbox activities
|
|
||||||
# - update actor support
|
# - update actor support
|
||||||
# - hash config/profile to detect when to send Update actor
|
# - hash config/profile to detect when to send Update actor
|
||||||
#
|
#
|
||||||
# - [ ] block support
|
# - [ ] block support
|
||||||
# - [ ] make the media proxy authenticated
|
|
||||||
# - [ ] prevent SSRF (urlutils from little-boxes)
|
# - [ ] prevent SSRF (urlutils from little-boxes)
|
||||||
# - [ ] Dockerization
|
# - [ ] Dockerization
|
||||||
# - [ ] Webmentions
|
|
||||||
# - [ ] custom emoji
|
|
||||||
# - [ ] poll/questions support
|
|
||||||
# - [ ] cleanup tasks
|
# - [ ] cleanup tasks
|
||||||
|
|
||||||
app = FastAPI(docs_url=None, redoc_url=None)
|
app = FastAPI(docs_url=None, redoc_url=None)
|
||||||
|
@ -564,25 +555,56 @@ async def tag_by_name(
|
||||||
if not tagged_count:
|
if not tagged_count:
|
||||||
raise HTTPException(status_code=404)
|
raise HTTPException(status_code=404)
|
||||||
|
|
||||||
outbox_objects = await db_session.execute(
|
if is_activitypub_requested(request):
|
||||||
|
outbox_object_ids = await db_session.execute(
|
||||||
select(models.OutboxObject.ap_id)
|
select(models.OutboxObject.ap_id)
|
||||||
.join(models.TaggedOutboxObject)
|
.join(
|
||||||
|
models.TaggedOutboxObject,
|
||||||
|
models.TaggedOutboxObject.outbox_object_id == models.OutboxObject.id,
|
||||||
|
)
|
||||||
.where(*where)
|
.where(*where)
|
||||||
.order_by(models.OutboxObject.ap_published_at.desc())
|
.order_by(models.OutboxObject.ap_published_at.desc())
|
||||||
.limit(20)
|
.limit(20)
|
||||||
)
|
)
|
||||||
# TODO(ts): implement HTML version
|
|
||||||
# if is_activitypub_requested(request):
|
|
||||||
return ActivityPubResponse(
|
return ActivityPubResponse(
|
||||||
{
|
{
|
||||||
"@context": ap.AS_CTX,
|
"@context": ap.AS_CTX,
|
||||||
"id": BASE_URL + f"/t/{tag}",
|
"id": BASE_URL + f"/t/{tag}",
|
||||||
"type": "OrderedCollection",
|
"type": "OrderedCollection",
|
||||||
"totalItems": tagged_count,
|
"totalItems": tagged_count,
|
||||||
"orderedItems": [outbox_object.ap_id for outbox_object in outbox_objects],
|
"orderedItems": [
|
||||||
|
outbox_object.ap_id for outbox_object in outbox_object_ids
|
||||||
|
],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
outbox_objects_result = await db_session.scalars(
|
||||||
|
select(models.OutboxObject)
|
||||||
|
.where(*where)
|
||||||
|
.join(
|
||||||
|
models.TaggedOutboxObject,
|
||||||
|
models.TaggedOutboxObject.outbox_object_id == models.OutboxObject.id,
|
||||||
|
)
|
||||||
|
.options(
|
||||||
|
joinedload(models.OutboxObject.outbox_object_attachments).options(
|
||||||
|
joinedload(models.OutboxObjectAttachment.upload)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.order_by(models.OutboxObject.ap_published_at.desc())
|
||||||
|
.limit(20)
|
||||||
|
)
|
||||||
|
outbox_objects = outbox_objects_result.unique().all()
|
||||||
|
|
||||||
|
return await templates.render_template(
|
||||||
|
db_session,
|
||||||
|
request,
|
||||||
|
"index.html",
|
||||||
|
{
|
||||||
|
"request": request,
|
||||||
|
"objects": outbox_objects,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.get("/e/{name}")
|
@app.get("/e/{name}")
|
||||||
def emoji_by_name(name: str) -> ActivityPubResponse:
|
def emoji_by_name(name: str) -> ActivityPubResponse:
|
||||||
|
|
|
@ -2,6 +2,9 @@ from fastapi.testclient import TestClient
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
from app import activitypub as ap
|
from app import activitypub as ap
|
||||||
|
from app import models
|
||||||
|
from app.config import generate_csrf_token
|
||||||
|
from tests.utils import generate_admin_session_cookies
|
||||||
|
|
||||||
|
|
||||||
def test_tags__no_tags(
|
def test_tags__no_tags(
|
||||||
|
@ -11,3 +14,43 @@ def test_tags__no_tags(
|
||||||
response = client.get("/t/nope", headers={"Accept": ap.AP_CONTENT_TYPE})
|
response = client.get("/t/nope", headers={"Accept": ap.AP_CONTENT_TYPE})
|
||||||
|
|
||||||
assert response.status_code == 404
|
assert response.status_code == 404
|
||||||
|
|
||||||
|
|
||||||
|
def test_tags__note_with_tag(db: Session, client: TestClient) -> None:
|
||||||
|
# Call admin endpoint to create a note with
|
||||||
|
note_content = "Hello #testing"
|
||||||
|
|
||||||
|
response = client.post(
|
||||||
|
"/admin/actions/new",
|
||||||
|
data={
|
||||||
|
"redirect_url": "http://testserver/",
|
||||||
|
"content": note_content,
|
||||||
|
"visibility": ap.VisibilityEnum.PUBLIC.name,
|
||||||
|
"csrf_token": generate_csrf_token(),
|
||||||
|
},
|
||||||
|
cookies=generate_admin_session_cookies(),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Then the server returns a 302
|
||||||
|
assert response.status_code == 302
|
||||||
|
|
||||||
|
# And the Follow activity was created in the outbox
|
||||||
|
outbox_object = db.query(models.OutboxObject).one()
|
||||||
|
assert outbox_object.ap_type == "Note"
|
||||||
|
assert len(outbox_object.tags) == 1
|
||||||
|
emoji_tag = outbox_object.tags[0]
|
||||||
|
assert emoji_tag["type"] == "Hashtag"
|
||||||
|
assert emoji_tag["name"] == "#testing"
|
||||||
|
|
||||||
|
# And the tag page returns this note
|
||||||
|
html_resp = client.get("/t/testing")
|
||||||
|
html_resp.raise_for_status()
|
||||||
|
assert html_resp.status_code == 200
|
||||||
|
assert "Hello" in html_resp.text
|
||||||
|
|
||||||
|
# And the AP version of the page turns the note too
|
||||||
|
ap_resp = client.get("/t/testing", headers={"Accept": ap.AP_CONTENT_TYPE})
|
||||||
|
ap_resp.raise_for_status()
|
||||||
|
ap_json_resp = ap_resp.json()
|
||||||
|
assert ap_json_resp["totalItems"] == 1
|
||||||
|
assert ap_json_resp["orderedItems"] == [outbox_object.ap_id]
|
||||||
|
|
Loading…
Reference in a new issue