split post creation and editing up into different forms for each type of content #78

This commit is contained in:
rimu 2024-04-07 11:15:04 +12:00
parent db99ea33e9
commit b8ddf0c481
14 changed files with 1064 additions and 558 deletions

View file

@ -85,21 +85,23 @@ class BanUserCommunityForm(FlaskForm):
submit = SubmitField(_l('Ban')) submit = SubmitField(_l('Ban'))
class CreatePostForm(FlaskForm): class CreateDiscussionForm(FlaskForm):
communities = SelectField(_l('Community'), validators=[DataRequired()], coerce=int) communities = SelectField(_l('Community'), validators=[DataRequired()], coerce=int)
post_type = HiddenField() # https://getbootstrap.com/docs/4.6/components/navs/#tabs discussion_title = StringField(_l('Title'), validators=[DataRequired(), Length(min=3, max=255)])
discussion_title = StringField(_l('Title'), validators=[Optional(), Length(min=3, max=255)]) discussion_body = TextAreaField(_l('Body'), validators=[Optional(), Length(min=3, max=5000)])
discussion_body = TextAreaField(_l('Body'), validators=[Optional(), Length(min=3, max=5000)], render_kw={'placeholder': 'Text (optional)'}) sticky = BooleanField(_l('Sticky'))
link_title = StringField(_l('Title'), validators=[Optional(), Length(min=3, max=255)]) nsfw = BooleanField(_l('NSFW'))
link_body = TextAreaField(_l('Body'), validators=[Optional(), Length(min=3, max=5000)], nsfl = BooleanField(_l('Gore/gross'))
render_kw={'placeholder': 'Text (optional)'}) notify_author = BooleanField(_l('Notify about replies'))
link_url = StringField(_l('URL'), validators=[Optional(), Regexp(r'^https?://', message='Submitted links need to start with "http://"" or "https://"')], render_kw={'placeholder': 'https://...'}) submit = SubmitField(_l('Save'))
image_title = StringField(_l('Title'), validators=[Optional(), Length(min=3, max=255)])
image_alt_text = StringField(_l('Alt text'), validators=[Optional(), Length(min=3, max=255)])
image_body = TextAreaField(_l('Body'), validators=[Optional(), Length(min=3, max=5000)], class CreateLinkForm(FlaskForm):
render_kw={'placeholder': 'Text (optional)'}) communities = SelectField(_l('Community'), validators=[DataRequired()], coerce=int)
image_file = FileField(_('Image')) link_title = StringField(_l('Title'), validators=[DataRequired(), Length(min=3, max=255)])
# flair = SelectField(_l('Flair'), coerce=int) link_body = TextAreaField(_l('Body'), validators=[Optional(), Length(min=3, max=5000)])
link_url = StringField(_l('URL'), validators=[DataRequired(), Regexp(r'^https?://', message='Submitted links need to start with "http://"" or "https://"')],
render_kw={'placeholder': 'https://...'})
sticky = BooleanField(_l('Sticky')) sticky = BooleanField(_l('Sticky'))
nsfw = BooleanField(_l('NSFW')) nsfw = BooleanField(_l('NSFW'))
nsfl = BooleanField(_l('Gore/gross')) nsfl = BooleanField(_l('Gore/gross'))
@ -107,33 +109,26 @@ class CreatePostForm(FlaskForm):
submit = SubmitField(_l('Save')) submit = SubmitField(_l('Save'))
def validate(self, extra_validators=None) -> bool: def validate(self, extra_validators=None) -> bool:
if not super().validate():
return False
if self.post_type.data is None or self.post_type.data == '':
self.post_type.data = 'discussion'
if self.post_type.data == 'discussion':
if self.discussion_title.data == '':
self.discussion_title.errors.append(_('Title is required.'))
return False
elif self.post_type.data == 'link':
if self.link_title.data == '':
self.link_title.errors.append(_('Title is required.'))
return False
if self.link_url.data == '':
self.link_url.errors.append(_('URL is required.'))
return False
domain = domain_from_url(self.link_url.data, create=False) domain = domain_from_url(self.link_url.data, create=False)
if domain and domain.banned: if domain and domain.banned:
self.link_url.errors.append(_("Links to %(domain)s are not allowed.", domain=domain.name)) self.link_url.errors.append(_("Links to %(domain)s are not allowed.", domain=domain.name))
return False return False
elif self.post_type.data == 'image': return True
if self.image_title.data == '':
self.image_title.errors.append(_('Title is required.'))
return False class CreateImageForm(FlaskForm):
if self.image_file.data == '': communities = SelectField(_l('Community'), validators=[DataRequired()], coerce=int)
self.image_file.errors.append(_('File is required.')) image_title = StringField(_l('Title'), validators=[DataRequired(), Length(min=3, max=255)])
return False image_alt_text = StringField(_l('Alt text'), validators=[Optional(), Length(min=3, max=255)])
image_body = TextAreaField(_l('Body'), validators=[Optional(), Length(min=3, max=5000)])
image_file = FileField(_('Image'), validators=[DataRequired()])
sticky = BooleanField(_l('Sticky'))
nsfw = BooleanField(_l('NSFW'))
nsfl = BooleanField(_l('Gore/gross'))
notify_author = BooleanField(_l('Notify about replies'))
submit = SubmitField(_l('Save'))
def validate(self, extra_validators=None) -> bool:
uploaded_file = request.files['image_file'] uploaded_file = request.files['image_file']
if uploaded_file and uploaded_file.filename != '': if uploaded_file and uploaded_file.filename != '':
Image.MAX_IMAGE_PIXELS = 89478485 Image.MAX_IMAGE_PIXELS = 89478485
@ -142,8 +137,10 @@ class CreatePostForm(FlaskForm):
image_text = pytesseract.image_to_string(Image.open(BytesIO(uploaded_file.read())).convert('L')) image_text = pytesseract.image_to_string(Image.open(BytesIO(uploaded_file.read())).convert('L'))
except FileNotFoundError as e: except FileNotFoundError as e:
image_text = '' image_text = ''
if 'Anonymous' in image_text and ('No.' in image_text or ' N0' in image_text): # chan posts usually contain the text 'Anonymous' and ' No.12345' if 'Anonymous' in image_text and (
self.image_file.errors.append(f"This image is an invalid file type.") # deliberately misleading error message 'No.' in image_text or ' N0' in image_text): # chan posts usually contain the text 'Anonymous' and ' No.12345'
self.image_file.errors.append(
f"This image is an invalid file type.") # deliberately misleading error message
current_user.reputation -= 1 current_user.reputation -= 1
db.session.commit() db.session.commit()
return False return False
@ -151,9 +148,6 @@ class CreatePostForm(FlaskForm):
community = Community.query.get(self.communities.data) community = Community.query.get(self.communities.data)
if community.is_local() and g.site.allow_local_image_posts is False: if community.is_local() and g.site.allow_local_image_posts is False:
self.communities.errors.append(_('Images cannot be posted to local communities.')) self.communities.errors.append(_('Images cannot be posted to local communities.'))
elif self.post_type.data == 'poll':
self.discussion_title.errors.append(_('Poll not implemented yet.'))
return False
return True return True

View file

