Add ability to import/export tagging rules

- Add missing translations
- Add some tests
- Add `/api/taggingrule/export` API endpoint
- Add baggy theme
- Add error message when importing tagging rules failed
- Also fix all translations (I think we are good now)
This commit is contained in:
Jeremy Benoist 2019-06-26 22:31:47 +02:00
parent 92cd51aa2c
commit 34be2d5de4
No known key found for this signature in database
GPG key ID: BCA73962457ACC3C
25 changed files with 584 additions and 31 deletions

1
.gitignore vendored
View file

@ -32,6 +32,7 @@ web/uploads/
# Build
/app/build
/build
/coverage
# Composer PHAR
/composer.phar

View file

@ -0,0 +1,39 @@
<?php
namespace Wallabag\ApiBundle\Controller;
use JMS\Serializer\SerializationContext;
use JMS\Serializer\SerializerBuilder;
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use Symfony\Component\HttpFoundation\Response;
class TaggingRuleRestController extends WallabagRestController
{
/**
* Export all tagging rules as a json file.
*
* @ApiDoc()
*
* @return Response
*/
public function getTaggingruleExportAction()
{
$this->validateAuthentication();
$data = SerializerBuilder::create()->build()->serialize(
$this->getUser()->getConfig()->getTaggingRules(),
'json',
SerializationContext::create()->setGroups(['export_tagging_rule'])
);
return Response::create(
$data,
200,
[
'Content-type' => 'application/json',
'Content-Disposition' => 'attachment; filename="tagging_rules_' . $this->getUser()->getUsername() . '.json"',
'Content-Transfer-Encoding' => 'UTF-8',
]
);
}
}

View file

@ -13,6 +13,11 @@ tag:
resource: "WallabagApiBundle:TagRest"
name_prefix: api_
tagging_rule:
type: rest
resource: "WallabagApiBundle:TaggingRuleRest"
name_prefix: api_
annotation:
type: rest
resource: "WallabagApiBundle:AnnotationRest"

View file

@ -2,11 +2,14 @@
namespace Wallabag\CoreBundle\Controller;
use JMS\Serializer\SerializationContext;
use JMS\Serializer\SerializerBuilder;
use PragmaRX\Recovery\Recovery as BackupCodes;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Validator\Constraints\Locale as LocaleConstraint;
@ -15,6 +18,7 @@ use Wallabag\CoreBundle\Entity\TaggingRule;
use Wallabag\CoreBundle\Form\Type\ChangePasswordType;
use Wallabag\CoreBundle\Form\Type\ConfigType;
use Wallabag\CoreBundle\Form\Type\FeedType;
use Wallabag\CoreBundle\Form\Type\TaggingRuleImportType;
use Wallabag\CoreBundle\Form\Type\TaggingRuleType;
use Wallabag\CoreBundle\Form\Type\UserInformationType;
use Wallabag\CoreBundle\Tools\Utils;
@ -140,6 +144,37 @@ class ConfigController extends Controller
return $this->redirect($this->generateUrl('config') . '#set5');
}
// handle tagging rules import
$taggingRulesImportform = $this->createForm(TaggingRuleImportType::class);
$taggingRulesImportform->handleRequest($request);
if ($taggingRulesImportform->isSubmitted() && $taggingRulesImportform->isValid()) {
$message = 'flashes.config.notice.tagging_rules_not_imported';
$file = $taggingRulesImportform->get('file')->getData();
if (null !== $file && $file->isValid() && \in_array($file->getClientMimeType(), ['application/json', 'application/octet-stream'], true)) {
$content = json_decode(file_get_contents($file->getPathname()), true);
if (\is_array($content)) {
foreach ($content as $rule) {
$taggingRule = new TaggingRule();
$taggingRule->setRule($rule['rule']);
$taggingRule->setTags($rule['tags']);
$taggingRule->setConfig($config);
$em->persist($taggingRule);
}
$em->flush();
$message = 'flashes.config.notice.tagging_rules_imported';
}
}
$this->addFlash('notice', $message);
return $this->redirect($this->generateUrl('config') . '#set5');
}
return $this->render('WallabagCoreBundle:Config:index.html.twig', [
'form' => [
'config' => $configForm->createView(),
@ -147,6 +182,7 @@ class ConfigController extends Controller
'pwd' => $pwdForm->createView(),
'user' => $userForm->createView(),
'new_tagging_rule' => $newTaggingRule->createView(),
'import_tagging_rule' => $taggingRulesImportform->createView(),
],
'feed' => [
'username' => $user->getUsername(),
@ -492,6 +528,32 @@ class ConfigController extends Controller
return $this->redirect($request->headers->get('referer', $this->generateUrl('homepage')));
}
/**
* Export tagging rules for the logged in user.
*
* @Route("/tagging-rule/export", name="export_tagging_rule")
*
* @return Response
*/
public function exportTaggingRulesAction()
{
$data = SerializerBuilder::create()->build()->serialize(
$this->getUser()->getConfig()->getTaggingRules(),
'json',
SerializationContext::create()->setGroups(['export_tagging_rule'])
);
return Response::create(
$data,
200,
[
'Content-type' => 'application/json',
'Content-Disposition' => 'attachment; filename="tagging_rules_' . $this->getUser()->getUsername() . '.json"',
'Content-Transfer-Encoding' => 'UTF-8',
]
);
}
/**
* Remove all tags for given tags and a given user and cleanup orphan tags.
*

View file

@ -3,12 +3,16 @@
namespace Wallabag\CoreBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation\Exclude;
use JMS\Serializer\Annotation\Groups;
use JMS\Serializer\Annotation\XmlRoot;
use Symfony\Bridge\RulerZ\Validator\Constraints as RulerZAssert;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Tagging rule.
*
* @XmlRoot("tagging_rule")
* @ORM\Entity(repositoryClass="Wallabag\CoreBundle\Repository\TaggingRuleRepository")
* @ORM\Table(name="`tagging_rule`")
* @ORM\Entity
@ -34,6 +38,8 @@ class TaggingRule
* allowed_operators={">", "<", ">=", "<=", "=", "is", "!=", "and", "not", "or", "matches", "notmatches"}
* )
* @ORM\Column(name="rule", type="string", nullable=false)
*
* @Groups({"export_tagging_rule"})
*/
private $rule;
@ -42,10 +48,14 @@ class TaggingRule
*
* @Assert\NotBlank()
* @ORM\Column(name="tags", type="simple_array", nullable=false)
*
* @Groups({"export_tagging_rule"})
*/
private $tags = [];
/**
* @Exclude
*
* @ORM\ManyToOne(targetEntity="Wallabag\CoreBundle\Entity\Config", inversedBy="taggingRules")
*/
private $config;

View file

@ -0,0 +1,29 @@
<?php
namespace Wallabag\CoreBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
class TaggingRuleImportType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('file', FileType::class, [
'label' => 'config.form_rules.file_label',
'required' => true,
])
->add('import', SubmitType::class, [
'label' => 'config.form_rules.import_submit',
])
;
}
public function getBlockPrefix()
{
return 'upload_tagging_rule_file';
}
}

