reimplement monsterpit bbcode and markdown extensions on top of new glitch-soc formatting system + bbcode feature parity + new `i:am` footer + set content type from `format` bangtag
parent
09b7532805
commit
7a0dc34cad
2
Gemfile
2
Gemfile
|
@ -149,3 +149,5 @@ group :production do
|
||||||
end
|
end
|
||||||
|
|
||||||
gem 'concurrent-ruby', require: false
|
gem 'concurrent-ruby', require: false
|
||||||
|
|
||||||
|
gem "ruby-bbcode", "~> 2.0"
|
||||||
|
|
|
@ -533,6 +533,8 @@ GEM
|
||||||
rainbow (>= 2.2.2, < 4.0)
|
rainbow (>= 2.2.2, < 4.0)
|
||||||
ruby-progressbar (~> 1.7)
|
ruby-progressbar (~> 1.7)
|
||||||
unicode-display_width (>= 1.4.0, < 1.7)
|
unicode-display_width (>= 1.4.0, < 1.7)
|
||||||
|
ruby-bbcode (2.0.3)
|
||||||
|
activesupport (>= 4.2.2)
|
||||||
ruby-progressbar (1.10.0)
|
ruby-progressbar (1.10.0)
|
||||||
ruby-saml (1.9.0)
|
ruby-saml (1.9.0)
|
||||||
nokogiri (>= 1.5.10)
|
nokogiri (>= 1.5.10)
|
||||||
|
@ -667,7 +669,6 @@ DEPENDENCIES
|
||||||
brakeman (~> 4.5)
|
brakeman (~> 4.5)
|
||||||
browser
|
browser
|
||||||
bullet (~> 6.0)
|
bullet (~> 6.0)
|
||||||
bundler (~> 1.17)
|
|
||||||
bundler-audit (~> 0.6)
|
bundler-audit (~> 0.6)
|
||||||
capistrano (~> 3.11)
|
capistrano (~> 3.11)
|
||||||
capistrano-rails (~> 1.4)
|
capistrano-rails (~> 1.4)
|
||||||
|
@ -751,6 +752,7 @@ DEPENDENCIES
|
||||||
rspec-rails (~> 3.8)
|
rspec-rails (~> 3.8)
|
||||||
rspec-sidekiq (~> 3.0)
|
rspec-sidekiq (~> 3.0)
|
||||||
rubocop (~> 0.69)
|
rubocop (~> 0.69)
|
||||||
|
ruby-bbcode (~> 2.0)
|
||||||
sanitize (~> 5.0)
|
sanitize (~> 5.0)
|
||||||
scss_lint (~> 0.58)
|
scss_lint (~> 0.58)
|
||||||
sidekiq (~> 5.2)
|
sidekiq (~> 5.2)
|
||||||
|
|
|
@ -25,6 +25,14 @@ const messages = defineMessages({
|
||||||
defaultMessage: 'Attach...',
|
defaultMessage: 'Attach...',
|
||||||
id: 'compose.attach',
|
id: 'compose.attach',
|
||||||
},
|
},
|
||||||
|
bbcode: {
|
||||||
|
defaultMessage: 'BBCode',
|
||||||
|
id: 'compose.content-type.bbcode',
|
||||||
|
},
|
||||||
|
bbdown: {
|
||||||
|
defaultMessage: 'BBdown',
|
||||||
|
id: 'compose.content-type.bbdown',
|
||||||
|
},
|
||||||
change_privacy: {
|
change_privacy: {
|
||||||
defaultMessage: 'Adjust status privacy',
|
defaultMessage: 'Adjust status privacy',
|
||||||
id: 'privacy.change',
|
id: 'privacy.change',
|
||||||
|
@ -232,7 +240,7 @@ class ComposerOptions extends ImmutablePureComponent {
|
||||||
|
|
||||||
const contentTypeItems = {
|
const contentTypeItems = {
|
||||||
plain: {
|
plain: {
|
||||||
icon: 'align-left',
|
icon: 'file-text',
|
||||||
name: 'text/plain',
|
name: 'text/plain',
|
||||||
text: <FormattedMessage {...messages.plain} />,
|
text: <FormattedMessage {...messages.plain} />,
|
||||||
},
|
},
|
||||||
|
@ -242,10 +250,20 @@ class ComposerOptions extends ImmutablePureComponent {
|
||||||
text: <FormattedMessage {...messages.html} />,
|
text: <FormattedMessage {...messages.html} />,
|
||||||
},
|
},
|
||||||
markdown: {
|
markdown: {
|
||||||
icon: 'arrow-circle-down',
|
icon: 'hashtag',
|
||||||
name: 'text/markdown',
|
name: 'text/markdown',
|
||||||
text: <FormattedMessage {...messages.markdown} />,
|
text: <FormattedMessage {...messages.markdown} />,
|
||||||
},
|
},
|
||||||
|
xbbcode: {
|
||||||
|
icon: 'thumb-tack',
|
||||||
|
name: 'text/x-bbcode',
|
||||||
|
text: <FormattedMessage {...messages.bbcode} />,
|
||||||
|
},
|
||||||
|
xbbcodemarkdown: {
|
||||||
|
icon: 'arrow-circle-down',
|
||||||
|
name: 'text/x-bbcode+markdown',
|
||||||
|
text: <FormattedMessage {...messages.bbdown} />,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// The result.
|
// The result.
|
||||||
|
@ -315,11 +333,13 @@ class ComposerOptions extends ImmutablePureComponent {
|
||||||
{showContentTypeChoice && (
|
{showContentTypeChoice && (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
icon={(contentTypeItems[contentType.split('/')[1]] || {}).icon}
|
icon={(contentTypeItems[contentType.split('/')[1].replace(/[+-]/g, '')] || {}).icon}
|
||||||
items={[
|
items={[
|
||||||
contentTypeItems.plain,
|
contentTypeItems.xbbcodemarkdown,
|
||||||
contentTypeItems.html,
|
|
||||||
contentTypeItems.markdown,
|
contentTypeItems.markdown,
|
||||||
|
contentTypeItems.xbbcode,
|
||||||
|
contentTypeItems.html,
|
||||||
|
contentTypeItems.plain,
|
||||||
]}
|
]}
|
||||||
onChange={onChangeContentType}
|
onChange={onChangeContentType}
|
||||||
onModalClose={onModalClose}
|
onModalClose={onModalClose}
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
// Original:
|
||||||
|
// https://github.com/computerfairies/mastodon/blob/master/app/javascript/styles/mastodon/bbcode.scss
|
||||||
|
*/
|
||||||
|
|
||||||
|
.bbcode {
|
||||||
|
&__flip-horizontal {
|
||||||
|
display: inline-block;
|
||||||
|
-webkit-transform: scale(-1, 1);
|
||||||
|
-ms-transform: scale(-1, 1);
|
||||||
|
transform: scale(-1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__flip-vertical {
|
||||||
|
display: inline-block;
|
||||||
|
-webkit-transform: scale(1, -1);
|
||||||
|
-ms-transform: scale(1, -1);
|
||||||
|
transform: scale(1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@for $i from 1 through 6 {
|
||||||
|
&__size-#{$i} {
|
||||||
|
font-size: #{6 * $i}px;
|
||||||
|
|
||||||
|
& .emojione {
|
||||||
|
width: #{6 * $i}px !important;
|
||||||
|
height: #{6 * $i}px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .hoverplay {
|
||||||
|
padding-left: #{6 * $i}px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@for $i from 1 through 2 {
|
||||||
|
&__size-#{$i}:hover {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__left { display: block; text-align: left; }
|
||||||
|
&__center { display: block; text-align: center; }
|
||||||
|
&__right { display: block; text-align: right; }
|
||||||
|
&__lfloat { float: left; }
|
||||||
|
&__rfloat { float: right; }
|
||||||
|
&__spoiler-wrapper {
|
||||||
|
background: black;
|
||||||
|
color: black;
|
||||||
|
padding: 1px 2em 1px 2em;
|
||||||
|
}
|
||||||
|
&__spoiler { color: black; visibility: hidden; }
|
||||||
|
&__spoiler-wrapper:hover > &__spoiler,
|
||||||
|
&__spoiler-wrapper:active > &__spoiler
|
||||||
|
{ color: white; visibility: visible; }
|
||||||
|
}
|
|
@ -24,3 +24,5 @@
|
||||||
@import 'accessibility';
|
@import 'accessibility';
|
||||||
@import 'rtl';
|
@import 'rtl';
|
||||||
@import 'dashboard';
|
@import 'dashboard';
|
||||||
|
@import 'bbcode';
|
||||||
|
@import 'monsterpit';
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
.status__content__text,
|
||||||
|
.reply-indicator__content,
|
||||||
|
.composer--reply > .content,
|
||||||
|
.account__header__content,
|
||||||
|
{
|
||||||
|
s { text-decoration: line-through; }
|
||||||
|
del { text-decoration: line-through; }
|
||||||
|
h6 { font-size: 8px; font-weight: bold; }
|
||||||
|
hr { border-color: lighten($dark-text-color, 10%); }
|
||||||
|
sub {
|
||||||
|
vertical-align: sub;
|
||||||
|
font-size: smaller;
|
||||||
|
}
|
||||||
|
sup {
|
||||||
|
vertical-align: super;
|
||||||
|
font-size: smaller;
|
||||||
|
}
|
||||||
|
pre, code {
|
||||||
|
color: lighten($dark-text-color, 33%);
|
||||||
|
}
|
||||||
|
mark {
|
||||||
|
background-color: #ccff15;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
blockquote {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
.caption {
|
||||||
|
display: block;
|
||||||
|
margin: auto;
|
||||||
|
font-size: 12px !important;
|
||||||
|
padding-top: 0;
|
||||||
|
text-align: center;
|
||||||
|
max-width: 80%;
|
||||||
|
}
|
||||||
|
.caption-hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
p.signature {
|
||||||
|
color: lighten($dark-text-color, 20%);
|
||||||
|
font-style: italic;
|
||||||
|
font-size: 12px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div.media-caption {
|
||||||
|
p {
|
||||||
|
font-size: 12px !important;
|
||||||
|
margin-bottom: 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color: $secondary-text-color;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
|
||||||
|
.fa {
|
||||||
|
color: lighten($dark-text-color, 7%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.mention {
|
||||||
|
&:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
span {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fa {
|
||||||
|
color: $dark-text-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,3 +26,5 @@
|
||||||
@import 'mastodon/dashboard';
|
@import 'mastodon/dashboard';
|
||||||
@import 'mastodon/rtl';
|
@import 'mastodon/rtl';
|
||||||
@import 'mastodon/accessibility';
|
@import 'mastodon/accessibility';
|
||||||
|
@import 'mastodon/bbcode';
|
||||||
|
@import 'mastodon/monsterpit';
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
// Original:
|
||||||
|
// https://github.com/computerfairies/mastodon/blob/master/app/javascript/styles/mastodon/bbcode.scss
|
||||||
|
*/
|
||||||
|
|
||||||
|
.bbcode {
|
||||||
|
&__flip-horizontal {
|
||||||
|
display: inline-block;
|
||||||
|
-webkit-transform: scale(-1, 1);
|
||||||
|
-ms-transform: scale(-1, 1);
|
||||||
|
transform: scale(-1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__flip-vertical {
|
||||||
|
display: inline-block;
|
||||||
|
-webkit-transform: scale(1, -1);
|
||||||
|
-ms-transform: scale(1, -1);
|
||||||
|
transform: scale(1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@for $i from 1 through 6 {
|
||||||
|
&__size-#{$i} {
|
||||||
|
font-size: #{6 * $i}px;
|
||||||
|
|
||||||
|
& .emojione {
|
||||||
|
width: #{6 * $i}px !important;
|
||||||
|
height: #{6 * $i}px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .hoverplay {
|
||||||
|
padding-left: #{6 * $i}px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@for $i from 1 through 2 {
|
||||||
|
&__size-#{$i}:hover {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__left { display: block; text-align: left; }
|
||||||
|
&__center { display: block; text-align: center; }
|
||||||
|
&__right { display: block; text-align: right; }
|
||||||
|
&__lfloat { float: left; }
|
||||||
|
&__rfloat { float: right; }
|
||||||
|
&__spoiler-wrapper {
|
||||||
|
background: black;
|
||||||
|
color: black;
|
||||||
|
padding: 1px 2em 1px 2em;
|
||||||
|
}
|
||||||
|
&__spoiler { color: black; visibility: hidden; }
|
||||||
|
&__spoiler-wrapper:hover > &__spoiler,
|
||||||
|
&__spoiler-wrapper:active > &__spoiler
|
||||||
|
{ color: white; visibility: visible; }
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
.status__content__text,
|
||||||
|
.reply-indicator__content,
|
||||||
|
.composer--reply > .content,
|
||||||
|
.account__header__content,
|
||||||
|
{
|
||||||
|
s { text-decoration: line-through; }
|
||||||
|
del { text-decoration: line-through; }
|
||||||
|
h6 { font-size: 8px; font-weight: bold; }
|
||||||
|
hr { border-color: lighten($dark-text-color, 10%); }
|
||||||
|
sub {
|
||||||
|
vertical-align: sub;
|
||||||
|
font-size: smaller;
|
||||||
|
}
|
||||||
|
sup {
|
||||||
|
vertical-align: super;
|
||||||
|
font-size: smaller;
|
||||||
|
}
|
||||||
|
pre, code {
|
||||||
|
color: lighten($dark-text-color, 33%);
|
||||||
|
}
|
||||||
|
mark {
|
||||||
|
background-color: #ccff15;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
blockquote {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
.caption {
|
||||||
|
display: block;
|
||||||
|
margin: auto;
|
||||||
|
font-size: 12px !important;
|
||||||
|
padding-top: 0;
|
||||||
|
text-align: center;
|
||||||
|
max-width: 80%;
|
||||||
|
}
|
||||||
|
.caption-hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div.media-caption {
|
||||||
|
p {
|
||||||
|
font-size: 12px !important;
|
||||||
|
margin-bottom: 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color: $secondary-text-color;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
|
||||||
|
.fa {
|
||||||
|
color: lighten($dark-text-color, 7%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.mention {
|
||||||
|
&:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
span {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fa {
|
||||||
|
color: $dark-text-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,7 +26,7 @@ class Bangtags
|
||||||
# list of transformation commands
|
# list of transformation commands
|
||||||
@tf_cmds = []
|
@tf_cmds = []
|
||||||
# list of post-processing commands
|
# list of post-processing commands
|
||||||
@post_cmds = [['signature']]
|
@post_cmds = []
|
||||||
# hash of bangtag variables
|
# hash of bangtag variables
|
||||||
@vars = account.vars
|
@vars = account.vars
|
||||||
# keep track of what variables we're appending the value of between chunks
|
# keep track of what variables we're appending the value of between chunks
|
||||||
|
@ -36,7 +36,7 @@ class Bangtags
|
||||||
end
|
end
|
||||||
|
|
||||||
def process
|
def process
|
||||||
return unless status.text&.present?
|
return unless status.text&.present? && status.text.include?('#!')
|
||||||
|
|
||||||
status.text.gsub!('#!!', "#\u200c!")
|
status.text.gsub!('#!!', "#\u200c!")
|
||||||
|
|
||||||
|
@ -367,16 +367,19 @@ class Bangtags
|
||||||
who = cmd[2]
|
who = cmd[2]
|
||||||
if who.blank?
|
if who.blank?
|
||||||
@vars.delete('_they:are')
|
@vars.delete('_they:are')
|
||||||
|
status.footer = nil
|
||||||
next
|
next
|
||||||
elsif who == 'not'
|
elsif who == 'not'
|
||||||
who = cmd[3]
|
who = cmd[3]
|
||||||
next if who.blank?
|
next if who.blank?
|
||||||
name = who.downcase.gsub(/\s+/, '')
|
name = who.downcase.gsub(/\s+/, '')
|
||||||
@vars.delete("_they:are:#{name}")
|
@vars.delete("_they:are:#{name}")
|
||||||
@vars.delete('_they:are') if @vars['_they:are'] == name
|
next unless @vars['_they:are'] == name
|
||||||
|
@vars.delete('_they:are')
|
||||||
|
status.footer = nil
|
||||||
next
|
next
|
||||||
end
|
end
|
||||||
name = who.downcase.gsub(/\s+/, '')
|
name = who.downcase.gsub(/\s+/, '').strip
|
||||||
description = cmd[3..-1].join(':').strip
|
description = cmd[3..-1].join(':').strip
|
||||||
if description.blank?
|
if description.blank?
|
||||||
if @vars["_they:are:#{name}"].nil?
|
if @vars["_they:are:#{name}"].nil?
|
||||||
|
@ -385,7 +388,8 @@ class Bangtags
|
||||||
else
|
else
|
||||||
@vars["_they:are:#{name}"] = description
|
@vars["_they:are:#{name}"] = description
|
||||||
end
|
end
|
||||||
@vars['_they:are'] = name.strip
|
@vars['_they:are'] = name
|
||||||
|
status.footer = @vars["_they:are:#{name}"]
|
||||||
end
|
end
|
||||||
when 'sharekey'
|
when 'sharekey'
|
||||||
next if cmd[1].nil?
|
next if cmd[1].nil?
|
||||||
|
@ -401,6 +405,30 @@ class Bangtags
|
||||||
@vore_stack.push('_draft')
|
@vore_stack.push('_draft')
|
||||||
@component_stack.push(:var)
|
@component_stack.push(:var)
|
||||||
add_tags(status, 'self:draft')
|
add_tags(status, 'self:draft')
|
||||||
|
when 'format', 'type'
|
||||||
|
chunk = nil
|
||||||
|
next if cmd[1].nil?
|
||||||
|
content_types = {
|
||||||
|
't' => 'text/plain',
|
||||||
|
'txt' => 'text/plain',
|
||||||
|
'text' => 'text/plain',
|
||||||
|
'plain' => 'text/plain',
|
||||||
|
'plaintext' => 'text/plain',
|
||||||
|
|
||||||
|
'm' => 'text/markdown',
|
||||||
|
'md' => 'text/markdown',
|
||||||
|
'markdown' => 'text/markdown',
|
||||||
|
|
||||||
|
'b' => 'text/x-bbcode',
|
||||||
|
'bbc' => 'text/x-bbcode',
|
||||||
|
'bbcode' => 'text/x-bbcode',
|
||||||
|
|
||||||
|
'bm' => 'text/x-bbcode+markdown',
|
||||||
|
'bbm' => 'text/x-bbcode+markdown',
|
||||||
|
'bbdown' => 'text/x-bbcode+markdown',
|
||||||
|
}
|
||||||
|
v = cmd[1].downcase
|
||||||
|
status.content_type = content_types[c] unless content_types[c].nil?
|
||||||
when 'visibility'
|
when 'visibility'
|
||||||
chunk = nil
|
chunk = nil
|
||||||
next if cmd[1].nil?
|
next if cmd[1].nil?
|
||||||
|
@ -421,7 +449,7 @@ class Bangtags
|
||||||
'world' => :public,
|
'world' => :public,
|
||||||
}
|
}
|
||||||
v = cmd[1].downcase
|
v = cmd[1].downcase
|
||||||
status.visibility = visibilities[v] if visibilities[v].nil?
|
status.visibility = visibilities[v] unless visibilities[v].nil?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -472,17 +500,6 @@ class Bangtags
|
||||||
def postprocess_before_save
|
def postprocess_before_save
|
||||||
@post_cmds.each do |post_cmd|
|
@post_cmds.each do |post_cmd|
|
||||||
case post_cmd[0]
|
case post_cmd[0]
|
||||||
when 'signature'
|
|
||||||
name = @vars['_they:are']
|
|
||||||
next if name.blank?
|
|
||||||
description = @vars["_they:are:#{name}"]
|
|
||||||
next if description.blank? || @chunks.last(5).join.include?('—')
|
|
||||||
status.local_only = true if Status::LOCAL_ONLY_TOKENS.match?(@chunks.last)
|
|
||||||
if @chunks.first(5).any? { |c| c.strip.match?(/[\r\n]/) || c.lstrip.match?(/^(?:[>#]|```|---|\* |\d+\)|\[\wi+)/) }
|
|
||||||
@chunks << "\n\n[right]— #{description}\u200c[/right]"
|
|
||||||
else
|
|
||||||
@chunks << " [rfloat]— #{description}\u200c[/rfloat]"
|
|
||||||
end
|
|
||||||
when 'media'
|
when 'media'
|
||||||
media_idx = post_cmd[1]
|
media_idx = post_cmd[1]
|
||||||
media_cmd = post_cmd[2]
|
media_cmd = post_cmd[2]
|
||||||
|
|
|
@ -30,6 +30,141 @@ class Formatter
|
||||||
|
|
||||||
include ActionView::Helpers::TextHelper
|
include ActionView::Helpers::TextHelper
|
||||||
|
|
||||||
|
BBCODE_TAGS = {
|
||||||
|
:url => {
|
||||||
|
:html_open => '<a href="%url%" rel="noopener nofollow" target="_blank">', :html_close => '</a>',
|
||||||
|
:description => '', :example => '',
|
||||||
|
:allow_quick_param => true, :allow_between_as_param => false,
|
||||||
|
:quick_param_format => /(\S+)/,
|
||||||
|
:quick_param_format_description => 'The size parameter \'%param%\' is incorrect, a number is expected',
|
||||||
|
:param_tokens => [{:token => :url}]
|
||||||
|
},
|
||||||
|
:ul => {
|
||||||
|
:html_open => '<ul>', :html_close => '</ul>',
|
||||||
|
:description => '', :example => '',
|
||||||
|
},
|
||||||
|
:ol => {
|
||||||
|
:html_open => '<ol>', :html_close => '</ol>',
|
||||||
|
:description => '', :example => '',
|
||||||
|
},
|
||||||
|
:li => {
|
||||||
|
:html_open => '<li>', :html_close => '</li>',
|
||||||
|
:description => '', :example => '',
|
||||||
|
},
|
||||||
|
:sub => {
|
||||||
|
:html_open => '<sub>', :html_close => '</sub>',
|
||||||
|
:description => '', :example => '',
|
||||||
|
},
|
||||||
|
:sup => {
|
||||||
|
:html_open => '<sup>', :html_close => '</sup>',
|
||||||
|
:description => '', :example => '',
|
||||||
|
},
|
||||||
|
:h1 => {
|
||||||
|
:html_open => '<h1>', :html_close => '</h1>',
|
||||||
|
:description => '', :example => '',
|
||||||
|
},
|
||||||
|
:h2 => {
|
||||||
|
:html_open => '<h2>', :html_close => '</h2>',
|
||||||
|
:description => '', :example => '',
|
||||||
|
},
|
||||||
|
:h3 => {
|
||||||
|
:html_open => '<h3>', :html_close => '</h3>',
|
||||||
|
:description => '', :example => '',
|
||||||
|
},
|
||||||
|
:h4 => {
|
||||||
|
:html_open => '<h4>', :html_close => '</h4>',
|
||||||
|
:description => '', :example => '',
|
||||||
|
},
|
||||||
|
:h5 => {
|
||||||
|
:html_open => '<h5>', :html_close => '</h5>',
|
||||||
|
:description => '', :example => '',
|
||||||
|
},
|
||||||
|
:h6 => {
|
||||||
|
:html_open => '<h6>', :html_close => '</h6>',
|
||||||
|
:description => '', :example => '',
|
||||||
|
},
|
||||||
|
:abbr => {
|
||||||
|
:html_open => '<abbr>', :html_close => '</abbr>',
|
||||||
|
:description => '', :example => '',
|
||||||
|
},
|
||||||
|
:hr => {
|
||||||
|
:html_open => '<hr>', :html_close => '</hr>',
|
||||||
|
:description => '', :example => '',
|
||||||
|
},
|
||||||
|
:b => {
|
||||||
|
:html_open => '<strong>', :html_close => '</strong>',
|
||||||
|
:description => '', :example => '',
|
||||||
|
},
|
||||||
|
:i => {
|
||||||
|
:html_open => '<em>', :html_close => '</em>',
|
||||||
|
:description => '', :example => '',
|
||||||
|
},
|
||||||
|
:flip => {
|
||||||
|
:html_open => '<span class="bbcode__flip-%direction%">', :html_close => '</span>',
|
||||||
|
:description => '', :example => '',
|
||||||
|
:allow_quick_param => true, :allow_between_as_param => false,
|
||||||
|
:quick_param_format => /(h|v)/,
|
||||||
|
:quick_param_format_description => 'The size parameter \'%param%\' is incorrect, a number is expected',
|
||||||
|
:param_tokens => [{:token => :direction}]
|
||||||
|
},
|
||||||
|
:size => {
|
||||||
|
:html_open => '<span class="bbcode__size-%size%">', :html_close => '</span>',
|
||||||
|
:description => '', :example => '',
|
||||||
|
:allow_quick_param => true, :allow_between_as_param => false,
|
||||||
|
:quick_param_format => /([1-6])/,
|
||||||
|
:quick_param_format_description => 'The size parameter \'%param%\' is incorrect, a number is expected',
|
||||||
|
:param_tokens => [{:token => :size}]
|
||||||
|
},
|
||||||
|
:quote => {
|
||||||
|
:html_open => '<blockquote>', :html_close => '</blockquote>',
|
||||||
|
:description => '', :example => '',
|
||||||
|
},
|
||||||
|
:kbd => {
|
||||||
|
:html_open => '<pre><code>', :html_close => '</code></pre>',
|
||||||
|
:description => '', :example => '',
|
||||||
|
},
|
||||||
|
:code => {
|
||||||
|
:html_open => '<pre>', :html_close => '</pre>',
|
||||||
|
:description => '', :example => '',
|
||||||
|
},
|
||||||
|
:u => {
|
||||||
|
:html_open => '<u>', :html_close => '</u>',
|
||||||
|
:description => '', :example => '',
|
||||||
|
},
|
||||||
|
:s => {
|
||||||
|
:html_open => '<s>', :html_close => '</s>',
|
||||||
|
:description => '', :example => '',
|
||||||
|
},
|
||||||
|
:del => {
|
||||||
|
:html_open => '<del>', :html_close => '</del>',
|
||||||
|
:description => '', :example => '',
|
||||||
|
},
|
||||||
|
:left => {
|
||||||
|
:html_open => '<span class="bbcode__left">', :html_close => '</span>',
|
||||||
|
:description => '', :example => '',
|
||||||
|
},
|
||||||
|
:center => {
|
||||||
|
:html_open => '<span class="bbcode__center">', :html_close => '</span>',
|
||||||
|
:description => '', :example => '',
|
||||||
|
},
|
||||||
|
:right => {
|
||||||
|
:html_open => '<span class="bbcode__right">', :html_close => '</span>',
|
||||||
|
:description => '', :example => '',
|
||||||
|
},
|
||||||
|
:lfloat => {
|
||||||
|
:html_open => '<span class="bbcode__lfloat">', :html_close => '</span>',
|
||||||
|
:description => '', :example => '',
|
||||||
|
},
|
||||||
|
:rfloat => {
|
||||||
|
:html_open => '<span class="bbcode__rfloat">', :html_close => '</span>',
|
||||||
|
:description => '', :example => '',
|
||||||
|
},
|
||||||
|
:spoiler => {
|
||||||
|
:html_open => '<span class="bbcode__spoiler-wrapper"><span class="bbcode__spoiler">', :html_close => '</span></span>',
|
||||||
|
:description => '', :example => '',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
def format(status, **options)
|
def format(status, **options)
|
||||||
if status.reblog?
|
if status.reblog?
|
||||||
prepend_reblog = status.reblog.account.acct
|
prepend_reblog = status.reblog.account.acct
|
||||||
|
@ -57,15 +192,26 @@ class Formatter
|
||||||
|
|
||||||
html = raw_content
|
html = raw_content
|
||||||
html = "RT @#{prepend_reblog} #{html}" if prepend_reblog
|
html = "RT @#{prepend_reblog} #{html}" if prepend_reblog
|
||||||
html = format_markdown(html) if status.content_type == 'text/markdown'
|
|
||||||
html = encode_and_link_urls(html, linkable_accounts, keep_html: %w(text/markdown text/html).include?(status.content_type))
|
case status.content_type
|
||||||
|
when 'text/markdown'
|
||||||
|
html = format_markdown(html)
|
||||||
|
when 'text/x-bbcode'
|
||||||
|
html = format_bbcode(html)
|
||||||
|
when 'text/x-bbcode+markdown'
|
||||||
|
html = format_bbdown(html)
|
||||||
|
end
|
||||||
|
|
||||||
|
html = encode_and_link_urls(html, linkable_accounts, keep_html: %w(text/markdown text/x-bbcode text/x-bbcode+markdown text/html).include?(status.content_type))
|
||||||
html = encode_custom_emojis(html, status.emojis, options[:autoplay]) if options[:custom_emojify]
|
html = encode_custom_emojis(html, status.emojis, options[:autoplay]) if options[:custom_emojify]
|
||||||
|
|
||||||
unless %w(text/markdown text/html).include?(status.content_type)
|
unless %w(text/markdown text/x-bbcode text/x-bbcode+markdown text/html).include?(status.content_type)
|
||||||
html = simple_format(html, {}, sanitize: false)
|
html = simple_format(html, {}, sanitize: false)
|
||||||
html = html.delete("\n")
|
html = html.delete("\n")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
html = append_footer(html, status.footer)
|
||||||
|
|
||||||
html.html_safe # rubocop:disable Rails/OutputSafety
|
html.html_safe # rubocop:disable Rails/OutputSafety
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -74,6 +220,19 @@ class Formatter
|
||||||
html.delete("\r").delete("\n")
|
html.delete("\r").delete("\n")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def format_bbcode(html, sanitize = true)
|
||||||
|
html = bbcode_formatter(html)
|
||||||
|
html = html.gsub(/<hr>.*<\/hr>/im, '<hr />')
|
||||||
|
return html unless sanitize
|
||||||
|
html = reformat(html)
|
||||||
|
html.delete("\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
def format_bbdown(html)
|
||||||
|
html = format_bbcode(html, false)
|
||||||
|
format_markdown(html)
|
||||||
|
end
|
||||||
|
|
||||||
def reformat(html)
|
def reformat(html)
|
||||||
sanitize(html, Sanitize::Config::MASTODON_STRICT)
|
sanitize(html, Sanitize::Config::MASTODON_STRICT)
|
||||||
end
|
end
|
||||||
|
@ -134,6 +293,19 @@ class Formatter
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def append_footer(html, footer)
|
||||||
|
return html if footer.blank?
|
||||||
|
"#{html.strip}<p class=\"signature\">— #{encode(footer)}</p>"
|
||||||
|
end
|
||||||
|
|
||||||
|
def bbcode_formatter(html)
|
||||||
|
begin
|
||||||
|
html = html.bbcode_to_html(false, BBCODE_TAGS, :enable, *BBCODE_TAGS.keys)
|
||||||
|
rescue Exception => e
|
||||||
|
end
|
||||||
|
html
|
||||||
|
end
|
||||||
|
|
||||||
def markdown_formatter
|
def markdown_formatter
|
||||||
return @markdown_formatter if defined?(@markdown_formatter)
|
return @markdown_formatter if defined?(@markdown_formatter)
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,8 @@ class Sanitize
|
||||||
next true if e =~ /^(h|p|u|dt|e)-/ # microformats classes
|
next true if e =~ /^(h|p|u|dt|e)-/ # microformats classes
|
||||||
next true if e =~ /^(mention|hashtag)$/ # semantic classes
|
next true if e =~ /^(mention|hashtag)$/ # semantic classes
|
||||||
next true if e =~ /^(ellipsis|invisible)$/ # link formatting classes
|
next true if e =~ /^(ellipsis|invisible)$/ # link formatting classes
|
||||||
|
next true if e =~ /^bbcode__([a-z1-6\-]+)$/ # bbcode
|
||||||
|
next true if e == 'signature'
|
||||||
end
|
end
|
||||||
|
|
||||||
node['class'] = class_list.join(' ')
|
node['class'] = class_list.join(' ')
|
||||||
|
@ -23,10 +25,11 @@ class Sanitize
|
||||||
elements: %w(p br span a abbr del pre sub sup blockquote code b strong u i em h1 h2 h3 h4 h5 h6 ul ol li hr),
|
elements: %w(p br span a abbr del pre sub sup blockquote code b strong u i em h1 h2 h3 h4 h5 h6 ul ol li hr),
|
||||||
|
|
||||||
attributes: {
|
attributes: {
|
||||||
'a' => %w(href rel class title),
|
'a' => %w(href rel class title alt),
|
||||||
'span' => %w(class),
|
'span' => %w(class),
|
||||||
'abbr' => %w(title),
|
'abbr' => %w(title),
|
||||||
'blockquote' => %w(cite),
|
'blockquote' => %w(cite),
|
||||||
|
'p' => %w(class),
|
||||||
},
|
},
|
||||||
|
|
||||||
add_attributes: {
|
add_attributes: {
|
||||||
|
|
|
@ -23,11 +23,12 @@
|
||||||
# in_reply_to_account_id :bigint(8)
|
# in_reply_to_account_id :bigint(8)
|
||||||
# local_only :boolean
|
# local_only :boolean
|
||||||
# poll_id :bigint(8)
|
# poll_id :bigint(8)
|
||||||
# content_type :string
|
|
||||||
# tsv :tsvector
|
# tsv :tsvector
|
||||||
# curated :boolean default(FALSE), not null
|
# curated :boolean default(FALSE), not null
|
||||||
# sharekey :string
|
# sharekey :string
|
||||||
# network :boolean default(FALSE), not null
|
# network :boolean default(FALSE), not null
|
||||||
|
# content_type :string
|
||||||
|
# footer :text
|
||||||
#
|
#
|
||||||
|
|
||||||
class Status < ApplicationRecord
|
class Status < ApplicationRecord
|
||||||
|
@ -81,7 +82,7 @@ class Status < ApplicationRecord
|
||||||
validates_with DisallowedHashtagsValidator
|
validates_with DisallowedHashtagsValidator
|
||||||
validates :reblog, uniqueness: { scope: :account }, if: :reblog?
|
validates :reblog, uniqueness: { scope: :account }, if: :reblog?
|
||||||
validates :visibility, exclusion: { in: %w(direct limited) }, if: :reblog?
|
validates :visibility, exclusion: { in: %w(direct limited) }, if: :reblog?
|
||||||
validates :content_type, inclusion: { in: %w(text/plain text/markdown text/html) }, allow_nil: true
|
validates :content_type, inclusion: { in: %w(text/plain text/markdown text/x-bbcode text/x-bbcode+markdown text/html) }, allow_nil: true
|
||||||
|
|
||||||
accepts_nested_attributes_for :poll
|
accepts_nested_attributes_for :poll
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,7 @@ defaults: &defaults
|
||||||
show_known_fediverse_at_about_page: true
|
show_known_fediverse_at_about_page: true
|
||||||
show_reblogs_in_public_timelines: false
|
show_reblogs_in_public_timelines: false
|
||||||
show_replies_in_public_timelines: false
|
show_replies_in_public_timelines: false
|
||||||
default_content_type: 'text/plain'
|
default_content_type: 'text/x-bbcode+markdown'
|
||||||
|
|
||||||
development:
|
development:
|
||||||
<<: *defaults
|
<<: *defaults
|
||||||
|
|
Loading…
Reference in New Issue