@ -12,7 +12,7 @@ from app import db, constants, cache
from app.activitypub.signature import RsaKeys, post_request from app.activitypub.signature import RsaKeys, post_request
from app.activitypub.util import default_context, notify_about_post, find_actor_or_create, make_image_sizes from app.activitypub.util import default_context, notify_about_post, find_actor_or_create, make_image_sizes
from app.chat.util import send_message from app.chat.util import send_message
from app.community.forms import SearchRemoteCommunity, CreatePostForm, ReportCommunityForm, \ from app.community.forms import SearchRemoteCommunity, CreateDiscussionForm, CreateImageForm, CreateLinkForm, ReportCommunityForm, \
DeleteCommunityForm, AddCommunityForm, EditCommunityForm, AddModeratorForm, BanUserCommunityForm, \ DeleteCommunityForm, AddCommunityForm, EditCommunityForm, AddModeratorForm, BanUserCommunityForm, \
EscalateReportForm, ResolveReportForm EscalateReportForm, ResolveReportForm
from app.community.util import search_for_community, community_url_exists, actor_to_community, \ from app.community.util import search_for_community, community_url_exists, actor_to_community, \
@ -444,11 +444,13 @@ def join_then_add(actor):
@bp.route('/<actor>/submit', methods=['GET', 'POST']) @bp.route('/<actor>/submit', methods=['GET', 'POST'])
@login_required @login_required
@validation_required @validation_required
def add_post(actor): def add_discussion_post(actor):
if current_user.banned: if current_user.banned:
return show_ban_message() return show_ban_message()
community = actor_to_community(actor) community = actor_to_community(actor)
form = CreatePostForm()
form = CreateDiscussionForm()
if g.site.enable_nsfl is False: if g.site.enable_nsfl is False:
form.nsfl.render_kw = {'disabled': True} form.nsfl.render_kw = {'disabled': True}
if community.nsfw: if community.nsfw:
@ -470,7 +472,63 @@ def add_post(actor):
if not can_create_post(current_user, community): if not can_create_post(current_user, community):
abort(401) abort(401)
post = Post(user_id=current_user.id, community_id=form.communities.data, instance_id=1) post = Post(user_id=current_user.id, community_id=form.communities.data, instance_id=1)
save_post(form, post) save_post(form, post, 'discussion')
community.post_count += 1
community.last_active = g.site.last_active = utcnow()
db.session.commit()
post.ap_id = f"https://{current_app.config['SERVER_NAME']}/post/{post.id}"
db.session.commit()
notify_about_post(post)
if not community.local_only:
federate_post(community, post)
return redirect(f"/c/{community.link()}")
else:
form.communities.data = community.id
form.notify_author.data = True
return render_template('community/add_discussion_post.html', title=_('Add post to community'), form=form, community=community,
markdown_editor=current_user.markdown_editor, low_bandwidth=False, actor=actor,
moderating_communities=moderating_communities(current_user.get_id()),
joined_communities=joined_communities(current_user.id),
inoculation=inoculation[randint(0, len(inoculation) - 1)]
)
@bp.route('/<actor>/submit_image', methods=['GET', 'POST'])
@login_required
@validation_required
def add_image_post(actor):
if current_user.banned:
return show_ban_message()
community = actor_to_community(actor)
form = CreateImageForm()
if g.site.enable_nsfl is False:
form.nsfl.render_kw = {'disabled': True}
if community.nsfw:
form.nsfw.data = True
form.nsfw.render_kw = {'disabled': True}
if community.nsfl:
form.nsfl.data = True
form.nsfw.render_kw = {'disabled': True}
if not(community.is_moderator() or community.is_owner() or current_user.is_admin()):
form.sticky.render_kw = {'disabled': True}
form.communities.choices = [(c.id, c.display_name()) for c in current_user.communities()]
if not can_create_post(current_user, community):
abort(401)
if form.validate_on_submit():
community = Community.query.get_or_404(form.communities.data)
if not can_create_post(current_user, community):
abort(401)
post = Post(user_id=current_user.id, community_id=form.communities.data, instance_id=1)
save_post(form, post, 'image')
community.post_count += 1 community.post_count += 1
community.last_active = g.site.last_active = utcnow() community.last_active = g.site.last_active = utcnow()
db.session.commit() db.session.commit()
@ -497,6 +555,95 @@ def add_post(actor):
notify_about_post(post) notify_about_post(post)
if not community.local_only: if not community.local_only:
federate_post(community, post)
return redirect(f"/c/{community.link()}")
else:
form.communities.data = community.id
form.notify_author.data = True
return render_template('community/add_image_post.html', title=_('Add post to community'), form=form, community=community,
markdown_editor=current_user.markdown_editor, low_bandwidth=False, actor=actor,
moderating_communities=moderating_communities(current_user.get_id()),
joined_communities=joined_communities(current_user.id),
inoculation=inoculation[randint(0, len(inoculation) - 1)]
)
@bp.route('/<actor>/submit_link', methods=['GET', 'POST'])
@login_required
@validation_required
def add_link_post(actor):
if current_user.banned:
return show_ban_message()
community = actor_to_community(actor)
form = CreateLinkForm()
if g.site.enable_nsfl is False:
form.nsfl.render_kw = {'disabled': True}
if community.nsfw:
form.nsfw.data = True
form.nsfw.render_kw = {'disabled': True}
if community.nsfl:
form.nsfl.data = True
form.nsfw.render_kw = {'disabled': True}
if not(community.is_moderator() or community.is_owner() or current_user.is_admin()):
form.sticky.render_kw = {'disabled': True}
form.communities.choices = [(c.id, c.display_name()) for c in current_user.communities()]
if not can_create_post(current_user, community):
abort(401)
if form.validate_on_submit():
community = Community.query.get_or_404(form.communities.data)
if not can_create_post(current_user, community):
abort(401)
post = Post(user_id=current_user.id, community_id=form.communities.data, instance_id=1)
save_post(form, post, 'link')
community.post_count += 1
community.last_active = g.site.last_active = utcnow()
db.session.commit()
post.ap_id = f"https://{current_app.config['SERVER_NAME']}/post/{post.id}"
db.session.commit()
if post.image_id and post.image.file_path is None:
make_image_sizes(post.image_id, 150, 512, 'posts') # the 512 sized image is for masonry view
# Update list of cross posts
if post.url:
other_posts = Post.query.filter(Post.id != post.id, Post.url == post.url,
Post.posted_at > post.posted_at - timedelta(days=6)).all()
for op in other_posts:
if op.cross_posts is None:
op.cross_posts = [post.id]
else:
op.cross_posts.append(post.id)
if post.cross_posts is None:
post.cross_posts = [op.id]
else:
post.cross_posts.append(op.id)
db.session.commit()
notify_about_post(post)
if not community.local_only:
federate_post(community, post)
return redirect(f"/c/{community.link()}")
else:
form.communities.data = community.id
form.notify_author.data = True
return render_template('community/add_link_post.html', title=_('Add post to community'), form=form, community=community,
markdown_editor=current_user.markdown_editor, low_bandwidth=False, actor=actor,
moderating_communities=moderating_communities(current_user.get_id()),
joined_communities=joined_communities(current_user.id),
inoculation=inoculation[randint(0, len(inoculation) - 1)]
)
def federate_post(community, post):
page = { page = {
'type': 'Page', 'type': 'Page',
'id': post.ap_id, 'id': post.ap_id,
@ -539,15 +686,18 @@ def add_post(actor):
page['attachment'] = [{'href': post.url, 'type': 'Link'}] page['attachment'] = [{'href': post.url, 'type': 'Link'}]
elif post.image_id: elif post.image_id:
if post.image.file_path: if post.image.file_path:
image_url = post.image.file_path.replace('app/static/', f"https://{current_app.config['SERVER_NAME']}/static/") image_url = post.image.file_path.replace('app/static/',
f"https://{current_app.config['SERVER_NAME']}/static/")
elif post.image.thumbnail_path: elif post.image.thumbnail_path:
image_url = post.image.thumbnail_path.replace('app/static/', f"https://{current_app.config['SERVER_NAME']}/static/") image_url = post.image.thumbnail_path.replace('app/static/',
f"https://{current_app.config['SERVER_NAME']}/static/")
else: else:
image_url = post.image.source_url image_url = post.image.source_url
# NB image is a dict while attachment is a list of dicts (usually just one dict in the list) # NB image is a dict while attachment is a list of dicts (usually just one dict in the list)
page['image'] = {'type': 'Image', 'url': image_url} page['image'] = {'type': 'Image', 'url': image_url}
if post.type == POST_TYPE_IMAGE: if post.type == POST_TYPE_IMAGE:
page['attachment'] = [{'type': 'Link', 'href': post.image.source_url}] # source_url is always a https link, no need for .replace() as done above page['attachment'] = [{'type': 'Link',
'href': post.image.source_url}] # source_url is always a https link, no need for .replace() as done above
if not community.is_local(): # this is a remote community - send the post to the instance that hosts it if not community.is_local(): # this is a remote community - send the post to the instance that hosts it
success = post_request(community.ap_inbox_url, create, current_user.private_key, success = post_request(community.ap_inbox_url, create, current_user.private_key,
current_user.ap_profile_id + '#main-key') current_user.ap_profile_id + '#main-key')
@ -572,7 +722,8 @@ def add_post(actor):
sent_to = 0 sent_to = 0
for instance in community.following_instances(): for instance in community.following_instances():
if instance.inbox and not current_user.has_blocked_instance(instance.id) and not instance_banned(instance.domain): if instance.inbox and not current_user.has_blocked_instance(instance.id) and not instance_banned(
instance.domain):
send_to_remote_instance(instance.id, community.id, announce) send_to_remote_instance(instance.id, community.id, announce)
sent_to += 1 sent_to += 1
if sent_to: if sent_to:
@ -580,21 +731,6 @@ def add_post(actor):
else: else:
flash(_('Your post to %(name)s has been made.', name=community.title)) flash(_('Your post to %(name)s has been made.', name=community.title))
return redirect(f"/c/{community.link()}")
else:
# when request.form has some data in it, it means form validation failed. Set the post_type so the correct tab is shown. See setupPostTypeTabs() in scripts.js
if request.form.get('post_type', None):
form.post_type.data = request.form.get('post_type')
form.communities.data = community.id
form.notify_author.data = True
return render_template('community/add_post.html', title=_('Add post to community'), form=form, community=community,
markdown_editor=current_user.markdown_editor, low_bandwidth=False,
moderating_communities=moderating_communities(current_user.get_id()),
joined_communities=joined_communities(current_user.id),
inoculation=inoculation[randint(0, len(inoculation) - 1)]
)
@bp.route('/community/<int:community_id>/report', methods=['GET', 'POST']) @bp.route('/community/<int:community_id>/report', methods=['GET', 'POST'])
@login_required @login_required

