mirror of
				https://git.sr.ht/~tsileo/microblog.pub
				synced 2025-06-05 21:59:23 +02:00 
			
		
		
		
	HTML page to show tagged objects
This commit is contained in:
		
							
								
								
									
										64
									
								
								app/main.py
									
									
									
									
									
								
							
							
						
						
									
										64
									
								
								app/main.py
									
									
									
									
									
								
							@@ -64,22 +64,13 @@ _RESIZED_CACHE: MutableMapping[tuple[str, int], tuple[bytes, str, Any]] = LFUCac
 | 
			
		||||
# TODO(ts):
 | 
			
		||||
#
 | 
			
		||||
# Next:
 | 
			
		||||
# - inbox/outbox admin
 | 
			
		||||
# - no counters anymore?
 | 
			
		||||
# - allow to show tags in the menu
 | 
			
		||||
# - 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
 | 
			
		||||
# - hash config/profile to detect when to send Update actor
 | 
			
		||||
#
 | 
			
		||||
# - [ ] block support
 | 
			
		||||
# - [ ] make the media proxy authenticated
 | 
			
		||||
# - [ ] prevent SSRF (urlutils from little-boxes)
 | 
			
		||||
# - [ ] Dockerization
 | 
			
		||||
# - [ ] Webmentions
 | 
			
		||||
# - [ ] custom emoji
 | 
			
		||||
# - [ ] poll/questions support
 | 
			
		||||
# - [ ] cleanup tasks
 | 
			
		||||
 | 
			
		||||
app = FastAPI(docs_url=None, redoc_url=None)
 | 
			
		||||
@@ -564,23 +555,54 @@ async def tag_by_name(
 | 
			
		||||
    if not tagged_count:
 | 
			
		||||
        raise HTTPException(status_code=404)
 | 
			
		||||
 | 
			
		||||
    outbox_objects = await db_session.execute(
 | 
			
		||||
        select(models.OutboxObject.ap_id)
 | 
			
		||||
        .join(models.TaggedOutboxObject)
 | 
			
		||||
    if is_activitypub_requested(request):
 | 
			
		||||
        outbox_object_ids = await db_session.execute(
 | 
			
		||||
            select(models.OutboxObject.ap_id)
 | 
			
		||||
            .join(
 | 
			
		||||
                models.TaggedOutboxObject,
 | 
			
		||||
                models.TaggedOutboxObject.outbox_object_id == models.OutboxObject.id,
 | 
			
		||||
            )
 | 
			
		||||
            .where(*where)
 | 
			
		||||
            .order_by(models.OutboxObject.ap_published_at.desc())
 | 
			
		||||
            .limit(20)
 | 
			
		||||
        )
 | 
			
		||||
        return ActivityPubResponse(
 | 
			
		||||
            {
 | 
			
		||||
                "@context": ap.AS_CTX,
 | 
			
		||||
                "id": BASE_URL + f"/t/{tag}",
 | 
			
		||||
                "type": "OrderedCollection",
 | 
			
		||||
                "totalItems": tagged_count,
 | 
			
		||||
                "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)
 | 
			
		||||
    )
 | 
			
		||||
    # TODO(ts): implement HTML version
 | 
			
		||||
    # if is_activitypub_requested(request):
 | 
			
		||||
    return ActivityPubResponse(
 | 
			
		||||
    outbox_objects = outbox_objects_result.unique().all()
 | 
			
		||||
 | 
			
		||||
    return await templates.render_template(
 | 
			
		||||
        db_session,
 | 
			
		||||
        request,
 | 
			
		||||
        "index.html",
 | 
			
		||||
        {
 | 
			
		||||
            "@context": ap.AS_CTX,
 | 
			
		||||
            "id": BASE_URL + f"/t/{tag}",
 | 
			
		||||
            "type": "OrderedCollection",
 | 
			
		||||
            "totalItems": tagged_count,
 | 
			
		||||
            "orderedItems": [outbox_object.ap_id for outbox_object in outbox_objects],
 | 
			
		||||
        }
 | 
			
		||||
            "request": request,
 | 
			
		||||
            "objects": outbox_objects,
 | 
			
		||||
        },
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,9 @@ from fastapi.testclient import TestClient
 | 
			
		||||
from sqlalchemy.orm import Session
 | 
			
		||||
 | 
			
		||||
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(
 | 
			
		||||
@@ -11,3 +14,43 @@ def test_tags__no_tags(
 | 
			
		||||
    response = client.get("/t/nope", headers={"Accept": ap.AP_CONTENT_TYPE})
 | 
			
		||||
 | 
			
		||||
    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]
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user