Browse Source

composer is almost finished, ramins to record compos order through ajax

Bachir Soussi Chiadmi 6 years ago
parent
commit
90c9c68dcd
26 changed files with 1048 additions and 89 deletions
  1. 163 29
      sites/all/modules/figli/edlp_studio/assets/js/edlp_studio.js
  2. 1 0
      sites/all/modules/figli/edlp_studio/composition.page.inc
  3. 4 0
      sites/all/modules/figli/edlp_studio/edlp_studio.libraries.yml
  4. 21 1
      sites/all/modules/figli/edlp_studio/edlp_studio.module
  5. 1 1
      sites/all/modules/figli/edlp_studio/includes/edlp_chutier_ui.inc
  6. 6 5
      sites/all/modules/figli/edlp_studio/includes/edlp_composition_ui.inc
  7. 5 3
      sites/all/modules/figli/edlp_studio/includes/edlp_compositions_list.inc
  8. 23 0
      sites/all/modules/figli/edlp_studio/src/Controller/CompositionController.php
  9. 21 5
      sites/all/modules/figli/edlp_studio/src/Controller/StudioUIController.php
  10. 9 1
      sites/all/modules/figli/edlp_studio/templates/edlp-composition-ui.html.twig
  11. 9 4
      sites/all/themes/custom/edlptheme/assets/dist/scripts/main.min.js
  12. 187 14
      sites/all/themes/custom/edlptheme/assets/dist/styles/app.min.css
  13. 9 4
      sites/all/themes/custom/edlptheme/assets/scripts/main.js
  14. 161 8
      sites/all/themes/custom/edlptheme/assets/styles/app.scss
  15. 14 5
      sites/all/themes/custom/edlptheme/assets/styles/base/_layout.scss
  16. 3 0
      sites/all/themes/custom/edlptheme/edlptheme.libraries.yml
  17. 25 0
      sites/all/themes/custom/edlptheme/edlptheme.theme
  18. 91 0
      sites/all/themes/custom/edlptheme/templates/content/node--enregistrement--compo.html.twig
  19. 103 0
      sites/all/themes/custom/edlptheme/templates/content/node--page--image-2-columns0.html.twig
  20. 31 0
      sites/default/config/sync/core.entity_form_display.composition.composition.default.yml
  21. 24 0
      sites/default/config/sync/core.entity_view_display.composition.composition.default.yml
  22. 54 0
      sites/default/config/sync/core.entity_view_display.node.enregistrement.compo.yml
  23. 62 0
      sites/default/config/sync/core.entity_view_display.node.enregistrement.compo_document_.yml
  24. 1 9
      sites/default/config/sync/core.entity_view_display.node.enregistrement.default.yml
  25. 10 0
      sites/default/config/sync/core.entity_view_mode.node.compo.yml
  26. 10 0
      sites/default/config/sync/core.entity_view_mode.node.compo_document_.yml

+ 163 - 29
sites/all/modules/figli/edlp_studio/assets/js/edlp_studio.js

@@ -1,22 +1,25 @@
 (function ($, Drupal, drupalSettings) {
 
   var settings = drupalSettings.edlp_studio;
+  var composer;
 
   function init(){
     console.log('Studio Init');
     initEvents();
-    initAjaxLinks();
+    initStudio();
   };
 
   function initEvents(){
     $('body')
       .on('new-audio-cartel-loaded', initAjaxChutierLinks)
-      .on('new-content-ajax-loaded', initAjaxLinks);
+      .on('new-content-ajax-loaded', initStudio);
   };
 
-  function initAjaxLinks(){
+  function initStudio(){
+    $composer = $('.composition_ui .composer', '#studio-ui');
     initAjaxChutierLinks();
     initAjaxCompoLinks();
+    initDragAndDropUI();
   }
   //   ___ _        _   _           _  _         _       _    _      _
   //  / __| |_ _  _| |_(_)___ _ _  | \| |___  __| |___  | |  (_)_ _ | |__ ___
@@ -29,14 +32,12 @@
       .addClass('ajax-enabled')
       .on('click', onClickChutierAjaxLink);
   };
-
   function onClickChutierAjaxLink(e){
     console.log('studio onClickAjaxLink chutier');
     e.preventDefault();
     addRemoveToChutier($(this));
     return false;
   };
-
   function addRemoveToChutier($link){
     // var ajax_path = 'edlp_studio/ajax/chutier/add/'+id; // + '/' +cid;
     var ajax_path = $link.attr('data-drupal-link-system-path');
@@ -50,7 +51,6 @@
         onErrorActionToChutier(jqxhr, textStatus, error, $link);
       });
   };
-
   function onActionToChutierDone($link, data){
     console.log('onActionToChutierDone',data);
     $('body').trigger({
@@ -64,7 +64,6 @@
     // reload Studio chutier_ui's documents list
     updateStudioChutier();
   };
-
   function onErrorActionToChutier(jqxhr, textStatus, error, $link){
     console.warn('action to chuttier load failed : '+error, jqxhr.responseText);
   };
@@ -77,18 +76,16 @@
   function initAjaxCompoLinks(){
     console.log('studio initAjaxCompoLinks');
     $('.new-composition-link:not(.ajax-enabled)')
-      .addClass('ajax-enabled')
-      .on('click', onClickNewCompoLink);
+    .on('click', onClickNewCompoLink)
+      .addClass('ajax-enabled');
     $('.composition-link:not(.ajax-enabled)')
-      .addClass('ajax-enabled')
-      .on('click', onClickCompoLink);
+    .on('click', onClickCompoLink)
+      .addClass('ajax-enabled');
     $('.delete-composition-link:not(.ajax-enabled)')
-      .addClass('ajax-enabled')
-      .on('click', onClickDeleteCompoLink);
+      .on('click', onClickDeleteCompoLink)
+      .addClass('ajax-enabled');
   };
 
-
-
   //   ___                    ___
   //  / _ \ _ __  ___ _ _    / __|___ _ __  _ __  ___
   // | (_) | '_ \/ -_) ' \  | (__/ _ \ '  \| '_ \/ _ \
@@ -97,21 +94,46 @@
   function onClickCompoLink(e){
     e.preventDefault();
     console.log('onClickCompoLink');
-
+    openCompo($(this));
     return false;
   };
+  function openCompo($link){
+    var cid = $link.attr('cid');
+    var ajax_path = settings.open_compo_ajax_url+'/'+cid;
+    var path = window.location.origin + Drupal.url(ajax_path);
+    $link.addClass('ajax-loading');
+    $composer.addClass('ajax-loading');
+    $.getJSON(path, {})
+      .done(function(data){
+        onOpenCompoDone(data, $link);
+      })
+      .fail(function(jqxhr, textStatus, error){
+        onErrorOpenCompo(jqxhr, textStatus, error, $link);
+      });
+  };
+  function onOpenCompoDone(data, $link){
+    console.log('onActionToCompoDone',data);
+    $('.composition-link').removeClass('is-active');
+    $link.removeClass('ajax-loading').addClass('is-active');
+
+    $composer.html(data.compo);
+    $composer.removeClass('ajax-loading');
+    initDragAndDropUI();
+  };
+  function onErrorOpenCompo(jqxhr, textStatus, error, $link){
+    $link.removeClass('ajax-loading');
+    console.warn('open compo load failed : '+error, jqxhr.responseText);
+  };
 
   //  _ _  _____ __ __  __ ___ _ __  _ __  ___
   // | ' \/ -_) V  V / / _/ _ \ '  \| '_ \/ _ \
   // |_||_\___|\_/\_/  \__\___/_|_|_| .__/\___/
   //                                |_|
-
   function onClickNewCompoLink(e){
     e.preventDefault();
     setInputForNewCompoName($(this));
     return false;
   };
-
   function setInputForNewCompoName($link){
     var $form = $('<form>').addClass('new-compo-form')
       .append($('<input>').attr('type', 'text').attr('placeholder', 'new name'))
@@ -125,7 +147,6 @@
       .addClass('folded');
     $form.children('input[type="text"]').focus();
   };
-
   function onNewCompoFormSubmit(e, $link, $form){
     var name = $('input[type="text"]',$form).val();
     console.log('onNewCompoFormSubmit', name);
@@ -135,7 +156,6 @@
     }
     e.preventDefault();
   };
-
   function createNewCompo(name,$link, $form){
     var ajax_path = $link.attr('data-drupal-link-system-path');
     var path = window.location.origin + drupalSettings.path.baseUrl + ajax_path;
@@ -144,14 +164,13 @@
         'new_name':name
       })
       .done(function(data){
-        onActionToCompoDone(data, $link, $form);
+        onCreateCompoDone(data, $link, $form);
       })
       .fail(function(jqxhr, textStatus, error){
-        onErrorActionToCompo(jqxhr, textStatus, error, $link);
+        onErrorCreateCompo(jqxhr, textStatus, error, $link);
       });
   };
