/** * Retrieves the menu based on the group options. * * @param string $name * @param array $options * * @return \Knp\Menu\ItemInterface * * @throws \InvalidArgumentException if the menu does not exists */ public function get($name, array $options = array()) { $group = $options['group']; $menuItem = $this->menuFactory->createItem($options['name'], array('label' => $group['label'])); foreach ($group['items'] as $item) { if (isset($item['admin']) && !empty($item['admin'])) { $admin = $this->pool->getInstance($item['admin']); // skip menu item if no `list` url is available or user doesn't have the LIST access rights if (!$admin->hasRoute('list') || !$admin->isGranted('LIST')) { continue; } $label = $admin->getLabel(); $options = $admin->generateMenuUrl('list'); $options['extras'] = array('translation_domain' => $admin->getTranslationDomain(), 'admin' => $admin); } else { $label = $item['label']; $options = array('route' => $item['route'], 'routeParameters' => $item['route_params'], 'extras' => array('translation_domain' => $group['label_catalogue'])); } $menuItem->addChild($label, $options); } if (false === $menuItem->hasChildren()) { $menuItem->setDisplay(false); } return $menuItem; }
/** * {@inheritDoc} */ public function load($resource, $type = null) { $collection = new SymfonyRouteCollection(); foreach ($this->adminServiceIds as $id) { $admin = $this->pool->getInstance($id); foreach ($admin->getRoutes()->getElements() as $code => $route) { $collection->add($route->getDefault('_sonata_name'), $route); } $reflection = new \ReflectionObject($admin); $collection->addResource(new FileResource($reflection->getFileName())); } $reflection = new \ReflectionObject($this->container); $collection->addResource(new FileResource($reflection->getFileName())); return $collection; }
/** * @param ItemInterface $menu * @param AdminMenu[] $tree * @param int $level */ protected function generateMenu(&$menu, &$tree, $level = 0) { while (!empty($tree)) { $item = array_shift($tree); $type = $item->getType(); $itemLabel = $item->getTitle(); $itemLevel = $item->getLevel(); if ($itemLevel == $level) { $options = []; if (AdminMenu::TYPE_FOLDER !== $type) { $admin = $this->pool->getInstance($item->getServiceId()); if ($admin) { $options = $admin->generateMenuUrl('list'); $options['extras'] = ['admin' => $admin]; } else { $this->logger->alert('Admin not found for class', [$item->getServiceId()]); } } $child = $menu->addChild($itemLabel, $options); } elseif ($itemLevel > $level) { array_unshift($tree, $item); $this->generateMenu($child, $tree, $itemLevel); } else { array_unshift($tree, $item); break; } } }
/** * Get KnpMenu * * @param Request $request * * @return ItemInterface */ public function getKnpMenu(Request $request = null) { $menuFactory = new MenuFactory(); $menu = $menuFactory->createItem('root')->setExtra('request', $request); foreach ($this->pool->getAdminGroups() as $name => $group) { $menu->addChild($name, array('label' => $group['label']))->setAttributes(array('icon' => $group['icon'], 'label_catalogue' => $group['label_catalogue']))->setExtra('roles', $group['roles']); foreach ($group['items'] as $item) { if (array_key_exists('admin', $item) && $item['admin'] != null) { $admin = $this->pool->getInstance($item['admin']); // skip menu item if no `list` url is available or user doesn't have the LIST access rights if (!$admin->hasRoute('list') || !$admin->isGranted('LIST')) { continue; } $label = $admin->getLabel(); $route = $admin->generateUrl('list'); $translationDomain = $admin->getTranslationDomain(); } else { $label = $item['label']; $route = $this->router->generate($item['route'], $item['route_params']); $translationDomain = $group['label_catalogue']; $admin = null; } $menu[$name]->addChild($label, array('uri' => $route))->setExtra('translationdomain', $translationDomain)->setExtra('admin', $admin); } } return $menu; }
/** * @return array */ public function getRoles() { $roles = array(); $rolesReadOnly = array(); if (!$this->securityContext->getToken()) { return array($roles, $rolesReadOnly); } // get roles from the Admin classes foreach ($this->pool->getAdminServiceIds() as $id) { try { $admin = $this->pool->getInstance($id); } catch (\Exception $e) { continue; } $isMaster = $admin->isGranted('MASTER'); $securityHandler = $admin->getSecurityHandler(); // TODO get the base role from the admin or security handler $baseRole = $securityHandler->getBaseRole($admin); if (strlen($baseRole) == 0) { // the security handler related to the admin does not provide a valid string continue; } foreach ($admin->getSecurityInformation() as $role => $permissions) { $role = sprintf($baseRole, $role); if ($isMaster) { // if the user has the MASTER permission, allow to grant access the admin roles to other users $roles[$role] = $role; } elseif ($this->securityContext->isGranted($role)) { // although the user has no MASTER permission, allow the currently logged in user to view the role $rolesReadOnly[$role] = $role; } } } $isMaster = $this->securityContext->isGranted('ROLE_SUPER_ADMIN'); // get roles from the service container foreach ($this->rolesHierarchy as $name => $rolesHierarchy) { if ($this->securityContext->isGranted($name) || $isMaster) { $roles[$name] = $name . ': ' . implode(', ', $rolesHierarchy); foreach ($rolesHierarchy as $role) { if (!isset($roles[$role])) { $roles[$role] = $role; } } } } return array($roles, $rolesReadOnly); }
/** * Builds sidebar menu. * * @return ItemInterface */ public function createSidebarMenu() { $menu = $this->factory->createItem('root', array('extras' => array('request' => $this->request))); foreach ($this->pool->getAdminGroups() as $name => $group) { $attributes = array(); $extras = array('icon' => $group['icon'], 'label_catalogue' => $group['label_catalogue'], 'roles' => $group['roles']); // Check if the menu group is built by a menu provider if (isset($group['provider'])) { $subMenu = $this->provider->get($group['provider']); $menu->addChild($subMenu)->setExtras(array_merge($subMenu->getExtras(), $extras))->setAttributes(array_merge($subMenu->getAttributes(), $attributes)); continue; } // The menu group is built by config $menu->addChild($name, array('label' => $group['label'], 'attributes' => $attributes, 'extras' => $extras)); foreach ($group['items'] as $item) { if (isset($item['admin']) && !empty($item['admin'])) { $admin = $this->pool->getInstance($item['admin']); // skip menu item if no `list` url is available or user doesn't have the LIST access rights if (!$admin->hasRoute('list') || !$admin->isGranted('LIST')) { continue; } $label = $admin->getLabel(); $options = $admin->generateMenuUrl('list'); $options['extras'] = array('translation_domain' => $admin->getTranslationDomain(), 'admin' => $admin); } else { $label = $item['label']; $options = array('route' => $item['route'], 'routeParameters' => $item['route_params'], 'extras' => array('translation_domain' => $group['label_catalogue'])); } $menu[$name]->addChild($label, $options); } if (0 === count($menu[$name]->getChildren())) { $menu->removeChild($name); } } $event = new ConfigureMenuEvent($this->factory, $menu); $this->eventDispatcher->dispatch(ConfigureMenuEvent::SIDEBAR, $event); return $event->getMenu(); }
/** * @param \Symfony\Component\HttpFoundation\Request $request * @return \Symfony\Component\HttpFoundation\Response */ public function setObjectFieldValueAction(Request $request) { $field = $request->get('field'); $code = $request->get('code'); $objectId = $request->get('objectId'); $value = $request->get('value'); $context = $request->get('context'); $admin = $this->pool->getInstance($code); // alter should be done by using a post method if ($request->getMethod() != 'POST') { return new Response(json_encode(array('status' => 'KO', 'message' => 'Expected a POST Request')), 200, array('Content-Type' => 'application/json')); } $object = $admin->getObject($objectId); if (!$object) { return new Response(json_encode(array('status' => 'KO', 'message' => 'Object does not exist')), 200, array('Content-Type' => 'application/json')); } // check user permission if (false === $admin->isGranted('EDIT', $object)) { return new Response(json_encode(array('status' => 'KO', 'message' => 'Invalid permissions')), 200, array('Content-Type' => 'application/json')); } if ($context == 'list') { $fieldDescription = $admin->getListFieldDescription($field); } else { return new Response(json_encode(array('status' => 'KO', 'message' => 'Invalid context')), 200, array('Content-Type' => 'application/json')); } if (!$fieldDescription) { return new Response(json_encode(array('status' => 'KO', 'message' => 'The field does not exist')), 200, array('Content-Type' => 'application/json')); } if (!$fieldDescription->getOption('editable')) { return new Response(json_encode(array('status' => 'KO', 'message' => 'The field cannot be edit, editable option must be set to true')), 200, array('Content-Type' => 'application/json')); } // TODO : call the validator component ... $propertyPath = new PropertyPath($field); $propertyPath->setValue($object, $value); $admin->update($object); // render the widget // todo : fix this, the twig environment variable is not set inside the extension ... $extension = $this->twig->getExtension('sonata_admin'); $extension->initRuntime($this->twig); $content = $extension->renderListElement($object, $fieldDescription); return new Response(json_encode(array('status' => 'OK', 'content' => $content)), 200, array('Content-Type' => 'application/json')); }
/** * Retrieve list of items for autocomplete form field. * * @param Request $request * * @return JsonResponse * * @throws \RuntimeException * @throws AccessDeniedException */ public function retrieveAutocompleteItemsAction(Request $request) { $admin = $this->pool->getInstance($request->get('admin_code')); $admin->setRequest($request); $context = $request->get('_context', ''); if ($context === 'filter' && false === $admin->isGranted('LIST')) { throw new AccessDeniedException(); } if ($context !== 'filter' && false === $admin->isGranted('CREATE') && false === $admin->isGranted('EDIT')) { throw new AccessDeniedException(); } // subject will be empty to avoid unnecessary database requests and keep autocomplete function fast $admin->setSubject($admin->getNewInstance()); if ($context === 'filter') { // filter $fieldDescription = $this->retrieveFilterFieldDescription($admin, $request->get('field')); $filterAutocomplete = $admin->getDatagrid()->getFilter($fieldDescription->getName()); $property = $filterAutocomplete->getFieldOption('property'); $callback = $filterAutocomplete->getFieldOption('callback'); $minimumInputLength = $filterAutocomplete->getFieldOption('minimum_input_length', 3); $itemsPerPage = $filterAutocomplete->getFieldOption('items_per_page', 10); $reqParamPageNumber = $filterAutocomplete->getFieldOption('req_param_name_page_number', '_page'); $toStringCallback = $filterAutocomplete->getFieldOption('to_string_callback'); } else { // create/edit form $fieldDescription = $this->retrieveFormFieldDescription($admin, $request->get('field')); $formAutocomplete = $admin->getForm()->get($fieldDescription->getName()); if ($formAutocomplete->getConfig()->getAttribute('disabled')) { throw new AccessDeniedException('Autocomplete list can`t be retrieved because the form element is disabled or read_only.'); } $property = $formAutocomplete->getConfig()->getAttribute('property'); $callback = $formAutocomplete->getConfig()->getAttribute('callback'); $minimumInputLength = $formAutocomplete->getConfig()->getAttribute('minimum_input_length'); $itemsPerPage = $formAutocomplete->getConfig()->getAttribute('items_per_page'); $reqParamPageNumber = $formAutocomplete->getConfig()->getAttribute('req_param_name_page_number'); $toStringCallback = $formAutocomplete->getConfig()->getAttribute('to_string_callback'); } $searchText = $request->get('q'); $targetAdmin = $fieldDescription->getAssociationAdmin(); // check user permission if (false === $targetAdmin->isGranted('LIST')) { throw new AccessDeniedException(); } if (mb_strlen($searchText, 'UTF-8') < $minimumInputLength) { return new JsonResponse(array('status' => 'KO', 'message' => 'Too short search string.'), 403); } $targetAdmin->setPersistFilters(false); $datagrid = $targetAdmin->getDatagrid(); if ($callback !== null) { if (!is_callable($callback)) { throw new \RuntimeException('Callback does not contain callable function.'); } call_user_func($callback, $targetAdmin, $property, $searchText); } else { if (is_array($property)) { // multiple properties foreach ($property as $prop) { if (!$datagrid->hasFilter($prop)) { throw new \RuntimeException(sprintf('To retrieve autocomplete items, you should add filter "%s" to "%s" in configureDatagridFilters() method.', $prop, get_class($targetAdmin))); } $filter = $datagrid->getFilter($prop); $filter->setCondition(FilterInterface::CONDITION_OR); $datagrid->setValue($prop, null, $searchText); } } else { if (!$datagrid->hasFilter($property)) { throw new \RuntimeException(sprintf('To retrieve autocomplete items, you should add filter "%s" to "%s" in configureDatagridFilters() method.', $property, get_class($targetAdmin))); } $datagrid->setValue($property, null, $searchText); } } $datagrid->setValue('_per_page', null, $itemsPerPage); $datagrid->setValue('_page', null, $request->query->get($reqParamPageNumber, 1)); $datagrid->buildPager(); $pager = $datagrid->getPager(); $items = array(); $results = $pager->getResults(); foreach ($results as $entity) { if ($toStringCallback !== null) { if (!is_callable($toStringCallback)) { throw new \RuntimeException('Option "to_string_callback" does not contain callable function.'); } $label = call_user_func($toStringCallback, $entity, $property); } else { $resultMetadata = $targetAdmin->getObjectMetadata($entity); $label = $resultMetadata->getTitle(); } $items[] = array('id' => $admin->id($entity), 'label' => $label); } return new JsonResponse(array('status' => 'OK', 'more' => !$pager->isLastPage(), 'items' => $items)); }
/** * @deprecated * * @param string $code * * @return AdminInterface */ public function getAdmin($code) { return $this->pool->getInstance($code); }
/** * @expectedException \InvalidArgumentException * @expectedExceptionMessage Admin service "sonata.news.admin.post" not found in admin pool. */ public function testGetInstanceWithUndefinedServiceId() { $this->pool->getInstance('sonata.news.admin.post'); }
/** * @param Request $request * * @return Response * * @throws AccessDeniedException */ public function autoCompleteAction(Request $request) { /** @var Admin $admin */ $admin = $this->pool->getInstance($request->get('code')); $admin->setRequest($request); // check user permission if (false === $admin->isGranted('LIST')) { throw new AccessDeniedException(); } // subject will be empty to avoid unnecessary database requests and keep auto-complete function fast $admin->setSubject($admin->getNewInstance()); $fieldDescription = $this->retrieveFieldDescription($admin, $request->get('field')); $formAutocomplete = $admin->getForm()->get($fieldDescription->getName()); if ($formAutocomplete->getConfig()->getAttribute('disabled')) { throw new AccessDeniedException('Autocomplete list can`t be retrieved because the form element is disabled or read_only.'); } $class = $formAutocomplete->getConfig()->getOption('class'); $property = $formAutocomplete->getConfig()->getAttribute('property'); $minimumInputLength = $formAutocomplete->getConfig()->getAttribute('minimum_input_length'); $itemsPerPage = $formAutocomplete->getConfig()->getAttribute('items_per_page'); $reqParamPageNumber = $formAutocomplete->getConfig()->getAttribute('req_param_name_page_number'); $toStringCallback = $formAutocomplete->getConfig()->getAttribute('to_string_callback'); $searchText = $request->get('q'); if (mb_strlen($searchText, 'UTF-8') < $minimumInputLength) { return new JsonResponse(array('status' => 'KO', 'message' => 'Too short search string.'), 403); } $page = $request->get($reqParamPageNumber); $offset = ($page - 1) * $itemsPerPage; /** @var ModelManager $modelManager */ $modelManager = $formAutocomplete->getConfig()->getOption('model_manager'); $dm = $modelManager->getDocumentManager(); if ($class) { /** @var $qb \Doctrine\ODM\PHPCR\Query\Builder\QueryBuilder */ $qb = $dm->getRepository($class)->createQueryBuilder('a'); $qb->where()->fullTextSearch("a.{$property}", '*' . $searchText . '*'); $qb->setFirstResult($offset); //fetch one more to determine if there are more pages $qb->setMaxResults($itemsPerPage + 1); $query = $qb->getQuery(); $results = $query->execute(); } else { /** @var $qb \PHPCR\Util\QOM\QueryBuilder */ $qb = $dm->createPhpcrQueryBuilder(); // TODO: node type should probably be configurable $qb->from($qb->getQOMFactory()->selector('a', 'nt:unstructured')); $qb->where($qb->getQOMFactory()->fullTextSearch('a', $property, '*' . $searchText . '*')); // handle attribute translation $qb->orWhere($qb->getQOMFactory()->fullTextSearch('a', $dm->getTranslationStrategy('attribute')->getTranslatedPropertyName($request->getLocale(), $property), '*' . $searchText . '*')); $qb->setFirstResult($offset); //fetch one more to determine if there are more pages $qb->setMaxResults($itemsPerPage + 1); $results = $dm->getDocumentsByPhpcrQuery($qb->getQuery()); } //did we max out x+1 $more = count($results) == $itemsPerPage + 1; $method = $request->get('_method_name'); $items = array(); foreach ($results as $path => $document) { // handle child translation if (strpos(PathHelper::getNodeName($path), Translation::LOCALE_NAMESPACE . ':') === 0) { $document = $dm->find(null, PathHelper::getParentPath($path)); } if (!method_exists($document, $method)) { continue; } $label = $document->{$method}(); if ($toStringCallback !== null) { if (!is_callable($toStringCallback)) { throw new \RuntimeException('Option "to_string_callback" does not contain callable function.'); } $label = call_user_func($toStringCallback, $document, $property); } $items[] = array('id' => $admin->id($document), 'label' => $label); } return new JsonResponse(array('status' => 'OK', 'more' => $more, 'items' => $items)); }
/** * @param \Symfony\Component\HttpFoundation\Request $request * * @return \Symfony\Component\HttpFoundation\Response */ public function setObjectFieldValueAction(Request $request) { $field = $request->get('field'); $code = $request->get('code'); $objectId = $request->get('objectId'); $value = $request->get('value'); $context = $request->get('context'); $admin = $this->pool->getInstance($code); $admin->setRequest($request); // alter should be done by using a post method if (!$request->isXmlHttpRequest()) { return new JsonResponse(array('status' => 'KO', 'message' => 'Expected a XmlHttpRequest request header')); } if ($request->getMethod() != 'POST') { return new JsonResponse(array('status' => 'KO', 'message' => 'Expected a POST Request')); } $rootObject = $object = $admin->getObject($objectId); if (!$object) { return new JsonResponse(array('status' => 'KO', 'message' => 'Object does not exist')); } // check user permission if (false === $admin->isGranted('EDIT', $object)) { return new JsonResponse(array('status' => 'KO', 'message' => 'Invalid permissions')); } if ($context == 'list') { $fieldDescription = $admin->getListFieldDescription($field); } else { return new JsonResponse(array('status' => 'KO', 'message' => 'Invalid context')); } if (!$fieldDescription) { return new JsonResponse(array('status' => 'KO', 'message' => 'The field does not exist')); } if (!$fieldDescription->getOption('editable')) { return new JsonResponse(array('status' => 'KO', 'message' => 'The field cannot be edit, editable option must be set to true')); } $propertyAccessor = PropertyAccess::createPropertyAccessor(); $propertyPath = new PropertyPath($field); // If property path has more than 1 element, take the last object in order to validate it if ($propertyPath->getLength() > 1) { $object = $propertyAccessor->getValue($object, $propertyPath->getParent()); $elements = $propertyPath->getElements(); $field = end($elements); $propertyPath = new PropertyPath($field); } $propertyAccessor->setValue($object, $propertyPath, '' !== $value ? $value : null); $violations = $this->validator->validateProperty($object, $field); if (count($violations)) { $messages = array(); foreach ($violations as $violation) { $messages[] = $violation->getMessage(); } return new JsonResponse(array('status' => 'KO', 'message' => implode("\n", $messages))); } $admin->update($object); // render the widget // todo : fix this, the twig environment variable is not set inside the extension ... $extension = $this->twig->getExtension('sonata_admin'); $extension->initRuntime($this->twig); $content = $extension->renderListElement($rootObject, $fieldDescription); return new JsonResponse(array('status' => 'OK', 'content' => $content)); }
/** * {@inheritdoc} */ public function warmUp($cacheDir) { foreach ($this->pool->getAdminServiceIds() as $id) { $this->cache->load($this->pool->getInstance($id)); } }