浏览代码

Merge branch 'release/0.0.4'

Vincent GUFFON 4 年之前
父节点
当前提交
c76d8c753e
共有 49 个文件被更改,包括 1463 次插入382 次删除
  1. 4 3
      components/Form/InputComponent.vue
  2. 8 8
      components/Layout/MenuComponent.vue
  3. 83 0
      config/abilities/pages/admin2ios.yaml
  4. 63 0
      config/abilities/pages/billing.yaml
  5. 35 0
      config/abilities/pages/communication.yaml
  6. 135 0
      config/abilities/pages/cotisations.yaml
  7. 7 0
      config/abilities/pages/donor.yaml
  8. 1 1
      config/abilities/pages/equipment.yaml
  9. 7 0
      config/abilities/pages/medals.yaml
  10. 23 0
      config/abilities/pages/stats.yaml
  11. 1 0
      config/nuxtConfig/env.js
  12. 5 1
      config/nuxtConfig/i18n.js
  13. 54 1
      lang/layout/fr-FR.js
  14. 1 1
      middleware/auth.ts
  15. 2 2
      package.json
  16. 0 1
      pages/index.vue
  17. 2 10
      plugins/Queries/rest.ts
  18. 16 8
      plugins/Rights/ability.ts
  19. 4 4
      services/profile/accessProfile.ts
  20. 12 12
      services/profile/organizationProfile.ts
  21. 12 12
      services/rights/abilitiesUtils.ts
  22. 4 4
      services/rights/roleUtils.ts
  23. 71 45
      services/utils/hydraParser.ts
  24. 0 81
      services/utils/objectProperties.js
  25. 94 0
      services/utils/objectProperties.ts
  26. 15 2
      services/utils/yamlParser.ts
  27. 25 2
      store/index.js
  28. 14 0
      store/profile/access.ts
  29. 29 1
      store/profile/organization.ts
  30. 25 21
      test/services/rights/roleUtils.spec.js
  31. 2 4
      test/services/utils/hydraParser.spec.js
  32. 2 2
      test/services/utils/yamlParser.spec.js
  33. 25 0
      test/use/template/menu.spec.js
  34. 22 5
      types/types.d.ts
  35. 55 0
      use/template/Menus/accessMenu.ts
  36. 78 0
      use/template/Menus/admin2iosMenu.ts
  37. 34 0
      use/template/Menus/agendaMenu.ts
  38. 33 0
      use/template/Menus/baseMenu.ts
  39. 58 0
      use/template/Menus/billingMenu.ts
  40. 38 0
      use/template/Menus/communicationMenu.ts
  41. 94 0
      use/template/Menus/cotisationsMenu.ts
  42. 24 0
      use/template/Menus/donorsMenu.ts
  43. 50 0
      use/template/Menus/educationalMenu.ts
  44. 24 0
      use/template/Menus/equipmentMenu.ts
  45. 24 0
      use/template/Menus/medalsMenu.ts
  46. 38 0
      use/template/Menus/statsMenu.ts
  47. 39 0
      use/template/Menus/websiteMenu.ts
  48. 56 122
      use/template/menu.ts
  49. 15 29
      yarn.lock

+ 4 - 3
components/Form/InputComponent.vue

@@ -16,7 +16,8 @@
 <script lang="ts">
   import {unref, defineComponent, ref, useFetch, watch, onUnmounted, useContext} from '@nuxtjs/composition-api'
   import {Query} from "@vuex-orm/core";
