mirror of
https://github.com/wallabag/wallabag.git
synced 2024-12-22 07:36:33 +00:00
Add Markdown export
This commit is contained in:
parent
2560826b41
commit
2a382b15c1
9 changed files with 170 additions and 22 deletions
|
@ -114,6 +114,10 @@ parameters:
|
|||
name: export_xml
|
||||
value: 1
|
||||
section: export
|
||||
-
|
||||
name: export_md
|
||||
value: 1
|
||||
section: export
|
||||
-
|
||||
name: import_with_redis
|
||||
value: 0
|
||||
|
|
|
@ -87,6 +87,7 @@
|
|||
"jms/serializer-bundle": "^5.4",
|
||||
"laminas/laminas-code": "^4.7.1",
|
||||
"lcobucci/jwt": "^4.3",
|
||||
"league/html-to-markdown": "^5.1",
|
||||
"mgargano/simplehtmldom": "^1.5",
|
||||
"mnapoli/piwik-twig-extension": "^3.0",
|
||||
"monolog/monolog": "^2.9",
|
||||
|
|
93
composer.lock
generated
93
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": "6407ed5fbd4b0973ed565c2a136d3a81",
|
||||
"content-hash": "a45ce1bad60f024c66e17b6aca7ad88d",
|
||||
"packages": [
|
||||
{
|
||||
"name": "babdev/pagerfanta-bundle",
|
||||
|
@ -5303,6 +5303,95 @@
|
|||
],
|
||||
"time": "2023-01-02T13:28:00+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/html-to-markdown",
|
||||
"version": "5.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/html-to-markdown.git",
|
||||
"reference": "0b4066eede55c48f38bcee4fb8f0aa85654390fd"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/html-to-markdown/zipball/0b4066eede55c48f38bcee4fb8f0aa85654390fd",
|
||||
"reference": "0b4066eede55c48f38bcee4fb8f0aa85654390fd",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-dom": "*",
|
||||
"ext-xml": "*",
|
||||
"php": "^7.2.5 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"mikehaertl/php-shellcommand": "^1.1.0",
|
||||
"phpstan/phpstan": "^1.8.8",
|
||||
"phpunit/phpunit": "^8.5 || ^9.2",
|
||||
"scrutinizer/ocular": "^1.6",
|
||||
"unleashedtech/php-coding-standard": "^2.7 || ^3.0",
|
||||
"vimeo/psalm": "^4.22 || ^5.0"
|
||||
},
|
||||
"bin": [
|
||||
"bin/html-to-markdown"
|
||||
],
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "5.2-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"League\\HTMLToMarkdown\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Colin O'Dell",
|
||||
"email": "colinodell@gmail.com",
|
||||
"homepage": "https://www.colinodell.com",
|
||||
"role": "Lead Developer"
|
||||
},
|
||||
{
|
||||
"name": "Nick Cernis",
|
||||
"email": "nick@cern.is",
|
||||
"homepage": "http://modernnerd.net",
|
||||
"role": "Original Author"
|
||||
}
|
||||
],
|
||||
"description": "An HTML-to-markdown conversion helper for PHP",
|
||||
"homepage": "https://github.com/thephpleague/html-to-markdown",
|
||||
"keywords": [
|
||||
"html",
|
||||
"markdown"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/thephpleague/html-to-markdown/issues",
|
||||
"source": "https://github.com/thephpleague/html-to-markdown/tree/5.1.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://www.colinodell.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://www.paypal.me/colinpodell/10.00",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/colinodell",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/league/html-to-markdown",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-07-12T21:21:09+00:00"
|
||||
},
|
||||
{
|
||||
"name": "masterminds/html5",
|
||||
"version": "2.9.0",
|
||||
|
@ -20034,7 +20123,7 @@
|
|||
"ext-tokenizer": "*",
|
||||
"ext-xml": "*"
|
||||
},
|
||||
"platform-dev": [],
|
||||
"platform-dev": {},
|
||||
"platform-overrides": {
|
||||
"php": "7.4.29"
|
||||
},
|
||||
|
|
24
migrations/Version20241112193044.php
Normal file
24
migrations/Version20241112193044.php
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Application\Migrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Wallabag\Doctrine\WallabagMigration;
|
||||
|
||||
/**
|
||||
* Added the internal setting to export articles in markdown.
|
||||
*/
|
||||
final class Version20241112193044 extends WallabagMigration
|
||||
{
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('INSERT INTO ' . $this->getTable('internal_setting') . " (name, value, section) VALUES ('export_md', '1', 'export');");
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('DELETE FROM' . $this->getTable('internal_setting') . " WHERE name = 'export_md';");
|
||||
}
|
||||
}
|
|
@ -20,7 +20,7 @@ class ExportController extends AbstractController
|
|||
* Gets one entry content.
|
||||
*
|
||||
* @Route("/export/{id}.{format}", name="export_entry", requirements={
|
||||
* "format": "epub|pdf|json|xml|txt|csv",
|
||||
* "format": "epub|pdf|json|xml|txt|csv|md",
|
||||
* "id": "\d+"
|
||||
* })
|
||||
*
|
||||
|
@ -54,7 +54,7 @@ class ExportController extends AbstractController
|
|||
* Export all entries for current user.
|
||||
*
|
||||
* @Route("/export/{category}.{format}", name="export_entries", requirements={
|
||||
* "format": "epub|pdf|json|xml|txt|csv",
|
||||
* "format": "epub|pdf|json|xml|txt|csv|md",
|
||||
* "category": "all|unread|starred|archive|tag_entries|untagged|search|annotated|same_domain"
|
||||
* })
|
||||
*
|
||||
|
|
|
@ -5,6 +5,7 @@ namespace Wallabag\Helper;
|
|||
use Html2Text\Html2Text;
|
||||
use JMS\Serializer\SerializationContext;
|
||||
use JMS\Serializer\SerializerBuilder;
|
||||
use League\HTMLToMarkdown\HtmlConverter;
|
||||
use PHPePub\Core\EPub;
|
||||
use PHPePub\Core\Structure\OPF\DublinCore;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
@ -129,10 +130,8 @@ class EntriesExport
|
|||
|
||||
/**
|
||||
* Use PHPePub to dump a .epub file.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
private function produceEpub()
|
||||
private function produceEpub(): Response
|
||||
{
|
||||
$user = $this->tokenStorage->getToken() ? $this->tokenStorage->getToken()->getUser() : null;
|
||||
\assert($user instanceof User);
|
||||
|
@ -249,10 +248,8 @@ class EntriesExport
|
|||
|
||||
/**
|
||||
* Use TCPDF to dump a .pdf file.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
private function producePdf()
|
||||
private function producePdf(): Response
|
||||
{
|
||||
$user = $this->tokenStorage->getToken() ? $this->tokenStorage->getToken()->getUser() : null;
|
||||
\assert($user instanceof User);
|
||||
|
@ -326,10 +323,8 @@ class EntriesExport
|
|||
|
||||
/**
|
||||
* Inspired from CsvFileDumper.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
private function produceCsv()
|
||||
private function produceCsv(): Response
|
||||
{
|
||||
$delimiter = ';';
|
||||
$enclosure = '"';
|
||||
|
@ -372,10 +367,8 @@ class EntriesExport
|
|||
|
||||
/**
|
||||
* Dump a JSON file.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
private function produceJson()
|
||||
private function produceJson(): Response
|
||||
{
|
||||
return Response::create(
|
||||
$this->prepareSerializingContent('json'),
|
||||
|
@ -390,10 +383,8 @@ class EntriesExport
|
|||
|
||||
/**
|
||||
* Dump a XML file.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
private function produceXml()
|
||||
private function produceXml(): Response
|
||||
{
|
||||
return Response::create(
|
||||
$this->prepareSerializingContent('xml'),
|
||||
|
@ -408,10 +399,8 @@ class EntriesExport
|
|||
|
||||
/**
|
||||
* Dump a TXT file.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
private function produceTxt()
|
||||
private function produceTxt(): Response
|
||||
{
|
||||
$content = '';
|
||||
$bar = str_repeat('=', 100);
|
||||
|
@ -432,6 +421,29 @@ class EntriesExport
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump a Markdown file.
|
||||
*/
|
||||
private function produceMd(): Response
|
||||
{
|
||||
$content = '';
|
||||
$converter = new HtmlConverter();
|
||||
$converter->getConfig()->setOption('strip_tags', true);
|
||||
foreach ($this->entries as $entry) {
|
||||
$content .= $converter->convert('<h1>' . $entry->getTitle() . '</h1>' . $entry->getContent());
|
||||
}
|
||||
|
||||
return Response::create(
|
||||
$content,
|
||||
200,
|
||||
[
|
||||
'Content-type' => 'text/markdown',
|
||||
'Content-Disposition' => 'attachment; filename="' . $this->getSanitizedFilename() . '.md"',
|
||||
'Content-Transfer-Encoding' => 'UTF-8',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Serializer object for producing processes that need it (JSON & XML).
|
||||
*
|
||||
|
|
|
@ -105,6 +105,7 @@
|
|||
{% if craue_setting('export_csv') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', {'category': current_route, 'format': 'csv', 'tag': current_tag, 'search_entry[term]': export_search_term, 'currentRoute': previous_route, 'entry': entry}) }}">CSV</a></li>{% endif %}
|
||||
{% if craue_setting('export_txt') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', {'category': current_route, 'format': 'txt', 'tag': current_tag, 'search_entry[term]': export_search_term, 'currentRoute': previous_route, 'entry': entry}) }}">TXT</a></li>{% endif %}
|
||||
{% if craue_setting('export_xml') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', {'category': current_route, 'format': 'xml', 'tag': current_tag, 'search_entry[term]': export_search_term, 'currentRoute': previous_route, 'entry': entry}) }}">XML</a></li>{% endif %}
|
||||
{% if craue_setting('export_md') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', {'category': current_route, 'format': 'md', 'tag': current_tag, 'search_entry[term]': export_search_term, 'currentRoute': previous_route, 'entry': entry}) }}">Markdown</a></li>{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -242,6 +242,7 @@
|
|||
{% if craue_setting('export_json') %}<li><a href="{{ path('export_entry', {'id': entry.id, 'format': 'json'}) }}" title="Generate JSON file">JSON</a></li>{% endif %}
|
||||
{% if craue_setting('export_txt') %}<li><a href="{{ path('export_entry', {'id': entry.id, 'format': 'txt'}) }}" title="Generate TXT file">TXT</a></li>{% endif %}
|
||||
{% if craue_setting('export_xml') %}<li><a href="{{ path('export_entry', {'id': entry.id, 'format': 'xml'}) }}" title="Generate XML file">XML</a></li>{% endif %}
|
||||
{% if craue_setting('export_md') %}<li><a href="{{ path('export_entry', {'id': entry.id, 'format': 'md'}) }}" title="Generate MD file">Markdown</a></li>{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
|
|
|
@ -307,6 +307,22 @@ class ExportControllerTest extends WallabagTestCase
|
|||
$this->assertNotEmpty('updated_at', (string) $content->entry[0]->updated_at);
|
||||
}
|
||||
|
||||
public function testMdExport()
|
||||
{
|
||||
$this->logInAs('admin');
|
||||
$client = $this->getTestClient();
|
||||
$client->request('GET', '/export/all.md');
|
||||
|
||||
$this->assertSame(200, $client->getResponse()->getStatusCode());
|
||||
|
||||
$headers = $client->getResponse()->headers;
|
||||
$content = $client->getResponse()->getContent();
|
||||
$this->assertSame('text/markdown; charset=UTF-8', $headers->get('content-type'));
|
||||
$this->assertSame('attachment; filename="All articles.md"', $headers->get('content-disposition'));
|
||||
$this->assertSame('UTF-8', $headers->get('content-transfer-encoding'));
|
||||
$this->assertStringContainsString('=================', $content);
|
||||
}
|
||||
|
||||
public function testJsonExportFromSameDomain()
|
||||
{
|
||||
$this->logInAs('admin');
|
||||
|
|
Loading…
Reference in a new issue