Recherche

Sommaire

Pages enfant
  • ContentViews, Layouts et Widgets par l'exemple

Pour gérer la façon dont s'affiche les documents ainsi que les champs, nuxeo s'appuie sur les notions de contentViews, de layouts et de Widgets.

Cette page, au travers d'un exemple concret, a pour objectif d'expliquer chacun de ces concepts qui sont par ailleurs bien documentés sur le site de Nuxeo.

Dans notre exemple, notre objectif est de ne plus afficher les champs "sujets" et "couverture" (aussi bien en édition qu'en consultation), de ne plus afficher l'état (mais de continuer à afficher la version) et d'afficher le lien universel.

Pour cela, nous allons déclarer un nouveau contentViews dans lequel on va pointer vers de nouveaux layouts.

Si l'on regarde le fichier qui décrit les types de documents, on a par exemple pour le document de type workspace :

 <type id="Workspace">
      <label>Workspace</label>
      [...]
     <!-- pointe sur des layouts du même nom -->
     <!-- mode = any => layout utilisé quel que que soit le contexte -->
      <layouts mode="any">
        <layout>heading</layout>
      </layouts>
      <!-- mode=edit => layouts utilisé lorsque l'on modifie un workspace -->
      <layouts mode="edit">
        <layout>heading</layout>
        <layout>dublincore</layout>
      </layouts>
     <!-- contentViews utilisés pour l'affichage des contenu
      document_content est utilisé pour l'affichage du contenu du workspace
      document_trash_content pour afficher le contenu de la "corbeille" -->
          document_content pour afficher
      <contentViews category="content">
        <contentView>document_content</contentView>
      </contentViews>
      <contentViews category="trash_content">
        <contentView>document_trash_content</contentView>
      </contentViews>
    </type>

contentViews ?

C'est un fichier qui définit comment obtenir et afficher des listes de documents. On  commence par déclarer la requête qui permet d'obtenir les document et ensuite on lie l'affichage des résultats à un (ou des) layouts qui vont gérer la façon dont il s'affiche .

Voici le contenu (commenté) de notre fichier templates/custom/config/esup-contentview-config.xml :

<?xml version="1.0"?>
<component name="org.esup.ecm.webapp.contentview.contrib">
  <extension target="org.nuxeo.ecm.platform.ui.web.ContentViewService"
    point="contentViews">
<!-- contenu d'un document (de type folder ou workspace -->
    <contentView name="esup_document_content">
      <!-- définition de la requête -->
      <coreQueryPageProvider>
        <property name="coreSession">#{documentManager}</property>
        <!-- requete au format NXQL, possibilités de définir des paramètres (le ?)
        qui sont remplacés par les varibales définis dans les attributs parameters : Attention à l'ordre !!! -->
        <pattern>
          SELECT * FROM Document WHERE ecm:parentId = ? AND
          ecm:isCheckedInVersion = 0 AND ecm:mixinType != 'HiddenInNavigation'
          AND ecm:currentLifeCycleState != 'deleted'
        </pattern>
        <!-- ici le paramètre est l'identifiant du document parent, la requete revient donc à lister tous les
         documents enfants n'ayant pas été supprimés et n'étant pas de type caché (HiddenInNavigation) -->
        <parameter>#{currentDocument.id}</parameter>
         <!-- critère de tri par defaut -->
        <sort column="dc:title" ascending="true" />
         <!-- nombre de resultats par defaut -->
        <pageSize>20</pageSize>
       </coreQueryPageProvider>
      <!-- permet de modifier le nombre de resultats affiches -->
      <useGlobalPageSize>true</useGlobalPageSize>
      <!-- critière imposant le rafraichissement du cache -->
      <refresh>
        <event>documentChanged</event>
        <event>documentChildrenChanged</event>
      </refresh>
       <!-- cache -->
      <cacheKey>#{currentDocument.id}</cacheKey>
      <cacheSize>10</cacheSize>

       <!-- définition des fichiers de layouts qui vont permettre l'affichage personnalisé des résultats -->
      <resultLayouts>
        <layout name="esup_document_listing_ajax" title="document_listing"
          translateTitle="true" iconPath="/icons/document_listing_icon.png" />
        <layout name="esup_document_listing_ajax_compact_2_columns"
          title="document_listing_compact_2_columns" translateTitle="true"
          iconPath="/icons/document_listing_compact_2_columns_icon.png" />
        <layout name="esup_document_listing_ajax_icon_2_columns"
          title="document_listing_icon_2_columns" translateTitle="true"
          iconPath="/icons/document_listing_icon_2_columns_icon.png" />
      </resultLayouts>
      <!-- gere l'affichage des actions (boutons) au bas de la liste de documents -->
      <selectionList>CURRENT_SELECTION</selectionList>
      <actions category="CURRENT_SELECTION_LIST" />
    </contentView>

<!-- on procède de la même façon pour les contenus des "corbeilles" -->
<!-- la requete NXQL remonte ici tous les documents enfant supprimés (ecm:currentLifeCycleState = 'deleted')-->
    <contentView name="esup_document_trash_content">
      <coreQueryPageProvider>
        <property name="coreSession">#{documentManager}</property>
        <pattern>
          SELECT * FROM Document WHERE ecm:parentId = ? AND
          ecm:isCheckedInVersion = 0 AND ecm:mixinType != 'HiddenInNavigation'
          AND ecm:currentLifeCycleState = 'deleted'
        </pattern>
        <parameter>#{currentDocument.id}</parameter>
        <sort column="dc:title" ascending="true" />
        <pageSize>20</pageSize>
      </coreQueryPageProvider>
      <useGlobalPageSize>true</useGlobalPageSize>
      <refresh>
        <event>documentChanged</event>
        <event>documentChildrenChanged</event>
      </refresh>
      <cacheKey>#{currentDocument.id}</cacheKey>
      <resultLayouts>
        <layout name="esup_document_listing_ajax" title="document_listing"
          translateTitle="true" iconPath="/icons/document_listing_icon.png" />
        <layout name="esup_document_listing_ajax_compact_2_columns"
          title="document_listing_compact_2_columns" translateTitle="false"
          iconPath="/icons/document_listing_compact_2_columns_icon.png" />
        <layout name="esup_document_listing_ajax_icon_2_columns"
          title="document_listing_icon_2_columns" translateTitle="true"
          iconPath="/icons/document_listing_icon_2_columns_icon.png" />
      </resultLayouts>
      <selectionList>CURRENT_SELECTION_TRASH</selectionList>
      <actions category="CURRENT_SELECTION_TRASH_LIST" />
    </contentView>
    <!-- contenu des sections de publications, même principe  -->
    <contentView name="esup_section_content">
      <coreQueryPageProvider>
        <property name="coreSession">#{documentManager}</property>
        <pattern>
          SELECT * FROM Document WHERE ecm:parentId = ? AND
          ecm:isCheckedInVersion = 0 AND ecm:mixinType != 'HiddenInNavigation'
          AND ecm:currentLifeCycleState != 'deleted'
        </pattern>
        <parameter>#{currentDocument.id}</parameter>
        <sort column="dc:title" ascending="true" />
        <pageSize>20</pageSize>
      </coreQueryPageProvider>
      <useGlobalPageSize>true</useGlobalPageSize>
      <refresh>
        <event>documentChanged</event>
        <event>documentChildrenChanged</event>
      </refresh>
      <cacheKey>#{currentDocument.id}</cacheKey>
      <cacheSize>10</cacheSize>
      <resultLayouts>
        <layout name="esup_document_listing_ajax" title="document_listing"
          translateTitle="true" iconPath="/icons/document_listing_icon.png" />
        <layout name="esup_document_listing_ajax_compact_2_columns"
          title="document_listing_compact_2_columns" translateTitle="true"
          iconPath="/icons/document_listing_compact_2_columns_icon.png" />
        <layout name="esup_document_listing_ajax_icon_2_columns"
          title="document_listing_icon_2_columns" translateTitle="true"
          iconPath="/icons/document_listing_icon_2_columns_icon.png" />
      </resultLayouts>
      <selectionList>CURRENT_SELECTION_SECTIONS</selectionList>
      <actions category="CURRENT_SELECTION_SECTIONS_LIST" />
    </contentView>
  </extension>
</component>

Il nous faut maintenant définir les layouts que l'on a lié à nos contentViews.

Un layout permet de définir quels champs d'un document (correspondant à des widgets) on affiche et comment on les affiche (typiquement on trouve deux types d'affichage : un affichage de type listing (tableau avec widget en colonnne) et un affichage de type summary (affichage en ligne des champs). Les layout (et les widgets) sont liés à des modes (VIEW, EDIT, CREATE, SUMMARY ou ANY qui correpond à tous les modes) permettant de définir un affichage différent suivant le contexte (consultation d'un document ou édition par exemple)

Voici le contenu (commenté toujours ...) de notre fichier templates/custom/config/esup-layouts-listing-config.xml :

 <?xml version="1.0"?>
<component name="org.esup.ecm.platform.forms.layouts.webapp.listing">
<!-- nous permet de bénéficier des la definition des widgets de ce composant -->
<require>org.nuxeo.ecm.platform.forms.layouts.webapp.listing</require>
  <extension target="org.nuxeo.ecm.platform.forms.layout.WebLayoutManager" point="layouts">
    <!-- definition du layout esup_document_listing qu'on appelle dans nos contentViews du fichier ci-dessus -->
    <layout name="esup_document_listing">
      <templates>
        <!-- fichier xhtml lié, on garde celui par defaut qui convient en general très bien -->
        <template mode="any">/layouts/layout_listing_template.xhtml</template>
      </templates>
      <properties mode="any">
       <!-- affichage des titres et couleur une ligne sur deux -->
        <property name="showListingHeader">true</property>
        <property name="showRowEvenOddClass">true</property>
      </properties>
      <!-- définition du contenu de chaque colonne -->
      <columns>
        <column>
          <!-- affiche le widget de selection (case a cocher) -->
          <properties mode="any">
            <property name="isListingSelectionBoxWithCurrentDocument">
              true
            </property>
            <property name="useFirstWidgetLabelAsColumnHeader">false</property>
            <property name="columnStyleClass">iconColumn</property>
          </properties>
          <widget>listing_selection_box_with_current_document</widget>
        </column>

        <column>
         <!-- widget pour affciher l'icone liée au document -->
          <properties mode="any">
            <property name="useFirstWidgetLabelAsColumnHeader">false</property>
            <property name="columnStyleClass">iconColumn</property>
          </properties>
          <widget>listing_icon_type</widget>
        </column>
        <column>
         <!-- widget affichant le titre du document avec un lien, colonne triable -->
          <properties mode="any">
            <property name="useFirstWidgetLabelAsColumnHeader">true</property>
            <property name="sortPropertyName">dc:title</property>
          </properties>
          <widget>listing_title_link</widget>
        </column>
        <column>
         <!-- widget affichant l'icone du cadenas si le document est verouillé -->
          <properties mode="any">
            <property name="columnStyleClass">iconColumn</property>
          </properties>
          <widget>listing_lock_icon</widget>
        </column>
        <column>
        <!-- widget affichant la date de modification, colonne triable -->
          <properties mode="any">
            <property name="useFirstWidgetLabelAsColumnHeader">true</property>
            <property name="sortPropertyName">dc:modified</property>
            <property name="defaultSortAscending">false</property>
          </properties>
          <widget>listing_modification_date</widget>
        </column>
        <column>
        <!-- widget affichant  le createur du document, triable -->
          <properties mode="any">
            <property name="useFirstWidgetLabelAsColumnHeader">true</property>
            <property name="sortPropertyName">dc:creator</property>
          </properties>
          <widget>listing_author</widget>
        </column>
        <column>
         <!-- widget affichant la version du document -->
          <properties mode="any">
            <property name="columnStyleClass">iconColumn</property>
            <property name="useFirstWidgetLabelAsColumnHeader">true</property>
            <property name="isSortable">false</property>
          </properties>
          <widget>listing_version</widget>
        </column>
        <column>
         <!--widget affichant le lien liveedit si installé dans le navigateur -->
          <properties mode="any">
            <property name="columnStyleClass">iconColumn</property>
          </properties>
          <widget>listing_livedit_link</widget>
        </column>
      </columns>
    </layout>
<!-- même principe pour les autres layouts de type listing -->
    <layout name="esup_document_listing_ajax">
      <templates>
        <template mode="any">
          /layouts/layout_listing_ajax_template.xhtml
        </template>
      </templates>
      <properties mode="any">
        <property name="showListingHeader">true</property>
        <property name="showRowEvenOddClass">true</property>
      </properties>
      <columns>
        <column>
          <properties mode="any">
            <property name="isListingSelectionBoxWithCurrentDocument">
              true
            </property>
            <property name="useFirstWidgetLabelAsColumnHeader">false</property>
            <property name="columnStyleClass">iconColumn</property>
          </properties>
          <widget>listing_ajax_selection_box_with_current_document</widget>
        </column>
        <column>
          <properties mode="any">
            <property name="useFirstWidgetLabelAsColumnHeader">false</property>
            <property name="columnStyleClass">iconColumn</property>
          </properties>
          <widget>listing_icon_type</widget>
        </column>
        <column>
          <properties mode="any">
            <property name="useFirstWidgetLabelAsColumnHeader">true</property>
            <property name="sortPropertyName">dc:title</property>
          </properties>
          <widget>listing_title_link</widget>
        </column>
        <column>
          <properties mode="any">
            <property name="columnStyleClass">iconColumn</property>
          </properties>
          <widget>listing_lock_icon</widget>
        </column>
        <column>
          <properties mode="any">
            <property name="useFirstWidgetLabelAsColumnHeader">true</property>
            <property name="sortPropertyName">dc:modified</property>
            <property name="defaultSortAscending">false</property>
          </properties>
          <widget>listing_modification_date</widget>
        </column>
        <column>
          <properties mode="any">
            <property name="useFirstWidgetLabelAsColumnHeader">true</property>
            <property name="sortPropertyName">dc:creator</property>
          </properties>
          <widget>listing_author</widget>
        </column>
        </columns>
    </layout>

    <layout name="esup_document_listing_compact_2_columns">
      <templates>
        <template mode="any">/layouts/layout_listing_template.xhtml</template>
      </templates>
      <columns>
        <column alwaysSelected="true">
          <properties mode="any">
            <property name="columnStyleClass">iconColumn</property>
          </properties>
          <widget>listing_selection_box_with_current_document</widget>
        </column>
        <column alwaysSelected="true">
          <properties mode="any">
            <property name="columnStyleClass">iconColumn</property>
          </properties>
          <widget>listing_icon_type</widget>
        </column>
        <column alwaysSelected="true">
          <widget>listing_title_link</widget>
          <widget>listing_modification_date</widget>
          <widget>listing_author</widget>
        </column>
      </columns>
    </layout>

    <layout name="esup_document_listing_ajax_compact_2_columns">
      <templates>
        <template mode="any">
          /layouts/layout_listing_ajax_template.xhtml
        </template>
      </templates>
      <columns>
        <column alwaysSelected="true">
          <properties mode="any">
            <property name="columnStyleClass">iconColumn</property>
          </properties>
          <widget>listing_ajax_selection_box_with_current_document</widget>
        </column>
        <column alwaysSelected="true">
          <properties mode="any">
            <property name="columnStyleClass">iconColumn</property>
          </properties>
          <widget>listing_icon_type</widget>
        </column>
        <column alwaysSelected="true">
          <widget>listing_title_link</widget>
          <widget>listing_modification_date</widget>
          <widget>listing_author</widget>
        </column>
      </columns>
    </layout>

    <layout name="esup_document_listing_icon_2_columns">
      <templates>
        <template mode="any">/layouts/layout_listing_template.xhtml</template>
      </templates>
      <columns>
        <column alwaysSelected="true">
          <widget>listing_selection_box_with_current_document</widget>
        </column>
        <column alwaysSelected="true">
          <widget>listing_big_icon_type_link</widget>
        </column>
        <column alwaysSelected="true">
          <widget>listing_title_link</widget>
        </column>
      </columns>
    </layout>

    <layout name="esup_document_listing_ajax_icon_2_columns">
      <templates>
        <template mode="any">
          /layouts/layout_listing_ajax_template.xhtml
        </template>
      </templates>
      <columns>
        <column alwaysSelected="true">
          <widget>listing_ajax_selection_box_with_current_document</widget>
        </column>
        <column alwaysSelected="true">
          <widget>listing_big_icon_type_link</widget>
        </column>
        <column alwaysSelected="true">
          <widget>listing_title_link</widget>
        </column>
      </columns>
    </layout>
  </extension>

</component>

La définition des contentViews et de ces layout nous ont permis de définir comme on le souhaitait nos listes de documents. Il nous faut maintenant configurer les layouts que nous souhaitons utiliser pour remplacer le layout dublincore.

Voici le fichier templates/custom/config/esup-layouts-config que nous utilisons à cet effet :

 <?xml version="1.0"?>
<component name="org.esup.ecm.platform.forms.layouts.webapp">
<require>org.nuxeo.ecm.platform.forms.layouts.webapp</require>
  <extension target="org.nuxeo.ecm.platform.forms.layout.WebLayoutManager"
    point="layouts">
     <!-- definitiond en notre layout qui va afficher seulement les champs dublin core que l'on désire
     ie ne pas afficher le sujet et la couverture -->
    <layout name="esupdublincore">
      <templates>
        <!-- fichier xhtml associé, on conserve toujours celui par défaut -->
        <template mode="any">/layouts/layout_default_template.xhtml</template>
      </templates>
      <!-- on définit quels sont les widgets que l'on affiche (par ligne =row ) -->
      <rows>
        <row>
          <widget>nature</widget>
        </row>
        <row>
          <widget>rights</widget>
        </row>
        <row>
          <widget>source</widget>
        </row>
        <row>
          <widget>created</widget>
        </row>
        <row>
          <widget>modified</widget>
        </row>
        <row>
          <widget>format</widget>
        </row>
        <row>
          <widget>language</widget>
        </row>
        <row>
          <widget>expired</widget>
        </row>
        <row>
          <widget>author</widget>
        </row>
        <row>
          <widget>contributors</widget>
        </row>
      </rows>
    </layout>
  </extension>
</component>

Dernier point, nous devons maintenant configurer un dernier layout, celui utilisé pour des affichage de type summary (onglet résumé dans un document de type fichier).

Dans ce fichier, nous souhaitons utiliser deux widgets qui n'existent pas et que nous devons donc définir : le widget affichant uniquement la version du document (celui par défaut affiche à la fois l'état et la version) et le widget affichant le lien Universel ESUP (cf page de documentation concernant ce dernier).

Nous n'abordons pas ici la façon de déployer les fichiers xhtml (ainsi que d'eventuels fichiers de langue associés) liés aux nouveaux widgets.
Il faut en effet faire un plugin à cet effet. Voir la documentation spécifique ce point

