Multiple links in Bazinga Hateoas with Symfony - symfony

I am using Bazinga Hateoas with Fosrest in one of my SF2 project.
In one of API call, I want to display link of friends with current user or user id supplied like this:
{
"_links": {
"self": { "href": "/users/1" },
"friends": [
{ "href": "/users/2" },
{ "href": "/users/3" },
]
},
}
I am using below code in Entity.User.yml file:
relations:
-
rel: self
href:
route: api_1_get_users
parameters:
id: expr(object.getId())
absolute: true
-
rel: expr(object.findFriends(object.getId()))
href:
route: api_1_get_users
parameters:
id: expr(object.getId())
absolute: true
I have put "findFriends" method in repository but its not accessible inside yml file. I guess this is not the correct way of doing things.
I have gone through https://github.com/willdurand/Hateoas but not able to figure out how to do it. Please guide me how I can achieve this...
Any help would be much appreciated !
Please guide me how I can achieve this

This is how you work with #RelationProvider.
/**
* Note:
* ====
* RelationProvider takes the method name which returns the relations.
*
* #Hateoas\RelationProvider("addRelations")
*/
class LinkContainingResource
{
public function addRelations($object, ClassMetadataInterface $classMetadata)
{
/**
* Important Note:
* ===============
* Relation is actually an Hateoas\Configuration\Relation object,
* NOT \Hateoas\Configuration\Annotation\Relation
*/
return [new Relation('relation_name', 'link1'),
new Relation('relation_name', 'link2'),
new Relation('relation_name', 'link3')];
}
}
Json/Hal Result:
{
"_links": {
"relation_name": [
{"href": "link1"},
{"href": "link2"},
{"href": "link3"}
]
}
}

Related

How can i write this JSON swagger-php below with annotations?

I'm using OpenAPI 3.0 to document my Symfony API. this is the JSON code that authenticates the user to send requests:
"securitySchemes": {
"Bearer": {
"type": "http",
"description": "Entrer le token JST",
"scheme": "bearer",
"bearerFormat": "JWT"
}
}
},
"security": [
{
"Bearer": []
}
]
How can i write this with annotations in controller ?
thanks
You almost can; currently swagger-php does not support the bearerFormat property.
=> https://github.com/zircote/swagger-php/issues/1258
The rest would look something like this globally.
/**
* #OA\SecurityScheme(
* securityScheme="bearerAuth",
* type="http",
* scheme="bearer",
* description="Entrer le token JST"
* )
*/
For a controller to require security you'd add this:
/**
* #OA\Get(
* path="/api/endpoint",
* ...
* security={{ "bearerAuth": {} }}
* )
*/

Alfresco dashlet post form

I have been searching google for a few days now and cant find any solution to my problem.
I have created a custom Alfresco Aukai dashlet and placed a form in it thus:
define(["dojo/_base/declare",
"dijit/_WidgetBase",
"alfresco/core/Core",
"alfresco/core/I18nUtils",
"alfresco/dashlets/Dashlet"],
function(declare, AlfCore, I18nUtils, Dashlet) {
// First define a form...
var form = {
name: "alfresco/forms/Form",
config: {
showOkButton: true,
okButtonLabel: "Save",
showCancelButton: false,
cancelButtonLabel: "Doesn't Matter",
okButtonPublishTopic: "PUBLISH_TOPIC",
okButtonPublishGlobal: true,
widgets: []
}
};
// Define a text box...
var textBox = {
name: "alfresco/forms/controls/DojoValidationTextBox",
config: {
fieldId: "EMAIL",
name: "email",
label: "Contact",
description: "Your e-mail address",
placeHolder: "name#address.com",
validationConfig: [
{
validation: "regex",
regex: "^([0-9a-zA-Z]([-.w]*[0-9a-zA-Z])*#([0-9a-zA-Z][-w]*[0-9a-zA-Z].)+[a-zA-Z]{2,9})$",
errorMessage: "Valid E-mail Address Required"
}
]
}
};
form.config.widgets.push(textBox);
// Define a checkbox...
var checkbox = {
name: "alfresco/forms/controls/DojoCheckBox",
config: {
fieldId: "SHOW",
name: "showEmail",
label: "Show E-mail",
description: "Uncheck to hide the e-mail field",
value: true
}
};
form.config.widgets.push(checkbox);
// Update the textbox to respond to checkbox changes...
textBox.config.visibilityConfig = {
initialValue: true,
rules: [
{
targetId: "SHOW",
is: [true]
}
]
};
return declare([Dashlet], {
/*
* Add padding to the body.
* smallpad (4px padding), mediumpad (10px padding - recommended) and largepad (16px padding)
*/
additionalCssClasses: "mediumpad",
/**
* Explicit height in pixels of the Dashlet body.
*/
bodyHeight: 200,
/**
* Id that will be used to store properties for this Dashlet.
* i.e. the Dashlet height when using the resizer.
*/
componentId: "component.messaging-dashlet",
/**
* The i18n scope to use for this Dashlet.
*/
i18nScope: "dashlets.MessagingDashlet",
/**
* An array of the i18n files to use with this Dashlet.
*/
i18nRequirements: [{i18nFile: "./i18n/MessagingDashlet.properties"}],
/**
* The widgets to be acting as title bar actions.
*/
widgetsForTitleBarActions: [
{
id: "MESSAGING_DASHLET_ACTIONS",
name: "alfresco/html/Label",
config: {
label: "Title-bar actions"
}
}
],
/**
* The widgets to be placed in the top toolbar.
*/
widgetsForToolbar: [
{
id: "MESSAGING_DASHLET_TOOLBAR",
name: "alfresco/html/Label",
config: {
label: "Toolbar"
}
}
],
/**
* The widgets to be placed in the body of the dashlet.
*/
widgetsForBody: [
{
id: "HELLO_DASHLET_VERTICAL_LAYOUT",
name: "alfresco/layout/VerticalWidgets",
config: {
widgetWidth: 50,
widgets: [
form
]
}
}
]
});
});
The form is displayed but the Save button is inactive.
When checking with fire bug I am informed that there is no javascript on the page!
Apart from this every thing works fine even get the error message when an invalid email address is entered.
Any suggestions? Alternatively a working example of a form in a dashlet posting would be nice :)
I have looked into this further while trying to populate a select box with the OptionsService.
optionsConfig: {
publishTopic: "ALF_GET_FORM_CONTROL_OPTIONS",
publishPayload: {
url: url.context + "/proxy/alfresco/api/people",
itemsAttribute: "people",
labelAttribute: "firstName",
valueAttribute: "userName"
}
In the debug I can see this but nothing happens over the wire, there is not corresponding request sent to the server!
This is the same issue I am getting with the post so this would suggest a more fundamental problem with my code.
Your form does not have any widgets / controls defined.
okButtonPublishGlobal: true,
widgets: []
Please refer simple CRUD example using aikau, http://www.codingfreaks.net/2015/03/aikau-form-example-for-simple-student.html
https://github.com/Alfresco/Aikau/blob/develop/aikau/src/main/resources/alfresco/reports/TopSiteContributorReport.js
Can you please look at this link?
Alfresco share is using Site Contributor Breakdown Dashlet, with the input form controls using aikau.

How to have absolute url with Hateoas Bundle on Symfony2

I just fresh installed the bundle from Willdurant's github and I got relative url like this:
"_links": {
"self": {
"href": "/1.0/users/?page=1&limit=10"
},
"first": {
"href": "/1.0/users/?page=1&limit=10"
},
"last": {
"href": "/1.0/users/?page=2&limit=10"
},
"next": {
"href": "/1.0/users/?page=2&limit=10"
}
}
For my Hateoas url, I really prefer absolute url but I don't find anything on google to change that. 2 hours of search, trying multiple keywords and nothing...
Thanks for your help.
You can use it many ways. All depends on the way of generating links.
If you use #Route annotation to generate link, it has a parameter absolute that need to be set to true:
/**
* #Hateoas\Relation(
* name = "self",
* href = #Hateoas\Route(
* "user_get",
* parameters = { "id" = "expr(object.getId())" },
* absolute = true
* )
* )
*/
If you use expression language to generate link you can pass true as third parameter to the link() function:
/**
* #Hateoas\Relation(
* "user",
* href = "expr(link(object.getUser(), 'self', true))"
* )
*/

Drupal 8 - How to add task and contextual links for specific node type?

I created a task link and a contextual one for base_route: entity.node.canonical
mymodule.routing.yml
mymodule.mycustomroute:
path: '/node/{node}/custom-path'
defaults:
_form: '\Drupal\mymodule\Form\MyForm'
requirements:
_permission: 'my permission'
node: '[0-9]+'
mymodule.links.tasks.yml
mymodule.mycustomroute:
route_name: mymodule.mycustomroute
base_route: entity.node.canonical
title: 'my title'
mymodule.links.contextual.yml
mymodule.mycustomroute:
route_name: mymodule.mycustomroute
group: node
My link shows up next to View / Edit / Delete links on each node as I wanted.
Now I am wondering how is it possible to make these links available only for specific node type(s)?
mymodule/mymodule.routing.yml :
mymodule.mycustomroute:
path: '/node/{node}/custom-path'
defaults:
_form: '\Drupal\mymodule\Form\MyForm'
requirements:
_permission: 'my permission'
_custom_access: '\Drupal\mymodule\Access\NodeTypeAccessCheck::access'
_node_types: 'node_type_1,node_type_2,node_type_n'
node: '\d+'
mymodule/src/Access/NodeTypeAccessCheck.php :
namespace Drupal\mymodule\Access;
use Drupal\Core\Access\AccessCheckInterface;
use Drupal\Core\Access\AccessResult;
use Drupal\node\NodeInterface;
use Symfony\Component\Routing\Route;
/**
* Check the access to a node task based on the node type.
*/
class NodeTypeAccessCheck implements AccessCheckInterface {
/**
* {#inheritdoc}
*/
public function applies(Route $route) {
return NULL;
}
/**
* A custom access check.
*
* #param \Drupal\node\NodeInterface $node
* Run access checks for this node.
*/
public function access(Route $route, NodeInterface $node) {
if ($route->hasRequirement('_node_types')) {
$allowed_node_types = explode(',', $route->getRequirement('_node_types'));
if (in_array($node->getType(), $allowed_node_types)) {
return AccessResult::allowed();
}
}
return AccessResult::forbidden();
}
}
Or you can specify route parameters in the mymodule.links.menu.yml file:
mymodule.add_whatever:
title: 'Add whatever'
description: 'Add whatever'
route_name: node.add
route_parameters: { node_type: 'name_of_node_type' }
menu_name: main
weight: 7

Gedmo\Translatable ignoring default locale that is set in config

I've decided to install "gedmo/doctrine-extensions" on Symfony to use Translatable.
It works fine, except that listener is ignoring default locale I've specified, always falling back to en_US.
Translatable is plugged in as service:
#config.yml
services:
gedmo.listener.translatable:
class: Gedmo\Translatable\TranslatableListener
tags:
- { name: doctrine.event_subscriber, connection: default }
calls:
- [ setAnnotationReader, [ #annotation_reader ] ]
- [ setDefaultLocale, [ ru ] ]
- [ setTranslationFallback, [ true ] ]
And when I try to find() object in database it always fetches en_US instead of ru:
$test = $em->find('Vendor\Entity\Test', 1);
//outputs row with 'locale' = "en_US" from `ext_translations_test` table
Only if I specify language directly, like:
$test->setTranslatableLocale('ru');
$em->refresh($test);
It gives desired translation.
Is there any way to set default locale that will work?
UPDATE
Adding another call function in config.yml fixed the problem, altough now I'm not quite sure what setDefaultLocale() function actually does, as Gedmo\Translatable\TranslatableListener::$defaultLocale property provided with a most horrid commentary I've ever seen. Will try to find more...
services:
gedmo.listener.translatable:
class: Gedmo\Translatable\TranslatableListener
tags:
- { name: doctrine.event_subscriber, connection: default }
calls:
- [ setAnnotationReader, [ #annotation_reader ] ]
- [ setDefaultLocale, [ ru ] ]
- [ setTranslatableLocale, [ ru ] ]
- [ setTranslationFallback, [ true ] ]
According to: https://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/symfony2.md
Note: if you noticed, there's Acme\DemoBundle\Listener\DoctrineExtensionListener you will need to create this listener class if you use loggable or translatable behaviors. This listener will set the locale used from request and username to loggable. So, to finish the setup create Acme\DemoBundle\Listener\DoctrineExtensionListener
Make sure you have setup the kernel listener as well.
namespace Acme\DemoBundle\Listener;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
class DoctrineExtensionListener implements ContainerAwareInterface
{
/**
* #var ContainerInterface
*/
protected $container;
public function setContainer(ContainerInterface $container = null)
{
$this->container = $container;
}
public function onLateKernelRequest(GetResponseEvent $event)
{
$translatable = $this->container->get('gedmo.listener.translatable');
$translatable->setTranslatableLocale($event->getRequest()->getLocale());
}
public function onKernelRequest(GetResponseEvent $event)
{
$securityContext = $this->container->get('security.context', ContainerInterface::NULL_ON_INVALID_REFERENCE);
if (null !== $securityContext && null !== $securityContext->getToken() && $securityContext->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
$loggable = $this->container->get('gedmo.listener.loggable');
$loggable->setUsername($securityContext->getToken()->getUsername());
}
}
}
And add the following to your config file:
services:
extension.listener:
class: Acme\DemoBundle\Listener\DoctrineExtensionListener
calls:
- [ setContainer, [ #service_container ] ]
tags:
# translatable sets locale after router processing
- { name: kernel.event_listener, event: kernel.request, method: onLateKernelRequest, priority: -10 }
# loggable hooks user username if one is in security context
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }

Resources