diff --git a/packages/manager/apps/telecom/package.json b/packages/manager/apps/telecom/package.json index 5ade8cbc42c6..a09dfa548912 100644 --- a/packages/manager/apps/telecom/package.json +++ b/packages/manager/apps/telecom/package.json @@ -33,6 +33,7 @@ "@bower_components/angular-ui-utils": "angular-ui/ui-utils#0.2.3", "@bower_components/crypto": "anodynos/node2web_crypto#3.46.1", "@ovh-ux/manager-banner": "^1.1.0", + "@ovh-ux/manager-carrier-sip": "^0.0.0", "@ovh-ux/manager-config": "^0.3.0", "@ovh-ux/manager-core": "^7.1.3", "@ovh-ux/manager-freefax": "^5.2.2", diff --git a/packages/manager/apps/telecom/src/app/app.js b/packages/manager/apps/telecom/src/app/app.js index 424e7b8916e2..651f4b364e05 100644 --- a/packages/manager/apps/telecom/src/app/app.js +++ b/packages/manager/apps/telecom/src/app/app.js @@ -31,6 +31,7 @@ import ngOvhLineDiagnostics from '@ovh-ux/ng-ovh-line-diagnostics'; import ngOvhContact from '@ovh-ux/ng-ovh-contact'; import TelecomAppCtrl from './app.controller'; +import carrierSip from './telecom/telephony/carrierSip'; import navbar from '../components/navbar'; import 'ovh-ui-kit-bs/dist/ovh-ui-kit-bs.css'; @@ -104,6 +105,7 @@ angular.module('managerApp', [ 'ui.sortable', 'ui.validate', 'validation.match', + carrierSip, ]) /*= ========= GLOBAL OPTIONS ========== */ @@ -211,7 +213,7 @@ angular.module('managerApp', [ $logProvider.debugEnabled(false); }) - .run(( + .run(/* @ngInject */ ( $transitions, $translate, $translatePartialLoader, diff --git a/packages/manager/apps/telecom/src/app/telecom/telephony/carrierSip/dashboard/cdr/cdr.module.js b/packages/manager/apps/telecom/src/app/telecom/telephony/carrierSip/dashboard/cdr/cdr.module.js new file mode 100644 index 000000000000..b419a4d87901 --- /dev/null +++ b/packages/manager/apps/telecom/src/app/telecom/telephony/carrierSip/dashboard/cdr/cdr.module.js @@ -0,0 +1,19 @@ +import angular from 'angular'; + +// Module dependencies. +import uiRouter from '@uirouter/angularjs'; +import ovhManagerCarrierSip from '@ovh-ux/manager-carrier-sip'; + +// Routing and configuration. +import routing from './cdr.routing'; + +const moduleName = 'ovhManagerTelecomCarrierSipDashboardCdr'; + +angular + .module(moduleName, [ + ovhManagerCarrierSip, + uiRouter, + ]) + .config(routing); + +export default moduleName; diff --git a/packages/manager/apps/telecom/src/app/telecom/telephony/carrierSip/dashboard/cdr/cdr.routing.js b/packages/manager/apps/telecom/src/app/telecom/telephony/carrierSip/dashboard/cdr/cdr.routing.js new file mode 100644 index 000000000000..b0d870aaf8b9 --- /dev/null +++ b/packages/manager/apps/telecom/src/app/telecom/telephony/carrierSip/dashboard/cdr/cdr.routing.js @@ -0,0 +1,8 @@ +export default /* @ngInject */ ($stateProvider) => { + $stateProvider.state('telecom.telephony.carrierSip.cdr', { + url: '/cdr', + views: { + '@telecom.telephony.carrierSip': 'carrierSipCdr', + }, + }); +}; diff --git a/packages/manager/apps/telecom/src/app/telecom/telephony/carrierSip/dashboard/cdr/index.js b/packages/manager/apps/telecom/src/app/telecom/telephony/carrierSip/dashboard/cdr/index.js new file mode 100644 index 000000000000..07483d000dd6 --- /dev/null +++ b/packages/manager/apps/telecom/src/app/telecom/telephony/carrierSip/dashboard/cdr/index.js @@ -0,0 +1,25 @@ +import angular from 'angular'; + +// Module dependencies. +import uiRouter from '@uirouter/angularjs'; +import oclazyload from 'oclazyload'; + +const moduleName = 'ovhManagerTelecomCarrierSipDashboardCdrLazyLoading'; + +angular + .module(moduleName, [ + oclazyload, + uiRouter, + ]) + .config(/* @ngInject */($stateProvider) => { + $stateProvider.state('telecom.telephony.carrierSip.cdr.**', { + url: '/cdr', + lazyLoad: ($transition$) => { + const $ocLazyLoad = $transition$.injector().get('$ocLazyLoad'); + + return import('./cdr.module') + .then(mod => $ocLazyLoad.inject(mod.default || mod)); + }, + }); + }); +export default moduleName; diff --git a/packages/manager/apps/telecom/src/app/telecom/telephony/carrierSip/dashboard/dashboard.module.js b/packages/manager/apps/telecom/src/app/telecom/telephony/carrierSip/dashboard/dashboard.module.js new file mode 100644 index 000000000000..1701293fb1c2 --- /dev/null +++ b/packages/manager/apps/telecom/src/app/telecom/telephony/carrierSip/dashboard/dashboard.module.js @@ -0,0 +1,22 @@ +import angular from 'angular'; + +// Module dependencies. +import uiRouter from '@uirouter/angularjs'; + +import cdr from './cdr'; +import endpoints from './endpoints'; + +// Routing and configuration. +import routing from './dashboard.routing'; + +const moduleName = 'ovhManagerTelecomCarrierSipDashboard'; + +angular + .module(moduleName, [ + cdr, + endpoints, + uiRouter, + ]) + .config(routing); + +export default moduleName; diff --git a/packages/manager/apps/telecom/src/app/telecom/telephony/carrierSip/dashboard/dashboard.routing.js b/packages/manager/apps/telecom/src/app/telecom/telephony/carrierSip/dashboard/dashboard.routing.js new file mode 100644 index 000000000000..2529fd85c9f0 --- /dev/null +++ b/packages/manager/apps/telecom/src/app/telecom/telephony/carrierSip/dashboard/dashboard.routing.js @@ -0,0 +1,47 @@ +export default /* @ngInject */ ($stateProvider) => { + $stateProvider.state('telecom.telephony.carrierSip', { + url: '/carrierSip/:serviceName', + views: { + 'telephonyView@telecom.telephony': 'carrierSipDashboard', + }, + resolve: { + billingAccount: /* @ngInject */ $transition$ => $transition$.params().billingAccount, + billingLink: /* @ngInject */ ( + $state, + billingAccount, + ) => $state.href('telecom.telephony.billing', { billingAccount }), + carrierSip: /* @ngInject */ ( + billingAccount, + CarrierSipService, + serviceName, + ) => CarrierSipService.getCarrierSip(billingAccount, serviceName), + cdrsLink: /* @ngInject */ ( + $state, + billingAccount, + serviceName, + ) => $state.href('telecom.telephony.carrierSip.cdr', { billingAccount, serviceName }), + currentActiveLink: /* @ngInject */ ($transition$, $state) => () => $state + .href($state.current.name, $transition$.params()), + dashboardLink: /* @ngInject */ ( + $state, + billingAccount, + serviceName, + ) => $state.href('telecom.telephony.carrierSip', { billingAccount, serviceName }), + endpointsLink: /* @ngInject */ ( + $state, + billingAccount, + serviceName, + ) => $state.href('telecom.telephony.carrierSip.endpoints', { billingAccount, serviceName }), + serviceInfos: /* @ngInject */ ( + CarrierSipService, + serviceName, + ) => CarrierSipService.getServiceInfos(serviceName), + serviceName: /* @ngInject */ $transition$ => $transition$.params().serviceName, + settings: /* @ngInject */ ( + billingAccount, + CarrierSipService, + serviceName, + ) => CarrierSipService.getSettings(billingAccount, serviceName), + }, + }); +}; diff --git a/packages/manager/apps/telecom/src/app/telecom/telephony/carrierSip/dashboard/endpoints/endpoints.module.js b/packages/manager/apps/telecom/src/app/telecom/telephony/carrierSip/dashboard/endpoints/endpoints.module.js new file mode 100644 index 000000000000..f37c079d7a66 --- /dev/null +++ b/packages/manager/apps/telecom/src/app/telecom/telephony/carrierSip/dashboard/endpoints/endpoints.module.js @@ -0,0 +1,19 @@ +import angular from 'angular'; + +// Module dependencies. +import uiRouter from '@uirouter/angularjs'; +import ovhManagerCarrierSip from '@ovh-ux/manager-carrier-sip'; + +// Routing and configuration. +import routing from './endpoints.routing'; + +const moduleName = 'ovhManagerTelecomCarrierSipDashboardEndpoints'; + +angular + .module(moduleName, [ + ovhManagerCarrierSip, + uiRouter, + ]) + .config(routing); + +export default moduleName; diff --git a/packages/manager/apps/telecom/src/app/telecom/telephony/carrierSip/dashboard/endpoints/endpoints.routing.js b/packages/manager/apps/telecom/src/app/telecom/telephony/carrierSip/dashboard/endpoints/endpoints.routing.js new file mode 100644 index 000000000000..49eaa4df360a --- /dev/null +++ b/packages/manager/apps/telecom/src/app/telecom/telephony/carrierSip/dashboard/endpoints/endpoints.routing.js @@ -0,0 +1,15 @@ +export default /* @ngInject */ ($stateProvider) => { + $stateProvider.state('telecom.telephony.carrierSip.endpoints', { + url: '/endpoints', + views: { + '@telecom.telephony.carrierSip': 'carrierSipEndpoints', + }, + resolve: { + endpoints: /* @ngInject */ ( + billingAccount, + CarrierSipService, + serviceName, + ) => CarrierSipService.getEndpoints(billingAccount, serviceName), + }, + }); +}; diff --git a/packages/manager/apps/telecom/src/app/telecom/telephony/carrierSip/dashboard/endpoints/index.js b/packages/manager/apps/telecom/src/app/telecom/telephony/carrierSip/dashboard/endpoints/index.js new file mode 100644 index 000000000000..1879805c2f5d --- /dev/null +++ b/packages/manager/apps/telecom/src/app/telecom/telephony/carrierSip/dashboard/endpoints/index.js @@ -0,0 +1,26 @@ +import angular from 'angular'; + +// Module dependencies. +import uiRouter from '@uirouter/angularjs'; +import oclazyload from 'oclazyload'; + +const moduleName = 'ovhManagerTelecomCarrierSipDashboardEndpointsLazyLoading'; + +angular + .module(moduleName, [ + oclazyload, + uiRouter, + ]) + .config(/* @ngInject */($stateProvider) => { + $stateProvider.state('telecom.telephony.carrierSip.endpoints.**', { + url: '/endpoints', + lazyLoad: ($transition$) => { + const $ocLazyLoad = $transition$.injector().get('$ocLazyLoad'); + + return import('./endpoints.module') + .then(mod => $ocLazyLoad.inject(mod.default || mod)); + }, + }); + }); + +export default moduleName; diff --git a/packages/manager/apps/telecom/src/app/telecom/telephony/carrierSip/dashboard/index.js b/packages/manager/apps/telecom/src/app/telecom/telephony/carrierSip/dashboard/index.js new file mode 100644 index 000000000000..a4af66e4523a --- /dev/null +++ b/packages/manager/apps/telecom/src/app/telecom/telephony/carrierSip/dashboard/index.js @@ -0,0 +1,25 @@ +import angular from 'angular'; + +// Module dependencies. +import uiRouter from '@uirouter/angularjs'; +import oclazyload from 'oclazyload'; + +const moduleName = 'ovhManagerTelecomCarrierSipDashboardLazyLoading'; + +angular + .module(moduleName, [ + oclazyload, + uiRouter, + ]) + .config(/* @ngInject */($stateProvider) => { + $stateProvider.state('telecom.telephony.carrierSip.**', { + url: '/carrierSip', + lazyLoad: ($transition$) => { + const $ocLazyLoad = $transition$.injector().get('$ocLazyLoad'); + + return import('./dashboard.module') + .then(mod => $ocLazyLoad.inject(mod.default || mod)); + }, + }); + }); +export default moduleName; diff --git a/packages/manager/apps/telecom/src/app/telecom/telephony/carrierSip/index.js b/packages/manager/apps/telecom/src/app/telecom/telephony/carrierSip/index.js new file mode 100644 index 000000000000..d7ddfa3d45e6 --- /dev/null +++ b/packages/manager/apps/telecom/src/app/telecom/telephony/carrierSip/index.js @@ -0,0 +1,15 @@ +import angular from 'angular'; +import ovhManagerCarrierSip from '@ovh-ux/manager-carrier-sip'; + +// Module dependencies. +import dashboard from './dashboard'; + +const moduleName = 'ovhManagerTelecomCarrierSip'; + +angular + .module(moduleName, [ + dashboard, + ovhManagerCarrierSip, + ]); + +export default moduleName; diff --git a/packages/manager/apps/telecom/src/components/sidebar/translations/Messages_fr_FR.json b/packages/manager/apps/telecom/src/components/sidebar/translations/Messages_fr_FR.json index 6d95af0bae2f..a366470e2e34 100644 --- a/packages/manager/apps/telecom/src/components/sidebar/translations/Messages_fr_FR.json +++ b/packages/manager/apps/telecom/src/components/sidebar/translations/Messages_fr_FR.json @@ -9,6 +9,7 @@ "telecom_sidebar_view_more": "Afficher plus de résultats", "telecom_sidebar_dashboard": "Tableau de bord", "telecom_sidebar_informations": "Informations Générales", + "telecom_sidebar_section_telephony_carrier_sip": "Carrier SIP", "telecom_sidebar_section_telephony_number": "N°", "telecom_sidebar_section_telephony_line": "SIP", "telecom_sidebar_section_telephony_fax": "Fax", @@ -44,4 +45,4 @@ "telecom_sidebar_actions_menu_sms_hlr": "HLR", "telecom_sidebar_actions_menu_fax": "Fax", "telecom_sidebar_load_error": "Oups, nous n'arrivons pas à charger vos services." -} \ No newline at end of file +} diff --git a/packages/manager/apps/telecom/src/components/telecom/telephony/sidebar/telephony-sidebar.service.js b/packages/manager/apps/telecom/src/components/telecom/telephony/sidebar/telephony-sidebar.service.js index 372c66f7bfca..eb6c5df9ea46 100644 --- a/packages/manager/apps/telecom/src/components/telecom/telephony/sidebar/telephony-sidebar.service.js +++ b/packages/manager/apps/telecom/src/components/telecom/telephony/sidebar/telephony-sidebar.service.js @@ -1,6 +1,6 @@ import filter from 'lodash/filter'; -angular.module('managerApp').service('TelephonySidebar', function TelephonySidebar($translate, SidebarMenu, tucTelecomVoip, tucVoipService) { +angular.module('managerApp').service('TelephonySidebar', function TelephonySidebar($q, $translate, CarrierSipService, SidebarMenu, tucTelecomVoip, tucVoipService) { const self = this; self.mainSectionItem = null; @@ -23,79 +23,93 @@ angular.module('managerApp').service('TelephonySidebar', function TelephonySideb /* * Telephony sidebar initilization */ - self.initTelephonySubsection = function initTelephonySubsection() { - return tucTelecomVoip.fetchAll().then((billingAccounts) => { - /* ---------- billingAccount display ---------- */ - - // first sort by getDisplayedName - const sortedBillingAccounts = billingAccounts - .sort((first, second) => first.getDisplayedName().localeCompare(second.getDisplayedName())); - - // add billingAccount subsections to telephony sidebar menu item - sortedBillingAccounts.forEach((billingAccount) => { - // create subsection object - const billingAccountSubSection = SidebarMenu.addMenuItem({ - id: billingAccount.billingAccount, - title: billingAccount.getDisplayedName(), - state: 'telecom.telephony', - stateParams: { - billingAccount: billingAccount.billingAccount, - }, - allowSubItems: billingAccount.services.length > 0, - }, self.mainSectionItem); - - /* ---------- Numbers (alias) display ---------- */ - - // first sort numbers of the billingAccount - const sortedAlias = tucVoipService - .constructor.sortServicesByDisplayedName(billingAccount.getAlias()); - - // add number subsections to billingAccount subsection - addServiceMenuItems(sortedAlias, { - state: 'telecom.telephony.alias', - prefix: $translate.instant('telecom_sidebar_section_telephony_number'), - }, billingAccountSubSection); - - /* ---------- Lines display ---------- */ - - // first sort lines of the billingAccount - const sortedLines = tucVoipService - .constructor.sortServicesByDisplayedName(billingAccount.getLines()); - - // display lines except plugAndFax and fax - const sortedSipLines = filter(sortedLines, line => ['plugAndFax', 'fax', 'voicefax'].indexOf(line.featureType) === -1); - - // add line subsections to billingAccount subsection - const sipTrunkPrefix = $translate.instant('telecom_sidebar_section_telephony_trunk'); - const sipPrefix = $translate.instant('telecom_sidebar_section_telephony_line'); - - addServiceMenuItems(sortedSipLines, { - state: 'telecom.telephony.line', - prefix(lineService) { - return lineService.isSipTrunk() ? sipTrunkPrefix : sipPrefix; - }, - }, billingAccountSubSection); - - // second get plugAndFax - const sortedPlugAndFaxLines = tucVoipService.constructor - .filterPlugAndFaxServices(sortedLines); - - // add plugAndFax subsections to billingAccount subsection - addServiceMenuItems(sortedPlugAndFaxLines, { - state: 'telecom.telephony.line', - prefix: $translate.instant('telecom_sidebar_section_telephony_plug_fax'), - }, billingAccountSubSection); - - // then get fax - const sortedFaxLines = tucVoipService.constructor.filterFaxServices(sortedLines); - - // add fax subsections to billingAccount subsection - addServiceMenuItems(sortedFaxLines, { - state: 'telecom.telephony.fax', - prefix: $translate.instant('telecom_sidebar_section_telephony_fax'), - }, billingAccountSubSection); - }); - }); + self.initTelephonySubsection = function () { + return tucTelecomVoip.fetchAll() + .then(billingAccounts => $q + .all(billingAccounts.map(({ billingAccount }) => CarrierSipService + .fetchAll(billingAccount))) + .then((allCarrierSipLines) => { + // first sort by getDisplayedName + const sortedBillingAccounts = billingAccounts + .sort((first, second) => first.getDisplayedName() + .localeCompare(second.getDisplayedName())); + + sortedBillingAccounts.forEach((billingAccount) => { + // create subsection object + const billingAccountSubSection = SidebarMenu.addMenuItem({ + id: billingAccount.billingAccount, + title: billingAccount.getDisplayedName(), + state: 'telecom.telephony', + stateParams: { + billingAccount: billingAccount.billingAccount, + }, + allowSubItems: billingAccount.services.length > 0, + }, self.mainSectionItem); + + /* ---------- Numbers (alias) display ---------- */ + + // first sort numbers of the billingAccount + const sortedAlias = tucVoipService + .constructor.sortServicesByDisplayedName(billingAccount.getAlias()); + + // add number subsections to billingAccount subsection + addServiceMenuItems(sortedAlias, { + state: 'telecom.telephony.alias', + prefix: $translate.instant('telecom_sidebar_section_telephony_number'), + }, billingAccountSubSection); + + /* ---------- Lines display ---------- */ + + // first sort lines of the billingAccount + const sortedLines = tucVoipService + .constructor.sortServicesByDisplayedName(billingAccount.getLines()); + + // display lines except plugAndFax and fax + const sortedSipLines = filter(sortedLines, line => ['plugAndFax', 'fax', 'voicefax'].indexOf(line.featureType) === -1); + + // add line subsections to billingAccount subsection + const sipTrunkPrefix = $translate.instant('telecom_sidebar_section_telephony_trunk'); + const sipPrefix = $translate.instant('telecom_sidebar_section_telephony_line'); + + addServiceMenuItems(sortedSipLines, { + state: 'telecom.telephony.line', + prefix(lineService) { + return lineService.isSipTrunk() ? sipTrunkPrefix : sipPrefix; + }, + }, billingAccountSubSection); + + // second get plugAndFax + const sortedPlugAndFaxLines = tucVoipService.constructor + .filterPlugAndFaxServices(sortedLines); + + // add plugAndFax subsections to billingAccount subsection + addServiceMenuItems(sortedPlugAndFaxLines, { + state: 'telecom.telephony.line', + prefix: $translate.instant('telecom_sidebar_section_telephony_plug_fax'), + }, billingAccountSubSection); + + // then get fax + const sortedFaxLines = tucVoipService.constructor.filterFaxServices(sortedLines); + + // add fax subsections to billingAccount subsection + addServiceMenuItems(sortedFaxLines, { + state: 'telecom.telephony.fax', + prefix: $translate.instant('telecom_sidebar_section_telephony_fax'), + }, billingAccountSubSection); + + /* ---------- CarrierSip Lines display ----------- */ + + const filteredCaerierSipLines = allCarrierSipLines + .flat() + .filter(carrierSipLine => carrierSipLine + .billingAccount === billingAccount.billingAccount); + + addServiceMenuItems(filteredCaerierSipLines, { + state: 'telecom.telephony.carrierSip', + prefix: $translate.instant('telecom_sidebar_section_telephony_carrier_sip'), + }, billingAccountSubSection); + }); + })); }; self.init = function init() { diff --git a/packages/manager/apps/telecom/webpack.config.js b/packages/manager/apps/telecom/webpack.config.js index 18829b9b4801..06500f4a3ba7 100644 --- a/packages/manager/apps/telecom/webpack.config.js +++ b/packages/manager/apps/telecom/webpack.config.js @@ -24,7 +24,9 @@ function foundNodeModulesFolder(checkedDir, cwd = '.') { fs.readdirSync(folder).forEach((file) => { const stats = fs.lstatSync(`${folder}/${file}`); if (stats.isDirectory()) { - const jsFiles = glob.sync(`${folder}/${file}/**/*.js`); + const jsFiles = glob.sync(`${folder}/${file}/**/*.js`, { + ignore: `${folder}/${file}/carrierSip/*.js`, + }); if (jsFiles.length > 0) { bundles[file] = jsFiles; } diff --git a/yarn.lock b/yarn.lock index e9f5777d5059..921db8018a2e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3605,6 +3605,7 @@ bootstrap@^3.3.6, bootstrap@^3.3.7, bootstrap@^3.4.1, bootstrap@~3: integrity sha512-yN5oZVmRCwe5aKwzRj6736nSmKDX7pLYwsXiCj/EYmo16hODaBiT4En5btW/jhBF/seV+XMx3aYwukYC3A49DA== bootstrap@^4.3.1: + name bootstrap4 version "4.3.1" resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.3.1.tgz#280ca8f610504d99d7b6b4bfc4b68cec601704ac" integrity sha512-rXqOmH1VilAt2DyPzluTi2blhk17bO7ef+zLLPlWvG494pDxcM234pJ8wTc/6R40UWizAIIMgxjvxZg5kmsbag== @@ -9324,7 +9325,7 @@ jquery.scrollto@^1.4.13: dependencies: jquery ">=1.4" -"jquery@>= 1.9.1", jquery@>=1.4, jquery@>=1.7.1, jquery@>=1.8: +"jquery@>= 1.9.1", jquery@>=1.4, jquery@>=1.7.1, jquery@>=1.8, jquery@^3.4.1: version "3.4.1" resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.4.1.tgz#714f1f8d9dde4bdfa55764ba37ef214630d80ef2" integrity sha512-36+AdBzCL+y6qjw5Tx7HgzeGCzC81MDDgaUP8ld2zhx58HdqXGoBd+tHdrBMiyjGQs0Hxs/MLZTu/eHNJJuWPw== @@ -11853,6 +11854,13 @@ ovh-ui-kit@^2.34.1: dependencies: less-plugin-remcalc "^0.1.0" +ovh-ui-kit@^2.34.3: + version "2.34.3" + resolved "https://registry.yarnpkg.com/ovh-ui-kit/-/ovh-ui-kit-2.34.3.tgz#882832cc2f561a16da14cebfe8d940977c2ac2db" + integrity sha512-vcDbFf4fa6MkJF6wdpzBl757FqV9QAXxQ0hAJvosLQ3jHI1WmyRmes5izwLCrxv+yOmZmoQAUODqgz+8ZZXUuQ== + dependencies: + less-plugin-remcalc "^0.1.0" + "ovh-ui-kit@git+https://github.com/ovh-ux/ovh-ui-kit.git": version "0.0.0" resolved "git+https://github.com/ovh-ux/ovh-ui-kit.git#1b4b27fc073ab81fe1c8f24f115ebb7af31e5d6e"