diff options
Diffstat (limited to 'src')
14 files changed, 257 insertions, 18 deletions
diff --git a/src/Wallabag/CoreBundle/Controller/TagController.php b/src/Wallabag/CoreBundle/Controller/TagController.php index a342ec0b..fd2069e0 100644 --- a/src/Wallabag/CoreBundle/Controller/TagController.php +++ b/src/Wallabag/CoreBundle/Controller/TagController.php | |||
@@ -4,10 +4,60 @@ namespace Wallabag\CoreBundle\Controller; | |||
4 | 4 | ||
5 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; | 5 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; |
6 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; | 6 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; |
7 | use Symfony\Component\HttpFoundation\Request; | ||
8 | use Wallabag\CoreBundle\Form\Type\NewTagType; | ||
9 | use Wallabag\CoreBundle\Entity\Tag; | ||
10 | use Wallabag\CoreBundle\Entity\Entry; | ||
7 | 11 | ||
8 | class TagController extends Controller | 12 | class TagController extends Controller |
9 | { | 13 | { |
10 | /** | 14 | /** |
15 | * @param Request $request | ||
16 | * | ||
17 | * @Route("/new-tag/{entry}", requirements={"entry" = "\d+"}, name="new_tag") | ||
18 | * | ||
19 | * @return \Symfony\Component\HttpFoundation\Response | ||
20 | */ | ||
21 | public function addTagFormAction(Request $request, Entry $entry) | ||
22 | { | ||
23 | $tag = new Tag($this->getUser()); | ||
24 | $form = $this->createForm(new NewTagType(), $tag); | ||
25 | $form->handleRequest($request); | ||
26 | |||
27 | if ($form->isValid()) { | ||
28 | $existingTag = $this->getDoctrine() | ||
29 | ->getRepository('WallabagCoreBundle:Tag') | ||
30 | ->findOneByLabelAndUserId($tag->getLabel(), $this->getUser()->getId()); | ||
31 | |||
32 | $em = $this->getDoctrine()->getManager(); | ||
33 | |||
34 | if (is_null($existingTag)) { | ||
35 | $entry->addTag($tag); | ||
36 | $em->persist($tag); | ||
37 | } else { | ||
38 | if (!$existingTag->hasEntry($entry)) { | ||
39 | $entry->addTag($existingTag); | ||
40 | $em->persist($existingTag); | ||
41 | } | ||
42 | } | ||
43 | |||
44 | $em->flush(); | ||
45 | |||
46 | $this->get('session')->getFlashBag()->add( | ||
47 | 'notice', | ||
48 | 'Tag added' | ||
49 | ); | ||
50 | |||
51 | return $this->redirect($this->generateUrl('view', array('id' => $entry->getId()))); | ||
52 | } | ||
53 | |||
54 | return $this->render('WallabagCoreBundle:Tag:new_form.html.twig', array( | ||
55 | 'form' => $form->createView(), | ||
56 | 'entry' => $entry, | ||
57 | )); | ||
58 | } | ||
59 | |||
60 | /** | ||
11 | * Shows tags for current user. | 61 | * Shows tags for current user. |
12 | * | 62 | * |
13 | * @Route("/tag/list", name="tag") | 63 | * @Route("/tag/list", name="tag") |
diff --git a/src/Wallabag/CoreBundle/Entity/Tag.php b/src/Wallabag/CoreBundle/Entity/Tag.php index 6f005314..97c4579f 100644 --- a/src/Wallabag/CoreBundle/Entity/Tag.php +++ b/src/Wallabag/CoreBundle/Entity/Tag.php | |||
@@ -96,6 +96,11 @@ class Tag | |||
96 | $this->entries[] = $entry; | 96 | $this->entries[] = $entry; |
97 | } | 97 | } |
98 | 98 | ||
99 | public function hasEntry($entry) | ||
100 | { | ||
101 | return $this->entries->contains($entry); | ||
102 | } | ||
103 | |||
99 | /** | 104 | /** |
100 | * @return User | 105 | * @return User |
101 | */ | 106 | */ |
diff --git a/src/Wallabag/CoreBundle/Event/Subscriber/CustomDoctrineORMSubscriber.php b/src/Wallabag/CoreBundle/Event/Subscriber/CustomDoctrineORMSubscriber.php new file mode 100644 index 00000000..cfdbfe97 --- /dev/null +++ b/src/Wallabag/CoreBundle/Event/Subscriber/CustomDoctrineORMSubscriber.php | |||
@@ -0,0 +1,38 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\Event\Subscriber; | ||
4 | |||
5 | use Lexik\Bundle\FormFilterBundle\Event\Subscriber\DoctrineORMSubscriber; | ||
6 | use Lexik\Bundle\FormFilterBundle\Event\GetFilterConditionEvent; | ||
7 | use Symfony\Component\EventDispatcher\EventSubscriberInterface; | ||
8 | |||
9 | /** | ||
10 | * This custom class override the default behavior of LexikFilterBundle on `filter_date_range` | ||
11 | * It converts a date_range to date_time_range to add hour to be able to grab a whole day (from 00:00:00 to 23:59:59). | ||
12 | */ | ||
13 | class CustomDoctrineORMSubscriber extends DoctrineORMSubscriber implements EventSubscriberInterface | ||
14 | { | ||
15 | /** | ||
16 | * @param GetFilterConditionEvent $event | ||
17 | */ | ||
18 | public function filterDateRange(GetFilterConditionEvent $event) | ||
19 | { | ||
20 | $expr = $event->getFilterQuery()->getExpressionBuilder(); | ||
21 | $values = $event->getValues(); | ||
22 | $value = $values['value']; | ||
23 | |||
24 | // left date should start at midnight | ||
25 | if (isset($value['left_date'][0]) && $value['left_date'][0] instanceof \DateTime) { | ||
26 | $value['left_date'][0]->setTime(0, 0, 0); | ||
27 | } | ||
28 | |||
29 | // right adte should end one second before midnight | ||
30 | if (isset($value['right_date'][0]) && $value['right_date'][0] instanceof \DateTime) { | ||
31 | $value['right_date'][0]->setTime(23, 59, 59); | ||
32 | } | ||
33 | |||
34 | if (isset($value['left_date'][0]) || isset($value['right_date'][0])) { | ||
35 | $event->setCondition($expr->dateTimeInRange($event->getField(), $value['left_date'][0], $value['right_date'][0])); | ||
36 | } | ||
37 | } | ||
38 | } | ||
diff --git a/src/Wallabag/CoreBundle/Filter/EntryFilterType.php b/src/Wallabag/CoreBundle/Filter/EntryFilterType.php index ff51785b..85d1a061 100644 --- a/src/Wallabag/CoreBundle/Filter/EntryFilterType.php +++ b/src/Wallabag/CoreBundle/Filter/EntryFilterType.php | |||
@@ -27,8 +27,9 @@ class EntryFilterType extends AbstractType | |||
27 | ), | 27 | ), |
28 | 'format' => 'dd/MM/yyyy', | 28 | 'format' => 'dd/MM/yyyy', |
29 | 'widget' => 'single_text', | 29 | 'widget' => 'single_text', |
30 | ), | 30 | ), |
31 | )) | 31 | ) |
32 | ) | ||
32 | ->add('domainName', 'filter_text', array( | 33 | ->add('domainName', 'filter_text', array( |
33 | 'apply_filter' => function (QueryInterface $filterQuery, $field, $values) { | 34 | 'apply_filter' => function (QueryInterface $filterQuery, $field, $values) { |
34 | $value = $values['value']; | 35 | $value = $values['value']; |
diff --git a/src/Wallabag/CoreBundle/Form/Type/NewTagType.php b/src/Wallabag/CoreBundle/Form/Type/NewTagType.php new file mode 100644 index 00000000..8e4ab649 --- /dev/null +++ b/src/Wallabag/CoreBundle/Form/Type/NewTagType.php | |||
@@ -0,0 +1,30 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\Form\Type; | ||
4 | |||
5 | use Symfony\Component\Form\AbstractType; | ||
6 | use Symfony\Component\Form\FormBuilderInterface; | ||
7 | use Symfony\Component\OptionsResolver\OptionsResolver; | ||
8 | |||
9 | class NewTagType extends AbstractType | ||
10 | { | ||
11 | public function buildForm(FormBuilderInterface $builder, array $options) | ||
12 | { | ||
13 | $builder | ||
14 | ->add('label', 'text', array('required' => true)) | ||
15 | ->add('save', 'submit') | ||
16 | ; | ||
17 | } | ||
18 | |||
19 | public function configureOptions(OptionsResolver $resolver) | ||
20 | { | ||
21 | $resolver->setDefaults(array( | ||
22 | 'data_class' => 'Wallabag\CoreBundle\Entity\Tag', | ||
23 | )); | ||
24 | } | ||
25 | |||
26 | public function getName() | ||
27 | { | ||
28 | return 'tag'; | ||
29 | } | ||
30 | } | ||
diff --git a/src/Wallabag/CoreBundle/Repository/TagRepository.php b/src/Wallabag/CoreBundle/Repository/TagRepository.php index 9c409607..ac3145a1 100644 --- a/src/Wallabag/CoreBundle/Repository/TagRepository.php +++ b/src/Wallabag/CoreBundle/Repository/TagRepository.php | |||
@@ -24,4 +24,21 @@ class TagRepository extends EntityRepository | |||
24 | 24 | ||
25 | return new Pagerfanta($pagerAdapter); | 25 | return new Pagerfanta($pagerAdapter); |
26 | } | 26 | } |
27 | |||
28 | /** | ||
29 | * Find a tag by its label and its owner. | ||
30 | * | ||
31 | * @param string $label | ||
32 | * @param int $userId | ||
33 | * | ||
34 | * @return Tag|null | ||
35 | */ | ||
36 | public function findOneByLabelAndUserId($label, $userId) | ||
37 | { | ||
38 | return $this->createQueryBuilder('t') | ||
39 | ->where('t.label = :label')->setParameter('label', $label) | ||
40 | ->andWhere('t.user = :user_id')->setParameter('user_id', $userId) | ||
41 | ->getQuery() | ||
42 | ->getOneOrNullResult(); | ||
43 | } | ||
27 | } | 44 | } |
diff --git a/src/Wallabag/CoreBundle/Resources/views/Entry/entry.html.twig b/src/Wallabag/CoreBundle/Resources/views/Entry/entry.html.twig index 00480d1a..18cfd59d 100644 --- a/src/Wallabag/CoreBundle/Resources/views/Entry/entry.html.twig +++ b/src/Wallabag/CoreBundle/Resources/views/Entry/entry.html.twig | |||
@@ -28,7 +28,8 @@ | |||
28 | <h1>{{ entry.title|raw }} <a href="{{ path('edit', { 'id': entry.id }) }}" title="{% trans %}Edit tags{% endtrans %}">✎</a></h1> | 28 | <h1>{{ entry.title|raw }} <a href="{{ path('edit', { 'id': entry.id }) }}" title="{% trans %}Edit tags{% endtrans %}">✎</a></h1> |
29 | </header> | 29 | </header> |
30 | <aside class="tags"> | 30 | <aside class="tags"> |
31 | tags: {% for tag in entry.tags %}<a href="./?view=tag&id={{ tag.id }}">{{ tag.label }}</a> {% endfor %}<a href="./?view=edit-tags&id={{ entry.id }}" title="{% trans %}Edit tags{% endtrans %}">✎</a> | 31 | {% for tag in entry.tags %}<span class="mdi-action-label-outline">{{ tag.label }}</span>{% endfor %} |
32 | {{ render(controller( "WallabagCoreBundle:Tag:addTagForm", { 'id': entry.id } )) }} | ||
32 | </aside> | 33 | </aside> |
33 | <article> | 34 | <article> |
34 | {{ entry.content | raw }} | 35 | {{ entry.content | raw }} |
diff --git a/src/Wallabag/CoreBundle/Resources/views/Tag/new_form.html.twig b/src/Wallabag/CoreBundle/Resources/views/Tag/new_form.html.twig new file mode 100644 index 00000000..0b5a530d --- /dev/null +++ b/src/Wallabag/CoreBundle/Resources/views/Tag/new_form.html.twig | |||
@@ -0,0 +1,15 @@ | |||
1 | <form name="tag" method="post" action="{{ path('new_tag', { 'entry': entry.id })}}"> | ||
2 | |||
3 | {% if form_errors(form) %} | ||
4 | <span class="black-text">{{ form_errors(form) }}</span> | ||
5 | {% endif %} | ||
6 | |||
7 | {% if form_errors(form.label) %} | ||
8 | <span class="black-text">{{ form_errors(form.label) }}</span> | ||
9 | {% endif %} | ||
10 | |||
11 | {{ form_widget(form.label, { 'attr': {'autocomplete': 'off'} }) }} | ||
12 | {{ form_widget(form.save, { 'attr': {'class': 'btn waves-effect waves-light'}, 'label': 'add tag' }) }} | ||
13 | |||
14 | <div class="hidden">{{ form_rest(form) }}</div> | ||
15 | </form> | ||
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig index 287c5fbb..3ec31a02 100644 --- a/src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig +++ b/src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig | |||
@@ -63,13 +63,15 @@ | |||
63 | <div class="row"> | 63 | <div class="row"> |
64 | <div class="input-field col s12"> | 64 | <div class="input-field col s12"> |
65 | <label>Rss token</label> | 65 | <label>Rss token</label> |
66 | {% if rss.token %} | 66 | <div> |
67 | {{ rss.token }} | 67 | {% if rss.token %} |
68 | {% else %} | 68 | {{ rss.token }} |
69 | <em>No token</em> | 69 | {% else %} |
70 | {% endif %} | 70 | <em>No token</em> |
71 | – | 71 | {% endif %} |
72 | <a href="{{ path('generate_token') }}">Regenerate ?</a> | 72 | – |
73 | <a href="{{ path('generate_token') }}">Regenerate ?</a> | ||
74 | </div> | ||
73 | </div> | 75 | </div> |
74 | </div> | 76 | </div> |
75 | 77 | ||
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entry.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entry.html.twig index b92c41b6..31b2c664 100644 --- a/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entry.html.twig +++ b/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entry.html.twig | |||
@@ -137,7 +137,8 @@ main { | |||
137 | <a href="{{ entry.url|e }}" target="_blank" title="{% trans %}original{% endtrans %} : {{ entry.title|e }}" class="tool link"><span>{{ entry.domainName }}</span></a> | 137 | <a href="{{ entry.url|e }}" target="_blank" title="{% trans %}original{% endtrans %} : {{ entry.title|e }}" class="tool link"><span>{{ entry.domainName }}</span></a> |
138 | </header> | 138 | </header> |
139 | <aside class="tags"> | 139 | <aside class="tags"> |
140 | tags: {% for tag in entry.tags %}<a href="./?view=tag&id={{ tag.id }}">{{ tag.label }}</a> {% endfor %}<a href="./?view=edit-tags&id={{ entry.id }}" title="{% trans %}Edit tags{% endtrans %}">✎</a> | 140 | {% for tag in entry.tags %}<span class="mdi-action-label-outline">{{ tag.label }}</span>{% endfor %} |
141 | {{ render(controller( "WallabagCoreBundle:Tag:addTagForm", { 'id': entry.id } )) }} | ||
141 | </aside> | 142 | </aside> |
142 | <article> | 143 | <article> |
143 | {{ entry.content | raw }} | 144 | {{ entry.content | raw }} |
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/material/Tag/new_form.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/Tag/new_form.html.twig new file mode 100644 index 00000000..0b5a530d --- /dev/null +++ b/src/Wallabag/CoreBundle/Resources/views/themes/material/Tag/new_form.html.twig | |||
@@ -0,0 +1,15 @@ | |||
1 | <form name="tag" method="post" action="{{ path('new_tag', { 'entry': entry.id })}}"> | ||
2 | |||
3 | {% if form_errors(form) %} | ||
4 | <span class="black-text">{{ form_errors(form) }}</span> | ||
5 | {% endif %} | ||
6 | |||
7 | {% if form_errors(form.label) %} | ||
8 | <span class="black-text">{{ form_errors(form.label) }}</span> | ||
9 | {% endif %} | ||
10 | |||
11 | {{ form_widget(form.label, { 'attr': {'autocomplete': 'off'} }) }} | ||
12 | {{ form_widget(form.save, { 'attr': {'class': 'btn waves-effect waves-light'}, 'label': 'add tag' }) }} | ||
13 | |||
14 | <div class="hidden">{{ form_rest(form) }}</div> | ||
15 | </form> | ||
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/material/public/css/main.css b/src/Wallabag/CoreBundle/Resources/views/themes/material/public/css/main.css index 52a2be80..a976da10 100755 --- a/src/Wallabag/CoreBundle/Resources/views/themes/material/public/css/main.css +++ b/src/Wallabag/CoreBundle/Resources/views/themes/material/public/css/main.css | |||
@@ -4,13 +4,11 @@ | |||
4 | 0 = Common | 4 | 0 = Common |
5 | 1 = Nav | 5 | 1 = Nav |
6 | 2 = Side-nav | 6 | 2 = Side-nav |
7 | 7 | 3 = Filters slider | |
8 | 2 = Layout | 8 | 4 = Cards |
9 | 3 = Pictos | ||
10 | 4 = Messages | ||
11 | 5 = Article | 9 | 5 = Article |
12 | |||
13 | 6 = Media queries | 10 | 6 = Media queries |
11 | 7 = Others | ||
14 | 12 | ||
15 | ========================================================================== */ | 13 | ========================================================================== */ |
16 | 14 | ||
@@ -212,7 +210,7 @@ nav input { | |||
212 | } | 210 | } |
213 | 211 | ||
214 | /* ========================================================================== | 212 | /* ========================================================================== |
215 | 3 = Cards | 213 | 4 = Cards |
216 | ========================================================================== */ | 214 | ========================================================================== */ |
217 | 215 | ||
218 | main #content { | 216 | main #content { |
@@ -261,7 +259,7 @@ main ul.row { | |||
261 | } | 259 | } |
262 | 260 | ||
263 | /* ========================================================================== | 261 | /* ========================================================================== |
264 | 4 = Article | 262 | 5 = Article |
265 | ========================================================================== */ | 263 | ========================================================================== */ |
266 | 264 | ||
267 | #article { | 265 | #article { |
@@ -365,3 +363,10 @@ main ul.row { | |||
365 | display: none; | 363 | display: none; |
366 | } | 364 | } |
367 | } | 365 | } |
366 | /* ========================================================================== | ||
367 | 7 = Others | ||
368 | ========================================================================== */ | ||
369 | |||
370 | div.input-field div, div.input-field ul { | ||
371 | margin-top: 40px; | ||
372 | } | ||
diff --git a/src/Wallabag/CoreBundle/Tests/Controller/EntryControllerTest.php b/src/Wallabag/CoreBundle/Tests/Controller/EntryControllerTest.php index 5f0a6076..a0966285 100644 --- a/src/Wallabag/CoreBundle/Tests/Controller/EntryControllerTest.php +++ b/src/Wallabag/CoreBundle/Tests/Controller/EntryControllerTest.php | |||
@@ -279,6 +279,15 @@ class EntryControllerTest extends WallabagCoreTestCase | |||
279 | $this->assertCount(5, $crawler->filter('div[class=entry]')); | 279 | $this->assertCount(5, $crawler->filter('div[class=entry]')); |
280 | 280 | ||
281 | $data = array( | 281 | $data = array( |
282 | 'entry_filter[createdAt][left_date]' => date('d/m/Y'), | ||
283 | 'entry_filter[createdAt][right_date]' => date('d/m/Y'), | ||
284 | ); | ||
285 | |||
286 | $crawler = $client->submit($form, $data); | ||
287 | |||
288 | $this->assertCount(5, $crawler->filter('div[class=entry]')); | ||
289 | |||
290 | $data = array( | ||
282 | 'entry_filter[createdAt][left_date]' => '01/01/1970', | 291 | 'entry_filter[createdAt][left_date]' => '01/01/1970', |
283 | 'entry_filter[createdAt][right_date]' => '01/01/1970', | 292 | 'entry_filter[createdAt][right_date]' => '01/01/1970', |
284 | ); | 293 | ); |
diff --git a/src/Wallabag/CoreBundle/Tests/Controller/TagControllerTest.php b/src/Wallabag/CoreBundle/Tests/Controller/TagControllerTest.php index 4a43e049..af39d6ce 100644 --- a/src/Wallabag/CoreBundle/Tests/Controller/TagControllerTest.php +++ b/src/Wallabag/CoreBundle/Tests/Controller/TagControllerTest.php | |||
@@ -15,4 +15,54 @@ class TagControllerTest extends WallabagCoreTestCase | |||
15 | 15 | ||
16 | $this->assertEquals(200, $client->getResponse()->getStatusCode()); | 16 | $this->assertEquals(200, $client->getResponse()->getStatusCode()); |
17 | } | 17 | } |
18 | |||
19 | public function testAddTagToEntry() | ||
20 | { | ||
21 | $this->logInAs('admin'); | ||
22 | $client = $this->getClient(); | ||
23 | |||
24 | $entry = $client->getContainer() | ||
25 | ->get('doctrine.orm.entity_manager') | ||
26 | ->getRepository('WallabagCoreBundle:Entry') | ||
27 | ->findOneByIsArchived(false); | ||
28 | |||
29 | $crawler = $client->request('GET', '/view/'.$entry->getId()); | ||
30 | |||
31 | $form = $crawler->filter('button[id=tag_save]')->form(); | ||
32 | |||
33 | $data = array( | ||
34 | 'tag[label]' => 'opensource', | ||
35 | ); | ||
36 | |||
37 | $client->submit($form, $data); | ||
38 | $this->assertEquals(302, $client->getResponse()->getStatusCode()); | ||
39 | |||
40 | $this->assertEquals(1, count($entry->getTags())); | ||
41 | |||
42 | # tag already exists and already assigned | ||
43 | $client->submit($form, $data); | ||
44 | $this->assertEquals(302, $client->getResponse()->getStatusCode()); | ||
45 | |||
46 | $newEntry = $client->getContainer() | ||
47 | ->get('doctrine.orm.entity_manager') | ||
48 | ->getRepository('WallabagCoreBundle:Entry') | ||
49 | ->findOneById($entry->getId()); | ||
50 | |||
51 | $this->assertEquals(1, count($newEntry->getTags())); | ||
52 | |||
53 | # tag already exists but still not assigned to this entry | ||
54 | $data = array( | ||
55 | 'tag[label]' => 'foo', | ||
56 | ); | ||
57 | |||
58 | $client->submit($form, $data); | ||
59 | $this->assertEquals(302, $client->getResponse()->getStatusCode()); | ||
60 | |||
61 | $newEntry = $client->getContainer() | ||
62 | ->get('doctrine.orm.entity_manager') | ||
63 | ->getRepository('WallabagCoreBundle:Entry') | ||
64 | ->findOneById($entry->getId()); | ||
65 | |||
66 | $this->assertEquals(2, count($newEntry->getTags())); | ||
67 | } | ||
18 | } | 68 | } |