diff options
Diffstat (limited to 'src')
53 files changed, 1385 insertions, 11 deletions
diff --git a/src/Wallabag/CoreBundle/Command/AbstractNotificationCommand.php b/src/Wallabag/CoreBundle/Command/AbstractNotificationCommand.php new file mode 100644 index 00000000..b40b589a --- /dev/null +++ b/src/Wallabag/CoreBundle/Command/AbstractNotificationCommand.php | |||
@@ -0,0 +1,41 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\Command; | ||
4 | |||
5 | use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; | ||
6 | use Symfony\Component\Console\Input\InputArgument; | ||
7 | use Symfony\Component\Console\Output\OutputInterface; | ||
8 | |||
9 | abstract class AbstractNotificationCommand extends ContainerAwareCommand | ||
10 | { | ||
11 | /** @var OutputInterface */ | ||
12 | protected $output; | ||
13 | |||
14 | protected function configure() | ||
15 | { | ||
16 | $this | ||
17 | ->addArgument( | ||
18 | 'username', | ||
19 | InputArgument::OPTIONAL, | ||
20 | 'User to send the notification to' | ||
21 | ) | ||
22 | ; | ||
23 | } | ||
24 | |||
25 | /** | ||
26 | * Fetches a user from its username. | ||
27 | * | ||
28 | * @param string $username | ||
29 | * | ||
30 | * @return \Wallabag\UserBundle\Entity\User | ||
31 | */ | ||
32 | protected function getUser($username) | ||
33 | { | ||
34 | return $this->getDoctrine()->getRepository('WallabagUserBundle:User')->findOneByUserName($username); | ||
35 | } | ||
36 | |||
37 | protected function getDoctrine() | ||
38 | { | ||
39 | return $this->getContainer()->get('doctrine'); | ||
40 | } | ||
41 | } | ||
diff --git a/src/Wallabag/CoreBundle/Command/AdminNotificationCommand.php b/src/Wallabag/CoreBundle/Command/AdminNotificationCommand.php new file mode 100644 index 00000000..cfde714b --- /dev/null +++ b/src/Wallabag/CoreBundle/Command/AdminNotificationCommand.php | |||
@@ -0,0 +1,103 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\Command; | ||
4 | |||
5 | use Doctrine\ORM\NoResultException; | ||
6 | use Symfony\Component\Console\Input\InputArgument; | ||
7 | use Symfony\Component\Console\Input\InputInterface; | ||
8 | use Symfony\Component\Console\Input\InputOption; | ||
9 | use Symfony\Component\Console\Output\OutputInterface; | ||
10 | use Wallabag\CoreBundle\Entity\Notification; | ||
11 | use Wallabag\CoreBundle\Notifications\InfoAction; | ||
12 | use Wallabag\UserBundle\Entity\User; | ||
13 | |||
14 | class AdminNotificationCommand extends AbstractNotificationCommand | ||
15 | { | ||
16 | protected function configure() | ||
17 | { | ||
18 | $this | ||
19 | ->setName('wallabag:notification:send') | ||
20 | ->setDescription('Emits a notification to all users') | ||
21 | ->setHelp('This command helps you send notifications to all of the users instance, or just for one user.') | ||
22 | ->addArgument( | ||
23 | 'title', | ||
24 | InputArgument::REQUIRED, | ||
25 | 'Title of your notification. This is required if if the type of notification is an admin one.' | ||
26 | ) | ||
27 | ->addArgument( | ||
28 | 'message', | ||
29 | InputArgument::REQUIRED, | ||
30 | 'Message of your notification. This is required if the type of notification is an admin one.' | ||
31 | ) | ||
32 | ->addOption( | ||
33 | 'link', | ||
34 | 'l', | ||
35 | InputOption::VALUE_REQUIRED, | ||
36 | 'A link to display with the notification' | ||
37 | ) | ||
38 | ; | ||
39 | parent::configure(); | ||
40 | } | ||
41 | |||
42 | protected function execute(InputInterface $input, OutputInterface $output) | ||
43 | { | ||
44 | $this->output = $output; | ||
45 | |||
46 | $username = $input->getArgument('username'); | ||
47 | |||
48 | $message = $input->getArgument('message'); | ||
49 | $title = $input->getArgument('title'); | ||
50 | |||
51 | $link = $input->getOption('link'); | ||
52 | |||
53 | if ($username) { | ||
54 | try { | ||
55 | $user = $this->getUser($username); | ||
56 | $this->sendNotification($user, $title, $message, $link); | ||
57 | } catch (NoResultException $e) { | ||
58 | $output->writeln(sprintf('<error>User "%s" not found.</error>', $username)); | ||
59 | |||
60 | return 1; | ||
61 | } | ||
62 | } else { | ||
63 | $users = $this->getDoctrine()->getRepository('WallabagUserBundle:User')->findAll(); | ||
64 | |||
65 | $output->writeln(sprintf('Sending notifications to %d user accounts. This can take some time.', count($users))); | ||
66 | |||
67 | foreach ($users as $user) { | ||
68 | $output->writeln(sprintf('Processing user %s', $user->getUsername())); | ||
69 | $this->sendNotification($user, $title, $message, $link); | ||
70 | } | ||
71 | $output->writeln('Finished sending notifications.'); | ||
72 | } | ||
73 | |||
74 | return 0; | ||
75 | } | ||
76 | |||
77 | /** | ||
78 | * @param User $user | ||
79 | * @param $title | ||
80 | * @param $message | ||
81 | * @param null $link | ||
82 | */ | ||
83 | private function sendNotification(User $user, $title, $message, $link = null) | ||
84 | { | ||
85 | $em = $this->getContainer()->get('doctrine.orm.entity_manager'); | ||
86 | |||
87 | $notification = new Notification($user); | ||
88 | $notification->setTitle($title) | ||
89 | ->setDescription($message) | ||
90 | ->setType(Notification::TYPE_ADMIN); | ||
91 | |||
92 | if ($link) { | ||
93 | $action = new InfoAction($link); | ||
94 | |||
95 | $notification->addAction($action); | ||
96 | } | ||
97 | |||
98 | $em->persist($notification); | ||
99 | $em->flush(); | ||
100 | |||
101 | $this->output->writeln(sprintf('Sent notification for user %s', $user->getUserName())); | ||
102 | } | ||
103 | } | ||
diff --git a/src/Wallabag/CoreBundle/Command/ReleaseNotificationCommand.php b/src/Wallabag/CoreBundle/Command/ReleaseNotificationCommand.php new file mode 100644 index 00000000..cd9c61a3 --- /dev/null +++ b/src/Wallabag/CoreBundle/Command/ReleaseNotificationCommand.php | |||
@@ -0,0 +1,88 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\Command; | ||
4 | |||
5 | use Doctrine\ORM\NoResultException; | ||
6 | use Symfony\Component\Console\Input\InputArgument; | ||
7 | use Symfony\Component\Console\Input\InputInterface; | ||
8 | use Symfony\Component\Console\Output\OutputInterface; | ||
9 | use Wallabag\CoreBundle\Entity\Notification; | ||
10 | use Wallabag\CoreBundle\Notifications\Action; | ||
11 | use Wallabag\UserBundle\Entity\User; | ||
12 | |||
13 | class ReleaseNotificationCommand extends AbstractNotificationCommand | ||
14 | { | ||
15 | /** @var OutputInterface */ | ||
16 | protected $output; | ||
17 | |||
18 | protected function configure() | ||
19 | { | ||
20 | $this | ||
21 | ->setName('wallabag:notification:release') | ||
22 | ->setDescription('Emits a notification to all users to let them know of a new release') | ||
23 | ->setHelp('This command helps you send a release notification to all of the users instance, or just for one user.') | ||
24 | ->addArgument( | ||
25 | 'link', | ||
26 | InputArgument::OPTIONAL, | ||
27 | 'A link to display with the notification' | ||
28 | ) | ||
29 | ; | ||
30 | parent::configure(); | ||
31 | } | ||
32 | |||
33 | protected function execute(InputInterface $input, OutputInterface $output) | ||
34 | { | ||
35 | $this->output = $output; | ||
36 | |||
37 | $username = $input->getArgument('username'); | ||
38 | |||
39 | $link = $input->getArgument('link'); | ||
40 | |||
41 | if ($username) { | ||
42 | try { | ||
43 | $user = $this->getUser($username); | ||
44 | $this->sendNotification($user, $link); | ||
45 | } catch (NoResultException $e) { | ||
46 | $output->writeln(sprintf('<error>User "%s" not found.</error>', $username)); | ||
47 | |||
48 | return 1; | ||
49 | } | ||
50 | } else { | ||
51 | $users = $this->getDoctrine()->getRepository('WallabagUserBundle:User')->findAll(); | ||
52 | |||
53 | $output->writeln(sprintf('Sending notifications to %d user accounts. This can take some time.', count($users))); | ||
54 | |||
55 | foreach ($users as $user) { | ||
56 | $output->writeln(sprintf('Processing user %s', $user->getUsername())); | ||
57 | $this->sendNotification($user, $link); | ||
58 | } | ||
59 | $output->writeln('Finished sending notifications.'); | ||
60 | } | ||
61 | |||
62 | return 0; | ||
63 | } | ||
64 | |||
65 | /** | ||
66 | * @param User $user | ||
67 | */ | ||
68 | private function sendNotification(User $user, $link) | ||
69 | { | ||
70 | $em = $this->getContainer()->get('doctrine.orm.entity_manager'); | ||
71 | |||
72 | $notification = new Notification($user); | ||
73 | $notification->setTitle('notifications.release.title') | ||
74 | ->addParameter('%version%', $this->getContainer()->getParameter('wallabag_core.version')) | ||
75 | ->setType(Notification::TYPE_RELEASE); | ||
76 | if ($link) { | ||
77 | $details = new Action(); | ||
78 | $details->setType(Action::TYPE_INFO) | ||
79 | ->setLabel('notifications.release.details') | ||
80 | ->setLink($link); | ||
81 | $notification->addAction($details); | ||
82 | } | ||
83 | $em->persist($notification); | ||
84 | $em->flush(); | ||
85 | |||
86 | $this->output->writeln(sprintf('Sent notification for user %s', $user->getUserName())); | ||
87 | } | ||
88 | } | ||
diff --git a/src/Wallabag/CoreBundle/Controller/NotificationsController.php b/src/Wallabag/CoreBundle/Controller/NotificationsController.php new file mode 100644 index 00000000..17e576cd --- /dev/null +++ b/src/Wallabag/CoreBundle/Controller/NotificationsController.php | |||
@@ -0,0 +1,96 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\Controller; | ||
4 | |||
5 | use Pagerfanta\Adapter\DoctrineORMAdapter; | ||
6 | use Pagerfanta\Exception\OutOfRangeCurrentPageException; | ||
7 | use Pagerfanta\Pagerfanta; | ||
8 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; | ||
9 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; | ||
10 | use Symfony\Component\HttpFoundation\Request; | ||
11 | use Symfony\Component\HttpFoundation\Response; | ||
12 | use Wallabag\CoreBundle\Entity\Notification; | ||
13 | |||
14 | class NotificationsController extends Controller | ||
15 | { | ||
16 | /** | ||
17 | * @Route("/notifications/{page}", name="notifications-all", defaults={"page" = "1"}) | ||
18 | * | ||
19 | * @param int $page | ||
20 | * | ||
21 | * @return Response | ||
22 | */ | ||
23 | public function getAllNotificationsAction($page = 1) | ||
24 | { | ||
25 | $qb = $this->getDoctrine()->getRepository('WallabagCoreBundle:Notification')->getBuilderForNotificationsByUser($this->getUser()->getId()); | ||
26 | $pagerAdapter = new DoctrineORMAdapter($qb->getQuery(), true, false); | ||
27 | |||
28 | $notifications = new Pagerfanta($pagerAdapter); | ||
29 | $notifications->setMaxPerPage($this->getParameter('wallabag_core.notifications_nb')); | ||
30 | |||
31 | try { | ||
32 | $notifications->setCurrentPage($page); | ||
33 | } catch (OutOfRangeCurrentPageException $e) { | ||
34 | if ($page > 1) { | ||
35 | return $this->redirect($this->generateUrl('notifications-all', ['page' => $notifications->getNbPages()]), 302); | ||
36 | } | ||
37 | } | ||
38 | |||
39 | return $this->render('WallabagCoreBundle:Notification:notifications.html.twig', [ | ||
40 | 'notifications' => $notifications, | ||
41 | 'currentPage' => $page, | ||
42 | ]); | ||
43 | } | ||
44 | |||
45 | /** | ||
46 | * @Route("/notifications/readall", name="notification-archive-all") | ||
47 | * | ||
48 | * @param Request $request | ||
49 | * | ||
50 | * @return Response | ||
51 | */ | ||
52 | public function markAllNotificationsAsReadAction(Request $request) | ||
53 | { | ||
54 | $this->getDoctrine()->getRepository('WallabagCoreBundle:Notification')->markAllAsReadForUser($this->getUser()->getId()); | ||
55 | |||
56 | return $this->redirectToRoute('notifications-all'); | ||
57 | } | ||
58 | |||
59 | /** | ||
60 | * @Route("/notifications/read/{notification}", name="notification-archive") | ||
61 | * | ||
62 | * @param Notification $notification | ||
63 | * | ||
64 | * @return Response | ||
65 | */ | ||
66 | public function markNotificationsAsReadAction(Notification $notification) | ||
67 | { | ||
68 | $em = $this->getDoctrine()->getManager(); | ||
69 | |||
70 | $notification->setRead(true); | ||
71 | |||
72 | $em->persist($notification); | ||
73 | $em->flush(); | ||
74 | |||
75 | return $this->redirectToRoute('notifications-all'); | ||
76 | } | ||
77 | |||
78 | /** | ||
79 | * @Route("/notifications/read/{notification}/redirect", name="notification-archive-redirect", requirements={"notification" = "\d+"}) | ||
80 | * | ||
81 | * @param Request $request | ||
82 | * @param Notification $notification | ||
83 | */ | ||
84 | public function markNotificationAsReadAndRedirectAction(Request $request, Notification $notification) | ||
85 | { | ||
86 | $em = $this->getDoctrine()->getManager(); | ||
87 | |||
88 | $notification->setRead(true); | ||
89 | |||
90 | $em->persist($notification); | ||
91 | $em->flush(); | ||
92 | |||
93 | $redirection = $request->get('redirection'); | ||
94 | $this->redirect($redirection); | ||
95 | } | ||
96 | } | ||
diff --git a/src/Wallabag/CoreBundle/DependencyInjection/Configuration.php b/src/Wallabag/CoreBundle/DependencyInjection/Configuration.php index a9791f6b..478e4cba 100644 --- a/src/Wallabag/CoreBundle/DependencyInjection/Configuration.php +++ b/src/Wallabag/CoreBundle/DependencyInjection/Configuration.php | |||
@@ -49,6 +49,9 @@ class Configuration implements ConfigurationInterface | |||
49 | ->scalarNode('list_mode') | 49 | ->scalarNode('list_mode') |
50 | ->defaultValue(1) | 50 | ->defaultValue(1) |
51 | ->end() | 51 | ->end() |
52 | ->scalarNode('notifications_nb') | ||
53 | ->defaultValue(5) | ||
54 | ->end() | ||
52 | ->scalarNode('api_limit_mass_actions') | 55 | ->scalarNode('api_limit_mass_actions') |
53 | ->defaultValue(10) | 56 | ->defaultValue(10) |
54 | ->end() | 57 | ->end() |
diff --git a/src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php b/src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php index a3ef2b53..bce66b76 100644 --- a/src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php +++ b/src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php | |||
@@ -25,6 +25,7 @@ class WallabagCoreExtension extends Extension | |||
25 | $container->setParameter('wallabag_core.cache_lifetime', $config['cache_lifetime']); | 25 | $container->setParameter('wallabag_core.cache_lifetime', $config['cache_lifetime']); |
26 | $container->setParameter('wallabag_core.action_mark_as_read', $config['action_mark_as_read']); | 26 | $container->setParameter('wallabag_core.action_mark_as_read', $config['action_mark_as_read']); |
27 | $container->setParameter('wallabag_core.list_mode', $config['list_mode']); | 27 | $container->setParameter('wallabag_core.list_mode', $config['list_mode']); |
28 | $container->setParameter('wallabag_core.notifications_nb', $config['notifications_nb']); | ||
28 | $container->setParameter('wallabag_core.fetching_error_message', $config['fetching_error_message']); | 29 | $container->setParameter('wallabag_core.fetching_error_message', $config['fetching_error_message']); |
29 | $container->setParameter('wallabag_core.fetching_error_message_title', $config['fetching_error_message_title']); | 30 | $container->setParameter('wallabag_core.fetching_error_message_title', $config['fetching_error_message_title']); |
30 | $container->setParameter('wallabag_core.api_limit_mass_actions', $config['api_limit_mass_actions']); | 31 | $container->setParameter('wallabag_core.api_limit_mass_actions', $config['api_limit_mass_actions']); |
diff --git a/src/Wallabag/CoreBundle/Entity/Notification.php b/src/Wallabag/CoreBundle/Entity/Notification.php new file mode 100644 index 00000000..6b30b044 --- /dev/null +++ b/src/Wallabag/CoreBundle/Entity/Notification.php | |||
@@ -0,0 +1,308 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\Entity; | ||
4 | |||
5 | use Doctrine\Common\Collections\ArrayCollection; | ||
6 | use Doctrine\ORM\Mapping as ORM; | ||
7 | use Psr\Log\LoggerInterface; | ||
8 | use Psr\Log\NullLogger; | ||
9 | use Wallabag\CoreBundle\Notifications\ActionInterface; | ||
10 | use Wallabag\CoreBundle\Notifications\NotificationInterface; | ||
11 | use Wallabag\UserBundle\Entity\User; | ||
12 | |||
13 | /** | ||
14 | * Class Notification. | ||
15 | * | ||
16 | * @ORM\Entity(repositoryClass="Wallabag\CoreBundle\Repository\NotificationRepository") | ||
17 | * @ORM\Table(name="`notification`") | ||
18 | */ | ||
19 | class Notification implements NotificationInterface | ||
20 | { | ||
21 | /** | ||
22 | * @var int | ||
23 | * | ||
24 | * @ORM\Column(name="id", type="integer") | ||
25 | * @ORM\Id | ||
26 | * @ORM\GeneratedValue(strategy="AUTO") | ||
27 | */ | ||
28 | protected $id; | ||
29 | |||
30 | /** | ||
31 | * @var int | ||
32 | * | ||
33 | * @ORM\Column(name="type", type="integer") | ||
34 | */ | ||
35 | protected $type; | ||
36 | |||
37 | /** | ||
38 | * @var User | ||
39 | * | ||
40 | * @ORM\ManyToOne(targetEntity="Wallabag\UserBundle\Entity\User", inversedBy="notifications") | ||
41 | */ | ||
42 | protected $user; | ||
43 | |||
44 | /** | ||
45 | * @var \DateTime | ||
46 | * | ||
47 | * @ORM\Column(name="timestamp", type="datetime") | ||
48 | */ | ||
49 | protected $timestamp; | ||
50 | |||
51 | /** | ||
52 | * @var string | ||
53 | * | ||
54 | * @ORM\Column(name="title", type="string") | ||
55 | */ | ||
56 | protected $title; | ||
57 | |||
58 | /** | ||
59 | * @var string | ||
60 | * | ||
61 | * @ORM\Column(name="description", type="string", nullable=true) | ||
62 | */ | ||
63 | protected $description; | ||
64 | |||
65 | /** | ||
66 | * @var bool | ||
67 | * | ||
68 | * @ORM\Column(name="read", type="boolean") | ||
69 | */ | ||
70 | protected $read; | ||
71 | |||
72 | /** | ||
73 | * @var array | ||
74 | * | ||
75 | * @ORM\Column(name="parameters", type="array", nullable=true) | ||
76 | */ | ||
77 | protected $parameters; | ||
78 | |||
79 | protected $logger; | ||
80 | |||
81 | /** | ||
82 | * @var ArrayCollection<ActionInterface> | ||
83 | * | ||
84 | * @ORM\Column(name="actions", type="array", nullable=true) | ||
85 | */ | ||
86 | protected $actions; | ||
87 | |||
88 | protected $actionTypes = []; | ||
89 | |||
90 | const TYPE_ADMIN = 0; | ||
91 | const TYPE_USER = 1; | ||
92 | const TYPE_RELEASE = 2; | ||
93 | |||
94 | public function __construct(User $user = null) | ||
95 | { | ||
96 | $this->logger = new NullLogger(); | ||
97 | $this->timestamp = new \DateTime(); | ||
98 | $this->actions = new ArrayCollection(); | ||
99 | $this->parameters = []; | ||
100 | $this->read = false; | ||
101 | $this->user = $user; | ||
102 | } | ||
103 | |||
104 | /** | ||
105 | * @param LoggerInterface $logger | ||
106 | * | ||
107 | * @return NotificationInterface | ||
108 | */ | ||
109 | public function setLogger(LoggerInterface $logger) | ||
110 | { | ||
111 | $this->logger = $logger; | ||
112 | |||
113 | return $this; | ||
114 | } | ||
115 | |||
116 | /** | ||
117 | * @return mixed | ||
118 | */ | ||
119 | public function getId() | ||
120 | { | ||
121 | return $this->id; | ||
122 | } | ||
123 | |||
124 | /** | ||
125 | * @return mixed | ||
126 | */ | ||
127 | public function getType() | ||
128 | { | ||
129 | return $this->type; | ||
130 | } | ||
131 | |||
132 | /** | ||
133 | * @param mixed $type | ||
134 | * | ||
135 | * @return NotificationInterface | ||
136 | */ | ||
137 | public function setType($type) | ||
138 | { | ||
139 | $this->type = $type; | ||
140 | |||
141 | return $this; | ||
142 | } | ||
143 | |||
144 | /** | ||
145 | * @return User | ||
146 | */ | ||
147 | public function getUser() | ||
148 | { | ||
149 | return $this->user; | ||
150 | } | ||
151 | |||
152 | /** | ||
153 | * @param User $user | ||
154 | * | ||
155 | * @return NotificationInterface | ||
156 | */ | ||
157 | public function setUser(User $user) | ||
158 | { | ||
159 | $this->user = $user; | ||
160 | |||
161 | return $this; | ||
162 | } | ||
163 | |||
164 | /** | ||
165 | * @return \DateTime | ||
166 | */ | ||
167 | public function getTimestamp() | ||
168 | { | ||
169 | return $this->timestamp; | ||
170 | } | ||
171 | |||
172 | /** | ||
173 | * @param \DateTime $timestamp | ||
174 | * | ||
175 | * @return NotificationInterface | ||
176 | */ | ||
177 | public function setTimestamp(\DateTime $timestamp) | ||
178 | { | ||
179 | $this->timestamp = $timestamp; | ||
180 | |||
181 | return $this; | ||
182 | } | ||
183 | |||
184 | /** | ||
185 | * @return string | ||
186 | */ | ||
187 | public function getTitle() | ||
188 | { | ||
189 | return $this->title; | ||
190 | } | ||
191 | |||
192 | /** | ||
193 | * @param string $title | ||
194 | * | ||
195 | * @return NotificationInterface | ||
196 | */ | ||
197 | public function setTitle($title) | ||
198 | { | ||
199 | $this->title = $title; | ||
200 | |||
201 | return $this; | ||
202 | } | ||
203 | |||
204 | /** | ||
205 | * @return bool | ||
206 | */ | ||
207 | public function isRead() | ||
208 | { | ||
209 | return $this->read; | ||
210 | } | ||
211 | |||
212 | /** | ||
213 | * @param bool $read | ||
214 | * | ||
215 | * @return NotificationInterface | ||
216 | */ | ||
217 | public function setRead($read) | ||
218 | { | ||
219 | $this->read = $read; | ||
220 | |||
221 | return $this; | ||
222 | } | ||
223 | |||
224 | /** | ||
225 | * @param ActionInterface $action | ||
226 | * | ||
227 | * @return NotificationInterface | ||
228 | * | ||
229 | * @throws \InvalidArgumentException | ||
230 | */ | ||
231 | public function addAction(ActionInterface $action) | ||
232 | { | ||
233 | if (isset($this->actionTypes[$action->getType()])) { | ||
234 | throw new \InvalidArgumentException('The notification already has a primary action'); | ||
235 | } | ||
236 | $this->actionTypes[$action->getType()] = true; | ||
237 | $this->actions->add($action); | ||
238 | |||
239 | return $this; | ||
240 | } | ||
241 | |||
242 | /** | ||
243 | * @return ArrayCollection<ActionInterface> | ||
244 | */ | ||
245 | public function getActions() | ||
246 | { | ||
247 | return $this->actions; | ||
248 | } | ||
249 | |||
250 | /** | ||
251 | * @return string | ||
252 | */ | ||
253 | public function getDescription() | ||
254 | { | ||
255 | return $this->description; | ||
256 | } | ||
257 | |||
258 | /** | ||
259 | * @param string $description | ||
260 | * | ||
261 | * @return Notification | ||
262 | */ | ||
263 | public function setDescription($description) | ||
264 | { | ||
265 | $this->description = $description; | ||
266 | |||
267 | return $this; | ||
268 | } | ||
269 | |||
270 | /** | ||
271 | * @return array | ||
272 | */ | ||
273 | public function getParameters() | ||
274 | { | ||
275 | return $this->parameters; | ||
276 | } | ||
277 | |||
278 | /** | ||
279 | * @param array $parameters | ||
280 | * | ||
281 | * @return Notification | ||
282 | */ | ||
283 | public function setParameters($parameters) | ||
284 | { | ||
285 | $this->parameters = $parameters; | ||
286 | |||
287 | return $this; | ||
288 | } | ||
289 | |||
290 | /** | ||
291 | * @param string $key | ||
292 | * @param string $value | ||
293 | * | ||
294 | * @return Notification | ||
295 | * | ||
296 | * @throws \InvalidArgumentException | ||
297 | */ | ||
298 | public function addParameter($key, $value) | ||
299 | { | ||
300 | if (in_array($key, $this->parameters, true)) { | ||
301 | throw new \InvalidArgumentException('This parameter already is set'); | ||
302 | } | ||
303 | |||
304 | $this->parameters[$key] = $value; | ||
305 | |||
306 | return $this; | ||
307 | } | ||
308 | } | ||
diff --git a/src/Wallabag/CoreBundle/Notifications/Action.php b/src/Wallabag/CoreBundle/Notifications/Action.php new file mode 100644 index 00000000..d032adf9 --- /dev/null +++ b/src/Wallabag/CoreBundle/Notifications/Action.php | |||
@@ -0,0 +1,91 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\Notifications; | ||
4 | |||
5 | class Action implements ActionInterface | ||
6 | { | ||
7 | /** | ||
8 | * @var string | ||
9 | */ | ||
10 | protected $label; | ||
11 | |||
12 | /** | ||
13 | * @var int | ||
14 | */ | ||
15 | protected $type; | ||
16 | |||
17 | const TYPE_OK = 1; | ||
18 | const TYPE_YES = 2; | ||
19 | const TYPE_NO = 3; | ||
20 | const TYPE_INFO = 4; | ||
21 | |||
22 | /** | ||
23 | * @var string | ||
24 | */ | ||
25 | protected $link; | ||
26 | |||
27 | /** | ||
28 | * @return string | ||
29 | */ | ||
30 | public function getLabel() | ||
31 | { | ||
32 | return $this->label; | ||
33 | } | ||
34 | |||
35 | /** | ||
36 | * @param string $label | ||
37 | * | ||
38 | * @return ActionInterface | ||
39 | */ | ||
40 | public function setLabel($label) | ||
41 | { | ||
42 | $this->label = $label; | ||
43 | |||
44 | return $this; | ||
45 | } | ||
46 | |||
47 | /** | ||
48 | * @return int | ||
49 | */ | ||
50 | public function getType() | ||
51 | { | ||
52 | return $this->type; | ||
53 | } | ||
54 | |||
55 | /** | ||
56 | * @param int $type | ||
57 | * | ||
58 | * @return ActionInterface | ||
59 | * | ||
60 | * @throws \InvalidArgumentException | ||
61 | */ | ||
62 | public function setType($type) | ||
63 | { | ||
64 | if ($type <= 0 || $type > 4) { | ||
65 | throw new \InvalidArgumentException('The given type option is invalid'); | ||
66 | } | ||
67 | $this->type = $type; | ||
68 | |||
69 | return $this; | ||
70 | } | ||
71 | |||
72 | /** | ||
73 | * @return string | ||
74 | */ | ||
75 | public function getLink() | ||
76 | { | ||
77 | return $this->link; | ||
78 | } | ||
79 | |||
80 | /** | ||
81 | * @param string $link | ||
82 | * | ||
83 | * @return ActionInterface | ||
84 | */ | ||
85 | public function setLink($link) | ||
86 | { | ||
87 | $this->link = $link; | ||
88 | |||
89 | return $this; | ||
90 | } | ||
91 | } | ||
diff --git a/src/Wallabag/CoreBundle/Notifications/ActionInterface.php b/src/Wallabag/CoreBundle/Notifications/ActionInterface.php new file mode 100644 index 00000000..e166e45b --- /dev/null +++ b/src/Wallabag/CoreBundle/Notifications/ActionInterface.php | |||
@@ -0,0 +1,42 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\Notifications; | ||
4 | |||
5 | interface ActionInterface | ||
6 | { | ||
7 | /** | ||
8 | * @return string | ||
9 | */ | ||
10 | public function getLabel(); | ||
11 | |||
12 | /** | ||
13 | * @param string $label | ||
14 | * | ||
15 | * @return ActionInterface | ||
16 | */ | ||
17 | public function setLabel($label); | ||
18 | |||
19 | /** | ||
20 | * @return int | ||
21 | */ | ||
22 | public function getType(); | ||
23 | |||
24 | /** | ||
25 | * @param int $type | ||
26 | * | ||
27 | * @return ActionInterface | ||
28 | */ | ||
29 | public function setType($type); | ||
30 | |||
31 | /** | ||
32 | * @return string | ||
33 | */ | ||
34 | public function getLink(); | ||
35 | |||
36 | /** | ||
37 | * @param string $link | ||
38 | * | ||
39 | * @return ActionInterface | ||
40 | */ | ||
41 | public function setLink($link); | ||
42 | } | ||
diff --git a/src/Wallabag/CoreBundle/Notifications/InfoAction.php b/src/Wallabag/CoreBundle/Notifications/InfoAction.php new file mode 100644 index 00000000..3006b04a --- /dev/null +++ b/src/Wallabag/CoreBundle/Notifications/InfoAction.php | |||
@@ -0,0 +1,13 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\Notifications; | ||
4 | |||
5 | class InfoAction extends Action | ||
6 | { | ||
7 | public function __construct($link) | ||
8 | { | ||
9 | $this->link = $link; | ||
10 | $this->label = 'Info'; | ||
11 | $this->type = Action::TYPE_INFO; | ||
12 | } | ||
13 | } | ||
diff --git a/src/Wallabag/CoreBundle/Notifications/NoAction.php b/src/Wallabag/CoreBundle/Notifications/NoAction.php new file mode 100644 index 00000000..606372b6 --- /dev/null +++ b/src/Wallabag/CoreBundle/Notifications/NoAction.php | |||
@@ -0,0 +1,13 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\Notifications; | ||
4 | |||
5 | class NoAction extends Action | ||
6 | { | ||
7 | public function __construct($link) | ||
8 | { | ||
9 | $this->link = $link; | ||
10 | $this->label = 'No'; | ||
11 | $this->type = Action::TYPE_NO; | ||
12 | } | ||
13 | } | ||
diff --git a/src/Wallabag/CoreBundle/Notifications/NotificationInterface.php b/src/Wallabag/CoreBundle/Notifications/NotificationInterface.php new file mode 100644 index 00000000..4a3c2759 --- /dev/null +++ b/src/Wallabag/CoreBundle/Notifications/NotificationInterface.php | |||
@@ -0,0 +1,103 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\Notifications; | ||
4 | |||
5 | use Psr\Log\LoggerAwareInterface; | ||
6 | |||
7 | interface NotificationInterface extends LoggerAwareInterface | ||
8 | { | ||
9 | /** | ||
10 | * Title of the notification. | ||
11 | * | ||
12 | * @return string | ||
13 | */ | ||
14 | public function getTitle(); | ||
15 | |||
16 | /** | ||
17 | * @param string $title | ||
18 | * | ||
19 | * @return NotificationInterface | ||
20 | */ | ||
21 | public function setTitle($title); | ||
22 | |||
23 | /** | ||
24 | * Type of the notification. | ||
25 | * | ||
26 | * @return string | ||
27 | */ | ||
28 | public function getType(); | ||
29 | |||
30 | /** | ||
31 | * @param int $type | ||
32 | * | ||
33 | * @return NotificationInterface | ||
34 | */ | ||
35 | public function setType($type); | ||
36 | |||
37 | /** | ||
38 | * If the notification has been viewed / dismissed or not. | ||
39 | * | ||
40 | * @return bool | ||
41 | */ | ||
42 | public function isRead(); | ||
43 | |||
44 | /** | ||
45 | * @param bool $read | ||
46 | * | ||
47 | * @return NotificationInterface | ||
48 | */ | ||
49 | public function setRead($read); | ||
50 | |||
51 | /** | ||
52 | * When the notification was sent. | ||
53 | * | ||
54 | * @return \DateTime | ||
55 | */ | ||
56 | public function getTimestamp(); | ||
57 | |||
58 | /** | ||
59 | * @param \DateTime $timestamp | ||
60 | * | ||
61 | * @return NotificationInterface | ||
62 | */ | ||
63 | public function setTimestamp(\DateTime $timestamp); | ||
64 | |||
65 | /** | ||
66 | * @param ActionInterface $action | ||
67 | * | ||
68 | * @return NotificationInterface | ||
69 | */ | ||
70 | public function addAction(ActionInterface $action); | ||
71 | |||
72 | /** | ||
73 | * @return string | ||
74 | */ | ||
75 | public function getDescription(); | ||
76 | |||
77 | /** | ||
78 | * @param string $description | ||
79 | * | ||
80 | * @return NotificationInterface | ||
81 | */ | ||
82 | public function setDescription($description); | ||
83 | |||
84 | /** | ||
85 | * @return array | ||
86 | */ | ||
87 | public function getParameters(); | ||
88 | |||
89 | /** | ||
90 | * @param array $parameters | ||
91 | * | ||
92 | * @return NotificationInterface | ||
93 | */ | ||
94 | public function setParameters($parameters); | ||
95 | |||
96 | /** | ||
97 | * @param string $key | ||
98 | * @param string $value | ||
99 | * | ||
100 | * @return NotificationInterface | ||
101 | */ | ||
102 | public function addParameter($key, $value); | ||
103 | } | ||
diff --git a/src/Wallabag/CoreBundle/Notifications/OkAction.php b/src/Wallabag/CoreBundle/Notifications/OkAction.php new file mode 100644 index 00000000..521e6742 --- /dev/null +++ b/src/Wallabag/CoreBundle/Notifications/OkAction.php | |||
@@ -0,0 +1,13 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\Notifications; | ||
4 | |||
5 | class OkAction extends Action | ||
6 | { | ||
7 | public function __construct($link) | ||
8 | { | ||
9 | $this->link = $link; | ||
10 | $this->label = 'OK'; | ||
11 | $this->type = Action::TYPE_OK; | ||
12 | } | ||
13 | } | ||
diff --git a/src/Wallabag/CoreBundle/Notifications/YesAction.php b/src/Wallabag/CoreBundle/Notifications/YesAction.php new file mode 100644 index 00000000..9b48eca5 --- /dev/null +++ b/src/Wallabag/CoreBundle/Notifications/YesAction.php | |||
@@ -0,0 +1,13 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\Notifications; | ||
4 | |||
5 | class YesAction extends Action | ||
6 | { | ||
7 | public function __construct($link) | ||
8 | { | ||
9 | $this->link = $link; | ||
10 | $this->label = 'Yes'; | ||
11 | $this->type = Action::TYPE_YES; | ||
12 | } | ||
13 | } | ||
diff --git a/src/Wallabag/CoreBundle/Repository/NotificationRepository.php b/src/Wallabag/CoreBundle/Repository/NotificationRepository.php new file mode 100644 index 00000000..6d6938ae --- /dev/null +++ b/src/Wallabag/CoreBundle/Repository/NotificationRepository.php | |||
@@ -0,0 +1,26 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\Repository; | ||
4 | |||
5 | use Doctrine\ORM\EntityRepository; | ||
6 | |||
7 | class NotificationRepository extends EntityRepository | ||
8 | { | ||
9 | public function markAllAsReadForUser($userId) | ||
10 | { | ||
11 | return $this->getEntityManager()->createQueryBuilder() | ||
12 | ->update('WallabagCoreBundle:Notification', 'n') | ||
13 | ->set('n.read', true) | ||
14 | ->where('n.user = :userId')->setParameter('userId', $userId) | ||
15 | ->getQuery() | ||
16 | ->getResult(); | ||
17 | } | ||
18 | |||
19 | public function getBuilderForNotificationsByUser($userId) | ||
20 | { | ||
21 | return $this->createQueryBuilder('n') | ||
22 | ->andWhere('n.user = :userId')->setParameter('userId', $userId) | ||
23 | ->orderBy('n.timestamp', 'desc') | ||
24 | ; | ||
25 | } | ||
26 | } | ||
diff --git a/src/Wallabag/CoreBundle/Resources/config/services.yml b/src/Wallabag/CoreBundle/Resources/config/services.yml index e09b0f18..183b6690 100644 --- a/src/Wallabag/CoreBundle/Resources/config/services.yml +++ b/src/Wallabag/CoreBundle/Resources/config/services.yml | |||
@@ -129,6 +129,12 @@ services: | |||
129 | calls: | 129 | calls: |
130 | - [ setCrypto, [ "@wallabag_core.helper.crypto_proxy" ] ] | 130 | - [ setCrypto, [ "@wallabag_core.helper.crypto_proxy" ] ] |
131 | 131 | ||
132 | wallabag_core.notification_repository: | ||
133 | class: Wallabag\CoreBundle\Repository\NotificationRepository | ||
134 | factory: [ "@doctrine.orm.default_entity_manager", getRepository ] | ||
135 | arguments: | ||
136 | - WallabagCoreBundle:Notification | ||
137 | |||
132 | wallabag_core.helper.entries_export: | 138 | wallabag_core.helper.entries_export: |
133 | class: Wallabag\CoreBundle\Helper\EntriesExport | 139 | class: Wallabag\CoreBundle\Helper\EntriesExport |
134 | arguments: | 140 | arguments: |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/date.da.yml b/src/Wallabag/CoreBundle/Resources/translations/date.da.yml new file mode 100644 index 00000000..3797b6bf --- /dev/null +++ b/src/Wallabag/CoreBundle/Resources/translations/date.da.yml | |||
@@ -0,0 +1,9 @@ | |||
1 | diff: | ||
2 | ago: | ||
3 | # empty: 'Now' | ||
4 | # second: 'One second ago|%count% seconds ago' | ||
5 | # minute: 'One minute ago|%count% minutes ago' | ||
6 | # hour: 'One hour ago|%count% hours ago' | ||
7 | # day: 'One day ago|%count% days ago' | ||
8 | # month: 'One month ago|%count% months ago' | ||
9 | # year: 'One year ago|%count% years ago' | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/date.de.yml b/src/Wallabag/CoreBundle/Resources/translations/date.de.yml new file mode 100644 index 00000000..3797b6bf --- /dev/null +++ b/src/Wallabag/CoreBundle/Resources/translations/date.de.yml | |||
@@ -0,0 +1,9 @@ | |||
1 | diff: | ||
2 | ago: | ||
3 | # empty: 'Now' | ||
4 | # second: 'One second ago|%count% seconds ago' | ||
5 | # minute: 'One minute ago|%count% minutes ago' | ||
6 | # hour: 'One hour ago|%count% hours ago' | ||
7 | # day: 'One day ago|%count% days ago' | ||
8 | # month: 'One month ago|%count% months ago' | ||
9 | # year: 'One year ago|%count% years ago' | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/date.en.yml b/src/Wallabag/CoreBundle/Resources/translations/date.en.yml new file mode 100644 index 00000000..7bcd6d0c --- /dev/null +++ b/src/Wallabag/CoreBundle/Resources/translations/date.en.yml | |||
@@ -0,0 +1,9 @@ | |||
1 | diff: | ||
2 | ago: | ||
3 | empty: 'Now' | ||
4 | second: 'One second ago|%count% seconds ago' | ||
5 | minute: 'One minute ago|%count% minutes ago' | ||
6 | hour: 'One hour ago|%count% hours ago' | ||
7 | day: 'One day ago|%count% days ago' | ||
8 | month: 'One month ago|%count% months ago' | ||
9 | year: 'One year ago|%count% years ago' | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/date.es.yml b/src/Wallabag/CoreBundle/Resources/translations/date.es.yml new file mode 100644 index 00000000..3797b6bf --- /dev/null +++ b/src/Wallabag/CoreBundle/Resources/translations/date.es.yml | |||
@@ -0,0 +1,9 @@ | |||
1 | diff: | ||
2 | ago: | ||
3 | # empty: 'Now' | ||
4 | # second: 'One second ago|%count% seconds ago' | ||
5 | # minute: 'One minute ago|%count% minutes ago' | ||
6 | # hour: 'One hour ago|%count% hours ago' | ||
7 | # day: 'One day ago|%count% days ago' | ||
8 | # month: 'One month ago|%count% months ago' | ||
9 | # year: 'One year ago|%count% years ago' | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/date.fa.yml b/src/Wallabag/CoreBundle/Resources/translations/date.fa.yml new file mode 100644 index 00000000..3797b6bf --- /dev/null +++ b/src/Wallabag/CoreBundle/Resources/translations/date.fa.yml | |||
@@ -0,0 +1,9 @@ | |||
1 | diff: | ||
2 | ago: | ||
3 | # empty: 'Now' | ||
4 | # second: 'One second ago|%count% seconds ago' | ||
5 | # minute: 'One minute ago|%count% minutes ago' | ||
6 | # hour: 'One hour ago|%count% hours ago' | ||
7 | # day: 'One day ago|%count% days ago' | ||
8 | # month: 'One month ago|%count% months ago' | ||
9 | # year: 'One year ago|%count% years ago' | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/date.fr.yml b/src/Wallabag/CoreBundle/Resources/translations/date.fr.yml new file mode 100644 index 00000000..02eb96b0 --- /dev/null +++ b/src/Wallabag/CoreBundle/Resources/translations/date.fr.yml | |||
@@ -0,0 +1,9 @@ | |||
1 | diff: | ||
2 | ago: | ||
3 | empty: 'Maintenant' | ||
4 | second: 'Il y a une seconde|il y a %count% secondes' | ||
5 | minute: 'Il y a une minute|il y a %count% minutes' | ||
6 | hour: 'Il y a une heure|il y a %count% heures' | ||
7 | day: 'Il y a un jour|Il y a %count% jours' | ||
8 | month: 'Il y a un mois|il y a %count% mois' | ||
9 | year: 'Il y a un an|il y a %count% ans' | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/date.it.yml b/src/Wallabag/CoreBundle/Resources/translations/date.it.yml new file mode 100644 index 00000000..3797b6bf --- /dev/null +++ b/src/Wallabag/CoreBundle/Resources/translations/date.it.yml | |||
@@ -0,0 +1,9 @@ | |||
1 | diff: | ||
2 | ago: | ||
3 | # empty: 'Now' | ||
4 | # second: 'One second ago|%count% seconds ago' | ||
5 | # minute: 'One minute ago|%count% minutes ago' | ||
6 | # hour: 'One hour ago|%count% hours ago' | ||
7 | # day: 'One day ago|%count% days ago' | ||
8 | # month: 'One month ago|%count% months ago' | ||
9 | # year: 'One year ago|%count% years ago' | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/date.oc.yml b/src/Wallabag/CoreBundle/Resources/translations/date.oc.yml new file mode 100644 index 00000000..3797b6bf --- /dev/null +++ b/src/Wallabag/CoreBundle/Resources/translations/date.oc.yml | |||
@@ -0,0 +1,9 @@ | |||
1 | diff: | ||
2 | ago: | ||
3 | # empty: 'Now' | ||
4 | # second: 'One second ago|%count% seconds ago' | ||
5 | # minute: 'One minute ago|%count% minutes ago' | ||
6 | # hour: 'One hour ago|%count% hours ago' | ||
7 | # day: 'One day ago|%count% days ago' | ||
8 | # month: 'One month ago|%count% months ago' | ||
9 | # year: 'One year ago|%count% years ago' | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/date.pl.yml b/src/Wallabag/CoreBundle/Resources/translations/date.pl.yml new file mode 100644 index 00000000..3797b6bf --- /dev/null +++ b/src/Wallabag/CoreBundle/Resources/translations/date.pl.yml | |||
@@ -0,0 +1,9 @@ | |||
1 | diff: | ||
2 | ago: | ||
3 | # empty: 'Now' | ||
4 | # second: 'One second ago|%count% seconds ago' | ||
5 | # minute: 'One minute ago|%count% minutes ago' | ||
6 | # hour: 'One hour ago|%count% hours ago' | ||
7 | # day: 'One day ago|%count% days ago' | ||
8 | # month: 'One month ago|%count% months ago' | ||
9 | # year: 'One year ago|%count% years ago' | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/date.pt.yml b/src/Wallabag/CoreBundle/Resources/translations/date.pt.yml new file mode 100644 index 00000000..3797b6bf --- /dev/null +++ b/src/Wallabag/CoreBundle/Resources/translations/date.pt.yml | |||
@@ -0,0 +1,9 @@ | |||
1 | diff: | ||
2 | ago: | ||
3 | # empty: 'Now' | ||
4 | # second: 'One second ago|%count% seconds ago' | ||
5 | # minute: 'One minute ago|%count% minutes ago' | ||
6 | # hour: 'One hour ago|%count% hours ago' | ||
7 | # day: 'One day ago|%count% days ago' | ||
8 | # month: 'One month ago|%count% months ago' | ||
9 | # year: 'One year ago|%count% years ago' | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/date.ro.yml b/src/Wallabag/CoreBundle/Resources/translations/date.ro.yml new file mode 100644 index 00000000..3797b6bf --- /dev/null +++ b/src/Wallabag/CoreBundle/Resources/translations/date.ro.yml | |||
@@ -0,0 +1,9 @@ | |||
1 | diff: | ||
2 | ago: | ||
3 | # empty: 'Now' | ||
4 | # second: 'One second ago|%count% seconds ago' | ||
5 | # minute: 'One minute ago|%count% minutes ago' | ||
6 | # hour: 'One hour ago|%count% hours ago' | ||
7 | # day: 'One day ago|%count% days ago' | ||
8 | # month: 'One month ago|%count% months ago' | ||
9 | # year: 'One year ago|%count% years ago' | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/date.tr.yml b/src/Wallabag/CoreBundle/Resources/translations/date.tr.yml new file mode 100644 index 00000000..3797b6bf --- /dev/null +++ b/src/Wallabag/CoreBundle/Resources/translations/date.tr.yml | |||
@@ -0,0 +1,9 @@ | |||
1 | diff: | ||
2 | ago: | ||
3 | # empty: 'Now' | ||
4 | # second: 'One second ago|%count% seconds ago' | ||
5 | # minute: 'One minute ago|%count% minutes ago' | ||
6 | # hour: 'One hour ago|%count% hours ago' | ||
7 | # day: 'One day ago|%count% days ago' | ||
8 | # month: 'One month ago|%count% months ago' | ||
9 | # year: 'One year ago|%count% years ago' | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml index 5229ac73..ad03fa91 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml | |||
@@ -38,6 +38,7 @@ menu: | |||
38 | search: 'Søg' | 38 | search: 'Søg' |
39 | filter_entries: 'Filtrer artikler' | 39 | filter_entries: 'Filtrer artikler' |
40 | # export: 'Export' | 40 | # export: 'Export' |
41 | # notifications: 'Notifications' | ||
41 | search_form: | 42 | search_form: |
42 | input_label: 'Indtast søgning' | 43 | input_label: 'Indtast søgning' |
43 | 44 | ||
@@ -250,6 +251,17 @@ entry: | |||
250 | # delete: "Are you sure you want to remove that article?" | 251 | # delete: "Are you sure you want to remove that article?" |
251 | # delete_tag: "Are you sure you want to remove that tag from that article?" | 252 | # delete_tag: "Are you sure you want to remove that tag from that article?" |
252 | 253 | ||
254 | # notifications: | ||
255 | # sidebar: | ||
256 | # view_more: 'View more' | ||
257 | # list: | ||
258 | # page_title: 'Notifications' | ||
259 | # mark_all_as_read: 'Mark all as read' | ||
260 | # none: "No notifications yet :'(" | ||
261 | # release: | ||
262 | # title: "wallabag has been updated to version %version%" | ||
263 | # details: "View update's details" | ||
264 | |||
253 | about: | 265 | about: |
254 | page_title: 'Om' | 266 | page_title: 'Om' |
255 | top_menu: | 267 | top_menu: |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml index 996f173a..f2d75a62 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml | |||
@@ -38,6 +38,7 @@ menu: | |||
38 | search: 'Suche' | 38 | search: 'Suche' |
39 | filter_entries: 'Artikel filtern' | 39 | filter_entries: 'Artikel filtern' |
40 | export: 'Exportieren' | 40 | export: 'Exportieren' |
41 | # notifications: 'Notifications' | ||
41 | search_form: | 42 | search_form: |
42 | input_label: 'Suchbegriff hier eingeben' | 43 | input_label: 'Suchbegriff hier eingeben' |
43 | 44 | ||
@@ -250,6 +251,17 @@ entry: | |||
250 | delete: "Bist du sicher, dass du diesen Artikel löschen möchtest?" | 251 | delete: "Bist du sicher, dass du diesen Artikel löschen möchtest?" |
251 | delete_tag: "Bist du sicher, dass du diesen Tag vom Artikel entfernen möchtest?" | 252 | delete_tag: "Bist du sicher, dass du diesen Tag vom Artikel entfernen möchtest?" |
252 | 253 | ||
254 | # notifications: | ||
255 | # sidebar: | ||
256 | # view_more: 'View more' | ||
257 | # list: | ||
258 | # page_title: 'Notifications' | ||
259 | # mark_all_as_read: 'Mark all as read' | ||
260 | # none: "No notifications yet :'(" | ||
261 | # release: | ||
262 | # title: "wallabag has been updated to version %version%" | ||
263 | # details: "View update's details" | ||
264 | |||
253 | about: | 265 | about: |
254 | page_title: 'Über' | 266 | page_title: 'Über' |
255 | top_menu: | 267 | top_menu: |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml index aa1cd1a9..228dd87e 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml | |||
@@ -38,6 +38,7 @@ menu: | |||
38 | search: 'Search' | 38 | search: 'Search' |
39 | filter_entries: 'Filter entries' | 39 | filter_entries: 'Filter entries' |
40 | export: 'Export' | 40 | export: 'Export' |
41 | notifications: 'Notifications' | ||
41 | search_form: | 42 | search_form: |
42 | input_label: 'Enter your search here' | 43 | input_label: 'Enter your search here' |
43 | 44 | ||
@@ -250,6 +251,16 @@ entry: | |||
250 | delete: "Are you sure you want to remove that article?" | 251 | delete: "Are you sure you want to remove that article?" |
251 | delete_tag: "Are you sure you want to remove that tag from that article?" | 252 | delete_tag: "Are you sure you want to remove that tag from that article?" |
252 | 253 | ||
254 | notifications: | ||
255 | sidebar: | ||
256 | view_more: 'View more' | ||
257 | list: | ||
258 | page_title: 'Notifications' | ||
259 | mark_all_as_read: 'Mark all as read' | ||
260 | none: "No notifications yet :'(" | ||
261 | release: | ||
262 | title: "wallabag has been updated to version %version%" | ||
263 | details: "View update's details" | ||
253 | about: | 264 | about: |
254 | page_title: 'About' | 265 | page_title: 'About' |
255 | top_menu: | 266 | top_menu: |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml index 96998f53..28f064c7 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml | |||
@@ -38,6 +38,7 @@ menu: | |||
38 | search: 'Buscar' | 38 | search: 'Buscar' |
39 | filter_entries: 'Filtrar los artículos' | 39 | filter_entries: 'Filtrar los artículos' |
40 | export: 'Exportar' | 40 | export: 'Exportar' |
41 | # notifications: 'Notifications' | ||
41 | search_form: | 42 | search_form: |
42 | input_label: 'Introduzca su búsqueda aquí' | 43 | input_label: 'Introduzca su búsqueda aquí' |
43 | 44 | ||
@@ -250,6 +251,17 @@ entry: | |||
250 | # delete: "Are you sure you want to remove that article?" | 251 | # delete: "Are you sure you want to remove that article?" |
251 | # delete_tag: "Are you sure you want to remove that tag from that article?" | 252 | # delete_tag: "Are you sure you want to remove that tag from that article?" |
252 | 253 | ||
254 | # notifications: | ||
255 | # sidebar: | ||
256 | # view_more: 'View more' | ||
257 | # list: | ||
258 | # page_title: 'Notifications' | ||
259 | # mark_all_as_read: 'Mark all as read' | ||
260 | # none: "No notifications yet :'(" | ||
261 | # release: | ||
262 | # title: "wallabag has been updated to version %version%" | ||
263 | # details: "View update's details" | ||
264 | |||
253 | about: | 265 | about: |
254 | page_title: 'Acerca de' | 266 | page_title: 'Acerca de' |
255 | top_menu: | 267 | top_menu: |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml index 57e6c029..4496a2f2 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml | |||
@@ -38,6 +38,7 @@ menu: | |||
38 | search: 'جستجو' | 38 | search: 'جستجو' |
39 | filter_entries: 'فیلترکردن مقالهها' | 39 | filter_entries: 'فیلترکردن مقالهها' |
40 | export: 'برونبری' | 40 | export: 'برونبری' |
41 | # notifications: 'Notifications' | ||
41 | search_form: | 42 | search_form: |
42 | input_label: 'جستجوی خود را اینجا بنویسید:' | 43 | input_label: 'جستجوی خود را اینجا بنویسید:' |
43 | 44 | ||
@@ -250,6 +251,17 @@ entry: | |||
250 | # delete: "Are you sure you want to remove that article?" | 251 | # delete: "Are you sure you want to remove that article?" |
251 | # delete_tag: "Are you sure you want to remove that tag from that article?" | 252 | # delete_tag: "Are you sure you want to remove that tag from that article?" |
252 | 253 | ||
254 | # notifications: | ||
255 | # sidebar: | ||
256 | # view_more: 'View more' | ||
257 | # list: | ||
258 | # page_title: 'Notifications' | ||
259 | # mark_all_as_read: 'Mark all as read' | ||
260 | # none: "No notifications yet :'(" | ||
261 | # release: | ||
262 | # title: "wallabag has been updated to version %version%" | ||
263 | # details: "View update's details" | ||
264 | |||
253 | about: | 265 | about: |
254 | page_title: 'درباره' | 266 | page_title: 'درباره' |
255 | top_menu: | 267 | top_menu: |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml index 6eac4c36..8bccc8a4 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml | |||
@@ -38,6 +38,7 @@ menu: | |||
38 | search: "Rechercher" | 38 | search: "Rechercher" |
39 | filter_entries: "Filtrer les articles" | 39 | filter_entries: "Filtrer les articles" |
40 | export: "Exporter" | 40 | export: "Exporter" |
41 | notifications: 'Notifications' | ||
41 | search_form: | 42 | search_form: |
42 | input_label: "Saisissez votre terme de recherche" | 43 | input_label: "Saisissez votre terme de recherche" |
43 | 44 | ||
@@ -250,6 +251,17 @@ entry: | |||
250 | delete: "Voulez-vous vraiment supprimer cet article ?" | 251 | delete: "Voulez-vous vraiment supprimer cet article ?" |
251 | delete_tag: "Voulez-vous vraiment supprimer ce tag de cet article ?" | 252 | delete_tag: "Voulez-vous vraiment supprimer ce tag de cet article ?" |
252 | 253 | ||
254 | notifications: | ||
255 | sidebar: | ||
256 | view_more: 'Voir plus' | ||
257 | list: | ||
258 | page_title: 'Notifications' | ||
259 | mark_all_as_read: 'Marquer tout comme lu' | ||
260 | none: "Aucune notification pour le moment :'(" | ||
261 | release: | ||
262 | title: "wallabag a été mis à jour vers la version %version%" | ||
263 | details: "Voir les détails de la mise à jour" | ||
264 | |||
253 | about: | 265 | about: |
254 | page_title: "À propos" | 266 | page_title: "À propos" |
255 | top_menu: | 267 | top_menu: |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml index fa7ae0b2..d15ce5f9 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml | |||
@@ -38,6 +38,7 @@ menu: | |||
38 | search: 'Cerca' | 38 | search: 'Cerca' |
39 | filter_entries: 'Filtra contenuti' | 39 | filter_entries: 'Filtra contenuti' |
40 | export: 'Esporta' | 40 | export: 'Esporta' |
41 | # notifications: 'Notifications' | ||
41 | search_form: | 42 | search_form: |
42 | input_label: 'Inserisci qui la tua ricerca' | 43 | input_label: 'Inserisci qui la tua ricerca' |
43 | 44 | ||
@@ -250,6 +251,16 @@ entry: | |||
250 | delete: "Vuoi veramente rimuovere quell'articolo?" | 251 | delete: "Vuoi veramente rimuovere quell'articolo?" |
251 | delete_tag: "Vuoi veramente rimuovere quell'etichetta da quell'articolo?" | 252 | delete_tag: "Vuoi veramente rimuovere quell'etichetta da quell'articolo?" |
252 | 253 | ||
254 | # notifications: | ||
255 | # sidebar: | ||
256 | # view_more: 'View more' | ||
257 | # list: | ||
258 | # page_title: 'Notifications' | ||
259 | # mark_all_as_read: 'Mark all as read' | ||
260 | # none: "No notifications yet :'(" | ||
261 | # release: | ||
262 | # title: "wallabag has been updated to version %version%" | ||
263 | # details: "View update's details" | ||
253 | about: | 264 | about: |
254 | page_title: 'A proposito' | 265 | page_title: 'A proposito' |
255 | top_menu: | 266 | top_menu: |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml index be57e903..af9d85c3 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml | |||
@@ -38,6 +38,7 @@ menu: | |||
38 | search: 'Cercar' | 38 | search: 'Cercar' |
39 | filter_entries: 'Filtrar los articles' | 39 | filter_entries: 'Filtrar los articles' |
40 | export: 'Exportar' | 40 | export: 'Exportar' |
41 | # notifications: 'Notifications' | ||
41 | search_form: | 42 | search_form: |
42 | input_label: 'Picatz vòstre mot-clau a cercar aquí' | 43 | input_label: 'Picatz vòstre mot-clau a cercar aquí' |
43 | 44 | ||
@@ -250,6 +251,17 @@ entry: | |||
250 | # delete: "Are you sure you want to remove that article?" | 251 | # delete: "Are you sure you want to remove that article?" |
251 | # delete_tag: "Are you sure you want to remove that tag from that article?" | 252 | # delete_tag: "Are you sure you want to remove that tag from that article?" |
252 | 253 | ||
254 | # notifications: | ||
255 | # sidebar: | ||
256 | # view_more: 'View more' | ||
257 | # list: | ||
258 | # page_title: 'Notifications' | ||
259 | # mark_all_as_read: 'Mark all as read' | ||
260 | # none: "No notifications yet :'(" | ||
261 | # release: | ||
262 | # title: "wallabag has been updated to version %version%" | ||
263 | # details: "View update's details" | ||
264 | |||
253 | about: | 265 | about: |
254 | page_title: 'A prepaus' | 266 | page_title: 'A prepaus' |
255 | top_menu: | 267 | top_menu: |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml index 00c559ed..78464162 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml | |||
@@ -38,6 +38,7 @@ menu: | |||
38 | search: 'Szukaj' | 38 | search: 'Szukaj' |
39 | filter_entries: 'Filtruj wpisy' | 39 | filter_entries: 'Filtruj wpisy' |
40 | export: 'Eksportuj' | 40 | export: 'Eksportuj' |
41 | # notifications: 'Notifications' | ||
41 | search_form: | 42 | search_form: |
42 | input_label: 'Wpisz swoje zapytanie tutaj' | 43 | input_label: 'Wpisz swoje zapytanie tutaj' |
43 | 44 | ||
@@ -250,6 +251,17 @@ entry: | |||
250 | delete: "Czy jesteś pewien, że chcesz usunąć ten artykuł?" | 251 | delete: "Czy jesteś pewien, że chcesz usunąć ten artykuł?" |
251 | delete_tag: "Czy jesteś pewien, że chcesz usunąć ten tag, z tego artykułu?" | 252 | delete_tag: "Czy jesteś pewien, że chcesz usunąć ten tag, z tego artykułu?" |
252 | 253 | ||
254 | # notifications: | ||
255 | # sidebar: | ||
256 | # view_more: 'View more' | ||
257 | # list: | ||
258 | # page_title: 'Notifications' | ||
259 | # mark_all_as_read: 'Mark all as read' | ||
260 | # none: "No notifications yet :'(" | ||
261 | # release: | ||
262 | # title: "wallabag has been updated to version %version%" | ||
263 | # details: "View update's details" | ||
264 | |||
253 | about: | 265 | about: |
254 | page_title: 'O nas' | 266 | page_title: 'O nas' |
255 | top_menu: | 267 | top_menu: |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml index 4ab5f144..07eeefc2 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml | |||
@@ -38,6 +38,7 @@ menu: | |||
38 | search: 'Pesquisa' | 38 | search: 'Pesquisa' |
39 | filter_entries: 'Filtrar entradas' | 39 | filter_entries: 'Filtrar entradas' |
40 | export: 'Exportar' | 40 | export: 'Exportar' |
41 | # notifications: 'Notifications' | ||
41 | search_form: | 42 | search_form: |
42 | input_label: 'Digite aqui sua pesquisa' | 43 | input_label: 'Digite aqui sua pesquisa' |
43 | 44 | ||
@@ -250,6 +251,17 @@ entry: | |||
250 | # delete: "Are you sure you want to remove that article?" | 251 | # delete: "Are you sure you want to remove that article?" |
251 | # delete_tag: "Are you sure you want to remove that tag from that article?" | 252 | # delete_tag: "Are you sure you want to remove that tag from that article?" |
252 | 253 | ||
254 | # notifications: | ||
255 | # sidebar: | ||
256 | # view_more: 'View more' | ||
257 | # list: | ||
258 | # page_title: 'Notifications' | ||
259 | # mark_all_as_read: 'Mark all as read' | ||
260 | # none: "No notifications yet :'(" | ||
261 | # release: | ||
262 | # title: "wallabag has been updated to version %version%" | ||
263 | # details: "View update's details" | ||
264 | |||
253 | about: | 265 | about: |
254 | page_title: 'Sobre' | 266 | page_title: 'Sobre' |
255 | top_menu: | 267 | top_menu: |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml index f16504ed..ba7b11d5 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml | |||
@@ -38,6 +38,7 @@ menu: | |||
38 | search: 'Căutare' | 38 | search: 'Căutare' |
39 | filter_entries: 'Filtrează articolele' | 39 | filter_entries: 'Filtrează articolele' |
40 | # export: 'Export' | 40 | # export: 'Export' |
41 | # notifications: 'Notifications' | ||
41 | search_form: | 42 | search_form: |
42 | input_label: 'Introdu căutarea ta' | 43 | input_label: 'Introdu căutarea ta' |
43 | 44 | ||
@@ -250,6 +251,17 @@ entry: | |||
250 | # delete: "Are you sure you want to remove that article?" | 251 | # delete: "Are you sure you want to remove that article?" |
251 | # delete_tag: "Are you sure you want to remove that tag from that article?" | 252 | # delete_tag: "Are you sure you want to remove that tag from that article?" |
252 | 253 | ||
254 | # notifications: | ||
255 | # sidebar: | ||
256 | # view_more: 'View more' | ||
257 | # list: | ||
258 | # page_title: 'Notifications' | ||
259 | # mark_all_as_read: 'Mark all as read' | ||
260 | # none: "No notifications yet :'(" | ||
261 | # release: | ||
262 | # title: "wallabag has been updated to version %version%" | ||
263 | # details: "View update's details" | ||
264 | |||
253 | about: | 265 | about: |
254 | page_title: 'Despre' | 266 | page_title: 'Despre' |
255 | top_menu: | 267 | top_menu: |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml index 90a140cd..6cbe6dcd 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml | |||
@@ -38,6 +38,7 @@ menu: | |||
38 | search: 'Ara' | 38 | search: 'Ara' |
39 | filter_entries: 'Filtrele' | 39 | filter_entries: 'Filtrele' |
40 | export: 'Dışa Aktar' | 40 | export: 'Dışa Aktar' |
41 | # notifications: 'Notifications' | ||
41 | search_form: | 42 | search_form: |
42 | input_label: 'Aramak istediğiniz herhangi bir şey yazın' | 43 | input_label: 'Aramak istediğiniz herhangi bir şey yazın' |
43 | 44 | ||
@@ -248,6 +249,17 @@ entry: | |||
248 | # delete: "Are you sure you want to remove that article?" | 249 | # delete: "Are you sure you want to remove that article?" |
249 | # delete_tag: "Are you sure you want to remove that tag from that article?" | 250 | # delete_tag: "Are you sure you want to remove that tag from that article?" |
250 | 251 | ||
252 | # notifications: | ||
253 | # sidebar: | ||
254 | # view_more: 'View more' | ||
255 | # list: | ||
256 | # page_title: 'Notifications' | ||
257 | # mark_all_as_read: 'Mark all as read' | ||
258 | # none: "No notifications yet :'(" | ||
259 | # release: | ||
260 | # title: "wallabag has been updated to version %version%" | ||
261 | # details: "View update's details" | ||
262 | |||
251 | about: | 263 | about: |
252 | page_title: 'Hakkımızda' | 264 | page_title: 'Hakkımızda' |
253 | top_menu: | 265 | top_menu: |
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/common/Developer/client.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/common/Developer/client.html.twig index 8a5da71a..d566795d 100644 --- a/src/Wallabag/CoreBundle/Resources/views/themes/common/Developer/client.html.twig +++ b/src/Wallabag/CoreBundle/Resources/views/themes/common/Developer/client.html.twig | |||
@@ -3,6 +3,7 @@ | |||
3 | {% block title %}{{ 'developer.client.page_title'|trans }}{% endblock %} | 3 | {% block title %}{{ 'developer.client.page_title'|trans }}{% endblock %} |
4 | 4 | ||
5 | {% block content %} | 5 | {% block content %} |
6 | {{ parent() }} | ||
6 | <div class="row"> | 7 | <div class="row"> |
7 | <div class="col s12"> | 8 | <div class="col s12"> |
8 | <div class="card-panel settings"> | 9 | <div class="card-panel settings"> |
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/common/Developer/index.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/common/Developer/index.html.twig index 528b055c..53210688 100644 --- a/src/Wallabag/CoreBundle/Resources/views/themes/common/Developer/index.html.twig +++ b/src/Wallabag/CoreBundle/Resources/views/themes/common/Developer/index.html.twig | |||
@@ -3,6 +3,7 @@ | |||
3 | {% block title %}{{ 'developer.page_title'|trans }}{% endblock %} | 3 | {% block title %}{{ 'developer.page_title'|trans }}{% endblock %} |
4 | 4 | ||
5 | {% block content %} | 5 | {% block content %} |
6 | {{ parent() }} | ||
6 | <div class="row"> | 7 | <div class="row"> |
7 | <div class="col s12"> | 8 | <div class="col s12"> |
8 | <div class="card-panel settings"> | 9 | <div class="card-panel settings"> |
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/common/Static/about.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/common/Static/about.html.twig index 1cd3485c..ffd9b1b8 100644 --- a/src/Wallabag/CoreBundle/Resources/views/themes/common/Static/about.html.twig +++ b/src/Wallabag/CoreBundle/Resources/views/themes/common/Static/about.html.twig | |||
@@ -3,7 +3,7 @@ | |||
3 | {% block title %}{{ 'about.page_title'|trans }}{% endblock %} | 3 | {% block title %}{{ 'about.page_title'|trans }}{% endblock %} |
4 | 4 | ||
5 | {% block content %} | 5 | {% block content %} |
6 | 6 | {{ parent() }} | |
7 | <div class="row"> | 7 | <div class="row"> |
8 | <div class="col s12"> | 8 | <div class="col s12"> |
9 | <div class="card-panel settings"> | 9 | <div class="card-panel settings"> |
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/common/Static/howto.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/common/Static/howto.html.twig index 231f9bdf..4c598b85 100644 --- a/src/Wallabag/CoreBundle/Resources/views/themes/common/Static/howto.html.twig +++ b/src/Wallabag/CoreBundle/Resources/views/themes/common/Static/howto.html.twig | |||
@@ -3,7 +3,7 @@ | |||
3 | {% block title %}{{ 'howto.page_title'|trans }}{% endblock %} | 3 | {% block title %}{{ 'howto.page_title'|trans }}{% endblock %} |
4 | 4 | ||
5 | {% block content %} | 5 | {% block content %} |
6 | 6 | {{ parent() }} | |
7 | <div class="row"> | 7 | <div class="row"> |
8 | <div class="col s12"> | 8 | <div class="col s12"> |
9 | <div class="card-panel settings"> | 9 | <div class="card-panel settings"> |
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/common/Static/quickstart.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/common/Static/quickstart.html.twig index 4580813c..70265be2 100644 --- a/src/Wallabag/CoreBundle/Resources/views/themes/common/Static/quickstart.html.twig +++ b/src/Wallabag/CoreBundle/Resources/views/themes/common/Static/quickstart.html.twig | |||
@@ -3,7 +3,7 @@ | |||
3 | {% block title %}{{ 'quickstart.page_title'|trans }}{% endblock %} | 3 | {% block title %}{{ 'quickstart.page_title'|trans }}{% endblock %} |
4 | 4 | ||
5 | {% block content %} | 5 | {% block content %} |
6 | 6 | {{ parent() }} | |
7 | <div class="row quickstart"> | 7 | <div class="row quickstart"> |
8 | <div class="col s12"> | 8 | <div class="col s12"> |
9 | <div class="card-panel settings"> | 9 | <div class="card-panel settings"> |
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 a8143315..bd5932b0 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 | |||
@@ -3,7 +3,7 @@ | |||
3 | {% block title %}{{ 'config.page_title'|trans }}{% endblock %} | 3 | {% block title %}{{ 'config.page_title'|trans }}{% endblock %} |
4 | 4 | ||
5 | {% block content %} | 5 | {% block content %} |
6 | 6 | {{ parent() }} | |
7 | <div class="row"> | 7 | <div class="row"> |
8 | <div class="col s12"> | 8 | <div class="col s12"> |
9 | <div class="card-panel settings"> | 9 | <div class="card-panel settings"> |
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entries.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entries.html.twig index 0c4dc80b..1cc5489f 100644 --- a/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entries.html.twig +++ b/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entries.html.twig | |||
@@ -19,6 +19,7 @@ | |||
19 | {% endblock %} | 19 | {% endblock %} |
20 | 20 | ||
21 | {% block content %} | 21 | {% block content %} |
22 | {{ parent() }} | ||
22 | {% set listMode = app.user.config.listMode %} | 23 | {% set listMode = app.user.config.listMode %} |
23 | {% set currentRoute = app.request.attributes.get('_route') %} | 24 | {% set currentRoute = app.request.attributes.get('_route') %} |
24 | <div class="results clearfix"> | 25 | <div class="results clearfix"> |
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/material/Notification/notifications.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/Notification/notifications.html.twig new file mode 100644 index 00000000..49cc0d40 --- /dev/null +++ b/src/Wallabag/CoreBundle/Resources/views/themes/material/Notification/notifications.html.twig | |||
@@ -0,0 +1,56 @@ | |||
1 | {% extends "WallabagCoreBundle::layout.html.twig" %} | ||
2 | |||
3 | {% block title %}{{ 'notifications.list.page_title' | trans }}{% endblock %} | ||
4 | |||
5 | {% block content %} | ||
6 | {{ parent() }} | ||
7 | <div class="row notifications-page"> | ||
8 | <div class="col l8 offset-l2"> | ||
9 | {% if app.user.notifications is not empty %} | ||
10 | <div class="row"> | ||
11 | <a href="{{ path('notification-archive-all') }}" class="btn-light waves-effect waves-light right"><i class="material-icons">done_all</i> {{ 'notifications.list.mark_all_as_read' | trans }}</a> | ||
12 | </div> | ||
13 | <ul class="collection"> | ||
14 | {% for notification in notifications | slice(0, 10) %} | ||
15 | <li class="notification collection-item avatar{% if not notification.read %} light-blue lighten-5{% else %} grey-text{% endif %}"> | ||
16 | <i class="material-icons circle">{% spaceless %} | ||
17 | {% if notification.type == constant('TYPE_ADMIN', notification) %} | ||
18 | build | ||
19 | {% elseif notification.type == constant('TYPE_USER', notification) %} | ||
20 | person | ||
21 | {% elseif notification.type == constant('TYPE_RELEASE', notification) %} | ||
22 | new_releases | ||
23 | {% endif %} | ||
24 | {% endspaceless %}</i> | ||
25 | <span class="title">{{ notification.title | trans(notification.parameters) }}</span> | ||
26 | <p>{{ notification.description | trans | trans(notification.parameters) }}</p> | ||
27 | <time datetime="{{ notification.timestamp | date }}">{{ notification.timestamp | time_diff }}</time> | ||
28 | <div class="secondary-content"> | ||
29 | {% if not notification.read %} | ||
30 | {% for action in notification.actions %} | ||
31 | <a class="notification-action btn waves-effect waves-light {% spaceless %} | ||
32 | {% if action.type == constant('TYPE_OK', action) %} | ||
33 | {% elseif action.type == constant('TYPE_YES', action) %} | ||
34 | cyan | ||
35 | {% elseif action.type == constant('TYPE_NO', action) %} | ||
36 | red | ||
37 | {% elseif action.type == constant('TYPE_INFO', action) %} | ||
38 | blue-grey | ||
39 | {% endif %} | ||
40 | {% endspaceless %}" data-id="{{ notification.id }}" href="{{ path('notification-archive-redirect', {'redirection': action.link, 'notification': notification.id}) }}">{{ action.label | trans(notification.parameters) }}</a> | ||
41 | {% endfor %} | ||
42 | <a href="{{ path('notification-archive', {'notification': notification.id}) }}" class="notification-action waves-effect waves-teal btn-flat"><i class="material-icons">check</i></a> | ||
43 | {% endif %} | ||
44 | </div> | ||
45 | </li> | ||
46 | {% endfor %} | ||
47 | </ul> | ||
48 | {% else %} | ||
49 | {{ 'notifications.list.none' | trans }} | ||
50 | {% endif %} | ||
51 | {% if notifications.getNbPages > 1 %} | ||
52 | {{ pagerfanta(notifications, 'twitter_bootstrap_translated', {'proximity': 1}) }} | ||
53 | {% endif %} | ||
54 | </div> | ||
55 | </div> | ||
56 | {% endblock %} | ||
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/material/Tag/tags.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/Tag/tags.html.twig index 97ddedc9..7a0f793c 100644 --- a/src/Wallabag/CoreBundle/Resources/views/themes/material/Tag/tags.html.twig +++ b/src/Wallabag/CoreBundle/Resources/views/themes/material/Tag/tags.html.twig | |||
@@ -3,6 +3,7 @@ | |||
3 | {% block title %}{{ 'tag.page_title'|trans }}{% endblock %} | 3 | {% block title %}{{ 'tag.page_title'|trans }}{% endblock %} |
4 | 4 | ||
5 | {% block content %} | 5 | {% block content %} |
6 | {{ parent() }} | ||
6 | <div class="results clearfix"> | 7 | <div class="results clearfix"> |
7 | <div class="nb-results left">{{ 'tag.list.number_on_the_page'|transchoice(tags|length) }}</div> | 8 | <div class="nb-results left">{{ 'tag.list.number_on_the_page'|transchoice(tags|length) }}</div> |
8 | </div> | 9 | </div> |
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/material/layout.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/layout.html.twig index 60907e11..ccc44931 100644 --- a/src/Wallabag/CoreBundle/Resources/views/themes/material/layout.html.twig +++ b/src/Wallabag/CoreBundle/Resources/views/themes/material/layout.html.twig | |||
@@ -110,6 +110,13 @@ | |||
110 | <i class="material-icons">search</i> | 110 | <i class="material-icons">search</i> |
111 | </a> | 111 | </a> |
112 | </li> | 112 | </li> |
113 | <li id="button_notifications"> | ||
114 | {% set unreadNotifs = get_notifications() | unread_notif | length %} | ||
115 | <a class="nav-panel-menu button-collapse-right tooltipped js-notifications-action" data-position="bottom" data-delay="50" data-tooltip="{{ 'menu.top.notifications' | trans }}" href="#" data-activates="notifications"> | ||
116 | <i class="material-icons">notifications{% if unreadNotifs == 0 %}_none{% endif %}</i> | ||
117 | {% if unreadNotifs > 0 %}<span id="notifications-count" class="red-text text-accent-2">{{ unreadNotifs }}</span>{% endif %} | ||
118 | </a> | ||
119 | </li> | ||
113 | <li id="button_filters"> | 120 | <li id="button_filters"> |
114 | <a class="nav-panel-menu button-collapse-right tooltipped js-filters-action" data-position="bottom" data-delay="50" data-tooltip="{{ 'menu.top.filter_entries'|trans }}" href="#" data-activates="filters"> | 121 | <a class="nav-panel-menu button-collapse-right tooltipped js-filters-action" data-position="bottom" data-delay="50" data-tooltip="{{ 'menu.top.filter_entries'|trans }}" href="#" data-activates="filters"> |
115 | <i class="material-icons">filter_list</i> | 122 | <i class="material-icons">filter_list</i> |
@@ -135,6 +142,53 @@ | |||
135 | </nav> | 142 | </nav> |
136 | {% endblock %} | 143 | {% endblock %} |
137 | 144 | ||
145 | {% block content %} | ||
146 | |||
147 | <div id="notifications" class="side-nav"> | ||
148 | {% if app.user.notifications is not empty %} | ||
149 | <div class="notifications-area"> | ||
150 | <ul class="collection"> | ||
151 | {% for notification in get_notifications() | slice(0, 10) %} | ||
152 | <li class="notification collection-item avatar{% if not notification.read %} light-blue lighten-5{% else %} grey-text{% endif %}"> | ||
153 | <i class="material-icons circle">{% spaceless %} | ||
154 | {% if notification.type == constant('TYPE_ADMIN', notification) %} | ||
155 | build | ||
156 | {% elseif notification.type == constant('TYPE_USER', notification) %} | ||
157 | person | ||
158 | {% elseif notification.type == constant('TYPE_RELEASE', notification) %} | ||
159 | new_releases | ||
160 | {% endif %} | ||
161 | {% endspaceless %}</i> | ||
162 | <span class="title">{{ notification.title | trans(notification.parameters) }}</span> | ||
163 | <p>{{ notification.description | trans(notification.parameters) }}</p> | ||
164 | <time datetime="{{ notification.timestamp | date }}">{{ notification.timestamp | time_diff }}</time> | ||
165 | <div> | ||
166 | {% for action in notification.actions %} | ||
167 | <a class="notification-action-button btn waves-effect waves-light {% spaceless %} | ||
168 | {% if action.type == constant('TYPE_OK', action) %} | ||
169 | {% elseif action.type == constant('TYPE_YES', action) %} | ||
170 | cyan | ||
171 | {% elseif action.type == constant('TYPE_NO', action) %} | ||
172 | red | ||
173 | {% elseif action.type == constant('TYPE_INFO', action) %} | ||
174 | blue-grey | ||
175 | {% endif %} | ||
176 | {% if notification.read %}lighten-3{% endif %} | ||
177 | {% endspaceless %}" href="{{ path('notification-archive-redirect', {'redirection': action.link, 'notification': notification.id}) }}">{{ action.label | trans(notification.parameters) }}</a> | ||
178 | {% endfor %} | ||
179 | </div> | ||
180 | </li> | ||
181 | {% endfor %} | ||
182 | </ul> | ||
183 | </div> | ||
184 | <a href="{{ path('notification-archive-all') }}" class="btn-light waves-effect waves-light right"><i class="material-icons">done_all</i> {{ 'notifications.list.mark_all_as_read' | trans }}</a> | ||
185 | <a class="waves-effect waves-light btn view-more" href="{{ path('notifications-all') }}">{{ 'notifications.sidebar.view_more' | trans }}</a> | ||
186 | {% else %} | ||
187 | <div class="no-notifications grey-text">{{ 'notifications.list.none' | trans }}</div> | ||
188 | {% endif %} | ||
189 | </div> | ||
190 | {% endblock %} | ||
191 | |||
138 | {% block footer %} | 192 | {% block footer %} |
139 | <footer class="page-footer cyan darken-2"> | 193 | <footer class="page-footer cyan darken-2"> |
140 | <div class="footer-copyright"> | 194 | <div class="footer-copyright"> |
diff --git a/src/Wallabag/CoreBundle/Twig/WallabagExtension.php b/src/Wallabag/CoreBundle/Twig/WallabagExtension.php index 351172c4..fa63ba24 100644 --- a/src/Wallabag/CoreBundle/Twig/WallabagExtension.php +++ b/src/Wallabag/CoreBundle/Twig/WallabagExtension.php | |||
@@ -4,7 +4,9 @@ namespace Wallabag\CoreBundle\Twig; | |||
4 | 4 | ||
5 | use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; | 5 | use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; |
6 | use Symfony\Component\Translation\TranslatorInterface; | 6 | use Symfony\Component\Translation\TranslatorInterface; |
7 | use Wallabag\CoreBundle\Notifications\NotificationInterface; | ||
7 | use Wallabag\CoreBundle\Repository\EntryRepository; | 8 | use Wallabag\CoreBundle\Repository\EntryRepository; |
9 | use Wallabag\CoreBundle\Repository\NotificationRepository; | ||
8 | use Wallabag\CoreBundle\Repository\TagRepository; | 10 | use Wallabag\CoreBundle\Repository\TagRepository; |
9 | 11 | ||
10 | class WallabagExtension extends \Twig_Extension implements \Twig_Extension_GlobalsInterface | 12 | class WallabagExtension extends \Twig_Extension implements \Twig_Extension_GlobalsInterface |
@@ -12,15 +14,19 @@ class WallabagExtension extends \Twig_Extension implements \Twig_Extension_Globa | |||
12 | private $tokenStorage; | 14 | private $tokenStorage; |
13 | private $entryRepository; | 15 | private $entryRepository; |
14 | private $tagRepository; | 16 | private $tagRepository; |
17 | private $notificationRepository; | ||
15 | private $lifeTime; | 18 | private $lifeTime; |
19 | private $nbNotifications; | ||
16 | private $translator; | 20 | private $translator; |
17 | 21 | ||
18 | public function __construct(EntryRepository $entryRepository, TagRepository $tagRepository, TokenStorageInterface $tokenStorage, $lifeTime, TranslatorInterface $translator) | 22 | public function __construct(EntryRepository $entryRepository, TagRepository $tagRepository, NotificationRepository $notificationRepository, TokenStorageInterface $tokenStorage, $lifeTime, $nbNotifications, TranslatorInterface $translator) |
19 | { | 23 | { |
20 | $this->entryRepository = $entryRepository; | 24 | $this->entryRepository = $entryRepository; |
21 | $this->tagRepository = $tagRepository; | 25 | $this->tagRepository = $tagRepository; |
26 | $this->notificationRepository = $notificationRepository; | ||
22 | $this->tokenStorage = $tokenStorage; | 27 | $this->tokenStorage = $tokenStorage; |
23 | $this->lifeTime = $lifeTime; | 28 | $this->lifeTime = $lifeTime; |
29 | $this->nbNotifications = $nbNotifications; | ||
24 | $this->translator = $translator; | 30 | $this->translator = $translator; |
25 | } | 31 | } |
26 | 32 | ||
@@ -28,6 +34,7 @@ class WallabagExtension extends \Twig_Extension implements \Twig_Extension_Globa | |||
28 | { | 34 | { |
29 | return [ | 35 | return [ |
30 | new \Twig_SimpleFilter('removeWww', [$this, 'removeWww']), | 36 | new \Twig_SimpleFilter('removeWww', [$this, 'removeWww']), |
37 | new \Twig_SimpleFilter('unread_notif', [$this, 'unreadNotif']), | ||
31 | ]; | 38 | ]; |
32 | } | 39 | } |
33 | 40 | ||
@@ -37,6 +44,7 @@ class WallabagExtension extends \Twig_Extension implements \Twig_Extension_Globa | |||
37 | new \Twig_SimpleFunction('count_entries', [$this, 'countEntries']), | 44 | new \Twig_SimpleFunction('count_entries', [$this, 'countEntries']), |
38 | new \Twig_SimpleFunction('count_tags', [$this, 'countTags']), | 45 | new \Twig_SimpleFunction('count_tags', [$this, 'countTags']), |
39 | new \Twig_SimpleFunction('display_stats', [$this, 'displayStats']), | 46 | new \Twig_SimpleFunction('display_stats', [$this, 'displayStats']), |
47 | new \Twig_SimpleFunction('get_notifications', [$this, 'getNotifications']), | ||
40 | ]; | 48 | ]; |
41 | } | 49 | } |
42 | 50 | ||
@@ -46,6 +54,17 @@ class WallabagExtension extends \Twig_Extension implements \Twig_Extension_Globa | |||
46 | } | 54 | } |
47 | 55 | ||
48 | /** | 56 | /** |
57 | * @param $notifs | ||
58 | * @return array | ||
59 | */ | ||
60 | public function unreadNotif($notifs) | ||
61 | { | ||
62 | return array_filter($notifs, function (NotificationInterface $notif) { | ||
63 | return !$notif->isRead(); | ||
64 | }); | ||
65 | } | ||
66 | |||
67 | /** | ||
49 | * Return number of entries depending of the type (unread, archive, starred or all). | 68 | * Return number of entries depending of the type (unread, archive, starred or all). |
50 | * | 69 | * |
51 | * @param string $type Type of entries to count | 70 | * @param string $type Type of entries to count |
@@ -107,6 +126,21 @@ class WallabagExtension extends \Twig_Extension implements \Twig_Extension_Globa | |||
107 | return $this->tagRepository->countAllTags($user->getId()); | 126 | return $this->tagRepository->countAllTags($user->getId()); |
108 | } | 127 | } |
109 | 128 | ||
129 | public function getNotifications() | ||
130 | { | ||
131 | $user = $this->tokenStorage->getToken() ? $this->tokenStorage->getToken()->getUser() : null; | ||
132 | |||
133 | if (null === $user || !is_object($user)) { | ||
134 | return 0; | ||
135 | } | ||
136 | |||
137 | return $this->notificationRepository->findBy( | ||
138 | ['user' => $user->getId()], | ||
139 | ['timestamp' => 'DESC'], | ||
140 | $this->nbNotifications | ||
141 | ); | ||
142 | } | ||
143 | |||
110 | /** | 144 | /** |
111 | * Display a single line about reading stats. | 145 | * Display a single line about reading stats. |
112 | * | 146 | * |
@@ -143,9 +177,4 @@ class WallabagExtension extends \Twig_Extension implements \Twig_Extension_Globa | |||
143 | '%per_day%' => round($nbArchives / $nbDays, 2), | 177 | '%per_day%' => round($nbArchives / $nbDays, 2), |
144 | ]); | 178 | ]); |
145 | } | 179 | } |
146 | |||
147 | public function getName() | ||
148 | { | ||
149 | return 'wallabag_extension'; | ||
150 | } | ||
151 | } | 180 | } |
diff --git a/src/Wallabag/UserBundle/Entity/User.php b/src/Wallabag/UserBundle/Entity/User.php index 53c327f9..815a1902 100644 --- a/src/Wallabag/UserBundle/Entity/User.php +++ b/src/Wallabag/UserBundle/Entity/User.php | |||
@@ -88,6 +88,11 @@ class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterf | |||
88 | protected $entries; | 88 | protected $entries; |
89 | 89 | ||
90 | /** | 90 | /** |
91 | * @ORM\OneToMany(targetEntity="Wallabag\CoreBundle\Entity\Notification", mappedBy="user", cascade={"remove"}) | ||
92 | */ | ||
93 | protected $notifications; | ||
94 | |||
95 | /** | ||
91 | * @ORM\OneToOne(targetEntity="Wallabag\CoreBundle\Entity\Config", mappedBy="user", cascade={"remove"}) | 96 | * @ORM\OneToOne(targetEntity="Wallabag\CoreBundle\Entity\Config", mappedBy="user", cascade={"remove"}) |
92 | */ | 97 | */ |
93 | protected $config; | 98 | protected $config; |
@@ -319,4 +324,20 @@ class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterf | |||
319 | return $this->clients->first(); | 324 | return $this->clients->first(); |
320 | } | 325 | } |
321 | } | 326 | } |
327 | |||
328 | /** | ||
329 | * @return ArrayCollection<NotificationInterface> | ||
330 | */ | ||
331 | public function getNotifications() | ||
332 | { | ||
333 | return $this->notifications; | ||
334 | } | ||
335 | |||
336 | /** | ||
337 | * @param ArrayCollection<NotificationInterface> $notifications | ||
338 | */ | ||
339 | public function setNotifications($notifications) | ||
340 | { | ||
341 | $this->notifications = $notifications; | ||
342 | } | ||
322 | } | 343 | } |
diff --git a/src/Wallabag/UserBundle/Resources/views/manage.html.twig b/src/Wallabag/UserBundle/Resources/views/manage.html.twig index c614c55f..10d8050c 100644 --- a/src/Wallabag/UserBundle/Resources/views/manage.html.twig +++ b/src/Wallabag/UserBundle/Resources/views/manage.html.twig | |||
@@ -3,7 +3,7 @@ | |||
3 | {% block title %}{{ 'user.manage.page_title'|trans }}{% endblock %} | 3 | {% block title %}{{ 'user.manage.page_title'|trans }}{% endblock %} |
4 | 4 | ||
5 | {% block content %} | 5 | {% block content %} |
6 | 6 | {{ parent() }} | |
7 | <div class="row"> | 7 | <div class="row"> |
8 | <div class="col s12"> | 8 | <div class="col s12"> |
9 | <div class="card-panel"> | 9 | <div class="card-panel"> |