View file

@ -196,18 +196,18 @@ def url_to_thumbnail_file(filename) -> File:
source_url=filename) source_url=filename)
def save_post(form, post: Post): def save_post(form, post: Post, type: str):
post.indexable = current_user.indexable post.indexable = current_user.indexable
post.sticky = form.sticky.data post.sticky = form.sticky.data
post.nsfw = form.nsfw.data post.nsfw = form.nsfw.data
post.nsfl = form.nsfl.data post.nsfl = form.nsfl.data
post.notify_author = form.notify_author.data post.notify_author = form.notify_author.data
if form.post_type.data == '' or form.post_type.data == 'discussion': if type == '' or type == 'discussion':
post.title = form.discussion_title.data post.title = form.discussion_title.data
post.body = form.discussion_body.data post.body = form.discussion_body.data
post.body_html = markdown_to_html(post.body) post.body_html = markdown_to_html(post.body)
post.type = POST_TYPE_ARTICLE post.type = POST_TYPE_ARTICLE
elif form.post_type.data == 'link': elif type == 'link':
post.title = form.link_title.data post.title = form.link_title.data
post.body = form.link_body.data post.body = form.link_body.data
post.body_html = markdown_to_html(post.body) post.body_html = markdown_to_html(post.body)
@ -244,7 +244,7 @@ def save_post(form, post: Post):
post.image = file post.image = file
db.session.add(file) db.session.add(file)
elif form.post_type.data == 'image': elif type == 'image':
post.title = form.image_title.data post.title = form.image_title.data
post.body = form.image_body.data post.body = form.image_body.data
post.body_html = markdown_to_html(post.body) post.body_html = markdown_to_html(post.body)
@ -304,7 +304,7 @@ def save_post(form, post: Post):
post.image = file post.image = file
db.session.add(file) db.session.add(file)
elif form.post_type.data == 'poll': elif type == 'poll':
... ...
else: else:
raise Exception('invalid post type') raise Exception('invalid post type')

View file

