aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorThomas Citharel <tcit@tcit.fr>2017-01-31 21:13:33 +0100
committerThomas Citharel <tcit@tcit.fr>2017-06-23 09:26:41 +0200
commit2041810adbc6e663e4520337805c0003c77762e2 (patch)
tree1715a1e57a0275efe0970807c65f424def897034
parentb115623e2c536bcfd59760347f22b4d70e1d0661 (diff)
downloadwallabag-2041810adbc6e663e4520337805c0003c77762e2.tar.gz
wallabag-2041810adbc6e663e4520337805c0003c77762e2.tar.zst
wallabag-2041810adbc6e663e4520337805c0003c77762e2.zip
WIP
-rw-r--r--src/Wallabag/CoreBundle/Entity/Entry.php6
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml22
-rw-r--r--src/Wallabag/GroupBundle/Controller/ManageController.php90
-rw-r--r--src/Wallabag/GroupBundle/Entity/Group.php172
-rw-r--r--src/Wallabag/GroupBundle/Entity/Invitation.php73
-rw-r--r--src/Wallabag/GroupBundle/Entity/UserGroup.php126
-rw-r--r--src/Wallabag/GroupBundle/Form/GroupType.php28
-rw-r--r--src/Wallabag/GroupBundle/Form/NewGroupType.php28
-rw-r--r--src/Wallabag/GroupBundle/Form/UserGroupType.php46
-rw-r--r--src/Wallabag/GroupBundle/Repository/GroupRepository.php33
-rw-r--r--src/Wallabag/GroupBundle/Resources/config/services.yml3
-rw-r--r--src/Wallabag/GroupBundle/Resources/views/Manage/edit.html.twig28
-rw-r--r--src/Wallabag/GroupBundle/Resources/views/Manage/index.html.twig3
-rw-r--r--src/Wallabag/GroupBundle/Resources/views/Manage/new.html.twig24
-rw-r--r--src/Wallabag/GroupBundle/Service/Sha256Salted.php18
-rw-r--r--src/Wallabag/UserBundle/Entity/User.php73
16 files changed, 737 insertions, 36 deletions
diff --git a/src/Wallabag/CoreBundle/Entity/Entry.php b/src/Wallabag/CoreBundle/Entity/Entry.php
index a0503c39..84555b36 100644
--- a/src/Wallabag/CoreBundle/Entity/Entry.php
+++ b/src/Wallabag/CoreBundle/Entity/Entry.php
@@ -233,6 +233,12 @@ class Entry
233 */ 233 */
234 private $tags; 234 private $tags;
235 235
236 /**
237 * @var ArrayCollection
238 * @ORM\ManyToMany(targetEntity="Wallabag\GroupBundle\Entity\Group", inversedBy="presentations", cascade={"persist"})
239 */
240 private $groupShares;
241
236 /* 242 /*
237 * @param User $user 243 * @param User $user
238 */ 244 */
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml
index beb8f324..d909cf15 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml
@@ -515,8 +515,8 @@ developer:
515 515
516group: 516group:
517 page_title: Gestion des groupes 517 page_title: Gestion des groupes
518 new_group: Créer un nouveau group 518 new_group: Créer un nouveau groupe
519 edit_group: Éditer un nouveau groupe 519 edit_group: Éditer le groupe « %group% »
520 description: Ici vous pouvez gérer vos groupes (création, mise à jour et suppression) 520 description: Ici vous pouvez gérer vos groupes (création, mise à jour et suppression)
521 list: 521 list:
522 actions: Actions 522 actions: Actions
@@ -531,6 +531,24 @@ group:
531 delete: Supprimer 531 delete: Supprimer
532 delete_confirm: Êtes-vous sur ? 532 delete_confirm: Êtes-vous sur ?
533 back_to_list: Revenir à la liste 533 back_to_list: Revenir à la liste
534 role_label: Rôles par défaut
535 access_label: Accès
536 password_label: Mot de passe (si nécessaire)
537 roles:
538 readonly: Lecture seule
539 write: Écriture
540 manage_entries: Gestion des articles
541 manage_users: Gestion des utilisateurs
542 admin: Administrateur
543 access:
544 open: Ouvert
545 request: Sur demande
546 password: Par mot de passe
547 invitation: Sur invitation
548 hidden: Sur invitation et privée
549 tab_menu:
550 public: Groupes publics
551 own: Mes groupes
534 552
535user: 553user:
536 page_title: "Gestion des utilisateurs" 554 page_title: "Gestion des utilisateurs"
diff --git a/src/Wallabag/GroupBundle/Controller/ManageController.php b/src/Wallabag/GroupBundle/Controller/ManageController.php
index 7015a465..94196040 100644
--- a/src/Wallabag/GroupBundle/Controller/ManageController.php
+++ b/src/Wallabag/GroupBundle/Controller/ManageController.php
@@ -2,11 +2,20 @@
2 2
3namespace Wallabag\GroupBundle\Controller; 3namespace Wallabag\GroupBundle\Controller;
4 4
5use Pagerfanta\Adapter\DoctrineORMAdapter;
6use Pagerfanta\Exception\OutOfRangeCurrentPageException;
7use Pagerfanta\Pagerfanta;
8use Strut\StrutBundle\Service\Sha256Salted;
5use Symfony\Component\HttpFoundation\Request; 9use Symfony\Component\HttpFoundation\Request;
6use Symfony\Bundle\FrameworkBundle\Controller\Controller; 10use Symfony\Bundle\FrameworkBundle\Controller\Controller;
7use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method; 11use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
8use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; 12use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
13use Symfony\Component\HttpFoundation\Response;
9use Wallabag\GroupBundle\Entity\Group; 14use Wallabag\GroupBundle\Entity\Group;
15use Wallabag\GroupBundle\Entity\UserGroup;
16use Wallabag\GroupBundle\Form\GroupType;
17use Wallabag\GroupBundle\Form\NewGroupType;
18use Wallabag\UserBundle\Entity\User;
10 19
11/** 20/**
12 * Group controller. 21 * Group controller.
@@ -14,19 +23,32 @@ use Wallabag\GroupBundle\Entity\Group;
14class ManageController extends Controller 23class ManageController extends Controller
15{ 24{
16 /** 25 /**
17 * Lists all Group entities. 26 * Lists all public Group entities.
18 * 27 *
19 * @Route("/", name="group_index") 28 * @Route("/{page}", name="group_index", defaults={"page" = "1"})
20 * @Method("GET") 29 * @Method("GET")
21 */ 30 */
22 public function indexAction() 31 public function indexAction($page = 1)
23 { 32 {
24 $em = $this->getDoctrine()->getManager(); 33 $em = $this->getDoctrine()->getManager();
25 34
26 $groups = $em->getRepository('WallabagGroupBundle:Group')->findAll(); 35 $groups = $em->getRepository('WallabagGroupBundle:Group')->findPublicGroups();
36
37 $pagerAdapter = new DoctrineORMAdapter($groups->getQuery(), true, false);
38 $pagerFanta = new Pagerfanta($pagerAdapter);
39 $pagerFanta->setMaxPerPage(1);
40
41 try {
42 $pagerFanta->setCurrentPage($page);
43 } catch (OutOfRangeCurrentPageException $e) {
44 if ($page > 1) {
45 return $this->redirect($this->generateUrl('group_index', ['page' => $pagerFanta->getNbPages()]), 302);
46 }
47 }
27 48
28 return $this->render('WallabagGroupBundle:Manage:index.html.twig', array( 49 return $this->render('WallabagGroupBundle:Manage:index.html.twig', array(
29 'groups' => $groups, 50 'groups' => $pagerFanta,
51 'currentPage' => $page,
30 )); 52 ));
31 } 53 }
32 54
@@ -38,14 +60,26 @@ class ManageController extends Controller
38 */ 60 */
39 public function newAction(Request $request) 61 public function newAction(Request $request)
40 { 62 {
41 $group = new Group(''); 63 $group = new Group();
42 64
43 $form = $this->createForm('Wallabag\GroupBundle\Form\NewGroupType', $group); 65 $form = $this->createForm(NewGroupType::class, $group);
44 $form->handleRequest($request); 66 $form->handleRequest($request);
45 67
46 if ($form->isSubmitted() && $form->isValid()) { 68 if ($form->isSubmitted() && $form->isValid()) {
47 $em = $this->getDoctrine()->getManager(); 69 $em = $this->getDoctrine()->getManager();
70
71 if ($group->getAcceptSystem() == Group::ACCESS_PASSWORD) {
72 /** @var Sha256Salted $encoder */
73 $encoder = $this->get('sha256salted_encoder');
74 $password = $encoder->encodePassword($group->getPassword(), $this->getParameter('secret'));
75 $group->setPassword($password);
76 }
77
48 $em->persist($group); 78 $em->persist($group);
79
80 $groupUser = new UserGroup($this->getUser(), $group, Group::ROLE_ADMIN);
81 $groupUser->setAccepted(true);
82 $em->persist($groupUser);
49 $em->flush(); 83 $em->flush();
50 84
51 $this->get('session')->getFlashBag()->add( 85 $this->get('session')->getFlashBag()->add(
@@ -70,12 +104,23 @@ class ManageController extends Controller
70 */ 104 */
71 public function editAction(Request $request, Group $group) 105 public function editAction(Request $request, Group $group)
72 { 106 {
107 if ($this->getUser()->getGroupRoleForUser($group) < Group::ROLE_ADMIN) {
108 $this->createAccessDeniedException();
109 }
110
73 $deleteForm = $this->createDeleteForm($group); 111 $deleteForm = $this->createDeleteForm($group);
74 $editForm = $this->createForm('Wallabag\GroupBundle\Form\GroupType', $group); 112 $editForm = $this->createForm(GroupType::class, $group);
75 $editForm->handleRequest($request); 113 $editForm->handleRequest($request);
76 114
77 if ($editForm->isSubmitted() && $editForm->isValid()) { 115 if ($editForm->isSubmitted() && $editForm->isValid()) {
78 $em = $this->getDoctrine()->getManager(); 116 $em = $this->getDoctrine()->getManager();
117
118 if ($group->getAcceptSystem() === Group::ACCESS_PASSWORD) {
119 $encoder = $this->get('sha256salted_encoder');
120 $password = $encoder->encodePassword($group->getPlainPassword(), $this->getParameter('secret'));
121 $group->setPassword($password);
122 }
123
79 $em->persist($group); 124 $em->persist($group);
80 $em->flush(); 125 $em->flush();
81 126
@@ -134,4 +179,33 @@ class ManageController extends Controller
134 ->getForm() 179 ->getForm()
135 ; 180 ;
136 } 181 }
182
183 /**
184 * @Route("/group-user-exclude/{group}/{user}", name="group-user-exclude")
185 * @param Group $group
186 * @param User $user
187 * @return Response
188 */
189 public function excludeMemberAction(Group $group, User $user)
190 {
191 $logger = $this->get('logger');
192 $logger->info('User ' . $this->getUser()->getUsername() . ' wants to exclude user ' . $user->getUsername() . ' from group ' . $group->getName());
193
194 if (!$this->getUser()->inGroup($group) || $this->getUser()->getGroupRoleForUser($group) < Group::ROLE_MANAGE_USERS) {
195 $logger->info('User ' . $this->getUser()->getUsername() . ' has not enough rights on group ' . $group->getName() . ' to exclude user ' . $user->getUsername());
196 throw $this->createAccessDeniedException();
197 }
198
199 if ($user->inGroup($group) && $user->getGroupRoleForUser($group) < Group::ROLE_ADMIN) {
200 $em = $this->getDoctrine()->getManager();
201
202 $logger->info('Removing user ' . $this->getUser()->getUsername() . ' from group ' . $group->getName());
203 $em->remove($this->getUser()->getUserGroupFromGroup($group));
204
205 $em->flush();
206
207 return $this->redirectToRoute('group-manage', ['group' => $group->getId()]);
208 }
209 throw $this->createAccessDeniedException();
210 }
137} 211}
diff --git a/src/Wallabag/GroupBundle/Entity/Group.php b/src/Wallabag/GroupBundle/Entity/Group.php
index 1381d1ea..31c82837 100644
--- a/src/Wallabag/GroupBundle/Entity/Group.php
+++ b/src/Wallabag/GroupBundle/Entity/Group.php
@@ -8,11 +8,50 @@ use Doctrine\ORM\Mapping as ORM;
8use Wallabag\UserBundle\Entity\User; 8use Wallabag\UserBundle\Entity\User;
9 9
10/** 10/**
11 * @ORM\Entity 11 * @ORM\Entity(repositoryClass="Wallabag\GroupBundle\Repository\GroupRepository")
12 * @ORM\Table(name="`group`") 12 * @ORM\Table(name="`group`")
13 */ 13 */
14class Group extends BaseGroup 14class Group extends BaseGroup
15{ 15{
16
17 /**
18 * User Roles
19 */
20
21 /** User can only preview presentations */
22 const ROLE_READ_ONLY = 1;
23
24 /** User can create new presentations */
25 const ROLE_WRITE = 2;
26
27 /** User can manage all group presentations */
28 const ROLE_MANAGE_ENTRIES = 3;
29
30 /** User can manage users in the group */
31 const ROLE_MANAGE_USERS = 5;
32
33 /** User can rename and delete the group */
34 const ROLE_ADMIN = 10;
35
36 /**
37 * Group join access
38 */
39
40 /** Any user can join the group */
41 const ACCESS_OPEN = 1;
42
43 /** An user needs to request to join the group */
44 const ACCESS_REQUEST = 2;
45
46 /** An user need the password to access the group */
47 const ACCESS_PASSWORD = 3;
48
49 /** An user needs to be invited to join the group */
50 const ACCESS_INVITATION_ONLY = 4;
51
52 /** An user needs to be invited to join the group, and the group is not publicly listed */
53 const ACCESS_HIDDEN = 10;
54
16 /** 55 /**
17 * @ORM\Id 56 * @ORM\Id
18 * @ORM\Column(type="integer") 57 * @ORM\Column(type="integer")
@@ -21,38 +60,141 @@ class Group extends BaseGroup
21 protected $id; 60 protected $id;
22 61
23 /** 62 /**
24 * @ORM\ManyToMany(targetEntity="Wallabag\UserBundle\Entity\User", mappedBy="groups", cascade={"persist"}) 63 * @ORM\Column(type="integer", options={"default" : 1})
64 */
65 protected $acceptSystem;
66
67 /**
68 * @ORM\Column(type="integer", options={"default" : 2})
69 */
70 protected $defaultRole;
71
72 /**
73 * @ORM\Column(type="string", nullable=true)
74 */
75 protected $password;
76 protected $plainPassword;
77
78 /**
79 * @ORM\ManyToMany(targetEntity="Wallabag\CoreBundle\Entity\Entry", mappedBy="groupShares", cascade={"persist"})
80 */
81 protected $entries;
82
83 /**
84 * @ORM\OneToMany(targetEntity="UserGroup", mappedBy="group", cascade={"persist"})
25 */ 85 */
26 protected $users; 86 protected $users;
27 87
28 public function getUsers() 88 public function __construct($name = '', array $roles = [])
29 { 89 {
30 return $this->users ?: $this->users = new ArrayCollection(); 90 parent::__construct($name, $roles);
91 $this->defaultRole = self::ROLE_READ_ONLY;
92 $this->acceptSystem = self::ACCESS_REQUEST;
31 } 93 }
32 94
33 public function addUser(User $user) 95 /**
96 * @return ArrayCollection
97 */
98 public function getUsers()
34 { 99 {
35 if (!$this->getUsers()->contains($user)) { 100 $userObj = new ArrayCollection();
36 $this->getUsers()->add($user); 101 foreach ($this->users as $userGroup) {
102 /** @var UserGroup $userGroup */
103 $userObj->add($userGroup->getUser());
37 } 104 }
105 return $userObj;
106 }
38 107
39 return $this; 108 /**
109 * @return int
110 */
111 public function getDefaultRole()
112 {
113 return $this->defaultRole;
40 } 114 }
41 115
42 /** 116 /**
43 * {@inheritdoc} 117 * @return int
44 */ 118 */
45 public function removeUser(User $user) 119 public function getAcceptSystem()
46 { 120 {
47 if ($this->getUsers()->contains($user)) { 121 return $this->acceptSystem;
48 $this->getUsers()->removeElement($user); 122 }
49 } 123
124 /**
125 * @param int $acceptSystem
126 */
127 public function setAcceptSystem($acceptSystem)
128 {
129 $this->acceptSystem = $acceptSystem;
130 }
50 131
51 return $this; 132 /**
133 * @return string
134 */
135 public function getPassword()
136 {
137 return $this->password ?: '';
138 }
139
140 /**
141 * @param string $password
142 */
143 public function setPassword($password)
144 {
145 $this->password = $password;
146 }
147
148 /**
149 * @return string
150 */
151 public function getPlainPassword()
152 {
153 return $this->plainPassword ?: '';
154 }
155
156 /**
157 * @param string $plainPassword
158 */
159 public function setPlainPassword($plainPassword)
160 {
161 $this->plainPassword = $plainPassword;
162 }
163
164 /**
165 * @param int $defaultRole
166 */
167 public function setDefaultRole($defaultRole)
168 {
169 $this->defaultRole = $defaultRole;
52 } 170 }
53 171
54 public function __toString() 172 public function __toString()
55 { 173 {
56 return $this->getName(); 174 return $this->name;
175 }
176
177 public function getRequests()
178 {
179 $requests = new ArrayCollection();
180 foreach ($this->users as $user) /** @var UserGroup $user */
181 {
182 if (!$user->isAccepted()) {
183 $requests->add($user->getUser());
184 }
185 }
186 return $requests;
187 }
188
189 public function getInvited()
190 {
191 $invited = new ArrayCollection();
192 foreach ($this->users as $userGroup) /** @var UserGroup $userGroup */
193 {
194 if ($userGroup->getInvitation()) {
195 $invited->add($userGroup);
196 }
197 }
198 return $invited;
57 } 199 }
58} 200}
diff --git a/src/Wallabag/GroupBundle/Entity/Invitation.php b/src/Wallabag/GroupBundle/Entity/Invitation.php
new file mode 100644
index 00000000..6946bede
--- /dev/null
+++ b/src/Wallabag/GroupBundle/Entity/Invitation.php
@@ -0,0 +1,73 @@
1<?php
2
3namespace Wallabag\GroupBundle\Entity;
4
5use Doctrine\ORM\Mapping as ORM;
6
7/**
8 * @ORM\Entity
9 */
10class Invitation
11{
12 /**
13 * @ORM\Id
14 * @ORM\Column(type="string", length=6)
15 */
16 protected $code;
17
18 /**
19 * @var \DateTime
20 * @ORM\Column(type="datetime")
21 */
22 protected $date;
23
24 /**
25 * @ORM\OneToOne(targetEntity="UserGroup", mappedBy="invitation")
26 */
27 protected $userGroup;
28
29 public function __construct(UserGroup $userGroup)
30 {
31 // generate identifier only once, here a 6 characters length code
32 $this->code = substr(md5(uniqid(rand(), true)), 0, 6);
33 $this->date = new \DateTime();
34 $this->userGroup = $userGroup;
35 }
36
37 public function getCode()
38 {
39 return $this->code;
40 }
41
42 /**
43 * @return \DateTime
44 */
45 public function getDate(): \DateTime
46 {
47 return $this->date;
48 }
49
50 /**
51 * @param \DateTime $date
52 */
53 public function setDate(\DateTime $date)
54 {
55 $this->date = $date;
56 }
57
58 /**
59 * @return mixed
60 */
61 public function getUserGroup(): UserGroup
62 {
63 return $this->userGroup;
64 }
65
66 /**
67 * @param mixed $userGroup
68 */
69 public function setUserGroup(UserGroup $userGroup)
70 {
71 $this->userGroup = $userGroup;
72 }
73}
diff --git a/src/Wallabag/GroupBundle/Entity/UserGroup.php b/src/Wallabag/GroupBundle/Entity/UserGroup.php
new file mode 100644
index 00000000..22d1400a
--- /dev/null
+++ b/src/Wallabag/GroupBundle/Entity/UserGroup.php
@@ -0,0 +1,126 @@
1<?php
2
3namespace Wallabag\GroupBundle\Entity;
4
5use Doctrine\ORM\Mapping as ORM;
6use FOS\UserBundle\Model\GroupInterface;
7use Symfony\Component\Validator\Constraints as Assert;
8use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
9use Wallabag\UserBundle\Entity\User;
10
11/**
12 * @ORM\Entity(repositoryClass="Wallabag\GroupBundle\Repository\UserGroupRepository")
13 * @UniqueEntity({"user_id", "group_id"})
14 * @ORM\Table(name="fos_user_group")
15 */
16class UserGroup
17{
18 /**
19 * @ORM\Column(name="id", type="integer")
20 * @ORM\Id
21 * @ORM\GeneratedValue(strategy="AUTO")
22 */
23 private $id;
24
25 /**
26 * @ORM\Column(name="role", type="integer")
27 */
28 private $role;
29
30 /**
31 * @ORM\ManyToOne(targetEntity="Wallabag\UserBundle\Entity\User", inversedBy="userGroups")
32 * @ORM\JoinColumn(name="user_id", referencedColumnName="id")
33 */
34 private $user;
35
36 /**
37 * @ORM\ManyToOne(targetEntity="Wallabag\GroupBundle\Entity\Group", inversedBy="users")
38 * @ORM\JoinColumn(name="group_id", referencedColumnName="id")
39 */
40 private $group;
41
42 /**
43 * @ORM\Column(name="accepted", type="boolean", options={"default" : false})
44 */
45 private $accepted;
46
47 /**
48 * @ORM\OneToOne(targetEntity="Wallabag\GroupBundle\Entity\Invitation", inversedBy="userGroup", cascade={"persist", "remove"})
49 * @ORM\JoinColumn(name="invitation", referencedColumnName="code")
50 */
51 protected $invitation;
52
53 /**
54 * UserGroup constructor.
55 * @param User $user
56 * @param Group $group
57 * @param $role
58 */
59 public function __construct(User $user, Group $group, $role, $request = false)
60 {
61 $this->user = $user;
62 $this->group = $group;
63 $this->role = $role;
64 $this->accepted = $request;
65 }
66
67 /**
68 * @return Group
69 */
70 public function getGroup()
71 {
72 return $this->group;
73 }
74
75 /**
76 * @return int
77 */
78 public function getRole()
79 {
80 return $this->role;
81 }
82
83 /**
84 * @return User
85 */
86 public function getUser()
87 {
88 return $this->user;
89 }
90
91 /**
92 * @param int $role
93 * @return UserGroup
94 */
95 public function setRole($role)
96 {
97 $this->role = $role;
98 return $this;
99 }
100
101 /**
102 * @param bool $accepted
103 */
104 public function setAccepted($accepted)
105 {
106 $this->accepted = $accepted;
107 }
108
109 /**
110 * @return bool
111 */
112 public function isAccepted()
113 {
114 return $this->accepted;
115 }
116
117 public function setInvitation($invitation)
118 {
119 $this->invitation = $invitation;
120 }
121
122 public function getInvitation()
123 {
124 return $this->invitation;
125 }
126}
diff --git a/src/Wallabag/GroupBundle/Form/GroupType.php b/src/Wallabag/GroupBundle/Form/GroupType.php
index c2ad764b..c708bdb8 100644
--- a/src/Wallabag/GroupBundle/Form/GroupType.php
+++ b/src/Wallabag/GroupBundle/Form/GroupType.php
@@ -3,10 +3,13 @@
3namespace Wallabag\GroupBundle\Form; 3namespace Wallabag\GroupBundle\Form;
4 4
5use Symfony\Component\Form\AbstractType; 5use Symfony\Component\Form\AbstractType;
6use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
7use Symfony\Component\Form\Extension\Core\Type\PasswordType;
6use Symfony\Component\Form\FormBuilderInterface; 8use Symfony\Component\Form\FormBuilderInterface;
7use Symfony\Component\OptionsResolver\OptionsResolver; 9use Symfony\Component\OptionsResolver\OptionsResolver;
8use Symfony\Component\Form\Extension\Core\Type\SubmitType; 10use Symfony\Component\Form\Extension\Core\Type\SubmitType;
9use Symfony\Component\Form\Extension\Core\Type\TextType; 11use Symfony\Component\Form\Extension\Core\Type\TextType;
12use Wallabag\GroupBundle\Entity\Group;
10 13
11class GroupType extends AbstractType 14class GroupType extends AbstractType
12{ 15{
@@ -21,6 +24,31 @@ class GroupType extends AbstractType
21 'required' => false, 24 'required' => false,
22 'label' => 'group.form.name_label', 25 'label' => 'group.form.name_label',
23 ]) 26 ])
27 ->add('defaultRole', ChoiceType::class, [
28 'label' => 'group.form.role_label',
29 'choices' => [
30 'group.roles.readonly' => Group::ROLE_READ_ONLY,
31 'group.roles.write' => Group::ROLE_WRITE,
32 'group.roles.manage_entries' => Group::ROLE_MANAGE_ENTRIES,
33 'group.roles.manage_users' => Group::ROLE_MANAGE_USERS,
34 'group.roles.admin' => Group::ROLE_ADMIN,
35 ],
36 ])
37 ->add('acceptSystem', ChoiceType::class, [
38 'label' => 'group.form.access_label',
39 'choices' => [
40 'group.access.open' => Group::ACCESS_OPEN,
41 'group.access.request' => Group::ACCESS_REQUEST,
42 'group.access.password' => Group::ACCESS_PASSWORD,
43 'group.access.invitation' => Group::ACCESS_INVITATION_ONLY,
44 'group.access.hidden' => Group::ACCESS_HIDDEN,
45 ],
46 ])
47 ->add('plainPassword', PasswordType::class, [
48 'label' => 'group.form.password_label',
49 'required' => false,
50 'attr' => ['autocomplete' => 'off'],
51 ])
24 ->add('save', SubmitType::class, [ 52 ->add('save', SubmitType::class, [
25 'label' => 'group.form.save', 53 'label' => 'group.form.save',
26 ]) 54 ])
diff --git a/src/Wallabag/GroupBundle/Form/NewGroupType.php b/src/Wallabag/GroupBundle/Form/NewGroupType.php
index 29b20fe0..55eebc46 100644
--- a/src/Wallabag/GroupBundle/Form/NewGroupType.php
+++ b/src/Wallabag/GroupBundle/Form/NewGroupType.php
@@ -3,10 +3,13 @@
3namespace Wallabag\GroupBundle\Form; 3namespace Wallabag\GroupBundle\Form;
4 4
5use Symfony\Component\Form\AbstractType; 5use Symfony\Component\Form\AbstractType;
6use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
7use Symfony\Component\Form\Extension\Core\Type\PasswordType;
6use Symfony\Component\Form\Extension\Core\Type\SubmitType; 8use Symfony\Component\Form\Extension\Core\Type\SubmitType;
7use Symfony\Component\Form\Extension\Core\Type\TextType; 9use Symfony\Component\Form\Extension\Core\Type\TextType;
8use Symfony\Component\Form\FormBuilderInterface; 10use Symfony\Component\Form\FormBuilderInterface;
9use Symfony\Component\OptionsResolver\OptionsResolver; 11use Symfony\Component\OptionsResolver\OptionsResolver;
12use Wallabag\GroupBundle\Entity\Group;
10 13
11class NewGroupType extends AbstractType 14class NewGroupType extends AbstractType
12{ 15{
@@ -17,6 +20,31 @@ class NewGroupType extends AbstractType
17 'required' => true, 20 'required' => true,
18 'label' => 'group.form.name_label', 21 'label' => 'group.form.name_label',
19 ]) 22 ])
23 ->add('defaultRole', ChoiceType::class, [
24 'label' => 'group.form.role_label',
25 'choices' => [
26 'group.roles.readonly' => Group::ROLE_READ_ONLY,
27 'group.roles.write' => Group::ROLE_WRITE,
28 'group.roles.manage_entries' => Group::ROLE_MANAGE_ENTRIES,
29 'group.roles.manage_users' => Group::ROLE_MANAGE_USERS,
30 'group.roles.admin' => Group::ROLE_ADMIN,
31 ],
32 ])
33 ->add('acceptSystem', ChoiceType::class, [
34 'label' => 'group.form.access_label',
35 'choices' => [
36 'group.access.open' => Group::ACCESS_OPEN,
37 'group.access.request' => Group::ACCESS_REQUEST,
38 'group.access.password' => Group::ACCESS_PASSWORD,
39 'group.access.invitation' => Group::ACCESS_INVITATION_ONLY,
40 'group.access.hidden' => Group::ACCESS_HIDDEN,
41 ],
42 ])
43 ->add('plainPassword', PasswordType::class, [
44 'label' => 'group.form.password_label',
45 'required' => false,
46 'attr' => ['autocomplete' => 'off'],
47 ])
20 ->add('save', SubmitType::class, [ 48 ->add('save', SubmitType::class, [
21 'label' => 'group.form.save', 49 'label' => 'group.form.save',
22 ]) 50 ])
diff --git a/src/Wallabag/GroupBundle/Form/UserGroupType.php b/src/Wallabag/GroupBundle/Form/UserGroupType.php
new file mode 100644
index 00000000..e8f814e4
--- /dev/null
+++ b/src/Wallabag/GroupBundle/Form/UserGroupType.php
@@ -0,0 +1,46 @@
1<?php
2
3namespace Wallabag\GroupBundle\Form\Type;
4
5use Wallabag\GroupBundle\Entity\Group;
6use Symfony\Component\Form\AbstractType;
7use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
8use Symfony\Component\Form\FormBuilderInterface;
9use Symfony\Component\OptionsResolver\OptionsResolver;
10use Symfony\Component\Form\Extension\Core\Type\SubmitType;
11
12class UserGroupType extends AbstractType
13{
14 /**
15 * @param FormBuilderInterface $builder
16 * @param array $options
17 */
18 public function buildForm(FormBuilderInterface $builder, array $options)
19 {
20 $builder
21 ->add('role', ChoiceType::class, [
22 'label' => 'group.edit_user.role',
23 'choices' => [
24 'group.roles.readonly' => Group::ROLE_READ_ONLY,
25 'group.roles.write' => Group::ROLE_WRITE,
26 'group.roles.manage_prez' => Group::ROLE_MANAGE_PREZ,
27 'group.roles.manage_users' => Group::ROLE_MANAGE_USERS,
28 'group.roles.admin' => Group::ROLE_ADMIN,
29 ],
30 ])
31 ->add('save', SubmitType::class, [
32 'label' => 'user.form.save',
33 ])
34 ;
35 }
36
37 /**
38 * @param OptionsResolver $resolver
39 */
40 public function configureOptions(OptionsResolver $resolver)
41 {
42 $resolver->setDefaults(array(
43 'data_class' => 'Strut\StrutBundle\Entity\UserGroup',
44 ));
45 }
46}
diff --git a/src/Wallabag/GroupBundle/Repository/GroupRepository.php b/src/Wallabag/GroupBundle/Repository/GroupRepository.php
new file mode 100644
index 00000000..47340d07
--- /dev/null
+++ b/src/Wallabag/GroupBundle/Repository/GroupRepository.php
@@ -0,0 +1,33 @@
1<?php
2
3namespace Wallabag\GroupBundle\Repository;
4
5use Doctrine\ORM\EntityRepository;
6use Doctrine\ORM\QueryBuilder;
7use Wallabag\UserBundle\Entity\User;
8
9class GroupRepository extends EntityRepository
10{
11 /**
12 * Return a query builder to used by other getBuilderFor* method.
13 *
14 * @return QueryBuilder
15 */
16 public function getBuilder()
17 {
18 return $this->createQueryBuilder('g');
19 }
20
21 public function findPublicGroups()
22 {
23 return $this->getBuilder()
24 ->where('g.acceptSystem < 10');
25 }
26
27 public function findGroupsByUser(User $user)
28 {
29 return $this->getBuilder()
30 ->join('Wallabag\GroupBundle\Entity\UserGroup', 'u', 'WITH', 'u.group = g.id')
31 ->where('u.user = :user')->setParameter(':user', $user->getId());
32 }
33}
diff --git a/src/Wallabag/GroupBundle/Resources/config/services.yml b/src/Wallabag/GroupBundle/Resources/config/services.yml
new file mode 100644
index 00000000..76e8427a
--- /dev/null
+++ b/src/Wallabag/GroupBundle/Resources/config/services.yml
@@ -0,0 +1,3 @@
1services:
2 sha256salted_encoder:
3 class: Strut\StrutBundle\Service\Sha256Salted \ No newline at end of file
diff --git a/src/Wallabag/GroupBundle/Resources/views/Manage/edit.html.twig b/src/Wallabag/GroupBundle/Resources/views/Manage/edit.html.twig
index 7de68c35..c12e8625 100644
--- a/src/Wallabag/GroupBundle/Resources/views/Manage/edit.html.twig
+++ b/src/Wallabag/GroupBundle/Resources/views/Manage/edit.html.twig
@@ -1,6 +1,6 @@
1{% extends "WallabagCoreBundle::layout.html.twig" %} 1{% extends "WallabagCoreBundle::layout.html.twig" %}
2 2
3{% block title %}{{ 'group.page_title'|trans }}{% endblock %} 3{% block title %}{{ 'group.page_title' | trans }}{% endblock %}
4 4
5{% block content %} 5{% block content %}
6 6
@@ -9,7 +9,7 @@
9 <div class="card-panel"> 9 <div class="card-panel">
10 <div class="row"> 10 <div class="row">
11 <div class="input-field col s12"> 11 <div class="input-field col s12">
12 <h4>{{ 'group.edit_group'|trans }}</h4> 12 <h4>{{ 'group.edit_group'|trans({'%group%': group.name }) }}</h4>
13 13
14 <div id="set6" class="col s12"> 14 <div id="set6" class="col s12">
15 {{ form_start(edit_form) }} 15 {{ form_start(edit_form) }}
@@ -23,6 +23,30 @@
23 </div> 23 </div>
24 </div> 24 </div>
25 25
26 <div class="row">
27 <div class="input-field col s12">
28 {{ form_label(edit_form.defaultRole) }}
29 {{ form_errors(edit_form.defaultRole) }}
30 {{ form_widget(edit_form.defaultRole) }}
31 </div>
32 </div>
33
34 <div class="row">
35 <div class="input-field col s12">
36 {{ form_label(edit_form.acceptSystem) }}
37 {{ form_errors(edit_form.acceptSystem) }}
38 {{ form_widget(edit_form.acceptSystem) }}
39 </div>
40 </div>
41
42 <div class="row">
43 <div class="input-field col s12">
44 {{ form_label(edit_form.plainPassword) }}
45 {{ form_errors(edit_form.plainPassword) }}
46 {{ form_widget(edit_form.plainPassword) }}
47 </div>
48 </div>
49
26 <br/> 50 <br/>
27 51
28 {{ form_widget(edit_form.save, {'attr': {'class': 'btn waves-effect waves-light'}}) }} 52 {{ form_widget(edit_form.save, {'attr': {'class': 'btn waves-effect waves-light'}}) }}
diff --git a/src/Wallabag/GroupBundle/Resources/views/Manage/index.html.twig b/src/Wallabag/GroupBundle/Resources/views/Manage/index.html.twig
index 58af3a75..01466e52 100644
--- a/src/Wallabag/GroupBundle/Resources/views/Manage/index.html.twig
+++ b/src/Wallabag/GroupBundle/Resources/views/Manage/index.html.twig
@@ -37,6 +37,9 @@
37 <p> 37 <p>
38 <a href="{{ path('group_new') }}" class="waves-effect waves-light btn">{{ 'group.list.create_new_one'|trans }}</a> 38 <a href="{{ path('group_new') }}" class="waves-effect waves-light btn">{{ 'group.list.create_new_one'|trans }}</a>
39 </p> 39 </p>
40 {% if groups.getNbPages > 1 %}
41 {{ pagerfanta(groups, 'twitter_bootstrap_translated', {'proximity': 1}) }}
42 {% endif %}
40 </div> 43 </div>
41 </div> 44 </div>
42 </div> 45 </div>
diff --git a/src/Wallabag/GroupBundle/Resources/views/Manage/new.html.twig b/src/Wallabag/GroupBundle/Resources/views/Manage/new.html.twig
index 3f1c2ad5..cb009b5d 100644
--- a/src/Wallabag/GroupBundle/Resources/views/Manage/new.html.twig
+++ b/src/Wallabag/GroupBundle/Resources/views/Manage/new.html.twig
@@ -23,6 +23,30 @@
23 </div> 23 </div>
24 </div> 24 </div>
25 25
26 <div class="row">
27 <div class="input-field col s12">
28 {{ form_label(form.defaultRole) }}
29 {{ form_errors(form.defaultRole) }}
30 {{ form_widget(form.defaultRole) }}
31 </div>
32 </div>
33
34 <div class="row">
35 <div class="input-field col s12">
36 {{ form_label(form.acceptSystem) }}
37 {{ form_errors(form.acceptSystem) }}
38 {{ form_widget(form.acceptSystem) }}
39 </div>
40 </div>
41
42 <div class="row">
43 <div class="input-field col s12">
44 {{ form_label(form.plainPassword) }}
45 {{ form_errors(form.plainPassword) }}
46 {{ form_widget(form.plainPassword) }}
47 </div>
48 </div>
49
26 {{ form_widget(form.save, {'attr': {'class': 'btn waves-effect waves-light'}}) }} 50 {{ form_widget(form.save, {'attr': {'class': 'btn waves-effect waves-light'}}) }}
27 {{ form_rest(form) }} 51 {{ form_rest(form) }}
28 </form> 52 </form>
diff --git a/src/Wallabag/GroupBundle/Service/Sha256Salted.php b/src/Wallabag/GroupBundle/Service/Sha256Salted.php
new file mode 100644
index 00000000..c72486ad
--- /dev/null
+++ b/src/Wallabag/GroupBundle/Service/Sha256Salted.php
@@ -0,0 +1,18 @@
1<?php
2
3namespace Strut\StrutBundle\Service;
4
5use Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface;
6
7class Sha256Salted implements PasswordEncoderInterface
8{
9 public function encodePassword($raw, $salt)
10 {
11 return hash('sha256', $salt . $raw); // Custom function for password encrypt
12 }
13
14 public function isPasswordValid($encoded, $raw, $salt)
15 {
16 return $encoded === $this->encodePassword($raw, $salt);
17 }
18}
diff --git a/src/Wallabag/UserBundle/Entity/User.php b/src/Wallabag/UserBundle/Entity/User.php
index ff658ca5..2b73e344 100644
--- a/src/Wallabag/UserBundle/Entity/User.php
+++ b/src/Wallabag/UserBundle/Entity/User.php
@@ -15,6 +15,8 @@ use Symfony\Component\Security\Core\User\UserInterface;
15use Wallabag\ApiBundle\Entity\Client; 15use Wallabag\ApiBundle\Entity\Client;
16use Wallabag\CoreBundle\Entity\Config; 16use Wallabag\CoreBundle\Entity\Config;
17use Wallabag\CoreBundle\Entity\Entry; 17use Wallabag\CoreBundle\Entity\Entry;
18use Wallabag\GroupBundle\Entity\Group;
19use Wallabag\GroupBundle\Entity\UserGroup;
18 20
19/** 21/**
20 * User. 22 * User.
@@ -98,13 +100,11 @@ class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterf
98 private $authCode; 100 private $authCode;
99 101
100 /** 102 /**
101 * @ORM\ManyToMany(targetEntity="Wallabag\GroupBundle\Entity\Group", inversedBy="users", cascade={"persist"}) 103 * @var ArrayCollection
102 * @ORM\JoinTable(name="user_group", 104 *
103 * joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")}, 105 * @ORM\OneToMany(targetEntity="Wallabag\GroupBundle\Entity\UserGroup", mappedBy="user", cascade={"persist", "remove"})
104 * inverseJoinColumns={@ORM\JoinColumn(name="group_id", referencedColumnName="id")}
105 * )
106 */ 106 */
107 protected $groups; 107 protected $userGroups;
108 108
109 /** 109 /**
110 * @var bool Enabled yes/no 110 * @var bool Enabled yes/no
@@ -136,6 +136,7 @@ class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterf
136 { 136 {
137 parent::__construct(); 137 parent::__construct();
138 $this->entries = new ArrayCollection(); 138 $this->entries = new ArrayCollection();
139 $this->userGroups = new ArrayCollection();
139 $this->roles = ['ROLE_USER']; 140 $this->roles = ['ROLE_USER'];
140 } 141 }
141 142
@@ -323,11 +324,65 @@ class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterf
323 } 324 }
324 325
325 /** 326 /**
326 * @param string $name 327 * @param Group $group
328 * @return UserGroup
329 */
330 public function getUserGroupFromGroup(Group $group)
331 {
332 foreach ($this->userGroups as $userGroup) {
333 if ($userGroup->getGroup() == $group) {
334 return $userGroup;
335 }
336 }
337 return null;
338 }
339
340
341 /**
342 * @param Group $group
343 * @param $role
344 */
345 public function setGroupRole(Group $group, $role)
346 {
347 if ($userGroup = $this->getUserGroupFromGroup($group)) {
348 $userGroup->setRole($role);
349 }
350 }
351
352 /**
353 * @param Group $group
354 * @return int
355 */
356 public function getGroupRoleForUser(Group $group)
357 {
358 if ($userGroup = $this->getUserGroupFromGroup($group)) {
359 return $userGroup->getRole();
360 }
361 return 0;
362 }
363
364 /**
365 * @param Group $group
327 * @return bool 366 * @return bool
328 */ 367 */
329 public function hasGroup($name = '') 368 public function inGroup(Group $group)
330 { 369 {
331 return in_array($name, $this->getGroupNames()); 370 if ($group::ACCESS_REQUEST === $group->getAcceptSystem()) {
371 $userGroup = $this->getUserGroupFromGroup($group);
372 return $userGroup->isAccepted();
373 }
374 return null !== $this->getUserGroupFromGroup($group);
375 }
376
377 /**
378 * @return ArrayCollection<Group>
379 */
380 public function getGroups()
381 {
382 $groups = new ArrayCollection();
383 foreach ($this->userGroups as $userGroup) {
384 $groups->add($userGroup->getGroup());
385 }
386 return $groups;
332 } 387 }
333} 388}