# CapDataOpera - plugin pour WordPress 

[![Pipeline badge](https://gitlab.com/capdataopera/capdataopera-wordpress-plugin/badges/develop/pipeline.svg)](https://gitlab.com/capdataopera/capdataopera-wordpress-plugin) [![Coverage badge](https://gitlab.com/capdataopera/capdataopera-wordpress-plugin/badges/develop/coverage.svg)](https://gitlab.com/capdataopera/capdataopera-wordpress-plugin) 

> Plugin WordPress permettant de préparer les données de son site web en vue de l'export RDF basé sur l’ontologie CapDataOpera : https://ontologie.capdataculture.fr/v1/owl/.

Le plugin pour WordPress permet : 

- [x] aux utilisateurs du plugin WordPress de définir les données à exporter en faisant un mapping (via des hooks) entre ses *custom post type* propre à son site et les entités du Feed **RDF**.
- [x] de générer un **RDF** sur un feed accessible à travers une URL

<!-- TOC start -->

Pour une utilisation simplifié, veuillez vou referé à la [documentation simplifié du plugin](https://gitlab.com/capdataopera/capdataopera-wordpress-plugin/-/blob/main/quickStart.md).

**Sommaire**

* [Description](#description)
* [Configuration requise](#configuration-requise)
* [Utilisation](#utilisation)
    + [Installation](#installation)
    + [Exemple d'utilisation](#exemple-dutilisation)
    + [Les entités supportées](#les-entites-supportees)
* [Plus en détails](#plus-en-détails)
    + [Référence des hooks](#reference-des-hooks)
    + [Référence des fonctions](#reference-des-fonctions)
* [Comment contribuer au projet](#comment-contribuer-au-projet)
* [Crédits](#crédits)

<!-- TOC end -->

## Description

Ce plugin WordPress permet aux maisons d’opéra et aux théâtres d’exporter au format RDF les données de leur programmation 
sur le schéma de l'ontologie CapDataOpera.

Ce plugin intègre le SDK PHP CapDataOpera dans votre installation WordPress et offre des hooks pour récolter les données
de votre installation et les mapper sur les entités du SDK.

Découvrez comment utiliser le SDK PHP https://gitlab.com/capdataopera/capdataopera-php-sdk

## Configuration requise

- [x] **Version actuelle** : 0.1.11
- [x] **PHP** : 7.4 ou plus
- [x] **WordPress** : 6.0 ou plus

## Utilisation

### Installation

##### Via composer

```bash
composer require capdataopera/wordpress-plugin
```
##### Manuellement via le répertoire de plugin WordPress

Le moyen le plus simple d’installer le plugin consiste à utiliser le répertoire WordPress Plugin. Si vous disposez d'une installation WordPress existante et que vous souhaitez ajouter CapdataOpera plugin WordPress, récupérer le plugin sur le lien https://fr.wordpress.org/plugins/capdataopera-wordpress-plugin/

### Exemple d'utilisation

Voici un exemple d'utilisation du plugin pour générer un feed au format **rdfxml** sur l'installation d'un site WordPress nommée https://mon-site.com/mon-lien-feeds.

><font color="red">IMPORTANT</font>: avant de commencer, assurez-vous que votre répertoire d'uploads est accessible en lecture/écriture pour le serveur web. Sinon, votre site risque de retourner une erreur fatale lors de la génération des flux (car ce plugin WordPress a besoin de ce répertoire pour effectuer les caches)..

*Toutes les codes suivants sont à ajouter dans le fichier **functions.php***

1. Configurer le lien d'accès au feed RDF

> On utilise le hook filter *capdata/get_rdf_route* pour définir la route du feed (par défaut : *feeds/programmation.rdf*).

```php
<?php

add_filter( 'capdata/get_rdf_route', 'configure_my_route' );
 
function configure_my_route(): string 
{
    return 'mon-lien-feeds';
}
```

2. Choisir les données à exporter par entités du feed

> On utilise le hook filter *capdata/get_datas_xxx* pour indiquer les données à charger par entité (ou **xxx** correspond au nom de l'entité à charger, par exemple personnes, lieux, événements, productions, type_evenement, etc). 

> La liste des hooks pour charger les données de chaque entité sont [ici](#reference-des-hooks) 

```php
<?php

add_filter( 'capdata/get_datas_personnes', 'load_my_persons_datas', 10, 1 );

add_filter( 'capdata/get_datas_lieux', 'load_my_locations_datas', 10, 1 );


function load_my_persons_datas($defaults): array 
{
    $file = __DIR__ . '/fixture/person.json';
    $personnes = json_decode(file_get_contents($file));
    ...

    return $personnes;
}

function load_my_locations_datas($defaults): array
{
    $file = __DIR__ . '/fixture/location.json';
    $lieux = json_decode(file_get_contents($file));
    ...

    return $lieux;
}
...
```
*Ces fonctions callback des hooks (ex: load_my_persons_datas) sont à définir par entité. S'ils ne sont pas définis, le plugin chargera les données du post type (ou la taxonomy) ayant le même nom que l’entité.*

*Par exemple :*
- L'entité **Personne** sera chargée par défaut à partir du post type **personnes**
- L'entité **Evenement** sera chargée par défaut à partir du post type **evenements**
- L'entité **Fonction** sera chargée par défaut à partir du taxonomy **fonctions**
- etc

<font color="red">IMPORTANT : les utilisateurs du plugin sont libres de choisir le format des données utilisées pour alimenter chaque entité (*tableau d'objets* ou *tableau associatif*), le seul prérequis étant qu'elles disposent d'un identifiant (**id** ou **ID** ou **term_id**).
</font>

Sinon, une exception <font color="red">*UnexpectedValueException*</font> sera levée dans le cas où l'identifiant est manquant.

3. Traiter les mappings pour chaque entité utilisée

> C'est ici que l'on traite les données chargées et que l'on y applique les mappings.

Pour cela, on utilise les hooks filter en fonctions des entités exploitées sur le site, par exemple le hook filter `capdata/get_transformed_lieu` est utilisé pour parser et mapper l'entité **Lieu**.\
Le filter attend deux paramètres **$result** et **$source** :\
a. **object $result**: instance de l'entité correspondante dans le SDK \
b. **mixed $source**: données récupérées par le hook filter *capdata/get_datas_xxx*.
```php
<?php

use CapDataOpera\PhpSdk\Model\Personne;
use CapDataOpera\PhpSdk\Model\Lieu;

add_filter( 'capdata/get_transformed_lieu', 'func_mapping_for_lieu', 10, 2 );
 
function func_mapping_for_lieu(Lieu $result, $source): Lieu 
{
    /* ... */
    $result->setName($source->nom_lieu);
    $result->setDescription($source->description_lieu);

    return $result;
}

add_filter( 'capdata/get_transformed_personne', 'func_mapping_for_personne', 10, 2 );
 
function func_mapping_for_personne(Personne $result, $source): Personne 
{
    /* ... */
    $result->setPrenom($source->field_prenom);
    $result->setNom($source->field_nom);
    $result->setBiographie($source->field_description);
    $result->setDateCreationRessource(capdata_get_immutable_date($source->created_date));
    $result->setDateModificationRessource(capdata_get_immutable_date($source->modified_date));
    
    return $result;
}
/* ... */
```
>La variable **$result** est une instance de l'entité correspondante dans le SDK, initiée avec l'URI de la ressource à partir de l'identifiant préalablement requis.

Les assignations des données (setters) correspondent exactement aux cas d'utilisation du SDK PHP (découvrez comment utiliser le SDK PHP https://gitlab.com/capdataopera/capdataopera-php-sdk#exemple-dutilisation). \
Particulièrement pour les sous-entités (entités utilisées dans d'autres entités), il est recommandé d'utiliser directement le helper *capdata_get_instance* afin d'éviter de réécrire le code pour créer une ressource URI de l'entité à partir d'un identifiant.

Chaque hook filter *capdata/get_transformed_xxx* doit retourner une instance de l'entité du SDK correspondante, par exemple pour le hook **capdata/get_transformed_personne** la valeur de retour de la fonction de callback `func_mapping_for_personne` doit être une instance de **CapDataOpera\PhpSdk\Model\Personne**.

Sinon et avec une valeur différente de **null**, une exception <font color="red">*UnexpectedValueException*</font> sera levée.

### Les entités supportées 

- [x] AdressePostale: CapDataOpera\PhpSdk\Model\AdressePostale
- [x] Collectivite: CapDataOpera\PhpSdk\Model\Collectivite
- [x] Fonction: CapDataOpera\PhpSdk\Model\Fonction
- [x] Lieu: CapDataOpera\PhpSdk\Model\Lieu
- [x] Media: CapDataOpera\PhpSdk\Model\Media (ne pas utiliser directement, utiliser les sous-classes)
- [x] Participation: CapDataOpera\PhpSdk\Model\Participation
- [x] Personne: CapDataOpera\PhpSdk\Model\Personne
- [x] Production: CapDataOpera\PhpSdk\Model\Production
- [x] Saison: CapDataOpera\PhpSdk\Model\Saison
- [x] Evenement: CapDataOpera\PhpSdk\Model\Evenement
- [x] ArkBnf: CapDataOpera\PhpSdk\Model\ArkBnf
- [x] Auteur: CapDataOpera\PhpSdk\Model\Auteur
- [x] CategorieOeuvre: CapDataOpera\PhpSdk\Model\CategorieOeuvre
- [x] Collaboration: CapDataOpera\PhpSdk\Model\Collaboration
- [x] GenreOeuvre: CapDataOpera\PhpSdk\Model\GenreOeuvre
- [x] HistoriqueProduction: CapDataOpera\PhpSdk\Model\HistoriqueProduction
- [x] Image: CapDataOpera\PhpSdk\Model\Image
- [x] Interpretation: CapDataOpera\PhpSdk\Model\Interpretation
- [x] Isni: CapDataOpera\PhpSdk\Model\Isni
- [x] MaitriseOeuvre: CapDataOpera\PhpSdk\Model\MaitriseOeuvre
- [x] MentionProduction: CapDataOpera\PhpSdk\Model\MentionProduction
- [x] Oeuvre: CapDataOpera\PhpSdk\Model\Oeuvre
- [x] Partenariat: CapDataOpera\PhpSdk\Model\Partenariat
- [x] Pays: CapDataOpera\PhpSdk\Model\Pays
- [x] ProductionPrimaire: CapDataOpera\PhpSdk\Model\ProductionPrimaire
- [x] Programmation: CapDataOpera\PhpSdk\Model\Programmation
- [x] Referentiel: CapDataOpera\PhpSdk\Model\Referentiel
- [x] Role: CapDataOpera\PhpSdk\Model\Role
- [x] Sound: CapDataOpera\PhpSdk\Model\Sound
- [x] StatusJuridique: CapDataOpera\PhpSdk\Model\StatusJuridique
- [x] Text: CapDataOpera\PhpSdk\Model\Text
- [x] TypeOeuvre: CapDataOpera\PhpSdk\Model\TypeOeuvre
- [x] TypeEvenement: CapDataOpera\PhpSdk\Model\TypeEvenement
- [x] TypeProduction: CapDataOpera\PhpSdk\Model\TypeProduction
- [x] TypePublic: CapDataOpera\PhpSdk\Model\TypePublic

Pour les médias, il est recommandé d'utiliser les sous-classes `Image`, `Sound` et `Text`.

## Plus en détails

### Référence des hooks

#### Hook filter chargement (<font color="royalblue">*capdata/get_datas_xxx*</font>)

Ce hook filter vous permet d'indiquer les données à exporter par entité. \
Les hooks par entité listable sont (toutes les entités ne sont pas listables) :

- [x] Auteur: **capdata/get_datas_auteurs**
- [x] CategorieOeuvre: **capdata/get_datas_categorie_oeuvre**
- [x] Collectivite: **capdata/get_datas_collectivites**
- [x] Fonction: **capdata/get_datas_fonctions**
- [x] GenreOeuvre: **capdata/get_datas_genre_oeuvre**
- [x] HistoriqueProduction: **capdata/get_datas_historique_production**
- [x] Lieu: **capdata/get_datas_lieux**
- [x] Oeuvre: **capdata/get_datas_oeuvres**
- [x] Pays: **capdata/get_datas_pays**
- [x] Personne: **capdata/get_datas_personnes**
- [x] Production: **capdata/get_datas_productions**
- [x] ProductionPrimaire: **capdata/get_datas_production_primaire**
- [x] Role: **capdata/get_datas_roles**
- [x] Saison: **capdata/get_datas_saisons**
- [x] StatusJuridique: **capdata/get_datas_status_juridique**
- [x] Type d'événement: **capdata/get_datas_type_evenement**
- [x] Type de production: **capdata/get_datas_type_production**
- [x] Type de public: **capdata/get_datas_type_public**
- [x] TypeOeuvre: **capdata/get_datas_type_oeuvre**
- [x] Événement: **capdata/get_datas_evenements**

#### Hook filter transformation (<font color="royalblue">*capdata/get_transformed_xxx*</font>)

Ce hook filter vous permet de faire les assignations des données (setters) pour l'entité correspondante. \
Les hooks par entité sont :

- [x] AdressePostale: **capdata/get_transformed_adresse_postale**
- [x] ArkBnf: **capdata/get_transformed_arkbnf**
- [x] Auteur: **capdata/get_transformed_auteur**
- [x] CategorieOeuvre: **capdata/get_transformed_categorie_oeuvre**
- [x] Collaboration: **capdata/get_transformed_collaboration**
- [x] Collectivite: **capdata/get_transformed_collectivite**
- [x] Fonction: **capdata/get_transformed_fonction**
- [x] GenreOeuvre: **capdata/get_transformed_genre_oeuvre**
- [x] HistoriqueProduction: **capdata/get_transformed_historique_production**
- [x] Image: **capdata/get_transformed_image**
- [x] Interpretation: **capdata/get_transformed_interpretation**
- [x] Isni: **capdata/get_transformed_isni**
- [x] Lieu: **capdata/get_transformed_lieu**
- [x] MaitriseOeuvre: **capdata/get_transformed_maitrise_oeuvre**
- [x] Media: **capdata/get_transformed_media** (ne pas utiliser directement, utiliser les sous-classes)
- [x] MentionProduction: **capdata/get_transformed_mention_production**
- [x] Oeuvre: **capdata/get_transformed_oeuvre**
- [x] Partenariat: **capdata/get_transformed_partenariat**
- [x] Participation: **capdata/get_transformed_participation**
- [x] Pays: **capdata/get_transformed_pays**
- [x] Personne: **capdata/get_transformed_personne**
- [x] Production: **capdata/get_transformed_production**
- [x] ProductionPrimaire: **capdata/get_transformed_production_primaire**
- [x] Programmation: **capdata/get_transformed_programmation**
- [x] Referentiel: **capdata/get_transformed_referentiel**
- [x] Role: **capdata/get_transformed_role**
- [x] Saison: **capdata/get_transformed_saison**
- [x] Sound: **capdata/get_transformed_sound**
- [x] StatusJuridique: **capdata/get_transformed_status_juridique** 
- [x] Text: **capdata/get_transformed_text**
- [x] Type d'événement: **capdata/get_transformed_type_evenement**
- [x] Type de production: **capdata/get_transformed_type_production**
- [x] Type de public: **capdata/get_transformed_type_public**
- [x] TypeOeuvre: **capdata/get_transformed_type_oeuvre**
- [x] Événement: **capdata/get_transformed_evenement**

#### Hook filter activation cache (<font color="royalblue">*capdata/using_feeds_datas_from_cache*</font>)

Ce hook filter vous permet de préciser si les données affichées dans les feeds seront chargées depuis le cache (utile de le désactiver pendant le développement, est activé par défaut). \
Le hook attend en retour une valeur boolean (**true** ou **false**).

Exemple des codes pour indiquer qu'on n'utilise pas le cache pour la génération de feeds :

```php
<?php

add_filter('capdata/using_feeds_datas_from_cache', function () {

    return false;
});
...
```
#### Hook filter fréquence CRON (<font color="royalblue">*capdata/get_cron_recurrence*</font>)

Par défaut, la tâche planifiée pour générer les données du cache s'exécute toutes les heures. \
Ce hook filter vous permet de modifier la fréquence d'exécution de la tâche planifiée. \
Le hook attend en retour une chaîne de caractère de type schedules (exemple **hourly**, **twicedaily**, **daily**, etc) documenter [ici](#https://developer.wordpress.org/reference/functions/wp_get_schedules/)


Exemple des codes pour modifier la fréquence d'exécution du CRON pour tous les jours :

```php
<?php

add_filter('capdata/using_feeds_datas_from_cache', function () {

    return 'daily';
});
...
```

### Référence des fonctions

> IMPORTANT : lorsque vous utilisez une ou plusieurs de ces fonctions, vous *devez* vérifier son existence avant de l'utiliser, sinon votre site vous retournera une erreur fatale lors de la mise à jour (car WordPress désactive temporairement le plugin pendant qu'il se met à jour).

#### capdata_get_instance

Renvoie une instance de la classe correspondante dans le SDK passée en paramètre. L'objet retourné est initié avec l'URI de la ressource construite à partir de l'identifiant passé en paramètre. L'utilisation de ce helper évite de réécrire le code pour créer une ressource URI à partir d'un identifiant.

**Utilisation**

```php
capdata_get_instance($entity_name, $id)
```
- **$entity_name** : le nom de l'entité à instancier, les noms supportés sont [ici](#les-entités-supportées)
- **$id** : l'identifiant unique de la ressource (entier ou chaîne de caractère unique)

**Exemples**

```php
<?php

use CapDataOpera\PhpSdk\Model\Personne;

add_filter( 'capdata/get_transformed_personne', 'func_mapping_for_personne', 10, 2 );
 
function func_mapping_for_personne(Personne $result, $source): Personne 
{
    ...
    $result->setPrenom($source->field_prenom);
    $result->setNom($source->field_nom);
    $result->setBiographie($source->field_description);
    
    if ($source->person_has_image) {
        $image_detail = get_image_detail($source->ID);
        $image = capdata_get_instance('Image', $image_detail->id); // $image retourne une instance de CapDataOpera\PhpSdk\Model\Image
        $image->setName($image_detail->title);
        $image->setImage($image_detail->url);
        
        $result->setMedia($image);
    }

    return $result;
}
```


#### capdata_get_refuri

Renvoie une instance de la classe **ExternalThing** ayant pour URI, l'URI construite à partir du nom de l'entité et de l'identifiant passé en paramètre. Le helper est principalement utilisé pour simplifier la création d'une instance de l'objet **ExternalThing** en fournissant l'URI de la ressource en paramètre, évitant ainsi d'écrire du code supplémentaire à cette fin.

**Utilisation**

```php
capdata_get_refuri($entity_class, $id)
```
- **$entity_class** : le nom de l'entité cible, les noms supportés sont [ici](#les-entités-supportées)
- **$id** : l'identifiant unique de la ressource (entier ou une chaîne de caractère unique)

**Exemples**

```php
<?php

use CapDataOpera\PhpSdk\Model\Personne;

add_filter( 'capdata/get_transformed_personne', 'func_mapping_for_personne', 10, 2 );
 
function func_mapping_for_personne(Personne $result, $source): Personne 
{
    ...
    $result->setPrenom($source->field_prenom);
    $result->setNom($source->field_nom);
    $result->setBiographie($source->field_description);
    
    $fonctions_ids = get_related_fonction_ids($source->ID);;

        // person fonctions
        if (count($fonctions_ids)) {
            $isni_fonctions = array();

            foreach ($fonctions_ids as $id) {
                $isni_fonctions[] = capdata_get_refuri('Fonction', $id);
            }
            $result->setAPourProfession($isni_fonctions);
        }

    return $result;
}
```
#### capdata_generate_isniuri

Renvoie une URI de la ressource externe, initialisée avec l'identifiant et le chemin fournis en paramètre.

**Utilisation**

```php
capdata_generate_isniuri($id, $entity_path)
```
- **$id** : l'identifiant unique de la ressource (entier ou une chaîne de caractère unique)
- **$entity_path** : le chemin vers la ressource

**Exemples**

```php
<?php

use CapDataOpera\PhpSdk\Model\Fonction;

$fonction_uri = capdata_generate_isniuri(2, 'custom_fonction'); // la fonction génère https://mon-site.com/custom_fonction/2
$fonction = new Fonction($fonction_uri);

```
#### capdata_get_immutable_date

Renvoie une instance de l'objet date `\DateTimeImmutable` au format ATOM.

**Utilisation**

```php
capdata_get_immutable_date($date)
```
- **$date** : une chaîne date/heure 

**Exemples**

```php
<?php
$now_date = capdata_get_immutable_date(date('Y-m-d H:i:s'));
```

#### capdata_is_date_valid

Valide si chaine date est valide au format date PHP.

**Utilisation**

```php
capdata_is_date_valid($date)
```
- **$date** : une chaîne date/heure 

**Exemples**

```php
<?php
$valid_date = capdata_is_date_valid('15/05/2024 18:45:30');
```

## Comment contribuer au projet

Veuillez lire [CONTRIBUTING.md](CONTRIBUTING.md) pour plus de détails sur notre code de conduite, 
et le processus pour soumettre une *pull-request* ou une *issue*.

## Crédits

Conception et développement du plugin WordPress CapdataOpera : Rezo Zero - https://www.rezo-zero.com/
