
* invisibleify completely removed trees only (fixes #431) * fix visibility state for shadowbanned users. this also ends up moving some of the complexity out of the templates. * comments: remove unused variable * moderation state machine * no seriously this really should check for v not being None * fix shadowban state * fix visibility state * update stateful counters * don't use bespoke function for show_descendants * properly mock ModerationState for cron submissions * fix approval discrepency * remove treenukes for removed comments * show shadowbans as removed
185 lines
4.9 KiB
Python
185 lines
4.9 KiB
Python
import functools
|
|
from datetime import datetime
|
|
|
|
from sqlalchemy.orm import relationship
|
|
from sqlalchemy.schema import Column, ForeignKey
|
|
from sqlalchemy.sql.sqltypes import Boolean, Integer, String, Text
|
|
|
|
from files.classes.cron.tasks import (RepeatableTask, ScheduledTaskType,
|
|
TaskRunContext)
|
|
from files.classes.submission import Submission
|
|
from files.helpers.config.const import SUBMISSION_TITLE_LENGTH_MAXIMUM
|
|
from files.helpers.content import ModerationState, body_displayed
|
|
from files.helpers.lazy import lazy
|
|
from files.helpers.sanitize import filter_emojis_only
|
|
|
|
__all__ = ('ScheduledSubmissionTask',)
|
|
|
|
|
|
class ScheduledSubmissionTask(RepeatableTask):
|
|
__tablename__ = "tasks_repeatable_scheduled_submissions"
|
|
|
|
__mapper_args__ = {
|
|
"polymorphic_identity": int(ScheduledTaskType.SCHEDULED_SUBMISSION),
|
|
}
|
|
|
|
id = Column(Integer, ForeignKey(RepeatableTask.id), primary_key=True)
|
|
author_id_submission = Column(Integer, ForeignKey("users.id"), nullable=False)
|
|
ghost = Column(Boolean, default=False, nullable=False)
|
|
private = Column(Boolean, default=False, nullable=False)
|
|
over_18 = Column(Boolean, default=False, nullable=False)
|
|
is_bot = Column(Boolean, default=False, nullable=False)
|
|
title = Column(String(SUBMISSION_TITLE_LENGTH_MAXIMUM), nullable=False)
|
|
url = Column(String)
|
|
body = Column(Text)
|
|
body_html = Column(Text)
|
|
flair = Column(String)
|
|
embed_url = Column(String)
|
|
|
|
author = relationship("User", foreign_keys=author_id_submission)
|
|
task = relationship(RepeatableTask)
|
|
submissions = relationship(Submission,
|
|
back_populates="task", order_by="Submission.id.desc()")
|
|
|
|
def run_task(self, ctx:TaskRunContext) -> None:
|
|
submission:Submission = self.make_submission(ctx)
|
|
with ctx.app_context():
|
|
# TODO: stop using app context (currently required for sanitize and
|
|
# username pings)
|
|
submission.submit(ctx.db) # TODO: thumbnails
|
|
submission.publish()
|
|
|
|
def make_submission(self, ctx:TaskRunContext) -> Submission:
|
|
title:str = self.make_title(ctx.trigger_time)
|
|
title_html:str = filter_emojis_only(title, graceful=True)
|
|
if len(title_html) > 1500: raise ValueError("Rendered title too large")
|
|
|
|
return Submission(
|
|
created_utc=int(ctx.trigger_time.timestamp()),
|
|
private=self.private,
|
|
author_id=self.author_id_submission,
|
|
over_18=self.over_18,
|
|
app_id=None,
|
|
is_bot =self.is_bot,
|
|
title=title,
|
|
title_html=title_html,
|
|
url=self.url,
|
|
body=self.body,
|
|
body_html=self.body_html,
|
|
flair=self.flair,
|
|
ghost=self.ghost,
|
|
filter_state='normal',
|
|
embed_url=self.embed_url,
|
|
task_id=self.id,
|
|
)
|
|
|
|
def make_title(self, trigger_time:datetime) -> str:
|
|
return trigger_time.strftime(self.title)
|
|
|
|
# properties below here are mocked in order to reuse part of the submission
|
|
# HTML template for previewing a submitted task
|
|
|
|
@property
|
|
def deleted_utc(self) -> int:
|
|
return int(not self.task.enabled)
|
|
|
|
@functools.cached_property
|
|
def title_html(self) -> str:
|
|
'''
|
|
This is used as a mock property for display in submission listings that
|
|
contain scheduled posts.
|
|
|
|
.. warning::
|
|
This property should not be used for generating the HTML for an actual
|
|
submission as this will be missing the special formatting that may be
|
|
applies to titles. Instead call
|
|
`ScheduledSubmissionContext.make_title()` with the `datetime` that the
|
|
event was triggered at.
|
|
'''
|
|
return filter_emojis_only(self.title)
|
|
|
|
@property
|
|
def author_name(self) -> str:
|
|
return self.author.username
|
|
|
|
@property
|
|
def upvotes(self) -> int:
|
|
return 1
|
|
|
|
@property
|
|
def score(self) -> int:
|
|
return 1
|
|
|
|
@property
|
|
def downvotes(self) -> int:
|
|
return 0
|
|
|
|
@property
|
|
def realupvotes(self) -> int:
|
|
return 1
|
|
|
|
@property
|
|
def comment_count(self) -> int:
|
|
return 0
|
|
|
|
@property
|
|
def views(self) -> int:
|
|
return 0
|
|
|
|
@property
|
|
def filter_state(self) -> str:
|
|
return 'normal'
|
|
|
|
def award_count(self, kind):
|
|
return 0
|
|
|
|
@lazy
|
|
def realurl(self, v):
|
|
return Submission.realurl(self, v)
|
|
|
|
def realbody(self, v):
|
|
return body_displayed(self, v, is_html=True)
|
|
|
|
def plainbody(self, v):
|
|
return body_displayed(self, v, is_html=False)
|
|
|
|
@lazy
|
|
def realtitle(self, v):
|
|
return self.title_html if self.title_html else self.title
|
|
|
|
@lazy
|
|
def plaintitle(self, v):
|
|
return self.title
|
|
|
|
@property
|
|
def permalink(self):
|
|
return f"/tasks/scheduled_posts/{self.id}"
|
|
|
|
@property
|
|
def shortlink(self):
|
|
return self.permalink
|
|
|
|
@property
|
|
def is_real_submission(self) -> bool:
|
|
return False
|
|
|
|
@property
|
|
def should_hide_score(self) -> bool:
|
|
return True
|
|
|
|
@property
|
|
def edit_url(self) -> str:
|
|
return f"/tasks/scheduled_posts/{self.id}/content"
|
|
|
|
@property
|
|
def moderation_state(self) -> ModerationState:
|
|
return ModerationState(
|
|
removed=False,
|
|
removed_by_name=None,
|
|
deleted=False, # we only want to show deleted UI color if disabled
|
|
reports_ignored=False,
|
|
filtered=False,
|
|
op_shadowbanned=False,
|
|
op_id=self.author_id_submission,
|
|
op_name_safe=self.author_name
|
|
)
|