View file

@ -140,6 +140,15 @@ config:
# edit_rule_label: 'edit'
# rule_label: 'Rule'
# tags_label: 'Tags'
# card:
# new_tagging_rule: Create a tagging rule
# import_tagging_rules: Import tagging rules
# import_tagging_rules_detail: You have to select the JSON file you previously exported.
# export_tagging_rules: Export tagging rules
# export_tagging_rules_detail: This will download a JSON file that you can use to import tagging rules elsewhere or to backup them.
# file_label: JSON file
# import_submit: Import
# export: Export
# faq:
# title: 'FAQ'
# tagging_rules_definition_title: 'What does « tagging rules » mean?'
@ -602,6 +611,9 @@ flashes:
# tags_reset: Tags reset
# entries_reset: Entries reset
# archived_reset: Archived entries deleted
# otp_enabled: Two-factor authentication enabled
# tagging_rules_imported: Tagging rules imported
# tagging_rules_not_imported: Error while importing tagging rules
entry:
notice:
# entry_already_saved: 'Entry already saved on %date%'

View file

@ -140,6 +140,15 @@ config:
edit_rule_label: 'bearbeiten'
rule_label: 'Regel'
tags_label: 'Tags'
# card:
# new_tagging_rule: Create a tagging rule
# import_tagging_rules: Import tagging rules
# import_tagging_rules_detail: You have to select the JSON file you previously exported.
# export_tagging_rules: Export tagging rules
# export_tagging_rules_detail: This will download a JSON file that you can use to import tagging rules elsewhere or to backup them.
# file_label: JSON file
# import_submit: Import
# export: Export
faq:
title: 'FAQ'
tagging_rules_definition_title: 'Was bedeuten die "Tagging-Regeln"?'
@ -172,6 +181,15 @@ config:
and: 'Eine Regel UND eine andere'
matches: 'Testet, ob eine <i>Variable</i> auf eine <i>Suche</i> zutrifft (Groß- und Kleinschreibung wird nicht berücksichtigt).<br />Beispiel: <code>title matches "Fußball"</code>'
notmatches: 'Testet, ob ein <i>Titel</i> nicht auf eine <i>Suche</i> zutrifft (Groß- und Kleinschreibung wird nicht berücksichtigt).<br />Beispiel: <code>title notmatches "Fußball"</code>'
otp:
# page_title: Two-factor authentication
# app:
# two_factor_code_description_1: You just enabled the OTP two factor authentication, open your OTP app and use that code to get a one time password. It'll disapear after a page reload.
# two_factor_code_description_2: 'You can scan that QR Code with your app:'
# two_factor_code_description_3: 'Also, save these backup codes in a safe place, you can use them in case you lose access to your OTP app:'
# two_factor_code_description_4: 'Test an OTP code from your configured app:'
# cancel: Cancel
# enable: Enable
entry:
default_title: 'Titel des Eintrags'
@ -593,6 +611,9 @@ flashes:
tags_reset: Tags zurücksetzen
entries_reset: Einträge zurücksetzen
archived_reset: Archiverte Einträge zurücksetzen
# otp_enabled: Two-factor authentication enabled
# tagging_rules_imported: Tagging rules imported
# tagging_rules_not_imported: Error while importing tagging rules
entry:
notice:
entry_already_saved: 'Eintrag bereits am %date% gespeichert'

View file