-
-  function onActionToCompoDone(data, $link, $form){
+  function onCreateCompoDone(data, $link, $form){
     console.log('onActionToCompoDone',data);
     $link
       .removeClass('folded')
@@ -160,12 +179,10 @@
     $form.remove();
     // TODO: open new composition to composer
   };
-
-  function onErrorActionToCompo(jqxhr, textStatus, error, $link){
+  function onErrorCreateCompo(jqxhr, textStatus, error, $link){
     console.warn('action to compo load failed : '+error, jqxhr.responseText);
   };
 
-
   // ___      _     _
   // |   \ ___| |___| |_ ___   __ ___ _ __  _ __  ___
   // | |) / -_) / -_)  _/ -_) / _/ _ \ '  \| '_ \/ _ \
@@ -223,7 +240,6 @@
         });
     }
   };
-
   function onLoadedChutier($studioChutier, data){
     $studioChutier.replaceWith(data.rendered);
     initAjaxChutierLinks();
@@ -231,12 +247,130 @@
       'type':'on-studio-chutier-updated'
     });
   }
-
   function onLoadChutierError(jqxhr, textStatus, error, $studioChutier){
     console.warn('Chutier load failed', jqxhr.responseText);
     $studioChutier.removeClass('loading');
 
   }
 
+
+  //  ___                  __       ___                 _   _ ___
+  // |   \ _ _ __ _ __ _  / _|___  |   \ _ _ ___ _ __  | | | |_ _|
+  // | |) | '_/ _` / _` | > _|_ _| | |) | '_/ _ \ '_ \ | |_| || |
+  // |___/|_| \__,_\__, | \_____|  |___/|_| \___/ .__/  \___/|___|
+  //               |___/                        |_|
+  function initDragAndDropUI(){
+
+    // add real dom square element to handle sorting
+    $('.composition_ui .composer .composition .field--name-documents .field__item').each(function(i){
+      $(this).prepend($('<span>').addClass('handler'));
+    });
+
+    // create remove dropzone
+    var $remove_zone = $('<div>').addClass('remove-drop-zone')
+      .appendTo('.composition_ui .composer .composition')
+      .droppable({
+        tolerance:'pointer',
+        // scope:'composition_ui',
+        over:function(e,ui){
+          // console.log('on over remove : ui', ui);
+          ui.draggable.addClass('ready-to-remove');
+        },
+        out:function(e,ui){
+          // console.log('on over remove : ui', ui);
+          ui.draggable.removeClass('ready-to-remove');
+        },
+        drop:function(e,ui){
+          // console.log('on drop remove : ui', ui);
+          ui.draggable.remove();
+          // onOrderChanged(ui);
+        }
+      });
+
+    // create sortable
+    $('.composition_ui .composer .composition .field--name-documents').sortable({
+      revert:false,
+      handle: ".handler",
+      receive:function(e, ui){
+        $('.field__item', this).attr('style', '');
+        // TODO: trigger event to enable new audiolink player
+      },
+      update:function(e, ui){
+        onOrderChanged(e, ui);
+      },
+      connectWith : $remove_zone,
+    });
+
+    // set chutier element draggable
+    $('.chutier_ui .item-list li>div').draggable({
+      containment:$('#studio-ui'),
+      scroll:false,
+      helper:function(e){
+        // we reproduce here the irem structure of destination sortable elements
+        // var $audio_link = $('.audio-link',this);
+        return $('<div>').addClass('field__item')
+          .append(
+            $('<article>').addClass('node').append(
+              $('<h2>').addClass('node-title').append(
+                // $('<a>')
+                //   .attr('nid', $audio_link.attr('nid'))
+                //   .html($audio_link.html())
+                $('.audio-link',this).clone().removeClass('ajax-enable')
+              )
+            )
+          )
+          .prepend($('<span>').addClass('handler'));
+      },
+      // cursor:'grab',
+      cursorAt:{bottom:10,left:10},
+      zIndex:999,
+      revert:true,
+      revertDuration:0,
+      connectToSortable:$('.composition_ui .composer .composition .field--name-documents'),
+    });
+
+  };
+
+  // TODO: how to remove an item
+
+  function onOrderChanged(e, ui){
+    console.log('onOrderChanged : e', e);
+    // TODO: record the current order through ajax
+    var documents = [];
+    $('a', e.target).each(function(i) {
+      // console.log(this);
+      documents.push($(this).attr('nid'));
+    });
+    console.log(documents);
+    // recordCompoList(cid, documents);
+  };
+
+  function recordCompoList(cid, docs){
+    var ajax_path = 'path';
+    var path = window.location.origin + Drupal.url(ajax_path);
+    $.getJSON(path, {
+        documents:docs
+      })
+      .done(function(data){
+        onRecordCompoDone(data);
+      })
+      .fail(function(jqxhr, textStatus, error){
+        onErrorRecordCompo(jqxhr, textStatus, error);
+      });
+  }
+
+  function onRecordCompoDone(data){
+    console.log('onDeleteCompoDone',data);
+    if(data.status == "ok"){
+    }else{
+      console.warn(data.message);
+    }
+  };
+  function onErrorRecordCompo(jqxhr, textStatus, error){
+    console.warn('record compo failed : '+error, jqxhr.responseText);
+  };
+
+
+
   init();
 })(jQuery, Drupal, drupalSettings);

+ 1 - 0
sites/all/modules/figli/edlp_studio/composition.page.inc

