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