diff --git a/app/admin.py b/app/admin.py index c228c49..4212845 100644 --- a/app/admin.py +++ b/app/admin.py @@ -661,6 +661,7 @@ async def admin_actions_new( is_sensitive: bool = Form(False), visibility: str = Form(), poll_type: str | None = Form(None), + name: str | None = Form(None), csrf_check: None = Depends(verify_csrf_token), db_session: AsyncSession = Depends(get_db_session), ) -> RedirectResponse: @@ -687,6 +688,8 @@ async def admin_actions_new( raise ValueError("Question must have at least 2 answers") poll_duration_in_minutes = int(raw_form_data["poll_duration"]) + elif name: + ap_type = "Article" public_id = await boxes.send_create( db_session, @@ -700,6 +703,7 @@ async def admin_actions_new( poll_type=poll_type, poll_answers=poll_answers, poll_duration_in_minutes=poll_duration_in_minutes, + name=name, ) return RedirectResponse( request.url_for("outbox_by_public_id", public_id=public_id), diff --git a/app/boxes.py b/app/boxes.py index 8e5849c..bd39908 100644 --- a/app/boxes.py +++ b/app/boxes.py @@ -297,6 +297,7 @@ async def send_create( poll_type: str | None = None, poll_answers: list[str] | None = None, poll_duration_in_minutes: int | None = None, + name: str | None = None, ) -> str: note_id = allocate_outbox_id() published = now().replace(microsecond=0).isoformat().replace("+00:00", "Z") @@ -367,6 +368,11 @@ async def send_create( for answer in poll_answers ], } + elif ap_type == "Article": + if not name: + raise ValueError("Article must have a name") + + extra_obj_attrs = {"name": name} obj = { "@context": ap.AS_EXTENDED_CTX, diff --git a/app/main.py b/app/main.py index fdc7d3f..22f43ce 100644 --- a/app/main.py +++ b/app/main.py @@ -215,6 +215,7 @@ async def index( models.OutboxObject.visibility == ap.VisibilityEnum.PUBLIC, models.OutboxObject.is_deleted.is_(False), models.OutboxObject.is_hidden_from_homepage.is_(False), + models.OutboxObject.ap_type != "Article", ) q = select(models.OutboxObject).where(*where) total_count = await db_session.scalar( @@ -257,6 +258,51 @@ async def index( ) +@app.get("/articles") +async def articles( + request: Request, + db_session: AsyncSession = Depends(get_db_session), + _: httpsig.HTTPSigInfo = Depends(httpsig.httpsig_checker), + page: int | None = None, +) -> templates.TemplateResponse | ActivityPubResponse: + # TODO: special ActivityPub collection for Article + + where = ( + models.OutboxObject.visibility == ap.VisibilityEnum.PUBLIC, + models.OutboxObject.is_deleted.is_(False), + models.OutboxObject.is_hidden_from_homepage.is_(False), + models.OutboxObject.ap_type == "Article", + ) + q = select(models.OutboxObject).where(*where) + + outbox_objects_result = await db_session.scalars( + q.options( + joinedload(models.OutboxObject.outbox_object_attachments).options( + joinedload(models.OutboxObjectAttachment.upload) + ), + joinedload(models.OutboxObject.relates_to_inbox_object).options( + joinedload(models.InboxObject.actor), + ), + joinedload(models.OutboxObject.relates_to_outbox_object).options( + joinedload(models.OutboxObject.outbox_object_attachments).options( + joinedload(models.OutboxObjectAttachment.upload) + ), + ), + ).order_by(models.OutboxObject.ap_published_at.desc()) + ) + outbox_objects = outbox_objects_result.unique().all() + + return await templates.render_template( + db_session, + request, + "articles.html", + { + "request": request, + "objects": outbox_objects, + }, + ) + + async def _build_followx_collection( db_session: AsyncSession, model_cls: Type[models.Following | models.Follower], diff --git a/app/scss/main.scss b/app/scss/main.scss index 664bf2a..fbcbd0f 100644 --- a/app/scss/main.scss +++ b/app/scss/main.scss @@ -142,6 +142,15 @@ footer { max-width: 50px; } } +#articles { + list-style-type: none; + margin: 30px 0; + padding: 0 20px; + li { + display: block; + } +} + #notifications, #followers, #following { ul { list-style-type: none; diff --git a/app/templates.py b/app/templates.py index e584a37..2c51bbe 100644 --- a/app/templates.py +++ b/app/templates.py @@ -109,6 +109,14 @@ async def render_template( ) if is_admin else 0, + "articles_count": await db_session.scalar( + select(func.count(models.OutboxObject.id)).where( + models.OutboxObject.visibility == ap.VisibilityEnum.PUBLIC, + models.OutboxObject.is_deleted.is_(False), + models.OutboxObject.is_hidden_from_homepage.is_(False), + models.OutboxObject.ap_type == "Article", + ) + ), "local_actor": LOCAL_ACTOR, "followers_count": await db_session.scalar( select(func.count(models.Follower.id)) diff --git a/app/templates/admin_new.html b/app/templates/admin_new.html index 2eb2cca..37e16af 100644 --- a/app/templates/admin_new.html +++ b/app/templates/admin_new.html @@ -15,7 +15,7 @@