forked from wallabag/wallabag
Compare commits
260 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ff5623baac | |||
| 39e84e74a4 | |||
| 092a11b21e | |||
| 980646c52c | |||
| bf85187304 | |||
| f044dddcc0 | |||
| 25282de2be | |||
| dfda2bfbb5 | |||
| fd11d7476d | |||
| 18f5d402b4 | |||
| 2de4d2464f | |||
| 9fce559cb7 | |||
| db2f5d4c5f | |||
| a74ebc65cf | |||
| c589ee9b3c | |||
| db465ace02 | |||
| d954899f5d | |||
| 188da933e0 | |||
| 0cae692681 | |||
| fad18335b1 | |||
| 80b98fbd68 | |||
| 04c2f0de18 | |||
| ed5912e272 | |||
| e229389fd1 | |||
| 7780af1d4b | |||
| e832f1e0be | |||
| 3855de88ff | |||
| da3eaed297 | |||
| 00b33e3855 | |||
| a5972d7983 | |||
| 820d1c69c0 | |||
| fc6a7da71c | |||
| 68aa2cdeaf | |||
| 507ef47bd8 | |||
| ec964ab724 | |||
| dbac4e8591 | |||
| 9ca9b8eb85 | |||
| 3015551b23 | |||
| 5b5584d9f9 | |||
| ebc46ef1fc | |||
| 19178c5a20 | |||
| fc6aafe302 | |||
| ffece8d19a | |||
| 764a79b0ed | |||
| 431d49eb8c | |||
| 4a5f769428 | |||
| 5a9abf8821 | |||
| 68fabb71f8 | |||
| afc3a9b088 | |||
| 70991255b4 | |||
| ca79f8c018 | |||
| d0d7f5ab5e | |||
| 97e922dddc | |||
| a55c4ebf32 | |||
| 5a2821b3ce | |||
| 0364a171bb | |||
| 913fb51707 | |||
| e97902412f | |||
| dfe370eaa3 | |||
| 9fa207bdc6 | |||
| c4a5806180 | |||
| 826ff3f71e | |||
| 486f943e9a | |||
| 2417471bcc | |||
| 41991b326d | |||
| 734bf415c9 | |||
| 470b4aa15b | |||
| f3458bdbd6 | |||
| 15ebd406d4 | |||
| e2d2ce66e7 | |||
| 0cf4a61344 | |||
| ffa9e370f2 | |||
| 7aef830815 | |||
| 9ea4c136d9 | |||
| 74fa98fee3 | |||
| c195dbbd6c | |||
| 2c904346af | |||
| 8f6a3d412c | |||
| 57c896ce81 | |||
| be6695e041 | |||
| d93756d298 | |||
| 5a9612a15e | |||
| 36100365c6 | |||
| 77dbf189ff | |||
| cd17f86c6d | |||
| 843fd97805 | |||
| 5afff609b3 | |||
| 8b0108d686 | |||
| adc79d0578 | |||
| 5d498135ab | |||
| d287ed6b54 | |||
| e69b07314b | |||
| f3291f5797 | |||
| 7cbe1dafb3 | |||
| 0ed133f3d5 | |||
| bfa5a4a556 | |||
| a0cd522db2 | |||
| 926ff63a73 | |||
| 1145810345 | |||
| 5ac6deda69 | |||
| d8e936add0 | |||
| af374b8cb0 | |||
| 6ebffd0f63 | |||
| e8663003c5 | |||
| 255d71727c | |||
| 0fc117ec41 | |||
| d21ccc1e28 | |||
| 752606941f | |||
| 728aa902bb | |||
| fde129e5c6 | |||
| 3b78bbae64 | |||
| 675d78f9c6 | |||
| a26a6bd3a5 | |||
| d021823bab | |||
| 26fbf050f1 | |||
| 8a670fe4fe | |||
| 775a23a6d0 | |||
| e096656e09 | |||
| b2a5e680d3 | |||
| 0a8c9ae64a | |||
| ee266ab12b | |||
| f006bf4324 | |||
| 735bd89cfd | |||
| 46f51c68a9 | |||
| 35c1060f9a | |||
| 5fc91cca06 | |||
| 6a1a7e5f26 | |||
| da0d504c10 | |||
| 25caadf301 | |||
| a083aaf8d3 | |||
| a00763209f | |||
| 2910fb6da4 | |||
| 1761a646ac | |||
| 51c68655fb | |||
| 094604a87c | |||
| 111e17e74d | |||
| 95736f9ada | |||
| 9a29118df9 | |||
| 2bbd1b2e6f | |||
| d640600a40 | |||
| 2feefbf221 | |||
| ed858ef2ac | |||
| 9901556382 | |||
| f4e3cde352 | |||
| f9f320c6dc | |||
| 49e8619c01 | |||
| 94846eb82b | |||
| ace1fa3251 | |||
| a71aeef135 | |||
| 5165ffa6a4 | |||
| a0037b1103 | |||
| 944461d67c | |||
| bec697e23b | |||
| 234ba7c05f | |||
| 049502c444 | |||
| 4c235ddb39 | |||
| af01b63bc4 | |||
| 827bc27e66 | |||
| d231645889 | |||
| efe9e4d291 | |||
| b3308ed8d0 | |||
| f1c9e4ca3a | |||
| 13ee2cf20c | |||
| 9c8d3f1ba0 | |||
| e59a807aa1 | |||
| a4221caec7 | |||
| 91b67ced99 | |||
| b21413cf9a | |||
| 203be88474 | |||
| a8bbc1cc88 | |||
| 750fff8370 | |||
| f46900ca98 | |||
| c0ecd38e76 | |||
| da588f6202 | |||
| ff7c692da0 | |||
| bce6bb7f7d | |||
| d84c44aaf7 | |||
| d08c4fdaaa | |||
| 820ff41363 | |||
| b49f49f8e6 | |||
| 5db058ff72 | |||
| ab2203ccb7 | |||
| 098c69e845 | |||
| b4ba6584aa | |||
| 120d9b42f9 | |||
| cddc90b7dd | |||
| fdc4d13db2 | |||
| d96d1bf0dc | |||
| 4a5302dd20 | |||
| bf62bf52f7 | |||
| eeeceb193f | |||
| 2dda155f95 | |||
| f042f37bfc | |||
| 9e399a61f4 | |||
| 3ed7c7c37c | |||
| 476f3afec3 | |||
| f78c088304 | |||
| 444d20ba4b | |||
| f6cfae5ed7 | |||
| 340dfe7125 | |||
| faaa98c6d6 | |||
| e5c937dffc | |||
| 10727cd261 | |||
| 3a859d9fa0 | |||
| ca03eb7332 | |||
| de64ca1251 | |||
| 20f8c369c3 | |||
| c309d86c07 | |||
| e051771782 | |||
| c52bb0cc18 | |||
| b10100ebfd | |||
| 9663747719 | |||
| a606e463ca | |||
| 90c21fa1fb | |||
| f27b0fe405 | |||
| 1958b86777 | |||
| 47660ff6c5 | |||
| e42200ec17 | |||
| 49eea9f921 | |||
| d2e821d843 | |||
| bf66901e40 | |||
| 54ca4b67db | |||
| d012103142 | |||
| 2e134d81c4 | |||
| 22a2fc13a1 | |||
| 796b31c3b1 | |||
| 27e54e0634 | |||
| c93a677926 | |||
| 9313190303 | |||
| 2cf99296a1 | |||
| 9b663800fd | |||
| e30b5a856a | |||
| c6ff0bc691 | |||
| bdb297f2b4 | |||
| bbc20c2071 | |||
| 0fc6ae8cdb | |||
| b1752b619d | |||
| 0e44035b67 | |||
| 741db06447 | |||
| 4482e52f3a | |||
| 2af48b8174 | |||
| a1aac10bd5 | |||
| 40aeeafea2 | |||
| e6abd04026 | |||
| 8ef6a14652 | |||
| 1ce5164e70 | |||
| a3b64611f8 | |||
| 981d6a47da | |||
| 4b338afa40 | |||
| 1c2190fd68 | |||
| 407dd48ed0 | |||
| 397ad455e6 | |||
| c12239c4f8 | |||
| 88c9df9b80 | |||
| cbcfa69c05 | |||
| 20578f0b8e | |||
| 18e1106f76 | |||
| 6ff00315d0 | |||
| 0f17a8cf8a | |||
| 5e4eae42a0 |
15
.github/dependabot.yml
vendored
15
.github/dependabot.yml
vendored
@ -7,6 +7,13 @@ updates:
|
||||
time: "04:00"
|
||||
timezone: Europe/Paris
|
||||
open-pull-requests-limit: 10
|
||||
groups:
|
||||
babel-dependencies:
|
||||
patterns:
|
||||
- "*babel*"
|
||||
fontsource-dependencies:
|
||||
patterns:
|
||||
- "*fontsource*"
|
||||
ignore:
|
||||
- dependency-name: materialize-css
|
||||
versions:
|
||||
@ -18,6 +25,10 @@ updates:
|
||||
time: "04:00"
|
||||
timezone: Europe/Paris
|
||||
open-pull-requests-limit: 10
|
||||
groups:
|
||||
symfony-dependencies:
|
||||
patterns:
|
||||
- "symfony/*"
|
||||
reviewers:
|
||||
- j0k3r
|
||||
- tcitworld
|
||||
@ -26,6 +37,10 @@ updates:
|
||||
- dependency-name: lcobucci/jwt
|
||||
versions:
|
||||
- ">= 4.2.0"
|
||||
# until we add support for Symfony 5+
|
||||
- dependency-name: symfony/*
|
||||
versions:
|
||||
- ">= 5.0.0"
|
||||
- package-ecosystem: github-actions
|
||||
directory: "/"
|
||||
schedule:
|
||||
|
||||
44
.github/workflows/assets.yml
vendored
44
.github/workflows/assets.yml
vendored
@ -1,44 +0,0 @@
|
||||
name: "Assets"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- "2.**"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
js:
|
||||
name: "Building assets"
|
||||
runs-on: "ubuntu-20.04"
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v3"
|
||||
|
||||
- name: "Install Node"
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: "16"
|
||||
|
||||
- name: "Install dependencies with Yarn"
|
||||
run: "yarn install"
|
||||
|
||||
- name: "Build dev assets"
|
||||
run: "yarn run build:dev"
|
||||
|
||||
- name: "Build prod assets"
|
||||
run: "yarn run build:prod"
|
||||
|
||||
- name: "Validate no change were created"
|
||||
run: |
|
||||
GITDIFF=`git diff`
|
||||
if [ "$GITDIFF" == "" ]; then
|
||||
exit 0
|
||||
else
|
||||
git diff
|
||||
exit 1
|
||||
fi
|
||||
2
.github/workflows/coding-standards.yml
vendored
2
.github/workflows/coding-standards.yml
vendored
@ -17,7 +17,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v3"
|
||||
uses: "actions/checkout@v4"
|
||||
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
|
||||
5
.github/workflows/continuous-integration.yml
vendored
5
.github/workflows/continuous-integration.yml
vendored
@ -33,6 +33,7 @@ jobs:
|
||||
- "8.0"
|
||||
- "8.1"
|
||||
- "8.2"
|
||||
- "8.3"
|
||||
database:
|
||||
- "sqlite"
|
||||
- "mysql"
|
||||
@ -40,7 +41,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v3"
|
||||
uses: "actions/checkout@v4"
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
@ -106,7 +107,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v3"
|
||||
uses: "actions/checkout@v4"
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
|
||||
2
.github/workflows/translations.yml
vendored
2
.github/workflows/translations.yml
vendored
@ -22,7 +22,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v3"
|
||||
uses: "actions/checkout@v4"
|
||||
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
|
||||
8
.github/workflows/upload-release-package.yml
vendored
8
.github/workflows/upload-release-package.yml
vendored
@ -16,7 +16,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v3"
|
||||
uses: "actions/checkout@v4"
|
||||
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
@ -29,6 +29,12 @@ jobs:
|
||||
env:
|
||||
COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: "Install Node"
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: "yarn"
|
||||
|
||||
- name: Create the package
|
||||
run: make release VERSION=${{ github.event.release.tag_name }}
|
||||
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -51,6 +51,7 @@ bin
|
||||
app/Resources/build/
|
||||
!/src/Wallabag/CoreBundle/Resources/public
|
||||
/src/Wallabag/CoreBundle/Resources/public/*
|
||||
package-lock.json
|
||||
|
||||
# Test-generated files
|
||||
admin-export.json
|
||||
|
||||
26
app/DoctrineMigrations/Version20230728085538.php
Normal file
26
app/DoctrineMigrations/Version20230728085538.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Application\Migrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Wallabag\CoreBundle\Doctrine\WallabagMigration;
|
||||
|
||||
/**
|
||||
* Remove demonstration mode settings.
|
||||
*/
|
||||
final class Version20230728085538 extends WallabagMigration
|
||||
{
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('DELETE FROM' . $this->getTable('internal_setting') . " WHERE name = 'demo_mode_enabled';");
|
||||
$this->addSql('DELETE FROM' . $this->getTable('internal_setting') . " WHERE name = 'demo_mode_username';");
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('INSERT INTO ' . $this->getTable('internal_setting') . " (name, value, section) VALUES ('demo_mode_enabled', '0', 'misc');");
|
||||
$this->addSql('INSERT INTO ' . $this->getTable('internal_setting') . " (name, value, section) VALUES ('demo_mode_username', 'wallabag', 'misc');");
|
||||
}
|
||||
}
|
||||
24
app/DoctrineMigrations/Version20230728091417.php
Normal file
24
app/DoctrineMigrations/Version20230728091417.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Application\Migrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Wallabag\CoreBundle\Doctrine\WallabagMigration;
|
||||
|
||||
/**
|
||||
* Remove mobi export.
|
||||
*/
|
||||
final class Version20230728091417 extends WallabagMigration
|
||||
{
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('DELETE FROM' . $this->getTable('internal_setting') . " WHERE name = 'export_mobi';");
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('INSERT INTO ' . $this->getTable('internal_setting') . " (name, value, section) VALUES ('export_mobi', '1', 'export');");
|
||||
}
|
||||
}
|
||||
50
app/DoctrineMigrations/Version20230728093912.php
Normal file
50
app/DoctrineMigrations/Version20230728093912.php
Normal file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Application\Migrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Wallabag\CoreBundle\Doctrine\WallabagMigration;
|
||||
|
||||
/**
|
||||
* Add is_not_parsed field to entry table.
|
||||
*/
|
||||
final class Version20230728093912 extends WallabagMigration
|
||||
{
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$entryTable = $schema->getTable($this->getTable('entry'));
|
||||
|
||||
$this->skipIf($entryTable->hasColumn('is_not_parsed'), 'It seems that you already played this migration.');
|
||||
|
||||
$entryTable->addColumn('is_not_parsed', 'boolean', [
|
||||
'default' => 0,
|
||||
'notnull' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query to update entries where content is equal to `fetching_error_message`.
|
||||
*/
|
||||
public function postUp(Schema $schema): void
|
||||
{
|
||||
$entryTable = $schema->getTable($this->getTable('entry'));
|
||||
$this->skipIf(!$entryTable->hasColumn('is_not_parsed'), 'Unable to update is_not_parsed colum');
|
||||
|
||||
// Need to do a `LIKE` with a final percent to handle the new line character
|
||||
$this->connection->executeQuery(
|
||||
'UPDATE ' . $this->getTable('entry') . ' SET is_not_parsed = :isNotParsed WHERE content LIKE :content',
|
||||
[
|
||||
'isNotParsed' => true,
|
||||
'content' => str_replace("\n", '', addslashes($this->container->getParameter('wallabag_core.fetching_error_message'))) . '%',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$entryTable = $schema->getTable($this->getTable('entry'));
|
||||
$entryTable->dropColumn('is_not_parsed');
|
||||
}
|
||||
}
|
||||
47
app/DoctrineMigrations/Version20230729093853.php
Normal file
47
app/DoctrineMigrations/Version20230729093853.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Application\Migrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Wallabag\CoreBundle\Doctrine\WallabagMigration;
|
||||
|
||||
/**
|
||||
* Add custom_css column to config table.
|
||||
*/
|
||||
final class Version20230729093853 extends WallabagMigration
|
||||
{
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$configTable = $schema->getTable($this->getTable('config'));
|
||||
|
||||
$this->skipIf($configTable->hasColumn('custom_css'), 'It seems that you already played this migration.');
|
||||
|
||||
$configTable->addColumn('custom_css', 'text', [
|
||||
'notnull' => false,
|
||||
]);
|
||||
|
||||
$configTable->addColumn('font', 'text', [
|
||||
'notnull' => false,
|
||||
]);
|
||||
|
||||
$configTable->addColumn('fontsize', 'float', [
|
||||
'notnull' => false,
|
||||
]);
|
||||
|
||||
$configTable->addColumn('line_height', 'float', [
|
||||
'notnull' => false,
|
||||
]);
|
||||
|
||||
$configTable->addColumn('max_width', 'float', [
|
||||
'notnull' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$configTable = $schema->getTable($this->getTable('config'));
|
||||
$configTable->dropColumn('custom_css');
|
||||
}
|
||||
}
|
||||
@ -10,13 +10,18 @@ import 'mathjax/es5/tex-svg';
|
||||
/* Fonts */
|
||||
import 'material-design-icons-iconfont/dist/material-design-icons.css';
|
||||
import 'lato-font/css/lato-font.css';
|
||||
import 'open-dyslexic/open-dyslexic-regular.css';
|
||||
import '@fontsource/atkinson-hyperlegible';
|
||||
import '@fontsource/eb-garamond';
|
||||
import '@fontsource/montserrat';
|
||||
import '@fontsource/oswald';
|
||||
import './global.scss';
|
||||
|
||||
/* Shortcuts */
|
||||
import './js/shortcuts/entry';
|
||||
import './js/shortcuts/main';
|
||||
|
||||
/* Hightlight */
|
||||
/* Highlight */
|
||||
import './js/highlight';
|
||||
|
||||
import { savePercent, retrievePercent } from './js/tools';
|
||||
@ -26,7 +31,7 @@ import { savePercent, retrievePercent } from './js/tools';
|
||||
========================================================================== */
|
||||
|
||||
$(document).ready(() => {
|
||||
if ($('article').length) {
|
||||
if ($('#article').length) {
|
||||
const app = new annotator.App();
|
||||
|
||||
app.include(annotator.ui.main, {
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
Article
|
||||
========================================================================== */
|
||||
|
||||
#article {
|
||||
#article, #preview-article {
|
||||
font-size: 20px;
|
||||
margin: 0 auto;
|
||||
max-width: 45em;
|
||||
|
||||
@ -324,6 +324,12 @@ a.original:not(.waves-effect) {
|
||||
height: 3rem;
|
||||
}
|
||||
|
||||
.settings .settings-range-label {
|
||||
position: absolute;
|
||||
top: -14px;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.entries-row {
|
||||
display: grid;
|
||||
margin: 0.4rem 0 0;
|
||||
|
||||
@ -8,7 +8,9 @@ import 'materialize-css/dist/js/materialize';
|
||||
import '../_global/index';
|
||||
|
||||
/* Tools */
|
||||
import { initExport, initFilters, initRandom } from './js/tools';
|
||||
import {
|
||||
initExport, initFilters, initRandom, initPreviewText,
|
||||
} from './js/tools';
|
||||
|
||||
/* Import shortcuts */
|
||||
import './js/shortcuts/main';
|
||||
@ -177,6 +179,7 @@ $(document).ready(() => {
|
||||
initRandom();
|
||||
stickyNav();
|
||||
articleScroll();
|
||||
initPreviewText();
|
||||
|
||||
const toggleNav = (toShow, toFocus) => {
|
||||
$('.nav-panel-actions').hide(100);
|
||||
@ -199,6 +202,29 @@ $(document).ready(() => {
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#config_fontsize').on('input', () => {
|
||||
const value = $('#config_fontsize').val();
|
||||
const css = `${value}em`;
|
||||
$('#preview-content').css('font-size', css);
|
||||
});
|
||||
|
||||
$('#config_font').on('change', () => {
|
||||
const value = $('#config_font').val();
|
||||
$('#preview-content').css('font-family', value);
|
||||
});
|
||||
|
||||
$('#config_lineHeight').on('input', () => {
|
||||
const value = $('#config_lineHeight').val();
|
||||
const css = `${value}em`;
|
||||
$('#preview-content').css('line-height', css);
|
||||
});
|
||||
|
||||
$('#config_maxWidth').on('input', () => {
|
||||
const value = $('#config_maxWidth').val();
|
||||
const css = `${value}em`;
|
||||
$('#preview-article').css('max-width', css);
|
||||
});
|
||||
|
||||
const materialAddForm = $('.nav-panel-add');
|
||||
materialAddForm.on('submit', () => {
|
||||
materialAddForm.addClass('disabled');
|
||||
|
||||
@ -29,8 +29,25 @@ function initRandom() {
|
||||
}
|
||||
}
|
||||
|
||||
function initPreviewText() {
|
||||
// no display if preview_text not available
|
||||
if ($('div').is('#preview-article')) {
|
||||
const defaultFontFamily = $('#config_font').val();
|
||||
const defaultFontSize = $('#config_fontsize').val();
|
||||
const defaultLineHeight = $('#config_lineHeight').val();
|
||||
const defaultMaxWidth = $('#config_maxWidth').val();
|
||||
const previewContent = $('#preview-content');
|
||||
|
||||
previewContent.css('font-family', defaultFontFamily);
|
||||
previewContent.css('font-size', `${defaultFontSize}em`);
|
||||
previewContent.css('line-height', `${defaultLineHeight}em`);
|
||||
$('#preview-article').css('max-width', `${defaultMaxWidth}em`);
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
initExport,
|
||||
initFilters,
|
||||
initRandom,
|
||||
initPreviewText,
|
||||
};
|
||||
|
||||
@ -96,8 +96,6 @@ fos_rest:
|
||||
- 'application/pdf'
|
||||
epub:
|
||||
- 'application/epub+zip'
|
||||
mobi:
|
||||
- 'application/x-mobipocket-ebook'
|
||||
view_response_listener: 'force'
|
||||
formats:
|
||||
xml: true
|
||||
@ -106,13 +104,12 @@ fos_rest:
|
||||
csv: true
|
||||
pdf: true
|
||||
epub: true
|
||||
mobi: true
|
||||
failed_validation: HTTP_BAD_REQUEST
|
||||
routing_loader: false
|
||||
format_listener:
|
||||
enabled: true
|
||||
rules:
|
||||
- { path: "^/api/entries/([0-9]+)/export.(.*)", priorities: ['epub', 'mobi', 'pdf', 'txt', 'csv'], fallback_format: json, prefer_extension: false }
|
||||
- { path: "^/api/entries/([0-9]+)/export.(.*)", priorities: ['epub', 'pdf', 'txt', 'csv'], fallback_format: json, prefer_extension: false }
|
||||
- { path: "^/api", priorities: ['json', 'xml'], fallback_format: json, prefer_extension: false }
|
||||
- { path: "^/annotations", priorities: ['json', 'xml'], fallback_format: json, prefer_extension: false }
|
||||
# for an unknown reason, EACH REQUEST goes to FOS\RestBundle\EventListener\FormatListener
|
||||
@ -277,6 +274,16 @@ old_sound_rabbit_mq:
|
||||
exchange_options:
|
||||
name: 'wallabag.import.chrome'
|
||||
type: topic
|
||||
import_shaarli:
|
||||
connection: default
|
||||
exchange_options:
|
||||
name: 'wallabag.import.shaarli'
|
||||
type: topic
|
||||
import_pocket_html:
|
||||
connection: default
|
||||
exchange_options:
|
||||
name: 'wallabag.import.pocket_html'
|
||||
type: topic
|
||||
consumers:
|
||||
import_pocket:
|
||||
connection: default
|
||||
@ -368,6 +375,24 @@ old_sound_rabbit_mq:
|
||||
name: 'wallabag.import.chrome'
|
||||
callback: wallabag_import.consumer.amqp.chrome
|
||||
qos_options: {prefetch_count: "%rabbitmq_prefetch_count%"}
|
||||
import_shaarli:
|
||||
connection: default
|
||||
exchange_options:
|
||||
name: 'wallabag.import.shaarli'
|
||||
type: topic
|
||||
queue_options:
|
||||
name: 'wallabag.import.shaarli'
|
||||
callback: wallabag_import.consumer.amqp.shaarli
|
||||
qos_options: {prefetch_count: "%rabbitmq_prefetch_count%"}
|
||||
import_pocket_html:
|
||||
connection: default
|
||||
exchange_options:
|
||||
name: 'wallabag.import.pocket_html'
|
||||
type: topic
|
||||
queue_options:
|
||||
name: 'wallabag.import.pocket_html'
|
||||
callback: wallabag_import.consumer.amqp.pocket_html
|
||||
qos_options: {prefetch_count: "%rabbitmq_prefetch_count%"}
|
||||
|
||||
fos_js_routing:
|
||||
routes_to_expose:
|
||||
|
||||
@ -37,7 +37,7 @@ parameters:
|
||||
twofactor_sender: no-reply@wallabag.org
|
||||
|
||||
# fosuser stuff
|
||||
fosuser_registration: true
|
||||
fosuser_registration: false
|
||||
fosuser_confirmation: true
|
||||
|
||||
# how long the access token should live in seconds for the API
|
||||
|
||||
@ -5,5 +5,5 @@ parameters:
|
||||
opera: https://addons.opera.com/en/extensions/details/wallabagger/?display=en
|
||||
f_droid: https://f-droid.org/app/fr.gaulupeau.apps.InThePoche
|
||||
google_play: https://play.google.com/store/apps/details?id=fr.gaulupeau.apps.InThePoche
|
||||
ios: https://itunes.apple.com/app/wallabag-2/id1170800946?mt=8
|
||||
ios: https://apps.apple.com/app/wallabag-2/id1170800946?mt=8
|
||||
windows: https://www.microsoft.com/store/apps/wallabag/9nblggh11646
|
||||
|
||||
@ -8,3 +8,4 @@ parameters:
|
||||
test_database_path: "%env(TEST_DATABASE_PATH)%"
|
||||
env(TEST_DATABASE_PATH): "%kernel.project_dir%/data/db/wallabag_test.sqlite"
|
||||
test_database_charset: utf8
|
||||
fosuser_registration: true
|
||||
|
||||
@ -30,6 +30,7 @@ services:
|
||||
$senderName: "%scheb_two_factor.email.sender_name%"
|
||||
$storeArticleHeaders: '@=service(''craue_config'').get(''store_article_headers'')'
|
||||
$supportUrl: '@=service(''craue_config'').get(''wallabag_support_url'')'
|
||||
$fonts: '%wallabag_core.fonts%'
|
||||
|
||||
Wallabag\AnnotationBundle\:
|
||||
resource: '../../src/Wallabag/AnnotationBundle/*'
|
||||
@ -116,6 +117,16 @@ services:
|
||||
$rabbitMqProducer: '@old_sound_rabbit_mq.import_wallabag_v2_producer'
|
||||
$redisProducer: '@wallabag_import.producer.redis.wallabag_v2'
|
||||
|
||||
Wallabag\ImportBundle\Controller\ShaarliController:
|
||||
arguments:
|
||||
$rabbitMqProducer: '@old_sound_rabbit_mq.import_shaarli_producer'
|
||||
$redisProducer: '@wallabag_import.producer.redis.shaarli'
|
||||
|
||||
Wallabag\ImportBundle\Controller\PocketHtmlController:
|
||||
arguments:
|
||||
$rabbitMqProducer: '@old_sound_rabbit_mq.import_pocket_html_producer'
|
||||
$redisProducer: '@wallabag_import.producer.redis.pocket_html'
|
||||
|
||||
Wallabag\ImportBundle\:
|
||||
resource: '../../src/Wallabag/ImportBundle/*'
|
||||
exclude: '../../src/Wallabag/ImportBundle/{Consumer,Controller,Redis}'
|
||||
@ -172,6 +183,9 @@ services:
|
||||
MatomoTwigExtension\MatomoTwigExtension:
|
||||
public: false
|
||||
|
||||
ScssPhp\ScssPhp\Compiler:
|
||||
public: false
|
||||
|
||||
Wallabag\CoreBundle\Event\Listener\UserLocaleListener:
|
||||
tags:
|
||||
- { name: kernel.event_listener, event: security.interactive_login, method: onInteractiveLogin }
|
||||
@ -351,6 +365,14 @@ services:
|
||||
tags:
|
||||
- { name: wallabag_import.import, alias: chrome }
|
||||
|
||||
Wallabag\ImportBundle\Import\ShaarliImport:
|
||||
tags:
|
||||
- { name: wallabag_import.import, alias: shaarli }
|
||||
|
||||
Wallabag\ImportBundle\Import\PocketHtmlImport:
|
||||
tags:
|
||||
- { name: wallabag_import.import, alias: pocket_html }
|
||||
|
||||
# to factorize the proximity and bypass translation for prev & next
|
||||
pagerfanta.view.default_wallabag:
|
||||
class: Pagerfanta\View\OptionableView
|
||||
|
||||
@ -18,6 +18,8 @@ services:
|
||||
$pinboardConsumer: '@old_sound_rabbit_mq.import_pinboard_consumer'
|
||||
$deliciousConsumer: '@old_sound_rabbit_mq.import_delicious_consumer'
|
||||
$elcuratorConsumer: '@old_sound_rabbit_mq.import_elcurator_consumer'
|
||||
$shaarliConsumer: '@old_sound_rabbit_mq.import_shaarli_consumer'
|
||||
$pocketHtmlConsumer: '@old_sound_rabbit_mq.import_pocket_html_consumer'
|
||||
|
||||
wallabag_import.consumer.amqp.pocket:
|
||||
class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer
|
||||
@ -68,3 +70,13 @@ services:
|
||||
class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer
|
||||
arguments:
|
||||
$import: '@Wallabag\ImportBundle\Import\ChromeImport'
|
||||
|
||||
wallabag_import.consumer.amqp.shaarli:
|
||||
class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer
|
||||
arguments:
|
||||
$import: '@Wallabag\ImportBundle\Import\ShaarliImport'
|
||||
|
||||
wallabag_import.consumer.amqp.pocket_html:
|
||||
class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer
|
||||
arguments:
|
||||
$import: '@Wallabag\ImportBundle\Import\PocketHtmlImport'
|
||||
|
||||
@ -164,3 +164,35 @@ services:
|
||||
class: Wallabag\ImportBundle\Consumer\RedisEntryConsumer
|
||||
arguments:
|
||||
$import: '@Wallabag\ImportBundle\Import\ChromeImport'
|
||||
|
||||
# shaarli
|
||||
wallabag_import.queue.redis.shaarli:
|
||||
class: Simpleue\Queue\RedisQueue
|
||||
arguments:
|
||||
$queueName: "wallabag.import.shaarli"
|
||||
|
||||
wallabag_import.producer.redis.shaarli:
|
||||
class: Wallabag\ImportBundle\Redis\Producer
|
||||
arguments:
|
||||
- "@wallabag_import.queue.redis.shaarli"
|
||||
|
||||
wallabag_import.consumer.redis.shaarli:
|
||||
class: Wallabag\ImportBundle\Consumer\RedisEntryConsumer
|
||||
arguments:
|
||||
$import: '@Wallabag\ImportBundle\Import\ShaarliImport'
|
||||
|
||||
# pocket html
|
||||
wallabag_import.queue.redis.pocket_html:
|
||||
class: Simpleue\Queue\RedisQueue
|
||||
arguments:
|
||||
$queueName: "wallabag.import.pocket_html"
|
||||
|
||||
wallabag_import.producer.redis.pocket_html:
|
||||
class: Wallabag\ImportBundle\Redis\Producer
|
||||
arguments:
|
||||
- "@wallabag_import.queue.redis.pocket_html"
|
||||
|
||||
wallabag_import.consumer.redis.pocket_html:
|
||||
class: Wallabag\ImportBundle\Consumer\RedisEntryConsumer
|
||||
arguments:
|
||||
$import: '@Wallabag\ImportBundle\Import\PocketHtmlImport'
|
||||
|
||||
@ -8,3 +8,4 @@ parameters:
|
||||
test_database_path: ~
|
||||
env(TEST_DATABASE_PATH): ~
|
||||
test_database_charset: utf8mb4
|
||||
fosuser_registration: true
|
||||
|
||||
@ -8,3 +8,4 @@ parameters:
|
||||
test_database_path: ~
|
||||
env(TEST_DATABASE_PATH): ~
|
||||
test_database_charset: utf8
|
||||
fosuser_registration: true
|
||||
|
||||
@ -5,8 +5,9 @@ parameters:
|
||||
test_database_name: ~
|
||||
test_database_user: ~
|
||||
test_database_password: ~
|
||||
# Using an environnement variable in order to avoid the error "attempt to write a readonly database"
|
||||
# Using an environment variable in order to avoid the error "attempt to write a readonly database"
|
||||
# when the schema is dropped then recreate
|
||||
test_database_path: "%env(TEST_DATABASE_PATH)%"
|
||||
env(TEST_DATABASE_PATH): "%kernel.project_dir%/data/db/wallabag_test.sqlite"
|
||||
test_database_charset: utf8
|
||||
fosuser_registration: true
|
||||
|
||||
@ -85,10 +85,6 @@ wallabag_core:
|
||||
name: export_epub
|
||||
value: 1
|
||||
section: export
|
||||
-
|
||||
name: export_mobi
|
||||
value: 0
|
||||
section: export
|
||||
-
|
||||
name: export_pdf
|
||||
value: 1
|
||||
@ -129,14 +125,6 @@ wallabag_core:
|
||||
name: matomo_site_id
|
||||
value: 1
|
||||
section: analytics
|
||||
-
|
||||
name: demo_mode_enabled
|
||||
value: 0
|
||||
section: misc
|
||||
-
|
||||
name: demo_mode_username
|
||||
value: wallabag
|
||||
section: misc
|
||||
-
|
||||
name: download_images_enabled
|
||||
value: 0
|
||||
@ -165,7 +153,16 @@ wallabag_core:
|
||||
rule: host = "feeds.reuters.com"
|
||||
-
|
||||
rule: _all ~ "https?://www\.lemonde\.fr/tiny.*"
|
||||
fonts:
|
||||
- 'Sans-serif'
|
||||
- 'Serif'
|
||||
- 'Atkinson Hyperlegible'
|
||||
- 'EB Garamond'
|
||||
- 'Lato'
|
||||
- 'Montserrat'
|
||||
- 'OpenDyslexicRegular'
|
||||
- 'Oswald'
|
||||
|
||||
wallabag_import:
|
||||
allow_mimetypes: ['application/octet-stream', 'application/json', 'text/plain', 'text/csv']
|
||||
allow_mimetypes: ['application/octet-stream', 'application/json', 'text/plain', 'text/csv', 'text/html']
|
||||
resource_dir: "%kernel.project_dir%/web/uploads/import"
|
||||
|
||||
@ -114,8 +114,9 @@
|
||||
"scheb/2fa-google-authenticator": "^5.13",
|
||||
"scheb/2fa-qr-code": "^5.13",
|
||||
"scheb/2fa-trusted-device": "^5.13",
|
||||
"scssphp/scssphp": "^1.11",
|
||||
"sensio/framework-extra-bundle": "^6.2",
|
||||
"sentry/sentry-symfony": "4.10.0",
|
||||
"sentry/sentry-symfony": "4.12.0",
|
||||
"stof/doctrine-extensions-bundle": "^1.2",
|
||||
"symfony/asset": "^4.4",
|
||||
"symfony/config": "^4.4",
|
||||
@ -148,7 +149,6 @@
|
||||
"twig/extra-bundle": "^3.4",
|
||||
"twig/string-extra": "^3.4",
|
||||
"twig/twig": "^3.4.3",
|
||||
"wallabag/php-mobi": "~1.0",
|
||||
"wallabag/phpepub": "^4.0.10",
|
||||
"willdurand/hateoas": "^3.8",
|
||||
"willdurand/hateoas-bundle": "~2.1"
|
||||
|
||||
478
composer.lock
generated
478
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -25,7 +25,7 @@ parameters:
|
||||
twofactor_sender: ${TWOFACTOR_SENDER:-no-reply@wallabag.org}
|
||||
|
||||
# fosuser stuff
|
||||
fosuser_registration: ${FOSUSER_REGISTRATION:-true}
|
||||
fosuser_registration: ${FOSUSER_REGISTRATION:-false}
|
||||
fosuser_confirmation: ${FOSUSER_CONFIRMATION:-true}
|
||||
|
||||
fos_oauth_server_access_token_lifetime: 3600
|
||||
|
||||
41
package.json
41
package.json
@ -7,7 +7,7 @@
|
||||
"doc": "docs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
"node": ">=18"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -41,53 +41,56 @@
|
||||
"url": "https://github.com/wallabag/wallabag/issues"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.22.10",
|
||||
"@babel/eslint-parser": "^7.22.10",
|
||||
"@babel/preset-env": "^7.22.10",
|
||||
"autoprefixer": "^10.4.15",
|
||||
"@babel/core": "^7.23.3",
|
||||
"@babel/eslint-parser": "^7.23.3",
|
||||
"@babel/preset-env": "^7.23.3",
|
||||
"autoprefixer": "^10.4.16",
|
||||
"babel-loader": "^9.1.3",
|
||||
"css-loader": "^6.8.1",
|
||||
"eslint": "^8.47.0",
|
||||
"eslint": "^8.53.0",
|
||||
"eslint-config-airbnb-base": "^15.0.0",
|
||||
"eslint-plugin-import": "^2.28.1",
|
||||
"eslint-plugin-import": "^2.29.0",
|
||||
"eslint-webpack-plugin": "^4.0.1",
|
||||
"file-loader": "^6.2.0",
|
||||
"lato-font": "^3.0.0",
|
||||
"mini-css-extract-plugin": "^2.7.6",
|
||||
"node-sass": "^9.0.0",
|
||||
"postcss": "^8.4.28",
|
||||
"postcss": "^8.4.31",
|
||||
"postcss-loader": "^7.3.3",
|
||||
"postcss-scss": "^4.0.7",
|
||||
"sass": "^1.66.1",
|
||||
"postcss-scss": "^4.0.9",
|
||||
"sass": "^1.69.5",
|
||||
"sass-loader": "^13.3.2",
|
||||
"style-loader": "^3.3.3",
|
||||
"stylelint": "^15.10.3",
|
||||
"stylelint": "^15.11.0",
|
||||
"stylelint-config-standard": "^34.0.0",
|
||||
"stylelint-config-standard-scss": "^10.0.0",
|
||||
"stylelint-scss": "^5.1.0",
|
||||
"stylelint-config-standard-scss": "^11.1.0",
|
||||
"stylelint-scss": "^5.3.1",
|
||||
"stylelint-webpack-plugin": "^4.1.1",
|
||||
"terser-webpack-plugin": "^5.3.9",
|
||||
"url-loader": "^4.1.1",
|
||||
"webpack": "^5.88.2",
|
||||
"webpack": "^5.89.0",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"webpack-dev-server": "^4.15.1",
|
||||
"webpack-manifest-plugin": "^5.0.0",
|
||||
"webpack-merge": "^5.9.0"
|
||||
"webpack-merge": "^5.10.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fontsource/atkinson-hyperlegible": "^5.0.17",
|
||||
"@fontsource/eb-garamond": "^5.0.15",
|
||||
"@fontsource/montserrat": "^5.0.15",
|
||||
"@fontsource/oswald": "^5.0.17",
|
||||
"annotator": "wallabag/annotator#master",
|
||||
"clipboard": "^2.0.11",
|
||||
"hammerjs": "^2.0.8",
|
||||
"highlight.js": "^11.8.0",
|
||||
"highlight.js": "^11.9.0",
|
||||
"icomoon-free-npm": "^0.0.0",
|
||||
"jquery": "^3.7.0",
|
||||
"jquery": "^3.7.1",
|
||||
"jquery.cookie": "^1.4.1",
|
||||
"jr-qrcode": "^1.0.7",
|
||||
"material-design-icons-iconfont": "^6.7.0",
|
||||
"materialize-css": "^0.100.2",
|
||||
"mathjax": "^3.2.2",
|
||||
"mousetrap": "^1.6.0",
|
||||
"ptsans-npm-webfont": "^0.0.4",
|
||||
"open-dyslexic": "^1.0.3",
|
||||
"roboto-fontface": "^0.10.0",
|
||||
"waypoints": "^4.0.1"
|
||||
},
|
||||
|
||||
@ -1,10 +1,25 @@
|
||||
parameters:
|
||||
ignoreErrors:
|
||||
-
|
||||
message: "#^Method Wallabag\\\\AnnotationBundle\\\\Controller\\\\WallabagAnnotationController\\:\\:postAnnotationAction\\(\\) should return Symfony\\\\Component\\\\HttpFoundation\\\\JsonResponse but returns Symfony\\\\Component\\\\Form\\\\FormInterface\\<mixed\\>\\.$#"
|
||||
count: 1
|
||||
path: src/Wallabag/AnnotationBundle/Controller/WallabagAnnotationController.php
|
||||
|
||||
-
|
||||
message: "#^Method Wallabag\\\\AnnotationBundle\\\\Controller\\\\WallabagAnnotationController\\:\\:putAnnotationAction\\(\\) should return Symfony\\\\Component\\\\HttpFoundation\\\\JsonResponse but returns Symfony\\\\Component\\\\Form\\\\FormInterface\\<null\\>\\.$#"
|
||||
count: 1
|
||||
path: src/Wallabag/AnnotationBundle/Controller/WallabagAnnotationController.php
|
||||
|
||||
-
|
||||
message: "#^Call to an undefined method Wallabag\\\\CoreBundle\\\\Entity\\\\RuleInterface\\:\\:getConfig\\(\\)\\.$#"
|
||||
count: 1
|
||||
path: src/Wallabag/CoreBundle/Controller/ConfigController.php
|
||||
|
||||
-
|
||||
message: "#^Method FOS\\\\UserBundle\\\\Model\\\\UserManagerInterface\\:\\:updateUser\\(\\) invoked with 2 parameters, 1 required\\.$#"
|
||||
count: 6
|
||||
path: src/Wallabag/CoreBundle/Controller/ConfigController.php
|
||||
|
||||
-
|
||||
message: "#^Call to an undefined method Lexik\\\\Bundle\\\\FormFilterBundle\\\\Filter\\\\Query\\\\QueryInterface\\:\\:getExpressionBuilder\\(\\)\\.$#"
|
||||
count: 1
|
||||
@ -40,27 +55,27 @@ parameters:
|
||||
count: 2
|
||||
path: src/Wallabag/UserBundle/Mailer/AuthCodeMailer.php
|
||||
|
||||
-
|
||||
message: "#^PHPDoc type Symfony\\\\Component\\\\Mailer\\\\MailerInterface of property Wallabag\\\\UserBundle\\\\Mailer\\\\UserMailer\\:\\:\\$mailer is not covariant with PHPDoc type Swift_Mailer of overridden property FOS\\\\UserBundle\\\\Mailer\\\\TwigSwiftMailer\\:\\:\\$mailer\\.$#"
|
||||
count: 1
|
||||
path: src/Wallabag/UserBundle/Mailer/UserMailer.php
|
||||
|
||||
-
|
||||
message: "#^Call to an undefined method DOMNode\\:\\:getAttribute\\(\\)\\.$#"
|
||||
count: 1
|
||||
path: tests/Wallabag/CoreBundle/Controller/FeedControllerTest.php
|
||||
|
||||
-
|
||||
message: "#^Call to method generate\\(\\) on an unknown class PHPUnit_Framework_MockObject_MockObject\\.$#"
|
||||
count: 2
|
||||
path: tests/Wallabag/CoreBundle/Helper/RedirectTest.php
|
||||
message: "#^Call to an undefined method Wallabag\\\\ImportBundle\\\\Import\\\\ImportInterface\\:\\:setUser\\(\\)\\.$#"
|
||||
count: 1
|
||||
path: src/Wallabag/ImportBundle/Controller/HtmlController.php
|
||||
|
||||
-
|
||||
message: "#^Property Tests\\\\Wallabag\\\\CoreBundle\\\\Helper\\\\RedirectTest\\:\\:\\$routerMock has unknown class PHPUnit_Framework_MockObject_MockObject as its type\\.$#"
|
||||
message: "#^Call to an undefined method Wallabag\\\\ImportBundle\\\\Import\\\\ImportInterface\\:\\:setFilepath\\(\\)\\.$#"
|
||||
count: 1
|
||||
path: tests/Wallabag/CoreBundle/Helper/RedirectTest.php
|
||||
path: src/Wallabag/ImportBundle/Controller/HtmlController.php
|
||||
|
||||
-
|
||||
message: "#^Method Symfony\\\\Contracts\\\\EventDispatcher\\\\EventDispatcherInterface\\:\\:dispatch()#"
|
||||
count: 15
|
||||
count: 16
|
||||
path: src/*
|
||||
|
||||
-
|
||||
message: "#^Method FOS\\\\UserBundle\\\\Model\\\\UserManagerInterface\\:\\:updateUser()#"
|
||||
count: 6
|
||||
path: src/Wallabag/CoreBundle/Controller/ConfigController.php
|
||||
|
||||
@ -2,7 +2,7 @@ includes:
|
||||
- phpstan-baseline.neon
|
||||
|
||||
parameters:
|
||||
level: 2
|
||||
level: 3
|
||||
paths:
|
||||
- src
|
||||
- tests
|
||||
|
||||
@ -10,6 +10,8 @@ ENV=$4
|
||||
rm -rf "${TMP_FOLDER:?}"/"$RELEASE_FOLDER"
|
||||
mkdir "$TMP_FOLDER"/"$RELEASE_FOLDER"
|
||||
git clone https://github.com/wallabag/wallabag.git --single-branch --depth 1 --branch $1 "$TMP_FOLDER"/"$RELEASE_FOLDER"/"$VERSION"
|
||||
cd "$TMP_FOLDER"/"$RELEASE_FOLDER"/"$VERSION" && yarn install --non-interactive
|
||||
cd "$TMP_FOLDER"/"$RELEASE_FOLDER"/"$VERSION" && yarn run --non-interactive build:prod
|
||||
cd "$TMP_FOLDER"/"$RELEASE_FOLDER"/"$VERSION" && SYMFONY_ENV="$ENV" COMPOSER_MEMORY_LIMIT=-1 composer install -n --no-dev
|
||||
cd "$TMP_FOLDER"/"$RELEASE_FOLDER"/"$VERSION" && php bin/console wallabag:install --env="$ENV" -n
|
||||
cd "$TMP_FOLDER"/"$RELEASE_FOLDER"/"$VERSION" && php bin/console assets:install --env="$ENV" --symlink --relative
|
||||
|
||||
@ -123,7 +123,7 @@ class AnnotationRepository extends ServiceEntityRepository
|
||||
|
||||
/**
|
||||
* Remove all annotations for a user id.
|
||||
* Used when a user want to reset all informations.
|
||||
* Used when a user wants to reset all information.
|
||||
*
|
||||
* @param int $userId
|
||||
*/
|
||||
|
||||
@ -4,8 +4,8 @@ namespace Wallabag\ApiBundle\Controller;
|
||||
|
||||
use Nelmio\ApiDocBundle\Annotation\Operation;
|
||||
use OpenApi\Annotations as OA;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Wallabag\AnnotationBundle\Entity\Annotation;
|
||||
use Wallabag\CoreBundle\Entity\Entry;
|
||||
@ -36,7 +36,7 @@ class AnnotationRestController extends WallabagRestController
|
||||
*
|
||||
* @Route("/api/annotations/{entry}.{_format}", methods={"GET"}, name="api_get_annotations", defaults={"_format": "json"})
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @return Response
|
||||
*/
|
||||
public function getAnnotationsAction(Entry $entry)
|
||||
{
|
||||
@ -102,7 +102,7 @@ class AnnotationRestController extends WallabagRestController
|
||||
*
|
||||
* @Route("/api/annotations/{entry}.{_format}", methods={"POST"}, name="api_post_annotation", defaults={"_format": "json"})
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @return Response
|
||||
*/
|
||||
public function postAnnotationAction(Request $request, Entry $entry)
|
||||
{
|
||||
@ -138,7 +138,7 @@ class AnnotationRestController extends WallabagRestController
|
||||
*
|
||||
* @Route("/api/annotations/{annotation}.{_format}", methods={"PUT"}, name="api_put_annotation", defaults={"_format": "json"})
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @return Response
|
||||
*/
|
||||
public function putAnnotationAction(int $annotation, Request $request)
|
||||
{
|
||||
@ -174,7 +174,7 @@ class AnnotationRestController extends WallabagRestController
|
||||
*
|
||||
* @Route("/api/annotations/{annotation}.{_format}", methods={"DELETE"}, name="api_delete_annotation", defaults={"_format": "json"})
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @return Response
|
||||
*/
|
||||
public function deleteAnnotationAction(int $annotation)
|
||||
{
|
||||
|
||||
@ -176,6 +176,17 @@ class EntryRestController extends WallabagRestController
|
||||
* )
|
||||
* ),
|
||||
* @OA\Parameter(
|
||||
* name="notParsed",
|
||||
* in="query",
|
||||
* description="filter by notParsed status. all entries by default",
|
||||
* required=false,
|
||||
* @OA\Schema(
|
||||
* type="integer",
|
||||
* enum={"1", "0"},
|
||||
* default="0"
|
||||
* )
|
||||
* ),
|
||||
* @OA\Parameter(
|
||||
* name="sort",
|
||||
* in="query",
|
||||
* description="sort entries by date.",
|
||||
@ -286,6 +297,7 @@ class EntryRestController extends WallabagRestController
|
||||
$isArchived = (null === $request->query->get('archive')) ? null : (bool) $request->query->get('archive');
|
||||
$isStarred = (null === $request->query->get('starred')) ? null : (bool) $request->query->get('starred');
|
||||
$isPublic = (null === $request->query->get('public')) ? null : (bool) $request->query->get('public');
|
||||
$isNotParsed = (null === $request->query->get('notParsed')) ? null : (bool) $request->query->get('notParsed');
|
||||
$sort = strtolower($request->query->get('sort', 'created'));
|
||||
$order = strtolower($request->query->get('order', 'desc'));
|
||||
$page = (int) $request->query->get('page', 1);
|
||||
@ -307,7 +319,8 @@ class EntryRestController extends WallabagRestController
|
||||
$since,
|
||||
$tags,
|
||||
$detail,
|
||||
$domainName
|
||||
$domainName,
|
||||
$isNotParsed
|
||||
);
|
||||
} catch (\Exception $e) {
|
||||
throw new BadRequestHttpException($e->getMessage());
|
||||
@ -325,6 +338,7 @@ class EntryRestController extends WallabagRestController
|
||||
'archive' => $isArchived,
|
||||
'starred' => $isStarred,
|
||||
'public' => $isPublic,
|
||||
'notParsed' => $isNotParsed,
|
||||
'sort' => $sort,
|
||||
'order' => $order,
|
||||
'page' => $page,
|
||||
@ -397,7 +411,7 @@ class EntryRestController extends WallabagRestController
|
||||
* required=true,
|
||||
* @OA\Schema(
|
||||
* type="string",
|
||||
* enum={"xml", "json", "txt", "csv", "pdf", "epub", "mobi"},
|
||||
* enum={"xml", "json", "txt", "csv", "pdf", "epub"},
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
|
||||
@ -22,11 +22,11 @@ use Wallabag\UserBundle\Form\NewUserType;
|
||||
class UserRestController extends WallabagRestController
|
||||
{
|
||||
/**
|
||||
* Retrieve current logged in user informations.
|
||||
* Retrieve current logged in user information.
|
||||
*
|
||||
* @Operation(
|
||||
* tags={"User"},
|
||||
* summary="Retrieve current logged in user informations.",
|
||||
* summary="Retrieve current logged in user information.",
|
||||
* @OA\Response(
|
||||
* response="200",
|
||||
* description="Returned when successful",
|
||||
|
||||
@ -115,7 +115,7 @@ class InstallCommand extends Command
|
||||
|
||||
$rows[] = [sprintf($label, $this->databaseDriver), $status, $help];
|
||||
|
||||
// testing if connection to the database can be etablished
|
||||
// testing if connection to the database can be established
|
||||
$label = '<comment>Database connection</comment>';
|
||||
$status = '<info>OK!</info>';
|
||||
$help = '';
|
||||
@ -415,7 +415,7 @@ class InstallCommand extends Command
|
||||
|
||||
/**
|
||||
* Check if the schema is already created.
|
||||
* If we found at least oen table, it means the schema exists.
|
||||
* If we found at least one table, it means the schema exists.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
|
||||
@ -7,6 +7,7 @@ use Doctrine\ORM\NoResultException;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
@ -41,13 +42,19 @@ class ReloadEntryCommand extends Command
|
||||
->setDescription('Reload entries')
|
||||
->setHelp('This command reload entries')
|
||||
->addArgument('username', InputArgument::OPTIONAL, 'Reload entries only for the given user')
|
||||
;
|
||||
->addOption(
|
||||
'only-not-parsed',
|
||||
null,
|
||||
InputOption::VALUE_NONE,
|
||||
'Only reload entries which have `is_not_parsed` set to `true`'
|
||||
);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$onlyNotParsed = (bool) $input->getOption('only-not-parsed');
|
||||
$userId = null;
|
||||
if ($username = $input->getArgument('username')) {
|
||||
try {
|
||||
@ -61,7 +68,8 @@ class ReloadEntryCommand extends Command
|
||||
}
|
||||
}
|
||||
|
||||
$entryIds = $this->entryRepository->findAllEntriesIdByUserId($userId);
|
||||
$methodName = $onlyNotParsed ? 'findAllEntriesIdByUserIdAndNotParsed' : 'findAllEntriesIdByUserId';
|
||||
$entryIds = $this->entryRepository->$methodName($userId);
|
||||
|
||||
$nbEntries = \count($entryIds);
|
||||
if (!$nbEntries) {
|
||||
|
||||
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace Wallabag\CoreBundle\Command;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Wallabag\CoreBundle\Repository\EntryRepository;
|
||||
|
||||
class UpdatePicturesPathCommand extends Command
|
||||
{
|
||||
private EntityManagerInterface $entityManager;
|
||||
private EntryRepository $entryRepository;
|
||||
private string $wallabagUrl;
|
||||
|
||||
public function __construct(EntityManagerInterface $entityManager, EntryRepository $entryRepository, $wallabagUrl)
|
||||
{
|
||||
$this->entityManager = $entityManager;
|
||||
$this->entryRepository = $entryRepository;
|
||||
$this->wallabagUrl = $wallabagUrl;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('wallabag:update-pictures-path')
|
||||
->setDescription('Update the path of the pictures for each entry when you changed your wallabag instance URL.')
|
||||
->addArgument(
|
||||
'old-url',
|
||||
InputArgument::REQUIRED,
|
||||
'URL to replace'
|
||||
);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$oldUrl = $input->getArgument('old-url');
|
||||
|
||||
$query = $this->entryRepository->createQueryBuilder('e')->getQuery();
|
||||
$io->text('Retrieve existing entries');
|
||||
$i = 1;
|
||||
foreach ($query->toIterable() as $entry) {
|
||||
$content = $entry->getContent();
|
||||
if (null !== $content) {
|
||||
$entry->setContent(str_replace($oldUrl, $this->wallabagUrl, $content));
|
||||
}
|
||||
|
||||
$previewPicture = $entry->getPreviewPicture();
|
||||
if (null !== $previewPicture) {
|
||||
$entry->setPreviewPicture(str_replace($oldUrl, $this->wallabagUrl, $previewPicture));
|
||||
}
|
||||
|
||||
if (0 === ($i % 20)) {
|
||||
$this->entityManager->flush();
|
||||
}
|
||||
++$i;
|
||||
}
|
||||
$this->entityManager->flush();
|
||||
|
||||
$io->success('Finished updating.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -11,6 +11,7 @@ use JMS\Serializer\SerializerBuilder;
|
||||
use PragmaRX\Recovery\Recovery as BackupCodes;
|
||||
use Scheb\TwoFactorBundle\Security\TwoFactor\Provider\Google\GoogleAuthenticatorInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
@ -25,6 +26,7 @@ use Wallabag\CoreBundle\Entity\Config as ConfigEntity;
|
||||
use Wallabag\CoreBundle\Entity\IgnoreOriginUserRule;
|
||||
use Wallabag\CoreBundle\Entity\RuleInterface;
|
||||
use Wallabag\CoreBundle\Entity\TaggingRule;
|
||||
use Wallabag\CoreBundle\Event\ConfigUpdatedEvent;
|
||||
use Wallabag\CoreBundle\Form\Type\ChangePasswordType;
|
||||
use Wallabag\CoreBundle\Form\Type\ConfigType;
|
||||
use Wallabag\CoreBundle\Form\Type\FeedType;
|
||||
@ -48,15 +50,24 @@ class ConfigController extends AbstractController
|
||||
private TagRepository $tagRepository;
|
||||
private AnnotationRepository $annotationRepository;
|
||||
private ConfigRepository $configRepository;
|
||||
private EventDispatcherInterface $eventDispatcher;
|
||||
|
||||
public function __construct(EntityManagerInterface $entityManager, UserManagerInterface $userManager, EntryRepository $entryRepository, TagRepository $tagRepository, AnnotationRepository $annotationRepository, ConfigRepository $configRepository)
|
||||
{
|
||||
public function __construct(
|
||||
EntityManagerInterface $entityManager,
|
||||
UserManagerInterface $userManager,
|
||||
EntryRepository $entryRepository,
|
||||
TagRepository $tagRepository,
|
||||
AnnotationRepository $annotationRepository,
|
||||
ConfigRepository $configRepository,
|
||||
EventDispatcherInterface $eventDispatcher
|
||||
) {
|
||||
$this->entityManager = $entityManager;
|
||||
$this->userManager = $userManager;
|
||||
$this->entryRepository = $entryRepository;
|
||||
$this->tagRepository = $tagRepository;
|
||||
$this->annotationRepository = $annotationRepository;
|
||||
$this->configRepository = $configRepository;
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -72,6 +83,7 @@ class ConfigController extends AbstractController
|
||||
$configForm->handleRequest($request);
|
||||
|
||||
if ($configForm->isSubmitted() && $configForm->isValid()) {
|
||||
$this->eventDispatcher->dispatch(new ConfigUpdatedEvent($config), ConfigUpdatedEvent::NAME);
|
||||
$this->entityManager->persist($config);
|
||||
$this->entityManager->flush();
|
||||
|
||||
@ -90,14 +102,10 @@ class ConfigController extends AbstractController
|
||||
$pwdForm->handleRequest($request);
|
||||
|
||||
if ($pwdForm->isSubmitted() && $pwdForm->isValid()) {
|
||||
if ($craueConfig->get('demo_mode_enabled') && $craueConfig->get('demo_mode_username') === $user->getUsername()) {
|
||||
$message = 'flashes.config.notice.password_not_updated_demo';
|
||||
} else {
|
||||
$message = 'flashes.config.notice.password_updated';
|
||||
$message = 'flashes.config.notice.password_updated';
|
||||
|
||||
$user->setPlainPassword($pwdForm->get('new_password')->getData());
|
||||
$this->userManager->updateUser($user, true);
|
||||
}
|
||||
$user->setPlainPassword($pwdForm->get('new_password')->getData());
|
||||
$this->userManager->updateUser($user, true);
|
||||
|
||||
$this->addFlash('notice', $message);
|
||||
|
||||
|
||||
@ -518,7 +518,7 @@ class EntryController extends AbstractController
|
||||
);
|
||||
|
||||
// don't redirect user to the deleted entry (check that the referer doesn't end with the same url)
|
||||
$prev = $request->getSession()->get('prevUrl');
|
||||
$prev = $request->getSession()->get('prevUrl', '');
|
||||
$to = (1 !== preg_match('#' . $url . '$#i', $prev) ? $prev : null);
|
||||
|
||||
$redirectUrl = $this->redirectHelper->to($to);
|
||||
@ -615,7 +615,7 @@ class EntryController extends AbstractController
|
||||
*/
|
||||
private function showEntries($type, Request $request, $page)
|
||||
{
|
||||
$searchTerm = (isset($request->get('search_entry')['term']) ? $request->get('search_entry')['term'] : '');
|
||||
$searchTerm = (isset($request->get('search_entry')['term']) ? trim($request->get('search_entry')['term']) : '');
|
||||
$currentRoute = (null !== $request->query->get('currentRoute') ? $request->query->get('currentRoute') : '');
|
||||
$request->getSession()->set('prevUrl', $request->getRequestUri());
|
||||
|
||||
|
||||
@ -21,7 +21,7 @@ class ExportController extends AbstractController
|
||||
* Gets one entry content.
|
||||
*
|
||||
* @Route("/export/{id}.{format}", name="export_entry", requirements={
|
||||
* "format": "epub|mobi|pdf|json|xml|txt|csv",
|
||||
* "format": "epub|pdf|json|xml|txt|csv",
|
||||
* "id": "\d+"
|
||||
* })
|
||||
*
|
||||
@ -55,7 +55,7 @@ class ExportController extends AbstractController
|
||||
* Export all entries for current user.
|
||||
*
|
||||
* @Route("/export/{category}.{format}", name="export_entries", requirements={
|
||||
* "format": "epub|mobi|pdf|json|xml|txt|csv",
|
||||
* "format": "epub|pdf|json|xml|txt|csv",
|
||||
* "category": "all|unread|starred|archive|tag_entries|untagged|search|annotated|same_domain"
|
||||
* })
|
||||
*
|
||||
|
||||
@ -5,6 +5,7 @@ namespace Wallabag\CoreBundle\Controller;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\Form\Form;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
@ -138,7 +139,7 @@ class IgnoreOriginInstanceRuleController extends AbstractController
|
||||
*
|
||||
* @param IgnoreOriginInstanceRule $ignoreOriginInstanceRule The ignore origin instance rule entity
|
||||
*
|
||||
* @return Form The form
|
||||
* @return FormInterface The form
|
||||
*/
|
||||
private function createDeleteForm(IgnoreOriginInstanceRule $ignoreOriginInstanceRule)
|
||||
{
|
||||
|
||||
@ -6,6 +6,7 @@ use Craue\ConfigBundle\Util\Config;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\Form\Form;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
@ -173,7 +174,7 @@ class SiteCredentialController extends AbstractController
|
||||
*
|
||||
* @param SiteCredential $siteCredential The site credential entity
|
||||
*
|
||||
* @return Form The form
|
||||
* @return FormInterface The form
|
||||
*/
|
||||
private function createDeleteForm(SiteCredential $siteCredential)
|
||||
{
|
||||
|
||||
@ -17,6 +17,8 @@ class EntryFixtures extends Fixture implements DependentFixtureInterface
|
||||
*/
|
||||
public function load(ObjectManager $manager): void
|
||||
{
|
||||
$now = new \DateTime();
|
||||
|
||||
$entries = [
|
||||
'entry1' => [
|
||||
'user' => 'admin-user',
|
||||
@ -72,6 +74,7 @@ class EntryFixtures extends Fixture implements DependentFixtureInterface
|
||||
'content' => 'This is my content /o/',
|
||||
'language' => 'fr',
|
||||
'starred' => true,
|
||||
'starred_at' => $now,
|
||||
'preview' => 'http://0.0.0.0/image.jpg',
|
||||
],
|
||||
'entry6' => [
|
||||
@ -84,7 +87,9 @@ class EntryFixtures extends Fixture implements DependentFixtureInterface
|
||||
'content' => 'This is my content /o/',
|
||||
'language' => 'de',
|
||||
'archived' => true,
|
||||
'archived_at' => $now,
|
||||
'tags' => ['bar-tag'],
|
||||
'is_not_parsed' => true,
|
||||
],
|
||||
];
|
||||
|
||||
@ -112,14 +117,26 @@ class EntryFixtures extends Fixture implements DependentFixtureInterface
|
||||
$entry->setStarred($item['starred']);
|
||||
}
|
||||
|
||||
if (isset($item['starred_at'])) {
|
||||
$entry->setStarredAt($item['starred_at']);
|
||||
}
|
||||
|
||||
if (isset($item['archived'])) {
|
||||
$entry->setArchived($item['archived']);
|
||||
}
|
||||
|
||||
if (isset($item['archived_at'])) {
|
||||
$entry->setArchivedAt($item['archived_at']);
|
||||
}
|
||||
|
||||
if (isset($item['preview'])) {
|
||||
$entry->setPreviewPicture($item['preview']);
|
||||
}
|
||||
|
||||
if (isset($item['is_not_parsed'])) {
|
||||
$entry->setNotParsed($item['is_not_parsed']);
|
||||
}
|
||||
|
||||
$manager->persist($entry);
|
||||
$this->addReference($reference, $entry);
|
||||
}
|
||||
|
||||
@ -72,6 +72,9 @@ class Configuration implements ConfigurationInterface
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('fonts')
|
||||
->prototype('scalar')->end()
|
||||
->end()
|
||||
->end()
|
||||
;
|
||||
|
||||
|
||||
@ -29,6 +29,7 @@ class WallabagCoreExtension extends Extension
|
||||
$container->setParameter('wallabag_core.default_internal_settings', $config['default_internal_settings']);
|
||||
$container->setParameter('wallabag_core.site_credentials.encryption_key_path', $config['encryption_key_path']);
|
||||
$container->setParameter('wallabag_core.default_ignore_origin_instance_rules', $config['default_ignore_origin_instance_rules']);
|
||||
$container->setParameter('wallabag_core.fonts', $config['fonts']);
|
||||
}
|
||||
|
||||
public function getAlias()
|
||||
|
||||
@ -126,18 +126,65 @@ class Config
|
||||
*/
|
||||
private $displayThumbnails;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* @ORM\Column(name="font", type="text", nullable=true)
|
||||
*
|
||||
* @Groups({"config_api"})
|
||||
*/
|
||||
private $font;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*
|
||||
* @ORM\Column(name="fontsize", type="float", nullable=true)
|
||||
*
|
||||
* @Groups({"config_api"})
|
||||
*/
|
||||
private $fontsize;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*
|
||||
* @ORM\Column(name="line_height", type="float", nullable=true)
|
||||
*
|
||||
* @Groups({"config_api"})
|
||||
*/
|
||||
private $lineHeight;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*
|
||||
* @ORM\Column(name="max_width", type="float", nullable=true)
|
||||
*
|
||||
* @Groups({"config_api"})
|
||||
*/
|
||||
private $maxWidth;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* @ORM\Column(name="custom_css", type="text", nullable=true)
|
||||
*/
|
||||
private $customCSS;
|
||||
|
||||
/**
|
||||
* @ORM\OneToOne(targetEntity="Wallabag\UserBundle\Entity\User", inversedBy="config")
|
||||
*/
|
||||
private $user;
|
||||
|
||||
/**
|
||||
* @var ArrayCollection<TaggingRule>
|
||||
*
|
||||
* @ORM\OneToMany(targetEntity="Wallabag\CoreBundle\Entity\TaggingRule", mappedBy="config", cascade={"remove"})
|
||||
* @ORM\OrderBy({"id" = "ASC"})
|
||||
*/
|
||||
private $taggingRules;
|
||||
|
||||
/**
|
||||
@var ArrayCollection<IgnoreOriginUserRule>
|
||||
|
||||
* @ORM\OneToMany(targetEntity="Wallabag\CoreBundle\Entity\IgnoreOriginUserRule", mappedBy="config", cascade={"remove"})
|
||||
* @ORM\OrderBy({"id" = "ASC"})
|
||||
*/
|
||||
@ -371,12 +418,9 @@ class Config
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function getDisplayThumbnails(): ?bool
|
||||
public function getDisplayThumbnails(): bool
|
||||
{
|
||||
return $this->displayThumbnails;
|
||||
return (bool) $this->displayThumbnails;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -384,7 +428,94 @@ class Config
|
||||
*/
|
||||
public function setDisplayThumbnails(bool $displayThumbnails)
|
||||
{
|
||||
$this->displayThumbnails = $displayThumbnails;
|
||||
$this->displayThumbnails = $displayThumbnails ? 1 : 0;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getFont(): ?string
|
||||
{
|
||||
return $this->font;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function setFont(string $font): self
|
||||
{
|
||||
$this->font = $font;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return float
|
||||
*/
|
||||
public function getFontsize(): ?float
|
||||
{
|
||||
return $this->fontsize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function setFontsize(float $fontsize): self
|
||||
{
|
||||
$this->fontsize = $fontsize;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return float
|
||||
*/
|
||||
public function getLineHeight(): ?float
|
||||
{
|
||||
return $this->lineHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function setLineHeight(float $lineHeight): self
|
||||
{
|
||||
$this->lineHeight = $lineHeight;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return float
|
||||
*/
|
||||
public function getMaxWidth(): ?float
|
||||
{
|
||||
return $this->maxWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function setMaxWidth(float $maxWidth): self
|
||||
{
|
||||
$this->maxWidth = $maxWidth;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCustomCSS(): ?string
|
||||
{
|
||||
return $this->customCSS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function setCustomCSS(?string $customCSS): self
|
||||
{
|
||||
$this->customCSS = $customCSS;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -55,7 +55,7 @@ class Entry
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @var string|null
|
||||
*
|
||||
* @ORM\Column(name="uid", type="string", length=23, nullable=true)
|
||||
*
|
||||
@ -132,7 +132,7 @@ class Entry
|
||||
private $isArchived = false;
|
||||
|
||||
/**
|
||||
* @var \DateTime
|
||||
* @var \DateTimeInterface
|
||||
*
|
||||
* @ORM\Column(name="archived_at", type="datetime", nullable=true)
|
||||
*
|
||||
@ -161,7 +161,7 @@ class Entry
|
||||
private $content;
|
||||
|
||||
/**
|
||||
* @var \DateTime
|
||||
* @var \DateTimeInterface
|
||||
*
|
||||
* @ORM\Column(name="created_at", type="datetime")
|
||||
*
|
||||
@ -170,7 +170,7 @@ class Entry
|
||||
private $createdAt;
|
||||
|
||||
/**
|
||||
* @var \DateTime
|
||||
* @var \DateTimeInterface
|
||||
*
|
||||
* @ORM\Column(name="updated_at", type="datetime")
|
||||
*
|
||||
@ -179,7 +179,7 @@ class Entry
|
||||
private $updatedAt;
|
||||
|
||||
/**
|
||||
* @var \DateTime
|
||||
* @var \DateTimeInterface
|
||||
*
|
||||
* @ORM\Column(name="published_at", type="datetime", nullable=true)
|
||||
*
|
||||
@ -197,7 +197,7 @@ class Entry
|
||||
private $publishedBy;
|
||||
|
||||
/**
|
||||
* @var \DateTime
|
||||
* @var \DateTimeInterface
|
||||
*
|
||||
* @ORM\Column(name="starred_at", type="datetime", nullable=true)
|
||||
*
|
||||
@ -276,6 +276,17 @@ class Entry
|
||||
*/
|
||||
private $headers;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*
|
||||
* @Exclude
|
||||
*
|
||||
* @ORM\Column(name="is_not_parsed", type="boolean")
|
||||
*
|
||||
* @Groups({"entries_for_user", "export_all"})
|
||||
*/
|
||||
private $isNotParsed = false;
|
||||
|
||||
/**
|
||||
* @Exclude
|
||||
*
|
||||
@ -400,7 +411,7 @@ class Entry
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \DateTime|null
|
||||
* @return \DateTimeInterface|null
|
||||
*/
|
||||
public function getArchivedAt()
|
||||
{
|
||||
@ -408,7 +419,7 @@ class Entry
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DateTime|null $archivedAt
|
||||
* @param \DateTimeInterface|null $archivedAt
|
||||
*
|
||||
* @return Entry
|
||||
*/
|
||||
@ -482,7 +493,7 @@ class Entry
|
||||
|
||||
public function toggleStar()
|
||||
{
|
||||
$this->isStarred = $this->isStarred() ^ 1;
|
||||
$this->isStarred = !$this->isStarred();
|
||||
|
||||
return $this;
|
||||
}
|
||||
@ -560,7 +571,7 @@ class Entry
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \DateTime
|
||||
* @return \DateTimeInterface
|
||||
*/
|
||||
public function getCreatedAt()
|
||||
{
|
||||
@ -568,7 +579,7 @@ class Entry
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \DateTime
|
||||
* @return \DateTimeInterface
|
||||
*/
|
||||
public function getUpdatedAt()
|
||||
{
|
||||
@ -576,7 +587,7 @@ class Entry
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \DateTime|null
|
||||
* @return \DateTimeInterface|null
|
||||
*/
|
||||
public function getStarredAt()
|
||||
{
|
||||
@ -584,7 +595,7 @@ class Entry
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DateTime|null $starredAt
|
||||
* @param \DateTimeInterface|null $starredAt
|
||||
*
|
||||
* @return Entry
|
||||
*/
|
||||
@ -881,7 +892,7 @@ class Entry
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \DateTime
|
||||
* @return \DateTimeInterface
|
||||
*/
|
||||
public function getPublishedAt()
|
||||
{
|
||||
@ -891,7 +902,7 @@ class Entry
|
||||
/**
|
||||
* @return Entry
|
||||
*/
|
||||
public function setPublishedAt(\DateTime $publishedAt)
|
||||
public function setPublishedAt(\DateTimeInterface $publishedAt)
|
||||
{
|
||||
$this->publishedAt = $publishedAt;
|
||||
|
||||
@ -1006,4 +1017,28 @@ class Entry
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set isNotParsed.
|
||||
*
|
||||
* @param bool $isNotParsed
|
||||
*
|
||||
* @return Entry
|
||||
*/
|
||||
public function setNotParsed($isNotParsed)
|
||||
{
|
||||
$this->isNotParsed = $isNotParsed;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get isNotParsed.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isNotParsed()
|
||||
{
|
||||
return $this->isNotParsed;
|
||||
}
|
||||
}
|
||||
|
||||
26
src/Wallabag/CoreBundle/Event/ConfigUpdatedEvent.php
Normal file
26
src/Wallabag/CoreBundle/Event/ConfigUpdatedEvent.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Wallabag\CoreBundle\Event;
|
||||
|
||||
use Symfony\Contracts\EventDispatcher\Event;
|
||||
use Wallabag\CoreBundle\Entity\Config;
|
||||
|
||||
/**
|
||||
* This event is fired as soon as user configuration is updated.
|
||||
*/
|
||||
class ConfigUpdatedEvent extends Event
|
||||
{
|
||||
public const NAME = 'config.updated';
|
||||
|
||||
protected $config;
|
||||
|
||||
public function __construct(Config $entry)
|
||||
{
|
||||
$this->config = $entry;
|
||||
}
|
||||
|
||||
public function getConfig(): Config
|
||||
{
|
||||
return $this->config;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace Wallabag\CoreBundle\Event\Subscriber;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use ScssPhp\ScssPhp\Compiler;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Wallabag\CoreBundle\Event\ConfigUpdatedEvent;
|
||||
|
||||
class GenerateCustomCSSSubscriber implements EventSubscriberInterface
|
||||
{
|
||||
private $em;
|
||||
private $compiler;
|
||||
|
||||
public function __construct(EntityManagerInterface $em, Compiler $compiler)
|
||||
{
|
||||
$this->em = $em;
|
||||
$this->compiler = $compiler;
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return [
|
||||
ConfigUpdatedEvent::NAME => 'onConfigUpdated',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate custom CSS.
|
||||
*/
|
||||
public function onConfigUpdated(ConfigUpdatedEvent $event)
|
||||
{
|
||||
$config = $event->getConfig();
|
||||
|
||||
$css = $this->compiler->compileString(
|
||||
'h1 { font-family: "' . $config->getFont() . '";}
|
||||
#article {
|
||||
max-width: ' . $config->getMaxWidth() . 'em;
|
||||
font-family: "' . $config->getFont() . '";
|
||||
}
|
||||
#article article {
|
||||
font-size: ' . $config->getFontsize() . 'em;
|
||||
line-height: ' . $config->getLineHeight() . 'em;
|
||||
}
|
||||
;
|
||||
')->getCss();
|
||||
|
||||
$config->setCustomCSS($css);
|
||||
|
||||
$this->em->persist($config);
|
||||
$this->em->flush();
|
||||
}
|
||||
}
|
||||
@ -9,7 +9,7 @@ use Doctrine\ORM\Mapping\ClassMetadataInfo;
|
||||
/**
|
||||
* Puts a prefix to each table.
|
||||
* This way were used instead of using the built-in strategy from Doctrine, using `naming_strategy`
|
||||
* Because it conflicts with the DefaultQuoteStrategy (that espace table name, like user for Postgres)
|
||||
* Because it conflicts with the DefaultQuoteStrategy (that escape table name, like user for Postgres)
|
||||
* see #1498 for more detail.
|
||||
*
|
||||
* Solution from :
|
||||
|
||||
@ -49,7 +49,7 @@ class StringToListTransformer implements DataTransformerInterface
|
||||
public function reverseTransform($string)
|
||||
{
|
||||
if (null === $string) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
return array_values(array_filter(array_map('trim', explode($this->separator, $string))));
|
||||
|
||||
@ -6,6 +6,7 @@ use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\RangeType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
@ -14,18 +15,53 @@ use Wallabag\CoreBundle\Entity\Config;
|
||||
class ConfigType extends AbstractType
|
||||
{
|
||||
private $languages = [];
|
||||
private $fonts = [];
|
||||
|
||||
/**
|
||||
* @param array $languages Languages come from configuration, array just code language as key and label as value
|
||||
* @param array $fonts Fonts come from configuration, array just font name as key / value
|
||||
*/
|
||||
public function __construct($languages)
|
||||
public function __construct($languages, $fonts)
|
||||
{
|
||||
$this->languages = $languages;
|
||||
$this->fonts = $fonts;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder
|
||||
->add('font', ChoiceType::class, [
|
||||
'choices' => $this->initFonts(),
|
||||
'label' => 'config.form_settings.font_label',
|
||||
'property_path' => 'font',
|
||||
])
|
||||
->add('fontsize', RangeType::class, [
|
||||
'attr' => [
|
||||
'min' => 0.6,
|
||||
'max' => 2,
|
||||
'step' => 0.1,
|
||||
],
|
||||
'label' => 'config.form_settings.fontsize_label',
|
||||
'property_path' => 'fontsize',
|
||||
])
|
||||
->add('lineHeight', RangeType::class, [
|
||||
'attr' => [
|
||||
'min' => 0.6,
|
||||
'max' => 2,
|
||||
'step' => 0.1,
|
||||
],
|
||||
'label' => 'config.form_settings.lineheight_label',
|
||||
'property_path' => 'lineHeight',
|
||||
])
|
||||
->add('maxWidth', RangeType::class, [
|
||||
'attr' => [
|
||||
'min' => 20,
|
||||
'max' => 60,
|
||||
'step' => 5,
|
||||
],
|
||||
'label' => 'config.form_settings.maxwidth_label',
|
||||
'property_path' => 'maxWidth',
|
||||
])
|
||||
->add('items_per_page', IntegerType::class, [
|
||||
'label' => 'config.form_settings.items_per_page_label',
|
||||
'property_path' => 'itemsPerPage',
|
||||
@ -72,4 +108,20 @@ class ConfigType extends AbstractType
|
||||
{
|
||||
return 'config';
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an array with font name as key / value.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function initFonts()
|
||||
{
|
||||
$fonts = [];
|
||||
|
||||
foreach ($this->fonts as $font) {
|
||||
$fonts[$font] = $font;
|
||||
}
|
||||
|
||||
return $fonts;
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,7 +95,7 @@ class EntryFilterType extends AbstractType
|
||||
->add('domainName', TextFilterType::class, [
|
||||
'apply_filter' => function (QueryInterface $filterQuery, $field, $values) {
|
||||
$value = $values['value'];
|
||||
if (\strlen($value) <= 2 || empty($value)) {
|
||||
if (empty($value) || \strlen($value) <= 2) {
|
||||
return false;
|
||||
}
|
||||
$expression = $filterQuery->getExpr()->like($field, $filterQuery->getExpr()->lower($filterQuery->getExpr()->literal('%' . $value . '%')));
|
||||
@ -160,6 +160,10 @@ class EntryFilterType extends AbstractType
|
||||
$qb->innerJoin('e.annotations', 'a');
|
||||
},
|
||||
])
|
||||
->add('isNotParsed', CheckboxFilterType::class, [
|
||||
'label' => 'entry.filters.parsed_label',
|
||||
'data' => $options['filter_parsed'],
|
||||
])
|
||||
->add('previewPicture', CheckboxFilterType::class, [
|
||||
'apply_filter' => function (QueryInterface $filterQuery, $field, $values) {
|
||||
if (false === $values['value']) {
|
||||
@ -207,6 +211,7 @@ class EntryFilterType extends AbstractType
|
||||
'filter_starred' => false,
|
||||
'filter_unread' => false,
|
||||
'filter_annotated' => false,
|
||||
'filter_parsed' => false,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -260,6 +260,7 @@ class ContentProxy
|
||||
|
||||
if (empty($content['html'])) {
|
||||
$content['html'] = $this->fetchingErrorMessage;
|
||||
$entry->setNotParsed(true);
|
||||
|
||||
if (!empty($content['description'])) {
|
||||
$content['html'] .= '<p><i>But we found a short description: </i></p>';
|
||||
|
||||
@ -247,55 +247,6 @@ class EntriesExport
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use PHPMobi to dump a .mobi file.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
private function produceMobi()
|
||||
{
|
||||
$mobi = new \MOBI();
|
||||
$content = new \MOBIFile();
|
||||
|
||||
/*
|
||||
* Book metadata
|
||||
*/
|
||||
$content->set('title', $this->title);
|
||||
$content->set('author', $this->author);
|
||||
$content->set('subject', $this->title);
|
||||
|
||||
/*
|
||||
* Front page
|
||||
*/
|
||||
$content->appendParagraph($this->getExportInformation('PHPMobi'));
|
||||
if (file_exists($this->logoPath)) {
|
||||
$content->appendImage(imagecreatefrompng($this->logoPath));
|
||||
}
|
||||
$content->appendPageBreak();
|
||||
|
||||
/*
|
||||
* Adding actual entries
|
||||
*/
|
||||
foreach ($this->entries as $entry) {
|
||||
$content->appendChapterTitle($entry->getTitle());
|
||||
$content->appendParagraph($entry->getContent());
|
||||
$content->appendPageBreak();
|
||||
}
|
||||
$mobi->setContentProvider($content);
|
||||
|
||||
return Response::create(
|
||||
$mobi->toString(),
|
||||
200,
|
||||
[
|
||||
'Accept-Ranges' => 'bytes',
|
||||
'Content-Description' => 'File Transfer',
|
||||
'Content-type' => 'application/x-mobipocket-ebook',
|
||||
'Content-Disposition' => 'attachment; filename="' . $this->getSanitizedFilename() . '.mobi"',
|
||||
'Content-Transfer-Encoding' => 'binary',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use TCPDF to dump a .pdf file.
|
||||
*
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
namespace Wallabag\CoreBundle\Helper;
|
||||
|
||||
use Pagerfanta\Adapter\AdapterInterface;
|
||||
use Pagerfanta\Adapter\NullAdapter;
|
||||
use Pagerfanta\Pagerfanta;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||
use Wallabag\UserBundle\Entity\User;
|
||||
@ -19,7 +20,7 @@ class PreparePagerForEntries
|
||||
/**
|
||||
* @param User $user If user isn't logged in, we can force it (like for feed)
|
||||
*
|
||||
* @return Pagerfanta|null
|
||||
* @return Pagerfanta
|
||||
*/
|
||||
public function prepare(AdapterInterface $adapter, User $user = null)
|
||||
{
|
||||
@ -28,7 +29,7 @@ class PreparePagerForEntries
|
||||
}
|
||||
|
||||
if (!$user instanceof User) {
|
||||
return;
|
||||
return new Pagerfanta(new NullAdapter());
|
||||
}
|
||||
|
||||
$entries = new Pagerfanta($adapter);
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
namespace Wallabag\CoreBundle\Helper;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use RulerZ\RulerZ;
|
||||
use Wallabag\CoreBundle\Entity\Entry;
|
||||
@ -120,7 +121,7 @@ class RuleBasedTagger
|
||||
/**
|
||||
* Retrieves the tagging rules for a given user.
|
||||
*
|
||||
* @return array<TaggingRule>
|
||||
* @return ArrayCollection<TaggingRule>
|
||||
*/
|
||||
private function getRulesForUser(User $user)
|
||||
{
|
||||
|
||||
@ -192,8 +192,10 @@ class EntryRepository extends ServiceEntityRepository
|
||||
|
||||
// We lower() all parts here because PostgreSQL 'LIKE' verb is case-sensitive
|
||||
$qb
|
||||
->andWhere('lower(e.content) LIKE lower(:term) OR lower(e.title) LIKE lower(:term) OR lower(e.url) LIKE lower(:term)')->setParameter('term', '%' . $term . '%')
|
||||
->andWhere('lower(e.content) LIKE lower(:term) OR lower(e.title) LIKE lower(:term) OR lower(e.url) LIKE lower(:term) OR lower(a.text) LIKE lower(:term)')
|
||||
->setParameter('term', '%' . $term . '%')
|
||||
->leftJoin('e.tags', 't')
|
||||
->leftJoin('e.annotations', 'a')
|
||||
->groupBy('e.id');
|
||||
|
||||
return $qb;
|
||||
@ -281,14 +283,15 @@ class EntryRepository extends ServiceEntityRepository
|
||||
* @param string $order
|
||||
* @param int $since
|
||||
* @param string $tags
|
||||
* @param string $detail 'metadata' or 'full'. Include content field if 'full'
|
||||
* @param string $detail 'metadata' or 'full'. Include content field if 'full'
|
||||
* @param string $domainName
|
||||
* @param bool $isNotParsed
|
||||
*
|
||||
* @todo Breaking change: replace default detail=full by detail=metadata in a future version
|
||||
*
|
||||
* @return Pagerfanta
|
||||
*/
|
||||
public function findEntries($userId, $isArchived = null, $isStarred = null, $isPublic = null, $sort = 'created', $order = 'asc', $since = 0, $tags = '', $detail = 'full', $domainName = '')
|
||||
public function findEntries($userId, $isArchived = null, $isStarred = null, $isPublic = null, $sort = 'created', $order = 'asc', $since = 0, $tags = '', $detail = 'full', $domainName = '', $isNotParsed = null)
|
||||
{
|
||||
if (!\in_array(strtolower($detail), ['full', 'metadata'], true)) {
|
||||
throw new \Exception('Detail "' . $detail . '" parameter is wrong, allowed: full or metadata');
|
||||
@ -318,6 +321,10 @@ class EntryRepository extends ServiceEntityRepository
|
||||
$qb->andWhere('e.uid IS ' . (true === $isPublic ? 'NOT' : '') . ' NULL');
|
||||
}
|
||||
|
||||
if (null !== $isNotParsed) {
|
||||
$qb->andWhere('e.isNotParsed = :isNotParsed')->setParameter('isNotParsed', (bool) $isNotParsed);
|
||||
}
|
||||
|
||||
if ($since > 0) {
|
||||
$qb->andWhere('e.updatedAt > :since')->setParameter('since', new \DateTime(date('Y-m-d H:i:s', $since)));
|
||||
}
|
||||
@ -503,7 +510,7 @@ class EntryRepository extends ServiceEntityRepository
|
||||
/**
|
||||
* Find all entries which have an empty value for hash.
|
||||
*
|
||||
* @return Entry|false
|
||||
* @return Entry[]
|
||||
*/
|
||||
public function findByEmptyHashedUrlAndUserId(int $userId)
|
||||
{
|
||||
@ -587,7 +594,7 @@ class EntryRepository extends ServiceEntityRepository
|
||||
|
||||
/**
|
||||
* Remove all entries for a user id.
|
||||
* Used when a user want to reset all informations.
|
||||
* Used when a user wants to reset all information.
|
||||
*
|
||||
* @param int $userId
|
||||
*/
|
||||
@ -637,6 +644,24 @@ class EntryRepository extends ServiceEntityRepository
|
||||
return $qb->getQuery()->getArrayResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $userId
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function findAllEntriesIdByUserIdAndNotParsed($userId = null)
|
||||
{
|
||||
$qb = $this->createQueryBuilder('e')
|
||||
->select('e.id')
|
||||
->where('e.isNotParsed = true');
|
||||
|
||||
if (null !== $userId) {
|
||||
$qb->where('e.user = :userid')->setParameter(':userid', $userId);
|
||||
}
|
||||
|
||||
return $qb->getQuery()->getArrayResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $userId
|
||||
*
|
||||
|
||||
@ -42,7 +42,7 @@ class SiteCredentialRepository extends ServiceEntityRepository
|
||||
->getOneOrNullResult();
|
||||
|
||||
if (null === $res) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
// decrypt user & password before returning them
|
||||
|
||||
@ -25,18 +25,18 @@
|
||||
{{ form_start(form.config) }}
|
||||
{{ form_errors(form.config) }}
|
||||
|
||||
<div class="row">
|
||||
<div class="input-field col s11">
|
||||
{{ form_errors(form.config.items_per_page) }}
|
||||
{{ form_widget(form.config.items_per_page) }}
|
||||
{{ form_label(form.config.items_per_page) }}
|
||||
</div>
|
||||
<div class="input-field col s1">
|
||||
<a href="#" class="tooltipped" data-position="left" data-delay="50" data-tooltip="{{ 'config.form_settings.help_items_per_page'|trans }}">
|
||||
<i class="material-icons">live_help</i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="input-field col s11">
|
||||
{{ form_errors(form.config.items_per_page) }}
|
||||
{{ form_widget(form.config.items_per_page) }}
|
||||
{{ form_label(form.config.items_per_page) }}
|
||||
</div>
|
||||
<div class="input-field col s1">
|
||||
<a href="#" class="tooltipped" data-position="left" data-delay="50" data-tooltip="{{ 'config.form_settings.help_items_per_page'|trans }}">
|
||||
<i class="material-icons">live_help</i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="input-field col s11 settings-checkbox-col">
|
||||
@ -117,6 +117,66 @@
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<h5>{{ 'config.tab_menu.article_display'|trans }}</h5>
|
||||
|
||||
<div class="row">
|
||||
<div class="input-field col s5">
|
||||
{{ form_errors(form.config.font) }}
|
||||
{{ form_widget(form.config.font) }}
|
||||
{{ form_label(form.config.font) }}
|
||||
</div>
|
||||
<div class="input-field col s1">
|
||||
<a href="#" class="tooltipped" data-position="left" data-delay="50" data-tooltip="{{ 'config.form_settings.help_font'|trans }}">
|
||||
<i class="material-icons">live_help</i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="input-field col s5">
|
||||
{{ form_errors(form.config.lineHeight) }}
|
||||
{{ form_widget(form.config.lineHeight) }}
|
||||
{{ form_label(form.config.lineHeight, null, {'label_attr': {'class': 'settings-range-label'}}) }}
|
||||
</div>
|
||||
<div class="input-field col s1">
|
||||
<a href="#" class="tooltipped" data-position="left" data-delay="50" data-tooltip="{{ 'config.form_settings.help_lineheight'|trans }}">
|
||||
<i class="material-icons">live_help</i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="input-field col s5">
|
||||
{{ form_errors(form.config.fontsize) }}
|
||||
{{ form_widget(form.config.fontsize) }}
|
||||
{{ form_label(form.config.fontsize, null, {'label_attr': {'class': 'settings-range-label'}}) }}
|
||||
</div>
|
||||
<div class="input-field col s1">
|
||||
<a href="#" class="tooltipped" data-position="left" data-delay="50" data-tooltip="{{ 'config.form_settings.help_fontsize'|trans }}">
|
||||
<i class="material-icons">live_help</i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="input-field col s5">
|
||||
{{ form_errors(form.config.maxWidth) }}
|
||||
{{ form_widget(form.config.maxWidth) }}
|
||||
{{ form_label(form.config.maxWidth, null, {'label_attr': {'class': 'settings-range-label'}}) }}
|
||||
</div>
|
||||
<div class="input-field col s1">
|
||||
<a href="#" class="tooltipped" data-position="left" data-delay="50" data-tooltip="{{ 'config.form_settings.help_maxwidth'|trans }}">
|
||||
<i class="material-icons">live_help</i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="input-field col s12">
|
||||
<div id="preview-article">
|
||||
<article id="preview-content">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ form_widget(form.config.save, {'attr': {'class': 'btn waves-effect waves-light'}}) }}
|
||||
{{ form_rest(form.config) }}
|
||||
</form>
|
||||
|
||||
@ -99,7 +99,6 @@
|
||||
<h4 class="center">{{ 'entry.list.export_title'|trans }}</h4>
|
||||
<ul>
|
||||
{% if craue_setting('export_epub') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', {'category': current_route, 'format': 'epub', 'tag': current_tag, 'search_entry[term]': export_search_term, 'currentRoute': previous_route, 'entry': entry}) }}">EPUB</a></li>{% endif %}
|
||||
{% if craue_setting('export_mobi') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', {'category': current_route, 'format': 'mobi', 'tag': current_tag, 'search_entry[term]': export_search_term, 'currentRoute': previous_route, 'entry': entry}) }}">MOBI (deprecated)</a></li>{% endif %}
|
||||
{% if craue_setting('export_pdf') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', {'category': current_route, 'format': 'pdf', 'tag': current_tag, 'search_entry[term]': export_search_term, 'currentRoute': previous_route, 'entry': entry}) }}">PDF</a></li>{% endif %}
|
||||
{% if craue_setting('export_json') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', {'category': current_route, 'format': 'json', 'tag': current_tag, 'search_entry[term]': export_search_term, 'currentRoute': previous_route, 'entry': entry}) }}">JSON</a></li>{% endif %}
|
||||
{% 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 %}
|
||||
@ -136,7 +135,7 @@
|
||||
{{ form_label(form.isStarred) }}
|
||||
</div>
|
||||
|
||||
<div class="input-field col s6 with-checkbox">
|
||||
<div class="input-field col s12 with-checkbox">
|
||||
{{ form_widget(form.isUnread) }}
|
||||
{{ form_label(form.isUnread) }}
|
||||
</div>
|
||||
@ -146,6 +145,11 @@
|
||||
{{ form_label(form.isAnnotated) }}
|
||||
</div>
|
||||
|
||||
<div class="input-field col s12 with-checkbox">
|
||||
{{ form_widget(form.isNotParsed) }}
|
||||
{{ form_label(form.isNotParsed) }}
|
||||
</div>
|
||||
|
||||
<div class="col s12">
|
||||
<label>{{ 'entry.filters.preview_picture_help'|trans }}</label>
|
||||
</div>
|
||||
|
||||
@ -209,7 +209,6 @@
|
||||
<div class="collapsible-body">
|
||||
<ul>
|
||||
{% if craue_setting('export_epub') %}<li><a href="{{ path('export_entry', {'id': entry.id, 'format': 'epub'}) }}" title="Generate ePub file">EPUB</a></li>{% endif %}
|
||||
{% if craue_setting('export_mobi') %}<li><a href="{{ path('export_entry', {'id': entry.id, 'format': 'mobi'}) }}" title="Generate Mobi file">MOBI (deprecated)</a></li>{% endif %}
|
||||
{% if craue_setting('export_pdf') %}<li><a href="{{ path('export_entry', {'id': entry.id, 'format': 'pdf'}) }}" title="Generate PDF file">PDF</a></li>{% endif %}
|
||||
{% if craue_setting('export_csv') %}<li><a href="{{ path('export_entry', {'id': entry.id, 'format': 'csv'}) }}" title="Generate CSV file">CSV</a></li>{% endif %}
|
||||
{% if craue_setting('export_json') %}<li><a href="{{ path('export_entry', {'id': entry.id, 'format': 'json'}) }}" title="Generate JSON file">JSON</a></li>{% endif %}
|
||||
@ -320,4 +319,9 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block footer %}
|
||||
|
||||
<style>
|
||||
{{ app.user.config.customCSS|raw }}
|
||||
</style>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@ -140,7 +140,6 @@
|
||||
<tr><td>tecnickcom/tcpdf</td><td>LGPLv3</td></tr>
|
||||
<tr><td>twig/extensions</td><td>MIT</td></tr>
|
||||
<tr><td>twig/twig</td><td>BSD-3-Clause</td></tr>
|
||||
<tr><td>wallabag/php-mobi</td><td>Apache-2.0</td></tr>
|
||||
<tr><td>willdurand/hateoas</td><td>MIT</td></tr>
|
||||
<tr><td>willdurand/hateoas-bundle</td><td>MIT</td></tr>
|
||||
<tr><td>willdurand/jsonp-callback-validator</td><td>MIT</td></tr>
|
||||
|
||||
@ -58,11 +58,19 @@ class WallabagExtension extends AbstractExtension implements GlobalsInterface
|
||||
|
||||
public function removeWww($url)
|
||||
{
|
||||
if (!\is_string($url)) {
|
||||
return $url;
|
||||
}
|
||||
|
||||
return preg_replace('/^www\./i', '', $url);
|
||||
}
|
||||
|
||||
public function removeScheme($url)
|
||||
{
|
||||
if (!\is_string($url)) {
|
||||
return $url;
|
||||
}
|
||||
|
||||
return preg_replace('#^https?://#i', '', $url);
|
||||
}
|
||||
|
||||
@ -142,7 +150,7 @@ class WallabagExtension extends AbstractExtension implements GlobalsInterface
|
||||
$user = $this->tokenStorage->getToken() ? $this->tokenStorage->getToken()->getUser() : null;
|
||||
|
||||
if (!$user instanceof User) {
|
||||
return 0;
|
||||
return '';
|
||||
}
|
||||
|
||||
$query = $this->entryRepository->getCountBuilderForArchiveByUser($user->getId())
|
||||
|
||||
@ -13,10 +13,13 @@ use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInt
|
||||
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
|
||||
use Wallabag\ImportBundle\Import\ChromeImport;
|
||||
use Wallabag\ImportBundle\Import\DeliciousImport;
|
||||
use Wallabag\ImportBundle\Import\ElcuratorImport;
|
||||
use Wallabag\ImportBundle\Import\FirefoxImport;
|
||||
use Wallabag\ImportBundle\Import\InstapaperImport;
|
||||
use Wallabag\ImportBundle\Import\PinboardImport;
|
||||
use Wallabag\ImportBundle\Import\PocketHtmlImport;
|
||||
use Wallabag\ImportBundle\Import\ReadabilityImport;
|
||||
use Wallabag\ImportBundle\Import\ShaarliImport;
|
||||
use Wallabag\ImportBundle\Import\WallabagV1Import;
|
||||
use Wallabag\ImportBundle\Import\WallabagV2Import;
|
||||
use Wallabag\UserBundle\Entity\User;
|
||||
@ -35,9 +38,26 @@ class ImportCommand extends Command
|
||||
private PinboardImport $pinboardImport;
|
||||
private DeliciousImport $deliciousImport;
|
||||
private WallabagV1Import $wallabagV1Import;
|
||||
private ElcuratorImport $elcuratorImport;
|
||||
private ShaarliImport $shaarliImport;
|
||||
private PocketHtmlImport $pocketHtmlImport;
|
||||
|
||||
public function __construct(EntityManagerInterface $entityManager, TokenStorageInterface $tokenStorage, UserRepository $userRepository, WallabagV2Import $wallabagV2Import, FirefoxImport $firefoxImport, ChromeImport $chromeImport, ReadabilityImport $readabilityImport, InstapaperImport $instapaperImport, PinboardImport $pinboardImport, DeliciousImport $deliciousImport, WallabagV1Import $wallabagV1Import)
|
||||
{
|
||||
public function __construct(
|
||||
EntityManagerInterface $entityManager,
|
||||
TokenStorageInterface $tokenStorage,
|
||||
UserRepository $userRepository,
|
||||
WallabagV2Import $wallabagV2Import,
|
||||
FirefoxImport $firefoxImport,
|
||||
ChromeImport $chromeImport,
|
||||
ReadabilityImport $readabilityImport,
|
||||
InstapaperImport $instapaperImport,
|
||||
PinboardImport $pinboardImport,
|
||||
DeliciousImport $deliciousImport,
|
||||
WallabagV1Import $wallabagV1Import,
|
||||
ElcuratorImport $elcuratorImport,
|
||||
ShaarliImport $shaarliImport,
|
||||
PocketHtmlImport $pocketHtmlImport
|
||||
) {
|
||||
$this->entityManager = $entityManager;
|
||||
$this->tokenStorage = $tokenStorage;
|
||||
$this->userRepository = $userRepository;
|
||||
@ -49,6 +69,9 @@ class ImportCommand extends Command
|
||||
$this->pinboardImport = $pinboardImport;
|
||||
$this->deliciousImport = $deliciousImport;
|
||||
$this->wallabagV1Import = $wallabagV1Import;
|
||||
$this->elcuratorImport = $elcuratorImport;
|
||||
$this->shaarliImport = $shaarliImport;
|
||||
$this->pocketHtmlImport = $pocketHtmlImport;
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
@ -60,7 +83,7 @@ class ImportCommand extends Command
|
||||
->setDescription('Import entries from a JSON export')
|
||||
->addArgument('username', InputArgument::REQUIRED, 'User to populate')
|
||||
->addArgument('filepath', InputArgument::REQUIRED, 'Path to the JSON file')
|
||||
->addOption('importer', null, InputOption::VALUE_OPTIONAL, 'The importer to use: v1, v2, instapaper, pinboard, delicious, readability, firefox or chrome', 'v1')
|
||||
->addOption('importer', null, InputOption::VALUE_OPTIONAL, 'The importer to use: v1, v2, instapaper, pinboard, delicious, readability, firefox, chrome, elcurator, shaarli or pocket', 'v1')
|
||||
->addOption('markAsRead', null, InputOption::VALUE_OPTIONAL, 'Mark all entries as read', false)
|
||||
->addOption('useUserId', null, InputOption::VALUE_NONE, 'Use user id instead of username to find account')
|
||||
->addOption('disableContentUpdate', null, InputOption::VALUE_NONE, 'Disable fetching updated content from URL')
|
||||
@ -120,6 +143,15 @@ class ImportCommand extends Command
|
||||
case 'delicious':
|
||||
$import = $this->deliciousImport;
|
||||
break;
|
||||
case 'elcurator':
|
||||
$import = $this->elcuratorImport;
|
||||
break;
|
||||
case 'shaarli':
|
||||
$import = $this->shaarliImport;
|
||||
break;
|
||||
case 'pocket':
|
||||
$import = $this->pocketHtmlImport;
|
||||
break;
|
||||
default:
|
||||
$import = $this->wallabagV1Import;
|
||||
}
|
||||
|
||||
@ -20,9 +20,23 @@ class RabbitMQConsumerTotalProxy
|
||||
private Consumer $pinboardConsumer;
|
||||
private Consumer $deliciousConsumer;
|
||||
private Consumer $elcuratorConsumer;
|
||||
private Consumer $shaarliConsumer;
|
||||
private Consumer $pocketHtmlConsumer;
|
||||
|
||||
public function __construct(Consumer $pocketConsumer, Consumer $readabilityConsumer, Consumer $wallabagV1Consumer, Consumer $wallabagV2Consumer, Consumer $firefoxConsumer, Consumer $chromeConsumer, Consumer $instapaperConsumer, Consumer $pinboardConsumer, Consumer $deliciousConsumer, Consumer $elcuratorConsumer)
|
||||
{
|
||||
public function __construct(
|
||||
Consumer $pocketConsumer,
|
||||
Consumer $readabilityConsumer,
|
||||
Consumer $wallabagV1Consumer,
|
||||
Consumer $wallabagV2Consumer,
|
||||
Consumer $firefoxConsumer,
|
||||
Consumer $chromeConsumer,
|
||||
Consumer $instapaperConsumer,
|
||||
Consumer $pinboardConsumer,
|
||||
Consumer $deliciousConsumer,
|
||||
Consumer $elcuratorConsumer,
|
||||
Consumer $shaarliConsumer,
|
||||
Consumer $pocketHtmlConsumer
|
||||
) {
|
||||
$this->pocketConsumer = $pocketConsumer;
|
||||
$this->readabilityConsumer = $readabilityConsumer;
|
||||
$this->wallabagV1Consumer = $wallabagV1Consumer;
|
||||
@ -33,6 +47,8 @@ class RabbitMQConsumerTotalProxy
|
||||
$this->pinboardConsumer = $pinboardConsumer;
|
||||
$this->deliciousConsumer = $deliciousConsumer;
|
||||
$this->elcuratorConsumer = $elcuratorConsumer;
|
||||
$this->shaarliConsumer = $shaarliConsumer;
|
||||
$this->pocketHtmlConsumer = $pocketHtmlConsumer;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -77,6 +93,12 @@ class RabbitMQConsumerTotalProxy
|
||||
case 'elcurator':
|
||||
$consumer = $this->elcuratorConsumer;
|
||||
break;
|
||||
case 'shaarli':
|
||||
$consumer = $this->shaarliConsumer;
|
||||
break;
|
||||
case 'pocket_html':
|
||||
$consumer = $this->pocketHtmlConsumer;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
83
src/Wallabag/ImportBundle/Controller/HtmlController.php
Normal file
83
src/Wallabag/ImportBundle/Controller/HtmlController.php
Normal file
@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace Wallabag\ImportBundle\Controller;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
use Wallabag\ImportBundle\Form\Type\UploadImportType;
|
||||
use Wallabag\ImportBundle\Import\ImportInterface;
|
||||
|
||||
abstract class HtmlController extends AbstractController
|
||||
{
|
||||
/**
|
||||
* @Route("/html", name="import_html")
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function indexAction(Request $request, TranslatorInterface $translator)
|
||||
{
|
||||
$form = $this->createForm(UploadImportType::class);
|
||||
$form->handleRequest($request);
|
||||
|
||||
$wallabag = $this->getImportService();
|
||||
$wallabag->setUser($this->getUser());
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$file = $form->get('file')->getData();
|
||||
$markAsRead = $form->get('mark_as_read')->getData();
|
||||
$name = $this->getUser()->getId() . '.html';
|
||||
|
||||
if (null !== $file && \in_array($file->getClientMimeType(), $this->getParameter('wallabag_import.allow_mimetypes'), true) && $file->move($this->getParameter('wallabag_import.resource_dir'), $name)) {
|
||||
$res = $wallabag
|
||||
->setFilepath($this->getParameter('wallabag_import.resource_dir') . '/' . $name)
|
||||
->setMarkAsRead($markAsRead)
|
||||
->import();
|
||||
|
||||
$message = 'flashes.import.notice.failed';
|
||||
|
||||
if (true === $res) {
|
||||
$summary = $wallabag->getSummary();
|
||||
$message = $translator->trans('flashes.import.notice.summary', [
|
||||
'%imported%' => $summary['imported'],
|
||||
'%skipped%' => $summary['skipped'],
|
||||
]);
|
||||
|
||||
if (0 < $summary['queued']) {
|
||||
$message = $translator->trans('flashes.import.notice.summary_with_queue', [
|
||||
'%queued%' => $summary['queued'],
|
||||
]);
|
||||
}
|
||||
|
||||
unlink($this->getParameter('wallabag_import.resource_dir') . '/' . $name);
|
||||
}
|
||||
|
||||
$this->addFlash('notice', $message);
|
||||
|
||||
return $this->redirect($this->generateUrl('homepage'));
|
||||
}
|
||||
$this->addFlash('notice', 'flashes.import.notice.failed_on_file');
|
||||
}
|
||||
|
||||
return $this->render($this->getImportTemplate(), [
|
||||
'form' => $form->createView(),
|
||||
'import' => $wallabag,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the service to handle the import.
|
||||
*
|
||||
* @return ImportInterface
|
||||
*/
|
||||
abstract protected function getImportService();
|
||||
|
||||
/**
|
||||
* Return the template used for the form.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function getImportTemplate();
|
||||
}
|
||||
@ -57,6 +57,8 @@ class ImportController extends AbstractController
|
||||
+ $this->rabbitMQConsumerTotalProxy->getTotalMessage('pinboard')
|
||||
+ $this->rabbitMQConsumerTotalProxy->getTotalMessage('delicious')
|
||||
+ $this->rabbitMQConsumerTotalProxy->getTotalMessage('elcurator')
|
||||
+ $this->rabbitMQConsumerTotalProxy->getTotalMessage('shaarli')
|
||||
+ $this->rabbitMQConsumerTotalProxy->getTotalMessage('pocket_html')
|
||||
;
|
||||
} catch (\Exception $e) {
|
||||
$rabbitNotInstalled = true;
|
||||
@ -75,6 +77,8 @@ class ImportController extends AbstractController
|
||||
+ $redis->llen('wallabag.import.pinboard')
|
||||
+ $redis->llen('wallabag.import.delicious')
|
||||
+ $redis->llen('wallabag.import.elcurator')
|
||||
+ $redis->llen('wallabag.import.shaarli')
|
||||
+ $redis->llen('wallabag.import.pocket_html')
|
||||
;
|
||||
} catch (\Exception $e) {
|
||||
$redisNotInstalled = true;
|
||||
|
||||
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace Wallabag\ImportBundle\Controller;
|
||||
|
||||
use Craue\ConfigBundle\Util\Config;
|
||||
use OldSound\RabbitMqBundle\RabbitMq\Producer as RabbitMqProducer;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
use Wallabag\ImportBundle\Import\PocketHtmlImport;
|
||||
use Wallabag\ImportBundle\Redis\Producer as RedisProducer;
|
||||
|
||||
class PocketHtmlController extends HtmlController
|
||||
{
|
||||
private PocketHtmlImport $pocketHtmlImport;
|
||||
private Config $craueConfig;
|
||||
private RabbitMqProducer $rabbitMqProducer;
|
||||
private RedisProducer $redisProducer;
|
||||
|
||||
public function __construct(PocketHtmlImport $pocketHtmlImport, Config $craueConfig, RabbitMqProducer $rabbitMqProducer, RedisProducer $redisProducer)
|
||||
{
|
||||
$this->pocketHtmlImport = $pocketHtmlImport;
|
||||
$this->craueConfig = $craueConfig;
|
||||
$this->rabbitMqProducer = $rabbitMqProducer;
|
||||
$this->redisProducer = $redisProducer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/pocket_html", name="import_pocket_html")
|
||||
*/
|
||||
public function indexAction(Request $request, TranslatorInterface $translator)
|
||||
{
|
||||
return parent::indexAction($request, $translator);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getImportService()
|
||||
{
|
||||
if ($this->craueConfig->get('import_with_rabbitmq')) {
|
||||
$this->pocketHtmlImport->setProducer($this->rabbitMqProducer);
|
||||
} elseif ($this->craueConfig->get('import_with_redis')) {
|
||||
$this->pocketHtmlImport->setProducer($this->redisProducer);
|
||||
}
|
||||
|
||||
return $this->pocketHtmlImport;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getImportTemplate()
|
||||
{
|
||||
return '@WallabagImport/PocketHtml/index.html.twig';
|
||||
}
|
||||
}
|
||||
57
src/Wallabag/ImportBundle/Controller/ShaarliController.php
Normal file
57
src/Wallabag/ImportBundle/Controller/ShaarliController.php
Normal file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace Wallabag\ImportBundle\Controller;
|
||||
|
||||
use Craue\ConfigBundle\Util\Config;
|
||||
use OldSound\RabbitMqBundle\RabbitMq\Producer as RabbitMqProducer;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
use Wallabag\ImportBundle\Import\ShaarliImport;
|
||||
use Wallabag\ImportBundle\Redis\Producer as RedisProducer;
|
||||
|
||||
class ShaarliController extends HtmlController
|
||||
{
|
||||
private ShaarliImport $shaarliImport;
|
||||
private Config $craueConfig;
|
||||
private RabbitMqProducer $rabbitMqProducer;
|
||||
private RedisProducer $redisProducer;
|
||||
|
||||
public function __construct(ShaarliImport $shaarliImport, Config $craueConfig, RabbitMqProducer $rabbitMqProducer, RedisProducer $redisProducer)
|
||||
{
|
||||
$this->shaarliImport = $shaarliImport;
|
||||
$this->craueConfig = $craueConfig;
|
||||
$this->rabbitMqProducer = $rabbitMqProducer;
|
||||
$this->redisProducer = $redisProducer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/shaarli", name="import_shaarli")
|
||||
*/
|
||||
public function indexAction(Request $request, TranslatorInterface $translator)
|
||||
{
|
||||
return parent::indexAction($request, $translator);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getImportService()
|
||||
{
|
||||
if ($this->craueConfig->get('import_with_rabbitmq')) {
|
||||
$this->shaarliImport->setProducer($this->rabbitMqProducer);
|
||||
} elseif ($this->craueConfig->get('import_with_redis')) {
|
||||
$this->shaarliImport->setProducer($this->redisProducer);
|
||||
}
|
||||
|
||||
return $this->shaarliImport;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getImportTemplate()
|
||||
{
|
||||
return '@WallabagImport/Shaarli/index.html.twig';
|
||||
}
|
||||
}
|
||||
@ -107,7 +107,7 @@ abstract class AbstractImport implements ImportInterface
|
||||
/**
|
||||
* Parse one entry.
|
||||
*
|
||||
* @return Entry
|
||||
* @return Entry|null
|
||||
*/
|
||||
abstract public function parseEntry(array $importedEntry);
|
||||
|
||||
@ -218,7 +218,7 @@ abstract class AbstractImport implements ImportInterface
|
||||
|
||||
/**
|
||||
* Set current imported entry to archived / read.
|
||||
* Implementation is different accross all imports.
|
||||
* Implementation is different across all imports.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
|
||||
@ -81,28 +81,28 @@ abstract class BrowserImport extends AbstractImport
|
||||
if ($this->producer) {
|
||||
$this->parseEntriesForProducer($importedEntry);
|
||||
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->parseEntries($importedEntry);
|
||||
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (\array_key_exists('children', $importedEntry)) {
|
||||
if ($this->producer) {
|
||||
$this->parseEntriesForProducer($importedEntry['children']);
|
||||
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->parseEntries($importedEntry['children']);
|
||||
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!\array_key_exists('uri', $importedEntry) && !\array_key_exists('url', $importedEntry)) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
$url = \array_key_exists('uri', $importedEntry) ? $importedEntry['uri'] : $importedEntry['url'];
|
||||
@ -114,7 +114,7 @@ abstract class BrowserImport extends AbstractImport
|
||||
if (false !== $existingEntry) {
|
||||
++$this->skippedEntries;
|
||||
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
$data = $this->prepareEntry($importedEntry);
|
||||
|
||||
@ -104,7 +104,7 @@ class DeliciousImport extends AbstractImport
|
||||
if (false !== $existingEntry) {
|
||||
++$this->skippedEntries;
|
||||
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
$data = [
|
||||
|
||||
210
src/Wallabag/ImportBundle/Import/HtmlImport.php
Normal file
210
src/Wallabag/ImportBundle/Import/HtmlImport.php
Normal file
@ -0,0 +1,210 @@
|
||||
<?php
|
||||
|
||||
namespace Wallabag\ImportBundle\Import;
|
||||
|
||||
use Wallabag\CoreBundle\Entity\Entry;
|
||||
use Wallabag\CoreBundle\Event\EntrySavedEvent;
|
||||
|
||||
abstract class HtmlImport extends AbstractImport
|
||||
{
|
||||
protected $filepath;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
abstract public function getName();
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
abstract public function getUrl();
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
abstract public function getDescription();
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function import()
|
||||
{
|
||||
if (!$this->user) {
|
||||
$this->logger->error('Wallabag HTML Import: user is not defined');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!file_exists($this->filepath) || !is_readable($this->filepath)) {
|
||||
$this->logger->error('Wallabag HTML Import: unable to read file', ['filepath' => $this->filepath]);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$html = new \DOMDocument();
|
||||
|
||||
libxml_use_internal_errors(true);
|
||||
$html->loadHTMLFile($this->filepath);
|
||||
$hrefs = $html->getElementsByTagName('a');
|
||||
libxml_use_internal_errors(false);
|
||||
|
||||
if (0 === $hrefs->length) {
|
||||
$this->logger->error('Wallabag HTML: no entries in imported file');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$entries = [];
|
||||
foreach ($hrefs as $href) {
|
||||
$entry = [];
|
||||
$entry['url'] = $href->getAttribute('href');
|
||||
$entry['tags'] = $href->getAttribute('tags');
|
||||
$entry['created_at'] = $href->getAttribute('add_date');
|
||||
$entries[] = $entry;
|
||||
}
|
||||
|
||||
if ($this->producer) {
|
||||
$this->parseEntriesForProducer($entries);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->parseEntries($entries);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set file path to the html file.
|
||||
*
|
||||
* @param string $filepath
|
||||
*/
|
||||
public function setFilepath($filepath)
|
||||
{
|
||||
$this->filepath = $filepath;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function parseEntry(array $importedEntry)
|
||||
{
|
||||
$url = $importedEntry['url'];
|
||||
|
||||
$existingEntry = $this->em
|
||||
->getRepository(Entry::class)
|
||||
->findByUrlAndUserId($url, $this->user->getId());
|
||||
|
||||
if (false !== $existingEntry) {
|
||||
++$this->skippedEntries;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$data = $this->prepareEntry($importedEntry);
|
||||
|
||||
$entry = new Entry($this->user);
|
||||
$entry->setUrl($data['url']);
|
||||
$entry->updateArchived($data['is_archived']);
|
||||
$createdAt = new \DateTime();
|
||||
$createdAt->setTimestamp($data['created_at']);
|
||||
$entry->setCreatedAt($createdAt);
|
||||
|
||||
// update entry with content (in case fetching failed, the given entry will be return)
|
||||
$this->fetchContent($entry, $data['url'], $data);
|
||||
|
||||
if (\array_key_exists('tags', $data)) {
|
||||
$this->tagsAssigner->assignTagsToEntry(
|
||||
$entry,
|
||||
$data['tags']
|
||||
);
|
||||
}
|
||||
|
||||
$this->em->persist($entry);
|
||||
++$this->importedEntries;
|
||||
|
||||
return $entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse and insert all given entries.
|
||||
*/
|
||||
protected function parseEntries(array $entries)
|
||||
{
|
||||
$i = 1;
|
||||
$entryToBeFlushed = [];
|
||||
|
||||
foreach ($entries as $importedEntry) {
|
||||
$entry = $this->parseEntry($importedEntry);
|
||||
|
||||
if (null === $entry) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// @see AbstractImport
|
||||
$entryToBeFlushed[] = $entry;
|
||||
|
||||
// flush every 20 entries
|
||||
if (0 === ($i % 20)) {
|
||||
$this->em->flush();
|
||||
|
||||
foreach ($entryToBeFlushed as $entry) {
|
||||
$this->eventDispatcher->dispatch(new EntrySavedEvent($entry), EntrySavedEvent::NAME);
|
||||
}
|
||||
|
||||
$entryToBeFlushed = [];
|
||||
}
|
||||
++$i;
|
||||
}
|
||||
|
||||
$this->em->flush();
|
||||
|
||||
if (!empty($entryToBeFlushed)) {
|
||||
foreach ($entryToBeFlushed as $entry) {
|
||||
$this->eventDispatcher->dispatch(new EntrySavedEvent($entry), EntrySavedEvent::NAME);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse entries and send them to the queue.
|
||||
* It should just be a simple loop on all item, no call to the database should be done
|
||||
* to speedup queuing.
|
||||
*
|
||||
* Faster parse entries for Producer.
|
||||
* We don't care to make check at this time. They'll be done by the consumer.
|
||||
*/
|
||||
protected function parseEntriesForProducer(array $entries)
|
||||
{
|
||||
foreach ($entries as $importedEntry) {
|
||||
if ((array) $importedEntry !== $importedEntry) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// set userId for the producer (it won't know which user is connected)
|
||||
$importedEntry['userId'] = $this->user->getId();
|
||||
|
||||
if ($this->markAsRead) {
|
||||
$importedEntry = $this->setEntryAsRead($importedEntry);
|
||||
}
|
||||
|
||||
++$this->queuedEntries;
|
||||
|
||||
$this->producer->publish(json_encode($importedEntry));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setEntryAsRead(array $importedEntry)
|
||||
{
|
||||
$importedEntry['is_archived'] = 1;
|
||||
|
||||
return $importedEntry;
|
||||
}
|
||||
|
||||
abstract protected function prepareEntry(array $entry = []);
|
||||
}
|
||||
@ -132,7 +132,7 @@ class InstapaperImport extends AbstractImport
|
||||
if (false !== $existingEntry) {
|
||||
++$this->skippedEntries;
|
||||
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
$entry = new Entry($this->user);
|
||||
|
||||
@ -104,7 +104,7 @@ class PinboardImport extends AbstractImport
|
||||
if (false !== $existingEntry) {
|
||||
++$this->skippedEntries;
|
||||
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
$data = [
|
||||
|
||||
113
src/Wallabag/ImportBundle/Import/PocketHtmlImport.php
Normal file
113
src/Wallabag/ImportBundle/Import/PocketHtmlImport.php
Normal file
@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
namespace Wallabag\ImportBundle\Import;
|
||||
|
||||
class PocketHtmlImport extends HtmlImport
|
||||
{
|
||||
protected $filepath;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'Pocket HTML';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getUrl()
|
||||
{
|
||||
return 'import_pocket_html';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return 'import.pocket_html.description';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateEntry(array $importedEntry)
|
||||
{
|
||||
if (empty($importedEntry['url'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function import()
|
||||
{
|
||||
if (!$this->user) {
|
||||
$this->logger->error('Pocket HTML Import: user is not defined');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!file_exists($this->filepath) || !is_readable($this->filepath)) {
|
||||
$this->logger->error('Pocket HTML Import: unable to read file', ['filepath' => $this->filepath]);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$html = new \DOMDocument();
|
||||
|
||||
libxml_use_internal_errors(true);
|
||||
$html->loadHTMLFile($this->filepath);
|
||||
$hrefs = $html->getElementsByTagName('a');
|
||||
libxml_use_internal_errors(false);
|
||||
|
||||
if (0 === $hrefs->length) {
|
||||
$this->logger->error('Pocket HTML: no entries in imported file');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$entries = [];
|
||||
foreach ($hrefs as $href) {
|
||||
$entry = [];
|
||||
$entry['url'] = $href->getAttribute('href');
|
||||
$entry['tags'] = $href->getAttribute('tags');
|
||||
$entry['created_at'] = $href->getAttribute('time_added');
|
||||
$entries[] = $entry;
|
||||
}
|
||||
|
||||
if ($this->producer) {
|
||||
$this->parseEntriesForProducer($entries);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->parseEntries($entries);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function prepareEntry(array $entry = [])
|
||||
{
|
||||
$data = [
|
||||
'title' => '',
|
||||
'html' => false,
|
||||
'url' => $entry['url'],
|
||||
'is_archived' => (int) $this->markAsRead,
|
||||
'is_starred' => false,
|
||||
'tags' => '',
|
||||
'created_at' => $entry['created_at'],
|
||||
];
|
||||
|
||||
if (\array_key_exists('tags', $entry) && '' !== $entry['tags']) {
|
||||
$data['tags'] = $entry['tags'];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
@ -186,7 +186,7 @@ class PocketImport extends AbstractImport
|
||||
if (false !== $existingEntry) {
|
||||
++$this->skippedEntries;
|
||||
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
$entry = new Entry($this->user);
|
||||
|
||||
@ -104,7 +104,7 @@ class ReadabilityImport extends AbstractImport
|
||||
if (false !== $existingEntry) {
|
||||
++$this->skippedEntries;
|
||||
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
$data = [
|
||||
|
||||
66
src/Wallabag/ImportBundle/Import/ShaarliImport.php
Normal file
66
src/Wallabag/ImportBundle/Import/ShaarliImport.php
Normal file
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace Wallabag\ImportBundle\Import;
|
||||
|
||||
class ShaarliImport extends HtmlImport
|
||||
{
|
||||
protected $filepath;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'Shaarli';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getUrl()
|
||||
{
|
||||
return 'import_shaarli';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return 'import.shaarli.description';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateEntry(array $importedEntry)
|
||||
{
|
||||
if (empty($importedEntry['url'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function prepareEntry(array $entry = [])
|
||||
{
|
||||
$data = [
|
||||
'title' => '',
|
||||
'html' => false,
|
||||
'url' => $entry['url'],
|
||||
'is_archived' => (int) $this->markAsRead,
|
||||
'is_starred' => false,
|
||||
'tags' => '',
|
||||
'created_at' => $entry['created_at'],
|
||||
];
|
||||
|
||||
if (\array_key_exists('tags', $entry) && '' !== $entry['tags']) {
|
||||
$data['tags'] = $entry['tags'];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
@ -110,7 +110,7 @@ abstract class WallabagImport extends AbstractImport
|
||||
if (false !== $existingEntry) {
|
||||
++$this->skippedEntries;
|
||||
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
$data = $this->prepareEntry($importedEntry);
|
||||
@ -119,6 +119,10 @@ abstract class WallabagImport extends AbstractImport
|
||||
$entry->setUrl($data['url']);
|
||||
$entry->setTitle($data['title']);
|
||||
|
||||
if (\array_key_exists('is_parsed', $data)) {
|
||||
$entry->setNotParsed(true);
|
||||
}
|
||||
|
||||
// update entry with content (in case fetching failed, the given entry will be return)
|
||||
$this->fetchContent($entry, $data['url'], $data);
|
||||
|
||||
|
||||
@ -65,6 +65,7 @@ class WallabagV1Import extends WallabagImport
|
||||
if (\in_array($entry['title'], $this->untitled, true)) {
|
||||
$data['title'] = $this->fetchingErrorMessageTitle;
|
||||
$data['html'] = $this->fetchingErrorMessage;
|
||||
$entry['is_not_parsed'] = 1;
|
||||
}
|
||||
|
||||
if (\array_key_exists('tags', $entry) && '' !== $entry['tags']) {
|
||||
|
||||
@ -0,0 +1,45 @@
|
||||
{% extends "@WallabagCore/layout.html.twig" %}
|
||||
|
||||
{% block title %}{{ 'import.pocket_html.page_title'|trans }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<div class="card-panel settings">
|
||||
{% include '@WallabagImport/Import/_information.html.twig' %}
|
||||
|
||||
<div class="row">
|
||||
<blockquote>{{ import.description|trans|raw }}</blockquote>
|
||||
<p>{{ 'import.pocket_html.how_to'|trans }}</p>
|
||||
|
||||
<div class="col s12">
|
||||
{{ form_start(form, {'method': 'POST'}) }}
|
||||
{{ form_errors(form) }}
|
||||
<div class="row">
|
||||
<div class="file-field input-field col s12">
|
||||
{{ form_errors(form.file) }}
|
||||
<div class="btn">
|
||||
<span>{{ form.file.vars.label|trans }}</span>
|
||||
{{ form_widget(form.file) }}
|
||||
</div>
|
||||
<div class="file-path-wrapper">
|
||||
<input class="file-path validate" type="text">
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-field col s6 with-checkbox">
|
||||
<h6>{{ 'import.form.mark_as_read_title'|trans }}</h6>
|
||||
{{ form_widget(form.mark_as_read) }}
|
||||
{{ form_label(form.mark_as_read) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ form_widget(form.save, {'attr': {'class': 'btn waves-effect waves-light'}}) }}
|
||||
|
||||
{{ form_rest(form) }}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@ -0,0 +1,45 @@
|
||||
{% extends "@WallabagCore/layout.html.twig" %}
|
||||
|
||||
{% block title %}{{ 'import.shaarli.page_title'|trans }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<div class="card-panel settings">
|
||||
{% include '@WallabagImport/Import/_information.html.twig' %}
|
||||
|
||||
<div class="row">
|
||||
<blockquote>{{ import.description|trans|raw }}</blockquote>
|
||||
<p>{{ 'import.shaarli.how_to'|trans }}</p>
|
||||
|
||||
<div class="col s12">
|
||||
{{ form_start(form, {'method': 'POST'}) }}
|
||||
{{ form_errors(form) }}
|
||||
<div class="row">
|
||||
<div class="file-field input-field col s12">
|
||||
{{ form_errors(form.file) }}
|
||||
<div class="btn">
|
||||
<span>{{ form.file.vars.label|trans }}</span>
|
||||
{{ form_widget(form.file) }}
|
||||
</div>
|
||||
<div class="file-path-wrapper">
|
||||
<input class="file-path validate" type="text">
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-field col s6 with-checkbox">
|
||||
<h6>{{ 'import.form.mark_as_read_title'|trans }}</h6>
|
||||
{{ form_widget(form.mark_as_read) }}
|
||||
{{ form_label(form.mark_as_read) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ form_widget(form.save, {'attr': {'class': 'btn waves-effect waves-light'}}) }}
|
||||
|
||||
{{ form_rest(form) }}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@ -13,6 +13,7 @@ use Scheb\TwoFactorBundle\Security\TwoFactor\Provider\Google\GoogleAuthenticator
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\Form\Form;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
@ -184,7 +185,7 @@ class ManageController extends AbstractController
|
||||
*
|
||||
* @param User $user The User entity
|
||||
*
|
||||
* @return Form The form
|
||||
* @return FormInterface The form
|
||||
*/
|
||||
private function createDeleteForm(User $user)
|
||||
{
|
||||
|
||||
@ -75,13 +75,6 @@ class EntryRestControllerTest extends WallabagApiTestCase
|
||||
$this->assertStringContainsString('application/epub', $this->client->getResponse()->getContent());
|
||||
$this->assertSame('application/epub+zip', $this->client->getResponse()->headers->get('Content-Type'));
|
||||
|
||||
// re-auth client for mobi
|
||||
$client = $this->createAuthorizedClient();
|
||||
$client->request('GET', '/api/entries/' . $entry->getId() . '/export.mobi');
|
||||
$this->assertSame(200, $client->getResponse()->getStatusCode());
|
||||
|
||||
$this->assertSame('application/x-mobipocket-ebook', $client->getResponse()->headers->get('Content-Type'));
|
||||
|
||||
// re-auth client for pdf
|
||||
$client = $this->createAuthorizedClient();
|
||||
$client->request('GET', '/api/entries/' . $entry->getId() . '/export.pdf');
|
||||
@ -190,6 +183,7 @@ class EntryRestControllerTest extends WallabagApiTestCase
|
||||
'tags' => 'foo',
|
||||
'since' => 1443274283,
|
||||
'public' => 0,
|
||||
'notParsed' => 0,
|
||||
]);
|
||||
|
||||
$this->assertSame(200, $this->client->getResponse()->getStatusCode());
|
||||
@ -348,6 +342,60 @@ class EntryRestControllerTest extends WallabagApiTestCase
|
||||
$this->assertSame('application/json', $this->client->getResponse()->headers->get('Content-Type'));
|
||||
}
|
||||
|
||||
public function testGetNotParsedEntries()
|
||||
{
|
||||
$this->client->request('GET', '/api/entries', ['notParsed' => 1]);
|
||||
|
||||
$this->assertSame(200, $this->client->getResponse()->getStatusCode());
|
||||
|
||||
$content = json_decode($this->client->getResponse()->getContent(), true);
|
||||
|
||||
$this->assertGreaterThanOrEqual(1, \count($content));
|
||||
$this->assertNotEmpty($content['_embedded']['items']);
|
||||
$this->assertGreaterThanOrEqual(1, $content['total']);
|
||||
$this->assertSame(1, $content['page']);
|
||||
$this->assertGreaterThanOrEqual(1, $content['pages']);
|
||||
|
||||
$this->assertArrayHasKey('_links', $content);
|
||||
$this->assertArrayHasKey('self', $content['_links']);
|
||||
$this->assertArrayHasKey('first', $content['_links']);
|
||||
$this->assertArrayHasKey('last', $content['_links']);
|
||||
|
||||
foreach (['self', 'first', 'last'] as $link) {
|
||||
$this->assertArrayHasKey('href', $content['_links'][$link]);
|
||||
$this->assertStringContainsString('notParsed=1', $content['_links'][$link]['href']);
|
||||
}
|
||||
|
||||
$this->assertSame('application/json', $this->client->getResponse()->headers->get('Content-Type'));
|
||||
}
|
||||
|
||||
public function testGetParsedEntries()
|
||||
{
|
||||
$this->client->request('GET', '/api/entries', ['notParsed' => 0]);
|
||||
|
||||
$this->assertSame(200, $this->client->getResponse()->getStatusCode());
|
||||
|
||||
$content = json_decode($this->client->getResponse()->getContent(), true);
|
||||
|
||||
$this->assertGreaterThanOrEqual(1, \count($content));
|
||||
$this->assertNotEmpty($content['_embedded']['items']);
|
||||
$this->assertGreaterThanOrEqual(1, $content['total']);
|
||||
$this->assertSame(1, $content['page']);
|
||||
$this->assertGreaterThanOrEqual(1, $content['pages']);
|
||||
|
||||
$this->assertArrayHasKey('_links', $content);
|
||||
$this->assertArrayHasKey('self', $content['_links']);
|
||||
$this->assertArrayHasKey('first', $content['_links']);
|
||||
$this->assertArrayHasKey('last', $content['_links']);
|
||||
|
||||
foreach (['self', 'first', 'last'] as $link) {
|
||||
$this->assertArrayHasKey('href', $content['_links'][$link]);
|
||||
$this->assertStringContainsString('notParsed=0', $content['_links'][$link]['href']);
|
||||
}
|
||||
|
||||
$this->assertSame('application/json', $this->client->getResponse()->headers->get('Content-Type'));
|
||||
}
|
||||
|
||||
public function testGetTaggedEntries()
|
||||
{
|
||||
$this->client->request('GET', '/api/entries', ['tags' => 'foo,bar']);
|
||||
@ -967,6 +1015,7 @@ class EntryRestControllerTest extends WallabagApiTestCase
|
||||
|
||||
public function testSaveIsArchivedAfterPatch()
|
||||
{
|
||||
$now = new \DateTime();
|
||||
$entry = $this->client->getContainer()
|
||||
->get(EntityManagerInterface::class)
|
||||
->getRepository(Entry::class)
|
||||
@ -988,6 +1037,7 @@ class EntryRestControllerTest extends WallabagApiTestCase
|
||||
|
||||
$this->assertSame(1, $content['is_archived']);
|
||||
$this->assertSame($previousTitle . '++', $content['title']);
|
||||
$this->assertGreaterThanOrEqual((new \DateTime($content['archived_at']))->getTimestamp(), $now->getTimestamp());
|
||||
}
|
||||
|
||||
public function testSaveIsStarredAfterPatch()
|
||||
@ -1001,6 +1051,9 @@ class EntryRestControllerTest extends WallabagApiTestCase
|
||||
if (!$entry) {
|
||||
$this->markTestSkipped('No content found in db.');
|
||||
}
|
||||
|
||||
$previousTitle = $entry->getTitle();
|
||||
|
||||
$this->client->request('PATCH', '/api/entries/' . $entry->getId() . '.json', [
|
||||
'title' => $entry->getTitle() . '++',
|
||||
]);
|
||||
@ -1010,7 +1063,8 @@ class EntryRestControllerTest extends WallabagApiTestCase
|
||||
$content = json_decode($this->client->getResponse()->getContent(), true);
|
||||
|
||||
$this->assertSame(1, $content['is_starred']);
|
||||
$this->assertGreaterThanOrEqual($now->getTimestamp(), (new \DateTime($content['starred_at']))->getTimestamp());
|
||||
$this->assertSame($previousTitle . '++', $content['title']);
|
||||
$this->assertGreaterThanOrEqual((new \DateTime($content['starred_at']))->getTimestamp(), $now->getTimestamp());
|
||||
}
|
||||
|
||||
public function dataForEntriesExistWithUrl()
|
||||
|
||||
@ -21,6 +21,16 @@ class ReloadEntryCommandTest extends WallabagCoreTestCase
|
||||
*/
|
||||
public $bobEntry;
|
||||
|
||||
/**
|
||||
* @var Entry
|
||||
*/
|
||||
public $bobParsedEntry;
|
||||
|
||||
/**
|
||||
* @var Entry
|
||||
*/
|
||||
public $bobNotParsedEntry;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
@ -41,6 +51,19 @@ class ReloadEntryCommandTest extends WallabagCoreTestCase
|
||||
$this->bobEntry->setContent('');
|
||||
$this->getEntityManager()->persist($this->bobEntry);
|
||||
|
||||
$this->bobParsedEntry = new Entry($user);
|
||||
$this->bobParsedEntry->setUrl($this->url);
|
||||
$this->bobParsedEntry->setTitle('title foo');
|
||||
$this->bobParsedEntry->setContent('');
|
||||
$this->getEntityManager()->persist($this->bobParsedEntry);
|
||||
|
||||
$this->bobNotParsedEntry = new Entry($user);
|
||||
$this->bobNotParsedEntry->setUrl($this->url);
|
||||
$this->bobNotParsedEntry->setTitle('title foo');
|
||||
$this->bobNotParsedEntry->setContent('');
|
||||
$this->bobNotParsedEntry->setNotParsed(true);
|
||||
$this->getEntityManager()->persist($this->bobNotParsedEntry);
|
||||
|
||||
$this->getEntityManager()->flush();
|
||||
}
|
||||
|
||||
@ -95,6 +118,27 @@ class ReloadEntryCommandTest extends WallabagCoreTestCase
|
||||
$this->assertStringContainsString('Done', $tester->getDisplay());
|
||||
}
|
||||
|
||||
public function testRunReloadEntryWithNotParsedOption()
|
||||
{
|
||||
$application = new Application($this->getTestClient()->getKernel());
|
||||
|
||||
$command = $application->find('wallabag:entry:reload');
|
||||
$tester = new CommandTester($command);
|
||||
$tester->execute([
|
||||
'--only-not-parsed' => true,
|
||||
]);
|
||||
|
||||
$entryRepository = $this->getTestClient()->getContainer()->get('wallabag_core.entry_repository.test');
|
||||
|
||||
$reloadedBobParsedEntry = $entryRepository->find($this->bobParsedEntry->getId());
|
||||
$this->assertEmpty($reloadedBobParsedEntry->getContent());
|
||||
|
||||
$reloadedBobNotParsedEntry = $entryRepository->find($this->bobNotParsedEntry->getId());
|
||||
$this->assertNotEmpty($reloadedBobNotParsedEntry->getContent());
|
||||
|
||||
$this->assertStringContainsString('Done', $tester->getDisplay());
|
||||
}
|
||||
|
||||
public function testRunReloadEntryWithoutEntryCommand()
|
||||
{
|
||||
$application = new Application($this->getTestClient()->getKernel());
|
||||
|
||||
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Wallabag\CoreBundle\Command;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
||||
use Symfony\Component\Console\Exception\RuntimeException;
|
||||
use Symfony\Component\Console\Tester\CommandTester;
|
||||
use Tests\Wallabag\CoreBundle\WallabagCoreTestCase;
|
||||
use Wallabag\CoreBundle\Entity\Entry;
|
||||
|
||||
class UpdatePicturesPathCommandTest extends WallabagCoreTestCase
|
||||
{
|
||||
public function testRunUpdatePicturesPathCommandWithoutOldURL()
|
||||
{
|
||||
$this->expectException(RuntimeException::class);
|
||||
$this->expectExceptionMessage('Not enough arguments (missing: "old-url")');
|
||||
$application = new Application($this->getTestClient()->getKernel());
|
||||
|
||||
$command = $application->find('wallabag:update-pictures-path');
|
||||
|
||||
$tester = new CommandTester($command);
|
||||
$tester->execute([]);
|
||||
}
|
||||
|
||||
public function testRunGenerateUrlHashesCommandForUser()
|
||||
{
|
||||
$application = new Application($this->getTestClient()->getKernel());
|
||||
$this->logInAs('admin');
|
||||
|
||||
$url = 'https://wallabag.org/news/20230620-new-release-wallabag-260/';
|
||||
|
||||
$command = $application->find('wallabag:update-pictures-path');
|
||||
|
||||
$client = $this->getTestClient();
|
||||
$em = $client->getContainer()->get(EntityManagerInterface::class);
|
||||
$entry = new Entry($this->getLoggedInUser());
|
||||
$entry->setUrl($url);
|
||||
$entry->setPreviewPicture('https://old-url.test/mypicture.jpg');
|
||||
$entry->setContent('my great article with a picture <img src="https://old-url.test/mypicture.jpg" />');
|
||||
$em->persist($entry);
|
||||
$em->flush();
|
||||
|
||||
$tester = new CommandTester($command);
|
||||
$tester->execute([
|
||||
'old-url' => 'https://old-url.test',
|
||||
]);
|
||||
|
||||
$this->assertStringContainsString('Finished updating.', $tester->getDisplay());
|
||||
|
||||
$entry = $em->getRepository(Entry::class)->findOneByUrl($url);
|
||||
$this->assertSame($entry->getPreviewPicture(), $client->getContainer()->getParameter('domain_name') . '/mypicture.jpg');
|
||||
|
||||
$query = $em->createQuery('DELETE FROM Wallabag\CoreBundle\Entity\Entry e WHERE e.url = :url');
|
||||
$query->setParameter('url', $url);
|
||||
$query->execute();
|
||||
}
|
||||
}
|
||||
@ -2,7 +2,6 @@
|
||||
|
||||
namespace Tests\Wallabag\CoreBundle\Controller;
|
||||
|
||||
use Craue\ConfigBundle\Util\Config;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
use Symfony\Component\HttpFoundation\Session\SessionInterface;
|
||||
@ -736,36 +735,6 @@ class ConfigControllerTest extends WallabagCoreTestCase
|
||||
$this->assertStringContainsString('You can not access this rule', $body[0]);
|
||||
}
|
||||
|
||||
public function testDemoMode()
|
||||
{
|
||||
$this->logInAs('admin');
|
||||
$client = $this->getTestClient();
|
||||
|
||||
$config = $client->getContainer()->get(Config::class);
|
||||
$config->set('demo_mode_enabled', 1);
|
||||
$config->set('demo_mode_username', 'admin');
|
||||
|
||||
$crawler = $client->request('GET', '/config');
|
||||
|
||||
$this->assertSame(200, $client->getResponse()->getStatusCode());
|
||||
|
||||
$form = $crawler->filter('button[id=change_passwd_save]')->form();
|
||||
|
||||
$data = [
|
||||
'change_passwd[old_password]' => 'mypassword',
|
||||
'change_passwd[new_password][first]' => 'mypassword',
|
||||
'change_passwd[new_password][second]' => 'mypassword',
|
||||
];
|
||||
|
||||
$client->submit($form, $data);
|
||||
|
||||
$this->assertSame(302, $client->getResponse()->getStatusCode());
|
||||
$this->assertStringContainsString('flashes.config.notice.password_not_updated_demo', $client->getContainer()->get(SessionInterface::class)->getFlashBag()->get('notice')[0]);
|
||||
|
||||
$config->set('demo_mode_enabled', 0);
|
||||
$config->set('demo_mode_username', 'wallabag');
|
||||
}
|
||||
|
||||
public function testDeleteUserButtonVisibility()
|
||||
{
|
||||
$this->logInAs('admin');
|
||||
@ -1385,4 +1354,40 @@ class ConfigControllerTest extends WallabagCoreTestCase
|
||||
|
||||
$client->request('GET', '/config/view-mode');
|
||||
}
|
||||
|
||||
public function testGeneratedCSS()
|
||||
{
|
||||
$this->logInAs('admin');
|
||||
$client = $this->getTestClient();
|
||||
|
||||
// We check the current display
|
||||
$em = $client->getContainer()->get(EntityManagerInterface::class);
|
||||
$entry = $em->getRepository(Entry::class)->findByUrlAndUserId('http://0.0.0.0/entry1', $this->getLoggedInUserId());
|
||||
$client->request('GET', '/view/' . $entry->getId());
|
||||
|
||||
$this->assertStringNotContainsString('font-family: "OpenDyslexicRegular"', $client->getResponse()->getContent());
|
||||
$this->assertStringNotContainsString('max-width: 60em', $client->getResponse()->getContent());
|
||||
$this->assertStringNotContainsString('line-height: 2em', $client->getResponse()->getContent());
|
||||
$this->assertStringNotContainsString('font-size: 2em', $client->getResponse()->getContent());
|
||||
|
||||
// Change display configuration
|
||||
$crawler = $client->request('GET', '/config');
|
||||
$this->assertSame(200, $client->getResponse()->getStatusCode());
|
||||
$form = $crawler->filter('button[id=config_save]')->form();
|
||||
$data = [
|
||||
'config[font]' => 'OpenDyslexicRegular',
|
||||
'config[fontsize]' => 2.0,
|
||||
'config[lineHeight]' => 2.0,
|
||||
'config[maxWidth]' => 60,
|
||||
];
|
||||
$client->submit($form, $data);
|
||||
$client->followRedirect();
|
||||
|
||||
$client->request('GET', '/view/' . $entry->getId());
|
||||
|
||||
$this->assertStringContainsString('font-family: "OpenDyslexicRegular"', $client->getResponse()->getContent());
|
||||
$this->assertStringContainsString('max-width: 60em', $client->getResponse()->getContent());
|
||||
$this->assertStringContainsString('line-height: 2em', $client->getResponse()->getContent());
|
||||
$this->assertStringContainsString('font-size: 2em', $client->getResponse()->getContent());
|
||||
}
|
||||
}
|
||||
|
||||
@ -673,7 +673,7 @@ class EntryControllerTest extends WallabagCoreTestCase
|
||||
->getRepository(Entry::class)
|
||||
->findOneById($entry->getId());
|
||||
|
||||
$this->assertSame(1, $res->isStarred());
|
||||
$this->assertTrue($res->isStarred());
|
||||
}
|
||||
|
||||
public function testDelete()
|
||||
@ -967,6 +967,34 @@ class EntryControllerTest extends WallabagCoreTestCase
|
||||
$this->assertCount(3, $crawler->filter('ol.entries > li'));
|
||||
}
|
||||
|
||||
public function testFilterOnNotCorrectlyParsedStatus()
|
||||
{
|
||||
$this->logInAs('admin');
|
||||
$client = $this->getTestClient();
|
||||
|
||||
$crawler = $client->request('GET', '/all/list');
|
||||
|
||||
$form = $crawler->filter('button[id=submit-filter]')->form();
|
||||
|
||||
$data = [
|
||||
'entry_filter[isNotParsed]' => true,
|
||||
];
|
||||
|
||||
$crawler = $client->submit($form, $data);
|
||||
|
||||
$this->assertCount(1, $crawler->filter($this->entryDataTestAttribute));
|
||||
|
||||
$entry = new Entry($this->getLoggedInUser());
|
||||
$entry->setUrl($this->url);
|
||||
$entry->setNotParsed(true);
|
||||
$this->getEntityManager()->persist($entry);
|
||||
$this->getEntityManager()->flush();
|
||||
|
||||
$crawler = $client->submit($form, $data);
|
||||
|
||||
$this->assertCount(2, $crawler->filter($this->entryDataTestAttribute));
|
||||
}
|
||||
|
||||
public function testPaginationWithFilter()
|
||||
{
|
||||
$this->logInAs('admin');
|
||||
@ -1390,6 +1418,18 @@ class EntryControllerTest extends WallabagCoreTestCase
|
||||
|
||||
$this->assertCount(4, $crawler->filter($this->entryDataTestAttribute));
|
||||
|
||||
// Add a check with useless spaces before and after the search term
|
||||
$crawler = $client->request('GET', '/unread/list');
|
||||
|
||||
$form = $crawler->filter('form[name=search]')->form();
|
||||
$data = [
|
||||
'search_entry[term]' => ' title ',
|
||||
];
|
||||
|
||||
$crawler = $client->submit($form, $data);
|
||||
|
||||
$this->assertCount(4, $crawler->filter($this->entryDataTestAttribute));
|
||||
|
||||
// Search on starred list
|
||||
$crawler = $client->request('GET', '/starred/list');
|
||||
|
||||
@ -1471,6 +1511,17 @@ class EntryControllerTest extends WallabagCoreTestCase
|
||||
$crawler = $client->submit($form, $data);
|
||||
|
||||
$this->assertCount(1, $crawler->filter($this->entryDataTestAttribute));
|
||||
|
||||
$crawler = $client->request('GET', '/unread/list');
|
||||
|
||||
$form = $crawler->filter('form[name=search]')->form();
|
||||
$data = [
|
||||
'search_entry[term]' => 'annotation',
|
||||
];
|
||||
|
||||
$crawler = $client->submit($form, $data);
|
||||
|
||||
$this->assertCount(2, $crawler->filter($this->entryDataTestAttribute));
|
||||
}
|
||||
|
||||
public function dataForLanguage()
|
||||
@ -1748,14 +1799,14 @@ class EntryControllerTest extends WallabagCoreTestCase
|
||||
->getRepository(Entry::class)
|
||||
->find($entry1->getId());
|
||||
|
||||
$this->assertSame(1, $res->isStarred());
|
||||
$this->assertTrue($res->isStarred());
|
||||
|
||||
$res = $client->getContainer()
|
||||
->get(EntityManagerInterface::class)
|
||||
->getRepository(Entry::class)
|
||||
->find($entry2->getId());
|
||||
|
||||
$this->assertSame(1, $res->isStarred());
|
||||
$this->assertTrue($res->isStarred());
|
||||
|
||||
// Mass actions : tag
|
||||
$client->request('POST', '/mass', [
|
||||
|
||||
@ -65,7 +65,7 @@ class ExportControllerTest extends WallabagCoreTestCase
|
||||
$this->logInAs('admin');
|
||||
$client = $this->getTestClient();
|
||||
|
||||
$client->request('GET', '/export/0.mobi');
|
||||
$client->request('GET', '/export/0.pdf');
|
||||
|
||||
$this->assertSame(404, $client->getResponse()->getStatusCode());
|
||||
}
|
||||
@ -80,7 +80,7 @@ class ExportControllerTest extends WallabagCoreTestCase
|
||||
->getRepository(Entry::class)
|
||||
->findOneByUsernameAndNotArchived('bob');
|
||||
|
||||
$client->request('GET', '/export/' . $content->getId() . '.mobi');
|
||||
$client->request('GET', '/export/' . $content->getId() . '.pdf');
|
||||
|
||||
$this->assertSame(404, $client->getResponse()->getStatusCode());
|
||||
}
|
||||
@ -102,28 +102,6 @@ class ExportControllerTest extends WallabagCoreTestCase
|
||||
$this->assertSame('binary', $headers->get('content-transfer-encoding'));
|
||||
}
|
||||
|
||||
public function testMobiExport()
|
||||
{
|
||||
$this->logInAs('admin');
|
||||
$client = $this->getTestClient();
|
||||
|
||||
$content = $client->getContainer()
|
||||
->get(EntityManagerInterface::class)
|
||||
->getRepository(Entry::class)
|
||||
->findOneByUsernameAndNotArchived('admin');
|
||||
|
||||
ob_start();
|
||||
$crawler = $client->request('GET', '/export/' . $content->getId() . '.mobi');
|
||||
ob_end_clean();
|
||||
|
||||
$this->assertSame(200, $client->getResponse()->getStatusCode());
|
||||
|
||||
$headers = $client->getResponse()->headers;
|
||||
$this->assertSame('application/x-mobipocket-ebook', $headers->get('content-type'));
|
||||
$this->assertSame('attachment; filename="' . $this->getSanitizedFilename($content->getTitle()) . '.mobi"', $headers->get('content-disposition'));
|
||||
$this->assertSame('binary', $headers->get('content-transfer-encoding'));
|
||||
}
|
||||
|
||||
public function testPdfExport()
|
||||
{
|
||||
$this->logInAs('admin');
|
||||
|
||||
@ -57,6 +57,7 @@ class ContentProxyTest extends TestCase
|
||||
$this->assertEmpty($entry->getLanguage());
|
||||
$this->assertSame(0.0, $entry->getReadingTime());
|
||||
$this->assertNull($entry->getDomainName());
|
||||
$this->assertTrue($entry->isNotParsed());
|
||||
}
|
||||
|
||||
public function testWithEmptyContent()
|
||||
@ -96,6 +97,7 @@ class ContentProxyTest extends TestCase
|
||||
$this->assertEmpty($entry->getLanguage());
|
||||
$this->assertSame(0.0, $entry->getReadingTime());
|
||||
$this->assertSame('0.0.0.0', $entry->getDomainName());
|
||||
$this->assertTrue($entry->isNotParsed());
|
||||
}
|
||||
|
||||
public function testWithEmptyContentButOG()
|
||||
@ -138,6 +140,7 @@ class ContentProxyTest extends TestCase
|
||||
$this->assertEmpty($entry->getMimetype());
|
||||
$this->assertSame(0.0, $entry->getReadingTime());
|
||||
$this->assertSame('domain.io', $entry->getDomainName());
|
||||
$this->assertTrue($entry->isNotParsed());
|
||||
}
|
||||
|
||||
public function testWithContent()
|
||||
@ -183,6 +186,7 @@ class ContentProxyTest extends TestCase
|
||||
$this->assertSame('200', $entry->getHttpStatus());
|
||||
$this->assertSame(4.0, $entry->getReadingTime());
|
||||
$this->assertSame('1.1.1.1', $entry->getDomainName());
|
||||
$this->assertFalse($entry->isNotParsed());
|
||||
}
|
||||
|
||||
public function testWithContentAndNoOgImage()
|
||||
@ -228,6 +232,7 @@ class ContentProxyTest extends TestCase
|
||||
$this->assertSame('200', $entry->getHttpStatus());
|
||||
$this->assertSame(4.0, $entry->getReadingTime());
|
||||
$this->assertSame('1.1.1.1', $entry->getDomainName());
|
||||
$this->assertFalse($entry->isNotParsed());
|
||||
}
|
||||
|
||||
public function testWithContentAndContentImage()
|
||||
@ -272,6 +277,7 @@ class ContentProxyTest extends TestCase
|
||||
$this->assertSame('200', $entry->getHttpStatus());
|
||||
$this->assertSame(0.0, $entry->getReadingTime());
|
||||
$this->assertSame('1.1.1.1', $entry->getDomainName());
|
||||
$this->assertFalse($entry->isNotParsed());
|
||||
}
|
||||
|
||||
public function testWithContentImageAndOgImage()
|
||||
@ -316,6 +322,7 @@ class ContentProxyTest extends TestCase
|
||||
$this->assertSame('200', $entry->getHttpStatus());
|
||||
$this->assertSame(0.0, $entry->getReadingTime());
|
||||
$this->assertSame('1.1.1.1', $entry->getDomainName());
|
||||
$this->assertFalse($entry->isNotParsed());
|
||||
}
|
||||
|
||||
public function testWithContentAndBadLanguage()
|
||||
@ -363,6 +370,7 @@ class ContentProxyTest extends TestCase
|
||||
$this->assertSame('200', $entry->getHttpStatus());
|
||||
$this->assertSame(4.0, $entry->getReadingTime());
|
||||
$this->assertSame('1.1.1.1', $entry->getDomainName());
|
||||
$this->assertFalse($entry->isNotParsed());
|
||||
}
|
||||
|
||||
public function testWithContentAndBadOgImage()
|
||||
@ -416,6 +424,7 @@ class ContentProxyTest extends TestCase
|
||||
$this->assertSame('200', $entry->getHttpStatus());
|
||||
$this->assertSame(4.0, $entry->getReadingTime());
|
||||
$this->assertSame('1.1.1.1', $entry->getDomainName());
|
||||
$this->assertFalse($entry->isNotParsed());
|
||||
}
|
||||
|
||||
public function testWithForcedContent()
|
||||
@ -460,6 +469,7 @@ class ContentProxyTest extends TestCase
|
||||
$this->assertContains('Thomas', $entry->getPublishedBy());
|
||||
$this->assertNotNull($entry->getHeaders(), 'Headers are stored, so value is not null');
|
||||
$this->assertContains('no-cache', $entry->getHeaders());
|
||||
$this->assertFalse($entry->isNotParsed());
|
||||
}
|
||||
|
||||
public function testWithForcedContentAndDateTime()
|
||||
@ -498,6 +508,7 @@ class ContentProxyTest extends TestCase
|
||||
$this->assertSame(4.0, $entry->getReadingTime());
|
||||
$this->assertSame('1.1.1.1', $entry->getDomainName());
|
||||
$this->assertSame('08/09/2016', $entry->getPublishedAt()->format('d/m/Y'));
|
||||
$this->assertFalse($entry->isNotParsed());
|
||||
}
|
||||
|
||||
public function testWithForcedContentAndBadDate()
|
||||
@ -537,6 +548,7 @@ class ContentProxyTest extends TestCase
|
||||
$this->assertSame(4.0, $entry->getReadingTime());
|
||||
$this->assertSame('1.1.1.1', $entry->getDomainName());
|
||||
$this->assertNull($entry->getPublishedAt());
|
||||
$this->assertFalse($entry->isNotParsed());
|
||||
|
||||
$records = $handler->getRecords();
|
||||
|
||||
@ -625,6 +637,7 @@ class ContentProxyTest extends TestCase
|
||||
$this->assertSame('fr', $entry->getLanguage());
|
||||
$this->assertSame('200', $entry->getHttpStatus());
|
||||
$this->assertSame('1.1.1.1', $entry->getDomainName());
|
||||
$this->assertFalse($entry->isNotParsed());
|
||||
}
|
||||
|
||||
public function testWithImageAsContent()
|
||||
@ -663,6 +676,7 @@ class ContentProxyTest extends TestCase
|
||||
$this->assertSame('image/jpeg', $entry->getMimetype());
|
||||
$this->assertSame('200', $entry->getHttpStatus());
|
||||
$this->assertSame('1.1.1.1', $entry->getDomainName());
|
||||
$this->assertFalse($entry->isNotParsed());
|
||||
}
|
||||
|
||||
public function testWebsiteWithValidUTF8TitleDoNothing()
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
namespace Tests\Wallabag\CoreBundle\Helper;
|
||||
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Routing\Router;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
|
||||
@ -12,7 +13,7 @@ use Wallabag\UserBundle\Entity\User;
|
||||
|
||||
class RedirectTest extends TestCase
|
||||
{
|
||||
/** @var \PHPUnit_Framework_MockObject_MockObject */
|
||||
/** @var Router&MockObject */
|
||||
private $routerMock;
|
||||
|
||||
/** @var Redirect */
|
||||
|
||||
@ -59,6 +59,7 @@ class WallabagExtensionTest extends TestCase
|
||||
$this->assertSame('lemonde.fr', $extension->removeScheme('lemonde.fr'));
|
||||
$this->assertSame('gist.github.com', $extension->removeScheme('gist.github.com'));
|
||||
$this->assertSame('gist.github.com', $extension->removeScheme('https://gist.github.com'));
|
||||
$this->assertSame('gist.github.com', $extension->removeScheme('http://gist.github.com'));
|
||||
}
|
||||
|
||||
public function testRemoveSchemeAndWww()
|
||||
@ -82,8 +83,10 @@ class WallabagExtensionTest extends TestCase
|
||||
$extension = new WallabagExtension($entryRepository, $tagRepository, $tokenStorage, 0, $translator, '');
|
||||
|
||||
$this->assertSame('lemonde.fr', $extension->removeSchemeAndWww('www.lemonde.fr'));
|
||||
$this->assertSame('lemonde.fr', $extension->removeSchemeAndWww('http://www.lemonde.fr'));
|
||||
$this->assertSame('lemonde.fr', $extension->removeSchemeAndWww('http://lemonde.fr'));
|
||||
$this->assertSame('lemonde.fr', $extension->removeSchemeAndWww('https://www.lemonde.fr'));
|
||||
$this->assertSame('lemonde.fr', $extension->removeSchemeAndWww('https://lemonde.fr'));
|
||||
$this->assertSame('gist.github.com', $extension->removeSchemeAndWww('https://gist.github.com'));
|
||||
$this->assertSame('ftp://gist.github.com', $extension->removeSchemeAndWww('ftp://gist.github.com'));
|
||||
}
|
||||
|
||||
@ -138,6 +138,8 @@ abstract class WallabagCoreTestCase extends WebTestCase
|
||||
$token = static::$kernel->getContainer()->get(TokenStorageInterface::class)->getToken();
|
||||
|
||||
if (null !== $token) {
|
||||
\assert($token->getUser() instanceof User);
|
||||
|
||||
return $token->getUser();
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user