@ -13,9 +13,9 @@ from app.activitypub.util import default_context
from app.community.util import save_post, send_to_remote_instance from app.community.util import save_post, send_to_remote_instance
from app.inoculation import inoculation from app.inoculation import inoculation
from app.post.forms import NewReplyForm, ReportPostForm, MeaCulpaForm from app.post.forms import NewReplyForm, ReportPostForm, MeaCulpaForm
from app.community.forms import CreatePostForm from app.community.forms import CreateLinkForm, CreateImageForm, CreateDiscussionForm
from app.post.util import post_replies, get_comment_branch, post_reply_count from app.post.util import post_replies, get_comment_branch, post_reply_count
from app.constants import SUBSCRIPTION_MEMBER, POST_TYPE_LINK, POST_TYPE_IMAGE from app.constants import SUBSCRIPTION_MEMBER, POST_TYPE_LINK, POST_TYPE_IMAGE, POST_TYPE_ARTICLE
from app.models import Post, PostReply, \ from app.models import Post, PostReply, \
PostReplyVote, PostVote, Notification, utcnow, UserBlock, DomainBlock, InstanceBlock, Report, Site, Community, \ PostReplyVote, PostVote, Notification, utcnow, UserBlock, DomainBlock, InstanceBlock, Report, Site, Community, \
Topic, User, Instance Topic, User, Instance
@ -654,11 +654,82 @@ def post_reply_options(post_id: int, comment_id: int):
) )
@bp.route('/post/<int:post_id>/edit', methods=['GET', 'POST']) @bp.route('/post/<int:post_id>/edit', methods=['GET'])
@login_required @login_required
def post_edit(post_id: int): def post_edit(post_id: int):
post = Post.query.get_or_404(post_id) post = Post.query.get_or_404(post_id)
form = CreatePostForm() if post.type == POST_TYPE_ARTICLE:
return redirect(url_for('post.post_edit_discussion_post', post_id=post_id))
elif post.type == POST_TYPE_LINK:
return redirect(url_for('post.post_edit_link_post', post_id=post_id))
elif post.type == POST_TYPE_IMAGE:
return redirect(url_for('post.post_edit_image_post', post_id=post_id))
else:
abort(404)
@bp.route('/post/<int:post_id>/edit_discussion', methods=['GET', 'POST'])
@login_required
def post_edit_discussion_post(post_id: int):
post = Post.query.get_or_404(post_id)
form = CreateDiscussionForm()
del form.communities
mods = post.community.moderators()
if post.community.private_mods:
mod_list = []
else:
mod_user_ids = [mod.user_id for mod in mods]
mod_list = User.query.filter(User.id.in_(mod_user_ids)).all()
if post.user_id == current_user.id or post.community.is_moderator() or current_user.is_admin():
if g.site.enable_nsfl is False:
form.nsfl.render_kw = {'disabled': True}
if post.community.nsfw:
form.nsfw.data = True
form.nsfw.render_kw = {'disabled': True}
if post.community.nsfl:
form.nsfl.data = True
form.nsfw.render_kw = {'disabled': True}
if form.validate_on_submit():
save_post(form, post, 'discussion')
post.community.last_active = utcnow()
post.edited_at = utcnow()
db.session.commit()
post.flush_cache()
flash(_('Your changes have been saved.'), 'success')
# federate edit
if not post.community.local_only:
federate_post_update(post)
return redirect(url_for('activitypub.post_ap', post_id=post.id))
else:
form.discussion_title.data = post.title
form.discussion_body.data = post.body
form.notify_author.data = post.notify_author
form.nsfw.data = post.nsfw
form.nsfl.data = post.nsfl
form.sticky.data = post.sticky
if not (post.community.is_moderator() or post.community.is_owner() or current_user.is_admin()):
form.sticky.render_kw = {'disabled': True}
return render_template('post/post_edit_discussion.html', title=_('Edit post'), form=form, post=post,
markdown_editor=current_user.markdown_editor, mods=mod_list,
moderating_communities=moderating_communities(current_user.get_id()),
joined_communities=joined_communities(current_user.get_id()),
inoculation=inoculation[randint(0, len(inoculation) - 1)]
)
else:
abort(401)
@bp.route('/post/<int:post_id>/edit_image', methods=['GET', 'POST'])
@login_required
def post_edit_image_post(post_id: int):
post = Post.query.get_or_404(post_id)
form = CreateImageForm()
del form.communities del form.communities
mods = post.community.moderators() mods = post.community.moderators()
@ -678,11 +749,10 @@ def post_edit(post_id: int):
form.nsfl.data = True form.nsfl.data = True
form.nsfw.render_kw = {'disabled': True} form.nsfw.render_kw = {'disabled': True}
#form.communities.choices = [(c.id, c.display_name()) for c in current_user.communities()]
old_url = post.url old_url = post.url
if form.validate_on_submit(): if form.validate_on_submit():
save_post(form, post) save_post(form, post, 'image')
post.community.last_active = utcnow() post.community.last_active = utcnow()
post.edited_at = utcnow() post.edited_at = utcnow()
db.session.commit() db.session.commit()
@ -714,6 +784,112 @@ def post_edit(post_id: int):
# federate edit # federate edit
if not post.community.local_only: if not post.community.local_only:
federate_post_update(post)
return redirect(url_for('activitypub.post_ap', post_id=post.id))
else:
form.image_title.data = post.title
form.image_body.data = post.body
form.image_alt_text.data = post.image.alt_text
form.notify_author.data = post.notify_author
form.nsfw.data = post.nsfw
form.nsfl.data = post.nsfl
form.sticky.data = post.sticky
if not (post.community.is_moderator() or post.community.is_owner() or current_user.is_admin()):
form.sticky.render_kw = {'disabled': True}
return render_template('post/post_edit_image.html', title=_('Edit post'), form=form, post=post,
markdown_editor=current_user.markdown_editor, mods=mod_list,
moderating_communities=moderating_communities(current_user.get_id()),
joined_communities=joined_communities(current_user.get_id()),
inoculation=inoculation[randint(0, len(inoculation) - 1)]
)
else:
abort(401)
@bp.route('/post/<int:post_id>/edit_link', methods=['GET', 'POST'])
@login_required
def post_edit_link_post(post_id: int):
post = Post.query.get_or_404(post_id)
form = CreateLinkForm()
del form.communities
mods = post.community.moderators()
if post.community.private_mods:
mod_list = []
else:
mod_user_ids = [mod.user_id for mod in mods]
mod_list = User.query.filter(User.id.in_(mod_user_ids)).all()
if post.user_id == current_user.id or post.community.is_moderator() or current_user.is_admin():
if g.site.enable_nsfl is False:
form.nsfl.render_kw = {'disabled': True}
if post.community.nsfw:
form.nsfw.data = True
form.nsfw.render_kw = {'disabled': True}
if post.community.nsfl:
form.nsfl.data = True
form.nsfw.render_kw = {'disabled': True}
old_url = post.url
if form.validate_on_submit():
save_post(form, post, 'link')
post.community.last_active = utcnow()
post.edited_at = utcnow()
db.session.commit()
if post.url != old_url:
if post.cross_posts is not None:
old_cross_posts = Post.query.filter(Post.id.in_(post.cross_posts)).all()
post.cross_posts.clear()
for ocp in old_cross_posts:
if ocp.cross_posts is not None:
ocp.cross_posts.remove(post.id)
new_cross_posts = Post.query.filter(Post.id != post.id, Post.url == post.url,
Post.posted_at > post.edited_at - timedelta(days=6)).all()
for ncp in new_cross_posts:
if ncp.cross_posts is None:
ncp.cross_posts = [post.id]
else:
ncp.cross_posts.append(post.id)
if post.cross_posts is None:
post.cross_posts = [ncp.id]
else:
post.cross_posts.append(ncp.id)
db.session.commit()
post.flush_cache()
flash(_('Your changes have been saved.'), 'success')
# federate edit
if not post.community.local_only:
federate_post_update(post)
return redirect(url_for('activitypub.post_ap', post_id=post.id))
else:
form.link_title.data = post.title
form.link_body.data = post.body
form.link_url.data = post.url
form.notify_author.data = post.notify_author
form.nsfw.data = post.nsfw
form.nsfl.data = post.nsfl
form.sticky.data = post.sticky
if not (post.community.is_moderator() or post.community.is_owner() or current_user.is_admin()):
form.sticky.render_kw = {'disabled': True}
return render_template('post/post_edit_link.html', title=_('Edit post'), form=form, post=post,
markdown_editor=current_user.markdown_editor, mods=mod_list,
moderating_communities=moderating_communities(current_user.get_id()),
joined_communities=joined_communities(current_user.get_id()),
inoculation=inoculation[randint(0, len(inoculation) - 1)]
)
else:
abort(401)
def federate_post_update(post):
page_json = { page_json = {
'type': 'Page', 'type': 'Page',
'id': post.ap_id, 'id': post.ap_id,
@ -755,16 +931,18 @@ def post_edit(post_id: int):
page_json['attachment'] = [{'href': post.url, 'type': 'Link'}] page_json['attachment'] = [{'href': post.url, 'type': 'Link'}]
elif post.image_id: elif post.image_id:
if post.image.file_path: if post.image.file_path:
image_url = post.image.file_path.replace('app/static/', f"https://{current_app.config['SERVER_NAME']}/static/") image_url = post.image.file_path.replace('app/static/',
f"https://{current_app.config['SERVER_NAME']}/static/")
elif post.image.thumbnail_path: elif post.image.thumbnail_path:
image_url = post.image.thumbnail_path.replace('app/static/', f"https://{current_app.config['SERVER_NAME']}/static/") image_url = post.image.thumbnail_path.replace('app/static/',
f"https://{current_app.config['SERVER_NAME']}/static/")
else: else:
image_url = post.image.source_url image_url = post.image.source_url
# NB image is a dict while attachment is a list of dicts (usually just one dict in the list) # NB image is a dict while attachment is a list of dicts (usually just one dict in the list)
page_json['image'] = {'type': 'Image', 'url': image_url} page_json['image'] = {'type': 'Image', 'url': image_url}
if post.type == POST_TYPE_IMAGE: if post.type == POST_TYPE_IMAGE:
page_json['attachment'] = [{'type': 'Link', 'href': post.image.source_url}] # source_url is always a https link, no need for .replace() as done above page_json['attachment'] = [{'type': 'Link',
'href': post.image.source_url}] # source_url is always a https link, no need for .replace() as done above
if not post.community.is_local(): # this is a remote community, send it to the instance that hosts it if not post.community.is_local(): # this is a remote community, send it to the instance that hosts it
success = post_request(post.community.ap_inbox_url, update_json, current_user.private_key, success = post_request(post.community.ap_inbox_url, update_json, current_user.private_key,
current_user.ap_profile_id + '#main-key') current_user.ap_profile_id + '#main-key')
@ -786,40 +964,10 @@ def post_edit(post_id: int):
} }
for instance in post.community.following_instances(): for instance in post.community.following_instances():
if instance.inbox and not current_user.has_blocked_instance(instance.id) and not instance_banned(instance.domain): if instance.inbox and not current_user.has_blocked_instance(instance.id) and not instance_banned(
instance.domain):
send_to_remote_instance(instance.id, post.community.id, announce) send_to_remote_instance(instance.id, post.community.id, announce)
return redirect(url_for('activitypub.post_ap', post_id=post.id))
else:
if post.type == constants.POST_TYPE_ARTICLE:
form.post_type.data = 'discussion'
form.discussion_title.data = post.title
form.discussion_body.data = post.body
elif post.type == constants.POST_TYPE_LINK:
form.post_type.data = 'link'
form.link_title.data = post.title
form.link_body.data = post.body
form.link_url.data = post.url
elif post.type == constants.POST_TYPE_IMAGE:
form.post_type.data = 'image'
form.image_title.data = post.title
form.image_body.data = post.body
form.image_alt_text.data = post.image.alt_text
form.notify_author.data = post.notify_author
form.nsfw.data = post.nsfw
form.nsfl.data = post.nsfl
form.sticky.data = post.sticky
if not (post.community.is_moderator() or post.community.is_owner() or current_user.is_admin()):
form.sticky.render_kw = {'disabled': True}
return render_template('post/post_edit.html', title=_('Edit post'), form=form, post=post,
markdown_editor=current_user.markdown_editor, mods=mod_list,
moderating_communities=moderating_communities(current_user.get_id()),
joined_communities=joined_communities(current_user.get_id()),
inoculation=inoculation[randint(0, len(inoculation) - 1)]
)
else:
abort(401)
@bp.route('/post/<int:post_id>/delete', methods=['GET', 'POST']) @bp.route('/post/<int:post_id>/delete', methods=['GET', 'POST'])
@login_required @login_required