@@ -20,6 +20,7 @@ use Drupal\Core\Render\Element;
  *   - attributes: HTML attributes for the containing element.
  */
 function template_preprocess_composition(array &$variables) {
+  // dpm($variables);
   // Fetch Composition Entity Object.
   $composition = $variables['elements']['#composition'];
 

+ 4 - 0
sites/all/modules/figli/edlp_studio/edlp_studio.libraries.yml

@@ -4,3 +4,7 @@ edlp_studio-library:
   dependencies:
     - core/drupalSettings
     - core/jquery
+    - core/jquery.once
+    - core/jquery.ui.draggable
+    - core/jquery.ui.droppable
+    - core/jquery.ui.sortable

+ 21 - 1
sites/all/modules/figli/edlp_studio/edlp_studio.module

@@ -32,6 +32,8 @@ function edlp_studio_page_attachments(array &$attachments) {
   $attachments['#attached']['library'][] = 'edlp_studio/edlp_studio-library';
   $url = Url::fromRoute('edlp_studio.studio_chutier_ui_ajax', [], ['absolute' => TRUE]);
   $attachments['#attached']['drupalSettings']['edlp_studio']['chutier_ui_ajax'] = $url->getInternalPath();
+  $url = Url::fromRoute('edlp_studio.composition_controller_action_ajax', ['action'=>'open'], ['absolute' => TRUE]);
+  $attachments['#attached']['drupalSettings']['edlp_studio']['open_compo_ajax_url'] = $url->getInternalPath();
 }
 
 /**
@@ -54,7 +56,9 @@ function edlp_studio_theme($existing, $type, $theme, $path) {
       'variables' => array(
         'title' => t('Compositon'),
         'compositions' => null,
-        'composer' => null,
+        'composer_header' => null,
+        'lastcomposition' => null,
+        'composer_actions' => null,
       ),
     ),
     'edlp_compositions_list' => array(
@@ -147,3 +151,19 @@ function edlp_studio_node_view(array &$build, \Drupal\Core\Entity\EntityInterfac
     }
   }
 }
+
+
+/**
+ * Prepares variables for Composition templates.
+ *
+ * Default template: composition.html.twig.
+ *
+ * @param array $variables
+ *   An associative array containing:
+ *   - elements: An associative array containing the user information and any
+ *   - attributes: HTML attributes for the containing element.
+ */
+// function edlp_studio_preprocess_composition(array &$vars) {
+//   dpm($vars);
+//   $vars['test'] = 'test';
+// }

+ 1 - 1
sites/all/modules/figli/edlp_studio/includes/edlp_chutier_ui.inc

@@ -28,7 +28,7 @@ function template_preprocess_edlp_chutier_ui(&$vars){
     // get the title
     $title = $node->getTitle();
     if(!$title) continue;
-    $trunc_title = Unicode::truncate($title, 30, true, true);
+    $trunc_title = Unicode::truncate($title, 25, true, true);
     // classes
     $classes = array('audio-link', 'ajax-link');
     // get the entries

+ 6 - 5
sites/all/modules/figli/edlp_studio/includes/edlp_composition_ui.inc

@@ -2,9 +2,10 @@
 
 function template_preprocess_edlp_composition_ui(&$vars){
   // dpm($vars);
-/*
-  @see https://www.drupal8.ovh/index.php/en/tutoriels/339/render-a-node-or-an-entity
-  */
-  // $view_builder = \Drupal::entityTypeManager()->getViewBuilder($vars['entity_type']);
-  // $vars['content'] = $view_builder->view($vars['entity'], $vars['view_mode']);
+
+
+  $view_builder = \Drupal::entityTypeManager()->getViewBuilder('composition');
+  $vars['composition'] = $view_builder->view($vars['lastcomposition']);
+
+
 }

+ 5 - 3
sites/all/modules/figli/edlp_studio/includes/edlp_compositions_list.inc

@@ -9,6 +9,7 @@ function template_preprocess_edlp_compositions_list(&$vars){
     '#theme' => 'item_list',
     '#items' => [],
   );
+  $i = 0;
   foreach($vars['composition_entities'] as $entity){
     // get the url
     $url = Url::fromRoute('entity.composition.canonical',
@@ -27,8 +28,8 @@ function template_preprocess_edlp_compositions_list(&$vars){
         '#options'=>array(
           'attributes' => array(
             'data-drupal-link-system-path' => $url->getInternalPath(),
-            'nid' => $entity->id(),
-            'class' => ['composition-link'],
+            'cid' => $entity->id(),
+            'class' => ['composition-link', $i==0 ? 'is-active':''],
             'title'=>$title,
           ),
         ),
@@ -40,13 +41,14 @@ function template_preprocess_edlp_compositions_list(&$vars){
         '#options'=>array(
           'attributes' => array(
             'data-drupal-link-system-path' => $delete_url->getInternalPath(),
-            'nid' => $entity->id(),
+            'cid' => $entity->id(),
             'class' => ['delete-composition-link'],
             'title'=> t('Delete @title', array('@title'=>$title)),
           ),
         ),
       )
     );
+    $i++;
   }
 
   $vars['compositions']['#items'][] = array(

+ 23 - 0
sites/all/modules/figli/edlp_studio/src/Controller/CompositionController.php

@@ -61,6 +61,13 @@ class CompositionController extends ControllerBase {
           $this->error_message = t("Composition creation needs a name as query paramater!");
         }
         break;
+      case 'open':
+        if($cid){
+          $this->openComposition($cid);
+        }else{
+          $this->error_message = t("Composition opening needs a cid as url paramater!");
+        }
+        break;
       case 'delete':
         if($cid){
           $this->deleteComposition($cid);
@@ -109,6 +116,12 @@ class CompositionController extends ControllerBase {
             'new_link' => render($new_link_build),
           );
           break;
+        case 'open':
+          $this->getRendredComposition();
+          $data += array(
+            'compo' => $this->rendered_compo,
+          );
+          break;
         // case 'delete':
         //   # code...
         //   break;
@@ -135,6 +148,16 @@ class CompositionController extends ControllerBase {
     $this->compo->save();
   }
 
+  private function getRendredComposition(){
+    $view_builder = \Drupal::entityTypeManager()->getViewBuilder('composition');
+    $compobuild = $view_builder->view($this->compo, 'default');
+    $this->rendered_compo = render($compobuild);
+  }
+
+  private function openComposition($cid){
+    $this->compo = entity_load('composition', $cid);
+  }
+
   private function deleteComposition($cid){
     $this->compo = entity_load('composition', $cid);
     $this->compo->delete();

+ 21 - 5
sites/all/modules/figli/edlp_studio/src/Controller/StudioUIController.php

@@ -114,7 +114,14 @@ class StudioUIController extends ControllerBase {
       "#theme"=>'edlp_composition_ui',
       "#title" => t('Composition'),
       "#compositions" => $this->buildCompostionsList(),
-      "#composer" => $this->buildComposer(),
+      "#composer_header" => t("Drag and drop documents on the time line from your chutier above."),
+      "#lastcomposition" => $this->getLastCompo(),
+      '#composer_actions' => array(
+        'play_composition'=>array(
+          "#type"=>'container',
+          "#markup"=>t('Play my composition'),
+        ),// TODO: add social media links (what about composition visibility uotside studio_ui ?)
+      ),
     );
 
     return $composition_ui;
@@ -125,8 +132,8 @@ class StudioUIController extends ControllerBase {
     $query = \Drupal::entityQuery('composition')
       ->condition('user_id', $this->user->id());
 
-    $compos_nids = $query->execute();
-    $compos = entity_load_multiple('composition', $compos_nids);
+    $compos_ids = $query->execute();
+    $compos = entity_load_multiple('composition', $compos_ids);
 
     $createurl = Url::fromRoute('edlp_studio.composition_controller_action_ajax', ['action' => 'create'], ['absolute' => TRUE]);
 
@@ -137,7 +144,16 @@ class StudioUIController extends ControllerBase {
     );
   }
 
-  private function buildComposer(){
-    return array('#markup'=>'TODO : composer UI');
+  private function getLastCompo(){
+    // just get the last compositions
+    $query = \Drupal::entityQuery('composition')
+      ->condition('user_id', $this->user->id())
+      ->range(0,1)
+      ->sort('created');
+
+    $compos_id = $query->execute();
+    $lastcompo = entity_load('composition', array_pop($compos_id));
+
+    return $lastcompo;
   }
 }

+ 9 - 1
sites/all/modules/figli/edlp_studio/templates/edlp-composition-ui.html.twig

@@ -5,7 +5,15 @@
     {{ compositions }}
   </section>
   <section class="composer">
-    {{ composer }}
+    <header>
+      {{ composer_header }}
+    </header>
+    <section class="composition">
+      {{ composition }}
+    </section>
+    <section class="actions">
+      {{ composer_actions }}
+    </section>
   </section>
  </div>
 </div>

+ 9 - 4
sites/all/themes/custom/edlptheme/assets/dist/scripts/main.min.js

@@ -147,10 +147,15 @@
         this.setSRC(this.historic[this.currentHistoricIndex].audio_url);
         this.loadNode(this.historic[this.currentHistoricIndex].nid);
         // emmit new playing doc (e.g.: corpus map nowing that audio played from RandomPlayer)
-        _$corpus_canvas.trigger({
-          'type':'audio-node-opened',
-          'nid':this.historic[this.currentHistoricIndex].nid
-        });
+        try {
+          _$corpus_canvas.trigger({
+            'type':'audio-node-opened',
+            'nid':this.historic[this.currentHistoricIndex].nid
+          });
+        } catch (e) {
+          console.warn('AudioPlayer : _$corpus_canvas does not exists');
+        }
+
         this.showHidePreviousBtn();
         this.showHideNextBtn();
         this.show();

+ 187 - 14
sites/all/themes/custom/edlptheme/assets/dist/styles/app.min.css

@@ -1100,6 +1100,9 @@ body, html {
   margin: 0;
   padding: 0; }
 
+body.toolbar-horizontal.toolbar-themes.toolbar-no-tabs {
+  padding-top: 24px !important; }
+
 header[role="banner"] {
   z-index: 2;
   position: relative;
@@ -1136,7 +1139,7 @@ main[role="main"] {
   padding: 7em 2em 9em;
   overflow: hidden;
   pointer-events: none;
-  height: auto; }
+  height: 100%; }
   main[role="main"] .layout-content {
     width: 100%;
     height: 100%;
@@ -1144,8 +1147,8 @@ main[role="main"] {
     main[role="main"] .layout-content > * {
       pointer-events: auto; }
 
-body[corpus-map="ready"] main[role="main"] {
-  height: 100%; }
+body.toolbar-horizontal.toolbar-themes.toolbar-no-tabs main[role="main"] {
+  padding-top: 8em; }
 
 footer[role="contentinfo"] {
   z-index: 2;
@@ -1567,7 +1570,8 @@ main[role="main"] span.close-col-btn {
     #studio-ui .chutier_ui .documents {
       padding-top: 35px;
       height: calc(100% - 35px);
-      overflow-y: auto; }
+      overflow-y: auto;
+      overflow-x: hidden; }
       #studio-ui .chutier_ui .documents ul, #studio-ui .chutier_ui .documents li {
         margin: 0;
         padding: 0;
@@ -1578,6 +1582,12 @@ main[role="main"] span.close-col-btn {
         width: 49%;
         margin-bottom: 0.5em;
         white-space: nowrap; }
+        #studio-ui .chutier_ui .documents li > div {
+          display: inline-block;
+          padding-right: 2em; }
+          #studio-ui .chutier_ui .documents li > div.ui-draggable {
+            cursor: -webkit-grab;
+            cursor: grab; }
         #studio-ui .chutier_ui .documents li .entrees {
           line-height: 0; }
           #studio-ui .chutier_ui .documents li .entrees span {
@@ -1653,12 +1663,16 @@ main[role="main"] span.close-col-btn {
       white-space: nowrap; }
       #studio-ui .composition_ui > .wrapper section.compositions-list, #studio-ui .composition_ui > .wrapper section.composer {
         display: inline-block;
-        vertical-align: top;
+        vertical-align: bottom;
         height: 100%; }
       #studio-ui .composition_ui > .wrapper .compositions-list {
         width: 30%;
         height: 100%;
-        overflow-y: auto; }
+        overflow-y: auto;
+        -webkit-box-sizing: border-box;
+        box-sizing: border-box;
+        padding-right: 0.5em;
+        border-right: 1px solid #aaa; }
         #studio-ui .composition_ui > .wrapper .compositions-list li {
           opacity: 1;
           -webkit-transition: opacity 0.3s ease-in-out;
@@ -1671,7 +1685,20 @@ main[role="main"] span.close-col-btn {
           font-size: 0.756em; }
           #studio-ui .composition_ui > .wrapper .compositions-list a.composition-link {
             display: inline-block;
-            width: calc(100% - 14px); }
+            -webkit-box-sizing: border-box;
+            box-sizing: border-box;
+            width: calc(100% - 21px); }
+            #studio-ui .composition_ui > .wrapper .compositions-list a.composition-link:before {
+              content: "";
+              width: 7px;
+              height: 7px;
+              border: 1px solid black;
+              background-color: white;
+              display: inline-block;
+              vertical-align: middle;
+              margin-right: 5px; }
+            #studio-ui .composition_ui > .wrapper .compositions-list a.composition-link.is-active:before {
+              background-color: black; }
           #studio-ui .composition_ui > .wrapper .compositions-list a.delete-composition-link {
             display: inline-block;
             vertical-align: middle;
