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): | # 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,23 +555,54 @@ 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): | ||||||
|         select(models.OutboxObject.ap_id) |         outbox_object_ids = await db_session.execute( | ||||||
|         .join(models.TaggedOutboxObject) |             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) |         .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()) |         .order_by(models.OutboxObject.ap_published_at.desc()) | ||||||
|         .limit(20) |         .limit(20) | ||||||
|     ) |     ) | ||||||
|     # TODO(ts): implement HTML version |     outbox_objects = outbox_objects_result.unique().all() | ||||||
|     # if is_activitypub_requested(request): |  | ||||||
|     return ActivityPubResponse( |     return await templates.render_template( | ||||||
|  |         db_session, | ||||||
|  |         request, | ||||||
|  |         "index.html", | ||||||
|         { |         { | ||||||
|             "@context": ap.AS_CTX, |             "request": request, | ||||||
|             "id": BASE_URL + f"/t/{tag}", |             "objects": outbox_objects, | ||||||
|             "type": "OrderedCollection", |         }, | ||||||
|             "totalItems": tagged_count, |  | ||||||
|             "orderedItems": [outbox_object.ap_id for outbox_object in outbox_objects], |  | ||||||
|         } |  | ||||||
|     ) |     ) | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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] | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user