View file

@ -501,6 +501,14 @@ fieldset legend {
.form-group { .form-group {
margin-bottom: 1.1rem; margin-bottom: 1.1rem;
} }
.form-group.required label:after {
content: "*";
color: red;
margin-left: 2px;
font-size: 80%;
position: relative;
top: -1px;
}
.card { .card {
max-width: 350px; max-width: 350px;

View file

@ -65,6 +65,19 @@ html {
.form-group { .form-group {
margin-bottom: 1.1rem; margin-bottom: 1.1rem;
&.required {
label {
&:after {
content: '*';
color: red;
margin-left: 2px;
font-size: 80%;
position: relative;
top: -1px;
}
}
}
} }
.card { .card {

View file

@ -0,0 +1,95 @@
{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %}
{% extends 'themes/' + theme() + '/base.html' %}
{% else %}
{% extends "base.html" %}
{% endif %} %}
{% from 'bootstrap/form.html' import render_field %}
{% block app_content %}
<div class="row">
<div class="col-12 col-md-8 position-relative main_pane">
<h1>{{ _('Create post') }}</h1>
<form method="post" enctype="multipart/form-data" role="form">
{{ form.csrf_token() }}
<div class="form-group">
<label class="form-control-label" for="type_of_post">
{{ _('Type of post') }}
</label>
<div id="type_of_post" class="btn-group flex-wrap" role="navigation">
<a href="{{ url_for('community.add_discussion_post', actor=actor) }}" class="btn btn-primary" aria-label="{{ _('Start a discussion') }}">{{ _('Discussion') }}</a>
<a href="{{ url_for('community.add_link_post', actor=actor) }}" class="btn btn-outline-secondary" aria-label="{{ _('Share a link') }}">{{ _('Link') }}</a>
<a href="{{ url_for('community.add_image_post', actor=actor) }}" class="btn btn-outline-secondary" aria-label="{{ _('Share an image') }}">{{ _('Image') }}</a>
<!-- <a href="#" class="btn" aria-label="{{ _('Create a poll') }}">{{ _('Poll') }}</a>
<a href="#" class="btn" aria-label="{{ _('Create an event') }}">{{ _('Event') }}</a> -->
</div>
</div>
{{ render_field(form.communities) }}
{{ render_field(form.discussion_title) }}
{{ render_field(form.discussion_body) }}
{% if not low_bandwidth %}
{% if markdown_editor %}
<script nonce="{{ session['nonce'] }}">
window.addEventListener("load", function () {
var downarea = new DownArea({
elem: document.querySelector('#discussion_body'),
resize: DownArea.RESIZE_VERTICAL,
hide: ['heading', 'bold-italic'],
});
setupAutoResize('discussion_body');
});
</script>
{% else %}
<a href="#" aria-hidden="true" class="markdown_editor_enabler create_post_markdown_editor_enabler" data-id="discussion_body">{{ _('Enable markdown editor') }}</a>
{% endif %}
{% endif %}
<div class="row mt-4">
<div class="col-md-3">
{{ render_field(form.notify_author) }}
</div>
<div class="col-md-1">
{{ render_field(form.sticky) }}
</div>
<div class="col-md-1">
{{ render_field(form.nsfw) }}
</div>
<div class="col-md-1">
{{ render_field(form.nsfl) }}
</div>
<div class="col">
</div>
</div>
{{ render_field(form.submit) }}
</form>
</div>
<aside id="side_pane" class="col-12 col-md-4 side_pane" role="complementary">
<div class="card mb-3">
<div class="card-header">
<h2>{{ community.title }}</h2>
</div>
<div class="card-body">
<p>{{ community.description_html|safe if community.description_html else '' }}</p>
<p>{{ community.rules_html|safe if community.rules_html else '' }}</p>
{% if len(mods) > 0 and not community.private_mods %}
<h3>Moderators</h3>
<ul class="moderator_list">
{% for mod in mods %}
<li>{{ render_username(mod) }}</li>
{% endfor %}
</ul>
{% endif %}
{% if rss_feed %}
<p class="mt-4">
<a class="no-underline" href="{{ rss_feed }}" rel="nofollow"><span class="fe fe-rss"></span> RSS feed</a>
</p>
{% endif %}
</div>
</div>
{% include "_inoculation_links.html" %}
</aside>
</div>
{% endblock %}

View file

@ -0,0 +1,97 @@
{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %}
{% extends 'themes/' + theme() + '/base.html' %}
{% else %}
{% extends "base.html" %}
{% endif %} %}
{% from 'bootstrap/form.html' import render_field %}
{% block app_content %}
<div class="row">
<div class="col-12 col-md-8 position-relative main_pane">
<h1>{{ _('Create post') }}</h1>
<form method="post" enctype="multipart/form-data" role="form">
{{ form.csrf_token() }}
<div class="form-group">
<label class="form-control-label" for="type_of_post">
{{ _('Type of post') }}
</label>
<div id="type_of_post" class="btn-group flex-wrap" role="navigation">
<a href="{{ url_for('community.add_discussion_post', actor=actor) }}" class="btn btn-outline-secondary" aria-label="{{ _('Start a discussion') }}">{{ _('Discussion') }}</a>
<a href="{{ url_for('community.add_link_post', actor=actor) }}" class="btn btn-outline-secondary" aria-label="{{ _('Share a link') }}">{{ _('Link') }}</a>
<a href="{{ url_for('community.add_image_post', actor=actor) }}" class="btn btn-primary" aria-label="{{ _('Share an image') }}">{{ _('Image') }}</a>
<!-- <a href="#" class="btn" aria-label="{{ _('Create a poll') }}">{{ _('Poll') }}</a>
<a href="#" class="btn" aria-label="{{ _('Create an event') }}">{{ _('Event') }}</a> -->
</div>
</div>
{{ render_field(form.communities) }}
{{ render_field(form.image_title) }}
{{ render_field(form.image_file) }}
{{ render_field(form.image_alt_text) }}
<small class="field_hint">{{ _('Describe the image, to help visually impaired people.') }}</small>
{{ render_field(form.image_body) }}
{% if not low_bandwidth %}
{% if markdown_editor %}
<script nonce="{{ session['nonce'] }}">
window.addEventListener("load", function () {
var downarea = new DownArea({
elem: document.querySelector('#image_body'),
resize: DownArea.RESIZE_VERTICAL,
hide: ['heading', 'bold-italic'],
});
setupAutoResize('image_body');
});
</script>
{% else %}
<a href="#" aria-hidden="true" class="markdown_editor_enabler create_post_markdown_editor_enabler" data-id="image_body">{{ _('Enable markdown editor') }}</a>
{% endif %}
{% endif %}
<div class="row mt-4">
<div class="col-md-3">
{{ render_field(form.notify_author) }}
</div>
<div class="col-md-1">
{{ render_field(form.sticky) }}
</div>
<div class="col-md-1">
{{ render_field(form.nsfw) }}
</div>
<div class="col-md-1">
{{ render_field(form.nsfl) }}
</div>
<div class="col">
</div>
</div>
{{ render_field(form.submit) }}
</form>
</div>
<aside id="side_pane" class="col-12 col-md-4 side_pane" role="complementary">
<div class="card mb-3">
<div class="card-header">
<h2>{{ community.title }}</h2>
</div>
<div class="card-body">
<p>{{ community.description_html|safe if community.description_html else '' }}</p>
<p>{{ community.rules_html|safe if community.rules_html else '' }}</p>
{% if len(mods) > 0 and not community.private_mods %}
<h3>Moderators</h3>
<ul class="moderator_list">
{% for mod in mods %}
<li>{{ render_username(mod) }}</li>
{% endfor %}
</ul>
{% endif %}
{% if rss_feed %}
<p class="mt-4">
<a class="no-underline" href="{{ rss_feed }}" rel="nofollow"><span class="fe fe-rss"></span> RSS feed</a>
</p>
{% endif %}
</div>
</div>
{% include "_inoculation_links.html" %}
</aside>
</div>
{% endblock %}

View file

@ -0,0 +1,96 @@
{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %}
{% extends 'themes/' + theme() + '/base.html' %}
{% else %}
{% extends "base.html" %}
{% endif %} %}
{% from 'bootstrap/form.html' import render_field %}
{% block app_content %}
<div class="row">
<div class="col-12 col-md-8 position-relative main_pane">
<h1>{{ _('Create post') }}</h1>
<form method="post" enctype="multipart/form-data" role="form">
{{ form.csrf_token() }}
<div class="form-group">
<label class="form-control-label" for="type_of_post">
{{ _('Type of post') }}
</label>
<div id="type_of_post" class="btn-group flex-wrap" role="navigation">
<a href="{{ url_for('community.add_discussion_post', actor=actor) }}" class="btn btn-outline-secondary" aria-label="{{ _('Start a discussion') }}">{{ _('Discussion') }}</a>
<a href="{{ url_for('community.add_link_post', actor=actor) }}" class="btn btn-primary" aria-label="{{ _('Share a link') }}">{{ _('Link') }}</a>
<a href="{{ url_for('community.add_image_post', actor=actor) }}" class="btn btn-outline-secondary" aria-label="{{ _('Share an image') }}">{{ _('Image') }}</a>
<!-- <a href="#" class="btn" aria-label="{{ _('Create a poll') }}">{{ _('Poll') }}</a>
<a href="#" class="btn" aria-label="{{ _('Create an event') }}">{{ _('Event') }}</a> -->
</div>
</div>
{{ render_field(form.communities) }}
{{ render_field(form.link_title) }}
{{ render_field(form.link_url) }}
{{ render_field(form.link_body) }}
{% if not low_bandwidth %}
{% if markdown_editor %}
<script nonce="{{ session['nonce'] }}">
window.addEventListener("load", function () {
var downarea = new DownArea({
elem: document.querySelector('#link_body'),
resize: DownArea.RESIZE_VERTICAL,
hide: ['heading', 'bold-italic'],
});
setupAutoResize('link_body');
});
</script>
{% else %}
<a href="#" aria-hidden="true" class="markdown_editor_enabler create_post_markdown_editor_enabler" data-id="link_body">{{ _('Enable markdown editor') }}</a>
{% endif %}
{% endif %}
<div class="row mt-4">
<div class="col-md-3">
{{ render_field(form.notify_author) }}
</div>
<div class="col-md-1">
{{ render_field(form.sticky) }}
</div>
<div class="col-md-1">
{{ render_field(form.nsfw) }}
</div>
<div class="col-md-1">
{{ render_field(form.nsfl) }}
</div>
<div class="col">
</div>
</div>
{{ render_field(form.submit) }}
</form>
</div>
<aside id="side_pane" class="col-12 col-md-4 side_pane" role="complementary">
<div class="card mb-3">
<div class="card-header">
<h2>{{ community.title }}</h2>
</div>
<div class="card-body">
<p>{{ community.description_html|safe if community.description_html else '' }}</p>
<p>{{ community.rules_html|safe if community.rules_html else '' }}</p>
{% if len(mods) > 0 and not community.private_mods %}
<h3>Moderators</h3>
<ul class="moderator_list">
{% for mod in mods %}
<li>{{ render_username(mod) }}</li>
{% endfor %}
</ul>
{% endif %}
{% if rss_feed %}
<p class="mt-4">
<a class="no-underline" href="{{ rss_feed }}" rel="nofollow"><span class="fe fe-rss"></span> RSS feed</a>
</p>
{% endif %}
</div>
</div>
{% include "_inoculation_links.html" %}
</aside>
</div>
{% endblock %}

View file

@ -1,145 +0,0 @@
{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %}
{% extends 'themes/' + theme() + '/base.html' %}
{% else %}
{% extends "base.html" %}
{% endif %} %}
{% from 'bootstrap/form.html' import render_field %}
{% block app_content %}
<div class="row">
<div class="col-12 col-md-8 position-relative main_pane">
<h1>{{ _('Create post') }}</h1>
<form method="post" enctype="multipart/form-data" role="form">
{{ form.csrf_token() }}
{{ render_field(form.communities) }}
<nav id="post_type_chooser">
<div class="nav nav-tabs nav-justified" id="typeTab" role="tablist">
<button class="nav-link active" id="discussion-tab" data-bs-toggle="tab" data-bs-target="#discussion-tab-pane"
type="button" role="tab" aria-controls="discussion-tab-pane" aria-selected="true">Discussion</button>
<button class="nav-link" id="link-tab" data-bs-toggle="tab" data-bs-target="#link-tab-pane"
type="button" role="tab" aria-controls="link-tab-pane" aria-selected="false">Link</button>
<button class="nav-link" id="image-tab" data-bs-toggle="tab" data-bs-target="#image-tab-pane"
type="button" role="tab" aria-controls="image-tab-pane" aria-selected="false">Image</button>
<button class="nav-link" id="poll-tab" data-bs-toggle="tab" data-bs-target="#poll-tab-pane"
type="button" role="tab" aria-controls="poll-tab-pane" aria-selected="false" disabled>Poll</button>
</div>
</nav>
<div class="tab-content" id="myTabContent">
<div class="tab-pane fade show active" id="discussion-tab-pane" role="tabpanel" aria-labelledby="home-tab" tabindex="0">
{{ render_field(form.discussion_title) }}
{{ render_field(form.discussion_body) }}
{% if not low_bandwidth %}
{% if markdown_editor %}
<script nonce="{{ session['nonce'] }}">
window.addEventListener("load", function () {
var downarea = new DownArea({
elem: document.querySelector('#discussion_body'),
resize: DownArea.RESIZE_VERTICAL,
hide: ['heading', 'bold-italic'],
});
setupAutoResize('discussion_body');
});
</script>
{% else %}
<a href="#" aria-hidden="true" class="markdown_editor_enabler create_post_markdown_editor_enabler" data-id="discussion_body">{{ _('Enable markdown editor') }}</a>
{% endif %}
{% endif %}
</div>
<div class="tab-pane fade" id="link-tab-pane" role="tabpanel" aria-labelledby="profile-tab" tabindex="0">
{{ render_field(form.link_title) }}
{{ render_field(form.link_url) }}
{{ render_field(form.link_body) }}
{% if not low_bandwidth %}
{% if markdown_editor %}
<script nonce="{{ session['nonce'] }}">
window.addEventListener("load", function () {
var downarea = new DownArea({
elem: document.querySelector('#link_body'),
resize: DownArea.RESIZE_VERTICAL,
hide: ['heading', 'bold-italic'],
});
setupAutoResize('link_body');
});
</script>
{% else %}
<a href="#" aria-hidden="true" class="markdown_editor_enabler create_post_markdown_editor_enabler" data-id="link_body">{{ _('Enable markdown editor') }}</a>
{% endif %}
{% endif %}
</div>
<div class="tab-pane fade" id="image-tab-pane" role="tabpanel" aria-labelledby="contact-tab" tabindex="0">
{{ render_field(form.image_title) }}
{{ render_field(form.image_file) }}
{{ render_field(form.image_alt_text) }}
<small class="field_hint">{{ _('Describe the image, to help visually impaired people.') }}</small>
{{ render_field(form.image_body) }}
{% if not low_bandwidth %}
{% if markdown_editor %}
<script nonce="{{ session['nonce'] }}">
window.addEventListener("load", function () {
var downarea = new DownArea({
elem: document.querySelector('#image_body'),
resize: DownArea.RESIZE_VERTICAL,
hide: ['heading', 'bold-italic'],
});
setupAutoResize('image_body');
});
</script>
{% else %}
<a href="#" aria-hidden="true" class="markdown_editor_enabler create_post_markdown_editor_enabler" data-id="image_body">{{ _('Enable markdown editor') }}</a>
{% endif %}
{% endif %}
</div>
<div class="tab-pane fade" id="poll-tab-pane" role="tabpanel" aria-labelledby="disabled-tab" tabindex="0">
Poll
</div>
</div>
{{ render_field(form.post_type) }}
<div class="row mt-4">
<div class="col-md-3">
{{ render_field(form.notify_author) }}
</div>
<div class="col-md-1">
{{ render_field(form.sticky) }}
</div>
<div class="col-md-1">
{{ render_field(form.nsfw) }}
</div>
<div class="col-md-1">
{{ render_field(form.nsfl) }}
</div>
<div class="col">
</div>
</div>
{{ render_field(form.submit) }}
</form>
</div>
<aside id="side_pane" class="col-12 col-md-4 side_pane" role="complementary">
<div class="card mb-3">
<div class="card-header">
<h2>{{ community.title }}</h2>
</div>
<div class="card-body">
<p>{{ community.description_html|safe if community.description_html else '' }}</p>
<p>{{ community.rules_html|safe if community.rules_html else '' }}</p>
{% if len(mods) > 0 and not community.private_mods %}
<h3>Moderators</h3>
<ul class="moderator_list">
{% for mod in mods %}
<li>{{ render_username(mod) }}</li>
{% endfor %}
</ul>
{% endif %}
{% if rss_feed %}
<p class="mt-4">
<a class="no-underline" href="{{ rss_feed }}" rel="nofollow"><span class="fe fe-rss"></span> RSS feed</a>
</p>
{% endif %}
</div>
</div>
{% include "_inoculation_links.html" %}
</aside>
</div>
{% endblock %}

View file

@ -1,164 +0,0 @@
{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %}
{% extends 'themes/' + theme() + '/base.html' %}
{% else %}
{% extends "base.html" %}
{% endif %} %}
{% from 'bootstrap/form.html' import render_form, render_field %}
{% block app_content %}
<script nonce="{{ session['nonce'] }}" type="text/javascript">
window.addEventListener("load", function () {
var type = document.forms[0].elements['type'].value;
var toClick = undefined;
switch(type) {
case '':
case 'discussion':
toClick = document.getElementById('discussion-tab');
break;
case 'link':
toClick = document.getElementById('link-tab');
break;
case 'image':
toClick = document.getElementById('image-tab');
break;
case 'poll':
toClick = document.getElementById('poll-tab');
break;
}
if(toClick) {
toClick.click();
}
var downarea = new DownArea({
elem: document.querySelector('#discussion_body'),
resize: DownArea.RESIZE_VERTICAL,
hide: ['heading', 'bold-italic'],
value: {{ form.discussion_body.data | tojson | safe }}
});
setupAutoResize('discussion_body');
});
</script>
<div class="row">
<div class="col-12 col-md-8 position-relative main_pane">
<h1>{{ _('Edit post') }}</h1>
<form method="post" enctype="multipart/form-data" role="form">
{{ form.csrf_token() }}
<nav id="post_type_chooser">
<div class="nav nav-tabs nav-justified" id="typeTab" role="tablist">
<button class="nav-link active" id="discussion-tab" data-bs-toggle="tab" data-bs-target="#discussion-tab-pane"
type="button" role="tab" aria-controls="discussion-tab-pane" aria-selected="true">Discussion</button>
<button class="nav-link" id="link-tab" data-bs-toggle="tab" data-bs-target="#link-tab-pane"
type="button" role="tab" aria-controls="link-tab-pane" aria-selected="false">Link</button>
<button class="nav-link" id="image-tab" data-bs-toggle="tab" data-bs-target="#image-tab-pane"
type="button" role="tab" aria-controls="image-tab-pane" aria-selected="false">Image</button>
<button class="nav-link" id="poll-tab" data-bs-toggle="tab" data-bs-target="#poll-tab-pane"
type="button" role="tab" aria-controls="poll-tab-pane" aria-selected="false" disabled>Poll</button>
</div>
</nav>
<div class="tab-content" id="myTabContent">
<div class="tab-pane fade show active" id="discussion-tab-pane" role="tabpanel" aria-labelledby="home-tab" tabindex="0">
{{ render_field(form.discussion_title) }}
{{ render_field(form.discussion_body) }}
{% if markdown_editor %}
<script nonce="{{ session['nonce'] }}">
window.addEventListener("load", function () {
var downarea = new DownArea({
elem: document.querySelector('#discussion_body'),
resize: DownArea.RESIZE_VERTICAL,
hide: ['heading', 'bold-italic'],
value: {{ form.discussion_body.data | tojson | safe }},
});
setupAutoResize('discussion_body');
});
</script>
{% endif %}
</div>
<div class="tab-pane fade" id="link-tab-pane" role="tabpanel" aria-labelledby="profile-tab" tabindex="0">
{{ render_field(form.link_title) }}
{{ render_field(form.link_url) }}
{{ render_field(form.link_body) }}
{% if markdown_editor %}
<script nonce="{{ session['nonce'] }}">
window.addEventListener("load", function () {
var downarea = new DownArea({
elem: document.querySelector('#link_body'),
resize: DownArea.RESIZE_VERTICAL,
hide: ['heading', 'bold-italic'],
value: {{ form.link_body.data | tojson | safe }},
});
setupAutoResize('link_body');
});
</script>
{% endif %}
</div>
<div class="tab-pane fade" id="image-tab-pane" role="tabpanel" aria-labelledby="contact-tab" tabindex="0">
{{ render_field(form.image_title) }}
{{ render_field(form.image_file) }}
{{ render_field(form.image_alt_text) }}
<small class="field_hint">{{ _('Describe the image, to help visually impaired people.') }}</small>
{{ render_field(form.image_body) }}
{% if markdown_editor %}
<script nonce="{{ session['nonce'] }}">
window.addEventListener("load", function () {
var downarea = new DownArea({
elem: document.querySelector('#image_body'),
resize: DownArea.RESIZE_VERTICAL,
hide: ['heading', 'bold-italic'],
value: {{ form.image_body.data | tojson | safe }},
});
setupAutoResize('image_body');
});
</script>
{% endif %}
</div>
<div class="tab-pane fade" id="poll-tab-pane" role="tabpanel" aria-labelledby="disabled-tab" tabindex="0">
Poll
</div>
</div>
{{ render_field(form.post_type) }}
<div class="row mt-4">
<div class="col-md-3">
{{ render_field(form.notify_author) }}
</div>
<div class="col-md-1">
{{ render_field(form.sticky) }}
</div>
<div class="col-md-1">
{{ render_field(form.nsfw) }}
</div>
<div class="col-md-1">
{{ render_field(form.nsfl) }}
</div>
<div class="col">
</div>
</div>
{{ render_field(form.submit) }}
</form>
</div>
<aside id="side_pane" class="col-12 col-md-4 side_pane" role="complementary">
<div class="card mt-3">
<div class="card-header">
<h2>{{ post.community.title }}</h2>
</div>
<div class="card-body">
<p>{{ post.community.description_html|safe if post.community.description_html else '' }}</p>
<p>{{ post.community.rules_html|safe if post.community.rules_html else '' }}</p>
{% if len(mods) > 0 and not post.community.private_mods %}
<h3>Moderators</h3>
<ul class="moderator_list">
{% for mod in mods %}
<li><a href="/u/{{ mod.link() }}">{{ mod.display_name() }}</a></li>
{% endfor %}
</ul>
{% endif %}
</div>
</div>
{% include "_inoculation_links.html" %}
</aside>
</div>
{% endblock %}

View file

@ -0,0 +1,75 @@
{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %}
{% extends 'themes/' + theme() + '/base.html' %}
{% else %}
{% extends "base.html" %}
{% endif %} %}
{% from 'bootstrap/form.html' import render_form, render_field %}
{% block app_content %}
<div class="row">
<div class="col-12 col-md-8 position-relative main_pane">
<h1>{{ _('Edit post') }}</h1>
<form method="post" role="form">
{{ form.csrf_token() }}
{{ render_field(form.discussion_title) }}
{{ render_field(form.discussion_body) }}
{% if markdown_editor %}
<script nonce="{{ session['nonce'] }}">
window.addEventListener("load", function () {
var downarea = new DownArea({
elem: document.querySelector('#discussion_body'),
resize: DownArea.RESIZE_VERTICAL,
hide: ['heading', 'bold-italic'],
value: {{ form.discussion_body.data | tojson | safe }},
});
setupAutoResize('discussion_body');
});
</script>
{% endif %}
<div class="row mt-4">
<div class="col-md-3">
{{ render_field(form.notify_author) }}
</div>
<div class="col-md-1">
{{ render_field(form.sticky) }}
</div>
<div class="col-md-1">
{{ render_field(form.nsfw) }}
</div>
<div class="col-md-1">
{{ render_field(form.nsfl) }}
</div>
<div class="col">
</div>
</div>
{{ render_field(form.submit) }}
</form>
</div>
<aside id="side_pane" class="col-12 col-md-4 side_pane" role="complementary">
<div class="card mt-3">
<div class="card-header">
<h2>{{ post.community.title }}</h2>
</div>
<div class="card-body">
<p>{{ post.community.description_html|safe if post.community.description_html else '' }}</p>
<p>{{ post.community.rules_html|safe if post.community.rules_html else '' }}</p>
{% if len(mods) > 0 and not post.community.private_mods %}
<h3>Moderators</h3>
<ul class="moderator_list">
{% for mod in mods %}
<li><a href="/u/{{ mod.link() }}">{{ mod.display_name() }}</a></li>
{% endfor %}
</ul>
{% endif %}
</div>
</div>
{% include "_inoculation_links.html" %}
</aside>
</div>
{% endblock %}

