--- /dev/null
+<?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();
+ }
+}
'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')
},
"keywords": ["bookmark", "link", "share", "web"],
"config": {
+ "sort-packages": true,
"platform": {
"php": "7.1.29"
}
"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",
"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",
},
"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",
"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": {
"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",
"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",
"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",
"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",
"type": "tidelift"
}
],
- "time": "2020-09-24T17:02:11+00:00"
+ "time": "2020-10-08T21:02:27+00:00"
},
{
"name": "sebastian/code-unit-reverse-lookup",
--- /dev/null
+<?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']
+ );
+ }
+}
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']);
<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"}