@ -140,6 +140,15 @@ config:
edit_rule_label: 'edit'
rule_label: 'Rule'
tags_label: 'Tags'
card:
new_tagging_rule: Create a tagging rule
import_tagging_rules: Import tagging rules
import_tagging_rules_detail: You have to select the JSON file you previously exported.
export_tagging_rules: Export tagging rules
export_tagging_rules_detail: This will download a JSON file that you can use to import tagging rules elsewhere or to backup them.
file_label: JSON file
import_submit: Import
export: Export
faq:
title: 'FAQ'
tagging_rules_definition_title: 'What does « tagging rules » mean?'
@ -603,6 +612,8 @@ flashes:
entries_reset: Entries reset
archived_reset: Archived entries deleted
otp_enabled: Two-factor authentication enabled
tagging_rules_imported: Tagging rules imported
tagging_rules_not_imported: Error while importing tagging rules
entry:
notice:
entry_already_saved: 'Entry already saved on %date%'

View file

@ -140,6 +140,15 @@ config:
edit_rule_label: 'editar'
rule_label: 'Regla'
tags_label: 'Etiquetas'
# card:
# new_tagging_rule: Create a tagging rule
# import_tagging_rules: Import tagging rules
# import_tagging_rules_detail: You have to select the JSON file you previously exported.
# export_tagging_rules: Export tagging rules
# export_tagging_rules_detail: This will download a JSON file that you can use to import tagging rules elsewhere or to backup them.
# file_label: JSON file
# import_submit: Import
# export: Export
faq:
title: 'Preguntas frecuentes'
tagging_rules_definition_title: '¿Qué significa « reglas de etiquetado automático »?'
@ -602,6 +611,9 @@ flashes:
tags_reset: Etiquetas reiniciadas
entries_reset: Artículos reiniciados
# archived_reset: Archived entries deleted
# otp_enabled: Two-factor authentication enabled
# tagging_rules_imported: Tagging rules imported
# tagging_rules_not_imported: Error while importing tagging rules
entry:
notice:
entry_already_saved: 'Artículo ya guardado el %fecha%'

View file

@ -140,6 +140,15 @@ config:
# edit_rule_label: 'edit'
rule_label: 'قانون'
tags_label: 'برچسب‌ها'
# card:
# new_tagging_rule: Create a tagging rule
# import_tagging_rules: Import tagging rules
# import_tagging_rules_detail: You have to select the JSON file you previously exported.
# export_tagging_rules: Export tagging rules
# export_tagging_rules_detail: This will download a JSON file that you can use to import tagging rules elsewhere or to backup them.
# file_label: JSON file
# import_submit: Import
# export: Export
faq:
title: 'پرسش‌های متداول'
tagging_rules_definition_title: 'برچسب‌گذاری خودکار یعنی چه؟'
@ -602,6 +611,9 @@ flashes:
# tags_reset: Tags reset
# entries_reset: Entries reset
# archived_reset: Archived entries deleted
# otp_enabled: Two-factor authentication enabled
# tagging_rules_imported: Tagging rules imported
# tagging_rules_not_imported: Error while importing tagging rules
entry:
notice:
entry_already_saved: 'این مقاله در تاریخ %date% ذخیره شده بود'

View file

@ -140,6 +140,15 @@ config:
edit_rule_label: "éditer"
rule_label: "Règle"
tags_label: "Tags"
card:
new_tagging_rule: Créer une règle
import_tagging_rules: Importer des règles
import_tagging_rules_detail: Vous devez sélectionné un fichier JSON que vous avez précédemment exporté.
export_tagging_rules: Exporter les règles
export_tagging_rules_detail: Un fichier JSON sera téléchargé et vous pourrez l'utiliser pour ré-importer les règles ou comme sauvegarde.
file_label: Fichier JSON
import_submit: Importer
export: Export
faq:
title: "FAQ"
tagging_rules_definition_title: "Que signifient les règles de tag automatiques ?"
@ -604,6 +613,8 @@ flashes:
entries_reset: "Articles supprimés"
archived_reset: "Articles archivés supprimés"
otp_enabled: "Authentification à double-facteur activée"
tagging_rules_imported: Règles bien importées
tagging_rules_not_imported: Impossible d'importer les règles
entry:
notice:
entry_already_saved: "Article déjà sauvegardé le %date%"

View file

@ -105,6 +105,7 @@ config:
# login_label: 'Login (can not be changed)'
name_label: 'Nome'
email_label: 'E-mail'
two_factor:
# emailTwoFactor_label: 'Using email (receive a code by email)'
# googleTwoFactor_label: 'Using an OTP app (open the app, like Google Authenticator, Authy or FreeOTP, to get a one time code)'
# table_method: Method
@ -139,6 +140,15 @@ config:
edit_rule_label: 'modifica'
rule_label: 'Regola'
tags_label: 'Etichetta'
# card:
# new_tagging_rule: Create a tagging rule
# import_tagging_rules: Import tagging rules
# import_tagging_rules_detail: You have to select the JSON file you previously exported.
# export_tagging_rules: Export tagging rules
# export_tagging_rules_detail: This will download a JSON file that you can use to import tagging rules elsewhere or to backup them.
# file_label: JSON file
# import_submit: Import
# export: Export
faq:
title: 'FAQ'
tagging_rules_definition_title: 'Cosa significa « regole di etichettatura » ?'
@ -601,6 +611,9 @@ flashes:
tags_reset: Reset etichette
entries_reset: Reset articoli
# archived_reset: Archived entries deleted
# otp_enabled: Two-factor authentication enabled
# tagging_rules_imported: Tagging rules imported
# tagging_rules_not_imported: Error while importing tagging rules
entry:
notice:
entry_already_saved: 'Contenuto già salvato in data %date%'

View file