Voici le fichier templates/custom/config/esup-layouts-summary-config.xml que nous utilisons :

 <?xml version="1.0"?>
<component name="org.esup.ecm.platform.forms.layouts.webapp.summary">
<require>org.nuxeo.ecm.platform.forms.layouts.webapp.summary</require>
  <extension target="org.nuxeo.ecm.platform.forms.layout.WebLayoutManager"
    point="widgettypes">
    <!-- declaration de nouveaux type de widgets, un type, une classe et un fichier xhtml associé
     (ici, c'est un fichier que nous devons créer -->
    <widgetType name="esup_summary_current_document_version">
      <handler-class>
        org.nuxeo.ecm.platform.forms.layout.facelets.plugins.TemplateWidgetTypeHandler
      </handler-class>
      <property name="template">
        /widgets/summary/esup_version_widget_template.xhtml
      </property>
    </widgetType>
    <widgetType name="summary_current_document_esupLink">
      <handler-class>
        org.nuxeo.ecm.platform.forms.layout.facelets.plugins.TemplateWidgetTypeHandler
      </handler-class>
      <property name="template">
        /widgets/summary/esup_permanent_link_widget_template.xhtml
      </property>
    </widgetType>
  </extension>

  <extension target="org.nuxeo.ecm.platform.forms.layout.WebLayoutManager"
    point="widgets">
  <!-- une fois le type défini, on défini le nom du wodget et on l'associe au type précédemment défini -->
    <widget name="esup_summary_current_document_version"
      type="esup_summary_current_document_version" />
    <widget name="summary_current_document_esupLink"
      type="summary_current_document_esupLink" />
  </extension>

 <!-- nous pouvons maintenant surcharger le layout default_summary_layout avec
   les wigets que nous venons de définir --<
  <extension target="org.nuxeo.ecm.platform.forms.layout.WebLayoutManager"
    point="layouts">
    <layout name="default_summary_layout">
      <templates>
        <template mode="any">/layouts/layout_summary_template.xhtml</template>
      </templates>
      <rows>
        <row>
          <widget>summary_current_document_files</widget>
          <widget>summary_current_document_description</widget>
          <widget>summary_current_document_view</widget>
          <widget>summary_current_document_dublincore</widget>
          <widget>summary_current_document_comments</widget>
        </row>
        <row>
          <widget>esup_summary_current_document_version</widget>
        <widget>summary_current_document_esupLink</widget>
          <widget>summary_current_document_actions</widget>
          <widget>summary_current_document_tagging</widget>
          <widget>summary_current_document_relations</widget>
        </row>
        <row>
          <widget>summary_current_document_publications</widget>
        </row>
        <row>
          <widget>summary_current_document_single_tasks</widget>
        </row>
      </rows>
    </layout>
  </extension>
</component>

Maintenant, il ne nous reste plus qu'à modifier les types de documents concernés pour qu'ils utilisent nos layout et nos contentViews.

C'est ce que fait notre fichier templates/custom/config/esup-ecm-types-config.xml (extrait ...)

<?xml version="1.0" encoding="UTF-8"?>
<component name="org.esup.ecm.platform.types">
<require>org.nuxeo.ecm.platform.types</require>
  <extension target="org.nuxeo.ecm.platform.types.TypeService" point="types">
    <type id="Root">
      <label>Server Root</label>
      <icon>/icons/folder.gif</icon>
      <bigIcon>/icons/folder_100.png</bigIcon>
      <description>serverRoot.description</description>
      <category>SuperDocument</category>
      <default-view>view_domains</default-view>
      <subtypes>
        <type hidden="create">Domain</type>
      </subtypes>
      <layouts mode="any">
        <layout>heading</layout> 
    </layouts>
      <layouts mode="edit">
        <layout>heading</layout>
        <layout>esupdublincore</layout>
      </layouts>     
     <contentViews category="content">
        <contentView>esup_document_content</contentView>
      </contentViews>     
    <contentViews category="trash_content">
        <contentView>esup_document_trash_content</contentView>
      </contentViews>   
    </type>   
   <type id="Workspace">
      <label>Workspace</label>
      <icon>/icons/workspace.gif</icon>
      <bigIcon>/icons/workspace_100.png</bigIcon>
      <icon-expanded>/icons/workspace_open.gif</icon-expanded>
      <category>Collaborative</category>
      <description>Workspace.description</description>
      <default-view>view_documents</default-view>
      <create-view>create_workspace</create-view>
      <subtypes>
        <type>Workspace</type>
        <type>Folder</type>
        <type>File</type>
        <type>Note</type>
      </subtypes>
      <layouts mode="any">
        <layout>heading</layout>
     </layouts>
      <layouts mode="edit">
        <layout>heading</layout>
        <layout>esupdublincore</layout>
      </layouts>
      <contentViews category="content">
        <contentView>esup_document_content</contentView>
      </contentViews>
      <contentViews category="trash_content">
        <contentView>esup_document_trash_content</contentView>
      </contentViews>
    </type>
[...]
  • Aucune étiquette