FolderController::doOperation()   A
last analyzed

Complexity

Conditions 4
Paths 6

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 16
rs 9.2
c 0
b 0
f 0
cc 4
eloc 10
nc 6
nop 2
1
<?php
2
/*
3
  ÁTICA - Aplicación web para la gestión documental de centros educativos
4
5
  Copyright (C) 2015-2017: Luis Ramón López López
6
7
  This program is free software: you can redistribute it and/or modify
8
  it under the terms of the GNU Affero General Public License as published by
9
  the Free Software Foundation, either version 3 of the License, or
10
  (at your option) any later version.
11
12
  This program is distributed in the hope that it will be useful,
13
  but WITHOUT ANY WARRANTY; without even the implied warranty of
14
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
  GNU Affero General Public License for more details.
16
17
  You should have received a copy of the GNU Affero General Public License
18
  along with this program.  If not, see [http://www.gnu.org/licenses/].
19
*/
20
21
namespace AppBundle\Controller\Documentation;
22
23
use AppBundle\Entity\Documentation\Folder;
24
use AppBundle\Entity\Documentation\FolderPermission;
25
use AppBundle\Entity\Documentation\FolderRepository;
26
use AppBundle\Entity\ElementRepository;
27
use AppBundle\Entity\Organization;
28
use AppBundle\Form\Type\Documentation\FolderType;
29
use AppBundle\Security\OrganizationVoter;
30
use Doctrine\Common\Collections\ArrayCollection;
31
use Doctrine\ORM\EntityManager;
32
use Doctrine\ORM\QueryBuilder;
33
use Gedmo\Sortable\Entity\Repository\SortableRepository;
34
use Pagerfanta\Adapter\DoctrineORMAdapter;
35
use Pagerfanta\Pagerfanta;
36
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
37
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
38
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
39
use Symfony\Component\Form\Form;
40
use Symfony\Component\HttpFoundation\Request;
41
42
/**
43
 * @Route("/documentos")
44
 */
