#!/usr/bin/perl # # DW::Formats # # A helper package for getting info about DW's supported markup formats. Each of # these named formats represents a specific set of expected formatting # behaviors, and each entry and comment is assigned a specific format. By # remembering the expected formats of archived content, we can always display it # as originally intended despite changing our markup behaviors over time. # # When adding a new format, you must also: # - Implement its markup transformations in LJ::CleanHTML. We're deliberately # sacrificing some convenience in this package to keep CleanHTML more legible. # - If it replaces an existing format, update @active_formats, %format_upgrades, # and $default_format accordingly. # # Authors: # Nick Fagerlund # # Copyright (c) 2020 by Dreamwidth Studios, LLC. # # This program is free software; you may redistribute it and/or modify it under # the same terms as Perl itself. For a copy of the license, please reference # 'perldoc perlartistic' or 'perldoc perlgpl'. package DW::Formats; use strict; our @active_formats = qw( html_casual1 markdown0 html_raw0 rte0 ); our $default_format = 'html_casual1'; # obsolete version => newest version: our %format_upgrades = ( html_casual0 => 'html_casual1', ); # format hashref keys: # id: identifies formats everywhere, including the HTML cleaner. # name: displayed in web UI. # description and features: displayed on /dev/formats. our %formats = ( html_casual0 => { id => 'html_casual0', name => "Casual HTML (legacy 0)", features => q{DW tags, auto linebreaks, auto links}, description => q{An older version of casual HTML that doesn't support @mentions.}, }, html_casual1 => { id => 'html_casual1', name => "Casual HTML", features => q{@mentions, DW tags, auto linebreaks, auto links}, description => q{The classic default format: uses HTML tags for formatting, but automatically formats paragraphs.}, }, html_raw0 => { id => 'html_raw0', name => "Raw HTML", features => q{DW tags}, description => q{Normal HTML, plus Dreamwidth's special <user> and <cut> tags. Doesn't automatically format paragraphs, and doesn't support @mentions.}, }, html_extra_raw => { id => 'html_extra_raw', name => "Raw HTML (external source)", features => q{none}, description => q{Normal HTML from a feed or some other external source. Doesn't support any special Dreamwidth syntax, including tags like <user>.}, }, markdown0 => { id => 'markdown0', name => "Markdown", features => q{Markdown syntax, @mentions, DW tags}, description => q{A lightweight markup syntax that automatically formats paragraphs, provides shortcuts for the most common HTML tags, and allows inline HTML for more complex formatting. This implementation uses Perl's Text::Markdown module (which is very close to the original Markdown syntax), plus some DW-specific extensions.}, }, rte0 => { id => 'rte0', name => "Rich Text Editor", features => q{DW tags, auto linebreaks, auto links}, description => q{Markup generated by the legacy FCKEditor RTE.}, }, ); # Legacy aliases: $formats{markdown} = $formats{markdown0}; # Builds items that can be passed to an LJ::html_select for picking a format, # plus the format that should be initially selected. # Args: opts hash like (current => "format", preferred => "format"). # Returns: Hashref like {selected => "format", items => [...]}. (Unfortunately, # LJ::html_select doesn't support just setting selected=true on one of the items, # so we need to return an extra value.) sub select_items { my %opts = @_; # Canonicalize em my $current = validate( $opts{current} ); my $preferred = validate( $opts{preferred} ); my @formats = @active_formats; my $selected = $default_format; # Use the content's existing format if possible, then fall back to the # user's preference (IF it's active or has an active replacement), then the # site default. if ($current) { push( @formats, $current ) unless grep( $_ eq $current, @formats ); $selected = $current; } elsif ( $preferred && is_active($preferred) ) { $selected = $preferred; } elsif ( $preferred && is_active( upgrade($preferred) ) ) { $selected = upgrade($preferred); } return { selected => $selected, items => [ map { { value => $formats{$_}->{id}, text => $formats{$_}->{name}, } } @formats ], }; } # Return the canonical version of the provided format ID if valid, empty string if not. sub validate { my $format = $_[0]; if ( $format && $formats{$format} ) { return $formats{$format}->{id}; } else { return ''; } } sub is_active { my $format = $_[0]; if ( $format && grep( $_ eq $format, @active_formats ) ) { return 1; } return 0; } sub display_name { my $format = $_[0]; if ( $format && $formats{$format} ) { return $formats{$format}->{name}; } return "Unknown markup format"; } # If the provided format ID was replaced by a newer format, return the newest ID # in its lineage. sub upgrade { my $format = validate( $_[0] ); return $format_upgrades{$format} || $format; } # Convenience aliases for email posts -- these should never be entered into the # database as an editor prop value, but email posts pass these to validate() as # a way of asking which format a user currently means if they just said 'html' # or 'markdown'. (Why not just use 'html' and 'markdown' as the "current" # aliases? Because 'markdown' is already in use by old email comments.) $formats{markdown_latest} = $formats{ upgrade('markdown0') }; $formats{html_casual_latest} = $formats{ upgrade('html_casual1') };