View file

@ -0,0 +1,78 @@
{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %}
{% extends 'themes/' + theme() + '/base.html' %}
{% else %}
{% extends "base.html" %}
{% endif %} %}
{% from 'bootstrap/form.html' import render_form, render_field %}
{% block app_content %}
<div class="row">
<div class="col-12 col-md-8 position-relative main_pane">
<h1>{{ _('Edit post') }}</h1>
<form method="post" enctype="multipart/form-data" role="form">
{{ form.csrf_token() }}
{{ render_field(form.image_title) }}
{{ render_field(form.image_file) }}
{{ render_field(form.image_alt_text) }}
<small class="field_hint">{{ _('Describe the image, to help visually impaired people.') }}</small>
{{ render_field(form.image_body) }}
{% if markdown_editor %}
<script nonce="{{ session['nonce'] }}">
window.addEventListener("load", function () {
var downarea = new DownArea({
elem: document.querySelector('#image_body'),
resize: DownArea.RESIZE_VERTICAL,
hide: ['heading', 'bold-italic'],
value: {{ form.image_body.data | tojson | safe }},
});
setupAutoResize('image_body');
});
</script>
{% endif %}
<div class="row mt-4">
<div class="col-md-3">
{{ render_field(form.notify_author) }}
</div>
<div class="col-md-1">
{{ render_field(form.sticky) }}
</div>
<div class="col-md-1">
{{ render_field(form.nsfw) }}
</div>
<div class="col-md-1">
{{ render_field(form.nsfl) }}
</div>
<div class="col">
</div>
</div>
{{ render_field(form.submit) }}
</form>
</div>
<aside id="side_pane" class="col-12 col-md-4 side_pane" role="complementary">
<div class="card mt-3">
<div class="card-header">
<h2>{{ post.community.title }}</h2>
</div>
<div class="card-body">
<p>{{ post.community.description_html|safe if post.community.description_html else '' }}</p>
<p>{{ post.community.rules_html|safe if post.community.rules_html else '' }}</p>
{% if len(mods) > 0 and not post.community.private_mods %}
<h3>Moderators</h3>
<ul class="moderator_list">
{% for mod in mods %}
<li><a href="/u/{{ mod.link() }}">{{ mod.display_name() }}</a></li>
{% endfor %}
</ul>
{% endif %}
</div>
</div>
{% include "_inoculation_links.html" %}
</aside>
</div>
{% endblock %}

