Add Markdown Extra formatter
Library: [Parsedown Extra](https://github.com/erusev/parsedown-extra) Also sort dependencies alphabetically. Fixes #1169
This commit is contained in:
parent
84045ffbb1
commit
8fabcd0224
7 changed files with 256 additions and 13 deletions
24
application/formatter/BookmarkMarkdownExtraFormatter.php
Normal file
24
application/formatter/BookmarkMarkdownExtraFormatter.php
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace Shaarli\Formatter;
|
||||
|
||||
use Shaarli\Config\ConfigManager;
|
||||
|
||||
/**
|
||||
* Class BookmarkMarkdownExtraFormatter
|
||||
*
|
||||
* Format bookmark description into MarkdownExtra format.
|
||||
*
|
||||
* @see https://michelf.ca/projects/php-markdown/extra/
|
||||
*
|
||||
* @package Shaarli\Formatter
|
||||
*/
|
||||
class BookmarkMarkdownExtraFormatter extends BookmarkMarkdownFormatter
|
||||
{
|
||||
public function __construct(ConfigManager $conf, bool $isLoggedIn)
|
||||
{
|
||||
parent::__construct($conf, $isLoggedIn);
|
||||
|
||||
$this->parsedown = new \ParsedownExtra();
|
||||
}
|
||||
}
|
|
@ -30,7 +30,7 @@ public function index(Request $request, Response $response): Response
|
|||
'theme_available',
|
||||
ThemeUtils::getThemes($this->container->conf->get('resource.raintpl_tpl'))
|
||||
);
|
||||
$this->assignView('formatter_available', ['default', 'markdown']);
|
||||
$this->assignView('formatter_available', ['default', 'markdown', 'markdownExtra']);
|
||||
list($continents, $cities) = generateTimeZoneData(
|
||||
timezone_identifiers_list(),
|
||||
$this->container->conf->get('general.timezone')
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
},
|
||||
"keywords": ["bookmark", "link", "share", "web"],
|
||||
"config": {
|
||||
"sort-packages": true,
|
||||
"platform": {
|
||||
"php": "7.1.29"
|
||||
}
|
||||
|
@ -18,12 +19,13 @@
|
|||
"php": ">=7.1",
|
||||
"ext-json": "*",
|
||||
"ext-zlib": "*",
|
||||
"shaarli/netscape-bookmark-parser": "^2.1",
|
||||
"erusev/parsedown": "^1.6",
|
||||
"slim/slim": "^3.0",
|
||||
"arthurhoaro/web-thumbnailer": "^2.0",
|
||||
"erusev/parsedown": "^1.6",
|
||||
"erusev/parsedown-extra": "^0.8.1",
|
||||
"gettext/gettext": "^4.4",
|
||||
"pubsubhubbub/publisher": "dev-master",
|
||||
"gettext/gettext": "^4.4"
|
||||
"shaarli/netscape-bookmark-parser": "^2.1",
|
||||
"slim/slim": "^3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"roave/security-advisories": "dev-master",
|
||||
|
|
67
composer.lock
generated
67
composer.lock
generated
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "98520a05a7185503ee13d05ffaa535f6",
|
||||
"content-hash": "f84918821b0dceb0cd569875c0418bb8",
|
||||
"packages": [
|
||||
{
|
||||
"name": "arthurhoaro/web-thumbnailer",
|
||||
|
@ -107,6 +107,57 @@
|
|||
},
|
||||
"time": "2019-12-30T22:54:17+00:00"
|
||||
},
|
||||
{
|
||||
"name": "erusev/parsedown-extra",
|
||||
"version": "0.8.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/erusev/parsedown-extra.git",
|
||||
"reference": "91ac3ff98f0cea243bdccc688df43810f044dcef"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/erusev/parsedown-extra/zipball/91ac3ff98f0cea243bdccc688df43810f044dcef",
|
||||
"reference": "91ac3ff98f0cea243bdccc688df43810f044dcef",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"erusev/parsedown": "^1.7.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^4.8.35"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"ParsedownExtra": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Emanuil Rusev",
|
||||
"email": "hello@erusev.com",
|
||||
"homepage": "http://erusev.com"
|
||||
}
|
||||
],
|
||||
"description": "An extension of Parsedown that adds support for Markdown Extra.",
|
||||
"homepage": "https://github.com/erusev/parsedown-extra",
|
||||
"keywords": [
|
||||
"markdown",
|
||||
"markdown extra",
|
||||
"parsedown",
|
||||
"parser"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/erusev/parsedown-extra/issues",
|
||||
"source": "https://github.com/erusev/parsedown-extra/tree/0.8.x"
|
||||
},
|
||||
"time": "2019-12-30T23:20:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "gettext/gettext",
|
||||
"version": "v4.8.2",
|
||||
|
@ -1577,12 +1628,12 @@
|
|||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Roave/SecurityAdvisories.git",
|
||||
"reference": "0749ceaf15c136d085b722a5bb88141398a54142"
|
||||
"reference": "ba5d234b3a1559321b816b64aafc2ce6728799ff"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/0749ceaf15c136d085b722a5bb88141398a54142",
|
||||
"reference": "0749ceaf15c136d085b722a5bb88141398a54142",
|
||||
"url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/ba5d234b3a1559321b816b64aafc2ce6728799ff",
|
||||
"reference": "ba5d234b3a1559321b816b64aafc2ce6728799ff",
|
||||
"shasum": ""
|
||||
},
|
||||
"conflict": {
|
||||
|
@ -1642,7 +1693,7 @@
|
|||
"ezsystems/ezplatform-kernel": ">=1,<1.0.2.1",
|
||||
"ezsystems/ezplatform-user": ">=1,<1.0.1",
|
||||
"ezsystems/ezpublish-kernel": ">=5.3,<5.3.12.1|>=5.4,<5.4.14.2|>=6,<6.7.9.1|>=6.8,<6.13.6.3|>=7,<7.2.4.1|>=7.3,<7.3.2.1|>=7.5,<7.5.7.1",
|
||||
"ezsystems/ezpublish-legacy": ">=5.3,<5.3.12.6|>=5.4,<5.4.14.1|>=2011,<2017.12.7.2|>=2018.6,<2018.6.1.4|>=2018.9,<2018.9.1.3|>=2019.3,<2019.3.4.2",
|
||||
"ezsystems/ezpublish-legacy": ">=5.3,<5.3.12.6|>=5.4,<5.4.14.2|>=2011,<2017.12.7.3|>=2018.6,<2018.6.1.4|>=2018.9,<2018.9.1.3|>=2019.3,<2019.3.5.1",
|
||||
"ezsystems/platform-ui-assets-bundle": ">=4.2,<4.2.3",
|
||||
"ezsystems/repository-forms": ">=2.3,<2.3.2.1",
|
||||
"ezyang/htmlpurifier": "<4.1.1",
|
||||
|
@ -1685,6 +1736,8 @@
|
|||
"mittwald/typo3_forum": "<1.2.1",
|
||||
"monolog/monolog": ">=1.8,<1.12",
|
||||
"namshi/jose": "<2.2",
|
||||
"nette/application": ">=2,<2.0.19|>=2.1,<2.1.13|>=2.2,<2.2.10|>=2.3,<2.3.14|>=2.4,<2.4.16|>=3,<3.0.6",
|
||||
"nette/nette": ">=2,<2.0.19|>=2.1,<2.1.13",
|
||||
"nystudio107/craft-seomatic": "<3.3",
|
||||
"nzo/url-encryptor-bundle": ">=4,<4.3.2|>=5,<5.0.1",
|
||||
"october/backend": ">=1.0.319,<1.0.467",
|
||||
|
@ -1720,6 +1773,7 @@
|
|||
"privatebin/privatebin": "<1.2.2|>=1.3,<1.3.2",
|
||||
"propel/propel": ">=2-alpha.1,<=2-alpha.7",
|
||||
"propel/propel1": ">=1,<=1.7.1",
|
||||
"pterodactyl/panel": "<0.7.19|>=1-rc.0,<=1-rc.6",
|
||||
"pusher/pusher-php-server": "<2.2.1",
|
||||
"rainlab/debugbar-plugin": "<3.1",
|
||||
"robrichards/xmlseclibs": "<3.0.4",
|
||||
|
@ -1805,6 +1859,7 @@
|
|||
"typo3/flow": ">=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1|>=2.3,<2.3.16|>=3,<3.0.10|>=3.1,<3.1.7|>=3.2,<3.2.7|>=3.3,<3.3.5",
|
||||
"typo3/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4",
|
||||
"typo3/phar-stream-wrapper": ">=1,<2.1.1|>=3,<3.1.1",
|
||||
"typo3fluid/fluid": ">=2,<2.0.5|>=2.1,<2.1.4|>=2.2,<2.2.1|>=2.3,<2.3.5|>=2.4,<2.4.1|>=2.5,<2.5.5|>=2.6,<2.6.1",
|
||||
"ua-parser/uap-php": "<3.8",
|
||||
"usmanhalalit/pixie": "<1.0.3|>=2,<2.0.2",
|
||||
"verot/class.upload.php": "<=1.0.3|>=2,<=2.0.4",
|
||||
|
@ -1878,7 +1933,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2020-09-24T17:02:11+00:00"
|
||||
"time": "2020-10-08T21:02:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/code-unit-reverse-lookup",
|
||||
|
|
162
tests/formatter/BookmarkMarkdownExtraFormatterTest.php
Normal file
162
tests/formatter/BookmarkMarkdownExtraFormatterTest.php
Normal file
|
@ -0,0 +1,162 @@
|
|||
<?php
|
||||
|
||||
namespace Shaarli\Formatter;
|
||||
|
||||
use DateTime;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Shaarli\Bookmark\Bookmark;
|
||||
use Shaarli\Config\ConfigManager;
|
||||
|
||||
/**
|
||||
* Class BookmarkMarkdownExtraFormatterTest
|
||||
* @package Shaarli\Formatter
|
||||
*/
|
||||
class BookmarkMarkdownExtraFormatterTest extends TestCase
|
||||
{
|
||||
/** @var string Path of test config file */
|
||||
protected static $testConf = 'sandbox/config';
|
||||
|
||||
/** @var BookmarkFormatter */
|
||||
protected $formatter;
|
||||
|
||||
/** @var ConfigManager instance */
|
||||
protected $conf;
|
||||
|
||||
/**
|
||||
* Initialize formatter instance.
|
||||
*/
|
||||
public function setUp(): void
|
||||
{
|
||||
copy('tests/utils/config/configJson.json.php', self::$testConf .'.json.php');
|
||||
$this->conf = new ConfigManager(self::$testConf);
|
||||
$this->formatter = new BookmarkMarkdownExtraFormatter($this->conf, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test formatting a bookmark with all its attribute filled.
|
||||
*/
|
||||
public function testFormatExtra(): void
|
||||
{
|
||||
$bookmark = new Bookmark();
|
||||
$bookmark->setId($id = 11);
|
||||
$bookmark->setShortUrl($short = 'abcdef');
|
||||
$bookmark->setUrl('https://sub.domain.tld?query=here&for=real#hash');
|
||||
$bookmark->setTitle($title = 'This is a <strong>bookmark</strong>');
|
||||
$bookmark->setDescription('<h2>Content</h2><p>`Here is some content</p>');
|
||||
$bookmark->setTags($tags = ['tag1', 'bookmark', 'other', '<script>alert("xss");</script>']);
|
||||
$bookmark->setThumbnail('http://domain2.tdl2/?type=img&name=file.png');
|
||||
$bookmark->setSticky(true);
|
||||
$bookmark->setCreated($created = DateTime::createFromFormat('Ymd_His', '20190521_190412'));
|
||||
$bookmark->setUpdated($updated = DateTime::createFromFormat('Ymd_His', '20190521_191213'));
|
||||
$bookmark->setPrivate(true);
|
||||
|
||||
$link = $this->formatter->format($bookmark);
|
||||
$this->assertEquals($id, $link['id']);
|
||||
$this->assertEquals($short, $link['shorturl']);
|
||||
$this->assertEquals('https://sub.domain.tld?query=here&for=real#hash', $link['url']);
|
||||
$this->assertEquals(
|
||||
'https://sub.domain.tld?query=here&for=real#hash',
|
||||
$link['real_url']
|
||||
);
|
||||
$this->assertEquals('This is a <strong>bookmark</strong>', $link['title']);
|
||||
$this->assertEquals(
|
||||
'<div class="markdown"><p>'.
|
||||
'<h2>Content</h2><p>`Here is some content</p>'.
|
||||
'</p></div>',
|
||||
$link['description']
|
||||
);
|
||||
$tags[3] = '<script>alert("xss");</script>';
|
||||
$this->assertEquals($tags, $link['taglist']);
|
||||
$this->assertEquals(implode(' ', $tags), $link['tags']);
|
||||
$this->assertEquals(
|
||||
'http://domain2.tdl2/?type=img&name=file.png',
|
||||
$link['thumbnail']
|
||||
);
|
||||
$this->assertEquals($created, $link['created']);
|
||||
$this->assertEquals($created->getTimestamp(), $link['timestamp']);
|
||||
$this->assertEquals($updated, $link['updated']);
|
||||
$this->assertEquals($updated->getTimestamp(), $link['updated_timestamp']);
|
||||
$this->assertTrue($link['private']);
|
||||
$this->assertTrue($link['sticky']);
|
||||
$this->assertEquals('private', $link['class']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test formatting a bookmark with all its attribute filled.
|
||||
*/
|
||||
public function testFormatExtraMinimal(): void
|
||||
{
|
||||
$bookmark = new Bookmark();
|
||||
|
||||
$link = $this->formatter->format($bookmark);
|
||||
$this->assertEmpty($link['id']);
|
||||
$this->assertEmpty($link['shorturl']);
|
||||
$this->assertEmpty($link['url']);
|
||||
$this->assertEmpty($link['real_url']);
|
||||
$this->assertEmpty($link['title']);
|
||||
$this->assertEmpty($link['description']);
|
||||
$this->assertEmpty($link['taglist']);
|
||||
$this->assertEmpty($link['tags']);
|
||||
$this->assertEmpty($link['thumbnail']);
|
||||
$this->assertEmpty($link['created']);
|
||||
$this->assertEmpty($link['timestamp']);
|
||||
$this->assertEmpty($link['updated']);
|
||||
$this->assertEmpty($link['updated_timestamp']);
|
||||
$this->assertFalse($link['private']);
|
||||
$this->assertFalse($link['sticky']);
|
||||
$this->assertEmpty($link['class']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure that the description is properly formatted by the default formatter.
|
||||
*/
|
||||
public function testFormatExtrraDescription(): void
|
||||
{
|
||||
$description = 'This a <strong>description</strong>'. PHP_EOL;
|
||||
$description .= 'text https://sub.domain.tld?query=here&for=real#hash more text'. PHP_EOL;
|
||||
$description .= 'Also, there is an #hashtag added'. PHP_EOL;
|
||||
$description .= ' A N D KEEP SPACES ! '. PHP_EOL;
|
||||
$description .= '# Header {.class}'. PHP_EOL;
|
||||
|
||||
$bookmark = new Bookmark();
|
||||
$bookmark->setDescription($description);
|
||||
$link = $this->formatter->format($bookmark);
|
||||
|
||||
$description = '<div class="markdown"><p>';
|
||||
$description .= 'This a <strong>description</strong><br />'. PHP_EOL;
|
||||
$url = 'https://sub.domain.tld?query=here&for=real#hash';
|
||||
$description .= 'text <a href="'. $url .'">'. $url .'</a> more text<br />'. PHP_EOL;
|
||||
$description .= 'Also, there is an <a href="./add-tag/hashtag">#hashtag</a> added<br />'. PHP_EOL;
|
||||
$description .= 'A N D KEEP SPACES ! </p>' . PHP_EOL;
|
||||
$description .= '<h1 class="class">Header</h1>';
|
||||
$description .= '</div>';
|
||||
|
||||
$this->assertEquals($description, $link['description']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test formatting URL with an index_url set
|
||||
* It should prepend relative links.
|
||||
*/
|
||||
public function testFormatExtraNoteWithIndexUrl(): void
|
||||
{
|
||||
$bookmark = new Bookmark();
|
||||
$bookmark->setUrl($short = '?abcdef');
|
||||
$description = 'Text #hashtag more text';
|
||||
$bookmark->setDescription($description);
|
||||
|
||||
$this->formatter->addContextData('index_url', $root = 'https://domain.tld/hithere/');
|
||||
|
||||
$description = '<div class="markdown"><p>';
|
||||
$description .= 'Text <a href="'. $root .'./add-tag/hashtag">#hashtag</a> more text';
|
||||
$description .= '</p></div>';
|
||||
|
||||
$link = $this->formatter->format($bookmark);
|
||||
$this->assertEquals($root . $short, $link['url']);
|
||||
$this->assertEquals($root . $short, $link['real_url']);
|
||||
$this->assertEquals(
|
||||
$description,
|
||||
$link['description']
|
||||
);
|
||||
}
|
||||
}
|
|
@ -51,7 +51,7 @@ public function testIndex(): void
|
|||
static::assertSame('general.title', $assignedVariables['title']);
|
||||
static::assertSame('resource.theme', $assignedVariables['theme']);
|
||||
static::assertEmpty($assignedVariables['theme_available']);
|
||||
static::assertSame(['default', 'markdown'], $assignedVariables['formatter_available']);
|
||||
static::assertSame(['default', 'markdown', 'markdownExtra'], $assignedVariables['formatter_available']);
|
||||
static::assertNotEmpty($assignedVariables['continents']);
|
||||
static::assertNotEmpty($assignedVariables['cities']);
|
||||
static::assertSame('general.retrieve_description', $assignedVariables['retrieve_description']);
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<link href="{$asset_path}/img/favicon.png#" rel="shortcut icon" type="image/png" />
|
||||
<link href="{$asset_path}/img/apple-touch-icon.png#" rel="apple-touch-icon" sizes="180x180" />
|
||||
<link type="text/css" rel="stylesheet" href="{$asset_path}/css/shaarli.min.css?v={$version_hash}#" />
|
||||
{if="$formatter==='markdown'"}
|
||||
{if="strpos($formatter, 'markdown') !== false"}
|
||||
<link type="text/css" rel="stylesheet" href="{$asset_path}/css/markdown.min.css?v={$version_hash}#" />
|
||||
{/if}
|
||||
{loop="$plugins_includes.css_files"}
|
||||
|
|
Loading…
Reference in a new issue