CoreController.php 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723
  1. <?php
  2. namespace Muzich\CoreBundle\Controller;
  3. use Muzich\CoreBundle\lib\Controller;
  4. //use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
  5. use Muzich\CoreBundle\Entity\FollowUser;
  6. use Muzich\CoreBundle\Entity\FollowGroup;
  7. //use Doctrine\ORM\Query;
  8. use Muzich\CoreBundle\Form\Element\ElementAddForm;
  9. use Muzich\CoreBundle\Managers\ElementManager;
  10. use Muzich\CoreBundle\Entity\Element;
  11. use Symfony\Component\HttpFoundation\RedirectResponse;
  12. use Muzich\CoreBundle\Form\Search\ElementSearchForm;
  13. use Symfony\Component\Routing\Exception\ResourceNotFoundException;
  14. use Muzich\CoreBundle\Entity\Tag;
  15. use Muzich\CoreBundle\Managers\TagManager;
  16. use Muzich\CoreBundle\Entity\UsersTagsFavorites;
  17. use Muzich\CoreBundle\Managers\ElementReportManager;
  18. use Muzich\CoreBundle\Propagator\EventUser;
  19. use Muzich\CoreBundle\Entity\User;
  20. use Symfony\Component\HttpFoundation\Request;
  21. class CoreController extends Controller
  22. {
  23. /**
  24. * Action permettant de changer le language
  25. *
  26. * @param string $language
  27. * @return RedirectResponse
  28. */
  29. public function changeLanguageAction($language)
  30. {
  31. if($language != null)
  32. {
  33. $old = $this->container->get('request')->getLocale();
  34. $this->get('session')->setLocale($language);
  35. }
  36. $url_referer = $this->container->get('request')->headers->get('referer');
  37. $url_referer = str_replace(
  38. $siteurl = $this->container->getParameter('siteurl'),
  39. '',
  40. $url_referer
  41. );
  42. try {
  43. $params = $this->get('router')->match($url_referer);
  44. } catch (ResourceNotFoundException $exc) {
  45. return $this->redirect($this->generateUrl('home', array('_locale' => $language)));
  46. }
  47. $params['_locale'] = $language;
  48. $route = $params['_route'];
  49. unset($params['_route'], $params['_controller']);
  50. $new_url = $this->generateUrl($route, $params);
  51. return new RedirectResponse($new_url);
  52. }
  53. /**
  54. * Determiner la locale automatiquement
  55. * @return string
  56. */
  57. protected function determineLocale()
  58. {
  59. $lang = $this->container->get('request')
  60. ->getPreferredLanguage($this->container->getParameter('supported_langs'));
  61. // Si on a une lang en sortie,
  62. if (is_null($lang))
  63. {
  64. // TODO: Récupérer ce paramètre dans la config
  65. $lang = 'fr';
  66. }
  67. return $lang;
  68. }
  69. /**
  70. *
  71. * Cette action est écrite pour les utilisateur redirigé du a l'absence de
  72. * lague dans leur route.
  73. * Cette redirection n'est pas interne au code, elle est actuellement effectué
  74. * par le .htaccess lorsque il n'y as pas d'url (en plus de muzi.ch/
  75. */
  76. public function automaticLanguageAction()
  77. {
  78. $lang = $this->determineLocale();
  79. if ($this->getUser() != 'anon.')
  80. {
  81. return $this->redirect($this->generateUrl('home', array('_locale' => $lang)));
  82. }
  83. else
  84. {
  85. return $this->redirect($this->generateUrl('index', array('_locale' => $lang)));
  86. }
  87. }
  88. /**
  89. * Cette action permet a un utilisateur de suivre ou de ne plus suivre
  90. * un utilisateur ou un groupe.
  91. *
  92. * @param string $type
  93. * @param int $id
  94. * @param string $salt
  95. */
  96. public function followAction($type, $id, $token)
  97. {
  98. if (($response = $this->mustBeConnected()))
  99. {
  100. return $response;
  101. }
  102. $user = $this->getUser();
  103. /**
  104. * Bug lors des tests: L'user n'est pas 'lié' a celui en base par doctrine.
  105. * Docrine le voit si on faire une requete directe.
  106. */
  107. if ($this->container->getParameter('env') == 'test')
  108. {
  109. $user = $this->getDoctrine()->getRepository('MuzichCoreBundle:User')->findOneById(
  110. $this->container->get('security.context')->getToken()->getUser()->getId(),
  111. array()
  112. )->getSingleResult();
  113. }
  114. // Vérifications préléminaires
  115. if ($user->getPersonalHash($id) != $token
  116. || !in_array($type, array('user', 'group'))
  117. || !is_numeric($id)
  118. || ($user->getId() == $id && $type == 'user')
  119. )
  120. {
  121. throw $this->createNotFoundException();
  122. }
  123. // On tente de récupérer l'enregistrement FollowUser / FollowGroup
  124. $em = $this->getDoctrine()->getEntityManager();
  125. $Follow = $em
  126. ->getRepository('MuzichCoreBundle:Follow' . ucfirst($type))
  127. ->findOneBy(
  128. array(
  129. 'follower' => $user->getId(),
  130. ($type == 'user') ? 'followed' : 'group' => $id
  131. )
  132. )
  133. ;
  134. // Si il existe déjà c'est qu'il ne veut plus suivre
  135. if ($Follow)
  136. {
  137. if ($type == 'user')
  138. {
  139. // L'utilisateur suis déjà, on doit détruire l'entité
  140. $event = new EventUser($this->container);
  141. $event->removeFromFollow($Follow->getFollowed());
  142. $em->persist($Follow->getFollowed());
  143. }
  144. $em->remove($Follow);
  145. $em->flush();
  146. $following = false;
  147. }
  148. // Sinon, c'est qu'il veut le suivre
  149. else
  150. {
  151. // On récupére l'entité a suivre
  152. $followed = $em->getRepository('MuzichCoreBundle:'.ucfirst($type))->find($id);
  153. if (!$followed) {
  154. throw $this->createNotFoundException('No '.$type.' found for id '.$id);
  155. }
  156. // On instancie te renseigne l'objet Follow****
  157. if ($type == 'user') { $Follow = new FollowUser(); }
  158. else { $Follow = new FollowGroup(); }
  159. $Follow->setFollower($user);
  160. if ($type == 'user')
  161. {
  162. $Follow->setFollowed($followed);
  163. $event = new EventUser($this->container);
  164. $event->addToFollow($followed, $this->getUser());
  165. $em->persist($followed);
  166. }
  167. else { $Follow->setGroup($followed); }
  168. $em->persist($Follow);
  169. $em->flush();
  170. $following = true;
  171. }
  172. if ($this->getRequest()->isXmlHttpRequest())
  173. {
  174. return $this->jsonResponse(array(
  175. 'status' => 'success',
  176. 'following' => $following
  177. ));
  178. }
  179. else
  180. {
  181. return $this->redirect($this->container->get('request')->headers->get('referer'));
  182. }
  183. }
  184. /**
  185. * Procédure d'ajout d'un element
  186. */
  187. public function elementAddAction($group_slug)
  188. {
  189. if (($response = $this->mustBeConnected()))
  190. {
  191. return $response;
  192. }
  193. if ($this->getRequest()->getMethod() != 'POST')
  194. {
  195. throw $this->createNotFoundException('Cette ressource n\'est pas accessible');
  196. }
  197. $user = $this->getUser(true, array('join' => array('groups_owned_groups_tags')));
  198. $em = $this->getDoctrine()->getEntityManager();
  199. /*
  200. * Contrôle préléminaire si groupe précisé
  201. */
  202. $group = null;
  203. if ($group_slug)
  204. {
  205. $group = $this->findGroupWithSlug($group_slug);
  206. if (!$group->userCanAddElement($this->getUserId()))
  207. {
  208. $group = null;
  209. throw $this->createNotFoundException('Vous ne pouvez pas ajouter d\'éléments a ce groupe');
  210. }
  211. }
  212. $element = new Element();
  213. $element->setType('none');
  214. $form = $this->getAddForm($element);
  215. $form->bind($this->getRequest());
  216. if ($form->isValid())
  217. {
  218. /**
  219. * Bug lors des tests: L'user n'est pas 'lié' a celui en base par doctrine.
  220. * Docrine le voit si on faire une requete directe.
  221. */
  222. if ($this->container->getParameter('env') == 'test')
  223. {
  224. $user = $this->getDoctrine()->getRepository('MuzichCoreBundle:User')->findOneById(
  225. $this->container->get('security.context')->getToken()->getUser()->getId(),
  226. array()
  227. )->getSingleResult();
  228. }
  229. // On utilise le gestionnaire d'élément
  230. $factory = new ElementManager($element, $em, $this->container);
  231. $factory->proceedFill($user);
  232. // Si on a précisé un groupe dans lequel mettre l'element
  233. if ($group)
  234. {
  235. $element->setGroup($group);
  236. $redirect_url = $this->generateUrl('show_group', array('slug' => $group_slug));
  237. }
  238. else
  239. {
  240. $redirect_url = $this->generateUrl('home');
  241. }
  242. // Un bug fait que le champ 'need_tags' n'est pas automatiquement renseigné pour l'élement,
  243. // Alors on le fait en manuel ici ... zarb
  244. $form_values = $this->getRequest()->get($form->getName());
  245. if (array_key_exists('need_tags', $form_values))
  246. {
  247. if ($form_values['need_tags'])
  248. {
  249. $element->setNeedTags(true);
  250. }
  251. }
  252. // On signale que cet user a modifié ses diffusions
  253. $user->setData(User::DATA_DIFF_UPDATED, true);
  254. $em->persist($user);
  255. $em->persist($element);
  256. $em->flush();
  257. if ($this->getRequest()->isXmlHttpRequest())
  258. {
  259. // Récupération du li
  260. if (!$group)
  261. {
  262. $html = $this->render('MuzichCoreBundle:SearchElement:li.element.html.twig', array(
  263. 'element' => $element,
  264. 'class_color' => 'odd' // TODO: n'est plus utilisé
  265. ))->getContent();
  266. }
  267. else
  268. {
  269. $html = $this->render('MuzichCoreBundle:SearchElement:li.element.html.twig', array(
  270. 'element' => $element,
  271. 'class_color' => 'odd', // TODO: n'est plus utilisé
  272. 'no_group_name' => true
  273. ))->getContent();
  274. }
  275. return $this->jsonResponse(array(
  276. 'status' => 'success',
  277. 'html' => $html,
  278. 'groups' => (!$group)?$this->isAddedElementCanBeInGroup($element):array()
  279. ));
  280. }
  281. else
  282. {
  283. return $this->redirect($redirect_url);
  284. }
  285. }
  286. else
  287. {
  288. if ($this->getRequest()->isXmlHttpRequest())
  289. {
  290. // Récupération des erreurs
  291. $validator = $this->container->get('validator');
  292. $errorList = $validator->validate($form);
  293. $errors = array();
  294. foreach ($errorList as $error)
  295. {
  296. $errors[] = $this->trans($error->getMessage(), array(), 'validators');
  297. }
  298. foreach ($form->getErrors() as $error)
  299. {
  300. if (!in_array($err = $this->trans($error->getMessageTemplate(), array(), 'validators'), $errors))
  301. {
  302. $errors[] = $err;
  303. }
  304. }
  305. return $this->jsonResponse(array(
  306. 'status' => 'error',
  307. 'errors' => $errors
  308. ));
  309. }
  310. else
  311. {
  312. if (!$group_slug)
  313. {
  314. $search_object = $this->getElementSearcher();
  315. $search_form = $this->getSearchForm($search_object);
  316. $add_form = $form;
  317. return $this->render('MuzichHomeBundle:Home:index.html.twig', array(
  318. 'search_tags_id' => $search_object->getTags(),
  319. 'user' => $this->getUser(),
  320. 'add_form' => $add_form->createView(),
  321. 'add_form_name' => 'add',
  322. 'search_form' => $search_form->createView(),
  323. 'search_form_name' => 'search',
  324. 'network_public' => $search_object->isNetworkPublic(),
  325. 'elements' => $search_object->getElements($this->getDoctrine(), $this->getUserId()),
  326. 'more_count' => $this->container->getParameter('search_default_count')*2,
  327. 'ids_display' => $search_object->getIdsDisplay()
  328. ));
  329. }
  330. else
  331. {
  332. $group = $this->findGroupWithSlug($group_slug);
  333. $search_object = $this->createSearchObject(array(
  334. 'group_id' => $group->getId()
  335. ));
  336. ($group->getOwner()->getId() == $this->getUserId()) ? $his = true : $his = false;
  337. if ($his || $group->getOpen())
  338. {
  339. $add_form = $form;
  340. }
  341. return $this->render('MuzichHomeBundle:Show:showGroup.html.twig', array(
  342. 'group' => $group,
  343. 'his_group' => ($group->getOwner()->getId() == $this->getUserId()) ? true : false,
  344. 'elements' => $search_object->getElements($this->getDoctrine(), $this->getUserId()),
  345. 'following' => $this->getUser()->isFollowingGroupByQuery($this->getDoctrine(), $group->getId()),
  346. 'user' => $this->getUser(),
  347. 'add_form' => (isset($add_form)) ? $add_form->createView() : null,
  348. 'add_form_name' => (isset($add_form)) ? 'add' : null,
  349. 'more_count' => null,
  350. 'more_route' => 'show_group_more',
  351. 'ids_display' => $search_object->getIdsDisplay()
  352. ));
  353. }
  354. }
  355. }
  356. }
  357. /**
  358. * Action non ajax nettoyant la liste de tags du chercheur d'éléments
  359. *
  360. * @return RedirectResponse
  361. */
  362. public function filterClearAction()
  363. {
  364. $es = $this->getElementSearcher();
  365. $es->update(array('tags' => array()));
  366. $this->setElementSearcherParams($es->getParams());
  367. return $this->redirect($this->container->get('request')->headers->get('referer'));
  368. }
  369. /**
  370. * Action non ajax de selection de ses tags favoris pour le chercheur d'élément
  371. *
  372. * @return RedirectResponse
  373. */
  374. public function filterMytagsAction()
  375. {
  376. $this->getElementSearcher(null, true);
  377. return $this->redirect($this->container->get('request')->headers->get('referer'));
  378. }
  379. /**
  380. * Action de récupération ajax de l'id des tags favoris de son profil
  381. *
  382. * @return Response
  383. */
  384. public function getFavoriteTagsAction()
  385. {
  386. if (($response = $this->mustBeConnected()))
  387. {
  388. return $response;
  389. }
  390. // On construit l'element searcher avec les tags favoris
  391. $es = $this->getElementSearcher(null, true);
  392. // Et on retourne les tags
  393. return $this->jsonResponse(array(
  394. 'response' => 'success',
  395. 'tags' => $es->getTags()
  396. ));
  397. }
  398. /**
  399. * Ajout d'un tag en base.
  400. */
  401. public function addTagAction()
  402. {
  403. if (($response = $this->mustBeConnected(true)))
  404. {
  405. return $response;
  406. }
  407. if (strlen((($tag_name = $this->getRequest()->request->get('tag_name'))))
  408. < $this->container->getParameter('tag_add_min_length'))
  409. {
  410. return $this->jsonResponse(array(
  411. 'status' => 'error',
  412. 'errors' => array($this->trans(
  413. 'tags.add.errors.min',
  414. array(
  415. '%limit%' => $this->container->getParameter('tag_add_min_length')
  416. ),
  417. 'userui'
  418. )
  419. )));
  420. }
  421. if (strlen($tag_name) > $this->container->getParameter('tag_add_max_length'))
  422. {
  423. return $this->jsonResponse(array(
  424. 'status' => 'error',
  425. 'errors' => array($this->trans(
  426. 'tags.add.errors.max',
  427. array(
  428. '%limit%' => $this->container->getParameter('tag_add_max_length')
  429. ),
  430. 'userui'
  431. )
  432. )));
  433. }
  434. $tagManager = new TagManager();
  435. $tag = $tagManager->addTag(
  436. $this->getDoctrine(),
  437. $tag_name,
  438. $this->getUser(),
  439. $this->getRequest()->request->get('argument')
  440. );
  441. return $this->jsonResponse(array(
  442. 'status' => 'success',
  443. 'tag_id' => $tag->getId(),
  444. 'tag_name' => $tag->getName()
  445. ));
  446. }
  447. /**
  448. * Action ajax qui ajoute le tags précisé en paramétre aux tags favoris de
  449. * l'utilisateur.
  450. *
  451. * @param int $tag_id
  452. * @param string $token
  453. * @return Response
  454. */
  455. public function addTagToFavoritesAction($tag_id, $token)
  456. {
  457. if (($response = $this->mustBeConnected(true)))
  458. {
  459. return $response;
  460. }
  461. if (!($tag = $this->getDoctrine()->getRepository('MuzichCoreBundle:Tag')
  462. ->findOneById($tag_id)) || $this->getUser()->getPersonalHash($tag_id) != $token)
  463. {
  464. return $this->jsonResponse(array(
  465. 'status' => 'error',
  466. 'errors' => array('NotFound')
  467. ));
  468. }
  469. $user = $this->getUser();
  470. /**
  471. * Bug lors des tests: L'user n'est pas 'lié' a celui en base par doctrine.
  472. * Docrine le voit si on faire une requete directe.
  473. */
  474. if ($this->container->getParameter('env') == 'test')
  475. {
  476. $user = $this->getDoctrine()->getRepository('MuzichCoreBundle:User')->findOneById(
  477. $this->container->get('security.context')->getToken()->getUser()->getId(),
  478. array()
  479. )->getSingleResult();
  480. }
  481. // On contrôle au préalable que le tag ne fait pas déjà partie des favoris de
  482. // l'utilisateur
  483. if (!$this->getDoctrine()->getRepository('MuzichCoreBundle:UsersTagsFavorites')
  484. ->findOneBy(array(
  485. 'user' => $this->getUserId(),
  486. 'tag' => $tag->getId()
  487. )))
  488. {
  489. // Si il ne l'est pas, on créer ce nouvel objet de relation
  490. $fav = new UsersTagsFavorites();
  491. $fav->setTag($tag);
  492. $fav->setUser($user);
  493. $fav->setPosition(0);
  494. $this->getDoctrine()->getEntityManager()->persist($fav);
  495. $this->getDoctrine()->getEntityManager()->flush();
  496. }
  497. return $this->jsonResponse(array(
  498. 'status' => 'success'
  499. ));
  500. }
  501. /**
  502. * Cette action (ajax) configure l'appartenance d'un élément a un groupe.
  503. * Le groupe et l'élément doivent appartenir a l'utilisateur en cours.
  504. *
  505. * @param int $element_id
  506. * @param int $group_id
  507. * @param string $token
  508. * @return Response
  509. */
  510. public function setElementGroupAction($element_id, $group_id, $token)
  511. {
  512. if (($response = $this->mustBeConnected(true)))
  513. {
  514. return $response;
  515. }
  516. if (!($element = $this->getDoctrine()->getRepository('MuzichCoreBundle:Element')
  517. ->findOneById($element_id))
  518. || !($group = $this->getDoctrine()->getRepository('MuzichCoreBundle:Group')
  519. ->findOneById($group_id))
  520. || $this->getUser()->getPersonalHash($element_id) != $token)
  521. {
  522. return $this->jsonResponse(array(
  523. 'status' => 'error',
  524. 'errors' => array('NotFound')
  525. ));
  526. }
  527. if ($element->getOwner()->getId() != $this->getUserId()
  528. || $group->getOwner()->getId() != $this->getUserId()
  529. )
  530. {
  531. return $this->jsonResponse(array(
  532. 'status' => 'error',
  533. 'errors' => array('NotAllowed')
  534. ));
  535. }
  536. // a partir d'ici on a tout ce qu'il faut
  537. $element->setGroup($group);
  538. $this->getDoctrine()->getEntityManager()->persist($element);
  539. $this->getDoctrine()->getEntityManager()->flush();
  540. // On récupère le nouveau dom de l'élément
  541. $html = $this->render('MuzichCoreBundle:SearchElement:element.html.twig', array(
  542. 'element' => $element
  543. ))->getContent();
  544. return $this->jsonResponse(array(
  545. 'status' => 'success',
  546. 'html' => $html,
  547. 'dom_id' => 'element_'.$element->getId()
  548. ));
  549. }
  550. /**
  551. * Action (ajax) permettant de signaler un élément comme contenu non approprié.
  552. *
  553. * @param int $element_id
  554. * @param string $token
  555. * @return Response
  556. */
  557. public function reportElementAction($element_id, $token)
  558. {
  559. if (($response = $this->mustBeConnected(true)))
  560. {
  561. return $response;
  562. }
  563. if (!($element = $this->getDoctrine()->getRepository('MuzichCoreBundle:Element')
  564. ->findOneById($element_id))
  565. || $this->getUser()->getPersonalHash($element_id) != $token)
  566. {
  567. return $this->jsonResponse(array(
  568. 'status' => 'error',
  569. 'errors' => array('NotFound')
  570. ));
  571. }
  572. // On utilise le manager de rapport
  573. $erm = new ElementReportManager($element);
  574. $erm->add($this->getUser());
  575. $this->getDoctrine()->getEntityManager()->persist($element);
  576. $this->getDoctrine()->getEntityManager()->flush();
  577. return $this->jsonResponse(array(
  578. 'status' => 'success'
  579. ));
  580. }
  581. /**
  582. * Il arrive que l'on configure le chercheur d'élément de façon a ce qu'il
  583. * affiche une liste d'élément précis (collection d'id). Cette action
  584. * supprime cette configuration de façon a ce que le chercheur fonctionne
  585. * normalement.
  586. *
  587. * @return \Symfony\Component\HttpFoundation\Response
  588. */
  589. public function filterRemoveIdsAction()
  590. {
  591. if (($response = $this->mustBeConnected(true)))
  592. {
  593. return $response;
  594. }
  595. $es = $this->getElementSearcher();
  596. $es->setIds(null);
  597. $es->setIdsDisplay(null);
  598. $this->setElementSearcherParams($es->getParams());
  599. $html = $this->render('MuzichCoreBundle:SearchElement:default.html.twig', array(
  600. 'user' => $this->getUser(),
  601. 'elements' => $es->getElements($this->getDoctrine(), $this->getUserId())
  602. ))->getContent();
  603. return $this->jsonResponse(array(
  604. 'status' => 'success',
  605. 'html' => $html
  606. ));
  607. }
  608. /**
  609. * Url de récupération des plugins/application qui vienne partager une url
  610. * @param Request $request
  611. */
  612. public function shareFromAction(Request $request)
  613. {
  614. return $this->redirect($this->generateUrl('home', array(
  615. 'from_url' => $request->get('from_url'),
  616. // On ne se préoccupe pas de la locale coté plugins/applications
  617. '_locale' => $this->determineLocale()
  618. )));
  619. }
  620. public function renderSideMenuAction()
  621. {
  622. $user = $this->getUser(true, array('join' => array(
  623. 'followeds_users', 'followers_users', 'followeds_groups'
  624. )), true);
  625. return $this->render(
  626. 'MuzichCoreBundle:Menu:side_menu.html.twig',
  627. array(
  628. 'followeds_users' => $user->getFollowedsUsers(),
  629. 'followeds_groups' => $user->getFollowedGroups(),
  630. 'followers_users' => $user->getFollowersUsers()
  631. )
  632. );
  633. }
  634. }