45
class FolderController extends Controller
46
{
47
    /**
48
     * @Route("/carpeta/{id}/nueva", name="documentation_folder_new", methods={"GET", "POST"})
49
     * @Route("/carpeta/{id}", name="documentation_folder_form", requirements={"id" = "\d+"}, methods={"GET", "POST"})
50
     * @Security("is_granted('FOLDER_MANAGE', folder)")
51
     */
52
    public function folderFormAction(Folder $folder = null, Request $request)
53
    {
54
        $organization = $this->get('AppBundle\Service\UserExtensionService')->getCurrentOrganization();
55
        $this->denyAccessUnlessGranted(OrganizationVoter::MANAGE, $organization);
56
57
        $em = $this->getDoctrine()->getManager();
58
        $new = $request->get('_route') === 'documentation_folder_new';
59
60
        $sourceFolder = $folder;
61
62
        if ($new) {
63
            $newFolder = new Folder();
64
            $newFolder
65
                ->setOrganization($organization)
66
                ->setParent($folder);
67
            $folder = $newFolder;
68
            $em->persist($folder);
69
        } else {
70
            if (null === $sourceFolder->getParent()) {
71
                throw $this->createAccessDeniedException();
72
            }
73
        }
74
        $breadcrumb = $sourceFolder->getParent() ? $this->generateBreadcrumb($sourceFolder, false) : [];
75
76
        if ($request->request->get('folder')) {
77
            $folder->setType($request->request->get('folder')['type']);
78
        }
79
        $form = $this->createForm(FolderType::class, $folder, [
80
            'new' => $new,
81
            'allow_extra_fields' => !$request->request->has('submit')
82
        ]);
83
84
        $this->setFolderRolesInForm($folder, $form);
0 ignored issues
show
Compatibility introduced by
$form of type object<Symfony\Component\Form\FormInterface> is not a sub-type of object<Symfony\Component\Form\Form>. It seems like you assume a concrete implementation of the interface Symfony\Component\Form\FormInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
85
        $form->handleRequest($request);
86
        $breadcrumb[] = ['fixed' => $this->get('translator')->trans($new ? 'title.folder.new' : 'title.folder.edit', [], 'documentation')];
87
88
        if ($form->isSubmitted() && $form->isValid() && $request->request->has('submit')) {
89
            try {
90
                $this->updateFolderRolesFromForm($folder, $em, $form);
0 ignored issues
show
Compatibility introduced by
$form of type object<Symfony\Component\Form\FormInterface> is not a sub-type of object<Symfony\Component\Form\Form>. It seems like you assume a concrete implementation of the interface Symfony\Component\Form\FormInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
91
                $em->flush();
92
                $this->addFlash('success', $this->get('translator')->trans('message.folder.saved', [], 'documentation'));
93
                return $this->redirectToRoute('documentation', ['id' => $sourceFolder->getId()]);
94
            } catch (\Exception $e) {
95
                $this->addFlash('error', $this->get('translator')->trans('message.folder.save_error', [], 'documentation'));
96
            }
97
        }
98
99
        return $this->render('documentation/folder_form.html.twig', [
100
            'menu_path' => 'documentation',
101
            'breadcrumb' => $breadcrumb,
102
            'title' => $this->get('translator')->trans($new ? 'title.folder.new' : 'title.folder.edit', [], 'documentation'),
103
            'form' => $form->createView(),
104
            'folder' => $folder
105
        ]);
106
    }
107
108
    /**
109
     * @param Folder $folder
110
     * @param Form $form
111
     */
112
    private function setFolderRolesInForm(Folder $folder = null, Form $form)
113
    {
114
        if (null === $folder) {
115
            return;
116
        }
117
118
        $permissions = $folder->getPermissions();
119
120
        $permissionTypes = [
121
            'access' => FolderPermission::PERMISSION_VISIBLE,
122
            'manager' => FolderPermission::PERMISSION_MANAGE,
123
            'upload' => FolderPermission::PERMISSION_UPLOAD,
124
            'review' => FolderPermission::PERMISSION_REVIEW,
125
            'approve' => FolderPermission::PERMISSION_APPROVE
126
        ];
127
128
        foreach ($permissionTypes as $name => $type) {
129
            if ($form->has('profiles_'.$name)) {
130
                $data = [];
131
132
                /** @var FolderPermission $permission */
133
                foreach ($permissions as $permission) {
134
                    if ($permission->getPermission() === $type) {
135
                        $data[] = $permission->getElement();
136
                    }
137
                }
138
139
                if (!empty($data)) {
140
                    $form->get('profiles_'.$name)->setData($data);
141
                }
142
            }
143
        }
144
    }
145
146
    /**
147
     * @param Folder $folder
148
     * @param EntityManager $em
149
     * @param Form $form
150
     */
151
    private function updateFolderRolesFromForm($folder, EntityManager $em, $form)
152
    {
153
        $oldPermissions = $folder->getPermissions();
154
155
        $permissionTypes = [
156
            'access' => FolderPermission::PERMISSION_VISIBLE,
157
            'manager' => FolderPermission::PERMISSION_MANAGE,
158
            'upload' => FolderPermission::PERMISSION_UPLOAD,
159
            'review' => FolderPermission::PERMISSION_REVIEW,
160
            'approve' => FolderPermission::PERMISSION_APPROVE
161
        ];
162
163
        foreach ($permissionTypes as $name => $type) {
164
165
            $data = $form->has('profiles_'.$name) ? $form->get('profiles_'.$name)->getData() : [];
166
            if (!$data instanceof ArrayCollection) {
167
                $data = new ArrayCollection($data);
168
            }
169
170
            /** @var FolderPermission $permission */
171
            foreach ($oldPermissions as $permission) {
172
                if ($permission->getPermission() === $type) {
173
                    if (!$data->contains($permission->getElement())) {
174
                        $em->remove($permission);
175
                    } else {
176
                        $data->removeElement($permission->getElement());
177
                    }
178
                }
179
            }
180
181
            foreach ($data as $datum) {
182
                $permission = new FolderPermission();
183
                $permission
184
                    ->setFolder($folder)
185
                    ->setPermission($type)
186
                    ->setElement($datum);
187
                $em->persist($permission);
188
            }
189
        }
190
        $em->flush();
191
    }
192
193
    /**
194
     * @Route("/operacion/entrada/{id}", name="documentation_entry_operation", requirements={"id" = "\d+"}, methods={"POST"})
195
     * @Security("is_granted('FOLDER_MANAGE', folder)")
196
     */
197
    public function entryOperationAction(Request $request, Folder $folder)
198
    {
199
        $ok = false;
200
201
        $em = $this->getDoctrine()->getManager();
202
        foreach (['up' => -1, 'down' => 1] as $op => $step) {
203
            if ($request->get($op)) {
204
                /** @var SortableRepository $repository */
205
                $repository = $em->getRepository('AppBundle:Documentation\Entry');
206
207
                $entry = $repository->find($request->get($op));
208
209
                if ($entry && $entry->getFolder() === $folder) {
210
                    $entry->setPosition(max($entry->getPosition() + $step, 0));
211
                    $ok = true;
212
                }
213
            }
214
        }
215
        if ($ok) {
216
            $em->flush();
217
        }
218
219
        return $this->redirectToRoute('documentation', ['id' => $folder->getId()]);
220
    }
221
222
    /**
223
     * @Route("/operacion/{id}", name="documentation_folder_operation", requirements={"id" = "\d+"}, methods={"POST"})
224
     * @Security("is_granted('FOLDER_MANAGE', folder)")
225
     */
226
    public function folderOperationAction($id, Request $request)
227
    {
228
        $organization = $this->get('AppBundle\Service\UserExtensionService')->getCurrentOrganization();
229
230
        $folder = $this->getFolder($organization, $id);
231
232
        if (null === $folder || $folder->getOrganization() !== $organization) {
233
            throw $this->createNotFoundException();
234
        }
235
        $this->doOperation($request, $folder);
236
        return $this->redirectToRoute('documentation', ['id' => $folder->getId()]);
237
    }
238
239
240
    /**
241
     * @param Request $request
242
     * @param Folder $item
243
     */
244
    private function doOperation(Request $request, $item)
245
    {
246
        $ok = false;
247
248
        $em = $this->getDoctrine()->getManager();
249
        foreach (['up', 'down'] as $op) {
250
            if ($request->get($op)) {
251
                $method = 'move'.ucfirst($op);
252
                $em->getRepository(get_class($item))->$method($item);
253
                $ok = true;
254
            }
255
        }
256
        if ($ok) {
257
            $em->flush();
258
        }
259
    }
260
261
    /**
262
     * @Route("/{id}/{page}", name="documentation", requirements={"page" = "\d+", "id" = "\d+"}, defaults={"page" = "1", "folder" = null}, methods={"GET"})
263
     */
264
    public function browseAction($page, $id = null, Request $request)
265
    {
266
        $organization = $this->get('AppBundle\Service\UserExtensionService')->getCurrentOrganization();
267
268
        $q = $request->get('q', null);
269
270
        $folder = (null === $id) ? $this->getRootFolder($organization) : $this->getFolder($organization, $id);
271
272
        if (null === $folder) {
273
            throw $this->createNotFoundException();
274
        }
275
        $this->denyAccessUnlessGranted('FOLDER_ACCESS', $folder);
276
277
        $pager = $this->getFolderEntriesPager($page, $folder, $q);
278
279
        $breadcrumb = $this->generateBreadcrumb($folder);
280
281
        return $this->render('documentation/list.html.twig', [
282
            'breadcrumb' => $breadcrumb,
283
            'pager' => $pager,
284
            'current' => $folder,
285
            'permissions' => ['is_folder_manager' => $this->isGranted('FOLDER_MANAGE', $folder), 'is_organization_manager' => $this->isGranted('ORGANIZATION_MANAGE', $organization)],
286
            'tree' => $this->getOrganizationTree($this->getRootFolder($organization), $folder),
287
            'q' => $q,
288
            'domain' => 'element'
289
        ]);
290
    }
291
292
    /**
293
     * @param Organization $organization
294
     * @return Folder
295
     */
296 View Code Duplication
    private function getRootFolder($organization)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
297
    {
298
        /** @var FolderRepository $folderRepository */
299
        $folderRepository = $this->getDoctrine()->getManager()->getRepository('AppBundle:Documentation\Folder');
300
301
        /** @var Folder|null $folder */
302
        $folder = $folderRepository->findOneBy(['organization' => $organization, 'parent' => null]);
303
304
        return $folder;
305
    }
306
307
    /**
308
     * @param Organization $organization
309
     * @param int $id
310
     * @return Folder
311
     */
312 View Code Duplication
    private function getFolder($organization, $id)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
313
    {
314
        /** @var FolderRepository $folderRepository */
315
        $folderRepository = $this->getDoctrine()->getManager()->getRepository('AppBundle:Documentation\Folder');
316
317
        /** @var Folder|null $folder */
318
        $folder = $folderRepository->findOneBy(['organization' => $organization, 'id' => $id]);
319
320
        return $folder;
321
    }
322
323
    /**
324
     * @param $page
325
     * @param Folder|null $folder
326
     * @param $q
327
     * @return Pagerfanta
328
     */
329
    private function getFolderEntriesPager($page, Folder $folder = null, $q)
330
    {
331
        /** @var ElementRepository $entriesRepository */
332
        $entriesRepository = $this->getDoctrine()->getManager()->getRepository('AppBundle:Documentation\Entry');
333
334
        // obtener las carpetas
335
        $folders = $this->getDoctrine()->getManager()->getRepository('AppBundle:Documentation\Folder')->getChildren($folder, false, false, 'ASC', true);
336
337
        /** @var QueryBuilder $queryBuilder */
338
        $queryBuilder = $entriesRepository->createQueryBuilder('e')
339
            ->andWhere('e.folder IN (:folders)')
340
            ->setParameter('folders', $folders)
341
            ->join('e.folder', 'f')
342
            ->leftJoin('e.element', 'el')
343
            ->addOrderBy('f.left')
344
            ->addOrderBy('el.left')
345
            ->addOrderBy('e.position')
346
            ->addSelect('v')
347
            ->leftJoin('e.currentVersion', 'v');
348
349
        if ($q) {
350
            $queryBuilder
351
                ->andWhere('e.name LIKE :tq')
352
                ->setParameter('tq', '%'.$q.'%');
353
        }
354
355
        $adapter = new DoctrineORMAdapter($queryBuilder, false);
356
        $pager = new Pagerfanta($adapter);
357
        $pager
358
            ->setMaxPerPage($this->getParameter('page.size'))
359
            ->setCurrentPage($q ? 1 : $page);
360
361
        return $pager;
362
    }
363
364
    /**
365
     * Returns breadcrumb that matches the folder (ignores root element)
366
     * @param Folder $folder
367
     * @param bool $ignoreLast
368
     * @return array
369
     */
370 View Code Duplication
    static public function generateBreadcrumb(Folder $folder = null, $ignoreLast = true)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
371
    {
372
        $breadcrumb = [];
373
374
        if (null === $folder) {
375
            return null;
376
        }
377
378
        $item = $folder;
379
        while ($item->getParent()) {
380
            $entry = ['fixed' => $item->getName()];
381
            if ($item !== $folder || !$ignoreLast) {
382
                $entry['routeName'] = 'documentation';
383
                $entry['routeParams'] = ['id' => $item->getId()];
384
            }
385
            array_unshift($breadcrumb, $entry);
386
            $item = $item->getParent();
387
        }
388
        return $breadcrumb;
389
    }
390
391
    /**
392
     * Returns folder tree
393
     *
394
     * @param Folder $folder
395
     * @param Folder $current
396
     *
397
     * @return array
398
     */
399
    private function getOrganizationTree(Folder $folder, Folder $current = null)
400
    {
401
        /** @var FolderRepository $folderRepository */
402
        $folderRepository = $this->getDoctrine()->getManager()->getRepository('AppBundle:Documentation\Folder');
403
404
        $children = $folderRepository->childrenHierarchy($folder);
405
406
        $organization = $folder->getOrganization();
407
        $disabled = $this->isGranted('ORGANIZATION_MANAGE', $organization) ? [] : array_map(function(Folder $f) {
408
            return $f->getId();
409
        }, $folderRepository->getAccessDeniedFoldersForUserAndOrganizationArray($this->getUser(), $organization));
410
411
        list($tree) = $this->processChildren($children, $current ? $current->getId() : null, $disabled);
412
413
        return $tree;
414
    }
415
416
    /**
417
     * Convert children array into a treeview array
418
     *
419
     * @param array $children
420
     * @param integer $currentId
421
     * @param array $disabledId
422
     * @return mixed
423
     */
424
    private function processChildren(array $children, $currentId = null, $disabledId = [])
425
    {
426
        $result = [];
427
        $selected = false;
428
        foreach ($children as $child) {
429
            $item = [];
430
            $item['text'] = $child['name'];
431
432
            $disabled = in_array($child['id'], $disabledId, false);
433
            if ($disabled) {
434
                $item['state'] = ['disabled' => true];
435
            }
436
            if ($currentId === $child['id']) {
437
                $item['state'] = ['selected' => true, 'expanded' => true];
438
                $selected = true;
439
            }
440
            if (!$disabled && count($child['__children']) > 0) {
441
                list($item['nodes'], $selected) = $this->processChildren($child['__children'], $currentId, $disabledId);
442
            } else {
443
                $item['icon'] = 'fa fa-folder';
444
            }
445
            if ($selected) {
446
                if (!isset($item['state'])) {
447
                    $item['state'] = [];
448
                }
449
                $item['state']['expanded'] = true;
450
            }
451
            $item['href'] = $this->generateUrl('documentation', ['id' => $child['id']]);
452
            $result[] = $item;
453
        }
454
455
        return [$result, $selected];
456
    }
457
458
}
459