@ -105,6 +105,7 @@ config:
# login_label: 'Login (can not be changed)'
name_label: 'Nom'
email_label: 'Adreça de corrièl'
two_factor:
# emailTwoFactor_label: 'Using email (receive a code by email)'
# googleTwoFactor_label: 'Using an OTP app (open the app, like Google Authenticator, Authy or FreeOTP, to get a one time code)'
# table_method: Method
@ -139,6 +140,15 @@ config:
edit_rule_label: 'modificar'
rule_label: 'Règla'
tags_label: 'Etiquetas'
# card:
# new_tagging_rule: Create a tagging rule
# import_tagging_rules: Import tagging rules
# import_tagging_rules_detail: You have to select the JSON file you previously exported.
# export_tagging_rules: Export tagging rules
# export_tagging_rules_detail: This will download a JSON file that you can use to import tagging rules elsewhere or to backup them.
# file_label: JSON file
# import_submit: Import
# export: Export
faq:
title: 'FAQ'
tagging_rules_definition_title: "Qué significa las règlas d'etiquetas automaticas?"
@ -601,6 +611,9 @@ flashes:
tags_reset: Etiquetas levadas
entries_reset: Articles levats
archived_reset: Articles archivat suprimits
# otp_enabled: Two-factor authentication enabled
# tagging_rules_imported: Tagging rules imported
# tagging_rules_not_imported: Error while importing tagging rules
entry:
notice:
entry_already_saved: 'Article ja salvagardat lo %date%'

View file

@ -105,6 +105,7 @@ config:
# login_label: 'Login (can not be changed)'
name_label: 'Nazwa'
email_label: 'Adres email'
two_factor:
# emailTwoFactor_label: 'Using email (receive a code by email)'
# googleTwoFactor_label: 'Using an OTP app (open the app, like Google Authenticator, Authy or FreeOTP, to get a one time code)'
# table_method: Method
@ -139,6 +140,15 @@ config:
edit_rule_label: 'edytuj'
rule_label: 'Reguła'
tags_label: 'Tagi'
# card:
# new_tagging_rule: Create a tagging rule
# import_tagging_rules: Import tagging rules
# import_tagging_rules_detail: You have to select the JSON file you previously exported.
# export_tagging_rules: Export tagging rules
# export_tagging_rules_detail: This will download a JSON file that you can use to import tagging rules elsewhere or to backup them.
# file_label: JSON file
# import_submit: Import
# export: Export
faq:
title: 'FAQ'
tagging_rules_definition_title: 'Co oznaczają « reguły tagowania » ?'
@ -601,6 +611,9 @@ flashes:
tags_reset: Zresetuj tagi
entries_reset: Zresetuj wpisy
archived_reset: Zarchiwizowane wpisy usunięte
# otp_enabled: Two-factor authentication enabled
# tagging_rules_imported: Tagging rules imported
# tagging_rules_not_imported: Error while importing tagging rules
entry:
notice:
entry_already_saved: 'Wpis już został dodany %date%'

View file

@ -105,6 +105,7 @@ config:
# login_label: 'Login (can not be changed)'
name_label: 'Nome'
email_label: 'E-mail'
two_factor:
# emailTwoFactor_label: 'Using email (receive a code by email)'
# googleTwoFactor_label: 'Using an OTP app (open the app, like Google Authenticator, Authy or FreeOTP, to get a one time code)'
# table_method: Method
@ -139,6 +140,15 @@ config:
edit_rule_label: 'editar'
rule_label: 'Regras'
tags_label: 'Tags'
# card:
# new_tagging_rule: Create a tagging rule
# import_tagging_rules: Import tagging rules
# import_tagging_rules_detail: You have to select the JSON file you previously exported.
# export_tagging_rules: Export tagging rules
# export_tagging_rules_detail: This will download a JSON file that you can use to import tagging rules elsewhere or to backup them.
# file_label: JSON file
# import_submit: Import
# export: Export
faq:
title: 'FAQ'
tagging_rules_definition_title: 'O que as « regras de tags » significam?'
@ -601,6 +611,9 @@ flashes:
# tags_reset: Tags reset
# entries_reset: Entries reset
# archived_reset: Archived entries deleted
# otp_enabled: Two-factor authentication enabled
# tagging_rules_imported: Tagging rules imported
# tagging_rules_not_imported: Error while importing tagging rules
entry:
notice:
entry_already_saved: 'Entrada já foi salva em %date%'

View file

@ -105,6 +105,7 @@ config:
# login_label: 'Login (can not be changed)'
name_label: 'Nume'
email_label: 'E-mail'
two_factor:
# emailTwoFactor_label: 'Using email (receive a code by email)'
# googleTwoFactor_label: 'Using an OTP app (open the app, like Google Authenticator, Authy or FreeOTP, to get a one time code)'
# table_method: Method
@ -139,6 +140,15 @@ config:
# edit_rule_label: 'edit'
# rule_label: 'Rule'
# tags_label: 'Tags'
# card:
# new_tagging_rule: Create a tagging rule
# import_tagging_rules: Import tagging rules
# import_tagging_rules_detail: You have to select the JSON file you previously exported.
# export_tagging_rules: Export tagging rules
# export_tagging_rules_detail: This will download a JSON file that you can use to import tagging rules elsewhere or to backup them.
# file_label: JSON file
# import_submit: Import
# export: Export
# faq:
# title: 'FAQ'
# tagging_rules_definition_title: 'What does « tagging rules » mean?'
@ -429,9 +439,9 @@ tag:
rename:
# placeholder: 'You can update tag name.'
# export:
# footer_template: '<div style="text-align:center;"><p>Produced by wallabag with %method%</p><p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p></div>'
# unknown: 'Unknown'
export:
# footer_template: '<div style="text-align:center;"><p>Produced by wallabag with %method%</p><p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p></div>'
# unknown: 'Unknown'
import:
# page_title: 'Import'
@ -601,6 +611,9 @@ flashes:
# tags_reset: Tags reset
# entries_reset: Entries reset
# archived_reset: Archived entries deleted
# otp_enabled: Two-factor authentication enabled
# tagging_rules_imported: Tagging rules imported
# tagging_rules_not_imported: Error while importing tagging rules
entry:
notice:
# entry_already_saved: 'Entry already saved on %date%'