-  import {cloneAndFlatten, cloneAndNest} from "~/services/utils/objectProperties";
+  import {$objectProperties} from "~/services/utils/objectProperties";
+  import {AnyJson} from "~/types/types";
 
   export default defineComponent({
     props: {
@@ -39,11 +40,11 @@
 
       const { fetch, fetchState } = useFetch(async () => {
         const entry = await query.find(parseInt(route.value.params.id));
-        data.value = cloneAndFlatten(entry);
+        data.value = $objectProperties.cloneAndFlatten(entry as AnyJson);
       })
 
       const update = () => {
-        query.update(cloneAndNest(unref(data)))
+        query.update($objectProperties.cloneAndNest(unref(data)))
       }
 
       /* const unwatch = watch(

+ 8 - 8
components/Layout/MenuComponent.vue

@@ -10,8 +10,8 @@
       <div v-for="(item, i) in menu" :key="i">
         <v-list-item
           v-if="!item.children"
-          :href="item.old ? item.to : undefined"
-          :to="!item.old ? item.to : undefined"
+          :href="item.isExternalLink ? item.to : undefined"
+          :to="!item.isExternalLink ? item.to : undefined"
           router
           exact
         >
@@ -40,8 +40,8 @@
           <v-list-item
             v-for="child in item.children"
             :key="child.title"
-            :href="child.old ? child.to : undefined"
-            :to="!child.old ? child.to : undefined"
+            :href="child.isExternalLink ? child.to : undefined"
+            :to="!child.isExternalLink ? child.to : undefined"
             router
             exact
           >
@@ -61,7 +61,7 @@
 </template>
 
 <script lang="ts">
-  import Menu from '@/use/template/menu'
+  import {$useMenu} from '@/use/template/menu'
   import {defineComponent} from '@nuxtjs/composition-api'
   import {AnyJson} from "~/types/types";
 
@@ -77,7 +77,7 @@
       }
     },
     setup() {
-      const menu: AnyJson = new Menu().useMenuConstruct()
+      const menu: AnyJson = $useMenu.setUpContext().useLateralMenuConstruct()
 
       return {
         menu
@@ -100,10 +100,10 @@
     font-size: 16px;
   }
   .v-list-item{
-    min-height: 10px;
+    min-height: 10px !important;
   }
   .v-list-item__action {
-    margin: 8px 0;
+    margin: 10px 0;
   }
   .v-list-item__content {
     padding: 8px 0;

+ 83 - 0
config/abilities/pages/admin2ios.yaml

@@ -0,0 +1,83 @@
+  all_accesses_page:
+    action: 'display'
+    services:
+      access :
+        - {function: hasAbility, parameters: [{action: 'read', subject: 'user'}]}
+      organization  :
+        - {function: hasModule, parameters: ['Admin2IOS']}
+
+  all_organizations_page:
+    action: 'display'
+    services:
+      access :
+        - {function: hasAbility, parameters: [{action: 'read', subject: 'organization'}]}
+      organization  :
+        - {function: hasModule, parameters: ['Admin2IOS']}
+
+  tips_page:
+    action: 'display'
+    services:
+      access :
+        - {function: hasAbility, parameters: [{action: 'read', subject: 'tips'}]}
+      organization  :
+        - {function: hasModule, parameters: ['CorePremium']}
+
+  actions_lead_page:
+    action: 'display'
+    services:
+      organization  :
+        - {function: hasModule, parameters: ['Admin2IOS']}
+
+  renewall_list_page:
+    action: 'display'
+    services:
+      organization  :
+        - {function: hasModule, parameters: ['Admin2IOS']}
+
+  settlements_page:
+    action: 'display'
+    services:
+      organization  :
+        - {function: hasModule, parameters: ['Admin2IOS']}
+
+  pendings_settlements_page:
+    action: 'display'
+    services:
+      organization  :
+        - {function: hasModule, parameters: ['Admin2IOS']}
+
+  outages_notice_page:
+    action: 'display'
+    services:
+      organization  :
+        - {function: hasModule, parameters: ['Admin2IOS']}
+
+  degraded_page:
+    action: 'display'
+    services:
+      organization  :
+        - {function: hasModule, parameters: ['Admin2IOS']}
+
+  dgv_page:
+    action: 'display'
+    services:
+      organization  :
+        - {function: hasModule, parameters: ['Admin2IOS']}
+
+  cmf_cotisation_page:
+    action: 'display'
+    services:
+      organization  :
+        - {function: hasModule, parameters: ['Admin2IOS']}
+
+  right_page:
+    action: 'display'
+    services:
+      organization  :
+        - {function: hasModule, parameters: ['Admin2IOS']}
+
+  tree_page:
+    action: 'display'
+    services:
+      organization  :
+        - {function: hasModule, parameters: ['Admin2IOS']}

+ 63 - 0
config/abilities/pages/billing.yaml

@@ -0,0 +1,63 @@
+  billing_product_page:
+    action: 'display'
+    services:
+      access:
+        - {function: hasAbility, parameters: [{action: 'read', subject: 'billings-administration'}]}
+      organization:
+        - {function: hasModule, parameters: ['BillingAdministration']}
+
+  billing_products_by_student_page:
+    action: 'display'
+    services:
+      access:
+        - {function: hasAbility, parameters: [{action: 'read', subject: 'pedagogics-administration'}]}
+      organization:
+        - {function: hasModule, parameters: ['BillingAdministration']}
+
+  billing_edition_page:
+    action: 'display'
+    services:
+      access:
+        - {function: hasAbility, parameters: [{action: 'manage', subject: 'billings-administration'}]}
+      organization:
+        - {function: hasModule, parameters: ['BillingAdministration']}
+
+  billing_accounting_page:
+    action: 'display'
+    services:
+      access:
+        - {function: hasAbility, parameters: [{action: 'read', subject: 'billings-administration'}]}
+      organization:
+        - {function: hasModule, parameters: ['BillingAdministration']}
+
+  billing_payment_list_page:
+    action: 'display'
+    services:
+      access:
+        - {function: hasAbility, parameters: [{action: 'read', subject: 'billings-administration'}]}
+      organization:
+        - {function: hasModule, parameters: ['BillingAdministration']}
+
+  pes_page:
+    action: 'display'
+    services:
+      access:
+        - {function: hasAbility, parameters: [{action: 'manage', subject: 'billings-administration'}]}
+      organization:
+        - {function: hasModule, parameters: ['Pes']}
+
+  berger_levrault_page:
+    action: 'display'
+    services:
+      access:
+        - {function: hasAbility, parameters: [{action: 'manage', subject: 'billings-administration'}]}
+      organization:
+        - {function: hasModule, parameters: ['BergerLevrault']}
+
+  jvs_page:
+    action: 'display'
+    services:
+      access:
+        - {function: hasAbility, parameters: [{action: 'manage', subject: 'billings-administration'}]}
+      organization:
+        - {function: hasModule, parameters: ['Jvs']}

+ 35 - 0
config/abilities/pages/communication.yaml

@@ -0,0 +1,35 @@
+  inbox_page:
+    action: 'display'
+    services:
+      access:
+        - function: hasAbility
+          parameters:
+            - {action: 'read', subject: 'mails'}
+            - {action: 'read', subject: 'emails'}
+            - {action: 'read', subject: 'sms'}
+      organization:
+        - {function: hasModule, parameters: ['MessagesAdvanced']}
+
+  message_send_page:
+    action: 'display'
+    services:
+      access:
+        - function: hasAbility
+          parameters:
+            - {action: 'read', subject: 'mails'}
+            - {action: 'read', subject: 'emails'}
+            - {action: 'read', subject: 'sms'}
+      organization:
+        - {function: hasModule, parameters: ['MessagesAdvanced']}
+
+  message_templates_page:
+    action: 'display'
+    services:
+      access:
+        - function: hasAbility
+          parameters:
+            - {action: 'read', subject: 'mails'}
+            - {action: 'read', subject: 'emails'}
+            - {action: 'read', subject: 'sms'}
+      organization:
+        - {function: hasModule, parameters: ['MessagesAdvanced']}

+ 135 - 0
config/abilities/pages/cotisations.yaml

@@ -0,0 +1,135 @@
+  rate_cotisation_page:
+    action: 'display'
+    services:
+      access:
+        - {function: hasAbility, parameters: [{action: 'read', subject: 'cotisation'}]}
+      organization:
+        - {function: hasModule, parameters: ['CotisationRate', 'CotisationCall']}
+
+  parameters_cotisation_page:
+    action: 'display'
+    services:
+      access:
+        - {function: hasAbility, parameters: [{action: 'read', subject: 'cotisation'}]}
+      organization:
+        - {function: hasModule, parameters: ['CotisationCall']}
+
+  send_cotisation_page:
+    action: 'display'
+    services:
+      access:
+        - {function: hasAbility, parameters: [{action: 'read', subject: 'cotisation'}]}
+      organization:
+        - {function: hasModule, parameters: ['CotisationCall']}
+
+  state_cotisation_page:
+    action: 'display'
+    services:
+      access:
+        - {function: hasAbility, parameters: [{action: 'read', subject: 'cotisation'}]}
+      organization:
+        - {function: hasModule, parameters: ['CotisationCall']}
+
+  pay_cotisation_page:
+    action: 'display'
+    services:
+      access:
+        - {function: hasAbility, parameters: [{action: 'read', subject: 'cotisation'}]}
+      organization:
+        - {function: hasModule, parameters: ['CotisationCall']}
+
+  check_cotisation_page:
+    action: 'display'
+    services:
+      access:
+        - {function: hasAbility, parameters: [{action: 'read', subject: 'cotisation'}]}
+      organization:
+        - {function: hasModule, parameters: ['CotisationCall']}
+
+  ledger_cotisation_page:
+    action: 'display'
+    services:
+      access:
+        - {function: hasAbility, parameters: [{action: 'read', subject: 'cotisation'}]}
+      organization:
+        - {function: hasModule, parameters: ['CotisationCall']}
+
+  magazine_cotisation_page:
+    action: 'display'
+    services:
+      access:
+        - {function: hasAbility, parameters: [{action: 'read', subject: 'cotisation'}]}
+      organization:
+        - {function: hasModule, parameters: ['CotisationCMFAdministration']}
+
+  ventilated_cotisation_page:
+    action: 'display'
+    services:
+      access:
+        - {function: hasAbility, parameters: [{action: 'read', subject: 'cotisation'}]}
+      organization:
+        - {function: hasModule, parameters: ['CotisationCall']}
+
+  pay_erase_cotisation_page:
+    action: 'display'
+    services:
+      access:
+        - {function: hasAbility, parameters: [{action: 'read', subject: 'cotisation'}]}
+      organization:
+        - {function: hasModule, parameters: ['CotisationCall']}
+
+  resume_cotisation_page:
+    action: 'display'
+    services:
+      access:
+        - {function: hasAbility, parameters: [{action: 'read', subject: 'cotisation'}]}
+      organization:
+        - {function: hasModule, parameters: ['CotisationTransmissionState']}
+
+  history_cotisation_page:
+    action: 'display'
+    services:
+      access:
+        - {function: hasAbility, parameters: [{action: 'read', subject: 'cotisation'}]}
+      organization:
+        - {function: hasModule, parameters: ['CotisationCall']}
+
+  call_cotisation_page:
+    action: 'display'
+    services:
+      access:
+        - {function: hasAbility, parameters: [{action: 'read', subject: 'cotisation'}]}
+      organization:
+        - {function: hasModule, parameters: ['CotisationStructure']}
+
+  history_struture_cotisation_page:
+    action: 'display'
+    services:
+      access:
+        - {function: hasAbility, parameters: [{action: 'read', subject: 'cotisation'}]}
+      organization:
+        - {function: hasModule, parameters: ['CotisationStructure']}
+
+  insurance_cotisation_page:
+    action: 'display'
+    services:
+      access:
+        - {function: hasAbility, parameters: [{action: 'read', subject: 'cotisation'}]}
+      organization:
+        - {function: hasModule, parameters: ['CotisationStructure', 'CotisationTransmissionState']}
+
+  resume_all_cotisation_page:
+    action: 'display'
+    services:
+      access:
+        - {function: hasAbility, parameters: [{action: 'read', subject: 'cotisation'}]}
+      organization:
+        - {function: hasModule, parameters: ['CotisationTransmission']}
+
+  resume_pay_cotisation_page:
+    action: 'display'
+    services:
+      access:
+        - {function: hasAbility, parameters: [{action: 'read', subject: 'cotisation'}]}
+      organization:
+        - {function: hasModule, parameters: ['CotisationTransmission']}

+ 7 - 0
config/abilities/pages/donor.yaml

@@ -0,0 +1,7 @@
+  donors_page:
+    action: 'display'
+    services:
+      access:
+        - {function: hasAbility, parameters: [{action: 'read', subject: 'donors'}]}
+      organization:
+        - {function: hasModule, parameters: ['Donors']}

+ 1 - 1
config/abilities/pages/equipment.yaml

@@ -2,6 +2,6 @@
     action: 'display'
     services:
       access:
-        - {function: hasAbility, parameters: [{action: 'read', subject: 'equipment'}]}
+        - {function: hasAbility, parameters: [{action: 'read', subject: 'equipments'}]}
       organization:
         - {function: hasModule, parameters: ['Equipments']}

+ 7 - 0
config/abilities/pages/medals.yaml

@@ -0,0 +1,7 @@
+  medals_page:
+    action: 'display'
+    services:
+      access:
+        - {function: hasAbility, parameters: [{action: 'read', subject: 'medals'}]}
+      organization:
+        - {function: hasModule, parameters: ['Medals']}

+ 23 - 0
config/abilities/pages/stats.yaml

@@ -0,0 +1,23 @@
+  report_activity_page:
+    action: 'display'
+    services:
+      access:
+        - {function: hasAbility, parameters: [{action: 'read', subject: 'statistic'}]}
+      organization:
+        - {function: hasModule, parameters: ['Statistic']}
+
+  fede_stats_page:
+    action: 'display'
+    services:
+      access:
+        - {function: hasAbility, parameters: [{action: 'read', subject: 'statistic'}]}
+      organization:
+        - {function: hasModule, parameters: ['StatisticFederation']}
+
+  structure_stats_page:
+    action: 'display'
+    services:
+      access:
+        - {function: hasAbility, parameters: [{action: 'read', subject: 'statistic'}]}
+      organization:
+        - {function: hasModule, parameters: ['StatisticStructure']}

+ 1 - 0
config/nuxtConfig/env.js

@@ -12,6 +12,7 @@ export default {
     },
     baseURL_Legacy: process.env.NODE_ENV !== 'production' ? 'https://local.api.opentalent.fr' : 'https://local.api.opentalent.fr',
     baseURL_adminLegacy: process.env.NODE_ENV !== 'production' ? 'https://local.admin.opentalent.fr/#' : 'https://admin.opentalent.fr/#',
+    baseURL_typo3: process.env.NODE_ENV !== 'production' ? `https://local.sub.opentalent.fr/###subDomain###` : `https://###subDomain###.opentalent.fr/#`,
   },
   privateRuntimeConfig: {
     http: {

+ 5 - 1
config/nuxtConfig/i18n.js

@@ -8,6 +8,10 @@ export default {
     ],
     lazy: true,
     langDir: 'lang/',
-    defaultLocale: 'fr'
+    defaultLocale: 'fr',
+    vueI18n: {
+      silentTranslationWarn: true,
+      silentFallbackWarn: true
+    }
   }
 }

+ 54 - 1
lang/layout/fr-FR.js

@@ -6,7 +6,7 @@ export default (context, locale) => {
     family_view: 'Vue famille',
     education_student_next_year: 'Gestion des inscriptions',
     commissions: 'Commissions',
-    my_network: 'Mon réseau',
+    my_network: 'Répertoire du réseau',
     network: 'Réseau',
     schedule: 'Agenda',
     attendances: 'Absences',
@@ -18,5 +18,58 @@ export default (context, locale) => {
     test_validation: 'Validation par évaluation',
     examen_results: 'Résultats des examens',
     education_by_student_validation: 'Validation par enseignement',
+    billing: 'Facturation',
+    billing_product: 'Produit',
+    billing_products_by_student: 'Produits par élève',
+    billing_edition: 'Édition des factures',
+    billing_accounting: 'Factures et avoirs',
+    billing_payment_list: 'Journal des règlements',
+    pes_export: 'Export JVS',
+    berger_levrault_export: 'Export Berger Levrault',
+    jvs_export: 'Export JVS',
+    inbox: 'Boite d\'envoi',
+    message_send: 'Éléments envoyés',
+    message_templates: 'Modèles',
+    communication: 'Communication',
+    donors: 'Partenariats et dons',
+    medals: 'Médailles',
+    stats: 'Statistiques',
+    report_activity: 'Rapport d\'activité',
+    fede_stats: 'Fédérations',
+    structure_stats: 'Structures',
+    rate_cotisation: 'Saisie du tarif',
+    parameters_cotisation: 'Paramètrer l\'appel de cotisation',
+    send_cotisation: 'Appel des cotisations',
+    state_cotisation: 'Suivi des cotisations',
+    pay_cotisation: 'Saisie des réglements',
+    check_cotisation: 'Remise de chèques',
+    ledger_cotisation: 'Journal des réglements',
+    magazine_cotisation: 'Bulletin',
+    ventilated_cotisation: 'Cotisations ventilées par sous total',
+    pay_erase_cotisation: 'Suppression de réglements',
+    resume_cotisation: 'Etat des transmissions',
+    history_cotisation: 'Historique des cotisations',
+    call_cotisation: 'Règlements à la fédération',
+    history_struture_cotisation: 'Cotisations fédérales',
+    insurance_cotisation: 'Assurance CMF',
+    resume_all_cotisation: 'Toutes les cotisations',
+    resume_pay_cotisation: 'Cotisation avec un règlement reçu ou en attente',
+    cotisations: 'Cotisations',
+    all_accesses: 'Toutes les personnes',
+    admin2ios: 'Administration 2ios',
+    all_organizations: 'Toutes les structures',
+    tips: 'Tips',
+    actions_lead: 'Actions à conduire',
+    renewall_list: 'Structures à relancer',
+    settlements: 'Règlements effectués',
+    pendings_settlements: 'Règlements en attentes',
+    outages_notice: 'Coupure de service',
+    degraded: 'Clients',
+    dgv: 'Assurance CMF',
+    cmf_cotisation: 'Cotisation CMF',
+    right_menu: 'Droits version 5.9',
+    tree_menu: 'Gestion de l\'arbre',
+    website: 'Site internet',
+    advanced_modification: 'Modifications avancées',
   })
 }

+ 1 - 1
middleware/auth.ts

@@ -1,7 +1,7 @@
 import { Middleware } from '@nuxt/types'
 
 const auth: Middleware = async ({ store, redirect }) => {
-  // If the user is not authenticated
+  // Si l'utilisateur n'est pas connecté on le redirige vers la page login
   if (!store.state.profile.access) {
     return redirect('/login')
   }

+ 2 - 2
package.json

@@ -17,10 +17,10 @@
     "@casl/vue": "^1.2.1",
     "@nuxt/http": "^0.6.1",
     "@nuxt/typescript-runtime": "^2.0.0",
-    "@nuxtjs/composition-api": "^0.17.0",
+    "@nuxtjs/composition-api": "0.19",
     "@syncfusion/ej2-vue-grids": "^18.4.30",
     "@types/lodash": "^4.14.168",
-    "@vuex-orm/core": "1.0.0-draft.7",
+    "@vuex-orm/core": "1.0.0-draft.8",
     "cookieparser": "^0.1.0",
     "core-js": "^3.6.5",
     "js-yaml": "^4.0.0",

+ 0 - 1
pages/index.vue

@@ -12,7 +12,6 @@
 
   export default defineComponent({
     setup() {
-
       return {
 
       }

+ 2 - 10
plugins/Queries/rest.ts

@@ -1,12 +1,5 @@
 import {Plugin} from '@nuxt/types'
-import HydraParser from '../../services/utils/hydraParser'
-import {AnyJson} from "~/types/types";
-
-declare module '@nuxt/types' {
-  interface Context {
-    $rest: AnyJson
-  }
-}
+import  {$hydraParser} from '../../services/utils/hydraParser'
 
 const restPlugin: Plugin = (ctx) => {
 
@@ -23,8 +16,7 @@ const restPlugin: Plugin = (ctx) => {
   const queries = async (responseQuery: any) => {
     try {
       let response = await responseQuery.json();
-      const hydra = new HydraParser();
-      return hydra.parse(response);
+      return $hydraParser.parse(response);
     } catch (err) {
       console.log(err)
     }

+ 16 - 8
plugins/Rights/ability.ts

@@ -2,31 +2,39 @@ import { Plugin } from '@nuxt/types'
 import { Ability } from '@casl/ability'
 import { $abilitiesUtils } from "~/services/rights/abilitiesUtils";
 
-declare module '@nuxt/types' {
-  interface Context {
-    $ability(): Ability
-  }
-}
-
-export const ability = new Ability();
-
+/**
+ * Au moment de la phase D'init de Nuxt...
+ * @param ctx
+ */
 const abilityPlugin: Plugin = (ctx) => {
+  //Nécessaire pour que l'update des abilité soit correct après la phase SSR
   ability.update(ctx.store.state.profile.access.abilities);
 
+  /**
+   * Au moment où l'on effectue un SetProfile via le store Organization, il faut aller récupérer
+   * les différentes abilitées que l'utilisateur peut effectuer. (Tout cela se passe en SSR)
+   */
   const unsubscribe = ctx.store.subscribeAction({
     after: (action, state) => {
       switch (action.type) {
         case 'profile/organization/setProfile':
+          //On récupère les abilités
           const abilitiesUtils = $abilitiesUtils(ctx.store, ability)
           const abilities = abilitiesUtils.getAbilities();
 
+          //On les store puis on update le service ability pour le mettre à jour.
           ctx.store.commit('profile/access/setAbilities', abilities)
           ability.update(abilities);
+          //Unsubscribe pour éviter les memory leaks
           unsubscribe()
           break;
       }
     }
   })
+
+  //Déclare un nouvel accesseur de service via le context Nuxt
   ctx.$ability = () => {return ability}
 }
+
 export default abilityPlugin
+export const ability = new Ability();

+ 4 - 4
services/profile/accessProfile.ts

@@ -24,7 +24,7 @@ class AccessProfile{
   /**
    * Est ce que l'utilisateur possède le role.
    * @param {Array<string>} roles roles à tester
-   * @returns {boolean}
+   * @return {boolean}
    */
   hasRole(roles:Array<string>): boolean{
     if (null === roles)
@@ -41,7 +41,7 @@ class AccessProfile{
   /**
    * Est-ce que l'utilisateur possède l'abilité
    * @param {Array<AbilitiesType>} ability abilité à tester
-   * @returns {boolean}
+   * @return {boolean}
    */
   hasAbility(ability:Array<AbilitiesType>): boolean{
     if(ability === null)
@@ -57,7 +57,7 @@ class AccessProfile{
 
   /**
    * Factory
-   * @returns {AnyJson} retourne les fonction rendues publiques
+   * @return {AnyJson} retourne les fonction rendues publiques
    */
   handler():AnyJson{
     return {
@@ -67,5 +67,5 @@ class AccessProfile{
   }
 }
 
-export const accessProfile = (store:AccessStore, ability:Ability) => new AccessProfile(store, ability);
+export const $accessProfile = (store:AccessStore, ability:Ability) => new AccessProfile(store, ability);
 

+ 12 - 12
services/profile/organizationProfile.ts

@@ -19,12 +19,12 @@ class OrganizationProfile{
   /**
    * Est-ce que l'organisation possède le module
    * @param {Array<string>} modules Modules à tester
-   * @returns {boolean}
+   * @return {boolean}
    */
   hasModule(modules:Array<string>) {
     let hasModule = false;
     modules.map((module) => {
-      if (this.organizationProfile.modules.indexOf(module) > -1)
+      if (this.organizationProfile.modules && this.organizationProfile.modules.indexOf(module) > -1)
         hasModule = true;
     });
     return hasModule;
@@ -32,7 +32,7 @@ class OrganizationProfile{
 
   /**
    * L'organization possède t'elle un produit school ou school premium
-   * @returns {boolean}
+   * @return {boolean}
    */
   isSchool():boolean {
     return this.isSchoolProduct() || this.isSchoolPremiumProduct();
@@ -40,7 +40,7 @@ class OrganizationProfile{
 
   /**
    * L'organization possède t'elle un produit artiste ou artiste premium
-   * @returns {boolean}
+   * @return {boolean}
    */
   isArtist():boolean {
     return this.isArtistProduct() || this.isArtistPremiumProduct();
@@ -48,7 +48,7 @@ class OrganizationProfile{
 
   /**
    * L'organization possède t'elle un produit school
-   * @returns {boolean}
+   * @return {boolean}
    */
   isSchoolProduct() {
     return this.organizationProfile.product === process.env.school_product
@@ -56,7 +56,7 @@ class OrganizationProfile{
 
   /**
    * L'organization possède t'elle un produit school premium
-   * @returns {boolean}
+   * @return {boolean}
    */
   isSchoolPremiumProduct() {
     return this.organizationProfile.product === process.env.school_premium_product
@@ -64,7 +64,7 @@ class OrganizationProfile{
 
   /**
    * L'organization possède t'elle un produit premium
-   * @returns {boolean}
+   * @return {boolean}
    */
   isArtistProduct() {
     return this.organizationProfile.product === process.env.artist_product
@@ -72,7 +72,7 @@ class OrganizationProfile{
 
   /**
    * L'organization possède t'elle un produit artiste premium
-   * @returns {boolean}
+   * @return {boolean}
    */
   isArtistPremiumProduct() {
     return this.organizationProfile.product === process.env.artist_premium_product
@@ -80,7 +80,7 @@ class OrganizationProfile{
 
   /**
    * L'organization possède t'elle un produit manager
-   * @returns {boolean}
+   * @return {boolean}
    */
   isManagerProduct() {
     return this.organizationProfile.product === process.env.manager_product
@@ -88,7 +88,7 @@ class OrganizationProfile{
 
   /**
    * L'organization possède t'elledes enfants
-   * @returns {boolean}
+   * @return {boolean}
    */
   isOrganizationWithChildren(){
     return this.organizationProfile.hasChildren;
@@ -96,7 +96,7 @@ class OrganizationProfile{
 
   /**
    * Factory
-   * @returns {AnyJson} retourne les fonction rendues publiques
+   * @return {AnyJson} retourne les fonction rendues publiques
    */
   handler():AnyJson{
     return {
@@ -109,5 +109,5 @@ class OrganizationProfile{
   }
 }
 
-export const organizationProfile = (store:OrganizationStore) => new OrganizationProfile(store);
+export const $organizationProfile = (store:OrganizationStore) => new OrganizationProfile(store);
 

+ 12 - 12
services/rights/abilitiesUtils.ts

@@ -1,9 +1,9 @@
-import {accessProfile} from "@/services/profile/accessProfile"
-import {organizationProfile} from "@/services/profile/organizationProfile"
+import {$accessProfile} from "@/services/profile/accessProfile"
+import {$organizationProfile} from "@/services/profile/organizationProfile"
 import {$roleUtils} from "~/services/rights/roleUtils";
 import {AbilitiesType, AccessStore, AnyJson, AnyStore} from "~/types/types";
 import {Ability} from "@casl/ability";
-import YamlParser from "~/services/utils/yamlParser";
+import {$yamlParser} from "~/services/utils/yamlParser";
 import * as _ from "lodash";
 
 /**
@@ -29,14 +29,14 @@ class AbilitiesUtils {
    */
   initFactory() {
     this.factory = {
-      access: accessProfile(this.$store, this.$ability),
-      organization: organizationProfile(this.$store)
+      access: $accessProfile(this.$store, this.$ability),
+      organization: $organizationProfile(this.$store)
     }
   }
 
   /**
    * Récupération de l'ensemble des abilities quelles soient par Roles ou par Config.
-   * @returns {Array<AbilitiesType>}
+   * @return {Array<AbilitiesType>}
    */
   getAbilities():Array<AbilitiesType> {
     const abilitiesByRoles = this.getAbilitiesByRoles(this.$store.state.profile.access.roles)
@@ -48,7 +48,7 @@ class AbilitiesUtils {
   /**
    * Adaptation et transformations des roles en abilities
    * @param {Array<string>} roles
-   * @returns {Array<AbilitiesType>}
+   * @return {Array<AbilitiesType>}
    */
   getAbilitiesByRoles(roles: Array<string>): Array<AbilitiesType> {
     roles = $roleUtils.transformUnderscoreToHyphenBeforeCompleteMigration(roles);
@@ -60,12 +60,12 @@ class AbilitiesUtils {
    * - filtres la config pour ne garder que les abilities autorisées
    * - transform la config restante en Object Abilities
    * @param {string} configPath
-   * @returns {Array<AbilitiesType>}
+   * @return {Array<AbilitiesType>}
    */
   getAbilitiesByConfig(configPath:string): Array<AbilitiesType> {
     let abilitiesByConfig: Array<AbilitiesType> = []
    try {
-      const doc = YamlParser.parse(configPath);
+      const doc = $yamlParser.parse(configPath);
       const abilitiesAvailable = doc['abilities']
       const abilitiesFiltered = this.abilitiesAvailableFilter(abilitiesAvailable)
       abilitiesByConfig = this.transformAbilitiesConfigToAbility(abilitiesFiltered)
@@ -78,7 +78,7 @@ class AbilitiesUtils {
   /**
    * Filtre toutes les abilities possible suivant si l'utilisateur est autorisé ou non à les posséder
    * @param {AnyJson} abilitiesAvailable
-   * @returns {AnyJson}
+   * @return {AnyJson}
    */
   abilitiesAvailableFilter(abilitiesAvailable:AnyJson):AnyJson{
     return _.pickBy(abilitiesAvailable, (ability:any) =>{
@@ -90,7 +90,7 @@ class AbilitiesUtils {
   /**
    * Transform une config d'abilities en un tableau d'Abilities
    * @param {AnyJson} abilitiesAvailable
-   * @returns {Array<AbilitiesType>}
+   * @return {Array<AbilitiesType>}
    */
   transformAbilitiesConfigToAbility(abilitiesAvailable:AnyJson):Array<AbilitiesType>{
     let abilitiesByConfig: Array<AbilitiesType> = []
@@ -107,7 +107,7 @@ class AbilitiesUtils {
   /**
    * Parcours les fonctions par services et établit si oui ou non l'abilité est autorisée
    * @param {AnyJson} functionByservices
-   * @returns {boolean}
+   * @return {boolean}
    */
   canHaveTheAbility(functionByservices: AnyJson) {
     let hasAbility = true;

+ 4 - 4
services/rights/roleUtils.ts

@@ -62,7 +62,7 @@ class RoleUtils {
    * Test si une personne possède le role lui permettant d'acquérir la fonction
    * @param {string} function_name
    * @param {Array<string>} roles
-   * @returns {boolean}
+   * @return {boolean}
    */
   isA(function_name:string, roles:Array<string>): boolean {
     return roles.indexOf('ROLE_' + function_name + '_CORE') >= 0
@@ -71,7 +71,7 @@ class RoleUtils {
   /**
    * Filtre les roles afin d'en exclure les "Roles fonctions"
    * @param {Array<string>} roles
-   * @returns {Array<string>}
+   * @return {Array<string>}
    */
   filterFunctionRoles(roles:Array<string>):Array<string>{
     return roles.filter(role => {
@@ -82,7 +82,7 @@ class RoleUtils {
   /**
    * Avant la migration complète, quelque role disposent d'underscore en trop, on corrige cela...
    * @param {Array<string>} roles
-   * @returns {Array<string>}
+   * @return {Array<string>}
    */
   transformUnderscoreToHyphenBeforeCompleteMigration(roles: Array<string>): Array<string> {
     const regex = /(ROLE\_)([A-Z]*\_[A-Z]*)([A-Z\_]*)*/i;
@@ -104,7 +104,7 @@ class RoleUtils {
   /**
    * On transforme les ROLES Symfony en Abilities
    * @param {Array<string>} roles
-   * @returns {Array<AbilitiesType>}
+   * @return {Array<AbilitiesType>}
    */
   transformRoleToAbilities(roles: Array<string>): [] | Array<AbilitiesType> {
     let abilities:any = [];

+ 71 - 45
services/utils/hydraParser.ts

@@ -1,64 +1,62 @@
 import {AnyJson} from "~/types/types";
 
-export default class HydraParser {
-  constructor() {
-  }
-
-  parse(response: AnyJson): AnyJson {
-    if (response['hydra:member']) {
-      response.totalCount = response['hydra:totalItems'];
-      return this.parseCollection(response);
+/**
+ * @category Services/utils
+ * @class HydraParser
+ * Classe permettant d'assurer le parsing d'une réponse Hydra
+ */
+class HydraParser {
+  /**
+   * Parcours une réponse Hydra pour retourner son équivalent en Json
+   * @param {AnyJson} hydraData
+   * @return {AnyJson} réponse parsée
+   */
+  parse(hydraData: AnyJson): AnyJson {
+    if (hydraData['hydra:member']) {
+      hydraData.totalCount = hydraData['hydra:totalItems'];
+      return this.parseCollection(hydraData);
     } else {
-      return this.parseItem(response);
-    }
-  }
-
-  populateId(data: AnyJson) {
-    if (data['@id'] && data['@id'] instanceof String) {
-      var iriParts = data['@id'].split('/');
-      data.id = iriParts[iriParts.length - 1];
-    }
-  }
-
-  populateAllData(data: AnyJson):void {
-    this.populateId(data);
-    for (const key in data) {
-      const value = data[key];
-      if (value instanceof Object) {
-        this.populateAllData(value);
-      }
+      return this.parseItem(hydraData);
     }
   }
 
-  parseItem(data: AnyJson): AnyJson {
-    this.populateId(data);
+  /**
+   * Méthode de parsing appelé si on est dans un GET
+   * @param {AnyJson} hydraData
+   */
+  parseItem(hydraData: AnyJson): AnyJson {
+    this.populateId(hydraData);
 
-    if (data['hydra:previous']) {
-      let iriParts = data['hydra:previous'].split('/');
-      data.previous = iriParts[iriParts.length - 1];
+    if (hydraData['hydra:previous']) {
+      let iriParts = hydraData['hydra:previous'].split('/');
+      hydraData.previous = iriParts[iriParts.length - 1];
     }
-    if (data['hydra:next']) {
-      let iriParts = data['hydra:next'].split('/');
-      data.next = iriParts[iriParts.length - 1];
+    if (hydraData['hydra:next']) {
+      let iriParts = hydraData['hydra:next'].split('/');
+      hydraData.next = iriParts[iriParts.length - 1];
     }
-    if (data['hydra:totalItems']) {
-      data.totalItems = data['hydra:totalItems'];
+    if (hydraData['hydra:totalItems']) {
+      hydraData.totalItems = hydraData['hydra:totalItems'];
     }
-    if (data['hydra:itemPosition']) {
-      data.itemPosition = data['hydra:itemPosition'];
+    if (hydraData['hydra:itemPosition']) {
+      hydraData.itemPosition = hydraData['hydra:itemPosition'];
     }
-    return data;
+    return hydraData;
   }
 
-  parseCollection(data: AnyJson): AnyJson {
-    let collectionResponse = data['hydra:member'];
+  /**
+   * Méthode de parsing appelé si on est dans un GET Collection
+   * @param {AnyJson} hydraData
+   */
+  parseCollection(hydraData: AnyJson): AnyJson {
+    let collectionResponse = hydraData['hydra:member'];
     collectionResponse.metadata = {};
     collectionResponse.order = {};
     collectionResponse.search = {};
 
     // Put metadata in a property of the collection
-    for (const key in data) {
-      const value = data[key];
+    for (const key in hydraData) {
+      const value = hydraData[key];
       if ('hydra:member' !== key) {
         collectionResponse.metadata[key] = value;
       }
@@ -70,8 +68,8 @@ export default class HydraParser {
       this.populateAllData(value);
     }
 
-    if ('undefined' !== typeof (data['hydra:search'])) {
-      let collectionSearch = data['hydra:search']['hydra:mapping'];
+    if ('undefined' !== typeof (hydraData['hydra:search'])) {
+      let collectionSearch = hydraData['hydra:search']['hydra:mapping'];
       for (const key in collectionSearch) {
         const value = collectionSearch[key];
         if (value['variable'].indexOf("filter[order]") === 0) {
@@ -83,4 +81,32 @@ export default class HydraParser {
     }
     return collectionResponse;
   }
+
+
+  /**
+   * Hydrate l'objet Json avec la valeur id correspondant à l'uri de l'objet
+   * @param {AnyJson} data
+   */
+  populateId(data: AnyJson): void{
+    if (data['@id'] && data['@id'] instanceof String) {
+      var iriParts = data['@id'].split('/');
+      data.id = iriParts[iriParts.length - 1];
+    }
+  }
+
+  /**
+   * Hydrate l'objet JSON de façon récursive (afin de gérer les objet nested)
+   * @param {AnyJson} data
+   */
+  populateAllData(data: AnyJson):void {
+    this.populateId(data);
+    for (const key in data) {
+      const value = data[key];
+      if (value instanceof Object) {
+        this.populateAllData(value);
+      }
+    }
+  }
 }
+
+export const $hydraParser = new HydraParser()

+ 0 - 81
services/utils/objectProperties.js

@@ -1,81 +0,0 @@
-function isObject(value) {
-    if (value === null) return false;
-    if (typeof value !== 'object') return false;
-    if (Array.isArray(value)) return false;
-    if (Object.prototype.toString.call(value) === '[object Date]') return false;
-    return true;
-}
-
-export function clone(object) {
-    return Object.keys(object).reduce((values, name) => {
-        if (object.hasOwnProperty(name)) {
-            values[name] = object[name];
-        }
-        return values;
-    }, {});
-}
-
-/*
- * Flatten nested object into a single level object with 'foo.bar' property names
- *
- * The parameter object is left unchanged. All values in the returned object are scalar.
- *
- *     cloneAndFlatten({ a: 1, b: { c: 2 }, d: { e: 3, f: { g: 4, h: 5 } }, i: { j: 6 } }, ['i'])
- *     // { a: 1, 'b.c': 2, 'd.e': 3, 'd.f.g': 4, 'd.f.h': 5, i: { j: 6 } } }
- *
- * @param {Object} object
- * @param {String[]} excludedProperties
- * @return {Object}
- */
-export function cloneAndFlatten(object, excludedProperties = []) {
-    if (typeof object !== 'object') {
-        throw new Error('Expecting an object parameter');
-    }
-    return Object.keys(object).reduce((values, name) => {
-        if (!object.hasOwnProperty(name)) return values;
-        if (isObject(object[name])) {
-            if (excludedProperties.indexOf(name) === -1) {
-                let flatObject = cloneAndFlatten(object[name]);
-                Object.keys(flatObject).forEach(flatObjectKey => {
-                    if (!flatObject.hasOwnProperty(flatObjectKey)) return;
-                    values[name + '.' + flatObjectKey] = flatObject[flatObjectKey];
-                })
-            } else {
-                values[name] = clone(object[name]);
-            }
-        } else {
-            values[name] = object[name];
-        }
-        return values;
-    }, {});
-};
-
-/*
- * Clone flattened object into a nested object
- *
- * The parameter object is left unchanged.
- *
- *     cloneAndNest({ a: 1, 'b.c': 2, 'd.e': 3, 'd.f.g': 4, 'd.f.h': 5 } )
- *     // { a: 1, b: { c: 2 }, d: { e: 3, f: { g: 4, h: 5 } } }
- *
- * @param {Object} object
- * @return {Object}
- */
-export function cloneAndNest(object) {
-    if (typeof object !== 'object') {
-        throw new Error('Expecting an object parameter');
-    }
-    return Object.keys(object).reduce((values, name) => {
-        if (!object.hasOwnProperty(name)) return values;
-        name.split('.').reduce((previous, current, index, list) => {
-            if (previous != null) {
-                if (typeof previous[current] === 'undefined') previous[current] = {};
-                if (index < (list.length - 1)) {
-                    return previous[current];
-                };
-                previous[current] = object[name];
-            }
-        }, values)
-        return values;
-    }, {})
-}

+ 94 - 0
services/utils/objectProperties.ts

@@ -0,0 +1,94 @@
+/**
+ * @category Services/utils
+ * @class ObjectProperties
+ * Classe aidant à manipuler des Objets
+ */
+import {AnyJson} from "~/types/types";
+
+class ObjectProperties {
+  /**
+   * Flatten un objet nested en un objet avec un seul niveau avec des noms de propriétés transformées comme cela 'foo.bar'
+   * L'objet passé en paramètre reste inchangé car il est cloné
+   * @example  cloneAndFlatten({ a: 1, b: { c: 2 }, d: { e: 3, f: { g: 4, h: 5 } }, i: { j: 6 } }, ['i']) => { a: 1, 'b.c': 2, 'd.e': 3, 'd.f.g': 4, 'd.f.h': 5, i: { j: 6 } } }
+   * @param {AnyJson} object
+   * @param {Array<string>} excludedProperties
+   * @param excludedProperties
+   * @return {AnyJson}
+   */
+  cloneAndFlatten(object:AnyJson, excludedProperties:Array<string> = []):AnyJson {
+    if (typeof object !== 'object') {
+      throw new Error('Expecting an object parameter');
+    }
+    return Object.keys(object).reduce((values: AnyJson, name: string) => {
+      if (!object.hasOwnProperty(name)) return values;
+      if (this.isObject(object[name])) {
+        if (excludedProperties.indexOf(name) === -1) {
+          let flatObject = this.cloneAndFlatten(object[name]);
+          Object.keys(flatObject).forEach(flatObjectKey => {
+            if (!flatObject.hasOwnProperty(flatObjectKey)) return;
+            values[name + '.' + flatObjectKey] = flatObject[flatObjectKey];
+          })
+        } else {
+          values[name] = this.clone(object[name]);
+        }
+      } else {
+        values[name] = object[name];
+      }
+      return values;
+    }, {});
+  };
+
+  /**
+   * Trasnforme un objet flattened en un objet nested. L'objet passé en paramètre reste inchangé
+   * @example cloneAndNest({ a: 1, 'b.c': 2, 'd.e': 3, 'd.f.g': 4, 'd.f.h': 5 } ) => { a: 1, b: { c: 2 }, d: { e: 3, f: { g: 4, h: 5 } } }
+   * @param {AnyJson} object
+   * @return {AnyJson}
+   */
+   cloneAndNest(object:AnyJson):AnyJson {
+    if (typeof object !== 'object') {
+      throw new Error('Expecting an object parameter');
+    }
+    return Object.keys(object).reduce((values, name) => {
+      if (!object.hasOwnProperty(name)) return values;
+      name.split('.').reduce((previous: AnyJson, current: string, index: number, list: Array<string>) => {
+        if (previous != null) {
+          if (typeof previous[current] === 'undefined') previous[current] = {};
+          if (index < (list.length - 1)) {
+            return previous[current];
+          };
+          previous[current] = object[name];
+        }
+      }, values)
+      return values;
+    }, {})
+  }
+
+  /**
+   * Test si le paramètre est un objet
+   * @param {AnyJson} value
+   * @return {boolean}
+   */
+  isObject(value:any):boolean {
+    if (value === null) return false;
+    if (typeof value !== 'object') return false;
+    if (Array.isArray(value)) return false;
+    if (Object.prototype.toString.call(value) === '[object Date]') return false;
+    return true;
+  }
+
+  /**
+   * Clone l'objet et ces propriétés.
+   * @param {Object} object
+   * @return {Object}
+   */
+  clone(object: AnyJson): AnyJson {
+    return Object.keys(object).reduce((values: AnyJson, name: string) => {
+      if (object.hasOwnProperty(name)) {
+        values[name] = object[name];
+      }
+      return values;
+    }, {});
+  }
+}
+
+export const $objectProperties = new ObjectProperties()

+ 15 - 2
services/utils/yamlParser.ts

@@ -2,8 +2,19 @@ import {AnyJson} from "~/types/types";
 import { read } from 'yaml-import';
 const yaml = require('js-yaml');
 
-export default class YamlParser {
-  static parse(inPath: string): AnyJson {
+/**
+ * @category Services/utils
+ * @class YamlParser
+ * Classe permettant d'assurer le parsing d'un fichier Yaml
+ */
+class YamlParser {
+
+  /**
+   * Parse un fichier Yaml pour en retourner son équivalent en Json
+   * @param {string} inPath
+   * @return {AnyJson}
+   */
+  parse(inPath: string): AnyJson {
     try {
       return yaml.load(yaml.dump(read(inPath)));
     }catch (e) {
@@ -12,3 +23,5 @@ export default class YamlParser {
     }
   }
 }
+
+export const $yamlParser = new YamlParser()

+ 25 - 2
store/index.js

@@ -7,7 +7,25 @@ export const plugins = [
 ];
 
 export const actions = {
-  async nuxtServerInit({commit, dispatch}, {req}) {
+  /**
+   * Méthode appelée lors de la phase d'initialisation de Nuxt coté serveur (SSR)
+   * @param commit
+   * @param dispatch
+   * @param req
+   * @return {Promise<void>}
+   */
+  async nuxtServerInit({dispatch}, {req}) {
+    const initCookie = await dispatch('initCookies', {req: req})
+    const updateProfile = await dispatch('updateProfile')
+  },
+
+  /**
+   * Récupère et Store les Cookies Bearer et XAccessId
+   * @param commit
+   * @param req
+   * @return {Promise<void>}
+   */
+  async initCookies({commit}, {req}){
     let bearer = null
     let accessId = null
     if (req.headers.cookie) {
@@ -26,9 +44,14 @@ export const actions = {
     }
     commit('profile/access/setBearer', bearer)
     commit('profile/access/setAccessId', accessId)
-    await dispatch('updateProfile')
   },
 
+  /**
+   * Récupère les informations du profile connecté et update le Store Profile
+   * @param dispatch
+   * @param state
+   * @return {Promise<void>}
+   */
   async updateProfile({dispatch, state}) {
     const my_profile = await this.$http.$get(`/api/my_profile/${state.profile.access.accessId}`)
     dispatch('profile/access/setProfile', my_profile)

+ 14 - 0
store/profile/access.ts

@@ -3,6 +3,8 @@ import {AbilitiesType, accessState} from "~/types/types";
 
 export const state = () => ({
   bearer: null,
+  name: null,
+  givenName: null,
   accessId: null,
   roles: [],
   abilities: [],
@@ -24,12 +26,21 @@ export const mutations = {
   setAccessId(state:accessState, accessId:number){
     state.accessId = accessId
   },
+  setName(state:accessState, name:string){
+    state.name = name
+  },
+  setGivenName(state:accessState, givenName:string){
+    state.givenName = givenName
+  },
   setRoles(state:accessState, roles:Array<string>){
     state.roles = roles
   },
   setAbilities(state:accessState, abilities:Array<AbilitiesType>){
     state.abilities = abilities
   },
+  setIsAdminAccess(state:accessState, isAdminAccess:boolean){
+    state.isAdminAccess = isAdminAccess
+  },
   setIsAdmin(state:accessState, isRole:boolean){
     state.isAdmin = isRole
   },
@@ -63,6 +74,9 @@ export const actions = {
   setProfile(context:any, profile:any){
     let roles_to_array:Array<string> = Object.values(profile.roles)
 
+    context.commit('setName', profile.name)
+    context.commit('setGivenName', profile.givenName)
+    context.commit('setIsAdminAccess', profile.isAdminAccess)
     context.commit('setIsAdmin', $roleUtils.isA('ADMIN', roles_to_array))
     context.commit('setIsAdministratifManager', $roleUtils.isA('ADMINISTRATIF_MANAGER', roles_to_array))
     context.commit('setIsPedagogicManager', $roleUtils.isA('PEDAGOGICS_MANAGER', roles_to_array))

+ 29 - 1
store/profile/organization.ts

@@ -1,10 +1,14 @@
 import {organizationState} from "~/types/types";
+import * as _ from "lodash";
 
 export const state = () => ({
   name: '',
   product: '',
   modules: [],
-  hasChildren: false
+  hasChildren: false,
+  website: '',
+  subDomain: '',
+  parents: []
 })
 
 export const mutations = {
@@ -19,6 +23,18 @@ export const mutations = {
   },
   setHasChildren(state:organizationState, hasChildren:boolean) {
     state.hasChildren = hasChildren
+  },
+  setParents(state:organizationState, parents:Array<organizationState>) {
+    state.parents = parents
+  },
+  setWebsite(state:organizationState, website:string) {
+    state.website = website
+  },
+  setSubDomain(state:organizationState, subDomain:string) {
+    state.subDomain = subDomain
+  },
+  addParent(state:organizationState, parent:organizationState) {
+    state.parents.push(parent)
   }
 }
 
@@ -26,7 +42,19 @@ export const actions = {
   setProfile(context:any, profile:any){
     context.commit('setName', profile.name)
     context.commit('setProduct', profile.product)
+    context.commit('setWebsite', profile.website)
+    context.commit('setSubDomain', profile.subDomain)
     context.commit('setModules', profile.modules)
     context.commit('setHasChildren', profile.hasChildren)
+
+    _.each(profile.parents, parent => {
+      const p:organizationState = {
+        name: parent.name,
+        website: parent.website,
+        subDomain: parent.subDomain,
+        parents: []
+      }
+      context.commit('addParent', p)
+    });
   }
 }

+ 25 - 21
test/services/rights/roleUtils.spec.js

@@ -1,21 +1,25 @@
 import {$roleUtils} from "~/services/rights/roleUtils";
 
-const roles = [
-  'ROLE_BOOK_CONFIG_VIEW',
-  'ROLE_ROOM_CONFIG',
-  'ROLE_USER',
-  'ROLE_ADMIN',
-  'ROLE_ADMIN_CORE',
-  'ROLE_PLACE_VIEW',
-  'ROLE_ADMINISTRATIF_MANAGER_CORE',
-];
-
-const final_role = [
-  'ROLE_GENERAL-CONFIG_VIEW',
-  'ROLE_TAGG-ADVANCED',
-  'ROLE_ROOM',
-  'ROLE_USER_EDIT'
-];
+let roles, final_role
+
+beforeEach(() => {
+  roles = [
+    'ROLE_BOOK_CONFIG_VIEW',
+    'ROLE_ROOM_CONFIG',
+    'ROLE_USER',
+    'ROLE_ADMIN',
+    'ROLE_ADMIN_CORE',
+    'ROLE_PLACE_VIEW',
+    'ROLE_ADMINISTRATIF_MANAGER_CORE',
+  ];
+
+  final_role = [
+    'ROLE_GENERAL-CONFIG_VIEW',
+    'ROLE_TAGG-ADVANCED',
+    'ROLE_ROOM',
+    'ROLE_USER_VIEW'
+  ];
+});
 
 test('test isA', () => {
   expect($roleUtils.isA('ADMINISTRATIF_MANAGER', roles)).toBeTruthy();
@@ -38,7 +42,7 @@ test('test transformUnderscoreToHyphenBeforeCompleteMigration', () => {
     'ROLE_GENERAL_CONFIG_VIEW',
     'ROLE_TAGG_ADVANCED',
     'ROLE_ROOM',
-    'ROLE_USER_EDIT'
+    'ROLE_USER_VIEW'
   ];
 
   expect($roleUtils.transformUnderscoreToHyphenBeforeCompleteMigration(roles_to_array)).toStrictEqual(final_role);
@@ -46,10 +50,10 @@ test('test transformUnderscoreToHyphenBeforeCompleteMigration', () => {
 
 test('test transformRoleToAbilities', () => {
   let abilities_to_have = [
-    {action: 'read', subject: 'book-config'},
-    {action: 'read', subject: 'place'},
-    {action: 'manage', subject: 'room-config'},
-    {action: 'manage', subject: 'user'}
+    {action: 'read', subject: 'general-config'},
+    {action: 'manage', subject: 'tagg-advanced'},
+    {action: 'manage', subject: 'room'},
+    {action: 'read', subject: 'user'}
   ]
 
   expect($roleUtils.transformRoleToAbilities(final_role)).toStrictEqual(abilities_to_have);

+ 2 - 4
test/services/utils/hydraParser.spec.js

@@ -1,8 +1,6 @@
-import HydraParser from "~/services/utils/hydraParser";
+import {$hydraParser} from "~/services/utils/hydraParser";
 
 test('test parseItem', () => {
-  const hydra = new HydraParser()
-
   let serverResponse = {
     "@context": "\/api\/contexts\/Access",
     "@id": "\/api\/accesses\/7351",
@@ -17,7 +15,7 @@ test('test parseItem', () => {
     }
   }
 
-  expect(hydra.parse(serverResponse)).toStrictEqual({
+  expect($hydraParser.parse(serverResponse)).toStrictEqual({
     "@context": "/api/contexts/Access",
     "@id": "/api/accesses/7351",
     "@type": "Access",

+ 2 - 2
test/services/utils/yamlParser.spec.js

@@ -1,8 +1,8 @@
-import YamlParser from "~/services/utils/yamlParser";
+import {$yamlParser} from "~/services/utils/yamlParser";
 
 test('test parse', () => {
   const path = './test/services/utils/files/test.yaml'
-  expect(YamlParser.parse(path)).toStrictEqual({
+  expect($yamlParser.parse(path)).toStrictEqual({
     "abilities": {
       "accesses": {
         "action": "display",

+ 25 - 0
test/use/template/menu.spec.js

@@ -0,0 +1,25 @@
+import BaseMenu from "~/use/template/Menus/baseMenu";
+
+test('test constructMenu', () => {
+  const menuWithoutChildren = new BaseMenu({'baseURL_adminLegacy': 'base_url'}).constructMenu('icon', 'children', '/url', false)
+  expect(menuWithoutChildren).toStrictEqual({
+    "icon": "icon",
+    "title": "children",
+    "to": "/url",
+    "isExternalLink": false
+  });
+
+  const menuWithChildren = new BaseMenu({'baseURL_adminLegacy': 'base_url'}).constructMenu('icon', 'parent', undefined, undefined, [menuWithoutChildren])
+  expect(menuWithChildren).toStrictEqual({
+    "children": [
+      {
+        "icon": "icon",
+        "title": "children",
+        "to": "/url",
+        "isExternalLink": false
+      }
+    ],
+    "icon": "icon",
+    "title": "parent"
+  });
+})

+ 22 - 5
types/types.d.ts

@@ -1,12 +1,23 @@
 import {Store} from "vuex";
+import {Ability} from "@casl/ability";
 interface ItemsMenu extends Array<ItemMenu> {}
 
+/**
+ * Upgrade du @nuxt/types pour TypeScript
+ */
+declare module '@nuxt/types' {
+  interface Context {
+    $ability(): Ability,
+    $rest: AnyJson
+  }
+}
+
 interface ItemMenu {
   icon: string,
   title: string,
   to?: string,
-  old?: boolean,
-  children?: ItemsMenu
+  children?: ItemsMenu,
+  isExternalLink?: boolean,
 }
 
 interface AbilitiesType {
@@ -25,8 +36,11 @@ interface AbilitiesType {
 interface accessState {
   bearer: string,
   accessId: number,
+  name: string,
+  givenName: string,
   roles: Array<string>,
   abilities: Array<AbilitiesType>,
+  isAdminAccess: boolean,
   isAdmin: boolean,
   isAdministratifManager: boolean,
   isPedagogicManager: boolean,
@@ -41,9 +55,12 @@ interface AccessStore extends Store<{profile:{access:accessState}}> {}
 
 interface organizationState {
   name: string,
-  product: string,
-  modules: Array<string>,
-  hasChildren: boolean,
+  product?: string,
+  modules?: Array<string>,
+  hasChildren?: boolean,
+  website?: string,
+  subDomain?: string,
+  parents: Array<organizationState>,
 }
 interface OrganizationStore extends Store<{profile:{organization:organizationState}}> {}
 

+ 55 - 0
use/template/Menus/accessMenu.ts

@@ -0,0 +1,55 @@
+import {ItemMenu, ItemsMenu} from "~/types/types";
+import {$organizationProfile} from "~/services/profile/organizationProfile";
+import BaseMenu from "~/use/template/Menus/baseMenu";
+
+class AccessMenu extends BaseMenu{
+  private $ability:any;
+  private $store:any;
+
+  constructor($config:any, $ability:any, $store:any) {
+    super($config)
+    this.$ability = $ability
+    this.$store = $store
+  }
+
+  /**
+   * Construit le menu Répertoire ou null si aucune page accessible
+   * @return {ItemMenu | null}
+   */
+  getMenu():ItemMenu | null {
+    const children:ItemsMenu = [];
+
+    if (this.$ability().can('display', 'accesses_page')) {
+      const organization = $organizationProfile(this.$store)
+      let to = organization.isSchool() ? `/students/list/` : `/adherent/list/`
+      children.push(this.constructMenu('fa-user', 'person', to, true))
+    }
+
+    if (this.$ability().can('display', 'student_registration_page')) {
+      children.push(this.constructMenu('fa-users', 'family_view', '/student_registration/new', true))
+    }
+
+    if (this.$ability().can('display', 'education_student_next_year_page')) {
+      children.push(this.constructMenu('fa-list-alt', 'education_student_next_year', '/education_student_next_year/list/', true))
+    }
+
+    if (this.$ability().can('display', 'commissions_page')) {
+      children.push(this.constructMenu('fa-street-view', 'commissions', '/commissions/list/', true))
+    }
+
+    if (this.$ability().can('display', 'network_children_page')) {
+      children.push(this.constructMenu('fa-sitemap', 'network', 'networks/list/', true))
+    }
+
+    if (this.$ability().can('display', 'network_parents_page')) {
+      children.push(this.constructMenu('fa-sitemap', 'my_network', '/network_artist_schools/list/', true))
+    }
+
+    if(children.length === 1){
+      return children[0];
+    }
+    return children.length > 0 ? this.constructMenu('fa-address-book', 'address_book', undefined, undefined, children) : null;
+  }
+}
+
+export const getAccessMenu = ($config:any, $ability:any, $store:any) => new AccessMenu($config, $ability, $store).getMenu()

+ 78 - 0
use/template/Menus/admin2iosMenu.ts

@@ -0,0 +1,78 @@
+import {ItemMenu, ItemsMenu} from "~/types/types";
+import BaseMenu from "~/use/template/Menus/baseMenu";
+
+class Admin2iosMenu extends BaseMenu{
+  private $ability:any;
+
+  constructor($config:any, $ability:any) {
+    super($config)
+    this.$ability = $ability
+  }
+
+  /**
+   * Construit le menu Administration 2ios ou null si aucune page accessible
+   * @return {ItemMenu | null}
+   */
+  getMenu():ItemMenu | null {
+    const children:ItemsMenu = [];
+
+    if (this.$ability().can('display', 'all_accesses_page')) {
+      children.push(this.constructMenu('fa-users', 'all_accesses', '/all_accesses/list/', true))
+    }
+
+    if (this.$ability().can('display', 'all_organizations_page')) {
+      children.push(this.constructMenu('fa-building', 'all_organizations', '/organization_params/list/', true))
+    }
+
+    if (this.$ability().can('display', 'tips_page')) {
+      children.push(this.constructMenu('fa-info-circle', 'tips', '/tips/list/', true))
+    }
+
+    if (this.$ability().can('display', 'actions_lead_page')) {
+      children.push(this.constructMenu('fa-comments-dollar', 'actions_lead', '/admin2ios/actions_lead', true))
+    }
+
+    if (this.$ability().can('display', 'renewall_list_page')) {
+      children.push(this.constructMenu('fa-sync', 'renewall_list', '/admin2ios/renewalllist', true))
+    }
+
+    if (this.$ability().can('display', 'settlements_page')) {
+      children.push(this.constructMenu('fa-euro-sign', 'settlements', '/admin2ios/settlements', true))
+    }
+
+    if (this.$ability().can('display', 'pendings_settlements_page')) {
+      children.push(this.constructMenu('fa-euro-sign', 'pendings_settlements', '/admin2ios/pendings_settlements', true))
+    }
+
+    if (this.$ability().can('display', 'outages_notice_page')) {
+      children.push(this.constructMenu('fa-cut', 'outages_notice', '/admin2ios/outagesnotice', true))
+    }
+
+    if (this.$ability().can('display', 'degraded_page')) {
+      children.push(this.constructMenu('fa-users', 'degraded', '/admin2ios/degraded', true))
+    }
+
+    if (this.$ability().can('display', 'dgv_page')) {
+      children.push(this.constructMenu('fa-house-damage', 'dgv', '/admin2ios/dgv', true))
+    }
+
+    if (this.$ability().can('display', 'cmf_cotisation_page')) {
+      children.push(this.constructMenu('fa-info-circle', 'cmf_cotisation', '/admin2ios/cotisationcmf', true))
+    }
+
+    if (this.$ability().can('display', 'right_page')) {
+      children.push(this.constructMenu('fa-balance-scale-right', 'right_menu', '/admin2ios/right', true))
+    }
+
+    if (this.$ability().can('display', 'tree_page')) {
+      children.push(this.constructMenu('fa-sitemap', 'tree_menu', '/admin2ios/tree', true))
+    }
+
+    if(children.length === 1){
+      return children[0];
+    }
+    return children.length > 0 ? this.constructMenu('fa-sitemap', 'admin2ios', undefined, undefined, children) : null;
+  }
+}
+
+export const getAdmin2iosMenu = ($config:any, $ability:any) => new Admin2iosMenu($config, $ability).getMenu()

+ 34 - 0
use/template/Menus/agendaMenu.ts

@@ -0,0 +1,34 @@
+import {ItemMenu, ItemsMenu} from "~/types/types";
+import BaseMenu from "~/use/template/Menus/baseMenu";
+
+class AgendaMenu extends BaseMenu{
+  private $ability:any;
+
+  constructor($config:any, $ability:any) {
+    super($config)
+    this.$ability = $ability
+  }
+
+  /**
+   * Construit le menu Agenda ou null si aucune page accessible
+   * @return {ItemMenu | null}
+   */
+  getMenu():ItemMenu | null {
+    const children:ItemsMenu = [];
+
+    if (this.$ability().can('display', 'agenda_page')) {
+      children.push(this.constructMenu('fa-calendar-alt', 'schedule', '/calendar', true))
+    }
+
+    if (this.$ability().can('display', 'attendance_page')) {
+      children.push(this.constructMenu('fa-calendar-check', 'attendances', '/attendances/list/', true))
+    }
+
+    if(children.length === 1){
+      return children[0];
+    }
+    return children.length > 0 ? this.constructMenu('fa-calendar-alt', 'schedule', undefined, undefined, children) : null;
+  }
+}
+
+export const getAgendaMenu = ($config:any, $ability:any) => new AgendaMenu($config, $ability).getMenu()

+ 33 - 0
use/template/Menus/baseMenu.ts

@@ -0,0 +1,33 @@
+import {ItemMenu} from "~/types/types";
+
+class BaseMenu{
+  protected $config:any;
+
+  constructor($config:any) {
+    this.$config = $config;
+  }
+
+  /**
+   * Construit un ItemMenu
+   * @param {string} icon
+   * @param {string} title titre qui sera traduit
+   * @param {string} link lien
+   * @param {boolean} isOldLink est-ce un lien renvoyant vers l'ancien admin?
+   * @param {Array<ItemMenu>} children Tableau d'ItemMenu représentant les sous menu du menu principal
+   * @param {boolean} isExternalLink est-ce un lien renvoyant vers l'extérieur?
+   * @return {ItemMenu}
+   */
+  constructMenu(icon: string, title: string, link?: string, isOldLink?: boolean, children?: Array<ItemMenu>, isExternalLink?: boolean): ItemMenu{
+    return children ? {
+      icon: icon,
+      title: title,
+      children: children,
+    } : {
+      icon: icon,
+      title: title,
+      to: `${isExternalLink || !isOldLink ? '' : this.$config.baseURL_adminLegacy}${link}`,
+      isExternalLink: isExternalLink || isOldLink,
+    }
+  }
+}
+export default BaseMenu;

+ 58 - 0
use/template/Menus/billingMenu.ts

@@ -0,0 +1,58 @@
+import {ItemMenu, ItemsMenu} from "~/types/types";
+import BaseMenu from "~/use/template/Menus/baseMenu";
+
+class BillingMenu extends BaseMenu{
+  private $ability:any;
+
+  constructor($config:any, $ability:any) {
+    super($config)
+    this.$ability = $ability
+  }
+
+  /**
+   * Construit le menu Facturation ou null si aucune page accessible
+   * @return {ItemMenu | null}
+   */
+  getMenu():ItemMenu | null {
+    const children:ItemsMenu = [];
+
+    if (this.$ability().can('display', 'billing_product_page')) {
+      children.push(this.constructMenu('fa-cube', 'billing_product', '/intangibles/list/', true))
+    }
+
+    if (this.$ability().can('display', 'billing_products_by_student_page')) {
+      children.push(this.constructMenu('fa-cubes', 'billing_products_by_student', '/access_intangibles/list/', true))
+    }
+
+    if (this.$ability().can('display', 'billing_edition_page')) {
+      children.push(this.constructMenu('fa-copy', 'billing_edition', '/billing_edition', true))
+    }
+
+    if (this.$ability().can('display', 'billing_accounting_page')) {
+      children.push(this.constructMenu('fa-file-alt', 'billing_accounting', '/bill_accountings/list/', true))
+    }
+
+    if (this.$ability().can('display', 'billing_payment_list_page')) {
+      children.push(this.constructMenu('fa-credit-card', 'billing_payment_list', '/bill_payments_list/list/', true))
+    }
+
+    if (this.$ability().can('display', 'pes_page')) {
+      children.push(this.constructMenu('fa-align-justify', 'pes_export', '/pes/list/', true))
+    }
+
+    if (this.$ability().can('display', 'berger_levrault_page')) {
+      children.push(this.constructMenu('fa-align-justify', 'berger_levrault_export', '/berger_levraults/list/', true))
+    }
+
+    if (this.$ability().can('display', 'jvs_page')) {
+      children.push(this.constructMenu('fa-align-justify', 'jvs_export', '/jvs/list/', true))
+    }
+
+    if(children.length === 1){
+      return children[0];
+    }
+    return children.length > 0 ? this.constructMenu('fa-euro-sign', 'billing', undefined, undefined, children) : null;
+  }
+}
+
+export const getBillingMenu = ($config:any, $ability:any) => new BillingMenu($config, $ability).getMenu()

+ 38 - 0
use/template/Menus/communicationMenu.ts

@@ -0,0 +1,38 @@
+import {ItemMenu, ItemsMenu} from "~/types/types";
+import BaseMenu from "~/use/template/Menus/baseMenu";
+
+class CommunicationMenu extends BaseMenu{
+  private $ability:any;
+
+  constructor($config:any, $ability:any) {
+    super($config)
+    this.$ability = $ability
+  }
+
+  /**
+   * Construit le menu Communication ou null si aucune page accessible
+   * @return {ItemMenu | null}
+   */
+  getMenu():ItemMenu | null {
+    const children:ItemsMenu = [];
+
+    if (this.$ability().can('display', 'inbox_page')) {
+      children.push(this.constructMenu('fa-inbox', 'inbox', '/messages/list/', true))
+    }
+
+    if (this.$ability().can('display', 'message_send_page')) {
+      children.push(this.constructMenu('fa-paper-plane', 'message_send', '/messagessends/list/', true))
+    }
+
+    if (this.$ability().can('display', 'message_templates_page')) {
+      children.push(this.constructMenu('fa-edit', 'message_templates', '/templates/list/', true))
+    }
+
+    if(children.length === 1){
+      return children[0];
+    }
+    return children.length > 0 ? this.constructMenu('fa-comments', 'communication', undefined, undefined, children) : null;
+  }
+}
+
+export const getCommunicationMenu= ($config:any, $ability:any) => new CommunicationMenu($config, $ability).getMenu()

+ 94 - 0
use/template/Menus/cotisationsMenu.ts

@@ -0,0 +1,94 @@
+import {ItemMenu, ItemsMenu} from "~/types/types";
+import BaseMenu from "~/use/template/Menus/baseMenu";
+
+class CotisationsMenu extends BaseMenu{
+  private $ability:any;
+
+  constructor($config:any, $ability:any) {
+    super($config)
+    this.$ability = $ability
+  }
+
+  /**
+   * Construit le menu Cotisations ou null si aucune page accessible
+   * @return {ItemMenu | null}
+   */
+  getMenu():ItemMenu | null {
+    const children:ItemsMenu = [];
+
+    if (this.$ability().can('display', 'rate_cotisation_page')) {
+      children.push(this.constructMenu('fa-euro-sign', 'rate_cotisation', '/cotisation/rate', true))
+    }
+
+    if (this.$ability().can('display', 'parameters_cotisation_page')) {
+      children.push(this.constructMenu('fa-euro-sign', 'parameters_cotisation', '/cotisation/parameter', true))
+    }
+
+    if (this.$ability().can('display', 'send_cotisation_page')) {
+      children.push(this.constructMenu('fa-euro-sign', 'send_cotisation', '/cotisation/send', true))
+    }
+
+    if (this.$ability().can('display', 'state_cotisation_page')) {
+      children.push(this.constructMenu('fa-euro-sign', 'state_cotisation', '/cotisation/state', true))
+    }
+
+    if (this.$ability().can('display', 'pay_cotisation_page')) {
+      children.push(this.constructMenu('fa-euro-sign', 'pay_cotisation', '/cotisation/pay', true))
+    }
+
+    if (this.$ability().can('display', 'check_cotisation_page')) {
+      children.push(this.constructMenu('fa-euro-sign', 'check_cotisation', '/cotisation/check', true))
+    }
+
+    if (this.$ability().can('display', 'ledger_cotisation_page')) {
+      children.push(this.constructMenu('fa-euro-sign', 'ledger_cotisation', '/cotisation/ledger', true))
+    }
+
+    if (this.$ability().can('display', 'magazine_cotisation_page')) {
+      children.push(this.constructMenu('fa-euro-sign', 'magazine_cotisation', '/cotisation/magazine', true))
+    }
+
+    if (this.$ability().can('display', 'ventilated_cotisation_page')) {
+      children.push(this.constructMenu('fa-euro-sign', 'ventilated_cotisation', '/cotisation/ventilated', true))
+    }
+
+    if (this.$ability().can('display', 'pay_erase_cotisation_page')) {
+      children.push(this.constructMenu('fa-euro-sign', 'pay_erase_cotisation', '/cotisation/payerase', true))
+    }
+
+    if (this.$ability().can('display', 'resume_cotisation_page')) {
+      children.push(this.constructMenu('fa-euro-sign', 'resume_cotisation', '/cotisation/resume', true))
+    }
+
+    if (this.$ability().can('display', 'history_cotisation_page')) {
+      children.push(this.constructMenu('fa-euro-sign', 'history_cotisation', '/cotisation/history', true))
+    }
+
+    if (this.$ability().can('display', 'call_cotisation_page')) {
+      children.push(this.constructMenu('fa-euro-sign', 'call_cotisation', '/cotisation/call', true))
+    }
+
+    if (this.$ability().can('display', 'history_struture_cotisation_page')) {
+      children.push(this.constructMenu('fa-euro-sign', 'history_struture_cotisation', '/cotisation/historystructure', true))
+    }
+
+    if (this.$ability().can('display', 'insurance_cotisation_page')) {
+      children.push(this.constructMenu('fa-euro-sign', 'insurance_cotisation', '/cotisation/insurance', true))
+    }
+
+    if (this.$ability().can('display', 'resume_all_cotisation_page')) {
+      children.push(this.constructMenu('fa-euro-sign', 'resume_all_cotisation', '/cotisation/resumeall', true))
+    }
+
+    if (this.$ability().can('display', 'resume_pay_cotisation_page')) {
+      children.push(this.constructMenu('fa-euro-sign', 'resume_pay_cotisation', '/cotisation/resumepay', true))
+    }
+
+    if(children.length === 1){
+      return children[0];
+    }
+    return children.length > 0 ? this.constructMenu('fa-money-bill', 'cotisations', undefined, undefined, children) : null;
+  }
+}
+
+export const getCotisationsMenu = ($config:any, $ability:any) => new CotisationsMenu($config, $ability).getMenu()

+ 24 - 0
use/template/Menus/donorsMenu.ts

@@ -0,0 +1,24 @@
+import {ItemMenu, ItemsMenu} from "~/types/types";
+import BaseMenu from "~/use/template/Menus/baseMenu";
+
+class DonorsMenu extends BaseMenu{
+  private $ability:any;
+
+  constructor($config:any, $ability:any) {
+    super($config)
+    this.$ability = $ability
+  }
+
+  /**
+   * Construit le menu Partenariat et Dons ou null si aucune page accessible
+   * @return {ItemMenu | null}
+   */
+  getMenu():ItemMenu | null {
+    if (this.$ability().can('display', 'donors_page')) {
+      return this.constructMenu('far fa-handshake', 'donors', '/donors/list/', true)
+    }
+    return null;
+  }
+}
+
+export const getDonorsMenu = ($config:any, $ability:any) => new DonorsMenu($config, $ability).getMenu()

+ 50 - 0
use/template/Menus/educationalMenu.ts

@@ -0,0 +1,50 @@
+import {ItemMenu, ItemsMenu} from "~/types/types";
+import BaseMenu from "~/use/template/Menus/baseMenu";
+
+class EducationalMenu extends BaseMenu{
+  private $ability:any;
+
+  constructor($config:any, $ability:any) {
+    super($config)
+    this.$ability = $ability
+  }
+
+  /**
+   * Construit le menu Suivi pédagogique ou null si aucune page accessible
+   * @return {ItemMenu | null}
+   */
+  getMenu():ItemMenu | null {
+    const children:ItemsMenu = [];
+
+    if (this.$ability().can('display', 'criteria_notations_page')) {
+      children.push(this.constructMenu('fa-bars', 'criteria_notations', '/criteria_notations/list/', true))
+    }
+
+    if (this.$ability().can('display', 'seizure_period_page')) {
+      children.push(this.constructMenu('fa-calendar-alt', 'seizure_period', '/education_teachers/list/', true))
+    }
+
+    if (this.$ability().can('display', 'test_seizure_page')) {
+      children.push(this.constructMenu('fa-pencil-alt', 'test_seizure', '/education_input/list/', true))
+    }
+
+    if (this.$ability().can('display', 'test_validation_page')) {
+      children.push(this.constructMenu('fa-check', 'test_validation', '/education_notations/list/', true))
+    }
+
+    if (this.$ability().can('display', 'examen_results_page')) {
+      children.push(this.constructMenu('fa-graduation-cap', 'examen_results', '/examen_convocations/list/', true))
+    }
+
+    if (this.$ability().can('display', 'education_by_student_validation_page')) {
+      children.push(this.constructMenu('fa-check-square', 'education_by_student_validation', '/education_by_student/list/', true))
+    }
+
+    if(children.length === 1){
+      return children[0];
+    }
+    return children.length > 0 ? this.constructMenu('fa-graduation-cap', 'education_state', undefined, undefined, children) : null;
+  }
+}
+
+export const getEducationalMenu = ($config:any, $ability:any) => new EducationalMenu($config, $ability).getMenu()

+ 24 - 0
use/template/Menus/equipmentMenu.ts

@@ -0,0 +1,24 @@
+import {ItemMenu} from "~/types/types";
+import BaseMenu from "~/use/template/Menus/baseMenu";
+
+class EquipmentMenu extends BaseMenu{
+  private $ability:any;
+
+  constructor($config:any, $ability:any) {
+    super($config)
+    this.$ability = $ability
+  }
+
+  /**
+   * Construit le menu Equipement ou null si aucune page accessible
+   * @return {ItemMenu | null}
+   */
+  getMenu():ItemMenu | null {
+    if (this.$ability().can('display', 'equipment_page')) {
+      return this.constructMenu('fa-cube', 'equipment', '/equipment/list', true)
+    }
+    return null;
+  }
+}
+
+export const getEquipmentMenu = ($config:any, $ability:any) => new EquipmentMenu($config, $ability).getMenu()

+ 24 - 0
use/template/Menus/medalsMenu.ts

@@ -0,0 +1,24 @@
+import {ItemMenu, ItemsMenu} from "~/types/types";
+import BaseMenu from "~/use/template/Menus/baseMenu";
+
+class MedalsMenu extends BaseMenu{
+  private $ability:any;
+
+  constructor($config:any, $ability:any) {
+    super($config)
+    this.$ability = $ability
+  }
+
+  /**
+   * Construit le menu Médails et Dons ou null si aucune page accessible
+   * @return {ItemMenu | null}
+   */
+  getMenu():ItemMenu | null {
+    if (this.$ability().can('display', 'medals_page')) {
+      return this.constructMenu('fa-trophy', 'medals', '/medals/list/', true)
+    }
+    return null;
+  }
+}
+
+export const getMedalsMenu = ($config:any, $ability:any) => new MedalsMenu($config, $ability).getMenu()

+ 38 - 0
use/template/Menus/statsMenu.ts

@@ -0,0 +1,38 @@
+import {ItemMenu, ItemsMenu} from "~/types/types";
+import BaseMenu from "~/use/template/Menus/baseMenu";
+
+class StatsMenu extends BaseMenu{
+  private $ability:any;
+
+  constructor($config:any, $ability:any) {
+    super($config)
+    this.$ability = $ability
+  }
+
+  /**
+   * Construit le menu Statistique et Dons ou null si aucune page accessible
+   * @return {ItemMenu | null}
+   */
+  getMenu():ItemMenu | null {
+    const children:ItemsMenu = [];
+
+    if (this.$ability().can('display', 'report_activity_page')) {
+      children.push(this.constructMenu('fa-chart-bar', 'report_activity', '/report_activity', true))
+    }
+
+    if (this.$ability().can('display', 'fede_stats_page')) {
+      children.push(this.constructMenu('fa-chart-bar', 'fede_stats', '/statistic/membersfedeonly', true))
+    }
+
+    if (this.$ability().can('display', 'structure_stats_page')) {
+      children.push(this.constructMenu('fa-chart-bar', 'structure_stats', '/statistic/membersfedeassos', true))
+    }
+
+    if(children.length === 1){
+      return children[0];
+    }
+    return children.length > 0 ? this.constructMenu('fa-chart-bar', 'stats', undefined, undefined, children) : null;
+  }
+}
+
+export const getStatsMenu = ($config:any, $ability:any) => new StatsMenu($config, $ability).getMenu()

+ 39 - 0
use/template/Menus/websiteMenu.ts

@@ -0,0 +1,39 @@
+import {ItemMenu, ItemsMenu, organizationState} from "~/types/types";
+import BaseMenu from "~/use/template/Menus/baseMenu";
+import *  as _ from "lodash"
+
+class WebsiteMenu extends BaseMenu{
+  private $ability:any;
+  private $store:any;
+
+  constructor($config:any, $ability:any, $store:any) {
+    super($config)
+    this.$ability = $ability
+    this.$store = $store
+  }
+
+  /**
+   * Construit le menu Site internet ou null si aucune page accessible
+   * @return {ItemMenu | null}
+   */
+  getMenu():ItemMenu | null {
+    const children:ItemsMenu = [];
+
+    children.push(this.constructMenu('fa-globe-europe', this.$store.state.profile.organization.name, this.getWebsite(this.$store.state.profile.organization), false, undefined, true))
+
+    if(!this.$store.state.profile.organization.website && this.$store.state.profile.access.isAdminAccess)
+      children.push(this.constructMenu('fa-globe-europe', 'advanced_modification', this.getWebsite(this.$store.state.profile.organization) + '/typo3', false, undefined, true))
+
+    _.each(this.$store.state.profile.organization.parents, parent => {
+      children.push(this.constructMenu('fa-globe-europe', parent.name, this.getWebsite(parent), false, undefined, true))
+    })
+
+    return children.length > 0 ? this.constructMenu('fa-globe-europe', 'website', undefined, undefined, children) : null;
+  }
+
+  getWebsite(organization:organizationState):string{
+    return organization.website ? organization.website : this.$config.baseURL_typo3.replace('###subDomain###', organization.subDomain)
+  }
+}
+
+export const getWebsiteMenu = ($config:any, $ability:any, $store:any) => new WebsiteMenu($config, $ability, $store).getMenu()

+ 56 - 122
use/template/menu.ts

@@ -1,153 +1,87 @@
 import {ref, useContext} from "@nuxtjs/composition-api";
-import {ItemMenu, ItemsMenu} from "~/types/types";
-import {organizationProfile} from "~/services/profile/organizationProfile";
-
-export default class Menu{
+import {ItemsMenu} from "~/types/types";
+import {getAccessMenu} from "~/use/template/Menus/accessMenu";
+import {getAgendaMenu} from "~/use/template/Menus/agendaMenu";
+import {getEquipmentMenu} from "~/use/template/Menus/equipmentMenu";
+import {getEducationalMenu} from "~/use/template/Menus/educationalMenu";
+import {getBillingMenu} from "~/use/template/Menus/billingMenu";
+import {getCommunicationMenu} from "~/use/template/Menus/communicationMenu";
+import {getDonorsMenu} from "~/use/template/Menus/donorsMenu";
+import {getMedalsMenu} from "~/use/template/Menus/medalsMenu";
+import {getStatsMenu} from "~/use/template/Menus/statsMenu";
+import {getCotisationsMenu} from "~/use/template/Menus/cotisationsMenu";
+import {getAdmin2iosMenu} from "~/use/template/Menus/admin2iosMenu";
+import {getWebsiteMenu} from "~/use/template/Menus/websiteMenu";
+
+/**
+ * @category Use/template
+ * @class Menu
+ * Use Classe pour la construction du Menu
+ */
+class Menu{
   private $ability:any;
   private $config:any;
   private $store:any;
 
+  /**
+   * @constructor
+   * Initialisation des services issues du context
+   */
   constructor() {
+  }
+
+  setUpContext(){
     const {$ability, $config, store} = useContext();
     this.$ability = $ability;
     this.$config = $config;
     this.$store = store;
+    return this;
   }
 
-  useMenuConstruct(){
-    let menu:ItemsMenu = [ this.constructMenu('fa-home', 'welcome', '/dashboard', true) ]
+  /**
+   * Construit le menu
+   */
+  useLateralMenuConstruct(){
+    let menu:ItemsMenu = []
 
-    const accessMenu = this.accessMenu()
+    const accessMenu = getAccessMenu(this.$config,this.$ability,this.$store)
     if(accessMenu) menu.push(accessMenu)
 
-    const agendaMenu = this.agendaMenu()
+    const agendaMenu = getAgendaMenu(this.$config,this.$ability)
     if(agendaMenu) menu.push(agendaMenu)
 
-    const equipmentMenu = this.equipmentMenu()
+    const equipmentMenu = getEquipmentMenu(this.$config,this.$ability)
     if(equipmentMenu) menu.push(equipmentMenu)
 
-    const educationalMenu = this.educationalMenu()
+    const educationalMenu = getEducationalMenu(this.$config,this.$ability)
     if(educationalMenu) menu.push(educationalMenu)
 
-    return ref(menu)
-  }
-
-  /**
-   * Menu Répertoire
-   */
-  accessMenu():ItemMenu | null {
-    const children:ItemsMenu = [];
-
-    if (this.$ability().can('display', 'accesses_page')) {
-      const organization = organizationProfile(this.$store)
-      let to = organization.isSchool() ? `/students/list/` : `/adherent/list/`
-      children.push(this.constructMenu('fa-user', 'person', to, true))
-    }
-
-    if (this.$ability().can('display', 'student_registration_page')) {
-      children.push(this.constructMenu('fa-users', 'family_view', '/student_registration/new', true))
-    }
-
-    if (this.$ability().can('display', 'education_student_next_year_page')) {
-      children.push(this.constructMenu('fa-list-alt', 'education_student_next_year', '/education_student_next_year/list/', true))
-    }
-    if (this.$ability().can('display', 'commissions_page')) {
-      children.push(this.constructMenu('fa-street-view', 'commissions', '/commissions/list/', true))
-    }
-    if (this.$ability().can('display', 'network_children_page')) {
-      children.push(this.constructMenu('fa-sitemap', 'network', 'networks/list/', true))
-    }
-    if (this.$ability().can('display', 'network_parents_page')) {
-      children.push(this.constructMenu('fa-sitemap', 'my_network', '/network_artist_schools/list/', true))
-    }
-
-    if(children.length === 1){
-      return children[0];
-    }
-    return children.length > 0 ? this.constructMenu('fa-address-book', 'address_book', undefined, undefined, children) : null;
-  }
-
-  /**
-   * Menu agenda
-   */
-  agendaMenu():ItemMenu | null {
-    const children:ItemsMenu = [];
-
-    if (this.$ability().can('display', 'agenda_page')) {
-      children.push(this.constructMenu('fa-calendar-alt', 'schedule', '/calendar', true))
-    }
-
-    if (this.$ability().can('display', 'attendance_page')) {
-      children.push(this.constructMenu('fa-calendar-check', 'attendances', '/attendances/list/', true))
-    }
-
-    if(children.length === 1){
-      return children[0];
-    }
-    return children.length > 0 ? this.constructMenu('fa-calendar-alt', 'schedule', undefined, undefined, children) : null;
-  }
-
-  /**
-   * Menu equipment
-   */
-  equipmentMenu():ItemMenu | null {
-    return this.constructMenu('fa-cube', 'equipment', '/equipment/list', true)
-  }
-
-  /**
-   * Menu suivi pédagogique
-   */
-  educationalMenu():ItemMenu | null {
-    const children:ItemsMenu = [];
+    const billingMenu = getBillingMenu(this.$config,this.$ability)
+    if(billingMenu) menu.push(billingMenu)
 
-    if (this.$ability().can('display', 'criteria_notations_page')) {
-      children.push(this.constructMenu('fa-bars', 'criteria_notations', '/criteria_notations/list/', true))
-    }
+    const communicationMenu = getCommunicationMenu(this.$config,this.$ability)
+    if(communicationMenu) menu.push(communicationMenu)
 
-    if (this.$ability().can('display', 'seizure_period_page')) {
-      children.push(this.constructMenu('fa-calendar-alt', 'seizure_period', '/education_teachers/list/', true))
-    }
+    const donorsMenu = getDonorsMenu(this.$config,this.$ability)
+    if(donorsMenu) menu.push(donorsMenu)
 
-    if (this.$ability().can('display', 'test_seizure_page')) {
-      children.push(this.constructMenu('fa-pencil-alt', 'test_seizure', '/education_input/list/', true))
-    }
+    const medalsMenu = getMedalsMenu(this.$config,this.$ability)
+    if(medalsMenu) menu.push(medalsMenu)
 
-    if (this.$ability().can('display', 'test_validation_page')) {
-      children.push(this.constructMenu('fa-check', 'test_validation', '/education_notations/list/', true))
-    }
+    const websiteMenu = getWebsiteMenu(this.$config,this.$ability,this.$store)
+    if(websiteMenu) menu.push(websiteMenu)
 
-    if (this.$ability().can('display', 'examen_results_page')) {
-      children.push(this.constructMenu('fa-graduation-cap', 'examen_results', '/examen_convocations/list/', true))
-    }
+    const cotisationsMenu = getCotisationsMenu(this.$config,this.$ability)
+    if(cotisationsMenu) menu.push(cotisationsMenu)
 
-    if (this.$ability().can('display', 'education_by_student_validation_page')) {
-      children.push(this.constructMenu('fa-check-square', 'education_by_student_validation', '/education_by_student/list/', true))
-    }
+    const statsMenu = getStatsMenu(this.$config,this.$ability)
+    if(statsMenu) menu.push(statsMenu)
 
-    if(children.length === 1){
-      return children[0];
-    }
-    return children.length > 0 ? this.constructMenu('fa-graduation-cap', 'education_state', undefined, undefined, children) : null;
-  }
+    const admin2iosMenu = getAdmin2iosMenu(this.$config,this.$ability)
+    if(admin2iosMenu) menu.push(admin2iosMenu)
 
-  /**
-   * Construit un ItemMenu
-   * @param icon
-   * @param title
-   * @param link
-   * @param isOldLink
-   * @param children
-   */
-  constructMenu(icon: string, title: string, link?: string, isOldLink?: boolean, children?: Array<ItemMenu>): ItemMenu{
-    return children ? {
-      icon: icon,
-      title: title,
-      children: children,
-    } : {
-      icon: icon,
-      title: title,
-      to: `${isOldLink ? this.$config.baseURL_adminLegacy : ''}${link}`,
-      old: isOldLink,
-    }
+    return ref(menu)
   }
 }
+
+export const $useMenu = new Menu()

+ 15 - 29
yarn.lock

@@ -1607,14 +1607,14 @@
     webpack-node-externals "^2.5.2"
     webpackbar "^4.0.0"
 
-"@nuxtjs/composition-api@^0.17.0":
-  version "0.17.0"
-  resolved "https://registry.yarnpkg.com/@nuxtjs/composition-api/-/composition-api-0.17.0.tgz#533402f82dc860cbed17d9612ac8b41333789b26"
-  integrity sha512-hc/1amRpu1sr4/0jkb4T0N37YgvM00jcBTQeur5ymdUd52eJExKiOu+EwbWvTPRFz8IU29x6PhdLZ5LcJT6FgA==
+"@nuxtjs/composition-api@0.19":
+  version "0.19.0"
+  resolved "https://registry.yarnpkg.com/@nuxtjs/composition-api/-/composition-api-0.19.0.tgz#06ed97e70e4c5522bad151aa1890d85afb252b49"
+  integrity sha512-eej0MNm2KE4BSzAq+iZ2NrL8abDBfZoFrbBHV/GxuOVOQce2aKOEVZ3HB7UgEvdJywKmhM1/RajkGNG+YvT4CA==
   dependencies:
-    "@vue/composition-api" "1.0.0-beta.21"
+    "@vue/composition-api" "1.0.0-rc.1"
     defu "^3.2.2"
-    normalize-path "^3.0.0"
+    upath "^2.0.1"
 
 "@nuxtjs/eslint-config-typescript@^3.0.0":
   version "3.0.0"
@@ -2447,12 +2447,12 @@
   optionalDependencies:
     prettier "^1.18.2"
 
-"@vue/composition-api@1.0.0-beta.21":
-  version "1.0.0-beta.21"
-  resolved "https://registry.yarnpkg.com/@vue/composition-api/-/composition-api-1.0.0-beta.21.tgz#d5a3c68afc8b569dfc3eccd69998388bb7f6a16c"
-  integrity sha512-tgbvDpLvKQ1GrII424wsoyzPCsG0oTFf38emMq495SfLY7RmUqhVIl81pvnC5489PPrCxDkbauJHJrhlcXfbTQ==
+"@vue/composition-api@1.0.0-rc.1":
+  version "1.0.0-rc.1"
+  resolved "https://registry.yarnpkg.com/@vue/composition-api/-/composition-api-1.0.0-rc.1.tgz#d5286bbaffcd1987e56d5e3a9f26bfb6023e7c41"
+  integrity sha512-6I5LkfA+VvVOLZufRugzQ5sWKQH81/Cr9gRLidDSeUmjRPolEEWgG4MAnWtBR5zgP5qbRMwZJ4IEYtKhnadMQQ==
   dependencies:
-    tslib "^2.0.1"
+    tslib "^2.0.3"
 
 "@vue/test-utils@^1.1.0":
   version "1.1.1"
@@ -2463,10 +2463,10 @@
     lodash "^4.17.15"
     pretty "^2.0.0"
 
-"@vuex-orm/core@1.0.0-draft.7":
-  version "1.0.0-draft.7"
-  resolved "https://registry.yarnpkg.com/@vuex-orm/core/-/core-1.0.0-draft.7.tgz#959aa88bfd785d73e017d73955081c9a808642d1"
-  integrity sha512-XmmUjqXPEjgXR6GQbW0RinwW0TraGm3epJxdb8jyAMN43DEH37Yb56KycJb13yQPoz/MYSzs7Gqp0GvXkOZqvQ==
+"@vuex-orm/core@1.0.0-draft.8":
+  version "1.0.0-draft.8"
+  resolved "https://registry.yarnpkg.com/@vuex-orm/core/-/core-1.0.0-draft.8.tgz#3122ca7aff6f651a26e29de850f76404ad2686a6"
+  integrity sha512-pGfuHK+53Qh5S5Y5Pko5NhQPYJapO1V87KIeHQAX8twbcxPJUgZw6HTRVLUogrJ7WHtKcv6XbpPQgvE29UOE2Q==
   dependencies:
     "@types/uuid" "^8.0.0"
     normalizr "^3.6.1"
@@ -7787,13 +7787,6 @@ markdown-it@^10.0.0:
     mdurl "^1.0.1"
     uc.micro "^1.0.5"
 
-markdown@^0.5.0:
-  version "0.5.0"
-  resolved "https://registry.yarnpkg.com/markdown/-/markdown-0.5.0.tgz#28205b565a8ae7592de207463d6637dc182722b2"
-  integrity sha1-KCBbVlqK51kt4gdGPWY33BgnIrI=
-  dependencies:
-    nopt "~2.1.1"
-
 marked@^0.8.2:
   version "0.8.2"
   resolved "https://registry.yarnpkg.com/marked/-/marked-0.8.2.tgz#4faad28d26ede351a7a1aaa5fec67915c869e355"
@@ -8271,13 +8264,6 @@ nopt@^5.0.0:
   dependencies:
     abbrev "1"
 
-nopt@~2.1.1:
-  version "2.1.2"
-  resolved "https://registry.yarnpkg.com/nopt/-/nopt-2.1.2.tgz#6cccd977b80132a07731d6e8ce58c2c8303cf9af"
-  integrity sha1-bMzZd7gBMqB3MdbozljCyDA8+a8=
-  dependencies:
-    abbrev "1"
-
 normalize-package-data@^2.3.2, normalize-package-data@^2.5.0:
   version "2.5.0"
   resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"