mirror of
https://github.com/wallabag/wallabag.git
synced 2025-07-22 17:18:37 +00:00
Merge commit from fork
Protect actions with a CSRF token
This commit is contained in:
commit
99c8a06594
25 changed files with 565 additions and 244 deletions
|
@ -177,6 +177,7 @@ a.original:not(.waves-effect) {
|
||||||
.card-entry-tags a,
|
.card-entry-tags a,
|
||||||
.card-entry-labels a,
|
.card-entry-labels a,
|
||||||
.card-tag-labels a,
|
.card-tag-labels a,
|
||||||
|
.card-tag-labels button,
|
||||||
.card-entry-labels-hidden a,
|
.card-entry-labels-hidden a,
|
||||||
#list .chip a {
|
#list .chip a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
|
|
@ -62,7 +62,9 @@
|
||||||
.nav-panels .input-field input:focus,
|
.nav-panels .input-field input:focus,
|
||||||
.results-item,
|
.results-item,
|
||||||
.side-nav li > a,
|
.side-nav li > a,
|
||||||
.side-nav li > a > i.material-icons {
|
.side-nav li > a > i.material-icons,
|
||||||
|
.side-nav li button,
|
||||||
|
.side-nav li button > i.material-icons {
|
||||||
color: #dfdfdf;
|
color: #dfdfdf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,6 +89,7 @@
|
||||||
|
|
||||||
.mass-action-tags .mass-action-tags-input.mass-action-tags-input,
|
.mass-action-tags .mass-action-tags-input.mass-action-tags-input,
|
||||||
.side-nav li:not(.logo) > a:hover,
|
.side-nav li:not(.logo) > a:hover,
|
||||||
|
.side-nav li:not(.logo) button:hover,
|
||||||
.side-nav .collapsible-header:hover,
|
.side-nav .collapsible-header:hover,
|
||||||
.side-nav.fixed .collapsible-header:hover {
|
.side-nav.fixed .collapsible-header:hover {
|
||||||
background-color: #1d1d1d;
|
background-color: #1d1d1d;
|
||||||
|
|
|
@ -6,11 +6,32 @@ nav {
|
||||||
line-height: initial;
|
line-height: initial;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// adapted from anchor styles from node_modules/materialize-css/sass/components/_navbar.scss
|
||||||
|
nav ul button {
|
||||||
|
transition: background-color .3s;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: #fff;
|
||||||
|
display: block;
|
||||||
|
padding: 0 15px;
|
||||||
|
cursor: pointer;
|
||||||
|
background: none;
|
||||||
|
border: 0;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(0 0 0 / 10%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
nav {
|
nav {
|
||||||
input {
|
input {
|
||||||
color: #aaa;
|
color: #aaa;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ul button:hover,
|
||||||
ul a:hover {
|
ul a:hover {
|
||||||
background-color: initial;
|
background-color: initial;
|
||||||
}
|
}
|
||||||
|
@ -34,6 +55,7 @@ nav {
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
|
button,
|
||||||
a {
|
a {
|
||||||
padding: 10px 15px;
|
padding: 10px 15px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
background: initial;
|
background: initial;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
& button > i.material-icons.theme-toggle-icon,
|
||||||
& > a > i.material-icons.theme-toggle-icon {
|
& > a > i.material-icons.theme-toggle-icon {
|
||||||
float: none;
|
float: none;
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
|
@ -22,6 +23,7 @@
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.fixed button,
|
||||||
&.fixed a {
|
&.fixed a {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
line-height: 44px;
|
line-height: 44px;
|
||||||
|
@ -41,7 +43,35 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.bold > a {
|
// adapted from anchor styles from node_modules/materialize-css/sass/components/_sideNav.scss
|
||||||
|
.side-nav li button {
|
||||||
|
color: rgba(0 0 0 / 87%);
|
||||||
|
display: block;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
height: 48px;
|
||||||
|
line-height: 48px;
|
||||||
|
padding: 0 (16px * 2);
|
||||||
|
width: 100%;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(0 0 0 / 5%);
|
||||||
|
}
|
||||||
|
|
||||||
|
& > i,
|
||||||
|
& > i.material-icons {
|
||||||
|
float: left;
|
||||||
|
height: 48px;
|
||||||
|
line-height: 48px;
|
||||||
|
margin: 0 (16px * 2) 0 0;
|
||||||
|
width: 24px;
|
||||||
|
color: rgba(0 0 0 / 54%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bold > a,
|
||||||
|
.bold > button {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,3 +38,18 @@ nav .input-field input {
|
||||||
.tab {
|
.tab {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-link {
|
||||||
|
background: none;
|
||||||
|
border: 0;
|
||||||
|
padding: 0;
|
||||||
|
color: $blue-accent-color;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-block {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
|
@ -228,10 +228,10 @@ $(document).ready(() => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
$('form[name="form_mass_action"] input[name="tags"]').on('keydown', (e) => {
|
$('input[name="tags"][form="form_mass_action"]').on('keydown', (e) => {
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
$('form[name="form_mass_action"] button[name="tag"]').trigger('click');
|
$('button[name="tag"][form="form_mass_action"]').trigger('click');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -10,17 +10,17 @@ $(document).ready(() => {
|
||||||
|
|
||||||
/* mark as favorite */
|
/* mark as favorite */
|
||||||
Mousetrap.bind('f', () => {
|
Mousetrap.bind('f', () => {
|
||||||
$('ul.side-nav a.favorite i')[0].click();
|
$('ul.side-nav button.favorite i')[0].click();
|
||||||
});
|
});
|
||||||
|
|
||||||
/* mark as read */
|
/* mark as read */
|
||||||
Mousetrap.bind('a', () => {
|
Mousetrap.bind('a', () => {
|
||||||
$('ul.side-nav a.markasread i')[0].click();
|
$('ul.side-nav button.markasread i')[0].click();
|
||||||
});
|
});
|
||||||
|
|
||||||
/* delete */
|
/* delete */
|
||||||
Mousetrap.bind('del', () => {
|
Mousetrap.bind('del', () => {
|
||||||
$('ul.side-nav a.delete i')[0].click();
|
$('ul.side-nav button.delete i')[0].click();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -30,6 +30,7 @@ framework:
|
||||||
handler_id: session.handler.native_file
|
handler_id: session.handler.native_file
|
||||||
save_path: "%kernel.project_dir%/var/sessions/%kernel.environment%"
|
save_path: "%kernel.project_dir%/var/sessions/%kernel.environment%"
|
||||||
cookie_secure: auto
|
cookie_secure: auto
|
||||||
|
cookie_samesite: lax
|
||||||
fragments: ~
|
fragments: ~
|
||||||
http_method_override: true
|
http_method_override: true
|
||||||
assets: ~
|
assets: ~
|
||||||
|
|
|
@ -7,6 +7,7 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||||
use Symfony\Component\Routing\Annotation\Route;
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
use Wallabag\ApiBundle\Entity\Client;
|
use Wallabag\ApiBundle\Entity\Client;
|
||||||
|
@ -76,7 +77,7 @@ class DeveloperController extends AbstractController
|
||||||
public function deleteClientAction(Request $request, Client $client, EntityManagerInterface $entityManager, TranslatorInterface $translator)
|
public function deleteClientAction(Request $request, Client $client, EntityManagerInterface $entityManager, TranslatorInterface $translator)
|
||||||
{
|
{
|
||||||
if (!$this->isCsrfTokenValid('delete-client', $request->request->get('token'))) {
|
if (!$this->isCsrfTokenValid('delete-client', $request->request->get('token'))) {
|
||||||
throw $this->createAccessDeniedException('Bad CSRF token.');
|
throw new BadRequestHttpException('Bad CSRF token.');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (null === $this->getUser() || $client->getUser()->getId() !== $this->getUser()->getId()) {
|
if (null === $this->getUser() || $client->getUser()->getId() !== $this->getUser()->getId()) {
|
||||||
|
|
|
@ -16,6 +16,7 @@ use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||||
use Symfony\Component\Routing\Annotation\Route;
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||||
use Symfony\Component\Validator\Constraints\Locale as LocaleConstraint;
|
use Symfony\Component\Validator\Constraints\Locale as LocaleConstraint;
|
||||||
|
@ -262,7 +263,7 @@ class ConfigController extends AbstractController
|
||||||
public function disableOtpEmailAction(Request $request)
|
public function disableOtpEmailAction(Request $request)
|
||||||
{
|
{
|
||||||
if (!$this->isCsrfTokenValid('otp', $request->request->get('token'))) {
|
if (!$this->isCsrfTokenValid('otp', $request->request->get('token'))) {
|
||||||
throw $this->createAccessDeniedException('Bad CSRF token.');
|
throw new BadRequestHttpException('Bad CSRF token.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = $this->getUser();
|
$user = $this->getUser();
|
||||||
|
@ -286,7 +287,7 @@ class ConfigController extends AbstractController
|
||||||
public function otpEmailAction(Request $request)
|
public function otpEmailAction(Request $request)
|
||||||
{
|
{
|
||||||
if (!$this->isCsrfTokenValid('otp', $request->request->get('token'))) {
|
if (!$this->isCsrfTokenValid('otp', $request->request->get('token'))) {
|
||||||
throw $this->createAccessDeniedException('Bad CSRF token.');
|
throw new BadRequestHttpException('Bad CSRF token.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = $this->getUser();
|
$user = $this->getUser();
|
||||||
|
@ -313,7 +314,7 @@ class ConfigController extends AbstractController
|
||||||
public function disableOtpAppAction(Request $request)
|
public function disableOtpAppAction(Request $request)
|
||||||
{
|
{
|
||||||
if (!$this->isCsrfTokenValid('otp', $request->request->get('token'))) {
|
if (!$this->isCsrfTokenValid('otp', $request->request->get('token'))) {
|
||||||
throw $this->createAccessDeniedException('Bad CSRF token.');
|
throw new BadRequestHttpException('Bad CSRF token.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = $this->getUser();
|
$user = $this->getUser();
|
||||||
|
@ -339,7 +340,7 @@ class ConfigController extends AbstractController
|
||||||
public function otpAppAction(Request $request, GoogleAuthenticatorInterface $googleAuthenticator)
|
public function otpAppAction(Request $request, GoogleAuthenticatorInterface $googleAuthenticator)
|
||||||
{
|
{
|
||||||
if (!$this->isCsrfTokenValid('otp', $request->request->get('token'))) {
|
if (!$this->isCsrfTokenValid('otp', $request->request->get('token'))) {
|
||||||
throw $this->createAccessDeniedException('Bad CSRF token.');
|
throw new BadRequestHttpException('Bad CSRF token.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = $this->getUser();
|
$user = $this->getUser();
|
||||||
|
@ -398,7 +399,7 @@ class ConfigController extends AbstractController
|
||||||
public function otpAppCheckAction(Request $request, GoogleAuthenticatorInterface $googleAuthenticator)
|
public function otpAppCheckAction(Request $request, GoogleAuthenticatorInterface $googleAuthenticator)
|
||||||
{
|
{
|
||||||
if (!$this->isCsrfTokenValid('otp', $request->request->get('token'))) {
|
if (!$this->isCsrfTokenValid('otp', $request->request->get('token'))) {
|
||||||
throw $this->createAccessDeniedException('Bad CSRF token.');
|
throw new BadRequestHttpException('Bad CSRF token.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$isValid = $googleAuthenticator->checkCode(
|
$isValid = $googleAuthenticator->checkCode(
|
||||||
|
@ -429,22 +430,22 @@ class ConfigController extends AbstractController
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Route("/generate-token", name="generate_token")
|
* @Route("/generate-token", name="generate_token", methods={"POST"})
|
||||||
*
|
*
|
||||||
* @return RedirectResponse|JsonResponse
|
* @return RedirectResponse|JsonResponse
|
||||||
*/
|
*/
|
||||||
public function generateTokenAction(Request $request)
|
public function generateTokenAction(Request $request)
|
||||||
{
|
{
|
||||||
|
if (!$this->isCsrfTokenValid('generate-token', $request->request->get('token'))) {
|
||||||
|
throw new BadRequestHttpException('Bad CSRF token.');
|
||||||
|
}
|
||||||
|
|
||||||
$config = $this->getConfig();
|
$config = $this->getConfig();
|
||||||
$config->setFeedToken(Utils::generateToken());
|
$config->setFeedToken(Utils::generateToken());
|
||||||
|
|
||||||
$this->entityManager->persist($config);
|
$this->entityManager->persist($config);
|
||||||
$this->entityManager->flush();
|
$this->entityManager->flush();
|
||||||
|
|
||||||
if ($request->isXmlHttpRequest()) {
|
|
||||||
return new JsonResponse(['token' => $config->getFeedToken()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->addFlash(
|
$this->addFlash(
|
||||||
'notice',
|
'notice',
|
||||||
'flashes.config.notice.feed_token_updated'
|
'flashes.config.notice.feed_token_updated'
|
||||||
|
@ -454,22 +455,22 @@ class ConfigController extends AbstractController
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Route("/revoke-token", name="revoke_token")
|
* @Route("/revoke-token", name="revoke_token", methods={"POST"})
|
||||||
*
|
*
|
||||||
* @return RedirectResponse|JsonResponse
|
* @return RedirectResponse|JsonResponse
|
||||||
*/
|
*/
|
||||||
public function revokeTokenAction(Request $request)
|
public function revokeTokenAction(Request $request)
|
||||||
{
|
{
|
||||||
|
if (!$this->isCsrfTokenValid('revoke-token', $request->request->get('token'))) {
|
||||||
|
throw new BadRequestHttpException('Bad CSRF token.');
|
||||||
|
}
|
||||||
|
|
||||||
$config = $this->getConfig();
|
$config = $this->getConfig();
|
||||||
$config->setFeedToken(null);
|
$config->setFeedToken(null);
|
||||||
|
|
||||||
$this->entityManager->persist($config);
|
$this->entityManager->persist($config);
|
||||||
$this->entityManager->flush();
|
$this->entityManager->flush();
|
||||||
|
|
||||||
if ($request->isXmlHttpRequest()) {
|
|
||||||
return new JsonResponse();
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->addFlash(
|
$this->addFlash(
|
||||||
'notice',
|
'notice',
|
||||||
'flashes.config.notice.feed_token_revoked'
|
'flashes.config.notice.feed_token_revoked'
|
||||||
|
@ -481,12 +482,16 @@ class ConfigController extends AbstractController
|
||||||
/**
|
/**
|
||||||
* Deletes a tagging rule and redirect to the config homepage.
|
* Deletes a tagging rule and redirect to the config homepage.
|
||||||
*
|
*
|
||||||
* @Route("/tagging-rule/delete/{id}", requirements={"id" = "\d+"}, name="delete_tagging_rule")
|
* @Route("/tagging-rule/delete/{id}", name="delete_tagging_rule", methods={"POST"}, requirements={"id" = "\d+"})
|
||||||
*
|
*
|
||||||
* @return RedirectResponse
|
* @return RedirectResponse
|
||||||
*/
|
*/
|
||||||
public function deleteTaggingRuleAction(TaggingRule $rule)
|
public function deleteTaggingRuleAction(Request $request, TaggingRule $rule)
|
||||||
{
|
{
|
||||||
|
if (!$this->isCsrfTokenValid('delete-tagging-rule', $request->request->get('token'))) {
|
||||||
|
throw new BadRequestHttpException('Bad CSRF token.');
|
||||||
|
}
|
||||||
|
|
||||||
$this->validateRuleAction($rule);
|
$this->validateRuleAction($rule);
|
||||||
|
|
||||||
$this->entityManager->remove($rule);
|
$this->entityManager->remove($rule);
|
||||||
|
@ -517,12 +522,16 @@ class ConfigController extends AbstractController
|
||||||
/**
|
/**
|
||||||
* Deletes an ignore origin rule and redirect to the config homepage.
|
* Deletes an ignore origin rule and redirect to the config homepage.
|
||||||
*
|
*
|
||||||
* @Route("/ignore-origin-user-rule/delete/{id}", requirements={"id" = "\d+"}, name="delete_ignore_origin_rule")
|
* @Route("/ignore-origin-user-rule/delete/{id}", name="delete_ignore_origin_rule", methods={"POST"}, requirements={"id" = "\d+"})
|
||||||
*
|
*
|
||||||
* @return RedirectResponse
|
* @return RedirectResponse
|
||||||
*/
|
*/
|
||||||
public function deleteIgnoreOriginRuleAction(IgnoreOriginUserRule $rule)
|
public function deleteIgnoreOriginRuleAction(Request $request, IgnoreOriginUserRule $rule)
|
||||||
{
|
{
|
||||||
|
if (!$this->isCsrfTokenValid('delete-ignore-origin-rule', $request->request->get('token'))) {
|
||||||
|
throw new BadRequestHttpException('Bad CSRF token.');
|
||||||
|
}
|
||||||
|
|
||||||
$this->validateRuleAction($rule);
|
$this->validateRuleAction($rule);
|
||||||
|
|
||||||
$this->entityManager->remove($rule);
|
$this->entityManager->remove($rule);
|
||||||
|
@ -560,7 +569,7 @@ class ConfigController extends AbstractController
|
||||||
public function resetAction(Request $request, string $type, AnnotationRepository $annotationRepository, EntryRepository $entryRepository)
|
public function resetAction(Request $request, string $type, AnnotationRepository $annotationRepository, EntryRepository $entryRepository)
|
||||||
{
|
{
|
||||||
if (!$this->isCsrfTokenValid('reset-area', $request->request->get('token'))) {
|
if (!$this->isCsrfTokenValid('reset-area', $request->request->get('token'))) {
|
||||||
throw $this->createAccessDeniedException('Bad CSRF token.');
|
throw new BadRequestHttpException('Bad CSRF token.');
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ($type) {
|
switch ($type) {
|
||||||
|
@ -614,7 +623,7 @@ class ConfigController extends AbstractController
|
||||||
public function deleteAccountAction(Request $request, UserRepository $userRepository, TokenStorageInterface $tokenStorage)
|
public function deleteAccountAction(Request $request, UserRepository $userRepository, TokenStorageInterface $tokenStorage)
|
||||||
{
|
{
|
||||||
if (!$this->isCsrfTokenValid('delete-account', $request->request->get('token'))) {
|
if (!$this->isCsrfTokenValid('delete-account', $request->request->get('token'))) {
|
||||||
throw $this->createAccessDeniedException('Bad CSRF token.');
|
throw new BadRequestHttpException('Bad CSRF token.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$enabledUsers = $userRepository->getSumEnabledUsers();
|
$enabledUsers = $userRepository->getSumEnabledUsers();
|
||||||
|
@ -637,12 +646,16 @@ class ConfigController extends AbstractController
|
||||||
/**
|
/**
|
||||||
* Switch view mode for current user.
|
* Switch view mode for current user.
|
||||||
*
|
*
|
||||||
* @Route("/config/view-mode", name="switch_view_mode")
|
* @Route("/config/view-mode", name="switch_view_mode", methods={"POST"})
|
||||||
*
|
*
|
||||||
* @return RedirectResponse
|
* @return RedirectResponse
|
||||||
*/
|
*/
|
||||||
public function changeViewModeAction(Request $request)
|
public function changeViewModeAction(Request $request)
|
||||||
{
|
{
|
||||||
|
if (!$this->isCsrfTokenValid('switch-view-mode', $request->request->get('token'))) {
|
||||||
|
throw new BadRequestHttpException('Bad CSRF token.');
|
||||||
|
}
|
||||||
|
|
||||||
$user = $this->getUser();
|
$user = $this->getUser();
|
||||||
$user->getConfig()->setListMode(!$user->getConfig()->getListMode());
|
$user->getConfig()->setListMode(!$user->getConfig()->getListMode());
|
||||||
|
|
||||||
|
@ -659,12 +672,16 @@ class ConfigController extends AbstractController
|
||||||
*
|
*
|
||||||
* @param string $language
|
* @param string $language
|
||||||
*
|
*
|
||||||
* @Route("/locale/{language}", name="changeLocale")
|
* @Route("/locale/{language}", name="changeLocale", methods={"POST"})
|
||||||
*
|
*
|
||||||
* @return RedirectResponse
|
* @return RedirectResponse
|
||||||
*/
|
*/
|
||||||
public function setLocaleAction(Request $request, ValidatorInterface $validator, $language = null)
|
public function setLocaleAction(Request $request, ValidatorInterface $validator, $language = null)
|
||||||
{
|
{
|
||||||
|
if (!$this->isCsrfTokenValid('change-locale', $request->request->get('token'))) {
|
||||||
|
throw new BadRequestHttpException('Bad CSRF token.');
|
||||||
|
}
|
||||||
|
|
||||||
$errors = $validator->validate($language, (new LocaleConstraint()));
|
$errors = $validator->validate($language, (new LocaleConstraint()));
|
||||||
|
|
||||||
if (0 === \count($errors)) {
|
if (0 === \count($errors)) {
|
||||||
|
|
|
@ -14,6 +14,7 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||||
use Symfony\Component\Routing\Annotation\Route;
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
use Wallabag\CoreBundle\Entity\Entry;
|
use Wallabag\CoreBundle\Entity\Entry;
|
||||||
|
@ -52,12 +53,16 @@ class EntryController extends AbstractController
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Route("/mass", name="mass_action")
|
* @Route("/mass", name="mass_action", methods={"POST"})
|
||||||
*
|
*
|
||||||
* @return Response
|
* @return Response
|
||||||
*/
|
*/
|
||||||
public function massAction(Request $request, TagRepository $tagRepository)
|
public function massAction(Request $request, TagRepository $tagRepository)
|
||||||
{
|
{
|
||||||
|
if (!$this->isCsrfTokenValid('mass-action', $request->request->get('token'))) {
|
||||||
|
throw new BadRequestHttpException('Bad CSRF token.');
|
||||||
|
}
|
||||||
|
|
||||||
$values = $request->request->all();
|
$values = $request->request->all();
|
||||||
|
|
||||||
$tagsToAdd = [];
|
$tagsToAdd = [];
|
||||||
|
@ -400,12 +405,16 @@ class EntryController extends AbstractController
|
||||||
* Reload an entry.
|
* Reload an entry.
|
||||||
* Refetch content from the website and make it readable again.
|
* Refetch content from the website and make it readable again.
|
||||||
*
|
*
|
||||||
* @Route("/reload/{id}", requirements={"id" = "\d+"}, name="reload_entry")
|
* @Route("/reload/{id}", name="reload_entry", methods={"POST"}, requirements={"id" = "\d+"})
|
||||||
*
|
*
|
||||||
* @return RedirectResponse
|
* @return RedirectResponse
|
||||||
*/
|
*/
|
||||||
public function reloadAction(Entry $entry)
|
public function reloadAction(Request $request, Entry $entry)
|
||||||
{
|
{
|
||||||
|
if (!$this->isCsrfTokenValid('reload-entry', $request->request->get('token'))) {
|
||||||
|
throw new BadRequestHttpException('Bad CSRF token.');
|
||||||
|
}
|
||||||
|
|
||||||
$this->checkUserAction($entry);
|
$this->checkUserAction($entry);
|
||||||
|
|
||||||
$this->updateEntry($entry, 'entry_reloaded');
|
$this->updateEntry($entry, 'entry_reloaded');
|
||||||
|
@ -429,12 +438,16 @@ class EntryController extends AbstractController
|
||||||
/**
|
/**
|
||||||
* Changes read status for an entry.
|
* Changes read status for an entry.
|
||||||
*
|
*
|
||||||
* @Route("/archive/{id}", requirements={"id" = "\d+"}, name="archive_entry")
|
* @Route("/archive/{id}", name="archive_entry", methods={"POST"}, requirements={"id" = "\d+"})
|
||||||
*
|
*
|
||||||
* @return RedirectResponse
|
* @return RedirectResponse
|
||||||
*/
|
*/
|
||||||
public function toggleArchiveAction(Request $request, Entry $entry)
|
public function toggleArchiveAction(Request $request, Entry $entry)
|
||||||
{
|
{
|
||||||
|
if (!$this->isCsrfTokenValid('archive-entry', $request->request->get('token'))) {
|
||||||
|
throw new BadRequestHttpException('Bad CSRF token.');
|
||||||
|
}
|
||||||
|
|
||||||
$this->checkUserAction($entry);
|
$this->checkUserAction($entry);
|
||||||
|
|
||||||
$entry->toggleArchive();
|
$entry->toggleArchive();
|
||||||
|
@ -458,12 +471,16 @@ class EntryController extends AbstractController
|
||||||
/**
|
/**
|
||||||
* Changes starred status for an entry.
|
* Changes starred status for an entry.
|
||||||
*
|
*
|
||||||
* @Route("/star/{id}", requirements={"id" = "\d+"}, name="star_entry")
|
* @Route("/star/{id}", name="star_entry", methods={"POST"}, requirements={"id" = "\d+"})
|
||||||
*
|
*
|
||||||
* @return RedirectResponse
|
* @return RedirectResponse
|
||||||
*/
|
*/
|
||||||
public function toggleStarAction(Request $request, Entry $entry)
|
public function toggleStarAction(Request $request, Entry $entry)
|
||||||
{
|
{
|
||||||
|
if (!$this->isCsrfTokenValid('star-entry', $request->request->get('token'))) {
|
||||||
|
throw new BadRequestHttpException('Bad CSRF token.');
|
||||||
|
}
|
||||||
|
|
||||||
$this->checkUserAction($entry);
|
$this->checkUserAction($entry);
|
||||||
|
|
||||||
$entry->toggleStar();
|
$entry->toggleStar();
|
||||||
|
@ -488,12 +505,16 @@ class EntryController extends AbstractController
|
||||||
/**
|
/**
|
||||||
* Deletes entry and redirect to the homepage or the last viewed page.
|
* Deletes entry and redirect to the homepage or the last viewed page.
|
||||||
*
|
*
|
||||||
* @Route("/delete/{id}", requirements={"id" = "\d+"}, name="delete_entry")
|
* @Route("/delete/{id}", name="delete_entry", methods={"POST"}, requirements={"id" = "\d+"})
|
||||||
*
|
*
|
||||||
* @return RedirectResponse
|
* @return RedirectResponse
|
||||||
*/
|
*/
|
||||||
public function deleteEntryAction(Request $request, Entry $entry)
|
public function deleteEntryAction(Request $request, Entry $entry)
|
||||||
{
|
{
|
||||||
|
if (!$this->isCsrfTokenValid('delete-entry', $request->request->get('token'))) {
|
||||||
|
throw new BadRequestHttpException('Bad CSRF token.');
|
||||||
|
}
|
||||||
|
|
||||||
$this->checkUserAction($entry);
|
$this->checkUserAction($entry);
|
||||||
|
|
||||||
// generates the view url for this entry to check for redirection later
|
// generates the view url for this entry to check for redirection later
|
||||||
|
@ -526,12 +547,16 @@ class EntryController extends AbstractController
|
||||||
/**
|
/**
|
||||||
* Get public URL for entry (and generate it if necessary).
|
* Get public URL for entry (and generate it if necessary).
|
||||||
*
|
*
|
||||||
* @Route("/share/{id}", requirements={"id" = "\d+"}, name="share")
|
* @Route("/share/{id}", name="share", methods={"POST"}, requirements={"id" = "\d+"})
|
||||||
*
|
*
|
||||||
* @return Response
|
* @return Response
|
||||||
*/
|
*/
|
||||||
public function shareAction(Entry $entry)
|
public function shareAction(Request $request, Entry $entry)
|
||||||
{
|
{
|
||||||
|
if (!$this->isCsrfTokenValid('share-entry', $request->request->get('token'))) {
|
||||||
|
throw new BadRequestHttpException('Bad CSRF token.');
|
||||||
|
}
|
||||||
|
|
||||||
$this->checkUserAction($entry);
|
$this->checkUserAction($entry);
|
||||||
|
|
||||||
if (null === $entry->getUid()) {
|
if (null === $entry->getUid()) {
|
||||||
|
@ -549,12 +574,16 @@ class EntryController extends AbstractController
|
||||||
/**
|
/**
|
||||||
* Disable public sharing for an entry.
|
* Disable public sharing for an entry.
|
||||||
*
|
*
|
||||||
* @Route("/share/delete/{id}", requirements={"id" = "\d+"}, name="delete_share")
|
* @Route("/share/delete/{id}", name="delete_share", methods={"POST"}, requirements={"id" = "\d+"})
|
||||||
*
|
*
|
||||||
* @return Response
|
* @return Response
|
||||||
*/
|
*/
|
||||||
public function deleteShareAction(Entry $entry)
|
public function deleteShareAction(Request $request, Entry $entry)
|
||||||
{
|
{
|
||||||
|
if (!$this->isCsrfTokenValid('delete-share', $request->request->get('token'))) {
|
||||||
|
throw new BadRequestHttpException('Bad CSRF token.');
|
||||||
|
}
|
||||||
|
|
||||||
$this->checkUserAction($entry);
|
$this->checkUserAction($entry);
|
||||||
|
|
||||||
$entry->cleanUid();
|
$entry->cleanUid();
|
||||||
|
@ -570,7 +599,7 @@ class EntryController extends AbstractController
|
||||||
/**
|
/**
|
||||||
* Ability to view a content publicly.
|
* Ability to view a content publicly.
|
||||||
*
|
*
|
||||||
* @Route("/share/{uid}", requirements={"uid" = ".+"}, name="share_entry")
|
* @Route("/share/{uid}", name="share_entry", methods={"GET"}, requirements={"uid" = ".+"})
|
||||||
* @Cache(maxage="25200", smaxage="25200", public=true)
|
* @Cache(maxage="25200", smaxage="25200", public=true)
|
||||||
*
|
*
|
||||||
* @return Response
|
* @return Response
|
||||||
|
|
|
@ -10,6 +10,7 @@ use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||||
use Symfony\Component\Routing\Annotation\Route;
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
use Wallabag\CoreBundle\Entity\Entry;
|
use Wallabag\CoreBundle\Entity\Entry;
|
||||||
|
@ -87,12 +88,16 @@ class TagController extends AbstractController
|
||||||
/**
|
/**
|
||||||
* Removes tag from entry.
|
* Removes tag from entry.
|
||||||
*
|
*
|
||||||
* @Route("/remove-tag/{entry}/{tag}", requirements={"entry" = "\d+", "tag" = "\d+"}, name="remove_tag")
|
* @Route("/remove-tag/{entry}/{tag}", name="remove_tag", methods={"POST"}, requirements={"entry" = "\d+", "tag" = "\d+"})
|
||||||
*
|
*
|
||||||
* @return Response
|
* @return Response
|
||||||
*/
|
*/
|
||||||
public function removeTagFromEntry(Request $request, Entry $entry, Tag $tag)
|
public function removeTagFromEntry(Request $request, Entry $entry, Tag $tag)
|
||||||
{
|
{
|
||||||
|
if (!$this->isCsrfTokenValid('remove-tag', $request->request->get('token'))) {
|
||||||
|
throw new BadRequestHttpException('Bad CSRF token.');
|
||||||
|
}
|
||||||
|
|
||||||
$this->checkUserAction($entry);
|
$this->checkUserAction($entry);
|
||||||
|
|
||||||
$entry->removeTag($tag);
|
$entry->removeTag($tag);
|
||||||
|
@ -228,12 +233,16 @@ class TagController extends AbstractController
|
||||||
/**
|
/**
|
||||||
* Tag search results with the current search term.
|
* Tag search results with the current search term.
|
||||||
*
|
*
|
||||||
* @Route("/tag/search/{filter}", name="tag_this_search")
|
* @Route("/tag/search/{filter}", name="tag_this_search", methods={"POST"})
|
||||||
*
|
*
|
||||||
* @return Response
|
* @return Response
|
||||||
*/
|
*/
|
||||||
public function tagThisSearchAction($filter, Request $request, EntryRepository $entryRepository)
|
public function tagThisSearchAction($filter, Request $request, EntryRepository $entryRepository)
|
||||||
{
|
{
|
||||||
|
if (!$this->isCsrfTokenValid('tag-this-search', $request->request->get('token'))) {
|
||||||
|
throw new BadRequestHttpException('Bad CSRF token.');
|
||||||
|
}
|
||||||
|
|
||||||
$currentRoute = $request->query->has('currentRoute') ? $request->query->get('currentRoute') : '';
|
$currentRoute = $request->query->has('currentRoute') ? $request->query->get('currentRoute') : '';
|
||||||
|
|
||||||
/** @var QueryBuilder $qb */
|
/** @var QueryBuilder $qb */
|
||||||
|
@ -263,13 +272,17 @@ class TagController extends AbstractController
|
||||||
/**
|
/**
|
||||||
* Delete a given tag for the current user.
|
* Delete a given tag for the current user.
|
||||||
*
|
*
|
||||||
* @Route("/tag/delete/{slug}", name="tag_delete")
|
* @Route("/tag/delete/{slug}", name="tag_delete", methods={"POST"})
|
||||||
* @ParamConverter("tag", options={"mapping": {"slug": "slug"}})
|
* @ParamConverter("tag", options={"mapping": {"slug": "slug"}})
|
||||||
*
|
*
|
||||||
* @return Response
|
* @return Response
|
||||||
*/
|
*/
|
||||||
public function removeTagAction(Tag $tag, Request $request, EntryRepository $entryRepository)
|
public function removeTagAction(Tag $tag, Request $request, EntryRepository $entryRepository)
|
||||||
{
|
{
|
||||||
|
if (!$this->isCsrfTokenValid('tag-delete', $request->request->get('token'))) {
|
||||||
|
throw new BadRequestHttpException('Bad CSRF token.');
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($tag->getEntriesByUserId($this->getUser()->getId()) as $entry) {
|
foreach ($tag->getEntriesByUserId($this->getUser()->getId()) as $entry) {
|
||||||
$entryRepository->removeTag($this->getUser()->getId(), $tag);
|
$entryRepository->removeTag($this->getUser()->getId(), $tag);
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,48 +123,63 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="set2" class="col s12">
|
<div id="set2" class="col s12">
|
||||||
|
<div class="row">
|
||||||
|
<div class="input-field col s12">
|
||||||
|
{{ 'config.form_feed.description'|trans }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col s12">
|
||||||
|
<h6 class="grey-text">{{ 'config.form_feed.token_label'|trans }}</h6>
|
||||||
|
<div>
|
||||||
|
{% if feed.token %}
|
||||||
|
{{ feed.token }}
|
||||||
|
{% else %}
|
||||||
|
<em>{{ 'config.form_feed.no_token'|trans }}</em>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if feed.token %}
|
||||||
|
–
|
||||||
|
<form action="{{ path('generate_token') }}" method="post" class="inline-block">
|
||||||
|
<input type="hidden" name="token" value="{{ csrf_token('generate-token') }}"/>
|
||||||
|
|
||||||
|
<button type="submit" class="btn-link">{{ 'config.form_feed.token_reset'|trans }}</button>
|
||||||
|
</form>
|
||||||
|
–
|
||||||
|
<form action="{{ path('revoke_token') }}" method="post" class="inline-block">
|
||||||
|
<input type="hidden" name="token" value="{{ csrf_token('revoke-token') }}"/>
|
||||||
|
|
||||||
|
<button type="submit" class="btn-link">{{ 'config.form_feed.token_revoke'|trans }}</button>
|
||||||
|
</form>
|
||||||
|
{% else %}
|
||||||
|
–
|
||||||
|
<form action="{{ path('generate_token') }}" method="post" class="inline-block">
|
||||||
|
<input type="hidden" name="token" value="{{ csrf_token('generate-token') }}"/>
|
||||||
|
|
||||||
|
<button type="submit" class="btn-link">{{ 'config.form_feed.token_create'|trans }}</button>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% if feed.token %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col s12">
|
||||||
|
<h6 class="grey-text">{{ 'config.form_feed.feed_links'|trans }}</h6>
|
||||||
|
<ul>
|
||||||
|
<li><a href="{{ path('unread_feed', {'username': feed.username, 'token': feed.token}) }}">{{ 'config.form_feed.feed_link.unread'|trans }}</a></li>
|
||||||
|
<li><a href="{{ path('starred_feed', {'username': feed.username, 'token': feed.token}) }}">{{ 'config.form_feed.feed_link.starred'|trans }}</a></li>
|
||||||
|
<li><a href="{{ path('archive_feed', {'username': feed.username, 'token': feed.token}) }}">{{ 'config.form_feed.feed_link.archive'|trans }}</a></li>
|
||||||
|
<li><a href="{{ path('all_feed', {'username': feed.username, 'token': feed.token}) }}">{{ 'config.form_feed.feed_link.all'|trans }}</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{{ form_start(form.feed) }}
|
{{ form_start(form.feed) }}
|
||||||
{{ form_errors(form.feed) }}
|
{{ form_errors(form.feed) }}
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="input-field col s12">
|
|
||||||
{{ 'config.form_feed.description'|trans }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="col s12">
|
|
||||||
<h6 class="grey-text">{{ 'config.form_feed.token_label'|trans }}</h6>
|
|
||||||
<div>
|
|
||||||
{% if feed.token %}
|
|
||||||
{{ feed.token }}
|
|
||||||
{% else %}
|
|
||||||
<em>{{ 'config.form_feed.no_token'|trans }}</em>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if feed.token %}
|
|
||||||
– <a href="{{ path('generate_token') }}">{{ 'config.form_feed.token_reset'|trans }}</a>
|
|
||||||
– <a href="{{ path('revoke_token') }}">{{ 'config.form_feed.token_revoke'|trans }}</a>
|
|
||||||
{% else %}
|
|
||||||
– <a href="{{ path('generate_token') }}">{{ 'config.form_feed.token_create'|trans }}</a>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% if feed.token %}
|
|
||||||
<div class="row">
|
|
||||||
<div class="col s12">
|
|
||||||
<h6 class="grey-text">{{ 'config.form_feed.feed_links'|trans }}</h6>
|
|
||||||
<ul>
|
|
||||||
<li><a href="{{ path('unread_feed', {'username': feed.username, 'token': feed.token}) }}">{{ 'config.form_feed.feed_link.unread'|trans }}</a></li>
|
|
||||||
<li><a href="{{ path('starred_feed', {'username': feed.username, 'token': feed.token}) }}">{{ 'config.form_feed.feed_link.starred'|trans }}</a></li>
|
|
||||||
<li><a href="{{ path('archive_feed', {'username': feed.username, 'token': feed.token}) }}">{{ 'config.form_feed.feed_link.archive'|trans }}</a></li>
|
|
||||||
<li><a href="{{ path('all_feed', {'username': feed.username, 'token': feed.token}) }}">{{ 'config.form_feed.feed_link.all'|trans }}</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="input-field col s12">
|
<div class="input-field col s12">
|
||||||
{{ form_label(form.feed.feed_limit) }}
|
{{ form_label(form.feed.feed_limit) }}
|
||||||
|
@ -325,9 +340,13 @@
|
||||||
<a href="{{ path('edit_tagging_rule', {id: tagging_rule.id}) }}" title="{{ 'config.form_rules.edit_rule_label'|trans }}" class="mode_edit_tagging_rule">
|
<a href="{{ path('edit_tagging_rule', {id: tagging_rule.id}) }}" title="{{ 'config.form_rules.edit_rule_label'|trans }}" class="mode_edit_tagging_rule">
|
||||||
<i class="tool grey-text material-icons">mode_edit</i>
|
<i class="tool grey-text material-icons">mode_edit</i>
|
||||||
</a>
|
</a>
|
||||||
<a href="{{ path('delete_tagging_rule', {id: tagging_rule.id}) }}" title="{{ 'config.form_rules.delete_rule_label'|trans }}" class="delete_tagging_rule">
|
<form action="{{ path('delete_tagging_rule', {id: tagging_rule.id}) }}" method="post" class="inline-block">
|
||||||
<i class="tool grey-text material-icons">delete</i>
|
<input type="hidden" name="token" value="{{ csrf_token('delete-tagging-rule') }}"/>
|
||||||
</a>
|
|
||||||
|
<button type="submit" title="{{ 'config.form_rules.delete_rule_label'|trans }}" class="btn-link">
|
||||||
|
<i class="tool grey-text material-icons">delete</i>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -505,9 +524,13 @@
|
||||||
<a href="{{ path('edit_ignore_origin_rule', {id: ignore_origin_rule.id}) }}" title="{{ 'config.form_rules.edit_rule_label'|trans }}" class="mode_edit">
|
<a href="{{ path('edit_ignore_origin_rule', {id: ignore_origin_rule.id}) }}" title="{{ 'config.form_rules.edit_rule_label'|trans }}" class="mode_edit">
|
||||||
<i class="tool grey-text material-icons">mode_edit</i>
|
<i class="tool grey-text material-icons">mode_edit</i>
|
||||||
</a>
|
</a>
|
||||||
<a href="{{ path('delete_ignore_origin_rule', {id: ignore_origin_rule.id}) }}" title="{{ 'config.form_rules.delete_rule_label'|trans }}" class="delete">
|
<form action="{{ path('delete_ignore_origin_rule', {id: ignore_origin_rule.id}) }}" method="post" class="inline-block">
|
||||||
<i class="tool grey-text material-icons">delete</i>
|
<input type="hidden" name="token" value="{{ csrf_token('delete-ignore-origin-rule') }}"/>
|
||||||
</a>
|
|
||||||
|
<button type="submit" title="{{ 'config.form_rules.delete_rule_label'|trans }}" class="btn-link">
|
||||||
|
<i class="tool grey-text material-icons">delete</i>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
<label class="entry-checkbox">
|
<label class="entry-checkbox">
|
||||||
<input type="checkbox" class="entry-checkbox-input" data-js="entry-checkbox" name="entry-checkbox[]" value="{{ entry.id }}" />
|
<input type="checkbox" form="form_mass_action" class="entry-checkbox-input" data-js="entry-checkbox" name="entry-checkbox[]" value="{{ entry.id }}" />
|
||||||
</label>
|
</label>
|
||||||
|
|
|
@ -14,13 +14,31 @@
|
||||||
<a title="{{ 'entry.list.show_same_domain'|trans }}" class="tool grey-text" href="{{ path('same_domain', {'id': entry.id, redirect: current_path}) }}" data-action="same_domain" data-entry-id="{{ entry.id }}"><i class="material-icons">language</i></a>
|
<a title="{{ 'entry.list.show_same_domain'|trans }}" class="tool grey-text" href="{{ path('same_domain', {'id': entry.id, redirect: current_path}) }}" data-action="same_domain" data-entry-id="{{ entry.id }}"><i class="material-icons">language</i></a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a title="{{ 'entry.list.toogle_as_read'|trans }}" class="tool grey-text" href="{{ path('archive_entry', {'id': entry.id, redirect: current_path}) }}" data-action="archived" data-entry-id="{{ entry.id }}"><i class="material-icons">{% if entry.isArchived == 0 %}done{% else %}unarchive{% endif %}</i></a>
|
<form action="{{ path('archive_entry', {'id': entry.id, redirect: current_path}) }}" method="post" class="inline-block">
|
||||||
|
<input type="hidden" name="token" value="{{ csrf_token('archive-entry') }}"/>
|
||||||
|
|
||||||
|
<button type="submit" class="btn-link tool grey-text" title="{{ 'entry.list.toogle_as_read'|trans }}">
|
||||||
|
<i class="material-icons">{% if entry.isArchived == 0 %}done{% else %}unarchive{% endif %}</i>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a title="{{ 'entry.list.toogle_as_star'|trans }}" class="tool grey-text" href="{{ path('star_entry', {'id': entry.id, redirect: current_path}) }}" data-action="star" data-entry-id="{{ entry.id }}"><i class="material-icons">{% if entry.isStarred == 0 %}star_border{% else %}star{% endif %}</i></a>
|
<form action="{{ path('star_entry', {'id': entry.id, redirect: current_path}) }}" method="post" class="inline-block">
|
||||||
|
<input type="hidden" name="token" value="{{ csrf_token('star-entry') }}"/>
|
||||||
|
|
||||||
|
<button type="submit" class="btn-link tool grey-text" title="{{ 'entry.list.toogle_as_star'|trans }}">
|
||||||
|
<i class="material-icons">{% if entry.isStarred == 0 %}star_border{% else %}star{% endif %}</i>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a title="{{ 'entry.list.delete'|trans }}" onclick="return confirm('{{ 'entry.confirm.delete'|trans|escape('js') }}')" data-action-confirm="{{ 'entry.confirm.delete'|trans }}" class="tool grey-text delete" href="{{ path('delete_entry', {'id': entry.id, redirect: current_path}) }}" data-action="delete" data-entry-id="{{ entry.id }}"><i class="material-icons">delete</i></a>
|
<form action="{{ path('delete_entry', {'id': entry.id, redirect: current_path}) }}" method="post" class="inline-block">
|
||||||
|
<input type="hidden" name="token" value="{{ csrf_token('delete-entry') }}"/>
|
||||||
|
|
||||||
|
<button type="submit" class="btn-link tool grey-text" title="{{ 'entry.list.delete'|trans }}" onclick="return confirm('{{ 'entry.confirm.delete'|trans|escape('js') }}')">
|
||||||
|
<i class="material-icons">delete</i>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -15,9 +15,27 @@
|
||||||
<ul class="tools-list hide-on-small-only">
|
<ul class="tools-list hide-on-small-only">
|
||||||
<li>
|
<li>
|
||||||
<a title="{{ 'entry.list.show_same_domain'|trans }}" class="tool grey-text" href="{{ path('same_domain', {'id': entry.id, redirect: current_path}) }}"><i class="material-icons">language</i></a>
|
<a title="{{ 'entry.list.show_same_domain'|trans }}" class="tool grey-text" href="{{ path('same_domain', {'id': entry.id, redirect: current_path}) }}"><i class="material-icons">language</i></a>
|
||||||
<a title="{{ 'entry.list.toogle_as_read'|trans }}" class="tool grey-text" href="{{ path('archive_entry', {'id': entry.id, redirect: current_path}) }}"><i class="material-icons">{% if entry.isArchived == 0 %}done{% else %}unarchive{% endif %}</i></a>
|
<form action="{{ path('archive_entry', {'id': entry.id, redirect: current_path}) }}" method="post" class="inline-block">
|
||||||
<a title="{{ 'entry.list.toogle_as_star'|trans }}" class="tool grey-text" href="{{ path('star_entry', {'id': entry.id, redirect: current_path}) }}"><i class="material-icons">{% if entry.isStarred == 0 %}star_border{% else %}star{% endif %}</i></a>
|
<input type="hidden" name="token" value="{{ csrf_token('archive-entry') }}"/>
|
||||||
<a title="{{ 'entry.list.delete'|trans }}" onclick="return confirm('{{ 'entry.confirm.delete'|trans|escape('js') }}')" class="tool grey-text delete" href="{{ path('delete_entry', {'id': entry.id, redirect: current_path}) }}"><i class="material-icons">delete</i></a>
|
|
||||||
|
<button type="submit" class="btn-link tool grey-text" title="{{ 'entry.list.toogle_as_read'|trans }}">
|
||||||
|
<i class="material-icons">{% if entry.isArchived == 0 %}done{% else %}unarchive{% endif %}</i>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<form action="{{ path('star_entry', {'id': entry.id, redirect: current_path}) }}" method="post" class="inline-block">
|
||||||
|
<input type="hidden" name="token" value="{{ csrf_token('star-entry') }}"/>
|
||||||
|
|
||||||
|
<button type="submit" class="btn-link tool grey-text" title="{{ 'entry.list.toogle_as_star'|trans }}">
|
||||||
|
<i class="material-icons">{% if entry.isStarred == 0 %}star_border{% else %}star{% endif %}</i>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<form action="{{ path('delete_entry', {'id': entry.id, redirect: current_path}) }}" method="post" class="inline-block">
|
||||||
|
<input type="hidden" name="token" value="{{ csrf_token('delete-entry') }}"/>
|
||||||
|
|
||||||
|
<button type="submit" class="btn-link tool grey-text" title="{{ 'entry.list.delete'|trans }}" onclick="return confirm('{{ 'entry.confirm.delete'|trans|escape('js') }}')">
|
||||||
|
<i class="material-icons">delete</i>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -5,9 +5,13 @@
|
||||||
<a class="chip-label" href="{{ path('tag_entries', {'slug': tag.slug}) }}">{{ tag.label }}</a>
|
<a class="chip-label" href="{{ path('tag_entries', {'slug': tag.slug}) }}">{{ tag.label }}</a>
|
||||||
{% if withRemove is defined and withRemove == true %}
|
{% if withRemove is defined and withRemove == true %}
|
||||||
{% set current_path = path(app.request.attributes.get('_route'), app.request.attributes.get('_route_params')) %}
|
{% set current_path = path(app.request.attributes.get('_route'), app.request.attributes.get('_route_params')) %}
|
||||||
<a class="chip-action" href="{{ path('remove_tag', {'entry': entryId, 'tag': tag.id, redirect: current_path}) }}" onclick="return confirm('{{ 'entry.confirm.delete_tag'|trans|escape('js') }}')">
|
<form action="{{ path('remove_tag', {'entry': entryId, 'tag': tag.id, redirect: current_path}) }}" method="post">
|
||||||
<i class="material-icons vertical-align-middle">delete</i>
|
<input type="hidden" name="token" value="{{ csrf_token('remove-tag') }}"/>
|
||||||
</a>
|
|
||||||
|
<button type="submit" class="btn-link chip-action" onclick="return confirm('{{ 'entry.confirm.delete_tag'|trans|escape('js') }}')">
|
||||||
|
<i class="material-icons vertical-align-middle">delete</i>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -26,12 +26,20 @@
|
||||||
{% if current_route == 'homepage' %}
|
{% if current_route == 'homepage' %}
|
||||||
{% set current_route = 'unread' %}
|
{% set current_route = 'unread' %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<form name="form_mass_action" action="{{ path('mass_action', {redirect: current_path}) }}" method="post">
|
<form id="form_mass_action" name="form_mass_action" action="{{ path('mass_action', {redirect: current_path}) }}" method="post">
|
||||||
|
<input type="hidden" name="token" value="{{ csrf_token('mass-action') }}"/>
|
||||||
|
</form>
|
||||||
<div class="results">
|
<div class="results">
|
||||||
<div class="nb-results">
|
<div class="nb-results">
|
||||||
{{ 'entry.list.number_on_the_page'|trans({'%count%': entries.count}) }}
|
{{ 'entry.list.number_on_the_page'|trans({'%count%': entries.count}) }}
|
||||||
{% if entries.count > 0 %}
|
{% if entries.count > 0 %}
|
||||||
<a class="results-item" href="{{ path('switch_view_mode', {redirect: current_path}) }}"><i class="material-icons">{% if list_mode == 0 %}view_list{% else %}view_module{% endif %}</i></a>
|
<form action="{{ path('switch_view_mode', {redirect: current_path}) }}" method="post" class="inline-block">
|
||||||
|
<input type="hidden" name="token" value="{{ csrf_token('switch-view-mode') }}"/>
|
||||||
|
|
||||||
|
<button type="submit" class="btn-link results-item">
|
||||||
|
<i class="material-icons">{% if list_mode == 0 %}view_list{% else %}view_module{% endif %}</i>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if entries.count > 0 %}
|
{% if entries.count > 0 %}
|
||||||
<label for="mass-action-inputs-displayed" class="mass-action-toggle results-item tooltipped" data-position="right" data-delay="50" data-tooltip="{{ 'entry.list.toggle_mass_action'|trans }}"><i class="material-icons">library_add_check</i></label>
|
<label for="mass-action-inputs-displayed" class="mass-action-toggle results-item tooltipped" data-position="right" data-delay="50" data-tooltip="{{ 'entry.list.toggle_mass_action'|trans }}"><i class="material-icons">library_add_check</i></label>
|
||||||
|
@ -40,7 +48,13 @@
|
||||||
{% include "@WallabagCore/Entry/_feed_link.html.twig" %}
|
{% include "@WallabagCore/Entry/_feed_link.html.twig" %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% if current_route == 'search' %}<div><a href="{{ path('tag_this_search', {'filter': searchTerm, 'currentRoute': app.request.get('currentRoute'), redirect: current_path}) }}" title="{{ 'entry.list.assign_search_tag'|trans }}">{{ 'entry.list.assign_search_tag'|trans }}</a></div>{% endif %}
|
{% if current_route == 'search' %}
|
||||||
|
<form action="{{ path('tag_this_search', {'filter': searchTerm, 'currentRoute': app.request.get('currentRoute'), redirect: current_path}) }}" method="post">
|
||||||
|
<input type="hidden" name="token" value="{{ csrf_token('tag-this-search') }}"/>
|
||||||
|
|
||||||
|
<button type="submit" class="btn-link" title="{{ 'entry.list.assign_search_tag'|trans }}">{{ 'entry.list.assign_search_tag'|trans }}</button>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
{% if entries.getNbPages > 1 %}
|
{% if entries.getNbPages > 1 %}
|
||||||
{{ pagerfanta(entries, 'default_wallabag') }}
|
{{ pagerfanta(entries, 'default_wallabag') }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -50,15 +64,15 @@
|
||||||
<input id="mass-action-inputs-displayed" class="toggle-checkbox" type="checkbox" />
|
<input id="mass-action-inputs-displayed" class="toggle-checkbox" type="checkbox" />
|
||||||
<div class="mass-action">
|
<div class="mass-action">
|
||||||
<div class="mass-action-group">
|
<div class="mass-action-group">
|
||||||
<input type="checkbox" class="entry-checkbox-input" data-toggle="[data-js='entry-checkbox']" data-js="checkboxes-toggle" />
|
<input type="checkbox" form="form_mass_action" class="entry-checkbox-input" data-toggle="[data-js='entry-checkbox']" data-js="checkboxes-toggle" />
|
||||||
<button class="mass-action-button btn cyan darken-1" type="submit" name="toggle-read" title="{{ 'entry.list.toogle_as_read'|trans }}"><i class="material-icons">done</i></button>
|
<button class="mass-action-button btn cyan darken-1" type="submit" form="form_mass_action" name="toggle-read" title="{{ 'entry.list.toogle_as_read'|trans }}"><i class="material-icons">done</i></button>
|
||||||
<button class="mass-action-button btn cyan darken-1" type="submit" name="toggle-star" title="{{ 'entry.list.toogle_as_star'|trans }}" ><i class="material-icons">star</i></button>
|
<button class="mass-action-button btn cyan darken-1" type="submit" form="form_mass_action" name="toggle-star" title="{{ 'entry.list.toogle_as_star'|trans }}" ><i class="material-icons">star</i></button>
|
||||||
<button class="mass-action-button btn cyan darken-1" type="submit" name="delete" onclick="return confirm('{{ 'entry.confirm.delete_entries'|trans|escape('js') }}')" title="{{ 'entry.list.delete'|trans }}"><i class="material-icons">delete</i></button>
|
<button class="mass-action-button btn cyan darken-1" type="submit" form="form_mass_action" name="delete" onclick="return confirm('{{ 'entry.confirm.delete_entries'|trans|escape('js') }}')" title="{{ 'entry.list.delete'|trans }}"><i class="material-icons">delete</i></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mass-action-tags">
|
<div class="mass-action-tags">
|
||||||
<button class="btn cyan darken-1 mass-action-button mass-action-button--tags" type="submit" name="tag" title="{{ 'entry.list.add_tags'|trans }}"><i class="material-icons">label</i></button>
|
<button class="btn cyan darken-1 mass-action-button mass-action-button--tags" type="submit" form="form_mass_action" name="tag" title="{{ 'entry.list.add_tags'|trans }}"><i class="material-icons">label</i></button>
|
||||||
<input type="text" class="mass-action-tags-input" name="tags" placeholder="{{ 'entry.list.mass_action_tags_input_placeholder'|trans }}" />
|
<input type="text" form="form_mass_action" class="mass-action-tags-input" name="tags" placeholder="{{ 'entry.list.mass_action_tags_input_placeholder'|trans }}" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -77,7 +91,6 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ol>
|
</ol>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</form>
|
|
||||||
|
|
||||||
{% if entries.getNbPages > 1 %}
|
{% if entries.getNbPages > 1 %}
|
||||||
<div class="results">
|
<div class="results">
|
||||||
|
|
|
@ -26,14 +26,22 @@
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="right">
|
<ul class="right">
|
||||||
<li>
|
<li>
|
||||||
<a class="waves-effect" title="{{ 'entry.view.left_menu.set_as_read'|trans }}" href="{{ path('archive_entry', {'id': entry.id, redirect: current_path}) }}" id="markAsRead">
|
<form action="{{ path('archive_entry', {'id': entry.id, redirect: current_path}) }}" method="post" class="inline-block">
|
||||||
<i class="material-icons small">{% if entry.isArchived == 0 %}done{% else %}unarchive{% endif %}</i>
|
<input type="hidden" name="token" value="{{ csrf_token('archive-entry') }}"/>
|
||||||
</a>
|
|
||||||
|
<button type="submit" class="waves-effect" title="{{ 'entry.view.left_menu.set_as_read'|trans }}">
|
||||||
|
<i class="material-icons small">{% if entry.isArchived == 0 %}done{% else %}unarchive{% endif %}</i>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a class="waves-effect" title="{{ 'entry.view.left_menu.set_as_starred'|trans }}" href="{{ path('star_entry', {'id': entry.id, redirect: current_path}) }}" id="setFav">
|
<form action="{{ path('star_entry', {'id': entry.id, redirect: current_path}) }}" method="post" class="inline-block">
|
||||||
<i class="material-icons small">{% if entry.isStarred == 0 %}star_outline{% else %}star{% endif %}</i>
|
<input type="hidden" name="token" value="{{ csrf_token('star-entry') }}"/>
|
||||||
</a>
|
|
||||||
|
<button type="submit" class="waves-effect" title="{{ 'entry.view.left_menu.set_as_starred'|trans }}">
|
||||||
|
<i class="material-icons small">{% if entry.isStarred == 0 %}star_outline{% else %}star{% endif %}</i>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -56,10 +64,14 @@
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="bold">
|
<li class="bold">
|
||||||
<a class="waves-effect collapsible-header" onclick="return confirm('{{ 'entry.confirm.reload'|trans|escape('js') }}')" title="{{ 'entry.view.left_menu.re_fetch_content'|trans }}" href="{{ path('reload_entry', {'id': entry.id}) }}" id="reload">
|
<form action="{{ path('reload_entry', {'id': entry.id}) }}" method="post">
|
||||||
<i class="material-icons small">refresh</i>
|
<input type="hidden" name="token" value="{{ csrf_token('reload-entry') }}"/>
|
||||||
<span>{{ 'entry.view.left_menu.re_fetch_content'|trans }}</span>
|
|
||||||
</a>
|
<button type="submit" class="waves-effect collapsible-header" onclick="return confirm('{{ 'entry.confirm.reload'|trans|escape('js') }}')" title="{{ 'entry.view.left_menu.re_fetch_content'|trans }}">
|
||||||
|
<i class="material-icons small">refresh</i>
|
||||||
|
<span>{{ 'entry.view.left_menu.re_fetch_content'|trans }}</span>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
<div class="collapsible-body"></div>
|
<div class="collapsible-body"></div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
@ -69,25 +81,37 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<li class="bold hide-on-med-and-down">
|
<li class="bold hide-on-med-and-down">
|
||||||
<a class="waves-effect collapsible-header markasread" title="{{ mark_as_read_label|trans }}" href="{{ path('archive_entry', {'id': entry.id, redirect: current_path}) }}" id="markAsRead">
|
<form action="{{ path('archive_entry', {'id': entry.id, redirect: current_path}) }}" method="post">
|
||||||
<i class="material-icons small">{% if entry.isArchived == 0 %}done{% else %}unarchive{% endif %}</i>
|
<input type="hidden" name="token" value="{{ csrf_token('archive-entry') }}"/>
|
||||||
<span>{{ mark_as_read_label|trans }}</span>
|
|
||||||
</a>
|
<button type="submit" class="waves-effect collapsible-header markasread" title="{{ mark_as_read_label|trans }}">
|
||||||
|
<i class="material-icons small">{% if entry.isArchived == 0 %}done{% else %}unarchive{% endif %}</i>
|
||||||
|
<span>{{ mark_as_read_label|trans }}</span>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
<div class="collapsible-body"></div>
|
<div class="collapsible-body"></div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="bold hide-on-med-and-down">
|
<li class="bold hide-on-med-and-down">
|
||||||
<a class="waves-effect collapsible-header favorite" title="{{ 'entry.view.left_menu.set_as_starred'|trans }}" href="{{ path('star_entry', {'id': entry.id, redirect: current_path}) }}" id="setFav">
|
<form action="{{ path('star_entry', {'id': entry.id, redirect: current_path}) }}" method="post">
|
||||||
<i class="material-icons spall">{% if entry.isStarred == 0 %}star_outline{% else %}star{% endif %}</i>
|
<input type="hidden" name="token" value="{{ csrf_token('star-entry') }}"/>
|
||||||
<span>{{ 'entry.view.left_menu.set_as_starred'|trans }}</span>
|
|
||||||
</a>
|
<button type="submit" class="waves-effect collapsible-header favorite" title="{{ 'entry.view.left_menu.set_as_starred'|trans }}">
|
||||||
|
<i class="material-icons spall">{% if entry.isStarred == 0 %}star_outline{% else %}star{% endif %}</i>
|
||||||
|
<span>{{ 'entry.view.left_menu.set_as_starred'|trans }}</span>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
<div class="collapsible-body"></div>
|
<div class="collapsible-body"></div>
|
||||||
</li>
|
</li>
|
||||||
<li class="bold border-bottom">
|
<li class="bold border-bottom">
|
||||||
<a class="waves-effect collapsible-header delete" onclick="return confirm('{{ 'entry.confirm.delete'|trans|escape('js') }}')" title="{{ 'entry.view.left_menu.delete'|trans }}" href="{{ path('delete_entry', {'id': entry.id, redirect: current_path}) }}">
|
<form action="{{ path('delete_entry', {'id': entry.id, redirect: current_path}) }}" method="post">
|
||||||
<i class="material-icons small">delete</i>
|
<input type="hidden" name="token" value="{{ csrf_token('delete-entry') }}"/>
|
||||||
<span>{{ 'entry.view.left_menu.delete'|trans }}</span>
|
|
||||||
</a>
|
<button type="submit" class="waves-effect collapsible-header delete" onclick="return confirm('{{ 'entry.confirm.delete'|trans|escape('js') }}')" title="{{ 'entry.view.left_menu.delete'|trans }}">
|
||||||
|
<i class="material-icons small">delete</i>
|
||||||
|
<span>{{ 'entry.view.left_menu.delete'|trans }}</span>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
<div class="collapsible-body"></div>
|
<div class="collapsible-body"></div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
@ -135,14 +159,22 @@
|
||||||
<ul>
|
<ul>
|
||||||
{% if craue_setting('share_public') %}
|
{% if craue_setting('share_public') %}
|
||||||
<li>
|
<li>
|
||||||
<a href="{{ path('share', {'id': entry.id}) }}" target="_blank" title="{{ 'entry.view.left_menu.public_link'|trans }}" class="tool icon-eye">
|
<form action="{{ path('share', {'id': entry.id}) }}" method="post">
|
||||||
<span>{{ 'entry.view.left_menu.public_link'|trans }}</span>
|
<input type="hidden" name="token" value="{{ csrf_token('share-entry') }}"/>
|
||||||
</a>
|
|
||||||
|
<button type="submit" formtarget="_blank" class="btn-link tool icon-eye" title="{{ 'entry.view.left_menu.public_link'|trans }}">
|
||||||
|
<span>{{ 'entry.view.left_menu.public_link'|trans }}</span>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="{{ path('delete_share', {'id': entry.id}) }}" title="{{ 'entry.view.left_menu.delete_public_link'|trans }}" class="tool icon-no-eye">
|
<form action="{{ path('delete_share', {'id': entry.id}) }}" method="post">
|
||||||
<span>{{ 'entry.view.left_menu.delete_public_link'|trans }}</span>
|
<input type="hidden" name="token" value="{{ csrf_token('delete-share') }}"/>
|
||||||
</a>
|
|
||||||
|
<button type="submit" class="btn-link tool icon-no-eye" title="{{ 'entry.view.left_menu.delete_public_link'|trans }}">
|
||||||
|
<span>{{ 'entry.view.left_menu.delete_public_link'|trans }}</span>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if craue_setting('share_twitter') %}
|
{% if craue_setting('share_twitter') %}
|
||||||
|
@ -300,9 +332,33 @@
|
||||||
<i class="material-icons">menu</i>
|
<i class="material-icons">menu</i>
|
||||||
</a>
|
</a>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a class="btn-floating" href="{{ path('archive_entry', {'id': entry.id, redirect: current_path}) }}"><i class="material-icons">{% if entry.isArchived == 0 %}done{% else %}unarchive{% endif %}</i></a></li>
|
<li>
|
||||||
<li><a class="btn-floating" href="{{ path('star_entry', {'id': entry.id, redirect: current_path}) }}"><i class="material-icons">{% if entry.isStarred == 0 %}star_outline{% else %}star{% endif %}</i></a></li>
|
<form action="{{ path('archive_entry', {'id': entry.id, redirect: current_path}) }}" method="post" class="inline-block">
|
||||||
<li><a class="btn-floating" href="{{ path('delete_entry', {'id': entry.id, redirect: current_path}) }}" onclick="return confirm('{{ 'entry.confirm.delete'|trans|escape('js') }}')"><i class="material-icons">delete</i></a></li>
|
<input type="hidden" name="token" value="{{ csrf_token('archive-entry') }}"/>
|
||||||
|
|
||||||
|
<button type="submit" class="btn-floating">
|
||||||
|
<i class="material-icons">{% if entry.isArchived == 0 %}done{% else %}unarchive{% endif %}</i>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<form action="{{ path('star_entry', {'id': entry.id, redirect: current_path}) }}" method="post" class="inline-block">
|
||||||
|
<input type="hidden" name="token" value="{{ csrf_token('star-entry') }}"/>
|
||||||
|
|
||||||
|
<button type="submit" class="btn-floating">
|
||||||
|
<i class="material-icons">{% if entry.isStarred == 0 %}star_outline{% else %}star{% endif %}</i>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<form action="{{ path('delete_entry', {'id': entry.id, redirect: current_path}) }}" method="post" class="inline-block">
|
||||||
|
<input type="hidden" name="token" value="{{ csrf_token('delete-entry') }}"/>
|
||||||
|
|
||||||
|
<button type="submit" class="btn-floating" onclick="return confirm('{{ 'entry.confirm.delete'|trans|escape('js') }}')">
|
||||||
|
<i class="material-icons">delete</i>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -28,9 +28,13 @@
|
||||||
<i class="material-icons">mode_edit</i>
|
<i class="material-icons">mode_edit</i>
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a id="delete-{{ tag.slug }}" href="{{ path('tag_delete', {'slug': tag.slug, redirect: current_path}) }}" class="card-tag-icon card-tag-delete" onclick="return confirm('{{ 'tag.confirm.delete'|trans({'%name%': tag.label})|escape('js') }}')">
|
<form action="{{ path('tag_delete', {'slug': tag.slug, redirect: current_path}) }}" method="post" class="inline-block">
|
||||||
<i class="material-icons">delete</i>
|
<input type="hidden" name="token" value="{{ csrf_token('tag-delete') }}"/>
|
||||||
</a>
|
|
||||||
|
<button type="submit" class="btn-link card-tag-icon card-tag-delete" onclick="return confirm('{{ 'tag.confirm.delete'|trans({'%name%': tag.label})|escape('js') }}')">
|
||||||
|
<i class="material-icons">delete</i>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
{% if app.user.config.feedToken %}
|
{% if app.user.config.feedToken %}
|
||||||
<a rel="alternate" type="application/atom+xml" href="{{ path('tag_feed', {'username': app.user.username, 'token': app.user.config.feedToken, 'slug': tag.slug}) }}" class="card-tag-icon"><i class="material-icons">rss_feed</i></a>
|
<a rel="alternate" type="application/atom+xml" href="{{ path('tag_feed', {'username': app.user.username, 'token': app.user.config.feedToken, 'slug': tag.slug}) }}" class="card-tag-icon"><i class="material-icons">rss_feed</i></a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -16,9 +16,23 @@
|
||||||
{% endblock fos_user_content %}
|
{% endblock fos_user_content %}
|
||||||
</div>
|
</div>
|
||||||
<div class="center">
|
<div class="center">
|
||||||
<a href="{{ path('changeLocale', {'language': 'de'}) }}">Deutsch</a> –
|
<form action="{{ path('changeLocale', {'language': 'de'}) }}" method="post" class="inline-block">
|
||||||
<a href="{{ path('changeLocale', {'language': 'en'}) }}">English</a> –
|
<input type="hidden" name="token" value="{{ csrf_token('change-locale') }}"/>
|
||||||
<a href="{{ path('changeLocale', {'language': 'fr'}) }}">Français</a>
|
|
||||||
|
<button type="submit" class="btn-link">Deutsch</button>
|
||||||
|
</form>
|
||||||
|
–
|
||||||
|
<form action="{{ path('changeLocale', {'language': 'en'}) }}" method="post" class="inline-block">
|
||||||
|
<input type="hidden" name="token" value="{{ csrf_token('change-locale') }}"/>
|
||||||
|
|
||||||
|
<button type="submit" class="btn-link">English</button>
|
||||||
|
</form>
|
||||||
|
–
|
||||||
|
<form action="{{ path('changeLocale', {'language': 'fr'}) }}" method="post" class="inline-block">
|
||||||
|
<input type="hidden" name="token" value="{{ csrf_token('change-locale') }}"/>
|
||||||
|
|
||||||
|
<button type="submit" class="btn-link">Français</button>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
|
@ -105,7 +105,7 @@ class DeveloperControllerTest extends WallabagCoreTestCase
|
||||||
|
|
||||||
$this->logInAs('bob');
|
$this->logInAs('bob');
|
||||||
$client->request('POST', '/developer/client/delete/' . $adminApiClient->getId());
|
$client->request('POST', '/developer/client/delete/' . $adminApiClient->getId());
|
||||||
$this->assertSame(403, $client->getResponse()->getStatusCode());
|
$this->assertSame(400, $client->getResponse()->getStatusCode());
|
||||||
|
|
||||||
// Try to remove the admin's client with the good user
|
// Try to remove the admin's client with the good user
|
||||||
$this->logInAs('admin');
|
$this->logInAs('admin');
|
||||||
|
|
|
@ -328,7 +328,8 @@ class ConfigControllerTest extends WallabagCoreTestCase
|
||||||
$this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text']));
|
$this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text']));
|
||||||
$this->assertStringContainsString('config.form_feed.no_token', $body[0]);
|
$this->assertStringContainsString('config.form_feed.no_token', $body[0]);
|
||||||
|
|
||||||
$client->request('GET', '/generate-token');
|
$client->submit($crawler->selectButton('config.form_feed.token_create')->form());
|
||||||
|
|
||||||
$this->assertSame(302, $client->getResponse()->getStatusCode());
|
$this->assertSame(302, $client->getResponse()->getStatusCode());
|
||||||
|
|
||||||
$crawler = $client->followRedirect();
|
$crawler = $client->followRedirect();
|
||||||
|
@ -337,38 +338,34 @@ class ConfigControllerTest extends WallabagCoreTestCase
|
||||||
$this->assertStringContainsString('config.form_feed.token_reset', $body[0]);
|
$this->assertStringContainsString('config.form_feed.token_reset', $body[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGenerateTokenAjax()
|
|
||||||
{
|
|
||||||
$this->logInAs('admin');
|
|
||||||
$client = $this->getTestClient();
|
|
||||||
|
|
||||||
$client->request(
|
|
||||||
'GET',
|
|
||||||
'/generate-token',
|
|
||||||
[],
|
|
||||||
[],
|
|
||||||
['HTTP_X-Requested-With' => 'XMLHttpRequest']
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->assertSame(200, $client->getResponse()->getStatusCode());
|
|
||||||
$content = json_decode($client->getResponse()->getContent(), true);
|
|
||||||
$this->assertArrayHasKey('token', $content);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testRevokeTokenAjax()
|
public function testRevokeTokenAjax()
|
||||||
{
|
{
|
||||||
$this->logInAs('admin');
|
$this->logInAs('admin');
|
||||||
$client = $this->getTestClient();
|
$client = $this->getTestClient();
|
||||||
|
|
||||||
$client->request(
|
// set the token
|
||||||
'GET',
|
$em = $client->getContainer()->get(EntityManagerInterface::class);
|
||||||
'/revoke-token',
|
$user = $em
|
||||||
[],
|
->getRepository(User::class)
|
||||||
[],
|
->findOneByUsername('admin');
|
||||||
['HTTP_X-Requested-With' => 'XMLHttpRequest']
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->assertSame(200, $client->getResponse()->getStatusCode());
|
if (!$user) {
|
||||||
|
$this->markTestSkipped('No user found in db.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$config = $user->getConfig();
|
||||||
|
$config->setFeedToken('abcd1234');
|
||||||
|
$em->persist($config);
|
||||||
|
$em->flush();
|
||||||
|
|
||||||
|
$crawler = $client->request('GET', '/config');
|
||||||
|
|
||||||
|
$client->submit($crawler->selectButton('config.form_feed.token_revoke')->form());
|
||||||
|
|
||||||
|
$crawler = $client->followRedirect();
|
||||||
|
|
||||||
|
$this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text']));
|
||||||
|
$this->assertStringContainsString('config.form_feed.token_create', $body[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testFeedUpdate()
|
public function testFeedUpdate()
|
||||||
|
@ -484,9 +481,8 @@ class ConfigControllerTest extends WallabagCoreTestCase
|
||||||
|
|
||||||
$this->assertStringContainsString('readingTime <= 30', $crawler->filter('body')->extract(['_text'])[0]);
|
$this->assertStringContainsString('readingTime <= 30', $crawler->filter('body')->extract(['_text'])[0]);
|
||||||
|
|
||||||
$deleteLink = $crawler->filter('.delete_tagging_rule')->last()->link();
|
$crawler = $client->submit($crawler->filter('#set5')->selectButton('delete')->form());
|
||||||
|
|
||||||
$crawler = $client->click($deleteLink);
|
|
||||||
$this->assertSame(302, $client->getResponse()->getStatusCode());
|
$this->assertSame(302, $client->getResponse()->getStatusCode());
|
||||||
|
|
||||||
$crawler = $client->followRedirect();
|
$crawler = $client->followRedirect();
|
||||||
|
@ -576,11 +572,11 @@ class ConfigControllerTest extends WallabagCoreTestCase
|
||||||
->getRepository(TaggingRule::class)
|
->getRepository(TaggingRule::class)
|
||||||
->findAll()[0];
|
->findAll()[0];
|
||||||
|
|
||||||
$crawler = $client->request('GET', '/tagging-rule/delete/' . $rule->getId());
|
$crawler = $client->request('POST', '/tagging-rule/delete/' . $rule->getId());
|
||||||
|
|
||||||
$this->assertSame(403, $client->getResponse()->getStatusCode());
|
$this->assertSame(400, $client->getResponse()->getStatusCode());
|
||||||
$this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text']));
|
$this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text']));
|
||||||
$this->assertStringContainsString('You can not access this rule', $body[0]);
|
$this->assertStringContainsString('Bad CSRF token.', $body[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testEditingTaggingRuleFromAnOtherUser()
|
public function testEditingTaggingRuleFromAnOtherUser()
|
||||||
|
@ -646,9 +642,9 @@ class ConfigControllerTest extends WallabagCoreTestCase
|
||||||
|
|
||||||
$this->assertStringContainsString('host = "example.org"', $crawler->filter('body')->extract(['_text'])[0]);
|
$this->assertStringContainsString('host = "example.org"', $crawler->filter('body')->extract(['_text'])[0]);
|
||||||
|
|
||||||
$deleteLink = $crawler->filter('div[id=set6] a.delete')->last()->link();
|
$form = $crawler->filter('#set6')->selectButton('delete')->form();
|
||||||
|
|
||||||
$crawler = $client->click($deleteLink);
|
$crawler = $client->submit($form);
|
||||||
$this->assertSame(302, $client->getResponse()->getStatusCode());
|
$this->assertSame(302, $client->getResponse()->getStatusCode());
|
||||||
|
|
||||||
$crawler = $client->followRedirect();
|
$crawler = $client->followRedirect();
|
||||||
|
@ -713,11 +709,11 @@ class ConfigControllerTest extends WallabagCoreTestCase
|
||||||
->getRepository(IgnoreOriginUserRule::class)
|
->getRepository(IgnoreOriginUserRule::class)
|
||||||
->findAll()[0];
|
->findAll()[0];
|
||||||
|
|
||||||
$crawler = $client->request('GET', '/ignore-origin-user-rule/edit/' . $rule->getId());
|
$crawler = $client->request('POST', '/ignore-origin-user-rule/delete/' . $rule->getId());
|
||||||
|
|
||||||
$this->assertSame(403, $client->getResponse()->getStatusCode());
|
$this->assertSame(400, $client->getResponse()->getStatusCode());
|
||||||
$this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text']));
|
$this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text']));
|
||||||
$this->assertStringContainsString('You can not access this rule', $body[0]);
|
$this->assertStringContainsString('Bad CSRF token.', $body[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testEditingIgnoreOriginRuleFromAnOtherUser()
|
public function testEditingIgnoreOriginRuleFromAnOtherUser()
|
||||||
|
@ -798,7 +794,7 @@ class ConfigControllerTest extends WallabagCoreTestCase
|
||||||
$this->assertStringNotContainsString('config.form_user.delete.button', $body[0]);
|
$this->assertStringNotContainsString('config.form_user.delete.button', $body[0]);
|
||||||
|
|
||||||
$client->request('POST', '/account/delete');
|
$client->request('POST', '/account/delete');
|
||||||
$this->assertSame(403, $client->getResponse()->getStatusCode());
|
$this->assertSame(400, $client->getResponse()->getStatusCode());
|
||||||
|
|
||||||
$user = $em
|
$user = $em
|
||||||
->getRepository(User::class)
|
->getRepository(User::class)
|
||||||
|
@ -1120,37 +1116,38 @@ class ConfigControllerTest extends WallabagCoreTestCase
|
||||||
$this->logInAs('admin');
|
$this->logInAs('admin');
|
||||||
$client = $this->getTestClient();
|
$client = $this->getTestClient();
|
||||||
|
|
||||||
$client->request('GET', '/unread/list');
|
$crawler = $client->request('GET', '/unread/list');
|
||||||
|
|
||||||
$this->assertStringContainsString('row data', $client->getResponse()->getContent());
|
$this->assertStringContainsString('row data', $client->getResponse()->getContent());
|
||||||
|
|
||||||
$client->request('GET', '/config/view-mode');
|
$form = $crawler->filter('.nb-results')->selectButton('view_list')->form();
|
||||||
$crawler = $client->followRedirect();
|
|
||||||
|
|
||||||
$client->request('GET', '/unread/list');
|
$client->submit($form);
|
||||||
|
|
||||||
|
$client->followRedirect();
|
||||||
|
|
||||||
$this->assertStringContainsString('collection', $client->getResponse()->getContent());
|
$this->assertStringContainsString('collection', $client->getResponse()->getContent());
|
||||||
|
|
||||||
$client->request('GET', '/config/view-mode');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testChangeLocaleWithoutReferer()
|
public function testChangeLocaleWithoutReferer()
|
||||||
{
|
{
|
||||||
$client = $this->getTestClient();
|
$client = $this->getTestClient();
|
||||||
|
|
||||||
$client->request('GET', '/locale/de');
|
$crawler = $client->request('POST', '/locale/de');
|
||||||
$client->followRedirect();
|
|
||||||
|
|
||||||
$this->assertSame('de', $client->getRequest()->getLocale());
|
$this->assertSame(400, $client->getResponse()->getStatusCode());
|
||||||
$this->assertSame('de', $client->getContainer()->get(SessionInterface::class)->get('_locale'));
|
$this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text']));
|
||||||
|
$this->assertStringContainsString('Bad CSRF token.', $body[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testChangeLocaleWithReferer()
|
public function testChangeLocaleWithReferer()
|
||||||
{
|
{
|
||||||
$client = $this->getTestClient();
|
$client = $this->getTestClient();
|
||||||
|
|
||||||
$client->request('GET', '/login');
|
$crawler = $client->request('GET', '/login');
|
||||||
$client->request('GET', '/locale/de');
|
|
||||||
|
$client->submit($crawler->selectButton('Deutsch')->form());
|
||||||
|
|
||||||
$client->followRedirect();
|
$client->followRedirect();
|
||||||
|
|
||||||
$this->assertSame('de', $client->getRequest()->getLocale());
|
$this->assertSame('de', $client->getRequest()->getLocale());
|
||||||
|
@ -1161,8 +1158,12 @@ class ConfigControllerTest extends WallabagCoreTestCase
|
||||||
{
|
{
|
||||||
$client = $this->getTestClient();
|
$client = $this->getTestClient();
|
||||||
|
|
||||||
$client->request('GET', '/login');
|
$crawler = $client->request('GET', '/login');
|
||||||
$client->request('GET', '/locale/yuyuyuyu');
|
$token = $crawler->filter('form[action="/locale/de"] input[name=token]')->attr('value');
|
||||||
|
|
||||||
|
$client->request('POST', '/locale/yuyuyuyu', [
|
||||||
|
'token' => $token,
|
||||||
|
]);
|
||||||
$client->followRedirect();
|
$client->followRedirect();
|
||||||
|
|
||||||
$this->assertNotSame('yuyuyuyu', $client->getRequest()->getLocale());
|
$this->assertNotSame('yuyuyuyu', $client->getRequest()->getLocale());
|
||||||
|
@ -1382,7 +1383,5 @@ class ConfigControllerTest extends WallabagCoreTestCase
|
||||||
$client->request('GET', '/unread/list');
|
$client->request('GET', '/unread/list');
|
||||||
|
|
||||||
$this->assertStringNotContainsString('class="preview"', $client->getResponse()->getContent());
|
$this->assertStringNotContainsString('class="preview"', $client->getResponse()->getContent());
|
||||||
|
|
||||||
$client->request('GET', '/config/view-mode');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -509,7 +509,9 @@ class EntryControllerTest extends WallabagCoreTestCase
|
||||||
$this->getEntityManager()->flush();
|
$this->getEntityManager()->flush();
|
||||||
$this->getEntityManager()->clear();
|
$this->getEntityManager()->clear();
|
||||||
|
|
||||||
$client->request('GET', '/reload/' . $entry->getId());
|
$crawler = $client->request('GET', '/view/' . $entry->getId());
|
||||||
|
|
||||||
|
$client->submit($crawler->selectButton('entry.view.left_menu.re_fetch_content')->form());
|
||||||
|
|
||||||
$this->assertSame(302, $client->getResponse()->getStatusCode());
|
$this->assertSame(302, $client->getResponse()->getStatusCode());
|
||||||
|
|
||||||
|
@ -530,7 +532,9 @@ class EntryControllerTest extends WallabagCoreTestCase
|
||||||
$this->getEntityManager()->persist($entry);
|
$this->getEntityManager()->persist($entry);
|
||||||
$this->getEntityManager()->flush();
|
$this->getEntityManager()->flush();
|
||||||
|
|
||||||
$client->request('GET', '/reload/' . $entry->getId());
|
$crawler = $client->request('GET', '/view/' . $entry->getId());
|
||||||
|
|
||||||
|
$client->submit($crawler->selectButton('entry.view.left_menu.re_fetch_content')->form());
|
||||||
|
|
||||||
$this->assertSame(302, $client->getResponse()->getStatusCode());
|
$this->assertSame(302, $client->getResponse()->getStatusCode());
|
||||||
|
|
||||||
|
@ -641,7 +645,9 @@ class EntryControllerTest extends WallabagCoreTestCase
|
||||||
$this->getEntityManager()->flush();
|
$this->getEntityManager()->flush();
|
||||||
$this->getEntityManager()->clear();
|
$this->getEntityManager()->clear();
|
||||||
|
|
||||||
$client->request('GET', '/archive/' . $entry->getId());
|
$crawler = $client->request('GET', '/view/' . $entry->getId());
|
||||||
|
|
||||||
|
$client->submit($crawler->filter('.left-bar')->selectButton('entry.view.left_menu.set_as_read')->form());
|
||||||
|
|
||||||
$this->assertSame(302, $client->getResponse()->getStatusCode());
|
$this->assertSame(302, $client->getResponse()->getStatusCode());
|
||||||
|
|
||||||
|
@ -664,7 +670,9 @@ class EntryControllerTest extends WallabagCoreTestCase
|
||||||
$this->getEntityManager()->flush();
|
$this->getEntityManager()->flush();
|
||||||
$this->getEntityManager()->clear();
|
$this->getEntityManager()->clear();
|
||||||
|
|
||||||
$client->request('GET', '/star/' . $entry->getId());
|
$crawler = $client->request('GET', '/view/' . $entry->getId());
|
||||||
|
|
||||||
|
$client->submit($crawler->filter('.left-bar')->selectButton('entry.view.left_menu.set_as_starred')->form());
|
||||||
|
|
||||||
$this->assertSame(302, $client->getResponse()->getStatusCode());
|
$this->assertSame(302, $client->getResponse()->getStatusCode());
|
||||||
|
|
||||||
|
@ -686,13 +694,11 @@ class EntryControllerTest extends WallabagCoreTestCase
|
||||||
$this->getEntityManager()->persist($entry);
|
$this->getEntityManager()->persist($entry);
|
||||||
$this->getEntityManager()->flush();
|
$this->getEntityManager()->flush();
|
||||||
|
|
||||||
$client->request('GET', '/delete/' . $entry->getId());
|
$crawler = $client->request('POST', '/delete/' . $entry->getId());
|
||||||
|
|
||||||
$this->assertSame(302, $client->getResponse()->getStatusCode());
|
$this->assertSame(400, $client->getResponse()->getStatusCode());
|
||||||
|
$this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text']));
|
||||||
$client->request('GET', '/delete/' . $entry->getId());
|
$this->assertStringContainsString('Bad CSRF token.', $body[0]);
|
||||||
|
|
||||||
$this->assertSame(404, $client->getResponse()->getStatusCode());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -728,10 +734,11 @@ class EntryControllerTest extends WallabagCoreTestCase
|
||||||
$em->persist($content);
|
$em->persist($content);
|
||||||
$em->flush();
|
$em->flush();
|
||||||
|
|
||||||
$client->request('GET', '/view/' . $content->getId());
|
$crawler = $client->request('GET', '/view/' . $content->getId());
|
||||||
$this->assertSame(200, $client->getResponse()->getStatusCode());
|
$this->assertSame(200, $client->getResponse()->getStatusCode());
|
||||||
|
|
||||||
$client->request('GET', '/delete/' . $content->getId());
|
$client->submit($crawler->filter('.left-bar')->selectButton('entry.view.left_menu.delete')->form());
|
||||||
|
|
||||||
$this->assertSame(302, $client->getResponse()->getStatusCode());
|
$this->assertSame(302, $client->getResponse()->getStatusCode());
|
||||||
|
|
||||||
$client->followRedirect();
|
$client->followRedirect();
|
||||||
|
@ -1148,7 +1155,10 @@ class EntryControllerTest extends WallabagCoreTestCase
|
||||||
$this->assertSame(404, $client->getResponse()->getStatusCode());
|
$this->assertSame(404, $client->getResponse()->getStatusCode());
|
||||||
|
|
||||||
// generating the uid
|
// generating the uid
|
||||||
$client->request('GET', '/share/' . $content->getId());
|
$crawler = $client->request('GET', '/view/' . $content->getId());
|
||||||
|
|
||||||
|
$client->submit($crawler->filter('.left-bar')->selectButton('entry.view.left_menu.public_link')->form());
|
||||||
|
|
||||||
$this->assertSame(302, $client->getResponse()->getStatusCode());
|
$this->assertSame(302, $client->getResponse()->getStatusCode());
|
||||||
|
|
||||||
$shareUrl = $client->getResponse()->getTargetUrl();
|
$shareUrl = $client->getResponse()->getTargetUrl();
|
||||||
|
@ -1175,12 +1185,19 @@ class EntryControllerTest extends WallabagCoreTestCase
|
||||||
$this->assertSame(404, $client->getResponse()->getStatusCode());
|
$this->assertSame(404, $client->getResponse()->getStatusCode());
|
||||||
|
|
||||||
// removing the share
|
// removing the share
|
||||||
$client->request('GET', '/share/delete/' . $content->getId());
|
$client->getContainer()->get(Config::class)->set('share_public', 1);
|
||||||
|
$this->logInAs('admin');
|
||||||
|
$crawler = $client->request('GET', '/view/' . $content->getId());
|
||||||
|
|
||||||
|
$client->submit($crawler->filter('.left-bar')->selectButton('entry.view.left_menu.delete_public_link')->form());
|
||||||
|
|
||||||
$this->assertSame(302, $client->getResponse()->getStatusCode());
|
$this->assertSame(302, $client->getResponse()->getStatusCode());
|
||||||
|
|
||||||
// share is now disable
|
// share is now removed
|
||||||
$client->request('GET', '/share/' . $content->getUid());
|
$client->request('GET', '/share/' . $content->getUid());
|
||||||
$this->assertSame(404, $client->getResponse()->getStatusCode());
|
$this->assertSame(404, $client->getResponse()->getStatusCode());
|
||||||
|
|
||||||
|
$client->getContainer()->get(Config::class)->set('share_public', 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1256,7 +1273,9 @@ class EntryControllerTest extends WallabagCoreTestCase
|
||||||
->getRepository(Entry::class)
|
->getRepository(Entry::class)
|
||||||
->findByUrlAndUserId($url, $this->getLoggedInUserId());
|
->findByUrlAndUserId($url, $this->getLoggedInUserId());
|
||||||
|
|
||||||
$client->request('GET', '/delete/' . $content->getId());
|
$crawler = $client->request('GET', '/view/' . $content->getId());
|
||||||
|
|
||||||
|
$client->submit($crawler->filter('.left-bar')->selectButton('entry.view.left_menu.delete')->form());
|
||||||
|
|
||||||
$this->assertSame(302, $client->getResponse()->getStatusCode());
|
$this->assertSame(302, $client->getResponse()->getStatusCode());
|
||||||
|
|
||||||
|
@ -1279,8 +1298,9 @@ class EntryControllerTest extends WallabagCoreTestCase
|
||||||
|
|
||||||
$this->getEntityManager()->flush();
|
$this->getEntityManager()->flush();
|
||||||
|
|
||||||
$client->request('GET', '/view/' . $entry->getId());
|
$crawler = $client->request('GET', '/view/' . $entry->getId());
|
||||||
$client->request('GET', '/archive/' . $entry->getId());
|
|
||||||
|
$client->submit($crawler->filter('.left-bar')->selectButton('entry.view.left_menu.set_as_read')->form());
|
||||||
|
|
||||||
$this->assertSame(302, $client->getResponse()->getStatusCode());
|
$this->assertSame(302, $client->getResponse()->getStatusCode());
|
||||||
$this->assertSame('/', $client->getResponse()->headers->get('location'));
|
$this->assertSame('/', $client->getResponse()->headers->get('location'));
|
||||||
|
@ -1304,8 +1324,7 @@ class EntryControllerTest extends WallabagCoreTestCase
|
||||||
|
|
||||||
$crawler = $client->request('GET', '/view/' . $entry->getId());
|
$crawler = $client->request('GET', '/view/' . $entry->getId());
|
||||||
|
|
||||||
$link = $crawler->filter('a[id="markAsRead"]')->link();
|
$client->submit($crawler->filter('.left-bar')->selectButton('entry.view.left_menu.set_as_read')->form());
|
||||||
$client->click($link);
|
|
||||||
|
|
||||||
$this->assertSame(302, $client->getResponse()->getStatusCode());
|
$this->assertSame(302, $client->getResponse()->getStatusCode());
|
||||||
$this->assertStringContainsString('/view/' . $entry->getId(), $client->getResponse()->headers->get('location'));
|
$this->assertStringContainsString('/view/' . $entry->getId(), $client->getResponse()->headers->get('location'));
|
||||||
|
@ -1429,7 +1448,8 @@ class EntryControllerTest extends WallabagCoreTestCase
|
||||||
$crawler = $client->submit($form, $data);
|
$crawler = $client->submit($form, $data);
|
||||||
|
|
||||||
$this->assertCount(1, $crawler->filter($this->entryDataTestAttribute));
|
$this->assertCount(1, $crawler->filter($this->entryDataTestAttribute));
|
||||||
$client->request('GET', '/delete/' . $entry->getId());
|
|
||||||
|
$client->submit($crawler->filter('.tools, .tools-list')->selectButton('delete')->form());
|
||||||
|
|
||||||
// test on list of all articles
|
// test on list of all articles
|
||||||
$crawler = $client->request('GET', '/all/list');
|
$crawler = $client->request('GET', '/all/list');
|
||||||
|
@ -1500,8 +1520,8 @@ class EntryControllerTest extends WallabagCoreTestCase
|
||||||
|
|
||||||
$crawler = $client->submit($form, $data);
|
$crawler = $client->submit($form, $data);
|
||||||
$currentUrl = $client->getRequest()->getUri();
|
$currentUrl = $client->getRequest()->getUri();
|
||||||
$element = $crawler->filter('a[data-action="delete"]')->link();
|
$form = $crawler->filter('.tools, .tools-list')->selectButton('delete')->form();
|
||||||
$client->click($element);
|
$client->submit($form);
|
||||||
$client->followRedirect();
|
$client->followRedirect();
|
||||||
$nextUrl = $client->getRequest()->getUri();
|
$nextUrl = $client->getRequest()->getUri();
|
||||||
$this->assertSame($currentUrl, $nextUrl);
|
$this->assertSame($currentUrl, $nextUrl);
|
||||||
|
@ -1674,7 +1694,7 @@ class EntryControllerTest extends WallabagCoreTestCase
|
||||||
$this->assertSame('example.com', $content->getDomainName());
|
$this->assertSame('example.com', $content->getDomainName());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testEntryDeleteTagLink()
|
public function testEntryDeleteTagForm()
|
||||||
{
|
{
|
||||||
$this->logInAs('admin');
|
$this->logInAs('admin');
|
||||||
$client = $this->getTestClient();
|
$client = $this->getTestClient();
|
||||||
|
@ -1685,10 +1705,7 @@ class EntryControllerTest extends WallabagCoreTestCase
|
||||||
|
|
||||||
$crawler = $client->request('GET', '/view/' . $entry->getId());
|
$crawler = $client->request('GET', '/view/' . $entry->getId());
|
||||||
|
|
||||||
// As long as the deletion link of a tag is following
|
$link = $crawler->filter('body div#article div.tools ul.tags li.chip form')->extract(['action'])[0];
|
||||||
// a link to the tag view, we take the second one to retrieve
|
|
||||||
// the deletion link of the first tag
|
|
||||||
$link = $crawler->filter('body div#article div.tools ul.tags li.chip a')->extract(['href'])[1];
|
|
||||||
|
|
||||||
$this->assertStringStartsWith(sprintf('/remove-tag/%s/%s', $entry->getId(), $tag->getId()), $link);
|
$this->assertStringStartsWith(sprintf('/remove-tag/%s/%s', $entry->getId(), $tag->getId()), $link);
|
||||||
}
|
}
|
||||||
|
@ -1744,11 +1761,15 @@ class EntryControllerTest extends WallabagCoreTestCase
|
||||||
$this->getEntityManager()->clear();
|
$this->getEntityManager()->clear();
|
||||||
|
|
||||||
$entries = [];
|
$entries = [];
|
||||||
$entries[] = $entry1->getId();
|
$entries[] = $entry1Id = $entry1->getId();
|
||||||
$entries[] = $entry2->getId();
|
$entries[] = $entry2Id = $entry2->getId();
|
||||||
|
|
||||||
|
$crawler = $client->request('GET', '/all/list');
|
||||||
|
$token = $crawler->filter('#form_mass_action input[name=token]')->attr('value');
|
||||||
|
|
||||||
// Mass actions : archive
|
// Mass actions : archive
|
||||||
$client->request('POST', '/mass', [
|
$client->request('POST', '/mass', [
|
||||||
|
'token' => $token,
|
||||||
'toggle-archive' => '',
|
'toggle-archive' => '',
|
||||||
'entry-checkbox' => $entries,
|
'entry-checkbox' => $entries,
|
||||||
]);
|
]);
|
||||||
|
@ -1769,8 +1790,12 @@ class EntryControllerTest extends WallabagCoreTestCase
|
||||||
|
|
||||||
$this->assertSame(1, $res->isArchived());
|
$this->assertSame(1, $res->isArchived());
|
||||||
|
|
||||||
|
$crawler = $client->request('GET', '/all/list');
|
||||||
|
$token = $crawler->filter('#form_mass_action input[name=token]')->attr('value');
|
||||||
|
|
||||||
// Mass actions : star
|
// Mass actions : star
|
||||||
$client->request('POST', '/mass', [
|
$client->request('POST', '/mass', [
|
||||||
|
'token' => $token,
|
||||||
'toggle-star' => '',
|
'toggle-star' => '',
|
||||||
'entry-checkbox' => $entries,
|
'entry-checkbox' => $entries,
|
||||||
]);
|
]);
|
||||||
|
@ -1791,8 +1816,12 @@ class EntryControllerTest extends WallabagCoreTestCase
|
||||||
|
|
||||||
$this->assertSame(1, $res->isStarred());
|
$this->assertSame(1, $res->isStarred());
|
||||||
|
|
||||||
|
$crawler = $client->request('GET', '/all/list');
|
||||||
|
$token = $crawler->filter('#form_mass_action input[name=token]')->attr('value');
|
||||||
|
|
||||||
// Mass actions : tag
|
// Mass actions : tag
|
||||||
$client->request('POST', '/mass', [
|
$client->request('POST', '/mass', [
|
||||||
|
'token' => $token,
|
||||||
'tag' => '',
|
'tag' => '',
|
||||||
'tags' => 'foo',
|
'tags' => 'foo',
|
||||||
'entry-checkbox' => $entries,
|
'entry-checkbox' => $entries,
|
||||||
|
@ -1821,17 +1850,29 @@ class EntryControllerTest extends WallabagCoreTestCase
|
||||||
|
|
||||||
$this->assertNotContains('foo', $res->getTagsLabel());
|
$this->assertNotContains('foo', $res->getTagsLabel());
|
||||||
|
|
||||||
|
$crawler = $client->request('GET', '/all/list');
|
||||||
|
$token = $crawler->filter('#form_mass_action input[name=token]')->attr('value');
|
||||||
|
|
||||||
// Mass actions : delete
|
// Mass actions : delete
|
||||||
$client->request('POST', '/mass', [
|
$client->request('POST', '/mass', [
|
||||||
|
'token' => $token,
|
||||||
'delete' => '',
|
'delete' => '',
|
||||||
'entry-checkbox' => $entries,
|
'entry-checkbox' => $entries,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$client->request('GET', '/delete/' . $entry1->getId());
|
$res = $client->getContainer()
|
||||||
$this->assertSame(404, $client->getResponse()->getStatusCode());
|
->get(EntityManagerInterface::class)
|
||||||
|
->getRepository(Entry::class)
|
||||||
|
->find($entry1Id);
|
||||||
|
|
||||||
$client->request('GET', '/delete/' . $entry2->getId());
|
$this->assertNull($res);
|
||||||
$this->assertSame(404, $client->getResponse()->getStatusCode());
|
|
||||||
|
$res = $client->getContainer()
|
||||||
|
->get(EntityManagerInterface::class)
|
||||||
|
->getRepository(Entry::class)
|
||||||
|
->find($entry2Id);
|
||||||
|
|
||||||
|
$this->assertNull($res);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetSameDomainEntries()
|
public function testGetSameDomainEntries()
|
||||||
|
|
|
@ -126,8 +126,8 @@ class TagControllerTest extends WallabagCoreTestCase
|
||||||
$crawler = $client->request('GET', '/view/' . $entry->getId());
|
$crawler = $client->request('GET', '/view/' . $entry->getId());
|
||||||
$entryUri = $client->getRequest()->getRequestUri();
|
$entryUri = $client->getRequest()->getRequestUri();
|
||||||
|
|
||||||
$link = $crawler->filter('a[href^="/remove-tag/' . $entry->getId() . '/' . $tag->getId() . '"]')->link();
|
$form = $crawler->filter('form[action^="/remove-tag/' . $entry->getId() . '/' . $tag->getId() . '"]')->form();
|
||||||
$client->click($link);
|
$client->submit($form);
|
||||||
|
|
||||||
$this->assertSame(302, $client->getResponse()->getStatusCode());
|
$this->assertSame(302, $client->getResponse()->getStatusCode());
|
||||||
$this->assertSame($entryUri, $client->getResponse()->getTargetUrl());
|
$this->assertSame($entryUri, $client->getResponse()->getTargetUrl());
|
||||||
|
@ -136,9 +136,8 @@ class TagControllerTest extends WallabagCoreTestCase
|
||||||
$entry = $this->getEntityManager()->getRepository(Entry::class)->find($entry->getId());
|
$entry = $this->getEntityManager()->getRepository(Entry::class)->find($entry->getId());
|
||||||
$this->assertNotContains($this->tagName, $entry->getTagsLabel());
|
$this->assertNotContains($this->tagName, $entry->getTagsLabel());
|
||||||
|
|
||||||
$client->request('GET', '/remove-tag/' . $entry->getId() . '/' . $tag->getId());
|
$client->request('GET', '/view/' . $entry->getId());
|
||||||
|
$this->assertStringNotContainsString('/remove-tag/' . $entry->getId() . '/' . $tag->getId(), $client->getResponse()->getContent());
|
||||||
$this->assertSame(404, $client->getResponse()->getStatusCode());
|
|
||||||
|
|
||||||
$tag = $client->getContainer()
|
$tag = $client->getContainer()
|
||||||
->get(EntityManagerInterface::class)
|
->get(EntityManagerInterface::class)
|
||||||
|
@ -169,8 +168,8 @@ class TagControllerTest extends WallabagCoreTestCase
|
||||||
$this->getEntityManager()->clear();
|
$this->getEntityManager()->clear();
|
||||||
|
|
||||||
$crawler = $client->request('GET', '/tag/list');
|
$crawler = $client->request('GET', '/tag/list');
|
||||||
$link = $crawler->filter('a[id="delete-' . $tag->getSlug() . '"]')->link();
|
$form = $crawler->filter('#tag-' . $tag->getId())->selectButton('delete')->form();
|
||||||
$client->click($link);
|
$client->submit($form);
|
||||||
|
|
||||||
$tag = $client->getContainer()
|
$tag = $client->getContainer()
|
||||||
->get(EntityManagerInterface::class)
|
->get(EntityManagerInterface::class)
|
||||||
|
@ -548,7 +547,7 @@ class TagControllerTest extends WallabagCoreTestCase
|
||||||
|
|
||||||
$crawler = $client->submit($form, $data);
|
$crawler = $client->submit($form, $data);
|
||||||
|
|
||||||
$client->click($crawler->selectLink('entry.list.assign_search_tag')->link());
|
$client->submit($crawler->selectButton('entry.list.assign_search_tag')->form());
|
||||||
$client->followRedirect();
|
$client->followRedirect();
|
||||||
|
|
||||||
$entries = $client->getContainer()
|
$entries = $client->getContainer()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue