Commit 5f25845b by Mathieu Poisbeau

Merge branch 'dev' into 'master'

Sync partners from Kohinos to WP: commits realizing #255, #22 and #17 Dev

See merge request cooperatic/kohinos!1
parents 5166823e 20d30630
......@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "d4a364a00fe16a9ef06a06f73973f4f4",
"content-hash": "215d9dc36bb4f8c0921209bb611d636c",
"packages": [
{
"name": "api-platform/api-pack",
......@@ -1967,6 +1967,7 @@
"reflection",
"static"
],
"abandoned": "roave/better-reflection",
"time": "2020-03-27T11:06:43+00:00"
},
{
......@@ -2423,6 +2424,7 @@
"faker",
"fixtures"
],
"abandoned": true,
"time": "2019-12-12T13:22:17+00:00"
},
{
......@@ -10018,6 +10020,83 @@
"time": "2020-06-12T08:10:13+00:00"
},
{
"name": "symfony/http-client",
"version": "v4.4.16",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-client.git",
"reference": "3ead7e297f4cc8a84661ef1f411c029acb34bc11"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-client/zipball/3ead7e297f4cc8a84661ef1f411c029acb34bc11",
"reference": "3ead7e297f4cc8a84661ef1f411c029acb34bc11",
"shasum": ""
},
"require": {
"php": ">=7.1.3",
"psr/log": "^1.0",
"symfony/http-client-contracts": "^1.1.10|^2",
"symfony/polyfill-php73": "^1.11",
"symfony/service-contracts": "^1.0|^2"
},
"provide": {
"php-http/async-client-implementation": "*",
"php-http/client-implementation": "*",
"psr/http-client-implementation": "1.0",
"symfony/http-client-implementation": "1.1"
},
"require-dev": {
"guzzlehttp/promises": "^1.3.1",
"nyholm/psr7": "^1.0",
"php-http/httplug": "^1.0|^2.0",
"psr/http-client": "^1.0",
"symfony/dependency-injection": "^4.3|^5.0",
"symfony/http-kernel": "^4.4.13",
"symfony/process": "^4.2|^5.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\HttpClient\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony HttpClient component",
"homepage": "https://symfony.com",
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-10-24T11:50:19+00:00"
},
{
"name": "symfony/http-client-contracts",
"version": "v2.3.1",
"source": {
......
......@@ -96,7 +96,7 @@ security:
switch_user:
provider: fos_userbundle
# access_denied_handler: App\Security\AccessDeniedHandler
encoders:
# FOS\UserBundle\Model\UserInterface: bcrypt
FOS\UserBundle\Model\UserInterface:
......@@ -109,22 +109,25 @@ security:
ROLE_API: ROLE_USER
ROLE_ADHERENT: ROLE_USER
ROLE_PRESTATAIRE: ROLE_USER
ROLE_ADMIN_SIEGE: [ROLE_USER, ROLE_ADMIN]
ROLE_ADMIN_SIEGE: [ROLE_USER, ROLE_ADMIN, ROLE_ADMIN_COMPTOIR_READER]
ROLE_REDACTEUR: [ROLE_USER, ROLE_ADMIN]
ROLE_TRESORIER: [ROLE_USER, ROLE_ADMIN]
ROLE_TRESORIER: [ROLE_USER, ROLE_ADMIN, ROLE_ADMIN_COMPTOIR_READER]
ROLE_CONTROLEUR: [ROLE_USER, ROLE_ADMIN]
ROLE_GESTION_GROUPE: [ROLE_USER, ROLE_ADMIN]
ROLE_COMPTOIR: [ROLE_USER, ROLE_ADMIN]
ROLE_CONTACT: [ROLE_USER, ROLE_ADMIN]
ROLE_ADMIN: [ROLE_USER, ROLE_SONATA_ADMIN]
ROLE_ADMIN_COMPTOIR_READER:
- ROLE_ADMIN_COMPTOIR_GERER_LIST
- ROLE_ADMIN_COMPTOIR_GERER_VIEW
ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH, ROLE_API]
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
#
#
# @TODO : better access control !
#
#
# Admin login page needs to be accessed without credential
# - { path: ^/admin/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
# - { path: ^/admin/logout$, role: IS_AUTHENTICATED_ANONYMOUSLY }
......
......@@ -93,7 +93,7 @@ services:
app.twig.main.extension:
class: App\Twig\AppExtension
autowire: false
arguments: ["@service_container", "@security.helper", "@doctrine.orm.entity_manager", "@knp_paginator", "@session"]
arguments: ["@service_container", "@security.helper", "@doctrine.orm.entity_manager", "@knp_paginator", "@session", "@app.util.wordpress"]
app.twig.mlc.globals.extension:
class: App\Twig\MlcGlobalsExtension
......@@ -173,6 +173,9 @@ services:
tags:
- { name: payum.gateway_factory_builder, factory: payzen }
app.util.wordpress:
class: App\Util\WordpressUtil
###### Configuration de l'admin ######
admin.block.dashboard:
......@@ -238,7 +241,10 @@ services:
admin.prestataire.gerer:
class: App\Admin\PrestataireAdmin
arguments: [~, App\Entity\Prestataire, ~]
arguments:
- ~
- App\Entity\Prestataire
- App\Controller\CRUD\PrestataireCRUDController
tags:
- name: sonata.admin
manager_type: orm
......@@ -250,6 +256,7 @@ services:
- [ addChild, ['@sonata.user.admin.user', 'user']]
- [ setSecurity, ['@security.helper']]
- [ setPool, ['@sonata.media.pool']]
- [ setWordpressUtil, ['@app.util.wordpress']]
admin.prestataire.cotisations:
class: App\Admin\CotisationPrestataireAdmin
......
......@@ -639,6 +639,10 @@ App\Entity\GlobalParameter:
name: "USE_PAYZEN"
value: 'true'
mandatory: 1
gp16:
name: "WORDPRESS_URL"
value: ''
mandatory: 0
App\Entity\Siege:
siege_1:
......
<IfModule mod_rewrite.c>
Options -MultiViews
RewriteEngine On
# Https redirection
RewriteEngine on
RewriteCond %{HTTPS} !=on
RewriteRule .* https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
# Https redirection for OVH
RewriteEngine on
RewriteCond %{SERVER_PORT} 80
RewriteRule .* https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
# Https redirection for Gandi
RewriteEngine on
RewriteCond %{REQUEST_SCHEME} =http
RewriteRule .* https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php [QSA,L]
</IfModule>
......
<IfModule mod_rewrite.c>
Options -MultiViews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php [QSA,L]
</IfModule>
<IfModule !mod_rewrite.c>
<IfModule mod_alias.c>
RedirectMatch 302 ^/$ /index.php/
</IfModule>
</IfModule>
SetOutputFilter DEFLATE
AddOutputFilterByType DEFLATE "application/atom+xml" "application/javascript" "application/json" "application/ld+json" "application/manifest+json" "application/rdf+xml" "application/rss+xml" "application/schema+json" "application/vnd.geo+json" "application/vnd.ms-fontobject" "application/x-font-ttf" "application/x-javascript" "application/x-web-app-manifest+json" "application/xhtml+xml" "application/xml" "font/eot" "font/opentype" "image/bmp" "image/svg+xml" "image/vnd.microsoft.icon" "image/x-icon" "text/cache-manifest" "text/css" "text/html" "text/javascript" "text/plain" "text/vcard" "text/vnd.rim.location.xloc" "text/vtt" "text/x-component" "text/x-cross-domain-policy" "text/xml"
......@@ -53,7 +53,9 @@ class ComptoirAdmin extends AbstractAdmin
$query = parent::createQuery($context);
$user = $this->security->getUser();
if (empty($this->getRequest()->getSession()->get('_groupegere'))) {
if ($user->isGranted('ROLE_GESTION_GROUPE') || $user->isGranted('ROLE_CONTACT') || $user->isGranted('ROLE_TRESORIER')) {
// TODO: Pourquoi empêcher toutes les "requêtes comptoir" pour ces rôles ?
//if ($user->isGranted('ROLE_GESTION_GROUPE') || $user->isGranted('ROLE_CONTACT') || $user->isGranted('ROLE_TRESORIER')) {
if ($user->isGranted('ROLE_GESTION_GROUPE') || $user->isGranted('ROLE_CONTACT')) {
$query->andWhere('false = true');
}
} else {
......
......@@ -21,6 +21,7 @@ use App\Form\Type\CotisationFormType;
use App\Form\Type\GeolocPrestataireFormType;
use App\Form\Type\UserFormType;
use App\Form\Type\UserInfosFormType;
use App\Util\WordpressUtil;
use Doctrine\ORM\Query;
use FOS\CKEditorBundle\Form\Type\CKEditorType;
use FOS\UserBundle\Model\UserManagerInterface;
......@@ -65,6 +66,7 @@ class PrestataireAdmin extends AbstractAdmin
protected $baseRoutePattern = 'prestataire';
protected $security;
protected $pool;
protected $wp;
protected $datagridValues = [
// reverse order (default = 'ASC')
'_sort_order' => 'DESC',
......@@ -84,6 +86,11 @@ class PrestataireAdmin extends AbstractAdmin
$this->pool = $pool;
}
public function setWordpressUtil(WordpressUtil $wp)
{
$this->wp = $wp;
}
public function configure()
{
parent::configure();
......@@ -430,25 +437,15 @@ class PrestataireAdmin extends AbstractAdmin
{
unset($this->listModes['mosaic']);
$user = $this->security->getUser();
$isWordpressActivated = $this->getConfigurationPool()->getContainer()->get('doctrine')->getRepository(GlobalParameter::class)->val(GlobalParameter::USE_WORDPRESS);
if ($isWordpressActivated == 'false') {
$actions = [
'show' => ['template' => '@SonataAdmin/CRUD/list__action_showonfront.html.twig'],
'edit' => [
// You may add custom link parameters used to generate the action url
'link_parameters' => [
'full' => true,
]
]];
} else {
$actions = [
'edit' => [
// You may add custom link parameters used to generate the action url
'link_parameters' => [
'full' => true,
]
]];
$actions = ['edit' => ['link_parameters' => ['full' => true]]];
if (false === $this->wp->isUsed()) {
$actions['show'] = ['template' => '@SonataAdmin/CRUD/list__action_showonfront.html.twig'];
} elseif ($this->wp->canSync()) {
$actions['sync-wp'] = [];
}
$listMapper
->addIdentifier('raison')
->add('groupe', null, array(
......@@ -485,6 +482,22 @@ class PrestataireAdmin extends AbstractAdmin
protected function configureRoutes(RouteCollection $collection)
{
$collection->remove('delete');
if ($this->wp->canSync()) {
$collection->add('sync-wp', $this->getRouterIdParameter().'/sync-wp');
$collection->add('sync-all-wp');
}
}
public function configureActionButtons($action, $object = null)
{
$list = parent::configureActionButtons($action, $object);
if ($this->wp->canSync()) {
$list['sync-all-wp']['template'] = '@SonataAdmin/CRUD/sync-all-wp_button.html.twig';
}
return $list;
}
public function getObjectMetadata($object)
......@@ -502,11 +515,18 @@ class PrestataireAdmin extends AbstractAdmin
return new Metadata($object->getRaison(), strip_tags($object->getDescription()));
}
public function getBatchActions()
public function configureBatchActions($actions)
{
$actions = parent::getBatchActions();
unset($actions['delete']);
if ($this->wp->canSync()) {
$actions['sync-wp'] = [
'ask_confirmation' => true,
'label' => 'batch_sync-wp',
'translation_domain' => 'SonataAdminBundle',
];
}
return $actions;
}
......@@ -523,4 +543,20 @@ class PrestataireAdmin extends AbstractAdmin
'Tags' => 'etatsString'
];
}
/**
* Synchronize with Wordpress here (rather than through Doctrine ORM events).
*/
public function postPersist($presta)
{
if ($this->wp->runSync($presta->getId())) {
$this->getRequest()->getSession()->getFlashBag()->add(
'sonata_flash_success', 'Synchronisation vers Wordpress effectuée<br>'
);
}
}
public function postUpdate($presta)
{
$this->postPersist($presta);
}
}
<?php
namespace App\Controller\CRUD;
use App\Util\WordpressUtil;
use Sonata\AdminBundle\Datagrid\ProxyQueryInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
class PrestataireCRUDController extends CRUDController
{
protected $wp;
public function __construct(WordpressUtil $wp)
{
$this->wp = $wp;
}
/**
* @param array|int|null If $id is null, sync all the Prestataires
*/
protected function syncToWordpress($id)
{
if ($this->wp->runSync($id)) {
$this->addFlash('sonata_flash_success', 'Synchronisation vers Wordpress effectuée');
}
return new RedirectResponse(
$this->admin->generateUrl('list', ['filter' => $this->admin->getFilterParameters()])
);
}
public function syncAllWpAction()
{
return $this->syncToWordpress(null);
}
public function syncWpAction($id)
{
if (!$this->admin->hasSubject()) {
throw new NotFoundHttpException(sprintf('Unable to find the Prestataire with id: %s', $id));
}
return $this->syncToWordpress($id);
}
public function batchActionSyncWp(ProxyQueryInterface $query)
{
$ids = [];
$selected = $query->execute();
foreach ($selected as $e) {
$ids[] = $e->getId();
}
return $this->syncToWordpress($ids);
}
}
......@@ -189,13 +189,16 @@ class Comptoir
$this->contacts = $contacts;
return $this;
}
/**
* @param ContactComptoir $contact
* @return $this
*/
public function addContact(ContactComptoir $contact): self
{
if (is_null($this->contacts)) {
$this->contacts = new ArrayCollection();
}
if (!$this->contacts->contains($contact)) {
$this->contacts[] = $contact;
$contact->setComptoir($this);
......
......@@ -29,6 +29,7 @@ class GlobalParameter
const MAP_ZOOM = 'MAP_ZOOM';
const USE_PAYZEN = 'USE_PAYZEN';
const APP_TITLE = 'APP_TITLE';
const WORDPRESS_URL = 'WORDPRESS_URL';
/**
* @ORM\Id()
......
......@@ -203,7 +203,7 @@ class Prestataire
* @Groups({"read", "write"})
*/
private $groupeprestataires;
/**
* EtatPrestataire $etats
* @var ArrayCollection|EtatPrestataire[]
......@@ -421,7 +421,7 @@ class Prestataire
$this->geolocs = $geolocs;
return $this;
}
/**
* @param GeolocPrestataire $geoloc
* @return $this
......@@ -466,13 +466,16 @@ class Prestataire
$this->contacts = $contacts;
return $this;
}
/**
* @param ContactPrestataire $contact
* @return $this
*/
public function addContact(ContactPrestataire $contact): self
{
if (is_null($this->contacts)) {
$this->contacts = new ArrayCollection();
}
if (!$this->contacts->contains($contact)) {
$this->contacts[] = $contact;
$contact->setPrestataire($this);
......@@ -500,7 +503,7 @@ class Prestataire
{
return $this->users;
}
public function getUsersString()
{
return join(' - ', array_map(function ($user) {
......@@ -673,7 +676,7 @@ class Prestataire
{
return $this->etats;
}
public function getEtatsString(): ?string
{
return join(' - ', array_map(function ($etat) {
......@@ -763,7 +766,7 @@ class Prestataire
{
return $this->tauxreconversion;
}
/**
* Set tauxreconversion
* @return $this
......
......@@ -18,6 +18,7 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\Email as EmailConstraint;
use Symfony\Component\Validator\Constraints\Type as TypeConstraint;
use Symfony\Component\Validator\Constraints\Regex as RegexConstraint;
use Symfony\Component\Validator\Constraints\Url as UrlConstraint;
class GlobalConfigurationFormType extends AbstractType
{
......@@ -134,6 +135,15 @@ class GlobalConfigurationFormType extends AbstractType
],
'help' => 'Si cette option est activée (true), les utilisateurs pourront cotiser et acheter de la monnaie locale par CB sur l\'application.'
))
->add('wordpressurl', GlobalParameterType::class, array(
'label' => "URL du site Wordpress :",
'name_param' => GlobalParameter::WORDPRESS_URL,
'_placeholder' => 'https://mon-wordpress-mlcc.fr',
'constraints_param' => [
new UrlConstraint(['message' => 'URL invalide !'])
],
'help' => "Si une URL Wordpress est renseignée, la synchronisation des prestataires (de Kohinos vers Wordpress) pourra se faire."
))
;
}
......
......@@ -12,6 +12,7 @@ use App\Entity\Rubrique;
use App\Entity\Siege;
use App\Entity\User;
use App\Entity\GlobalParameter;
use App\Util\WordpressUtil;
use Doctrine\ORM\EntityManagerInterface;
use Knp\Component\Pager\PaginatorInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
......@@ -31,14 +32,16 @@ class AppExtension extends AbstractExtension
public $container;
public $paginator;
public $session;
public $wp;
public function __construct(ContainerInterface $container, Security $security, EntityManagerInterface $em, PaginatorInterface $paginator, SessionInterface $session)
public function __construct(ContainerInterface $container, Security $security, EntityManagerInterface $em, PaginatorInterface $paginator, SessionInterface $session, WordpressUtil $wp)
{
$this->em = $em;
$this->security = $security;
$this->container = $container;
$this->paginator = $paginator;
$this->session = $session;
$this->wp = $wp;
}
public function getFunctions()
......@@ -87,12 +90,7 @@ class AppExtension extends AbstractExtension
public function getWordpressApiKey()
{
$users = $this->em->getRepository(User::class)->findByRole('ROLE_API');
if (count($users) <= 0) {
// @TODO :erreur => crée un nouvel utilisateur API si non existant ?
return '';
}
return $users[0]->getApiKey();
return $this->wp->getApiKey();
}
public function showModalGroupChoice()
......
<?php
namespace App\Util;
use App\Entity\User;
use App\Entity\GlobalParameter;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
class WordpressUtil
{
protected $em;
protected $client;
protected $apiKey = null;
protected $isUsed = null;
protected $url = null;
public function __construct(EntityManagerInterface $em, HttpClientInterface $client)
{
$this->em = $em;
$this->client = $client;
}
public function getApiKey(): string
{
if (null !== $this->apiKey) {
return $this->apiKey;
}
$users = $this->em->getRepository(User::class)->findByRole('ROLE_API');
return $this->apiKey = $users[0]->getApiKey() ?? '';
}
public function getUrl(): string
{
if (null !== $this->url) {
return $this->url;
}
return $this->url = $this->em->getRepository(GlobalParameter::class)
->val(GlobalParameter::WORDPRESS_URL);
}
/**
* Determine whether Wordpress is used or not.
*
* @return bool
*/
public function isUsed(): bool
{
if (null !== $this->isUsed) {
return $this->isUsed;
}
return $this->isUsed = (
'true' === $this->em->getRepository(GlobalParameter::class)
->val(GlobalParameter::USE_WORDPRESS)
? true
: false
);
}
/**
* Determine whether synchronization with Wordpress is available or not.
*
* @return bool
*/
public function canSync(): bool
{
return $this->isUsed() && $this->getUrl();
}
/**
* Run the synchronization with Wordpress, through an API call.
* Actually, only 'Prestataires' can be sync.
*
* @param array|int|null $id If null, ask for a full sync.
*
* @return bool Return false if the sync isn't run. Else true.
*/
public function runSync($id = null): bool
{
if (! $this->canSync()) {
return false;
}
$endpoint = sprintf("%s/%s/%s",
$this->getUrl(),
"wp-json/wosmpl/v1/sync_partners",
null === $id ? '' : ('?ids=' . \implode(',', (array) $id))
);
$this->client->request('GET', $endpoint, [
'headers' => [
'API-AUTH-TOKEN' => $this->getApiKey(),
],
]);
return true;
}
}
......@@ -663,6 +663,9 @@
"ref": "2230e9f42b10616b91a28d15ed3a2d984e0b6c10"
}
},
"symfony/http-client": {
"version": "v4.4.16"
},
"symfony/http-client-contracts": {
"version": "v2.3.1"
},
......
{% if admin.hasRoute('sync-wp') %}
{% set confirmText = 'confirm_sync-wp'|trans({}, 'SonataAdminBundle') %}
<a href="{{ admin.generateObjectUrl('sync-wp', object) }}" class="btn btn-sm btn-default sync-wp_link"
title="{{ 'action_sync-wp'|trans({}, 'SonataAdminBundle') }}"
onclick="return confirm('{{- confirmText -}}')"
>
<i class="fa fa-refresh" aria-hidden="true"></i>
{{ 'action_sync-wp'|trans({}, 'SonataAdminBundle') }}
</a>
{% endif %}
{% if admin.hasRoute('sync-all-wp') %}
{% set confirmPhrase = 'confirm_phrase_sync-all-wp'|trans({}, 'SonataAdminBundle') %}
{% set promptText = 'confirm_sync-all-wp'|trans({'%phrase%': confirmPhrase}, 'SonataAdminBundle') %}
<li>
<a class="sonata-action-element danger" href="{{ admin.generateUrl('sync-all-wp') }}"
onclick="return '{{-confirmPhrase-}}' == prompt('{{ promptText }}')"
>
<i class="fa fa-refresh" aria-hidden="true"></i>
{{ 'action_sync-all-wp'|trans({}, 'SonataAdminBundle') }}
</a>
</li>
{% endif %}
app.admin.group.adherent: Adherent
app.admin.group.prestataire: Prestataire
app.admin.group.groupe: Groupe
###
# About Wordpress synchronisation
###
batch_sync-wp: 'Synchronisation Wordpress'
action_sync-wp: 'Sync. WP'
action_sync-all-wp: 'Tout synchroniser vers Wordpress'
# In the 3 following messages, please escape the " and ' characters.
confirm_sync-wp: 'Êtes-vous sûrs de vouloir synchroniser vers Wordpress ?'
confirm_sync-all-wp: 'Attention, vous vous apprêtez à TOUT synchroniser vers Wordpress.\nTapez \"%phrase%\" pour confirmer.'
confirm_phrase_sync-all-wp: 'Je confirme'
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment