Skip to content

Commit

Permalink
Allow for Markdown in Web Filtered Fields.
Browse files Browse the repository at this point in the history
  • Loading branch information
rizen committed Aug 23, 2016
1 parent 734a238 commit 4e4f6d4
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 66 deletions.
8 changes: 8 additions & 0 deletions CHANGES.txt
Expand Up @@ -4,6 +4,14 @@ This file tracks the changes to Wing over time. Especially
with respect to new features and compatibility changes.
==========================================================

2016-08-22
* Requires Text::MultiMarkdown and URI::Find::Delimited.
* Allow for Markdown in Web Filtered Fields.
* ContentFilter now uses ## instead of == for headings. Use something like this to upgrade your database:

update groups set description=replace(description, '==','##');


2016-08-18
* Add a login link to 401 ajax error messages.

Expand Down
4 changes: 4 additions & 0 deletions author.t/contentfilter.t
Expand Up @@ -80,6 +80,10 @@ is $list, 'foo<br>bar<ul><li>this is</li><li>a</li><li>list of epic proportions<
Wing::ContentFilter::format_html(\$list2);
is $list2, 'foo<br>bar<ul><li>this is</li><li>a</li><li>list of epic proportions</li><li>and more</li></ul>foo<br>barfoo<br>bar<ul><li>this is</li><li>a</li><li>list of epic proportions</li><li>and more</li></ul>foo<br>bar','can format multiple lists';

my $markdown = 'foo [![Alt text](/path/to/img.jpg)](http://example.net/) bar';
Wing::ContentFilter::format_markdown(\$markdown);
is $markdown, '<p>foo <a href="http://example.net/"><img src="/path/to/img.jpg" alt="Alt text"></a> bar</p>'."\n",'can format markdown';



done_testing();
5 changes: 4 additions & 1 deletion bin/setup/03_install_perl_modules.sh
Expand Up @@ -67,4 +67,7 @@ Image::ExifTool \
Data::OpenGraph \
File::Temp \
Daemon::Control \
Net::Amazon::S3
Net::Amazon::S3 \
Text::MultiMarkdown \
URI::Find::Delimited

160 changes: 97 additions & 63 deletions lib/Wing/ContentFilter.pm
Expand Up @@ -5,36 +5,61 @@ use Wing::Perl;
use pQuery;
use Data::OpenGraph;
use LWP::UserAgent;
use URI::Find;
use Text::MultiMarkdown;
use URI::Find::Delimited;