View file

@ -80,6 +80,7 @@ config:
redirect_current_page: 'На текущую страницу'
pocket_consumer_key_label: "Ключ от Pocket для импорта контента"
android_configuration: "Настройте Ваше Android приложение"
# android_instruction: "Touch here to prefill your Android application"
help_theme: "wallabag настраиваемый, здесь Вы можете выбрать тему."
help_items_per_page: "Вы можете выбрать количество отображаемых записей на странице."
help_reading_speed: "wallabag посчитает сколько времени занимает чтение каждой записи. Вы можете определить здесь, как быстро вы читаете. wallabag пересчитает время чтения для каждой записи."
@ -97,12 +98,14 @@ config:
unread: 'непрочитанные'
starred: 'помеченные'
archive: 'архивные'
# all: 'All'
feed_limit: 'Количество записей в фиде'
form_user:
# two_factor_description: "Enabling two factor authentication means you'll receive an email with a code OR need to use an OTP app (like Google Authenticator, Authy or FreeOTP) to get a one time code on every new untrusted connection. You can't choose both option."
# login_label: 'Login (can not be changed)'
name_label: 'Имя'
email_label: 'Email'
two_factor:
# emailTwoFactor_label: 'Using email (receive a code by email)'
# googleTwoFactor_label: 'Using an OTP app (open the app, like Google Authenticator, Authy or FreeOTP, to get a one time code)'
# table_method: Method
@ -123,6 +126,7 @@ config:
annotations: "Удалить все аннотации"
tags: "Удалить все теги"
entries: "Удалить все записи"
# archived: Remove ALL archived entries
confirm: "Вы уверены? (Данные будут БЕЗВОЗВРАТНО удалены, эти действия необратимы)"
form_password:
description: "Здесь Вы можете поменять своя пароль. Ваш пароль должен быть длиннее 8 символов."
@ -136,6 +140,15 @@ config:
edit_rule_label: 'изменить'
rule_label: 'Правило'
tags_label: 'теги'
# card:
# new_tagging_rule: Create a tagging rule
# import_tagging_rules: Import tagging rules
# import_tagging_rules_detail: You have to select the JSON file you previously exported.
# export_tagging_rules: Export tagging rules
# export_tagging_rules_detail: This will download a JSON file that you can use to import tagging rules elsewhere or to backup them.
# file_label: JSON file
# import_submit: Import
# export: Export
faq:
title: 'FAQ'
tagging_rules_definition_title: 'Что значит "правило тегирования"?'
@ -167,6 +180,7 @@ config:
or: 'Одно правило ИЛИ другое'
and: 'Одно правило И другое'
matches: 'Тесты, в которых <i> тема </i> соответствует <i> поиску </i> (без учета регистра). Пример: <code> title matches "футбол" </code>'
# notmatches: 'Tests that a <i>subject</i> doesn''t match match a <i>search</i> (case-insensitive).<br />Example: <code>title notmatches "football"</code>'
otp:
# page_title: Two-factor authentication
# app:
@ -187,6 +201,7 @@ entry:
filtered_tags: 'Отфильтрованные по тегу:'
filtered_search: 'Отфильтрованные по поиску:'
untagged: 'Записи без тегов'
# all: 'All entries'
list:
number_on_the_page: '{0} Записей не обнаружено.|{1} Одна запись.|]1,Inf[ Найдено %count% записей.'
reading_time: 'расчетное время чтения'
@ -208,6 +223,8 @@ entry:
unread_label: 'Непрочитанная'
preview_picture_label: 'Есть картинка предварительного просмотра'
preview_picture_help: 'Картинка предварительного просмотра'
# is_public_label: 'Has a public link'
# is_public_help: 'Public link'
language_label: 'Язык'
http_status_label: 'статус HTTP'
reading_time:
@ -246,6 +263,8 @@ entry:
original_article: 'оригинал'
annotations_on_the_entry: '{0} Нет аннотации|{1} Одна аннотация|]1,Inf[ %count% аннотаций'
created_at: 'Дата создания'
# published_at: 'Publication date'
# published_by: 'Published by'
# provided_by: 'Provided by'
new:
page_title: 'Сохранить новую запись'
@ -259,10 +278,12 @@ entry:
title_label: 'Название'
url_label: 'Ссылка'
# origin_url_label: 'Origin url (from where you found that entry)'
is_public_label: 'Публичная'
save_label: 'Сохранить'
public:
shared_by_wallabag: "Запись была опубликована <a href='%wallabag_instance%'>wallabag</a>"
confirm:
# delete: "Are you sure you want to remove that article?"
# delete_tag: "Are you sure you want to remove that tag from that article?"
metadata:
# reading_time: "Estimated reading time"
# reading_time_minutes_short: "%readingTime% min"
@ -418,9 +439,9 @@ tag:
rename:
# placeholder: 'You can update tag name.'
# export:
# footer_template: '<div style="text-align:center;"><p>Produced by wallabag with %method%</p><p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p></div>'
# unknown: 'Unknown'
export:
# footer_template: '<div style="text-align:center;"><p>Produced by wallabag with %method%</p><p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p></div>'
# unknown: 'Unknown'
import:
page_title: 'Импорт'
@ -548,6 +569,28 @@ user:
delete: "Удалить"
delete_confirm: "Вы уверены?"
back_to_list: "Назад к списку"
search:
# placeholder: Filter by login or email
site_credential:
# page_title: Site credentials management
# new_site_credential: Create a credential
# edit_site_credential: Edit an existing credential
# description: "Here you can manage all credentials for sites which required them (create, edit and delete), like a paywall, an authentication, etc."
# list:
# actions: Actions
# edit_action: Edit
# yes: Yes
# no: No
# create_new_one: Create a new credential
# form:
# username_label: 'Login'
# host_label: 'Host (subdomain.example.org, .example.org, etc.)'
# password_label: 'Password'
# save: Save
# delete: Delete
# delete_confirm: Are you sure?
# back_to_list: Back to list
error:
page_title: "Произошла ошибка"
@ -567,6 +610,10 @@ flashes:
annotations_reset: "Аннотации сброшены"
tags_reset: "Теги сброшены"
entries_reset: "Записи сброшены"
# archived_reset: Archived entries deleted
# otp_enabled: Two-factor authentication enabled
# tagging_rules_imported: Tagging rules imported
# tagging_rules_not_imported: Error while importing tagging rules
entry:
notice:
entry_already_saved: 'Запись была сохранена ранее %date%'
@ -603,3 +650,8 @@ flashes:
added: 'Пользователь "%username%" добавлен'
updated: 'Пользователь "%username%" обновлен'
deleted: 'Пользователь "%username%" удален'
site_credential:
notice:
# added: 'Site credential for "%host%" added'
# updated: 'Site credential for "%host%" updated'
# deleted: 'Site credential for "%host%" deleted'