View file

@ -0,0 +1,75 @@
{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %}
{% extends 'themes/' + theme() + '/base.html' %}
{% else %}
{% extends "base.html" %}
{% endif %} %}
{% from 'bootstrap/form.html' import render_form, render_field %}
{% block app_content %}
<div class="row">
<div class="col-12 col-md-8 position-relative main_pane">
<h1>{{ _('Edit post') }}</h1>
<form method="post" enctype="multipart/form-data" role="form">
{{ form.csrf_token() }}
{{ render_field(form.link_title) }}
{{ render_field(form.link_url) }}
{{ render_field(form.link_body) }}
{% if markdown_editor %}
<script nonce="{{ session['nonce'] }}">
window.addEventListener("load", function () {
var downarea = new DownArea({
elem: document.querySelector('#link_body'),
resize: DownArea.RESIZE_VERTICAL,
hide: ['heading', 'bold-italic'],
value: {{ form.link_body.data | tojson | safe }},
});
setupAutoResize('link_body');
});
</script>
{% endif %}
<div class="row mt-4">
<div class="col-md-3">
{{ render_field(form.notify_author) }}
</div>
<div class="col-md-1">
{{ render_field(form.sticky) }}
</div>
<div class="col-md-1">
{{ render_field(form.nsfw) }}
</div>
<div class="col-md-1">
{{ render_field(form.nsfl) }}
</div>
<div class="col">
</div>
</div>
{{ render_field(form.submit) }}
</form>
</div>
<aside id="side_pane" class="col-12 col-md-4 side_pane" role="complementary">
<div class="card mt-3">
<div class="card-header">
<h2>{{ post.community.title }}</h2>
</div>
<div class="card-body">
<p>{{ post.community.description_html|safe if post.community.description_html else '' }}</p>
<p>{{ post.community.rules_html|safe if post.community.rules_html else '' }}</p>
{% if len(mods) > 0 and not post.community.private_mods %}
<h3>Moderators</h3>
<ul class="moderator_list">
{% for mod in mods %}
<li><a href="/u/{{ mod.link() }}">{{ mod.display_name() }}</a></li>
{% endfor %}
</ul>
{% endif %}
</div>
</div>
{% include "_inoculation_links.html" %}
</aside>
</div>
{% endblock %}