sub format_html {
sub neutralize_html {
my ($content, $allowed) = @_;
${$content} =~ s/\&/&amp;/g unless $allowed->{entities}; # replace & with &amp; unless we're ok with entities
${$content} =~ s/\</&lt;/g; # disable HTML tags
${$content} =~ s/\>/&gt;/g;
${$content} =~ s/\*{2}(.*?)\*{2}/<strong>$1<\/strong>/g; # bold stuff marked with **
}

sub format_markdown {
my ($content) = @_;
my $m = Text::MultiMarkdown->new(
empty_element_suffix => '>',
tab_width => 2,
disable_definition_lists => 1,
disable_bibliography => 1,
disable_footnotes => 1,
strip_metadata => 1,
);
${$content} = $m->markdown(${$content});
${$content} =~ s{<table>}{<table class="table table-striped">}xmsg;
}

sub format_html {
my ($content, $allowed) = @_;
neutralize_html($content);
unless ($allowed->{with_markdown}) {
${$content} =~ s/\*{2}(.*?)\*{2}/<strong>$1<\/strong>/g; # bold stuff marked with **
}
${$content} =~ s/\_{2}(.*?)\_{2}/<em>$1<\/em>/g; # italicize stuff marked with __
${$content} =~ s/\~{2}(.*?)\~{2}/<s>$1<\/s>/g; # strike through stuff makred with ~~
${$content} =~ s/^\s*(\-{3,})\s*$/<hr>/gm; # --- creates a horizontal rule
my @headings = (6,5,4,3,2); # each = at the start of a line creates H1 through H6 tags
if (exists $allowed->{headings}) {
if (ref $allowed->{headings} eq 'ARRAY') {
@headings = reverse sort @{$allowed->{headings}};
}
else {
warn "Allowed headings should be an array ref of numbers 1 through 6.";
}
}
foreach my $i (@headings) {
${$content} =~ s/^\s*={\Q$i\E}\s*(.*)$/<h$i>$1<\/h$i>/gm;
}
${$content} =~ s{((?:(\n\s*[+-]))(?s:.+?)(?:\z|\n(?!(\s*[+-]))))}{ # convert lines starting with - or + into bulleted lists
my $list = "<ul>".$1."</ul>";
$list =~ s/^\s*[+-]\s*(.*?)$/<li>$1<\/li>/gmr;
}ge;
${$content} =~ s/\n/<br>/g; # convert carriage returns into break tags
${$content} =~ s/(<\/(h1|h2|h3|h4|h5|h6|ul|ol|li)>)(<br>)+/$1/g; # some tags should not be surrounded by break tags
${$content} =~ s/(<br>)+(<(h1|h2|h3|h4|h5|h6|ul|ol|li)>)/$2/g;
${$content} =~ s/^\s*\&gt;\s*(.*)$/<blockquote>$1<\/blockquote>/gm;
unless ($allowed->{with_markdown}) {
${$content} =~ s/^\s*(\-{3,})\s*$/<hr>/gm; # --- creates a horizontal rule
my @headings = (6,5,4,3,2); # each = at the start of a line creates H1 through H6 tags
if (exists $allowed->{headings}) {
if (ref $allowed->{headings} eq 'ARRAY') {
@headings = reverse sort @{$allowed->{headings}};
}
else {
warn "Allowed headings should be an array ref of numbers 1 through 6.";
}
}
foreach my $i (@headings) {
${$content} =~ s/^\s*\#{\Q$i\E}\s*(.*)$/<h$i>$1<\/h$i>/gm;
}
${$content} =~ s{((?:(\n\s*[+-]))(?s:.+?)(?:\z|\n(?!(\s*[+-]))))}{ # convert lines starting with - or + into bulleted lists
my $list = "<ul>".$1."</ul>";
$list =~ s/^\s*[+-]\s*(.*?)$/<li>$1<\/li>/gmr;
}ge;
${$content} =~ s/\n/<br>/g; # convert carriage returns into break tags
${$content} =~ s/(<\/(h1|h2|h3|h4|h5|h6|ul|ol|li)>)(<br>)+/$1/g; # some tags should not be surrounded by break tags
${$content} =~ s/(<br>)+(<(h1|h2|h3|h4|h5|h6|ul|ol|li)>)/$2/g;
}
}

sub format_line_item {
Expand All @@ -44,51 +69,60 @@ sub format_line_item {

sub find_and_format_uris {
my ($content, $allowed) = @_;
my $finder = URI::Find->new(sub {
my $uri = URI->new(shift);

# normal youtube
if ($allowed->{youtube} && $uri->host eq 'www.youtube.com') {
if ($uri->path eq '/watch') {
my %params = $uri->query_form;
return format_youtube($params{v});
my $finder = URI::Find::Delimited->new(
delimiter_re => [ '\(', '\)' ], # ignore markdown urls
callback => sub {
my ($opening_delim, $closing_delim, $uri_string, $title, $whitespace) = @_;

if ($opening_delim) { # we found a markdown url
return $opening_delim.$uri_string.$closing_delim;
}
elsif ($uri->path =~ m{/v/([\w\-_]+)}xms) {
return format_youtube($1);

my $uri = URI->new($uri_string);

# normal youtube
if ($allowed->{youtube} && $uri->host eq 'www.youtube.com') {
if ($uri->path eq '/watch') {
my %params = $uri->query_form;
return format_youtube($params{v});
}
elsif ($uri->path =~ m{/v/([\w\-_]+)}xms) {
return format_youtube($1);
}
elsif ($allowed->{links}) {
return format_link($uri);
}
else {
return $uri->as_string;
}
}

# short youtube
elsif ($allowed->{youtube} && $uri->host eq 'youtu.be') {
return format_youtube(substr($uri->path, 1));
}

# vimeo
elsif ($allowed->{vimeo} && $uri->host eq 'vimeo.com') {
return format_vimeo(substr($uri->path, 1));
}

# images
elsif ($allowed->{images} && $uri->path =~ m/(\.jpg|\.jpeg|\.gif|\.png)$/i) {
return format_image($uri);
}

# links
elsif ($allowed->{links}) {
return format_link($uri);
}

# just put the uri back into the text
else {
return $uri->as_string;
}
}

# short youtube
elsif ($allowed->{youtube} && $uri->host eq 'youtu.be') {
return format_youtube(substr($uri->path, 1));
}

# vimeo
elsif ($allowed->{vimeo} && $uri->host eq 'vimeo.com') {
return format_vimeo(substr($uri->path, 1));
}

# images
elsif ($allowed->{images} && $uri->path =~ m/(\.jpg|\.jpeg|\.gif|\.png)$/i) {
return format_image($uri);
}

# links
elsif ($allowed->{links}) {
return format_link($uri);
}

# just put the uri back into the text
else {
return $uri->as_string;
}
});
},
);
$finder->find($content);
}

Expand All @@ -104,19 +138,19 @@ sub format_link {
my $title = $og->property('title');
unless ($title) {
$title = pQuery($response->decoded_content)->find('title')->html();
}
}
format_html(\$title);
if ($uri->host eq Wing->config->get('sitename')) {
return sprintf '<a href="%s">%s</a>', $uri->as_string, $title || $uri->as_string;
}
else {
return sprintf '<a href="%s" target="_new" title="Links to external site: %s">%s <small><span class="glyphicon glyphicon-new-window"></span></a></small>', $uri->as_string, $uri->host, $title || $uri->as_string;
}
}
}
else {
warn $response->status_line;
return $uri;
}
}
}

sub format_image {
Expand Down
7 changes: 5 additions & 2 deletions lib/Wing/Role/Result/WebFilteredField.pm
Expand Up @@ -38,8 +38,11 @@ sub wing_webfiltered_field {
ouch 442, $sorted[-1].', in '.$field.' is too long. 200 characters max.', $field;
}
my $html = $value;
Wing::ContentFilter::format_html(\$html, { entities => 1 });
Wing::ContentFilter::find_and_format_uris(\$html, { youtube => 1, links => 1, images => 1, vimeo => 1});
Wing::ContentFilter::format_html(\$html, exists $options->{format_html} ? $options->{format_html} : { entities => 1, with_markdown => $options->{use_markdown} });
Wing::ContentFilter::find_and_format_uris(\$html, exists $options->{find_and_format_uris} ? $options->{find_and_format_uris} : { youtube => 1, links => 1, images => 1, vimeo => 1});
if ($options->{use_markdown}) {
Wing::ContentFilter::format_markdown(\$html);
}
$self->$field_html($html);
}
});
Expand Down

0 comments on commit 4e4f6d4

Please sign in to comment.