View file

@ -105,6 +105,7 @@ config:
# login_label: 'Login (can not be changed)'
name_label: 'ชื่อ'
email_label: 'อีเมล'
two_factor:
# emailTwoFactor_label: 'Using email (receive a code by email)'
# googleTwoFactor_label: 'Using an OTP app (open the app, like Google Authenticator, Authy or FreeOTP, to get a one time code)'
# table_method: Method
@ -139,6 +140,15 @@ config:
edit_rule_label: 'ปรับแก้'
rule_label: 'ข้อบังคับ'
tags_label: 'แท็ก'
# card:
# new_tagging_rule: Create a tagging rule
# import_tagging_rules: Import tagging rules
# import_tagging_rules_detail: You have to select the JSON file you previously exported.
# export_tagging_rules: Export tagging rules
# export_tagging_rules_detail: This will download a JSON file that you can use to import tagging rules elsewhere or to backup them.
# file_label: JSON file
# import_submit: Import
# export: Export
faq:
title: 'FAQ'
tagging_rules_definition_title: 'ข้อบังคับการแท็กคืออะไร?'
@ -255,6 +265,7 @@ entry:
created_at: 'วันที่สร้าง'
published_at: 'วันที่ประกาศ'
published_by: 'ประกาศโดย'
# provided_by: 'Provided by'
new:
page_title: 'บันทึกรายการใหม่'
placeholder: 'http://website.com'
@ -266,6 +277,7 @@ entry:
page_title: 'แก้ไขรายการ'
title_label: 'หัวข้อ'
url_label: 'Url'
# origin_url_label: 'Origin url (from where you found that entry)'
save_label: 'บันทึก'
public:
shared_by_wallabag: "บทความนี้จะมีการแชร์โดย %username% กับ <a href='%wallabag_instance%'>wallabag</a>"
@ -599,6 +611,9 @@ flashes:
tags_reset: รีเซ็ตแท็ก
entries_reset: รีเซ็ตรายการ
archived_reset: การลบเอกสารของรายการ
# otp_enabled: Two-factor authentication enabled
# tagging_rules_imported: Tagging rules imported
# tagging_rules_not_imported: Error while importing tagging rules
entry:
notice:
entry_already_saved: 'รายการพร้อมบันทึกที่ %date%'

View file