@@ -1690,13 +1717,13 @@ main[role="main"] span.close-col-btn {
             display: inline-block;
             vertical-align: top;
             overflow: hidden;
+            margin-top: 0.4em;
             height: 1em;
             -webkit-transition: height 0.2s ease-in-out;
             transition: height 0.2s ease-in-out; }
             #studio-ui .composition_ui > .wrapper .compositions-list a.new-composition-link:before {
               content: "+";
-              font-weight: bold;
-              margin-right: 0.2em; }
+              margin-right: 5px; }
             #studio-ui .composition_ui > .wrapper .compositions-list a.new-composition-link.folded {
               height: 0; }
         #studio-ui .composition_ui > .wrapper .compositions-list .new-compo-form {
@@ -1720,11 +1747,157 @@ main[role="main"] span.close-col-btn {
           #studio-ui .composition_ui > .wrapper .compositions-list .new-compo-form.ajax-loading {
             opacity: 0.4; }
       #studio-ui .composition_ui > .wrapper .composer {
-        -webkit-box-sizing: content-box;
-        box-sizing: content-box;
-        width: 69%;
-        padding-left: 1em;
-        border-left: 1px solid #aaa; }
+        position: relative;
+        -webkit-box-sizing: border-box;
+        box-sizing: border-box;
+        padding-left: 0.5em;
+        width: 69%; }
+        #studio-ui .composition_ui > .wrapper .composer > * {
+          -webkit-box-sizing: border-box;
+          box-sizing: border-box; }
+        #studio-ui .composition_ui > .wrapper .composer > header {
+          height: 25px;
+          padding-bottom: 5px; }
+        #studio-ui .composition_ui > .wrapper .composer > .composition {
+          position: relative;
+          height: calc(100% - 60px);
+          padding-bottom: 1em; }
+          #studio-ui .composition_ui > .wrapper .composer > .composition .field--name-documents {
+            position: relative;
+            -webkit-box-sizing: border-box;
+            box-sizing: border-box;
+            height: 100%;
+            width: 100%;
+            padding: 0 0.5em; }
+            #studio-ui .composition_ui > .wrapper .composer > .composition .field--name-documents:before, #studio-ui .composition_ui > .wrapper .composer > .composition .field--name-documents:after {
+              content: "";
+              width: calc(100% - 2px);
+              position: absolute;
+              bottom: 0;
+              left: 0; }
+            #studio-ui .composition_ui > .wrapper .composer > .composition .field--name-documents:before {
+              border-top: 1px solid #A1A1A1;
+              height: 14.5px; }
+            #studio-ui .composition_ui > .wrapper .composer > .composition .field--name-documents:after {
+              border-left: 1px solid #A1A1A1;
+              border-right: 1px solid #A1A1A1;
+              height: 29px; }
+            #studio-ui .composition_ui > .wrapper .composer > .composition .field--name-documents .field__item {
+              display: inline-block;
+              position: relative;
+              width: 25px;
+              height: 100%;
+              white-space: nowrap;
+              pointer-events: none;
+              opacity: 1;
+              -webkit-transition: opacity 0.1s ease-in-out;
+              transition: opacity 0.1s ease-in-out; }
+              #studio-ui .composition_ui > .wrapper .composer > .composition .field--name-documents .field__item.ui-draggable-dragging {
+                height: 100px; }
+              #studio-ui .composition_ui > .wrapper .composer > .composition .field--name-documents .field__item.ready-to-remove {
+                opacity: 0.3;
+                cursor: crosshair; }
+              #studio-ui .composition_ui > .wrapper .composer > .composition .field--name-documents .field__item > * {
+                pointer-events: all; }
+              #studio-ui .composition_ui > .wrapper .composer > .composition .field--name-documents .field__item article {
+                position: absolute;
+                bottom: 0;
+                -webkit-transform-origin: left top;
+                transform-origin: left top;
+                -webkit-transform: rotateZ(-45deg);
+                transform: rotateZ(-45deg); }
+                #studio-ui .composition_ui > .wrapper .composer > .composition .field--name-documents .field__item article h2 {
+                  margin: 0;
+                  text-transform: none; }
+                  #studio-ui .composition_ui > .wrapper .composer > .composition .field--name-documents .field__item article h2 a {
+                    font-size: 0.756em; }
+                    #studio-ui .composition_ui > .wrapper .composer > .composition .field--name-documents .field__item article h2 a:before {
+                      content: "";
+                      display: inline-block;
+                      vertical-align: middle;
+                      width: 12px;
+                      height: 12px;
+                      border: 1px solid white;
+                      background-color: white; }
+              #studio-ui .composition_ui > .wrapper .composer > .composition .field--name-documents .field__item .handler {
+                z-index: 99;
+                cursor: -webkit-grab;
+                cursor: grab;
+                position: absolute;
+                bottom: 0;
+                left: 1px;
+                display: block;
+                width: 12px;
+                height: 12px;
+                -webkit-transform-origin: left top;
+                transform-origin: left top;
+                -webkit-transform: rotateZ(-45deg);
+                transform: rotateZ(-45deg);
+                border: 1px solid red;
+                background-color: white; }
+          #studio-ui .composition_ui > .wrapper .composer > .composition .remove-drop-zone {
+            position: absolute;
+            top: 0;
+            left: 0;
+            width: 100%;
+            height: 45%;
+            z-index: 150; }
+        #studio-ui .composition_ui > .wrapper .composer > .actions {
+          padding-top: 5px;
+          height: 25px; }
+  #studio-ui .field__item.ui-draggable-dragging {
+    display: inline-block;
+    position: relative;
+    width: 25px;
+    height: 100%;
+    white-space: nowrap;
+    pointer-events: none;
+    opacity: 1;
+    -webkit-transition: opacity 0.1s ease-in-out;
+    transition: opacity 0.1s ease-in-out; }
+    #studio-ui .field__item.ui-draggable-dragging.ui-draggable-dragging {
+      height: 100px; }
+    #studio-ui .field__item.ui-draggable-dragging.ready-to-remove {
+      opacity: 0.3;
+      cursor: crosshair; }
+    #studio-ui .field__item.ui-draggable-dragging > * {
+      pointer-events: all; }
+    #studio-ui .field__item.ui-draggable-dragging article {
+      position: absolute;
+      bottom: 0;
+      -webkit-transform-origin: left top;
+      transform-origin: left top;
+      -webkit-transform: rotateZ(-45deg);
+      transform: rotateZ(-45deg); }
+      #studio-ui .field__item.ui-draggable-dragging article h2 {
+        margin: 0;
+        text-transform: none; }
+        #studio-ui .field__item.ui-draggable-dragging article h2 a {
+          font-size: 0.756em; }
+          #studio-ui .field__item.ui-draggable-dragging article h2 a:before {
+            content: "";
+            display: inline-block;
+            vertical-align: middle;
+            width: 12px;
+            height: 12px;
+            border: 1px solid white;
+            background-color: white; }
+    #studio-ui .field__item.ui-draggable-dragging .handler {
+      z-index: 99;
+      cursor: -webkit-grab;
+      cursor: grab;
+      position: absolute;
+      bottom: 0;
+      left: 1px;
+      display: block;
+      width: 12px;
+      height: 12px;
+      -webkit-transform-origin: left top;
+      transform-origin: left top;
+      -webkit-transform: rotateZ(-45deg);
+      transform: rotateZ(-45deg);
+      border: 1px solid red;
+      background-color: white; }
 
 .col[theme="edlp_search_search_form"], .col[theme="edlp_search_search_form"] > .wrapper {
   position: relative;

+ 9 - 4
sites/all/themes/custom/edlptheme/assets/scripts/main.js

@@ -147,10 +147,15 @@
         this.setSRC(this.historic[this.currentHistoricIndex].audio_url);
         this.loadNode(this.historic[this.currentHistoricIndex].nid);
         // emmit new playing doc (e.g.: corpus map nowing that audio played from RandomPlayer)
-        _$corpus_canvas.trigger({
-          'type':'audio-node-opened',
-          'nid':this.historic[this.currentHistoricIndex].nid
-        });
+        try {
+          _$corpus_canvas.trigger({
+            'type':'audio-node-opened',
+            'nid':this.historic[this.currentHistoricIndex].nid
+          });
+        } catch (e) {
+          console.warn('AudioPlayer : _$corpus_canvas does not exists');
+        }
+
         this.showHidePreviousBtn();
         this.showHideNextBtn();
         this.show();

+ 161 - 8
sites/all/themes/custom/edlptheme/assets/styles/app.scss

@@ -472,6 +472,66 @@ main[role="main"]{
 }
 #studio-ui{
   height:100%;
+  @mixin draggable_sortable_field__item {
+    // outline: 1px dotted green;
+    display: inline-block;
+    position: relative;
+    // margin-right: 25px;
+    width:25px;
+    height: 100%;
+    &.ui-draggable-dragging{
+      height:100px;
+      // cursor: grabbing;
+    }
+    white-space: nowrap;
+    pointer-events: none;
+    opacity: 1;
+    transition: opacity 0.1s ease-in-out;
+    &.ready-to-remove{
+      opacity:0.3;
+      cursor:crosshair;
+    }
+    &>*{
+      pointer-events: all;
+    }
+    article{
+      // outline: 1px dotted purple;
+      position: absolute;
+      bottom:0;
+      transform-origin: left top;
+      transform: rotateZ(-45deg);
+      h2{
+        margin:0;
+        text-transform: none;
+        a{
+          &:before{
+            content:"";
+            display: inline-block;
+            vertical-align: middle;
+            width:$compo_square_size; height:$compo_square_size;
+            border: 1px solid white;
+            background-color: white;
+            // TODO: entries color;
+          }
+          font-size: 0.756em;
+        }
+      }
+    }
+    .handler{
+      z-index: 99;
+      cursor: grab;
+      position: absolute;
+      bottom:0; left:1px;
+      display: block;
+      width:$compo_square_size; height:$compo_square_size;
+      transform-origin: left top;
+      transform: rotateZ(-45deg);
+      border: 1px solid red;
+      background-color: white;
+      // TODO: entries color;
+    }
+  } // end mixin
+
   .chutier_ui{
     height: 50%;
     border-bottom: 1px solid red;
@@ -492,6 +552,7 @@ main[role="main"]{
       padding-top: 35px;
       height:calc(100% - 35px);
       overflow-y: auto;
+      overflow-x: hidden;
       // box-sizing: content-box;
       // outline: 1px solid green;
       ul,li{
@@ -502,6 +563,13 @@ main[role="main"]{
         display: inline-block; vertical-align: top;
         width:49%; margin-bottom: 0.5em;
         white-space: nowrap;
+        >div{
+          display:inline-block;
+          padding-right: 2em;
+          &.ui-draggable{
+            cursor:grab;
+          }
+        }
         .entrees{
           line-height: 0;
           span{
@@ -523,6 +591,7 @@ main[role="main"]{
 
     }
   }
+  $compo_square_size:12px;
   .composition_ui{
     height:50%;
     &>h2{
@@ -543,12 +612,16 @@ main[role="main"]{
       overflow: hidden;
       white-space: nowrap;
       section.compositions-list, section.composer{
-        display: inline-block; vertical-align: top;
+        display: inline-block; vertical-align: bottom;
         height: 100%;
       }
       .compositions-list{
-        width:30%; height:100%;
+        width:30%;
+        height:100%; // this is not working
         overflow-y: auto;
+        box-sizing: border-box;
+        padding-right: 0.5em;
+        border-right: 1px solid #aaa;
         li{
           opacity: 1;
           transition: opacity 0.3s ease-in-out;
@@ -563,10 +636,25 @@ main[role="main"]{
         }
         a{
           font-size: 0.756em;
+          $w:7px;
           $s:7px; $m:5px;
           &.composition-link{
             display: inline-block;
-            width: calc(100% - #{$s +$m +2});
+            box-sizing: border-box;
+            // width:100%;
+            width: calc(100% - #{$w +$s +$m +2});
+            &:before{
+              content: "";
+              width:$w; height:$w;
+              border: 1px solid black;
+              background-color: white;
+              display: inline-block;
+              vertical-align: middle;
+              margin-right: $m;
+            }
+            &.is-active:before{
+              background-color: black;
+            }
           }
           &.delete-composition-link{
             display: inline-block;
@@ -584,12 +672,13 @@ main[role="main"]{
             // padding-left: 1em;
             &:before{
               content:"+";
-              font-weight: bold;
-              margin-right: 0.2em;
+              // font-weight: bold;
+              margin-right: $m;
             }
             display: inline-block;
             vertical-align: top;
             overflow: hidden;
+            margin-top: 0.4em;
             height:1em;
             transition: height 0.2s ease-in-out;
             &.folded{
@@ -622,13 +711,77 @@ main[role="main"]{
         }
       }
       .composer{
-        box-sizing: content-box;
+        position: relative;
+        box-sizing: border-box;
+        padding-left: 0.5em;
         width:69%;
-        padding-left: 1em;
-        border-left: 1px solid #aaa;
+        $header_height:25px;
+        $actions_height:25px;
+        $padding:5px;
+        >*{
+          box-sizing: border-box;
+        }
+        >header{
+          height:$header_height;
+          padding-bottom:$padding;
+        }
+        >.composition{
+          position: relative;
+          height:calc(100% - #{$header_height + $actions_height + $padding*2});
+          // outline: 1px solid red;
+          padding-bottom: 1em;
+
+          .field--name-documents{
+            position: relative;
+            box-sizing: border-box;
+            height: 100%;
+            width: 100%; padding:0 0.5em;
+            $h:29px; $w:calc(100% - 2px); $c:#A1A1A1;
+            &:before, &:after{
+              content: "";
+              width:$w;
+              position: absolute;
+              bottom:0; left:0;
+              // z-index: -1;
+            }
+            &:before{
+              border-top: 1px solid $c;
+              height:$h /2;
+            }
+            &:after{
+              border-left: 1px solid $c;
+              border-right: 1px solid $c;
+              height:$h;
+            }
+
+            .field__item{
+              @include draggable_sortable_field__item;
+            }
+          }
+
+          .remove-drop-zone{
+            position: absolute;
+            top:0; left:0; width:100%; height:45%;
+            z-index: 150;
+            // &.ui-droppable-hover{
+            //   background-color: red;
+            // }
+          }
+        }
+        >.actions{
+          padding-top:$padding;
+          height:$actions_height;
+          // outline: 1px solid blue;
+        }
       }
     }
   }
+  // drag and drop specifics
+  // created by js
+  .field__item.ui-draggable-dragging{
+    // outline: 1px solid green;
+    @include draggable_sortable_field__item;
+  }
 }
 
 

+ 14 - 5
sites/all/themes/custom/edlptheme/assets/styles/base/_layout.scss

@@ -9,7 +9,9 @@ body, html{
   padding:0;
   // background-color: rgb(219, 219, 219);
 }
-
+body.toolbar-horizontal.toolbar-themes.toolbar-no-tabs{
+  padding-top: 24px!important;
+}
 // div.layout-container{
 //   position: relative;
 //   width:100%; height:100%;
@@ -57,7 +59,7 @@ main[role="main"]{
   padding:7em 2em 9em;
   overflow: hidden;
   pointer-events: none;
-  height:auto;
+  height:100%;
   .layout-content{
     width: 100%; height:100%;
     overflow:hidden;
@@ -67,10 +69,17 @@ main[role="main"]{
   }
 }
 
-body[corpus-map="ready"] main[role="main"]{
-  height:100%;
-  // overflow-y: hidden;
+body.toolbar-horizontal.toolbar-themes.toolbar-no-tabs{
+    main[role="main"]{
+      padding-top: 7em + 1em;
+    }
 }
+// body.toolbar-fixed.toolbar-tray-open{
+//   main[role="main"]{
+//     padding-top: 7em + 4em;
+//     // overflow-y: hidden;
+//   }
+// }
 
 footer[role="contentinfo"]{
   // outline: 1px solid pink;

+ 3 - 0
sites/all/themes/custom/edlptheme/edlptheme.libraries.yml

@@ -22,6 +22,9 @@ global-js:
     - core/matchmedia.addListener
     - core/jquery
     - core/jquery.once
+    - core/jquery.ui.draggable
+    - core/jquery.ui.dropable
+    - core/jquery.ui.sortable
     - url_to_video_filter/player_embed
     - url_to_video_filter/vimeo_embed
     - url_to_video_filter/youtube_embed

+ 25 - 0
sites/all/themes/custom/edlptheme/edlptheme.theme

@@ -103,6 +103,31 @@ function edlptheme_preprocess_node__enregistrement__search_index(&$vars){
   // dpm($vars['link_attributes']);
 }
 
+
+function edlptheme_preprocess_node__enregistrement__compo(&$vars){
+  $node = $vars['elements']['#node'];
+  $options = ['absolute' => TRUE];
+  $url = Url::fromRoute('entity.node.canonical', ['node' => $node->id()], $options);
+  $system_path = $url->getInternalPath();
+  // get the audio file url
+  $field_son_values = $node->get('field_son')->getValue();
+  $son_fid = count($field_son_values) ? $field_son_values[0]['target_id'] : "";
+  $son_file = \Drupal\file\Entity\File::load($son_fid);
+  $son_url = null;
+  if($son_file){
+    $son_uri = $son_file->getFileUri();
+    $son_url = file_create_url($son_uri);
+  }
+
+  $vars['link_attributes'] = new Attribute(array(
+    'data-drupal-link-system-path' => $system_path=='' ? '<front>' : $system_path,
+    'audio_url' => $son_url,
+    'nid' => $node->id(),
+    'class' => array('audio-link', 'ajax-link')
+  ));
+  // dpm($vars['link_attributes']);
+}
+
 function edlptheme_preprocess_node__enregistrement__player_cartel(&$vars){
   // dpm($vars);
   // if transcript not empty

+ 91 - 0
sites/all/themes/custom/edlptheme/templates/content/node--enregistrement--compo.html.twig

@@ -0,0 +1,91 @@
+{#
+/**
+ * @file
+ * Theme override to display a node.
+ *
+ * Available variables:
+ * - node: The node entity with limited access to object properties and methods.
+ *   Only method names starting with "get", "has", or "is" and a few common
+ *   methods such as "id", "label", and "bundle" are available. For example:
+ *   - node.getCreatedTime() will return the node creation timestamp.
+ *   - node.hasField('field_example') returns TRUE if the node bundle includes
+ *     field_example. (This does not indicate the presence of a value in this
+ *     field.)
+ *   - node.isPublished() will return whether the node is published or not.
+ *   Calling other methods, such as node.delete(), will result in an exception.
+ *   See \Drupal\node\Entity\Node for a full list of public properties and
+ *   methods for the node object.
+ * - label: The title of the node.
+ * - content: All node items. Use {{ content }} to print them all,
+ *   or print a subset such as {{ content.field_example }}. Use
+ *   {{ content|without('field_example') }} to temporarily suppress the printing
+ *   of a given child element.
+ * - author_picture: The node author user entity, rendered using the "compact"
+ *   view mode.
+ * - metadata: Metadata for this node.
+ * - date: Themed creation date field.
+ * - author_name: Themed author name field.
+ * - url: Direct URL of the current node.
+ * - display_submitted: Whether submission information should be displayed.
+ * - attributes: HTML attributes for the containing element.
+ *   The attributes.class element may contain one or more of the following
+ *   classes:
+ *   - node: The current template type (also known as a "theming hook").
+ *   - node--type-[type]: The current node type. For example, if the node is an
+ *     "Article" it would result in "node--type-article". Note that the machine
+ *     name will often be in a short form of the human readable label.
+ *   - node--view-mode-[view_mode]: The View Mode of the node; for example, a
+ *     teaser would result in: "node--view-mode-teaser", and
+ *     full: "node--view-mode-full".
+ *   The following are controlled through the node publishing options.
+ *   - node--promoted: Appears on nodes promoted to the front page.
+ *   - node--sticky: Appears on nodes ordered above other non-sticky nodes in
+ *     teaser listings.
+ *   - node--unpublished: Appears on unpublished nodes visible only to site
+ *     admins.
+ * - title_attributes: Same as attributes, except applied to the main title
+ *   tag that appears in the template.
+ * - content_attributes: Same as attributes, except applied to the main
+ *   content tag that appears in the template.
+ * - author_attributes: Same as attributes, except applied to the author of
+ *   the node tag that appears in the template.
+ * - title_prefix: Additional output populated by modules, intended to be
+ *   displayed in front of the main title tag that appears in the template.
+ * - title_suffix: Additional output populated by modules, intended to be
+ *   displayed after the main title tag that appears in the template.
+ * - view_mode: View mode; for example, "teaser" or "full".
+ * - teaser: Flag for the teaser state. Will be true if view_mode is 'teaser'.
+ * - page: Flag for the full page state. Will be true if view_mode is 'full'.
+ * - readmore: Flag for more state. Will be true if the teaser content of the
+ *   node cannot hold the main body content.
+ * - logged_in: Flag for authenticated user status. Will be true when the
+ *   current user is a logged-in member.
+ * - is_admin: Flag for admin user status. Will be true when the current user
+ *   is an administrator.
+ *
+ * @see template_preprocess_node()
+ *
+ * @todo Remove the id attribute (or make it a class), because if that gets
+ *   rendered twice on a page this is invalid CSS for example: two lists
+ *   in different view modes.
+ */
+#}
+{%
+  set classes = [
+    'node',
+    'node--type-' ~ node.bundle|clean_class,
+    node.isPromoted() ? 'node--promoted',
+    node.isSticky() ? 'node--sticky',
+    not node.isPublished() ? 'node--unpublished',
+    view_mode ? 'node--view-mode-' ~ view_mode|clean_class,
+  ]
+%}
+{{ attach_library('classy/node') }}
+<article{{ attributes.addClass(classes) }}>
+  <h2{{ title_attributes.addClass('node-title') }}>
+    <a href="{{ url }}" rel="bookmark" {{ link_attributes }}>
+      {% set text = label|render|striptags %}
+      {{ text|length > 25 ? text|slice(0, 25)|split(' ')|slice(0, -1)|join(' ') ~ '…' : text }}
+    </a>
+  </h2>
+</article>

+ 103 - 0
sites/all/themes/custom/edlptheme/templates/content/node--page--image-2-columns0.html.twig

@@ -0,0 +1,103 @@
+{#
+/**
+ * @file
+ * Default theme implementation to display a node.
+ *
+ * Available variables:
+ * - node: The node entity with limited access to object properties and methods.
+ *   Only method names starting with "get", "has", or "is" and a few common
+ *   methods such as "id", "label", and "bundle" are available. For example:
+ *   - node.getCreatedTime() will return the node creation timestamp.
+ *   - node.hasField('field_example') returns TRUE if the node bundle includes
+ *     field_example. (This does not indicate the presence of a value in this
+ *     field.)
+ *   - node.isPublished() will return whether the node is published or not.
+ *   Calling other methods, such as node.delete(), will result in an exception.
+ *   See \Drupal\node\Entity\Node for a full list of public properties and
+ *   methods for the node object.
+ * - label: The title of the node.
+ * - content: All node items. Use {{ content }} to print them all,
+ *   or print a subset such as {{ content.field_example }}. Use
+ *   {{ content|without('field_example') }} to temporarily suppress the printing
+ *   of a given child element.
+ * - author_picture: The node author user entity, rendered using the "compact"
+ *   view mode.
+ * - metadata: Metadata for this node.
+ * - date: Themed creation date field.
+ * - author_name: Themed author name field.
+ * - url: Direct URL of the current node.
+ * - display_submitted: Whether submission information should be displayed.
+ * - attributes: HTML attributes for the containing element.
+ *   The attributes.class element may contain one or more of the following
+ *   classes:
+ *   - node: The current template type (also known as a "theming hook").
+ *   - node--type-[type]: The current node type. For example, if the node is an
+ *     "Article" it would result in "node--type-article". Note that the machine
+ *     name will often be in a short form of the human readable label.
+ *   - node--view-mode-[view_mode]: The View Mode of the node; for example, a
+ *     teaser would result in: "node--view-mode-teaser", and
+ *     full: "node--view-mode-full".
+ *   The following are controlled through the node publishing options.
+ *   - node--promoted: Appears on nodes promoted to the front page.
+ *   - node--sticky: Appears on nodes ordered above other non-sticky nodes in
+ *     teaser listings.
+ *   - node--unpublished: Appears on unpublished nodes visible only to site
+ *     admins.
+ * - title_attributes: Same as attributes, except applied to the main title
+ *   tag that appears in the template.
+ * - content_attributes: Same as attributes, except applied to the main
+ *   content tag that appears in the template.
+ * - author_attributes: Same as attributes, except applied to the author of
+ *   the node tag that appears in the template.
+ * - title_prefix: Additional output populated by modules, intended to be
+ *   displayed in front of the main title tag that appears in the template.
+ * - title_suffix: Additional output populated by modules, intended to be
+ *   displayed after the main title tag that appears in the template.
+ * - view_mode: View mode; for example, "teaser" or "full".
+ * - teaser: Flag for the teaser state. Will be true if view_mode is 'teaser'.
+ * - page: Flag for the full page state. Will be true if view_mode is 'full'.
+ * - readmore: Flag for more state. Will be true if the teaser content of the
+ *   node cannot hold the main body content.
+ * - logged_in: Flag for authenticated user status. Will be true when the
+ *   current user is a logged-in member.
+ * - is_admin: Flag for admin user status. Will be true when the current user
+ *   is an administrator.
+ *
+ * @see template_preprocess_node()
+ *
+ * @todo Remove the id attribute (or make it a class), because if that gets
+ *   rendered twice on a page this is invalid CSS for example: two lists
+ *   in different view modes.
+ *
+ * @ingroup themeable
+ */
+#}
+{%
+  set classes = [
+    'node',
+    'node--type-' ~ node.bundle|clean_class,
+    node.isPromoted() ? 'node--promoted',
+    node.isSticky() ? 'node--sticky',
+    not node.isPublished() ? 'node--unpublished',
+    view_mode ? 'node--view-mode-' ~ view_mode|clean_class,
+    'clearfix',
+  ]
+%}
+{{ attach_library('classy/node') }}
+<article{{ attributes.addClass(classes) }}>
+
+  <div{{ content_attributes }}>
+    {{ content.field_visuel }}
+  </div>
+  <header>
+    {{ title_prefix }}
+    {% if not page %}
+      <h2{{ title_attributes.addClass('node-title') }}>
+        <a href="{{ url }}" rel="bookmark"  {{ link_attributes }}>{{ label }}</a>
+      </h2>
+    {% endif %}
+    {{ title_suffix }}
+    {{ content|without('field_visuel') }}
+  </header>
+
+</article>

+ 31 - 0
sites/default/config/sync/core.entity_form_display.composition.composition.default.yml

@@ -0,0 +1,31 @@
+uuid: 5838eb4c-2373-447c-b26b-8f978fa2b6b2
+langcode: fr
+status: true
+dependencies:
+  module:
+    - edlp_studio
+id: composition.composition.default
+targetEntityType: composition
+bundle: composition
+mode: default
+content:
+  documents:
+    type: entity_reference_autocomplete
+    weight: 1
+    settings:
+      match_operator: CONTAINS
+      size: 60
+      placeholder: ''
+    region: content
+    third_party_settings: {  }
+  name:
+    type: string_textfield
+    weight: 0
+    region: content
+    settings:
+      size: 60
+      placeholder: ''
+    third_party_settings: {  }
+hidden:
+  langcode: true
+  user_id: true

+ 24 - 0
sites/default/config/sync/core.entity_view_display.composition.composition.default.yml

@@ -0,0 +1,24 @@
+uuid: cfabc365-2a06-409a-b620-ce6905b50c08
+langcode: fr
+status: true
+dependencies:
+  module:
+    - edlp_studio
+id: composition.composition.default
+targetEntityType: composition
+bundle: composition
+mode: default
+content:
+  documents:
+    label: hidden
+    type: entity_reference_entity_view
+    weight: 0
+    region: content
+    settings:
+      view_mode: compo
+      link: false
+    third_party_settings: {  }
+hidden:
+  langcode: true
+  name: true
+  user_id: true

+ 54 - 0
sites/default/config/sync/core.entity_view_display.node.enregistrement.compo.yml

@@ -0,0 +1,54 @@
+uuid: fa0a6cf7-0499-4f91-87d5-b58ec7d7154b
+langcode: fr
+status: true
+dependencies:
+  config:
+    - core.entity_view_mode.node.compo
+    - field.field.node.enregistrement.body
+    - field.field.node.enregistrement.field_collectionneurs
+    - field.field.node.enregistrement.field_description
+    - field.field.node.enregistrement.field_entrees
+    - field.field.node.enregistrement.field_genres
+    - field.field.node.enregistrement.field_langues
+    - field.field.node.enregistrement.field_locuteurs
+    - field.field.node.enregistrement.field_nbr_locuteurs
+    - field.field.node.enregistrement.field_son
+    - field.field.node.enregistrement.field_transcript_trad
+    - field.field.node.enregistrement.field_transcript_vo
+    - field.field.node.enregistrement.field_workflow
+    - node.type.enregistrement
+  module:
+    - user
+id: node.enregistrement.compo
+targetEntityType: node
+bundle: enregistrement
+mode: compo
+content:
+  content_moderation_control:
+    weight: -20
+    region: content
+    settings: {  }
+    third_party_settings: {  }
+  field_entrees:
+    type: entity_reference_label
+    weight: 0
+    region: content
+    label: hidden
+    settings:
+      link: false
+    third_party_settings: {  }
+hidden:
+  body: true
+  chutier_actions: true
+  field_collectionneurs: true
+  field_description: true
+  field_genres: true
+  field_langues: true
+  field_locuteurs: true
+  field_nbr_locuteurs: true
+  field_son: true
+  field_transcript_trad: true
+  field_transcript_vo: true
+  field_workflow: true
+  langcode: true
+  links: true

+ 62 - 0
sites/default/config/sync/core.entity_view_display.node.enregistrement.compo_document_.yml

@@ -0,0 +1,62 @@
+uuid: aba8a7c9-a4e5-4fa6-b3ed-7370a79ec05d
+langcode: fr
+status: false
+dependencies:
+  config:
+    - core.entity_view_mode.node.compo_document_
+    - field.field.node.enregistrement.body
+    - field.field.node.enregistrement.field_collectionneurs
+    - field.field.node.enregistrement.field_description
+    - field.field.node.enregistrement.field_entrees
+    - field.field.node.enregistrement.field_genres
+    - field.field.node.enregistrement.field_langues
+    - field.field.node.enregistrement.field_locuteurs
+    - field.field.node.enregistrement.field_nbr_locuteurs
+    - field.field.node.enregistrement.field_son
+    - field.field.node.enregistrement.field_transcript_trad
+    - field.field.node.enregistrement.field_transcript_vo
+    - field.field.node.enregistrement.field_workflow
+    - node.type.enregistrement
+  module:
+    - file
+    - user
+id: node.enregistrement.compo_document_
+targetEntityType: node
+bundle: enregistrement
+mode: compo_document_
+content:
+  content_moderation_control:
+    weight: -20
+    region: content
+    settings: {  }
+    third_party_settings: {  }
+  field_entrees:
+    type: entity_reference_label
+    weight: 0
+    region: content
+    label: hidden
+    settings:
+      link: false
+    third_party_settings: {  }
+  field_son:
+    type: file_default
+    weight: 1
+    region: content
+    label: hidden
+    settings:
+      use_description_as_link_text: true
+    third_party_settings: {  }
+hidden:
+  body: true
+  chutier_actions: true
+  field_collectionneurs: true
+  field_description: true
+  field_genres: true
+  field_langues: true
+  field_locuteurs: true
+  field_nbr_locuteurs: true
+  field_transcript_trad: true
+  field_transcript_vo: true
+  field_workflow: true
+  langcode: true
+  links: true

+ 1 - 9
sites/default/config/sync/core.entity_view_display.node.enregistrement.default.yml

@@ -44,15 +44,6 @@ content:
     settings:
       link: false
     third_party_settings: {  }
-  field_nbr_locuteurs:
-    weight: 2
-    label: above
-    settings:
-      thousand_separator: ''
-      prefix_suffix: true
-    third_party_settings: {  }
-    type: number_integer
-    region: content
 hidden:
   body: true
   chutier_actions: true
@@ -60,6 +51,7 @@ hidden:
   field_genres: true
   field_langues: true
   field_locuteurs: true
+  field_nbr_locuteurs: true
   field_son: true
   field_transcript_trad: true
   field_transcript_vo: true

+ 10 - 0
sites/default/config/sync/core.entity_view_mode.node.compo.yml

@@ -0,0 +1,10 @@
+uuid: 800d6f1f-c11e-4f91-9c21-c5e45cfdeecb
+langcode: fr
+status: true
+dependencies:
+  module:
+    - node
+id: node.compo
+label: 'compo (Document)'
+targetEntityType: node
+cache: true

+ 10 - 0
sites/default/config/sync/core.entity_view_mode.node.compo_document_.yml

@@ -0,0 +1,10 @@
+uuid: 6366c514-2948-44bf-b665-911d037545fe
+langcode: fr
+status: true
+dependencies:
+  module:
+    - node
+id: node.compo_document_
+label: 'compo (Document)'
+targetEntityType: node
+cache: true