@ -105,6 +105,7 @@ config:
# login_label: 'Login (can not be changed)'
name_label: 'İsim'
email_label: 'E-posta'
two_factor:
# emailTwoFactor_label: 'Using email (receive a code by email)'
# googleTwoFactor_label: 'Using an OTP app (open the app, like Google Authenticator, Authy or FreeOTP, to get a one time code)'
# table_method: Method
@ -139,6 +140,15 @@ config:
# edit_rule_label: 'edit'
rule_label: 'Kural'
tags_label: 'Etiketler'
# card:
# new_tagging_rule: Create a tagging rule
# import_tagging_rules: Import tagging rules
# import_tagging_rules_detail: You have to select the JSON file you previously exported.
# export_tagging_rules: Export tagging rules
# export_tagging_rules_detail: This will download a JSON file that you can use to import tagging rules elsewhere or to backup them.
# file_label: JSON file
# import_submit: Import
# export: Export
faq:
title: 'S.S.S.'
tagging_rules_definition_title: '« etiketleme kuralları » ne anlama geliyor?'
@ -213,6 +223,8 @@ entry:
unread_label: 'Okunmayan'
preview_picture_label: 'Resim önizlemesi varsa'
preview_picture_help: 'Resim önizlemesi'
# is_public_label: 'Has a public link'
# is_public_help: 'Public link'
language_label: 'Dil'
# http_status_label: 'HTTP status'
reading_time:
@ -427,9 +439,9 @@ tag:
rename:
# placeholder: 'You can update tag name.'
# export:
# footer_template: '<div style="text-align:center;"><p>Produced by wallabag with %method%</p><p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p></div>'
# unknown: 'Unknown'
export:
# footer_template: '<div style="text-align:center;"><p>Produced by wallabag with %method%</p><p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p></div>'
# unknown: 'Unknown'
import:
page_title: 'İçe Aktar'
@ -560,6 +572,26 @@ user:
search:
# placeholder: Filter by username or email
site_credential:
# page_title: Site credentials management
# new_site_credential: Create a credential
# edit_site_credential: Edit an existing credential
# description: "Here you can manage all credentials for sites which required them (create, edit and delete), like a paywall, an authentication, etc."
# list:
# actions: Actions
# edit_action: Edit
# yes: Yes
# no: No
# create_new_one: Create a new credential
# form:
# username_label: 'Login'
# host_label: 'Host (subdomain.example.org, .example.org, etc.)'
# password_label: 'Password'
# save: Save
# delete: Delete
# delete_confirm: Are you sure?
# back_to_list: Back to list
error:
# page_title: An error occurred
@ -571,13 +603,17 @@ flashes:
password_not_updated_demo: "In demonstration mode, you can't change password for this user."
user_updated: 'Bilgiler güncellendi'
feed_updated: 'RSS bilgiler güncellendi'
tagging_rules_updated: 'Tagging rules updated'
tagging_rules_deleted: 'Tagging rule deleted'
feed_token_updated: 'RSS token updated'
# tagging_rules_updated: 'Tagging rules updated'
# tagging_rules_deleted: 'Tagging rule deleted'
# feed_token_updated: 'RSS token updated'
# feed_token_revoked: 'RSS token revoked'
# annotations_reset: Annotations reset
# tags_reset: Tags reset
# entries_reset: Entries reset
# archived_reset: Archived entries deleted
# otp_enabled: Two-factor authentication enabled
# tagging_rules_imported: Tagging rules imported
# tagging_rules_not_imported: Error while importing tagging rules
entry:
notice:
entry_already_saved: 'Entry already saved on %date%'

View file

@ -291,6 +291,34 @@
{{ form_rest(form.new_tagging_rule) }}
</form>
<div class="row">
<h3>{{ 'config.form_rules.card.import_tagging_rules'|trans }}</h3>
<p>{{ 'config.form_rules.card.import_tagging_rules_detail'|trans }}</p>
</div>
{{ form_start(form.import_tagging_rule) }}
{{ form_errors(form.import_tagging_rule) }}
<fieldset class="w500p inline">
<div class="row">
{{ form_label(form.import_tagging_rule.file) }}
{{ form_errors(form.import_tagging_rule.file) }}
{{ form_widget(form.import_tagging_rule.file) }}
</div>
</fieldset>
{{ form_rest(form.import_tagging_rule) }}
</form>
{% if app.user.config.taggingRules is not empty %}
<div class="row">
<h3>{{ 'config.form_rules.card.export_tagging_rules'|trans }}</h3>
<p>{{ 'config.form_rules.card.export_tagging_rules_detail'|trans }}</p>
<p><a href="{{ path('export_tagging_rule') }}" class="waves-effect waves-light btn">{{ 'config.form_rules.export'|trans }}</a></p>
</div>
{% endif %}
<div class="row">
<div class="input-field col s12">
<h3>{{ 'config.form_rules.faq.title'|trans }}</h3>

View file

@ -307,28 +307,77 @@
</div>
{% endif %}
{{ form_start(form.new_tagging_rule) }}
{{ form_errors(form.new_tagging_rule) }}
<ul class="row">
<li class="col l6 m6 s12">
<div class="card">
<div class="card-content">
<span class="card-title">{{ 'config.form_rules.card.new_tagging_rule'|trans }}</span>
<div class="row">
<div class="input-field col s12">
{{ form_label(form.new_tagging_rule.rule) }}
{{ form_errors(form.new_tagging_rule.rule) }}
{{ form_widget(form.new_tagging_rule.rule) }}
{{ form_start(form.new_tagging_rule) }}
{{ form_errors(form.new_tagging_rule) }}
<div class="row">
<div class="input-field col s12">
{{ form_label(form.new_tagging_rule.rule) }}
{{ form_errors(form.new_tagging_rule.rule) }}
{{ form_widget(form.new_tagging_rule.rule) }}
</div>
</div>
<div class="row">
<div class="input-field col s12">
{{ form_label(form.new_tagging_rule.tags) }}
{{ form_errors(form.new_tagging_rule.tags) }}
{{ form_widget(form.new_tagging_rule.tags) }}
</div>
</div>
{{ form_widget(form.new_tagging_rule.save, {'attr': {'class': 'btn waves-effect waves-light'}}) }}
{{ form_rest(form.new_tagging_rule) }}
</form>
</div>
</div>
</div>
</li>
<li class="col l6 m6 s12">
<div class="card z-depth-1">
<div class="card-content">
<span class="card-title">{{ 'config.form_rules.card.import_tagging_rules'|trans }}</span>
<p>{{ 'config.form_rules.card.import_tagging_rules_detail'|trans }}</p>
{{ form_start(form.import_tagging_rule) }}
{{ form_errors(form.import_tagging_rule) }}
<div class="row">
<div class="file-field input-field col s12">
{{ form_errors(form.import_tagging_rule.file) }}
<div class="btn">
<span>{{ form.import_tagging_rule.file.vars.label|trans }}</span>
{{ form_widget(form.import_tagging_rule.file) }}
</div>
<div class="file-path-wrapper">
<input class="file-path validate" type="text">
</div>
</div>
</div>
<div class="row">
<div class="input-field col s12">
{{ form_label(form.new_tagging_rule.tags) }}
{{ form_errors(form.new_tagging_rule.tags) }}
{{ form_widget(form.new_tagging_rule.tags) }}
{{ form_widget(form.import_tagging_rule.import, { 'attr': {'class': 'btn waves-effect waves-light'} }) }}
{{ form_rest(form.import_tagging_rule) }}
</form>
</div>
</div>
</div>
{{ form_widget(form.new_tagging_rule.save, {'attr': {'class': 'btn waves-effect waves-light'}}) }}
{{ form_rest(form.new_tagging_rule) }}
</form>
</li>
{% if app.user.config.taggingRules is not empty %}
<li class="col l6 m6 s12">
<div class="card z-depth-1">
<div class="card-content">
<span class="card-title">{{ 'config.form_rules.card.export_tagging_rules'|trans }}</span>
<p>{{ 'config.form_rules.card.export_tagging_rules_detail'|trans }}</p>
<br/>
<p><a href="{{ path('export_tagging_rule') }}" class="waves-effect waves-light btn">{{ 'config.form_rules.export'|trans }}</a></p>
</div>
</div>
</li>
{% endif %}
</ul>
<div class="row">
<div class="input-field col s12">

View file

@ -0,0 +1,15 @@
<?php
namespace Tests\Wallabag\ApiBundle\Controller;
use Tests\Wallabag\ApiBundle\WallabagApiTestCase;
class TaggingRuleRestControllerTest extends WallabagApiTestCase
{
public function testExportEntry()
{
$this->client->request('GET', '/api/taggingrule/export');
$this->assertSame(200, $this->client->getResponse()->getStatusCode());
$this->assertSame('application/json', $this->client->getResponse()->headers->get('Content-Type'));
}
}

View file

@ -2,6 +2,7 @@
namespace Tests\Wallabag\CoreBundle\Controller;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Tests\Wallabag\CoreBundle\WallabagCoreTestCase;
use Wallabag\AnnotationBundle\Entity\Annotation;
use Wallabag\CoreBundle\Entity\Config;
@ -1097,4 +1098,67 @@ class ConfigControllerTest extends WallabagCoreTestCase
$this->assertFalse($user->isGoogleTwoFactor());
$this->assertEmpty($user->getBackupCodes());
}
public function testExportTaggingRule()
{
$this->logInAs('admin');
$client = $this->getClient();
ob_start();
$crawler = $client->request('GET', '/tagging-rule/export');
ob_end_clean();
$this->assertSame(200, $client->getResponse()->getStatusCode());
$headers = $client->getResponse()->headers;
$this->assertSame('application/json', $headers->get('content-type'));
$this->assertSame('attachment; filename="tagging_rules_admin.json"', $headers->get('content-disposition'));
$this->assertSame('UTF-8', $headers->get('content-transfer-encoding'));
$content = json_decode($client->getResponse()->getContent(), true);
$this->assertCount(4, $content);
$this->assertSame('content matches "spurs"', $content[0]['rule']);
$this->assertSame('sport', $content[0]['tags'][0]);
}
public function testImportTagginfRuleBadFile()
{
$this->logInAs('admin');
$client = $this->getClient();
$crawler = $client->request('GET', '/config');
$form = $crawler->filter('form[name=upload_tagging_rule_file] > button[type=submit]')->form();
$data = [
'upload_tagging_rule_file[file]' => '',
];
$client->submit($form, $data);
$this->assertSame(302, $client->getResponse()->getStatusCode());
}
public function testImportTagginfRuleFile()
{
$this->logInAs('admin');
$client = $this->getClient();
$crawler = $client->request('GET', '/config');
$form = $crawler->filter('form[name=upload_tagging_rule_file] > button[type=submit]')->form();
$file = new UploadedFile(__DIR__ . '/../fixtures/tagging_rules_admin.json', 'tagging_rules_admin.json');
$data = [
'upload_tagging_rule_file[file]' => $file,
];
$client->submit($form, $data);
$this->assertSame(302, $client->getResponse()->getStatusCode());
$user = $client->getContainer()->get('fos_user.user_manager.test')->findUserBy(['username' => 'admin']);
$taggingRules = $user->getConfig()->getTaggingRules()->toArray();
$this->assertCount(5, $taggingRules);
$this->assertSame('title matches "football"', $taggingRules[4]->getRule());
}
}

View file

@ -0,0 +1,4 @@
[{
"rule": "title matches \"football\"",
"tags": ["football"]
}]